Integrate purger to node

- Common
	- Add `IdxNonce` type used to track nonces in accounts to invalidate
	  l2txs in the pool
- Config
	- Update coordinator config will all the new configuration parameters
	  used in the coordinator
- Coordinator
	- Introduce the `Purger` to track how often to purge and do the job when
	  needed according to a configuration.
	- Implement the methods to invalidate l2txs transactions due to l2txs
	  selection in batches.  For now these functions are not used in favour
	  of the `Purger` methods, which check ALL the l2txs in the pool.
	- Call Invalidation and Purging methods of the purger both when the
	  node is forging (in the pipeline when starting a new batch) and when
	  the node is not forging (in coordinator when being notified about a
	  new synced block)
- L2DB:
	- Implement `GetPendingUniqueFromIdxs` to get all the unique idxs from
	  pending transactions (used to get their nonces and then invalidate
	  txs)
	- Redo `CheckNonces` with a single SQL query and using `common.IdxNonce`
	  instead of `common.Account`
- StateDB:
	- Expose GetIdx to check errors when invalidating pool txs
- Synchronizer:
	- Test forged L1UserTxs processed by TxProcessor
	- Improve checks of Effective values
- TxSelector:
	- Expose the internal LocalStateDB in order to check account nonces in
	  the coordinator when not forging.
This commit is contained in:
Eduard S
2020-12-03 14:58:26 +01:00
parent 8de7fe537a
commit 900d1fb6ce
15 changed files with 402 additions and 101 deletions

View File

@@ -1,6 +1,7 @@
package l2db
import (
"fmt"
"math/big"
"time"
@@ -242,38 +243,52 @@ func (l2db *L2DB) InvalidateTxs(txIDs []common.TxID, batchNum common.BatchNum) e
return tracerr.Wrap(err)
}
// CheckNonces invalidate txs with nonces that are smaller or equal than their respective accounts nonces.
// The state of the affected txs will be changed from Pending -> Invalid
func (l2db *L2DB) CheckNonces(updatedAccounts []common.Account, batchNum common.BatchNum) (err error) {
// GetPendingUniqueFromIdxs returns from all the pending transactions, the set
// of unique FromIdx
func (l2db *L2DB) GetPendingUniqueFromIdxs() ([]common.Idx, error) {
var idxs []common.Idx
rows, err := l2db.db.Query(`SELECT DISTINCT from_idx FROM tx_pool
WHERE state = $1;`, common.PoolL2TxStatePending)
if err != nil {
return nil, tracerr.Wrap(err)
}
var idx common.Idx
for rows.Next() {
err = rows.Scan(&idx)
if err != nil {
return nil, tracerr.Wrap(err)
}
idxs = append(idxs, idx)
}
return idxs, nil
}
var checkNoncesQuery = fmt.Sprintf(`
UPDATE tx_pool SET
state = '%s',
batch_num = %%d
FROM (VALUES
(NULL::::BIGINT, NULL::::BIGINT),
(:idx, :nonce)
) as updated_acc (idx, nonce)
WHERE tx_pool.from_idx = updated_acc.idx AND tx_pool.nonce <= updated_acc.nonce;
`, common.PoolL2TxStateInvalid)
// CheckNonces invalidate txs with nonces that are smaller or equal than their
// respective accounts nonces. The state of the affected txs will be changed
// from Pending to Invalid
func (l2db *L2DB) CheckNonces(updatedAccounts []common.IdxNonce, batchNum common.BatchNum) (err error) {
if len(updatedAccounts) == 0 {
return nil
}
txn, err := l2db.db.Beginx()
if err != nil {
// Fill the batch_num in the query with Sprintf because we are using a
// named query which works with slices, and doens't handle an extra
// individual argument.
query := fmt.Sprintf(checkNoncesQuery, batchNum)
if _, err := sqlx.NamedQuery(l2db.db, query, updatedAccounts); err != nil {
return tracerr.Wrap(err)
}
defer func() {
// Rollback the transaction if there was an error.
if err != nil {
db.Rollback(txn)
}
}()
for i := 0; i < len(updatedAccounts); i++ {
_, err = txn.Exec(
`UPDATE tx_pool
SET state = $1, batch_num = $2
WHERE state = $3 AND from_idx = $4 AND nonce <= $5;`,
common.PoolL2TxStateInvalid,
batchNum,
common.PoolL2TxStatePending,
updatedAccounts[i].Idx,
updatedAccounts[i].Nonce,
)
if err != nil {
return tracerr.Wrap(err)
}
}
return tracerr.Wrap(txn.Commit())
return nil
}
// Reorg updates the state of txs that were updated in a batch that has been discarted due to a blockchain reorg.

View File

@@ -334,12 +334,13 @@ func TestCheckNonces(t *testing.T) {
poolL2Txs, err := generatePoolL2Txs()
assert.NoError(t, err)
// Update Accounts currentNonce
var updateAccounts []common.Account
var updateAccounts []common.IdxNonce
const currentNonce = common.Nonce(1)
for i := range accs {
account := accs[i]
account.Nonce = common.Nonce(currentNonce)
updateAccounts = append(updateAccounts, account)
updateAccounts = append(updateAccounts, common.IdxNonce{
Idx: accs[i].Idx,
Nonce: common.Nonce(currentNonce),
})
}
// Add txs to DB
var invalidTxIDs []common.TxID

View File

@@ -303,7 +303,7 @@ func (s *StateDB) reset(batchNum common.BatchNum, closeCurrent bool) error {
return tracerr.Wrap(err)
}
// idx is obtained from the statedb reset
s.idx, err = s.getIdx()
s.idx, err = s.GetIdx()
if err != nil {
return tracerr.Wrap(err)
}

View File

@@ -1118,9 +1118,9 @@ func (s *StateDB) computeEffectiveAmounts(tx *common.L1Tx) {
}
}
// getIdx returns the stored Idx from the localStateDB, which is the last Idx
// GetIdx returns the stored Idx from the localStateDB, which is the last Idx
// used for an Account in the localStateDB.
func (s *StateDB) getIdx() (common.Idx, error) {
func (s *StateDB) GetIdx() (common.Idx, error) {
idxBytes, err := s.DB().Get(keyidx)
if tracerr.Unwrap(err) == db.ErrNotFound {
return 0, nil