diff --git a/db/migrations/state/0016.sql b/db/migrations/state/0016.sql new file mode 100644 index 0000000000..b51ef47a05 --- /dev/null +++ b/db/migrations/state/0016.sql @@ -0,0 +1,7 @@ +-- +migrate Up +ALTER TABLE state.batch + ADD COLUMN IF NOT EXISTS checked BOOLEAN NOT NULL DEFAULT TRUE; + +-- +migrate Down +ALTER TABLE state.batch + DROP COLUMN IF EXISTS checked; \ No newline at end of file diff --git a/db/migrations/state/0016_test.go b/db/migrations/state/0016_test.go new file mode 100644 index 0000000000..cab53501b9 --- /dev/null +++ b/db/migrations/state/0016_test.go @@ -0,0 +1,64 @@ +package migrations_test + +import ( + "database/sql" + "testing" + + "github.com/stretchr/testify/assert" +) + +type migrationTest0016 struct{} + +func (m migrationTest0016) InsertData(db *sql.DB) error { + const insertBatch0 = ` + INSERT INTO state.batch (batch_num, global_exit_root, local_exit_root, acc_input_hash, state_root, timestamp, coinbase, raw_txs_data, forced_batch_num, wip) + VALUES (0,'0x0000', '0x0000', '0x0000', '0x0000', now(), '0x0000', null, null, true)` + + // insert batch + _, err := db.Exec(insertBatch0) + if err != nil { + return err + } + + return nil +} + +func (m migrationTest0016) RunAssertsAfterMigrationUp(t *testing.T, db *sql.DB) { + var result int + + // Check column checked exists in state.batch table + const getCheckedColumn = `SELECT count(*) FROM information_schema.columns WHERE table_name='batch' and column_name='checked'` + row := db.QueryRow(getCheckedColumn) + assert.NoError(t, row.Scan(&result)) + assert.Equal(t, 1, result) + + const insertBatch0 = ` + INSERT INTO state.batch (batch_num, global_exit_root, local_exit_root, acc_input_hash, state_root, timestamp, coinbase, raw_txs_data, forced_batch_num, wip, checked) + VALUES (1,'0x0001', '0x0001', '0x0001', '0x0001', now(), '0x0001', null, null, true, false)` + + // insert batch 1 + _, err := db.Exec(insertBatch0) + assert.NoError(t, err) + + const insertBatch1 = ` + INSERT INTO state.batch (batch_num, global_exit_root, local_exit_root, acc_input_hash, state_root, timestamp, coinbase, raw_txs_data, forced_batch_num, wip, checked) + VALUES (2,'0x0002', '0x0002', '0x0002', '0x0002', now(), '0x0002', null, null, false, true)` + + // insert batch 2 + _, err = db.Exec(insertBatch1) + assert.NoError(t, err) +} + +func (m migrationTest0016) RunAssertsAfterMigrationDown(t *testing.T, db *sql.DB) { + var result int + + // Check column wip doesn't exists in state.batch table + const getCheckedColumn = `SELECT count(*) FROM information_schema.columns WHERE table_name='batch' and column_name='checked'` + row := db.QueryRow(getCheckedColumn) + assert.NoError(t, row.Scan(&result)) + assert.Equal(t, 0, result) +} + +func TestMigration0016(t *testing.T) { + runMigrationTest(t, 16, migrationTest0016{}) +} diff --git a/pool/interfaces.go b/pool/interfaces.go index fc18081475..e61796453a 100644 --- a/pool/interfaces.go +++ b/pool/interfaces.go @@ -38,6 +38,7 @@ type storage interface { MarkWIPTxsAsPending(ctx context.Context) error GetAllAddressesBlocked(ctx context.Context) ([]common.Address, error) MinL2GasPriceSince(ctx context.Context, timestamp time.Time) (uint64, error) + GetEarliestProcessedTx(ctx context.Context) (common.Hash, error) } type stateInterface interface { diff --git a/pool/pgpoolstorage/pgpoolstorage.go b/pool/pgpoolstorage/pgpoolstorage.go index 0c75a562d2..f4db73c860 100644 --- a/pool/pgpoolstorage/pgpoolstorage.go +++ b/pool/pgpoolstorage/pgpoolstorage.go @@ -790,3 +790,23 @@ func (p *PostgresPoolStorage) GetAllAddressesBlocked(ctx context.Context) ([]com return addrs, nil } + +// GetEarliestProcessedTx gets the earliest processed tx from the pool. Mainly used for cleanup +func (p *PostgresPoolStorage) GetEarliestProcessedTx(ctx context.Context) (common.Hash, error) { + const getEarliestProcessedTxnFromTxnPool = `SELECT hash + FROM pool.transaction + WHERE + status = 'selected' + ORDER BY received_at ASC + LIMIT 1` + + var txnHash string + err := p.db.QueryRow(ctx, getEarliestProcessedTxnFromTxnPool).Scan(&txnHash) + if errors.Is(err, pgx.ErrNoRows) { + return common.Hash{}, nil + } else if err != nil { + return common.Hash{}, err + } + + return common.HexToHash(txnHash), nil +} diff --git a/sequencer/batch.go b/sequencer/batch.go index 47b79cdf81..877dd76a0a 100644 --- a/sequencer/batch.go +++ b/sequencer/batch.go @@ -33,6 +33,32 @@ func (w *Batch) isEmpty() bool { return w.countOfL2Blocks == 0 } +// processBatchesPendingtoCheck performs a sanity check for batches closed but pending to be checked +func (f *finalizer) processBatchesPendingtoCheck(ctx context.Context) { + notCheckedBatches, err := f.stateIntf.GetNotCheckedBatches(ctx, nil) + if err != nil && err != state.ErrNotFound { + log.Fatalf("failed to get batches not checked, error: ", err) + } + + if len(notCheckedBatches) == 0 { + return + } + + log.Infof("executing sanity check for not checked batches") + + prevBatchNumber := notCheckedBatches[0].BatchNumber - 1 + prevBatch, err := f.stateIntf.GetBatchByNumber(ctx, prevBatchNumber, nil) + if err != nil { + log.Fatalf("failed to get batch %d, error: ", prevBatchNumber, err) + } + oldStateRoot := prevBatch.StateRoot + + for _, notCheckedBatch := range notCheckedBatches { + _, _ = f.batchSanityCheck(ctx, notCheckedBatch.BatchNumber, oldStateRoot, notCheckedBatch.StateRoot) + oldStateRoot = notCheckedBatch.StateRoot + } +} + // setWIPBatch sets finalizer wip batch to the state batch passed as parameter func (f *finalizer) setWIPBatch(ctx context.Context, wipStateBatch *state.Batch) (*Batch, error) { // Retrieve prevStateBatch to init the initialStateRoot of the wip batch @@ -89,7 +115,7 @@ func (f *finalizer) initWIPBatch(ctx context.Context) { // Get the last batch in trusted state lastStateBatch, err := f.stateIntf.GetBatchByNumber(ctx, lastBatchNum, nil) if err != nil { - log.Fatalf("failed to get last batch, error: %v", err) + log.Fatalf("failed to get last batch %d, error: %v", lastBatchNum, err) } isClosed := !lastStateBatch.WIP @@ -98,7 +124,7 @@ func (f *finalizer) initWIPBatch(ctx context.Context) { if isClosed { //if the last batch is close then open a new wip batch if lastStateBatch.BatchNumber+1 == f.cfg.HaltOnBatchNumber { - f.Halt(ctx, fmt.Errorf("finalizer reached stop sequencer on batch number: %d", f.cfg.HaltOnBatchNumber)) + f.Halt(ctx, fmt.Errorf("finalizer reached stop sequencer on batch number: %d", f.cfg.HaltOnBatchNumber), false) } f.wipBatch, err = f.openNewWIPBatch(ctx, lastStateBatch.BatchNumber+1, lastStateBatch.StateRoot) @@ -126,14 +152,14 @@ func (f *finalizer) finalizeWIPBatch(ctx context.Context, closeReason state.Clos prevTimestamp := f.wipL2Block.timestamp prevL1InfoTreeIndex := f.wipL2Block.l1InfoTreeExitRoot.L1InfoTreeIndex - // Close the wip L2 block if it has transactions, if not we keep it open to store it in the new wip batch + // Close the wip L2 block if it has transactions, otherwise we keep the wip L2 block to store it in the new wip batch if !f.wipL2Block.isEmpty() { f.closeWIPL2Block(ctx) } err := f.closeAndOpenNewWIPBatch(ctx, closeReason) if err != nil { - f.Halt(ctx, fmt.Errorf("failed to create new WIP batch, error: %v", err)) + f.Halt(ctx, fmt.Errorf("failed to create new WIP batch, error: %v", err), true) } // If we have closed the wipL2Block then we open a new one @@ -142,8 +168,18 @@ func (f *finalizer) finalizeWIPBatch(ctx context.Context, closeReason state.Clos } } -// closeAndOpenNewWIPBatch closes the current batch and opens a new one, potentially processing forced batches between the batch is closed and the resulting new empty batch +// closeAndOpenNewWIPBatch closes the current batch and opens a new one, potentially processing forced batches between the batch is closed and the resulting new wip batch func (f *finalizer) closeAndOpenNewWIPBatch(ctx context.Context, closeReason state.ClosingReason) error { + f.nextForcedBatchesMux.Lock() + processForcedBatches := len(f.nextForcedBatches) > 0 + f.nextForcedBatchesMux.Unlock() + + // If we will process forced batches after we close the wip batch then we must close the current wip L2 block, + // since the processForcedBatches function needs to create new L2 blocks (cannot "reuse" the current wip L2 block if it's empty) + if processForcedBatches { + f.closeWIPL2Block(ctx) + } + // Wait until all L2 blocks are processed by the executor startWait := time.Now() f.pendingL2BlocksToProcessWG.Wait() @@ -170,11 +206,7 @@ func (f *finalizer) closeAndOpenNewWIPBatch(ctx context.Context, closeReason sta // Reprocess full batch as sanity check if f.cfg.SequentialBatchSanityCheck { // Do the full batch reprocess now - _, err := f.batchSanityCheck(ctx, f.wipBatch.batchNumber, f.wipBatch.initialStateRoot, f.wipBatch.finalStateRoot) - if err != nil { - // There is an error reprocessing the batch. We halt the execution of the Sequencer at this point - return fmt.Errorf("halting sequencer because of error reprocessing full batch %d (sanity check), error: %v ", f.wipBatch.batchNumber, err) - } + _, _ = f.batchSanityCheck(ctx, f.wipBatch.batchNumber, f.wipBatch.initialStateRoot, f.wipBatch.finalStateRoot) } else { // Do the full batch reprocess in parallel go func() { @@ -183,7 +215,7 @@ func (f *finalizer) closeAndOpenNewWIPBatch(ctx context.Context, closeReason sta } if f.wipBatch.batchNumber+1 == f.cfg.HaltOnBatchNumber { - f.Halt(ctx, fmt.Errorf("finalizer reached stop sequencer on batch number: %d", f.cfg.HaltOnBatchNumber)) + f.Halt(ctx, fmt.Errorf("finalizer reached stop sequencer on batch number: %d", f.cfg.HaltOnBatchNumber), false) } // Metadata for the next batch @@ -191,7 +223,7 @@ func (f *finalizer) closeAndOpenNewWIPBatch(ctx context.Context, closeReason sta lastBatchNumber := f.wipBatch.batchNumber // Process forced batches - if len(f.nextForcedBatches) > 0 { + if processForcedBatches { lastBatchNumber, stateRoot = f.processForcedBatches(ctx, lastBatchNumber, stateRoot) // We must init/reset the wip L2 block from the state since processForcedBatches can created new L2 blocks f.initWIPL2Block(ctx) @@ -315,12 +347,13 @@ func (f *finalizer) batchSanityCheck(ctx context.Context, batchNum uint64, initi // Log batch detailed info log.Errorf("batch %d sanity check error: initialStateRoot: %s, expectedNewStateRoot: %s", batch.BatchNumber, initialStateRoot, expectedNewStateRoot) for i, rawL2block := range rawL2Blocks.Blocks { + log.Infof("block[%d], txs: %d, deltaTimestamp: %d, l1InfoTreeIndex: %d", i, len(rawL2block.Transactions), rawL2block.DeltaTimestamp, rawL2block.IndexL1InfoTree) for j, rawTx := range rawL2block.Transactions { - log.Infof("batch %d, block position: %d, tx position: %d, tx hash: %s", batch.BatchNumber, i, j, rawTx.Tx.Hash()) + log.Infof("block[%d].tx[%d]: %s, egpPct: %d", batch.BatchNumber, i, j, rawTx.Tx.Hash(), rawTx.EfficiencyPercentage) } } - f.Halt(ctx, fmt.Errorf("batch sanity check error. Check previous errors in logs to know which was the cause")) + f.Halt(ctx, fmt.Errorf("batch sanity check error. Check previous errors in logs to know which was the cause"), false) } log.Debugf("batch %d sanity check: initialStateRoot: %s, expectedNewStateRoot: %s", batchNum, initialStateRoot, expectedNewStateRoot) @@ -404,6 +437,13 @@ func (f *finalizer) batchSanityCheck(ctx context.Context, batchNum uint64, initi return nil, ErrStateRootNoMatch } + err = f.stateIntf.UpdateBatchAsChecked(ctx, batch.BatchNumber, nil) + if err != nil { + log.Errorf("failed to update batch %d as checked, error: %v", batch.BatchNumber, err) + reprocessError(batch) + return nil, ErrUpdateBatchAsChecked + } + log.Infof("successful sanity check for batch %d, initialStateRoot: %s, stateRoot: %s, l2Blocks: %d, time: %v, used counters: %s", batch.BatchNumber, initialStateRoot, batchResponse.NewStateRoot.String(), len(batchResponse.BlockResponses), endProcessing.Sub(startProcessing), f.logZKCounters(batchResponse.UsedZkCounters)) diff --git a/sequencer/errors.go b/sequencer/errors.go index 10c87aaa2f..a61ae7b4c1 100644 --- a/sequencer/errors.go +++ b/sequencer/errors.go @@ -14,6 +14,8 @@ var ( ErrReplacedTransaction = errors.New("replaced transaction") // ErrGetBatchByNumber happens when we get an error trying to get a batch by number (GetBatchByNumber) ErrGetBatchByNumber = errors.New("get batch by number error") + // ErrUpdateBatchAsChecked happens when we get an error trying to update a batch as checked (UpdateBatchAsChecked) + ErrUpdateBatchAsChecked = errors.New("update batch as checked error") // ErrDecodeBatchL2Data happens when we get an error trying to decode BatchL2Data (DecodeTxs) ErrDecodeBatchL2Data = errors.New("decoding BatchL2Data error") // ErrProcessBatch happens when we get an error trying to process (executor) a batch diff --git a/sequencer/finalizer.go b/sequencer/finalizer.go index 738c01ac0b..11521c0d0b 100644 --- a/sequencer/finalizer.go +++ b/sequencer/finalizer.go @@ -143,6 +143,9 @@ func (f *finalizer) Start(ctx context.Context) { mockL1InfoRoot[i] = byte(i) } + // Do sanity check for batches closed but pending to be checked + f.processBatchesPendingtoCheck(ctx) + // Update L1InfoRoot go f.checkL1InfoTreeUpdate(ctx) @@ -390,14 +393,13 @@ func (f *finalizer) processTransaction(ctx context.Context, tx *TxTracker, first // If EffectiveGasPrice >= txGasPrice, we process the tx with tx.GasPrice if tx.EffectiveGasPrice.Cmp(txGasPrice) >= 0 { - tx.EffectiveGasPrice.Set(txGasPrice) - loss := new(big.Int).Sub(tx.EffectiveGasPrice, txGasPrice) // If loss > 0 the warning message indicating we loss fee for thix tx if loss.Cmp(new(big.Int).SetUint64(0)) == 1 { log.Warnf("egp-loss: gasPrice: %d, effectiveGasPrice1: %d, loss: %d, tx: %s", txGasPrice, tx.EffectiveGasPrice, loss, tx.HashStr) } + tx.EffectiveGasPrice.Set(txGasPrice) tx.IsLastExecution = true } } @@ -438,7 +440,7 @@ func (f *finalizer) processTransaction(ctx context.Context, tx *TxTracker, first return nil, err } else if err == nil && !batchResponse.IsRomLevelError && len(batchResponse.BlockResponses) == 0 { err = fmt.Errorf("executor returned no errors and no responses for tx %s", tx.HashStr) - f.Halt(ctx, err) + f.Halt(ctx, err, false) } else if batchResponse.IsExecutorLevelError { log.Errorf("error received from executor, error: %v", err) // Delete tx from the worker @@ -721,7 +723,7 @@ func (f *finalizer) logZKCounters(counters state.ZKCounters) string { } // Halt halts the finalizer -func (f *finalizer) Halt(ctx context.Context, err error) { +func (f *finalizer) Halt(ctx context.Context, err error, isFatal bool) { f.haltFinalizer.Store(true) event := &event.Event{ @@ -738,8 +740,12 @@ func (f *finalizer) Halt(ctx context.Context, err error) { log.Errorf("error storing finalizer halt event, error: %v", eventErr) } - for { - log.Errorf("halting finalizer, fatal error: %v", err) - time.Sleep(5 * time.Second) //nolint:gomnd + if isFatal { + log.Fatalf("fatal error on finalizer, error: %v", err) + } else { + for { + log.Errorf("halting finalizer, error: %v", err) + time.Sleep(5 * time.Second) //nolint:gomnd + } } } diff --git a/sequencer/forcedbatch.go b/sequencer/forcedbatch.go index 9ad384761d..16fd40e564 100644 --- a/sequencer/forcedbatch.go +++ b/sequencer/forcedbatch.go @@ -129,16 +129,18 @@ func (f *finalizer) processForcedBatch(ctx context.Context, forcedBatch state.Fo return rollbackOnError(fmt.Errorf("error closing state batch %d for forced batch %d, error: %v", newBatchNumber, forcedBatch.ForcedBatchNumber, err)) } + if len(batchResponse.BlockResponses) > 0 && !batchResponse.IsRomOOCError { + err = f.handleProcessForcedBatchResponse(ctx, newBatchNumber, batchResponse, dbTx) + if err != nil { + return rollbackOnError(fmt.Errorf("error when handling batch response for forced batch %d, error: %v", forcedBatch.ForcedBatchNumber, err)) + } + } + err = dbTx.Commit(ctx) if err != nil { return rollbackOnError(fmt.Errorf("error when commit dbTx when processing forced batch %d, error: %v", forcedBatch.ForcedBatchNumber, err)) } - if len(batchResponse.BlockResponses) > 0 && !batchResponse.IsRomOOCError { - err = f.handleProcessForcedBatchResponse(ctx, batchResponse, dbTx) - return rollbackOnError(fmt.Errorf("error when handling batch response for forced batch %d, error: %v", forcedBatch.ForcedBatchNumber, err)) - } - return newBatchNumber, batchResponse.NewStateRoot, nil } @@ -157,7 +159,7 @@ func (f *finalizer) addForcedTxToWorker(forcedBatchResponse *state.ProcessBatchR } // handleProcessForcedTxsResponse handles the block/transactions responses for the processed forced batch. -func (f *finalizer) handleProcessForcedBatchResponse(ctx context.Context, batchResponse *state.ProcessBatchResponse, dbTx pgx.Tx) error { +func (f *finalizer) handleProcessForcedBatchResponse(ctx context.Context, newBatchNumber uint64, batchResponse *state.ProcessBatchResponse, dbTx pgx.Tx) error { f.addForcedTxToWorker(batchResponse) f.updateFlushIDs(batchResponse.FlushID, batchResponse.StoredFlushID) @@ -177,7 +179,7 @@ func (f *finalizer) handleProcessForcedBatchResponse(ctx context.Context, batchR // process L2 blocks responses for the forced batch for _, forcedL2BlockResponse := range batchResponse.BlockResponses { // Store forced L2 blocks in the state - err := f.stateIntf.StoreL2Block(ctx, batchResponse.NewBatchNumber, forcedL2BlockResponse, nil, dbTx) + err := f.stateIntf.StoreL2Block(ctx, newBatchNumber, forcedL2BlockResponse, nil, dbTx) if err != nil { return fmt.Errorf("database error on storing L2 block %d, error: %v", forcedL2BlockResponse.BlockNumber, err) } @@ -195,7 +197,7 @@ func (f *finalizer) handleProcessForcedBatchResponse(ctx context.Context, batchR } // Send L2 block to data streamer - err = f.DSSendL2Block(batchResponse.NewBatchNumber, forcedL2BlockResponse, 0) + err = f.DSSendL2Block(newBatchNumber, forcedL2BlockResponse, 0) if err != nil { //TODO: we need to halt/rollback the L2 block if we had an error sending to the data streamer? log.Errorf("error sending L2 block %d to data streamer, error: %v", forcedL2BlockResponse.BlockNumber, err) diff --git a/sequencer/interfaces.go b/sequencer/interfaces.go index 54b8276a41..21347787c1 100644 --- a/sequencer/interfaces.go +++ b/sequencer/interfaces.go @@ -5,10 +5,8 @@ import ( "math/big" "time" - ethmanTypes "github.com/0xPolygonHermez/zkevm-node/etherman/types" "github.com/0xPolygonHermez/zkevm-node/pool" "github.com/0xPolygonHermez/zkevm-node/state" - "github.com/0xPolygonHermez/zkevm-node/state/runtime/executor" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/jackc/pgx/v4" @@ -29,62 +27,42 @@ type txPool interface { GetGasPrices(ctx context.Context) (pool.GasPrices, error) GetDefaultMinGasPriceAllowed() uint64 GetL1AndL2GasPrice() (uint64, uint64) + GetEarliestProcessedTx(ctx context.Context) (common.Hash, error) } // etherman contains the methods required to interact with ethereum. type etherman interface { - EstimateGasSequenceBatches(sender common.Address, sequences []ethmanTypes.Sequence, l2CoinBase common.Address) (*types.Transaction, error) - GetSendSequenceFee(numBatches uint64) (*big.Int, error) TrustedSequencer() (common.Address, error) GetLatestBatchNumber() (uint64, error) - GetLatestBlockTimestamp(ctx context.Context) (uint64, error) - BuildSequenceBatchesTxData(sender common.Address, sequences []ethmanTypes.Sequence, l2CoinBase common.Address) (to *common.Address, data []byte, err error) GetLatestBlockNumber(ctx context.Context) (uint64, error) } // stateInterface gathers the methods required to interact with the state. type stateInterface interface { - GetTimeForLatestBatchVirtualization(ctx context.Context, dbTx pgx.Tx) (time.Time, error) - GetTxsOlderThanNL1Blocks(ctx context.Context, nL1Blocks uint64, dbTx pgx.Tx) ([]common.Hash, error) + GetTxsOlderThanNL1BlocksUntilTxHash(ctx context.Context, nL1Blocks uint64, earliestTxHash common.Hash, dbTx pgx.Tx) ([]common.Hash, error) GetBatchByNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.Batch, error) - GetTransactionsByBatchNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (txs []types.Transaction, effectivePercentages []uint8, err error) BeginStateTransaction(ctx context.Context) (pgx.Tx, error) GetLastVirtualBatchNum(ctx context.Context, dbTx pgx.Tx) (uint64, error) - IsBatchClosed(ctx context.Context, batchNum uint64, dbTx pgx.Tx) (bool, error) - Begin(ctx context.Context) (pgx.Tx, error) GetBalanceByStateRoot(ctx context.Context, address common.Address, root common.Hash) (*big.Int, error) GetNonceByStateRoot(ctx context.Context, address common.Address, root common.Hash) (*big.Int, error) GetLastStateRoot(ctx context.Context, dbTx pgx.Tx) (common.Hash, error) - ProcessBatch(ctx context.Context, request state.ProcessRequest, updateMerkleTree bool) (*state.ProcessBatchResponse, error) ProcessBatchV2(ctx context.Context, request state.ProcessRequest, updateMerkleTree bool) (*state.ProcessBatchResponse, error) CloseBatch(ctx context.Context, receipt state.ProcessingReceipt, dbTx pgx.Tx) error CloseWIPBatch(ctx context.Context, receipt state.ProcessingReceipt, dbTx pgx.Tx) error - ExecuteBatch(ctx context.Context, batch state.Batch, updateMerkleTree bool, dbTx pgx.Tx) (*executor.ProcessBatchResponse, error) - ExecuteBatchV2(ctx context.Context, batch state.Batch, L1InfoTreeRoot common.Hash, l1InfoTreeData map[uint32]state.L1DataV2, timestampLimit time.Time, updateMerkleTree bool, skipVerifyL1InfoRoot uint32, forcedBlockHashL1 *common.Hash, dbTx pgx.Tx) (*executor.ProcessBatchResponseV2, error) GetForcedBatch(ctx context.Context, forcedBatchNumber uint64, dbTx pgx.Tx) (*state.ForcedBatch, error) - GetLastBatch(ctx context.Context, dbTx pgx.Tx) (*state.Batch, error) GetLastBatchNumber(ctx context.Context, dbTx pgx.Tx) (uint64, error) OpenBatch(ctx context.Context, processingContext state.ProcessingContext, dbTx pgx.Tx) error OpenWIPBatch(ctx context.Context, batch state.Batch, dbTx pgx.Tx) error - GetWIPBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.Batch, error) - GetLastNBatches(ctx context.Context, numBatches uint, dbTx pgx.Tx) ([]*state.Batch, error) - StoreTransaction(ctx context.Context, batchNumber uint64, processedTx *state.ProcessTransactionResponse, coinbase common.Address, timestamp uint64, egpLog *state.EffectiveGasPriceLog, globalExitRoot, blockInfoRoot common.Hash, dbTx pgx.Tx) (*state.L2Header, error) - GetLastClosedBatch(ctx context.Context, dbTx pgx.Tx) (*state.Batch, error) GetLastL2Block(ctx context.Context, dbTx pgx.Tx) (*state.L2Block, error) GetLastBlock(ctx context.Context, dbTx pgx.Tx) (*state.Block, error) - GetLatestGlobalExitRoot(ctx context.Context, maxBlockNumber uint64, dbTx pgx.Tx) (state.GlobalExitRoot, time.Time, error) - GetLastL2BlockHeader(ctx context.Context, dbTx pgx.Tx) (*state.L2Header, error) UpdateWIPBatch(ctx context.Context, receipt state.ProcessingReceipt, dbTx pgx.Tx) error + UpdateBatchAsChecked(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error GetForcedBatchesSince(ctx context.Context, forcedBatchNumber, maxBlockNumber uint64, dbTx pgx.Tx) ([]*state.ForcedBatch, error) GetLastTrustedForcedBatchNumber(ctx context.Context, dbTx pgx.Tx) (uint64, error) - GetLatestVirtualBatchTimestamp(ctx context.Context, dbTx pgx.Tx) (time.Time, error) CountReorgs(ctx context.Context, dbTx pgx.Tx) (uint64, error) - GetLatestGer(ctx context.Context, maxBlockNumber uint64) (state.GlobalExitRoot, time.Time, error) GetLatestL1InfoRoot(ctx context.Context, maxBlockNumber uint64) (state.L1InfoTreeExitRootStorageEntry, error) - FlushMerkleTree(ctx context.Context, newStateRoot common.Hash) error GetStoredFlushID(ctx context.Context) (uint64, string, error) GetForkIDByBatchNumber(batchNumber uint64) uint64 - AddL2Block(ctx context.Context, batchNumber uint64, l2Block *state.L2Block, receipts []*types.Receipt, txsL2Hash []common.Hash, txsEGPData []state.StoreTxEGPData, dbTx pgx.Tx) error GetDSGenesisBlock(ctx context.Context, dbTx pgx.Tx) (*state.DSL2Block, error) GetDSBatches(ctx context.Context, firstBatchNumber, lastBatchNumber uint64, readWIPBatch bool, dbTx pgx.Tx) ([]*state.DSBatch, error) GetDSL2Blocks(ctx context.Context, firstBatchNumber, lastBatchNumber uint64, dbTx pgx.Tx) ([]*state.DSL2Block, error) @@ -97,8 +75,8 @@ type stateInterface interface { GetVirtualBatchParentHash(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (common.Hash, error) GetForcedBatchParentHash(ctx context.Context, forcedBatchNumber uint64, dbTx pgx.Tx) (common.Hash, error) GetL1InfoRootLeafByIndex(ctx context.Context, l1InfoTreeIndex uint32, dbTx pgx.Tx) (state.L1InfoTreeExitRootStorageEntry, error) - GetVirtualBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.VirtualBatch, error) GetLatestBatchGlobalExitRoot(ctx context.Context, dbTx pgx.Tx) (common.Hash, error) + GetNotCheckedBatches(ctx context.Context, dbTx pgx.Tx) ([]*state.Batch, error) } type workerInterface interface { diff --git a/sequencer/l2block.go b/sequencer/l2block.go index 772c4ec662..63dbabf3d1 100644 --- a/sequencer/l2block.go +++ b/sequencer/l2block.go @@ -19,7 +19,6 @@ type L2Block struct { trackingNum uint64 timestamp uint64 deltaTimestamp uint32 - initialStateRoot common.Hash imStateRoot common.Hash l1InfoTreeExitRoot state.L1InfoTreeExitRootStorageEntry l1InfoTreeExitRootChanged bool @@ -111,7 +110,9 @@ func (f *finalizer) processPendingL2Blocks(ctx context.Context) { err := f.processL2Block(ctx, l2Block) if err != nil { - f.Halt(ctx, fmt.Errorf("error processing L2 block [%d], error: %v", l2Block.trackingNum, err)) + // Dump L2Block info + f.dumpL2Block(l2Block) + f.Halt(ctx, fmt.Errorf("error processing L2 block [%d], error: %v", l2Block.trackingNum, err), false) } f.pendingL2BlocksToProcessWG.Done() @@ -141,7 +142,7 @@ func (f *finalizer) storePendingL2Blocks(ctx context.Context) { if err != nil { // Dump L2Block info f.dumpL2Block(l2Block) - f.Halt(ctx, fmt.Errorf("error storing L2 block %d [%d], error: %v", l2Block.batchResponse.BlockResponses[0].BlockNumber, l2Block.trackingNum, err)) + f.Halt(ctx, fmt.Errorf("error storing L2 block %d [%d], error: %v", l2Block.batchResponse.BlockResponses[0].BlockNumber, l2Block.trackingNum, err), true) } f.pendingL2BlocksToStoreWG.Done() @@ -159,13 +160,13 @@ func (f *finalizer) storePendingL2Blocks(ctx context.Context) { func (f *finalizer) processL2Block(ctx context.Context, l2Block *L2Block) error { startProcessing := time.Now() - l2Block.initialStateRoot = f.wipBatch.finalStateRoot + initialStateRoot := f.wipBatch.finalStateRoot log.Infof("processing L2 block [%d], batch: %d, deltaTimestamp: %d, timestamp: %d, l1InfoTreeIndex: %d, l1InfoTreeIndexChanged: %v, initialStateRoot: %s txs: %d", l2Block.trackingNum, f.wipBatch.batchNumber, l2Block.deltaTimestamp, l2Block.timestamp, l2Block.l1InfoTreeExitRoot.L1InfoTreeIndex, - l2Block.l1InfoTreeExitRootChanged, l2Block.initialStateRoot, len(l2Block.transactions)) + l2Block.l1InfoTreeExitRootChanged, initialStateRoot, len(l2Block.transactions)) - batchResponse, batchL2DataSize, err := f.executeL2Block(ctx, l2Block) + batchResponse, batchL2DataSize, err := f.executeL2Block(ctx, initialStateRoot, l2Block) if err != nil { return fmt.Errorf("failed to execute L2 block [%d], error: %v", l2Block.trackingNum, err) @@ -179,21 +180,26 @@ func (f *finalizer) processL2Block(ctx context.Context, l2Block *L2Block) error // Sanity check. Check blockResponse.TransactionsReponses match l2Block.Transactions length, order and tx hashes if len(blockResponse.TransactionResponses) != len(l2Block.transactions) { - return fmt.Errorf("length of TransactionsResponses %d don't match length of l2Block.transactions %d", len(blockResponse.TransactionResponses), len(l2Block.transactions)) + return fmt.Errorf("length of TransactionsResponses %d doesn't match length of l2Block.transactions %d", len(blockResponse.TransactionResponses), len(l2Block.transactions)) } for i, txResponse := range blockResponse.TransactionResponses { if txResponse.TxHash != l2Block.transactions[i].Hash { - return fmt.Errorf("blockResponse.TransactionsResponses[%d] hash %s don't match l2Block.transactions[%d] hash %s", i, txResponse.TxHash.String(), i, l2Block.transactions[i].Hash) + return fmt.Errorf("blockResponse.TransactionsResponses[%d] hash %s doesn't match l2Block.transactions[%d] hash %s", i, txResponse.TxHash.String(), i, l2Block.transactions[i].Hash) } } + // Sanity check. Check blockResponse.timestamp matches l2block.timestamp + if blockResponse.Timestamp != l2Block.timestamp { + return fmt.Errorf("blockResponse.Timestamp %d doesn't match l2Block.timestamp %d", blockResponse.Timestamp, l2Block.timestamp) + } + l2Block.batchResponse = batchResponse // Update finalRemainingResources of the batch overflow, overflowResource := f.wipBatch.finalRemainingResources.Sub(state.BatchResources{ZKCounters: batchResponse.UsedZkCounters, Bytes: batchL2DataSize}) if overflow { return fmt.Errorf("error sustracting L2 block %d [%d] resources from the batch %d, overflow resource: %s, batch remaining counters: %s, L2Block used counters: %s, batch remaining bytes: %d, L2Block used bytes: %d", - blockResponse.BlockNumber, l2Block.trackingNum, f.wipBatch.batchNumber, overflowResource, f.logZKCounters(f.wipBatch.imRemainingResources.ZKCounters), f.logZKCounters(batchResponse.UsedZkCounters), f.wipBatch.imRemainingResources.Bytes, batchL2DataSize) + blockResponse.BlockNumber, l2Block.trackingNum, f.wipBatch.batchNumber, overflowResource, f.logZKCounters(f.wipBatch.finalRemainingResources.ZKCounters), f.logZKCounters(batchResponse.UsedZkCounters), f.wipBatch.finalRemainingResources.Bytes, batchL2DataSize) } // Update finalStateRoot of the batch to the newStateRoot for the L2 block @@ -205,17 +211,17 @@ func (f *finalizer) processL2Block(ctx context.Context, l2Block *L2Block) error endProcessing := time.Now() - log.Infof("processed L2 block %d [%d], batch: %d, deltaTimestamp: %d, timestamp: %d, l1InfoTreeIndex: %d, l1InfoTreeIndexChanged: %v, initialStateRoot: %s, stateRoot: %s, txs: %d/%d, blockHash: %s, infoRoot: %s, time: %v, used counters: %s", - blockResponse.BlockNumber, l2Block.trackingNum, f.wipBatch.batchNumber, l2Block.deltaTimestamp, l2Block.timestamp, l2Block.l1InfoTreeExitRoot.L1InfoTreeIndex, l2Block.l1InfoTreeExitRootChanged, l2Block.initialStateRoot, + log.Infof("processed L2 block %d [%d], batch: %d, deltaTimestamp: %d, timestamp: %d, l1InfoTreeIndex: %d, l1InfoTreeIndexChanged: %v, initialStateRoot: %s, newStateRoot: %s, txs: %d/%d, blockHash: %s, infoRoot: %s, time: %v, used counters: %s", + blockResponse.BlockNumber, l2Block.trackingNum, f.wipBatch.batchNumber, l2Block.deltaTimestamp, l2Block.timestamp, l2Block.l1InfoTreeExitRoot.L1InfoTreeIndex, l2Block.l1InfoTreeExitRootChanged, initialStateRoot, l2Block.batchResponse.NewStateRoot, len(l2Block.transactions), len(blockResponse.TransactionResponses), blockResponse.BlockHash, blockResponse.BlockInfoRoot, endProcessing.Sub(startProcessing), f.logZKCounters(batchResponse.UsedZkCounters)) return nil } // executeL2Block executes a L2 Block in the executor and returns the batch response from the executor and the batchL2Data size -func (f *finalizer) executeL2Block(ctx context.Context, l2Block *L2Block) (*state.ProcessBatchResponse, uint64, error) { +func (f *finalizer) executeL2Block(ctx context.Context, initialStateRoot common.Hash, l2Block *L2Block) (*state.ProcessBatchResponse, uint64, error) { executeL2BLockError := func(err error) { - log.Errorf("execute L2 block [%d] error %v, batch: %d, initialStateRoot: %s", l2Block.trackingNum, err, f.wipBatch.batchNumber, l2Block.initialStateRoot.String()) + log.Errorf("execute L2 block [%d] error %v, batch: %d, initialStateRoot: %s", l2Block.trackingNum, err, f.wipBatch.batchNumber, initialStateRoot) // Log batch detailed info for i, tx := range l2Block.transactions { log.Infof("batch: %d, block: [%d], tx position: %d, tx hash: %s", f.wipBatch.batchNumber, l2Block.trackingNum, i, tx.HashStr) @@ -243,7 +249,7 @@ func (f *finalizer) executeL2Block(ctx context.Context, l2Block *L2Block) (*stat batchRequest := state.ProcessRequest{ BatchNumber: f.wipBatch.batchNumber, - OldStateRoot: l2Block.initialStateRoot, + OldStateRoot: initialStateRoot, Coinbase: f.wipBatch.coinbase, L1InfoRoot_V2: mockL1InfoRoot, TimestampLimit_V2: l2Block.timestamp, @@ -431,7 +437,9 @@ func (f *finalizer) closeWIPL2Block(ctx context.Context) { if f.cfg.SequentialProcessL2Block { err := f.processL2Block(ctx, f.wipL2Block) if err != nil { - f.Halt(ctx, fmt.Errorf("error processing L2 block [%d], error: %v", f.wipL2Block.trackingNum, err)) + // Dump L2Block info + f.dumpL2Block(f.wipL2Block) + f.Halt(ctx, fmt.Errorf("error processing L2 block [%d], error: %v", f.wipL2Block.trackingNum, err), false) } // We update imStateRoot (used in tx-by-tx execution) to the finalStateRoot that has been updated after process the WIP L2 Block f.wipBatch.imStateRoot = f.wipBatch.finalStateRoot @@ -482,11 +490,11 @@ func (f *finalizer) openNewWIPL2Block(ctx context.Context, prevTimestamp uint64, // We process (execute) the new wip L2 block to update the imStateRoot and also get the counters used by the wip l2block batchResponse, err := f.executeNewWIPL2Block(ctx) if err != nil { - f.Halt(ctx, fmt.Errorf("failed to execute new WIP L2 block [%d], error: %v ", f.wipL2Block.trackingNum, err)) + f.Halt(ctx, fmt.Errorf("failed to execute new WIP L2 block [%d], error: %v ", f.wipL2Block.trackingNum, err), false) } if len(batchResponse.BlockResponses) != 1 { - f.Halt(ctx, fmt.Errorf("number of L2 block [%d] responses returned by the executor is %d and must be 1", f.wipL2Block.trackingNum, len(batchResponse.BlockResponses))) + f.Halt(ctx, fmt.Errorf("number of L2 block [%d] responses returned by the executor is %d and must be 1", f.wipL2Block.trackingNum, len(batchResponse.BlockResponses)), false) } // Update imStateRoot @@ -506,7 +514,7 @@ func (f *finalizer) openNewWIPL2Block(ctx context.Context, prevTimestamp uint64, f.wipL2Block.trackingNum, f.wipBatch.batchNumber, overflowResource) err := f.closeAndOpenNewWIPBatch(ctx, state.ResourceExhaustedClosingReason) if err != nil { - f.Halt(ctx, fmt.Errorf("failed to create new WIP batch [%d], error: %v", f.wipL2Block.trackingNum, err)) + f.Halt(ctx, fmt.Errorf("failed to create new WIP batch [%d], error: %v", f.wipL2Block.trackingNum, err), true) } } diff --git a/sequencer/mock_etherman.go b/sequencer/mock_etherman.go index 57034054ba..6a2229fcfa 100644 --- a/sequencer/mock_etherman.go +++ b/sequencer/mock_etherman.go @@ -4,15 +4,10 @@ package sequencer import ( context "context" - big "math/big" common "github.com/ethereum/go-ethereum/common" - coretypes "github.com/ethereum/go-ethereum/core/types" - mock "github.com/stretchr/testify/mock" - - types "github.com/0xPolygonHermez/zkevm-node/etherman/types" ) // EthermanMock is an autogenerated mock type for the etherman type @@ -20,75 +15,6 @@ type EthermanMock struct { mock.Mock } -// BuildSequenceBatchesTxData provides a mock function with given fields: sender, sequences, l2CoinBase -func (_m *EthermanMock) BuildSequenceBatchesTxData(sender common.Address, sequences []types.Sequence, l2CoinBase common.Address) (*common.Address, []byte, error) { - ret := _m.Called(sender, sequences, l2CoinBase) - - if len(ret) == 0 { - panic("no return value specified for BuildSequenceBatchesTxData") - } - - var r0 *common.Address - var r1 []byte - var r2 error - if rf, ok := ret.Get(0).(func(common.Address, []types.Sequence, common.Address) (*common.Address, []byte, error)); ok { - return rf(sender, sequences, l2CoinBase) - } - if rf, ok := ret.Get(0).(func(common.Address, []types.Sequence, common.Address) *common.Address); ok { - r0 = rf(sender, sequences, l2CoinBase) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*common.Address) - } - } - - if rf, ok := ret.Get(1).(func(common.Address, []types.Sequence, common.Address) []byte); ok { - r1 = rf(sender, sequences, l2CoinBase) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).([]byte) - } - } - - if rf, ok := ret.Get(2).(func(common.Address, []types.Sequence, common.Address) error); ok { - r2 = rf(sender, sequences, l2CoinBase) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// EstimateGasSequenceBatches provides a mock function with given fields: sender, sequences, l2CoinBase -func (_m *EthermanMock) EstimateGasSequenceBatches(sender common.Address, sequences []types.Sequence, l2CoinBase common.Address) (*coretypes.Transaction, error) { - ret := _m.Called(sender, sequences, l2CoinBase) - - if len(ret) == 0 { - panic("no return value specified for EstimateGasSequenceBatches") - } - - var r0 *coretypes.Transaction - var r1 error - if rf, ok := ret.Get(0).(func(common.Address, []types.Sequence, common.Address) (*coretypes.Transaction, error)); ok { - return rf(sender, sequences, l2CoinBase) - } - if rf, ok := ret.Get(0).(func(common.Address, []types.Sequence, common.Address) *coretypes.Transaction); ok { - r0 = rf(sender, sequences, l2CoinBase) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Transaction) - } - } - - if rf, ok := ret.Get(1).(func(common.Address, []types.Sequence, common.Address) error); ok { - r1 = rf(sender, sequences, l2CoinBase) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // GetLatestBatchNumber provides a mock function with given fields: func (_m *EthermanMock) GetLatestBatchNumber() (uint64, error) { ret := _m.Called() @@ -145,64 +71,6 @@ func (_m *EthermanMock) GetLatestBlockNumber(ctx context.Context) (uint64, error return r0, r1 } -// GetLatestBlockTimestamp provides a mock function with given fields: ctx -func (_m *EthermanMock) GetLatestBlockTimestamp(ctx context.Context) (uint64, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetLatestBlockTimestamp") - } - - var r0 uint64 - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (uint64, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) uint64); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(uint64) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSendSequenceFee provides a mock function with given fields: numBatches -func (_m *EthermanMock) GetSendSequenceFee(numBatches uint64) (*big.Int, error) { - ret := _m.Called(numBatches) - - if len(ret) == 0 { - panic("no return value specified for GetSendSequenceFee") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(uint64) (*big.Int, error)); ok { - return rf(numBatches) - } - if rf, ok := ret.Get(0).(func(uint64) *big.Int); ok { - r0 = rf(numBatches) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(uint64) error); ok { - r1 = rf(numBatches) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // TrustedSequencer provides a mock function with given fields: func (_m *EthermanMock) TrustedSequencer() (common.Address, error) { ret := _m.Called() diff --git a/sequencer/mock_pool.go b/sequencer/mock_pool.go index 705e5a9b95..5882c83557 100644 --- a/sequencer/mock_pool.go +++ b/sequencer/mock_pool.go @@ -93,6 +93,36 @@ func (_m *PoolMock) GetDefaultMinGasPriceAllowed() uint64 { return r0 } +// GetEarliestProcessedTx provides a mock function with given fields: ctx +func (_m *PoolMock) GetEarliestProcessedTx(ctx context.Context) (common.Hash, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetEarliestProcessedTx") + } + + var r0 common.Hash + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (common.Hash, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) common.Hash); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(common.Hash) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetGasPrices provides a mock function with given fields: ctx func (_m *PoolMock) GetGasPrices(ctx context.Context) (pool.GasPrices, error) { ret := _m.Called(ctx) diff --git a/sequencer/mock_state.go b/sequencer/mock_state.go index 0e0a1aaf85..2a53e28c25 100644 --- a/sequencer/mock_state.go +++ b/sequencer/mock_state.go @@ -8,17 +8,11 @@ import ( common "github.com/ethereum/go-ethereum/common" - executor "github.com/0xPolygonHermez/zkevm-node/state/runtime/executor" - mock "github.com/stretchr/testify/mock" pgx "github.com/jackc/pgx/v4" state "github.com/0xPolygonHermez/zkevm-node/state" - - time "time" - - types "github.com/ethereum/go-ethereum/core/types" ) // StateMock is an autogenerated mock type for the stateInterface type @@ -26,54 +20,6 @@ type StateMock struct { mock.Mock } -// AddL2Block provides a mock function with given fields: ctx, batchNumber, l2Block, receipts, txsL2Hash, txsEGPData, dbTx -func (_m *StateMock) AddL2Block(ctx context.Context, batchNumber uint64, l2Block *state.L2Block, receipts []*types.Receipt, txsL2Hash []common.Hash, txsEGPData []state.StoreTxEGPData, dbTx pgx.Tx) error { - ret := _m.Called(ctx, batchNumber, l2Block, receipts, txsL2Hash, txsEGPData, dbTx) - - if len(ret) == 0 { - panic("no return value specified for AddL2Block") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, *state.L2Block, []*types.Receipt, []common.Hash, []state.StoreTxEGPData, pgx.Tx) error); ok { - r0 = rf(ctx, batchNumber, l2Block, receipts, txsL2Hash, txsEGPData, dbTx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Begin provides a mock function with given fields: ctx -func (_m *StateMock) Begin(ctx context.Context) (pgx.Tx, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for Begin") - } - - var r0 pgx.Tx - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (pgx.Tx, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) pgx.Tx); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(pgx.Tx) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // BeginStateTransaction provides a mock function with given fields: ctx func (_m *StateMock) BeginStateTransaction(ctx context.Context) (pgx.Tx, error) { ret := _m.Called(ctx) @@ -188,84 +134,6 @@ func (_m *StateMock) CountReorgs(ctx context.Context, dbTx pgx.Tx) (uint64, erro return r0, r1 } -// ExecuteBatch provides a mock function with given fields: ctx, batch, updateMerkleTree, dbTx -func (_m *StateMock) ExecuteBatch(ctx context.Context, batch state.Batch, updateMerkleTree bool, dbTx pgx.Tx) (*executor.ProcessBatchResponse, error) { - ret := _m.Called(ctx, batch, updateMerkleTree, dbTx) - - if len(ret) == 0 { - panic("no return value specified for ExecuteBatch") - } - - var r0 *executor.ProcessBatchResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, state.Batch, bool, pgx.Tx) (*executor.ProcessBatchResponse, error)); ok { - return rf(ctx, batch, updateMerkleTree, dbTx) - } - if rf, ok := ret.Get(0).(func(context.Context, state.Batch, bool, pgx.Tx) *executor.ProcessBatchResponse); ok { - r0 = rf(ctx, batch, updateMerkleTree, dbTx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*executor.ProcessBatchResponse) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, state.Batch, bool, pgx.Tx) error); ok { - r1 = rf(ctx, batch, updateMerkleTree, dbTx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ExecuteBatchV2 provides a mock function with given fields: ctx, batch, L1InfoTreeRoot, l1InfoTreeData, timestampLimit, updateMerkleTree, skipVerifyL1InfoRoot, forcedBlockHashL1, dbTx -func (_m *StateMock) ExecuteBatchV2(ctx context.Context, batch state.Batch, L1InfoTreeRoot common.Hash, l1InfoTreeData map[uint32]state.L1DataV2, timestampLimit time.Time, updateMerkleTree bool, skipVerifyL1InfoRoot uint32, forcedBlockHashL1 *common.Hash, dbTx pgx.Tx) (*executor.ProcessBatchResponseV2, error) { - ret := _m.Called(ctx, batch, L1InfoTreeRoot, l1InfoTreeData, timestampLimit, updateMerkleTree, skipVerifyL1InfoRoot, forcedBlockHashL1, dbTx) - - if len(ret) == 0 { - panic("no return value specified for ExecuteBatchV2") - } - - var r0 *executor.ProcessBatchResponseV2 - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, state.Batch, common.Hash, map[uint32]state.L1DataV2, time.Time, bool, uint32, *common.Hash, pgx.Tx) (*executor.ProcessBatchResponseV2, error)); ok { - return rf(ctx, batch, L1InfoTreeRoot, l1InfoTreeData, timestampLimit, updateMerkleTree, skipVerifyL1InfoRoot, forcedBlockHashL1, dbTx) - } - if rf, ok := ret.Get(0).(func(context.Context, state.Batch, common.Hash, map[uint32]state.L1DataV2, time.Time, bool, uint32, *common.Hash, pgx.Tx) *executor.ProcessBatchResponseV2); ok { - r0 = rf(ctx, batch, L1InfoTreeRoot, l1InfoTreeData, timestampLimit, updateMerkleTree, skipVerifyL1InfoRoot, forcedBlockHashL1, dbTx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*executor.ProcessBatchResponseV2) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, state.Batch, common.Hash, map[uint32]state.L1DataV2, time.Time, bool, uint32, *common.Hash, pgx.Tx) error); ok { - r1 = rf(ctx, batch, L1InfoTreeRoot, l1InfoTreeData, timestampLimit, updateMerkleTree, skipVerifyL1InfoRoot, forcedBlockHashL1, dbTx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// FlushMerkleTree provides a mock function with given fields: ctx, newStateRoot -func (_m *StateMock) FlushMerkleTree(ctx context.Context, newStateRoot common.Hash) error { - ret := _m.Called(ctx, newStateRoot) - - if len(ret) == 0 { - panic("no return value specified for FlushMerkleTree") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) error); ok { - r0 = rf(ctx, newStateRoot) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // GetBalanceByStateRoot provides a mock function with given fields: ctx, address, root func (_m *StateMock) GetBalanceByStateRoot(ctx context.Context, address common.Address, root common.Hash) (*big.Int, error) { ret := _m.Called(ctx, address, root) @@ -660,36 +528,6 @@ func (_m *StateMock) GetL1InfoTreeDataFromBatchL2Data(ctx context.Context, batch return r0, r1, r2, r3 } -// GetLastBatch provides a mock function with given fields: ctx, dbTx -func (_m *StateMock) GetLastBatch(ctx context.Context, dbTx pgx.Tx) (*state.Batch, error) { - ret := _m.Called(ctx, dbTx) - - if len(ret) == 0 { - panic("no return value specified for GetLastBatch") - } - - var r0 *state.Batch - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) (*state.Batch, error)); ok { - return rf(ctx, dbTx) - } - if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) *state.Batch); ok { - r0 = rf(ctx, dbTx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*state.Batch) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, pgx.Tx) error); ok { - r1 = rf(ctx, dbTx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // GetLastBatchNumber provides a mock function with given fields: ctx, dbTx func (_m *StateMock) GetLastBatchNumber(ctx context.Context, dbTx pgx.Tx) (uint64, error) { ret := _m.Called(ctx, dbTx) @@ -748,36 +586,6 @@ func (_m *StateMock) GetLastBlock(ctx context.Context, dbTx pgx.Tx) (*state.Bloc return r0, r1 } -// GetLastClosedBatch provides a mock function with given fields: ctx, dbTx -func (_m *StateMock) GetLastClosedBatch(ctx context.Context, dbTx pgx.Tx) (*state.Batch, error) { - ret := _m.Called(ctx, dbTx) - - if len(ret) == 0 { - panic("no return value specified for GetLastClosedBatch") - } - - var r0 *state.Batch - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) (*state.Batch, error)); ok { - return rf(ctx, dbTx) - } - if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) *state.Batch); ok { - r0 = rf(ctx, dbTx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*state.Batch) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, pgx.Tx) error); ok { - r1 = rf(ctx, dbTx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // GetLastL2Block provides a mock function with given fields: ctx, dbTx func (_m *StateMock) GetLastL2Block(ctx context.Context, dbTx pgx.Tx) (*state.L2Block, error) { ret := _m.Called(ctx, dbTx) @@ -808,66 +616,6 @@ func (_m *StateMock) GetLastL2Block(ctx context.Context, dbTx pgx.Tx) (*state.L2 return r0, r1 } -// GetLastL2BlockHeader provides a mock function with given fields: ctx, dbTx -func (_m *StateMock) GetLastL2BlockHeader(ctx context.Context, dbTx pgx.Tx) (*state.L2Header, error) { - ret := _m.Called(ctx, dbTx) - - if len(ret) == 0 { - panic("no return value specified for GetLastL2BlockHeader") - } - - var r0 *state.L2Header - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) (*state.L2Header, error)); ok { - return rf(ctx, dbTx) - } - if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) *state.L2Header); ok { - r0 = rf(ctx, dbTx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*state.L2Header) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, pgx.Tx) error); ok { - r1 = rf(ctx, dbTx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetLastNBatches provides a mock function with given fields: ctx, numBatches, dbTx -func (_m *StateMock) GetLastNBatches(ctx context.Context, numBatches uint, dbTx pgx.Tx) ([]*state.Batch, error) { - ret := _m.Called(ctx, numBatches, dbTx) - - if len(ret) == 0 { - panic("no return value specified for GetLastNBatches") - } - - var r0 []*state.Batch - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint, pgx.Tx) ([]*state.Batch, error)); ok { - return rf(ctx, numBatches, dbTx) - } - if rf, ok := ret.Get(0).(func(context.Context, uint, pgx.Tx) []*state.Batch); ok { - r0 = rf(ctx, numBatches, dbTx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*state.Batch) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint, pgx.Tx) error); ok { - r1 = rf(ctx, numBatches, dbTx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // GetLastStateRoot provides a mock function with given fields: ctx, dbTx func (_m *StateMock) GetLastStateRoot(ctx context.Context, dbTx pgx.Tx) (common.Hash, error) { ret := _m.Called(ctx, dbTx) @@ -984,76 +732,6 @@ func (_m *StateMock) GetLatestBatchGlobalExitRoot(ctx context.Context, dbTx pgx. return r0, r1 } -// GetLatestGer provides a mock function with given fields: ctx, maxBlockNumber -func (_m *StateMock) GetLatestGer(ctx context.Context, maxBlockNumber uint64) (state.GlobalExitRoot, time.Time, error) { - ret := _m.Called(ctx, maxBlockNumber) - - if len(ret) == 0 { - panic("no return value specified for GetLatestGer") - } - - var r0 state.GlobalExitRoot - var r1 time.Time - var r2 error - if rf, ok := ret.Get(0).(func(context.Context, uint64) (state.GlobalExitRoot, time.Time, error)); ok { - return rf(ctx, maxBlockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, uint64) state.GlobalExitRoot); ok { - r0 = rf(ctx, maxBlockNumber) - } else { - r0 = ret.Get(0).(state.GlobalExitRoot) - } - - if rf, ok := ret.Get(1).(func(context.Context, uint64) time.Time); ok { - r1 = rf(ctx, maxBlockNumber) - } else { - r1 = ret.Get(1).(time.Time) - } - - if rf, ok := ret.Get(2).(func(context.Context, uint64) error); ok { - r2 = rf(ctx, maxBlockNumber) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// GetLatestGlobalExitRoot provides a mock function with given fields: ctx, maxBlockNumber, dbTx -func (_m *StateMock) GetLatestGlobalExitRoot(ctx context.Context, maxBlockNumber uint64, dbTx pgx.Tx) (state.GlobalExitRoot, time.Time, error) { - ret := _m.Called(ctx, maxBlockNumber, dbTx) - - if len(ret) == 0 { - panic("no return value specified for GetLatestGlobalExitRoot") - } - - var r0 state.GlobalExitRoot - var r1 time.Time - var r2 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) (state.GlobalExitRoot, time.Time, error)); ok { - return rf(ctx, maxBlockNumber, dbTx) - } - if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) state.GlobalExitRoot); ok { - r0 = rf(ctx, maxBlockNumber, dbTx) - } else { - r0 = ret.Get(0).(state.GlobalExitRoot) - } - - if rf, ok := ret.Get(1).(func(context.Context, uint64, pgx.Tx) time.Time); ok { - r1 = rf(ctx, maxBlockNumber, dbTx) - } else { - r1 = ret.Get(1).(time.Time) - } - - if rf, ok := ret.Get(2).(func(context.Context, uint64, pgx.Tx) error); ok { - r2 = rf(ctx, maxBlockNumber, dbTx) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - // GetLatestL1InfoRoot provides a mock function with given fields: ctx, maxBlockNumber func (_m *StateMock) GetLatestL1InfoRoot(ctx context.Context, maxBlockNumber uint64) (state.L1InfoTreeExitRootStorageEntry, error) { ret := _m.Called(ctx, maxBlockNumber) @@ -1082,27 +760,29 @@ func (_m *StateMock) GetLatestL1InfoRoot(ctx context.Context, maxBlockNumber uin return r0, r1 } -// GetLatestVirtualBatchTimestamp provides a mock function with given fields: ctx, dbTx -func (_m *StateMock) GetLatestVirtualBatchTimestamp(ctx context.Context, dbTx pgx.Tx) (time.Time, error) { - ret := _m.Called(ctx, dbTx) +// GetNonceByStateRoot provides a mock function with given fields: ctx, address, root +func (_m *StateMock) GetNonceByStateRoot(ctx context.Context, address common.Address, root common.Hash) (*big.Int, error) { + ret := _m.Called(ctx, address, root) if len(ret) == 0 { - panic("no return value specified for GetLatestVirtualBatchTimestamp") + panic("no return value specified for GetNonceByStateRoot") } - var r0 time.Time + var r0 *big.Int var r1 error - if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) (time.Time, error)); ok { - return rf(ctx, dbTx) + if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Hash) (*big.Int, error)); ok { + return rf(ctx, address, root) } - if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) time.Time); ok { - r0 = rf(ctx, dbTx) + if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Hash) *big.Int); ok { + r0 = rf(ctx, address, root) } else { - r0 = ret.Get(0).(time.Time) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } } - if rf, ok := ret.Get(1).(func(context.Context, pgx.Tx) error); ok { - r1 = rf(ctx, dbTx) + if rf, ok := ret.Get(1).(func(context.Context, common.Address, common.Hash) error); ok { + r1 = rf(ctx, address, root) } else { r1 = ret.Error(1) } @@ -1110,29 +790,29 @@ func (_m *StateMock) GetLatestVirtualBatchTimestamp(ctx context.Context, dbTx pg return r0, r1 } -// GetNonceByStateRoot provides a mock function with given fields: ctx, address, root -func (_m *StateMock) GetNonceByStateRoot(ctx context.Context, address common.Address, root common.Hash) (*big.Int, error) { - ret := _m.Called(ctx, address, root) +// GetNotCheckedBatches provides a mock function with given fields: ctx, dbTx +func (_m *StateMock) GetNotCheckedBatches(ctx context.Context, dbTx pgx.Tx) ([]*state.Batch, error) { + ret := _m.Called(ctx, dbTx) if len(ret) == 0 { - panic("no return value specified for GetNonceByStateRoot") + panic("no return value specified for GetNotCheckedBatches") } - var r0 *big.Int + var r0 []*state.Batch var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Hash) (*big.Int, error)); ok { - return rf(ctx, address, root) + if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) ([]*state.Batch, error)); ok { + return rf(ctx, dbTx) } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Hash) *big.Int); ok { - r0 = rf(ctx, address, root) + if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) []*state.Batch); ok { + r0 = rf(ctx, dbTx) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) + r0 = ret.Get(0).([]*state.Batch) } } - if rf, ok := ret.Get(1).(func(context.Context, common.Address, common.Hash) error); ok { - r1 = rf(ctx, address, root) + if rf, ok := ret.Get(1).(func(context.Context, pgx.Tx) error); ok { + r1 = rf(ctx, dbTx) } else { r1 = ret.Error(1) } @@ -1205,126 +885,29 @@ func (_m *StateMock) GetStoredFlushID(ctx context.Context) (uint64, string, erro return r0, r1, r2 } -// GetTimeForLatestBatchVirtualization provides a mock function with given fields: ctx, dbTx -func (_m *StateMock) GetTimeForLatestBatchVirtualization(ctx context.Context, dbTx pgx.Tx) (time.Time, error) { - ret := _m.Called(ctx, dbTx) - - if len(ret) == 0 { - panic("no return value specified for GetTimeForLatestBatchVirtualization") - } - - var r0 time.Time - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) (time.Time, error)); ok { - return rf(ctx, dbTx) - } - if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) time.Time); ok { - r0 = rf(ctx, dbTx) - } else { - r0 = ret.Get(0).(time.Time) - } - - if rf, ok := ret.Get(1).(func(context.Context, pgx.Tx) error); ok { - r1 = rf(ctx, dbTx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetTransactionsByBatchNumber provides a mock function with given fields: ctx, batchNumber, dbTx -func (_m *StateMock) GetTransactionsByBatchNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) ([]types.Transaction, []uint8, error) { - ret := _m.Called(ctx, batchNumber, dbTx) - - if len(ret) == 0 { - panic("no return value specified for GetTransactionsByBatchNumber") - } - - var r0 []types.Transaction - var r1 []uint8 - var r2 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) ([]types.Transaction, []uint8, error)); ok { - return rf(ctx, batchNumber, dbTx) - } - if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) []types.Transaction); ok { - r0 = rf(ctx, batchNumber, dbTx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]types.Transaction) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint64, pgx.Tx) []uint8); ok { - r1 = rf(ctx, batchNumber, dbTx) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).([]uint8) - } - } - - if rf, ok := ret.Get(2).(func(context.Context, uint64, pgx.Tx) error); ok { - r2 = rf(ctx, batchNumber, dbTx) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// GetTxsOlderThanNL1Blocks provides a mock function with given fields: ctx, nL1Blocks, dbTx -func (_m *StateMock) GetTxsOlderThanNL1Blocks(ctx context.Context, nL1Blocks uint64, dbTx pgx.Tx) ([]common.Hash, error) { - ret := _m.Called(ctx, nL1Blocks, dbTx) +// GetTxsOlderThanNL1BlocksUntilTxHash provides a mock function with given fields: ctx, nL1Blocks, earliestTxHash, dbTx +func (_m *StateMock) GetTxsOlderThanNL1BlocksUntilTxHash(ctx context.Context, nL1Blocks uint64, earliestTxHash common.Hash, dbTx pgx.Tx) ([]common.Hash, error) { + ret := _m.Called(ctx, nL1Blocks, earliestTxHash, dbTx) if len(ret) == 0 { - panic("no return value specified for GetTxsOlderThanNL1Blocks") + panic("no return value specified for GetTxsOlderThanNL1BlocksUntilTxHash") } var r0 []common.Hash var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) ([]common.Hash, error)); ok { - return rf(ctx, nL1Blocks, dbTx) + if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Hash, pgx.Tx) ([]common.Hash, error)); ok { + return rf(ctx, nL1Blocks, earliestTxHash, dbTx) } - if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) []common.Hash); ok { - r0 = rf(ctx, nL1Blocks, dbTx) + if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Hash, pgx.Tx) []common.Hash); ok { + r0 = rf(ctx, nL1Blocks, earliestTxHash, dbTx) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]common.Hash) } } - if rf, ok := ret.Get(1).(func(context.Context, uint64, pgx.Tx) error); ok { - r1 = rf(ctx, nL1Blocks, dbTx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetVirtualBatch provides a mock function with given fields: ctx, batchNumber, dbTx -func (_m *StateMock) GetVirtualBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.VirtualBatch, error) { - ret := _m.Called(ctx, batchNumber, dbTx) - - if len(ret) == 0 { - panic("no return value specified for GetVirtualBatch") - } - - var r0 *state.VirtualBatch - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) (*state.VirtualBatch, error)); ok { - return rf(ctx, batchNumber, dbTx) - } - if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) *state.VirtualBatch); ok { - r0 = rf(ctx, batchNumber, dbTx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*state.VirtualBatch) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint64, pgx.Tx) error); ok { - r1 = rf(ctx, batchNumber, dbTx) + if rf, ok := ret.Get(1).(func(context.Context, uint64, common.Hash, pgx.Tx) error); ok { + r1 = rf(ctx, nL1Blocks, earliestTxHash, dbTx) } else { r1 = ret.Error(1) } @@ -1362,64 +945,6 @@ func (_m *StateMock) GetVirtualBatchParentHash(ctx context.Context, batchNumber return r0, r1 } -// GetWIPBatch provides a mock function with given fields: ctx, batchNumber, dbTx -func (_m *StateMock) GetWIPBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.Batch, error) { - ret := _m.Called(ctx, batchNumber, dbTx) - - if len(ret) == 0 { - panic("no return value specified for GetWIPBatch") - } - - var r0 *state.Batch - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) (*state.Batch, error)); ok { - return rf(ctx, batchNumber, dbTx) - } - if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) *state.Batch); ok { - r0 = rf(ctx, batchNumber, dbTx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*state.Batch) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint64, pgx.Tx) error); ok { - r1 = rf(ctx, batchNumber, dbTx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IsBatchClosed provides a mock function with given fields: ctx, batchNum, dbTx -func (_m *StateMock) IsBatchClosed(ctx context.Context, batchNum uint64, dbTx pgx.Tx) (bool, error) { - ret := _m.Called(ctx, batchNum, dbTx) - - if len(ret) == 0 { - panic("no return value specified for IsBatchClosed") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) (bool, error)); ok { - return rf(ctx, batchNum, dbTx) - } - if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) bool); ok { - r0 = rf(ctx, batchNum, dbTx) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context, uint64, pgx.Tx) error); ok { - r1 = rf(ctx, batchNum, dbTx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // OpenBatch provides a mock function with given fields: ctx, processingContext, dbTx func (_m *StateMock) OpenBatch(ctx context.Context, processingContext state.ProcessingContext, dbTx pgx.Tx) error { ret := _m.Called(ctx, processingContext, dbTx) @@ -1456,36 +981,6 @@ func (_m *StateMock) OpenWIPBatch(ctx context.Context, batch state.Batch, dbTx p return r0 } -// ProcessBatch provides a mock function with given fields: ctx, request, updateMerkleTree -func (_m *StateMock) ProcessBatch(ctx context.Context, request state.ProcessRequest, updateMerkleTree bool) (*state.ProcessBatchResponse, error) { - ret := _m.Called(ctx, request, updateMerkleTree) - - if len(ret) == 0 { - panic("no return value specified for ProcessBatch") - } - - var r0 *state.ProcessBatchResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, state.ProcessRequest, bool) (*state.ProcessBatchResponse, error)); ok { - return rf(ctx, request, updateMerkleTree) - } - if rf, ok := ret.Get(0).(func(context.Context, state.ProcessRequest, bool) *state.ProcessBatchResponse); ok { - r0 = rf(ctx, request, updateMerkleTree) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*state.ProcessBatchResponse) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, state.ProcessRequest, bool) error); ok { - r1 = rf(ctx, request, updateMerkleTree) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // ProcessBatchV2 provides a mock function with given fields: ctx, request, updateMerkleTree func (_m *StateMock) ProcessBatchV2(ctx context.Context, request state.ProcessRequest, updateMerkleTree bool) (*state.ProcessBatchResponse, error) { ret := _m.Called(ctx, request, updateMerkleTree) @@ -1534,34 +1029,22 @@ func (_m *StateMock) StoreL2Block(ctx context.Context, batchNumber uint64, l2Blo return r0 } -// StoreTransaction provides a mock function with given fields: ctx, batchNumber, processedTx, coinbase, timestamp, egpLog, globalExitRoot, blockInfoRoot, dbTx -func (_m *StateMock) StoreTransaction(ctx context.Context, batchNumber uint64, processedTx *state.ProcessTransactionResponse, coinbase common.Address, timestamp uint64, egpLog *state.EffectiveGasPriceLog, globalExitRoot common.Hash, blockInfoRoot common.Hash, dbTx pgx.Tx) (*state.L2Header, error) { - ret := _m.Called(ctx, batchNumber, processedTx, coinbase, timestamp, egpLog, globalExitRoot, blockInfoRoot, dbTx) +// UpdateBatchAsChecked provides a mock function with given fields: ctx, batchNumber, dbTx +func (_m *StateMock) UpdateBatchAsChecked(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error { + ret := _m.Called(ctx, batchNumber, dbTx) if len(ret) == 0 { - panic("no return value specified for StoreTransaction") + panic("no return value specified for UpdateBatchAsChecked") } - var r0 *state.L2Header - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, *state.ProcessTransactionResponse, common.Address, uint64, *state.EffectiveGasPriceLog, common.Hash, common.Hash, pgx.Tx) (*state.L2Header, error)); ok { - return rf(ctx, batchNumber, processedTx, coinbase, timestamp, egpLog, globalExitRoot, blockInfoRoot, dbTx) - } - if rf, ok := ret.Get(0).(func(context.Context, uint64, *state.ProcessTransactionResponse, common.Address, uint64, *state.EffectiveGasPriceLog, common.Hash, common.Hash, pgx.Tx) *state.L2Header); ok { - r0 = rf(ctx, batchNumber, processedTx, coinbase, timestamp, egpLog, globalExitRoot, blockInfoRoot, dbTx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*state.L2Header) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint64, *state.ProcessTransactionResponse, common.Address, uint64, *state.EffectiveGasPriceLog, common.Hash, common.Hash, pgx.Tx) error); ok { - r1 = rf(ctx, batchNumber, processedTx, coinbase, timestamp, egpLog, globalExitRoot, blockInfoRoot, dbTx) + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) error); ok { + r0 = rf(ctx, batchNumber, dbTx) } else { - r1 = ret.Error(1) + r0 = ret.Error(0) } - return r0, r1 + return r0 } // UpdateWIPBatch provides a mock function with given fields: ctx, receipt, dbTx diff --git a/sequencer/sequencer.go b/sequencer/sequencer.go index 1c2716b67c..5a6ab8671c 100644 --- a/sequencer/sequencer.go +++ b/sequencer/sequencer.go @@ -123,7 +123,7 @@ func (s *Sequencer) checkStateInconsistency(ctx context.Context) { } if stateInconsistenciesDetected != s.numberOfStateInconsistencies { - s.finalizer.Halt(ctx, fmt.Errorf("state inconsistency detected, halting finalizer")) + s.finalizer.Halt(ctx, fmt.Errorf("state inconsistency detected, halting finalizer"), false) } } } @@ -140,7 +140,13 @@ func (s *Sequencer) deleteOldPoolTxs(ctx context.Context) { for { time.Sleep(s.cfg.DeletePoolTxsCheckInterval.Duration) log.Infof("trying to get txs to delete from the pool...") - txHashes, err := s.stateIntf.GetTxsOlderThanNL1Blocks(ctx, s.cfg.DeletePoolTxsL1BlockConfirmations, nil) + earliestTxHash, err := s.pool.GetEarliestProcessedTx(ctx) + if err != nil { + log.Errorf("failed to get earliest tx hash to delete, err: %v", err) + continue + } + + txHashes, err := s.stateIntf.GetTxsOlderThanNL1BlocksUntilTxHash(ctx, s.cfg.DeletePoolTxsL1BlockConfirmations, earliestTxHash, nil) if err != nil { log.Errorf("failed to get txs hashes to delete, error: %v", err) continue diff --git a/sequencesender/interfaces.go b/sequencesender/interfaces.go index d69546012f..3fd69540c4 100644 --- a/sequencesender/interfaces.go +++ b/sequencesender/interfaces.go @@ -28,6 +28,7 @@ type etherman interface { type stateInterface interface { GetLastVirtualBatchNum(ctx context.Context, dbTx pgx.Tx) (uint64, error) IsBatchClosed(ctx context.Context, batchNum uint64, dbTx pgx.Tx) (bool, error) + IsBatchChecked(ctx context.Context, batchNum uint64, dbTx pgx.Tx) (bool, error) GetBatchByNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.Batch, error) GetForcedBatch(ctx context.Context, forcedBatchNumber uint64, dbTx pgx.Tx) (*state.ForcedBatch, error) GetTimeForLatestBatchVirtualization(ctx context.Context, dbTx pgx.Tx) (time.Time, error) diff --git a/sequencesender/mock_state.go b/sequencesender/mock_state.go index b823809846..029e6dfc14 100644 --- a/sequencesender/mock_state.go +++ b/sequencesender/mock_state.go @@ -252,6 +252,34 @@ func (_m *StateMock) GetTimeForLatestBatchVirtualization(ctx context.Context, db return r0, r1 } +// IsBatchChecked provides a mock function with given fields: ctx, batchNum, dbTx +func (_m *StateMock) IsBatchChecked(ctx context.Context, batchNum uint64, dbTx pgx.Tx) (bool, error) { + ret := _m.Called(ctx, batchNum, dbTx) + + if len(ret) == 0 { + panic("no return value specified for IsBatchChecked") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) (bool, error)); ok { + return rf(ctx, batchNum, dbTx) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) bool); ok { + r0 = rf(ctx, batchNum, dbTx) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64, pgx.Tx) error); ok { + r1 = rf(ctx, batchNum, dbTx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // IsBatchClosed provides a mock function with given fields: ctx, batchNum, dbTx func (_m *StateMock) IsBatchClosed(ctx context.Context, batchNum uint64, dbTx pgx.Tx) (bool, error) { ret := _m.Called(ctx, batchNum, dbTx) diff --git a/sequencesender/sequencesender.go b/sequencesender/sequencesender.go index 980d5cbf43..082a522304 100644 --- a/sequencesender/sequencesender.go +++ b/sequencesender/sequencesender.go @@ -224,7 +224,7 @@ func (s *SequenceSender) tryToSendSequence(ctx context.Context) { func (s *SequenceSender) getSequencesToSend(ctx context.Context) ([]types.Sequence, error) { lastVirtualBatchNum, err := s.state.GetLastVirtualBatchNum(ctx, nil) if err != nil { - return nil, fmt.Errorf("failed to get last virtual batch num, err: %w", err) + return nil, fmt.Errorf("failed to get last virtual batch num, err: %v", err) } log.Debugf("last virtual batch number: %d", lastVirtualBatchNum) @@ -250,19 +250,19 @@ func (s *SequenceSender) getSequencesToSend(ctx context.Context) ([]types.Sequen if err == state.ErrNotFound { break } - log.Debugf("failed to get batch by number %d, err: %w", currentBatchNumToSequence, err) + log.Debugf("failed to get batch by number %d, err: %v", currentBatchNumToSequence, err) return nil, err } - // Check if batch is closed - isClosed, err := s.state.IsBatchClosed(ctx, currentBatchNumToSequence, nil) + // Check if batch is closed and checked (sequencer sanity check was successful) + isChecked, err := s.state.IsBatchChecked(ctx, currentBatchNumToSequence, nil) if err != nil { - log.Debugf("failed to check if batch %d is closed, err: %w", currentBatchNumToSequence, err) + log.Debugf("failed to check if batch %d is closed and checked, err: %v", currentBatchNumToSequence, err) return nil, err } - if !isClosed { - // Reached current (WIP) batch + if !isChecked { + // Batch is not closed and checked break } diff --git a/state/datastream.go b/state/datastream.go index 4e8995b137..6bad955516 100644 --- a/state/datastream.go +++ b/state/datastream.go @@ -234,11 +234,9 @@ type DSState interface { GetDSL2Blocks(ctx context.Context, firstBatchNumber, lastBatchNumber uint64, dbTx pgx.Tx) ([]*DSL2Block, error) GetDSL2Transactions(ctx context.Context, firstL2Block, lastL2Block uint64, dbTx pgx.Tx) ([]*DSL2Transaction, error) GetStorageAt(ctx context.Context, address common.Address, position *big.Int, root common.Hash) (*big.Int, error) - GetLastL2BlockHeader(ctx context.Context, dbTx pgx.Tx) (*L2Header, error) GetVirtualBatchParentHash(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (common.Hash, error) GetForcedBatchParentHash(ctx context.Context, forcedBatchNumber uint64, dbTx pgx.Tx) (common.Hash, error) GetL1InfoRootLeafByIndex(ctx context.Context, l1InfoTreeIndex uint32, dbTx pgx.Tx) (L1InfoTreeExitRootStorageEntry, error) - GetVirtualBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*VirtualBatch, error) } // GenerateDataStreamerFile generates or resumes a data stream file diff --git a/state/interfaces.go b/state/interfaces.go index d677d510fe..17636f5ce1 100644 --- a/state/interfaces.go +++ b/state/interfaces.go @@ -21,6 +21,7 @@ type storage interface { ResetTrustedState(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error AddBlock(ctx context.Context, block *Block, dbTx pgx.Tx) error GetTxsOlderThanNL1Blocks(ctx context.Context, nL1Blocks uint64, dbTx pgx.Tx) ([]common.Hash, error) + GetTxsOlderThanNL1BlocksUntilTxHash(ctx context.Context, nL1Blocks uint64, earliestTxHash common.Hash, dbTx pgx.Tx) ([]common.Hash, error) GetLastBlock(ctx context.Context, dbTx pgx.Tx) (*Block, error) GetPreviousBlock(ctx context.Context, offset uint64, dbTx pgx.Tx) (*Block, error) AddGlobalExitRoot(ctx context.Context, exitRoot *GlobalExitRoot, dbTx pgx.Tx) error @@ -153,4 +154,7 @@ type storage interface { GetSyncInfoData(ctx context.Context, dbTx pgx.Tx) (SyncInfoDataOnStorage, error) GetFirstL2BlockNumberForBatchNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (uint64, error) GetForkIDInMemory(forkId uint64) *ForkIDInterval + IsBatchChecked(ctx context.Context, batchNum uint64, dbTx pgx.Tx) (bool, error) + UpdateBatchAsChecked(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error + GetNotCheckedBatches(ctx context.Context, dbTx pgx.Tx) ([]*Batch, error) } diff --git a/state/mocks/mock_storage.go b/state/mocks/mock_storage.go index 4a9ddac5ec..559e73a930 100644 --- a/state/mocks/mock_storage.go +++ b/state/mocks/mock_storage.go @@ -5251,6 +5251,65 @@ func (_c *StorageMock_GetNextForcedBatches_Call) RunAndReturn(run func(context.C return _c } +// GetNotCheckedBatches provides a mock function with given fields: ctx, dbTx +func (_m *StorageMock) GetNotCheckedBatches(ctx context.Context, dbTx pgx.Tx) ([]*state.Batch, error) { + ret := _m.Called(ctx, dbTx) + + if len(ret) == 0 { + panic("no return value specified for GetNotCheckedBatches") + } + + var r0 []*state.Batch + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) ([]*state.Batch, error)); ok { + return rf(ctx, dbTx) + } + if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) []*state.Batch); ok { + r0 = rf(ctx, dbTx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*state.Batch) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, pgx.Tx) error); ok { + r1 = rf(ctx, dbTx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// StorageMock_GetNotCheckedBatches_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetNotCheckedBatches' +type StorageMock_GetNotCheckedBatches_Call struct { + *mock.Call +} + +// GetNotCheckedBatches is a helper method to define mock.On call +// - ctx context.Context +// - dbTx pgx.Tx +func (_e *StorageMock_Expecter) GetNotCheckedBatches(ctx interface{}, dbTx interface{}) *StorageMock_GetNotCheckedBatches_Call { + return &StorageMock_GetNotCheckedBatches_Call{Call: _e.mock.On("GetNotCheckedBatches", ctx, dbTx)} +} + +func (_c *StorageMock_GetNotCheckedBatches_Call) Run(run func(ctx context.Context, dbTx pgx.Tx)) *StorageMock_GetNotCheckedBatches_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(pgx.Tx)) + }) + return _c +} + +func (_c *StorageMock_GetNotCheckedBatches_Call) Return(_a0 []*state.Batch, _a1 error) *StorageMock_GetNotCheckedBatches_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *StorageMock_GetNotCheckedBatches_Call) RunAndReturn(run func(context.Context, pgx.Tx) ([]*state.Batch, error)) *StorageMock_GetNotCheckedBatches_Call { + _c.Call.Return(run) + return _c +} + // GetNumberOfBlocksSinceLastGERUpdate provides a mock function with given fields: ctx, dbTx func (_m *StorageMock) GetNumberOfBlocksSinceLastGERUpdate(ctx context.Context, dbTx pgx.Tx) (uint64, error) { ret := _m.Called(ctx, dbTx) @@ -6590,6 +6649,67 @@ func (_c *StorageMock_GetTxsOlderThanNL1Blocks_Call) RunAndReturn(run func(conte return _c } +// GetTxsOlderThanNL1BlocksUntilTxHash provides a mock function with given fields: ctx, nL1Blocks, earliestTxHash, dbTx +func (_m *StorageMock) GetTxsOlderThanNL1BlocksUntilTxHash(ctx context.Context, nL1Blocks uint64, earliestTxHash common.Hash, dbTx pgx.Tx) ([]common.Hash, error) { + ret := _m.Called(ctx, nL1Blocks, earliestTxHash, dbTx) + + if len(ret) == 0 { + panic("no return value specified for GetTxsOlderThanNL1BlocksUntilTxHash") + } + + var r0 []common.Hash + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Hash, pgx.Tx) ([]common.Hash, error)); ok { + return rf(ctx, nL1Blocks, earliestTxHash, dbTx) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Hash, pgx.Tx) []common.Hash); ok { + r0 = rf(ctx, nL1Blocks, earliestTxHash, dbTx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]common.Hash) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64, common.Hash, pgx.Tx) error); ok { + r1 = rf(ctx, nL1Blocks, earliestTxHash, dbTx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// StorageMock_GetTxsOlderThanNL1BlocksUntilTxHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTxsOlderThanNL1BlocksUntilTxHash' +type StorageMock_GetTxsOlderThanNL1BlocksUntilTxHash_Call struct { + *mock.Call +} + +// GetTxsOlderThanNL1BlocksUntilTxHash is a helper method to define mock.On call +// - ctx context.Context +// - nL1Blocks uint64 +// - earliestTxHash common.Hash +// - dbTx pgx.Tx +func (_e *StorageMock_Expecter) GetTxsOlderThanNL1BlocksUntilTxHash(ctx interface{}, nL1Blocks interface{}, earliestTxHash interface{}, dbTx interface{}) *StorageMock_GetTxsOlderThanNL1BlocksUntilTxHash_Call { + return &StorageMock_GetTxsOlderThanNL1BlocksUntilTxHash_Call{Call: _e.mock.On("GetTxsOlderThanNL1BlocksUntilTxHash", ctx, nL1Blocks, earliestTxHash, dbTx)} +} + +func (_c *StorageMock_GetTxsOlderThanNL1BlocksUntilTxHash_Call) Run(run func(ctx context.Context, nL1Blocks uint64, earliestTxHash common.Hash, dbTx pgx.Tx)) *StorageMock_GetTxsOlderThanNL1BlocksUntilTxHash_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(common.Hash), args[3].(pgx.Tx)) + }) + return _c +} + +func (_c *StorageMock_GetTxsOlderThanNL1BlocksUntilTxHash_Call) Return(_a0 []common.Hash, _a1 error) *StorageMock_GetTxsOlderThanNL1BlocksUntilTxHash_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *StorageMock_GetTxsOlderThanNL1BlocksUntilTxHash_Call) RunAndReturn(run func(context.Context, uint64, common.Hash, pgx.Tx) ([]common.Hash, error)) *StorageMock_GetTxsOlderThanNL1BlocksUntilTxHash_Call { + _c.Call.Return(run) + return _c +} + // GetVerifiedBatch provides a mock function with given fields: ctx, batchNumber, dbTx func (_m *StorageMock) GetVerifiedBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.VerifiedBatch, error) { ret := _m.Called(ctx, batchNumber, dbTx) @@ -6950,6 +7070,64 @@ func (_c *StorageMock_GetWIPBatchInStorage_Call) RunAndReturn(run func(context.C return _c } +// IsBatchChecked provides a mock function with given fields: ctx, batchNum, dbTx +func (_m *StorageMock) IsBatchChecked(ctx context.Context, batchNum uint64, dbTx pgx.Tx) (bool, error) { + ret := _m.Called(ctx, batchNum, dbTx) + + if len(ret) == 0 { + panic("no return value specified for IsBatchChecked") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) (bool, error)); ok { + return rf(ctx, batchNum, dbTx) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) bool); ok { + r0 = rf(ctx, batchNum, dbTx) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64, pgx.Tx) error); ok { + r1 = rf(ctx, batchNum, dbTx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// StorageMock_IsBatchChecked_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsBatchChecked' +type StorageMock_IsBatchChecked_Call struct { + *mock.Call +} + +// IsBatchChecked is a helper method to define mock.On call +// - ctx context.Context +// - batchNum uint64 +// - dbTx pgx.Tx +func (_e *StorageMock_Expecter) IsBatchChecked(ctx interface{}, batchNum interface{}, dbTx interface{}) *StorageMock_IsBatchChecked_Call { + return &StorageMock_IsBatchChecked_Call{Call: _e.mock.On("IsBatchChecked", ctx, batchNum, dbTx)} +} + +func (_c *StorageMock_IsBatchChecked_Call) Run(run func(ctx context.Context, batchNum uint64, dbTx pgx.Tx)) *StorageMock_IsBatchChecked_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(pgx.Tx)) + }) + return _c +} + +func (_c *StorageMock_IsBatchChecked_Call) Return(_a0 bool, _a1 error) *StorageMock_IsBatchChecked_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *StorageMock_IsBatchChecked_Call) RunAndReturn(run func(context.Context, uint64, pgx.Tx) (bool, error)) *StorageMock_IsBatchChecked_Call { + _c.Call.Return(run) + return _c +} + // IsBatchClosed provides a mock function with given fields: ctx, batchNum, dbTx func (_m *StorageMock) IsBatchClosed(ctx context.Context, batchNum uint64, dbTx pgx.Tx) (bool, error) { ret := _m.Called(ctx, batchNum, dbTx) @@ -7813,6 +7991,54 @@ func (_c *StorageMock_StoreGenesisBatch_Call) RunAndReturn(run func(context.Cont return _c } +// UpdateBatchAsChecked provides a mock function with given fields: ctx, batchNumber, dbTx +func (_m *StorageMock) UpdateBatchAsChecked(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error { + ret := _m.Called(ctx, batchNumber, dbTx) + + if len(ret) == 0 { + panic("no return value specified for UpdateBatchAsChecked") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, pgx.Tx) error); ok { + r0 = rf(ctx, batchNumber, dbTx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// StorageMock_UpdateBatchAsChecked_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateBatchAsChecked' +type StorageMock_UpdateBatchAsChecked_Call struct { + *mock.Call +} + +// UpdateBatchAsChecked is a helper method to define mock.On call +// - ctx context.Context +// - batchNumber uint64 +// - dbTx pgx.Tx +func (_e *StorageMock_Expecter) UpdateBatchAsChecked(ctx interface{}, batchNumber interface{}, dbTx interface{}) *StorageMock_UpdateBatchAsChecked_Call { + return &StorageMock_UpdateBatchAsChecked_Call{Call: _e.mock.On("UpdateBatchAsChecked", ctx, batchNumber, dbTx)} +} + +func (_c *StorageMock_UpdateBatchAsChecked_Call) Run(run func(ctx context.Context, batchNumber uint64, dbTx pgx.Tx)) *StorageMock_UpdateBatchAsChecked_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(pgx.Tx)) + }) + return _c +} + +func (_c *StorageMock_UpdateBatchAsChecked_Call) Return(_a0 error) *StorageMock_UpdateBatchAsChecked_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *StorageMock_UpdateBatchAsChecked_Call) RunAndReturn(run func(context.Context, uint64, pgx.Tx) error) *StorageMock_UpdateBatchAsChecked_Call { + _c.Call.Return(run) + return _c +} + // UpdateBatchL2Data provides a mock function with given fields: ctx, batchNumber, batchL2Data, dbTx func (_m *StorageMock) UpdateBatchL2Data(ctx context.Context, batchNumber uint64, batchL2Data []byte, dbTx pgx.Tx) error { ret := _m.Called(ctx, batchNumber, batchL2Data, dbTx) diff --git a/state/pgstatestorage/batch.go b/state/pgstatestorage/batch.go index b77b5b1da7..214be8a685 100644 --- a/state/pgstatestorage/batch.go +++ b/state/pgstatestorage/batch.go @@ -604,7 +604,7 @@ func (p *PostgresStorage) OpenBatchInStorage(ctx context.Context, batchContext s // OpenWIPBatchInStorage adds a new wip batch into the state storage func (p *PostgresStorage) OpenWIPBatchInStorage(ctx context.Context, batch state.Batch, dbTx pgx.Tx) error { - const openBatchSQL = "INSERT INTO state.batch (batch_num, global_exit_root, state_root, local_exit_root, timestamp, coinbase, forced_batch_num, raw_txs_data, batch_resources, wip) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, TRUE)" + const openBatchSQL = "INSERT INTO state.batch (batch_num, global_exit_root, state_root, local_exit_root, timestamp, coinbase, forced_batch_num, raw_txs_data, batch_resources, wip, checked) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, TRUE, FALSE)" resourcesData, err := json.Marshal(batch.Resources) if err != nil { @@ -900,6 +900,25 @@ func (p *PostgresStorage) UpdateWIPBatch(ctx context.Context, receipt state.Proc return err } +// updateBatchAsChecked updates the batch to set it as checked (sequencer sanity check was successful) +func (p *PostgresStorage) UpdateBatchAsChecked(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error { + const updateL2DataSQL = "UPDATE state.batch SET checked = TRUE WHERE batch_num = $1" + + e := p.getExecQuerier(dbTx) + _, err := e.Exec(ctx, updateL2DataSQL, batchNumber) + return err +} + +// IsBatchChecked indicates if the batch is closed and checked (sequencer sanity check was successful) +func (p *PostgresStorage) IsBatchChecked(ctx context.Context, batchNum uint64, dbTx pgx.Tx) (bool, error) { + const isBatchCheckedSQL = "SELECT not(wip) AND checked FROM state.batch WHERE batch_num = $1" + + q := p.getExecQuerier(dbTx) + var isChecked bool + err := q.QueryRow(ctx, isBatchCheckedSQL, batchNum).Scan(&isChecked) + return isChecked, err +} + // AddAccumulatedInputHash adds the accumulated input hash func (p *PostgresStorage) AddAccumulatedInputHash(ctx context.Context, batchNum uint64, accInputHash common.Hash, dbTx pgx.Tx) error { const addAccInputHashBatchSQL = "UPDATE state.batch SET acc_input_hash = $1 WHERE batch_num = $2" @@ -1025,3 +1044,31 @@ func (p *PostgresStorage) GetLatestBatchGlobalExitRoot(ctx context.Context, dbTx return common.HexToHash(lastGER), nil } + +// GetNotCheckedBatches returns the batches that are closed but not checked +func (p *PostgresStorage) GetNotCheckedBatches(ctx context.Context, dbTx pgx.Tx) ([]*state.Batch, error) { + const getBatchesNotCheckedSQL = ` + SELECT batch_num, global_exit_root, local_exit_root, acc_input_hash, state_root, timestamp, coinbase, raw_txs_data, forced_batch_num, batch_resources, wip + from state.batch WHERE wip IS FALSE AND checked IS FALSE ORDER BY batch_num ASC` + + e := p.getExecQuerier(dbTx) + rows, err := e.Query(ctx, getBatchesNotCheckedSQL) + if errors.Is(err, pgx.ErrNoRows) { + return nil, state.ErrNotFound + } else if err != nil { + return nil, err + } + defer rows.Close() + + batches := make([]*state.Batch, 0, len(rows.RawValues())) + + for rows.Next() { + batch, err := scanBatch(rows) + if err != nil { + return nil, err + } + batches = append(batches, &batch) + } + + return batches, nil +} diff --git a/state/pgstatestorage/transaction.go b/state/pgstatestorage/transaction.go index 43185894ab..5f6cd2856f 100644 --- a/state/pgstatestorage/transaction.go +++ b/state/pgstatestorage/transaction.go @@ -15,6 +15,67 @@ import ( const maxTopics = 4 +// GetTxsOlderThanNL1BlocksUntilTxHash get txs hashes to delete from tx pool from the oldest processed transaction to the latest +// txn that has been virtualized. +// Works like GetTxsOlderThanNL1Blocks but pulls hashes until earliestTxHash +func (p *PostgresStorage) GetTxsOlderThanNL1BlocksUntilTxHash(ctx context.Context, nL1Blocks uint64, earliestTxHash common.Hash, dbTx pgx.Tx) ([]common.Hash, error) { + var earliestBatchNum, latestBatchNum, blockNum uint64 + const getLatestBatchNumByBlockNumFromVirtualBatch = "SELECT batch_num FROM state.virtual_batch WHERE block_num <= $1 ORDER BY batch_num DESC LIMIT 1" + const getTxsHashesBeforeBatchNum = "SELECT hash FROM state.transaction JOIN state.l2block ON state.transaction.l2_block_num = state.l2block.block_num AND state.l2block.batch_num >= $1 AND state.l2block.batch_num <= $2" + + // Get lower bound batch_num which is the batch num from the oldest tx in txpool + const getEarliestBatchNumByTxHashFromVirtualBatch = `SELECT batch_num + FROM state.transaction + JOIN state.l2block ON + state.transaction.l2_block_num = state.l2block.block_num AND state.transaction.hash = $1` + + e := p.getExecQuerier(dbTx) + + err := e.QueryRow(ctx, getLastBlockNumSQL).Scan(&blockNum) + if errors.Is(err, pgx.ErrNoRows) { + return nil, state.ErrNotFound + } else if err != nil { + return nil, err + } + + blockNum = blockNum - nL1Blocks + if blockNum <= 0 { + return nil, errors.New("blockNumDiff is too big, there are no txs to delete") + } + + err = e.QueryRow(ctx, getEarliestBatchNumByTxHashFromVirtualBatch, earliestTxHash.String()).Scan(&earliestBatchNum) + if errors.Is(err, pgx.ErrNoRows) { + return nil, state.ErrNotFound + } else if err != nil { + return nil, err + } + + err = e.QueryRow(ctx, getLatestBatchNumByBlockNumFromVirtualBatch, blockNum).Scan(&latestBatchNum) + if errors.Is(err, pgx.ErrNoRows) { + return nil, state.ErrNotFound + } else if err != nil { + return nil, err + } + + rows, err := e.Query(ctx, getTxsHashesBeforeBatchNum, earliestBatchNum, latestBatchNum) + if errors.Is(err, pgx.ErrNoRows) { + return nil, state.ErrNotFound + } else if err != nil { + return nil, err + } + hashes := make([]common.Hash, 0, len(rows.RawValues())) + for rows.Next() { + var hash string + err := rows.Scan(&hash) + if err != nil { + return nil, err + } + hashes = append(hashes, common.HexToHash(hash)) + } + + return hashes, nil +} + // GetTxsOlderThanNL1Blocks get txs hashes to delete from tx pool func (p *PostgresStorage) GetTxsOlderThanNL1Blocks(ctx context.Context, nL1Blocks uint64, dbTx pgx.Tx) ([]common.Hash, error) { var batchNum, blockNum uint64 diff --git a/test/Makefile b/test/Makefile index 13259c2e0b..adba545b78 100644 --- a/test/Makefile +++ b/test/Makefile @@ -653,7 +653,7 @@ install-mockery: ## Installs mockery with the correct version to generate the mo go install github.com/vektra/mockery/v2@v2.39.0 .PHONY: generate-mocks -generate-mocks: generate-mocks-jsonrpc generate-mocks-sequencer generate-mocks-synchronizer generate-mocks-etherman generate-mocks-aggregator generate-mocks-state ## Generates mocks for the tests, using mockery tool +generate-mocks: generate-mocks-jsonrpc generate-mocks-sequencer generate-mocks-sequencesender generate-mocks-synchronizer generate-mocks-etherman generate-mocks-aggregator generate-mocks-state ## Generates mocks for the tests, using mockery tool .PHONY: generate-mocks-jsonrpc generate-mocks-jsonrpc: ## Generates mocks for jsonrpc , using mockery tool @@ -671,6 +671,13 @@ generate-mocks-sequencer: ## Generates mocks for sequencer , using mockery tool export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=Tx --srcpkg=github.com/jackc/pgx/v4 --output=../sequencer --outpkg=sequencer --structname=DbTxMock --filename=mock_dbtx.go export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=etherman --dir=../sequencer --output=../sequencer --outpkg=sequencer --inpackage --structname=EthermanMock --filename=mock_etherman.go +.PHONY: generate-mocks-sequencesender +generate-mocks-sequencesender: ## Generates mocks for sequencesender , using mockery tool + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=stateInterface --dir=../sequencesender --output=../sequencesender --outpkg=sequencesender --inpackage --structname=StateMock --filename=mock_state.go + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=etherman --dir=../sequencesender --output=../sequencesender --outpkg=sequencesender --inpackage --structname=EthermanMock --filename=mock_etherman.go + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=ethTxManager --dir=../sequencesender --output=../sequencesender --outpkg=sequencesender --inpackage --structname=EthTxManagerMock --filename=mock_ethtxmanager.go + + SYNC_L1_PARALLEL_FOLDER="../synchronizer/l1_parallel_sync" SYNC_L1_PARALLEL_MOCKS_FOLDER="../synchronizer/l1_parallel_sync/mocks" SYNC_L1_PARALLEL_PARAMS=--inpackage --outpkg=l1_parallel_sync