diff --git a/aggregator/aggregator.go b/aggregator/aggregator.go index 70db280d97..fac03c0df2 100644 --- a/aggregator/aggregator.go +++ b/aggregator/aggregator.go @@ -354,7 +354,7 @@ func (a *Aggregator) buildInputProver(ctx context.Context, batchToVerify *state. if err != nil { return nil, err } - leaves, err := a.State.GetLeafsByL1InfoRoot(ctx, *l1InfoRoot, nil) + leaves, err := a.State.GetLeavesByL1InfoRoot(ctx, *l1InfoRoot, nil) if err != nil { return nil, err } diff --git a/aggregator/aggregator_test.go b/aggregator/aggregator_test.go index 2baf93b2d5..259966b818 100644 --- a/aggregator/aggregator_test.go +++ b/aggregator/aggregator_test.go @@ -801,7 +801,7 @@ func TestTryGenerateBatchProof(t *testing.T) { } m.etherman.On("GetLatestBlockHeader", mock.Anything).Return(&types.Header{Number: new(big.Int).SetUint64(1)}, nil).Once() m.stateMock.On("GetVirtualBatch", mock.Anything, lastVerifiedBatchNum+1, nil).Return(&vb, nil).Twice() - m.stateMock.On("GetLeafsByL1InfoRoot", mock.Anything, *vb.L1InfoRoot, nil).Return([]state.L1InfoTreeExitRootStorageEntry{}, nil).Twice() + m.stateMock.On("GetLeavesByL1InfoRoot", mock.Anything, *vb.L1InfoRoot, nil).Return([]state.L1InfoTreeExitRootStorageEntry{}, nil).Twice() expectedInputProver, err := a.buildInputProver(context.Background(), &batchToProve) require.NoError(err) m.proverMock.On("BatchProof", expectedInputProver).Return(nil, errBanana).Once() @@ -844,7 +844,7 @@ func TestTryGenerateBatchProof(t *testing.T) { } m.etherman.On("GetLatestBlockHeader", mock.Anything).Return(&types.Header{Number: new(big.Int).SetUint64(1)}, nil).Once() m.stateMock.On("GetVirtualBatch", mock.Anything, lastVerifiedBatchNum+1, nil).Return(&vb, nil).Twice() - m.stateMock.On("GetLeafsByL1InfoRoot", mock.Anything, *vb.L1InfoRoot, nil).Return([]state.L1InfoTreeExitRootStorageEntry{}, nil).Twice() + m.stateMock.On("GetLeavesByL1InfoRoot", mock.Anything, *vb.L1InfoRoot, nil).Return([]state.L1InfoTreeExitRootStorageEntry{}, nil).Twice() expectedInputProver, err := a.buildInputProver(context.Background(), &batchToProve) require.NoError(err) m.proverMock.On("BatchProof", expectedInputProver).Return(&proofID, nil).Once() @@ -888,7 +888,7 @@ func TestTryGenerateBatchProof(t *testing.T) { } m.etherman.On("GetLatestBlockHeader", mock.Anything).Return(&types.Header{Number: new(big.Int).SetUint64(1)}, nil).Once() m.stateMock.On("GetVirtualBatch", mock.Anything, lastVerifiedBatchNum+1, nil).Return(&vb, nil).Twice() - m.stateMock.On("GetLeafsByL1InfoRoot", mock.Anything, *vb.L1InfoRoot, nil).Return([]state.L1InfoTreeExitRootStorageEntry{}, nil).Twice() + m.stateMock.On("GetLeavesByL1InfoRoot", mock.Anything, *vb.L1InfoRoot, nil).Return([]state.L1InfoTreeExitRootStorageEntry{}, nil).Twice() expectedInputProver, err := a.buildInputProver(context.Background(), &batchToProve) require.NoError(err) m.proverMock.On("BatchProof", expectedInputProver).Return(&proofID, nil).Once() @@ -932,7 +932,7 @@ func TestTryGenerateBatchProof(t *testing.T) { } m.etherman.On("GetLatestBlockHeader", mock.Anything).Return(&types.Header{Number: new(big.Int).SetUint64(1)}, nil).Once() m.stateMock.On("GetVirtualBatch", mock.Anything, lastVerifiedBatchNum+1, nil).Return(&vb, nil).Twice() - m.stateMock.On("GetLeafsByL1InfoRoot", mock.Anything, *vb.L1InfoRoot, nil).Return([]state.L1InfoTreeExitRootStorageEntry{}, nil).Twice() + m.stateMock.On("GetLeavesByL1InfoRoot", mock.Anything, *vb.L1InfoRoot, nil).Return([]state.L1InfoTreeExitRootStorageEntry{}, nil).Twice() expectedInputProver, err := a.buildInputProver(context.Background(), &batchToProve) require.NoError(err) m.proverMock.On("BatchProof", expectedInputProver).Return(&proofID, nil).Once() @@ -989,7 +989,7 @@ func TestTryGenerateBatchProof(t *testing.T) { TimestampBatchEtrog: &t, } m.stateMock.On("GetVirtualBatch", mock.Anything, lastVerifiedBatchNum+1, nil).Return(&vb, nil).Twice() - m.stateMock.On("GetLeafsByL1InfoRoot", mock.Anything, *vb.L1InfoRoot, nil).Return([]state.L1InfoTreeExitRootStorageEntry{}, nil).Twice() + m.stateMock.On("GetLeavesByL1InfoRoot", mock.Anything, *vb.L1InfoRoot, nil).Return([]state.L1InfoTreeExitRootStorageEntry{}, nil).Twice() expectedInputProver, err := a.buildInputProver(context.Background(), &batchToProve) require.NoError(err) m.proverMock.On("BatchProof", expectedInputProver).Return(&proofID, nil).Once() diff --git a/aggregator/interfaces.go b/aggregator/interfaces.go index 87a993e25b..0f599e8735 100644 --- a/aggregator/interfaces.go +++ b/aggregator/interfaces.go @@ -65,7 +65,7 @@ type stateInterface interface { CleanupBatchProofs(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error CleanupLockedBatchProofs(ctx context.Context, duration string, dbTx pgx.Tx) (int64, error) GetL1InfoRootLeafByIndex(ctx context.Context, l1InfoTreeIndex uint32, dbTx pgx.Tx) (state.L1InfoTreeExitRootStorageEntry, error) - GetLeafsByL1InfoRoot(ctx context.Context, l1InfoRoot common.Hash, dbTx pgx.Tx) ([]state.L1InfoTreeExitRootStorageEntry, error) + GetLeavesByL1InfoRoot(ctx context.Context, l1InfoRoot common.Hash, dbTx pgx.Tx) ([]state.L1InfoTreeExitRootStorageEntry, 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) GetVirtualBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.VirtualBatch, error) diff --git a/aggregator/mocks/mock_state.go b/aggregator/mocks/mock_state.go index 64900a124d..70e7a111f3 100644 --- a/aggregator/mocks/mock_state.go +++ b/aggregator/mocks/mock_state.go @@ -334,12 +334,12 @@ func (_m *StateMock) GetLastVerifiedBatch(ctx context.Context, dbTx pgx.Tx) (*st return r0, r1 } -// GetLeafsByL1InfoRoot provides a mock function with given fields: ctx, l1InfoRoot, dbTx -func (_m *StateMock) GetLeafsByL1InfoRoot(ctx context.Context, l1InfoRoot common.Hash, dbTx pgx.Tx) ([]state.L1InfoTreeExitRootStorageEntry, error) { +// GetLeavesByL1InfoRoot provides a mock function with given fields: ctx, l1InfoRoot, dbTx +func (_m *StateMock) GetLeavesByL1InfoRoot(ctx context.Context, l1InfoRoot common.Hash, dbTx pgx.Tx) ([]state.L1InfoTreeExitRootStorageEntry, error) { ret := _m.Called(ctx, l1InfoRoot, dbTx) if len(ret) == 0 { - panic("no return value specified for GetLeafsByL1InfoRoot") + panic("no return value specified for GetLeavesByL1InfoRoot") } var r0 []state.L1InfoTreeExitRootStorageEntry diff --git a/l1infotree/tree.go b/l1infotree/tree.go index e0c19da6bf..d3fe48ed2f 100644 --- a/l1infotree/tree.go +++ b/l1infotree/tree.go @@ -26,7 +26,7 @@ func NewL1InfoTree(height uint8, initialLeaves [][32]byte) (*L1InfoTree, error) var err error mt.siblings, mt.currentRoot, err = mt.initSiblings(initialLeaves) if err != nil { - log.Error("error initializing si siblings. Error: ", err) + log.Error("error initializing siblings. Error: ", err) return nil, err } log.Debug("Initial count: ", mt.count) @@ -34,6 +34,25 @@ func NewL1InfoTree(height uint8, initialLeaves [][32]byte) (*L1InfoTree, error) return mt, nil } +// ResetL1InfoTree resets the L1InfoTree. +func (mt *L1InfoTree) ResetL1InfoTree(initialLeaves [][32]byte) (*L1InfoTree, error) { + log.Info("Resetting L1InfoTree...") + newMT := &L1InfoTree{ + zeroHashes: generateZeroHashes(32), // nolint:gomnd + height: 32, // nolint:gomnd + count: uint32(len(initialLeaves)), + } + var err error + newMT.siblings, newMT.currentRoot, err = newMT.initSiblings(initialLeaves) + if err != nil { + log.Error("error initializing siblings. Error: ", err) + return nil, err + } + log.Debug("Reset initial count: ", newMT.count) + log.Debug("Reset initial root: ", newMT.currentRoot) + return newMT, nil +} + func buildIntermediate(leaves [][32]byte) ([][][]byte, [][32]byte) { var ( nodes [][][]byte diff --git a/state/interfaces.go b/state/interfaces.go index 74cb9b6a74..1ffc6c292e 100644 --- a/state/interfaces.go +++ b/state/interfaces.go @@ -145,7 +145,7 @@ type storage interface { GetRawBatchTimestamps(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*time.Time, *time.Time, error) GetL1InfoRootLeafByL1InfoRoot(ctx context.Context, l1InfoRoot common.Hash, dbTx pgx.Tx) (L1InfoTreeExitRootStorageEntry, error) GetL1InfoRootLeafByIndex(ctx context.Context, l1InfoTreeIndex uint32, dbTx pgx.Tx) (L1InfoTreeExitRootStorageEntry, error) - GetLeafsByL1InfoRoot(ctx context.Context, l1InfoRoot common.Hash, dbTx pgx.Tx) ([]L1InfoTreeExitRootStorageEntry, error) + GetLeavesByL1InfoRoot(ctx context.Context, l1InfoRoot common.Hash, dbTx pgx.Tx) ([]L1InfoTreeExitRootStorageEntry, error) GetBlockByNumber(ctx context.Context, blockNumber uint64, dbTx pgx.Tx) (*Block, 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) diff --git a/state/l1infotree.go b/state/l1infotree.go index ea89d0e206..8cac9ea5d7 100644 --- a/state/l1infotree.go +++ b/state/l1infotree.go @@ -3,7 +3,6 @@ package state import ( "context" "errors" - "fmt" "github.com/0xPolygonHermez/zkevm-node/l1infotree" "github.com/0xPolygonHermez/zkevm-node/log" @@ -34,20 +33,20 @@ func (s *State) buildL1InfoTreeCacheIfNeed(ctx context.Context, dbTx pgx.Tx) err if s.l1InfoTree != nil { return nil } - log.Debugf("Building L1InfoTree cache") - allLeaves, err := s.storage.GetAllL1InfoRootEntries(ctx, dbTx) + // Reset L1InfoTree siblings and leaves + allLeaves, err := s.GetAllL1InfoRootEntries(ctx, dbTx) if err != nil { - log.Error("error getting all leaves. Error: ", err) - return fmt.Errorf("error getting all leaves. Error: %w", err) + log.Error("error getting all leaves to reset l1InfoTree. Error: ", err) + return err } var leaves [][32]byte for _, leaf := range allLeaves { leaves = append(leaves, leaf.Hash()) } - mt, err := l1infotree.NewL1InfoTree(uint8(32), leaves) //nolint:gomnd + mt, err := s.l1InfoTree.ResetL1InfoTree(leaves) if err != nil { - log.Error("error creating L1InfoTree. Error: ", err) - return fmt.Errorf("error creating L1InfoTree. Error: %w", err) + log.Error("error resetting l1InfoTree. Error: ", err) + return err } s.l1InfoTree = mt return nil diff --git a/state/mocks/mock_storage.go b/state/mocks/mock_storage.go index a9a83eab43..eb7cb1b34a 100644 --- a/state/mocks/mock_storage.go +++ b/state/mocks/mock_storage.go @@ -4966,12 +4966,12 @@ func (_c *StorageMock_GetLatestVirtualBatchTimestamp_Call) RunAndReturn(run func return _c } -// GetLeafsByL1InfoRoot provides a mock function with given fields: ctx, l1InfoRoot, dbTx -func (_m *StorageMock) GetLeafsByL1InfoRoot(ctx context.Context, l1InfoRoot common.Hash, dbTx pgx.Tx) ([]state.L1InfoTreeExitRootStorageEntry, error) { +// GetLeavesByL1InfoRoot provides a mock function with given fields: ctx, l1InfoRoot, dbTx +func (_m *StorageMock) GetLeavesByL1InfoRoot(ctx context.Context, l1InfoRoot common.Hash, dbTx pgx.Tx) ([]state.L1InfoTreeExitRootStorageEntry, error) { ret := _m.Called(ctx, l1InfoRoot, dbTx) if len(ret) == 0 { - panic("no return value specified for GetLeafsByL1InfoRoot") + panic("no return value specified for GetLeavesByL1InfoRoot") } var r0 []state.L1InfoTreeExitRootStorageEntry @@ -4996,32 +4996,32 @@ func (_m *StorageMock) GetLeafsByL1InfoRoot(ctx context.Context, l1InfoRoot comm return r0, r1 } -// StorageMock_GetLeafsByL1InfoRoot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLeafsByL1InfoRoot' -type StorageMock_GetLeafsByL1InfoRoot_Call struct { +// StorageMock_GetLeavesByL1InfoRoot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLeavesByL1InfoRoot' +type StorageMock_GetLeavesByL1InfoRoot_Call struct { *mock.Call } -// GetLeafsByL1InfoRoot is a helper method to define mock.On call +// GetLeavesByL1InfoRoot is a helper method to define mock.On call // - ctx context.Context // - l1InfoRoot common.Hash // - dbTx pgx.Tx -func (_e *StorageMock_Expecter) GetLeafsByL1InfoRoot(ctx interface{}, l1InfoRoot interface{}, dbTx interface{}) *StorageMock_GetLeafsByL1InfoRoot_Call { - return &StorageMock_GetLeafsByL1InfoRoot_Call{Call: _e.mock.On("GetLeafsByL1InfoRoot", ctx, l1InfoRoot, dbTx)} +func (_e *StorageMock_Expecter) GetLeavesByL1InfoRoot(ctx interface{}, l1InfoRoot interface{}, dbTx interface{}) *StorageMock_GetLeavesByL1InfoRoot_Call { + return &StorageMock_GetLeavesByL1InfoRoot_Call{Call: _e.mock.On("GetLeavesByL1InfoRoot", ctx, l1InfoRoot, dbTx)} } -func (_c *StorageMock_GetLeafsByL1InfoRoot_Call) Run(run func(ctx context.Context, l1InfoRoot common.Hash, dbTx pgx.Tx)) *StorageMock_GetLeafsByL1InfoRoot_Call { +func (_c *StorageMock_GetLeavesByL1InfoRoot_Call) Run(run func(ctx context.Context, l1InfoRoot common.Hash, dbTx pgx.Tx)) *StorageMock_GetLeavesByL1InfoRoot_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(common.Hash), args[2].(pgx.Tx)) }) return _c } -func (_c *StorageMock_GetLeafsByL1InfoRoot_Call) Return(_a0 []state.L1InfoTreeExitRootStorageEntry, _a1 error) *StorageMock_GetLeafsByL1InfoRoot_Call { +func (_c *StorageMock_GetLeavesByL1InfoRoot_Call) Return(_a0 []state.L1InfoTreeExitRootStorageEntry, _a1 error) *StorageMock_GetLeavesByL1InfoRoot_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *StorageMock_GetLeafsByL1InfoRoot_Call) RunAndReturn(run func(context.Context, common.Hash, pgx.Tx) ([]state.L1InfoTreeExitRootStorageEntry, error)) *StorageMock_GetLeafsByL1InfoRoot_Call { +func (_c *StorageMock_GetLeavesByL1InfoRoot_Call) RunAndReturn(run func(context.Context, common.Hash, pgx.Tx) ([]state.L1InfoTreeExitRootStorageEntry, error)) *StorageMock_GetLeavesByL1InfoRoot_Call { _c.Call.Return(run) return _c } diff --git a/state/pgstatestorage/l1infotree.go b/state/pgstatestorage/l1infotree.go index 450124dde2..ed3fe2dd38 100644 --- a/state/pgstatestorage/l1infotree.go +++ b/state/pgstatestorage/l1infotree.go @@ -112,7 +112,7 @@ func (p *PostgresStorage) GetL1InfoRootLeafByIndex(ctx context.Context, l1InfoTr return entry, nil } -func (p *PostgresStorage) GetLeafsByL1InfoRoot(ctx context.Context, l1InfoRoot common.Hash, dbTx pgx.Tx) ([]state.L1InfoTreeExitRootStorageEntry, error) { +func (p *PostgresStorage) GetLeavesByL1InfoRoot(ctx context.Context, l1InfoRoot common.Hash, dbTx pgx.Tx) ([]state.L1InfoTreeExitRootStorageEntry, error) { // TODO: Optimize this query const getLeafsByL1InfoRootSQL = `SELECT block_num, timestamp, mainnet_exit_root, rollup_exit_root, global_exit_root, prev_block_hash, l1_info_root, l1_info_tree_index FROM state.exit_root diff --git a/state/reset.go b/state/reset.go index 62571250e0..655f5f3dd1 100644 --- a/state/reset.go +++ b/state/reset.go @@ -3,6 +3,7 @@ package state import ( "context" + "github.com/0xPolygonHermez/zkevm-node/log" "github.com/jackc/pgx/v4" ) @@ -13,12 +14,14 @@ func (s *State) Reset(ctx context.Context, blockNumber uint64, dbTx pgx.Tx) erro // - VerifiedBatches // - Entries in exit_root table err := s.ResetToL1BlockNumber(ctx, blockNumber, dbTx) - if err == nil { - // Discard L1InfoTree cache - // We can't rebuild cache, because we are inside a transaction, so we dont known - // is going to be a commit or a rollback. So is going to be rebuild on the next - // request that needs it. - s.l1InfoTree = nil + if err != nil { + log.Error("error resetting L1BlockNumber. Error: ", err) + return err } - return err + // Discard L1InfoTree cache + // We can't rebuild cache, because we are inside a transaction, so we dont known + // is going to be a commit or a rollback. So is going to be rebuild on the next + // request that needs it. + s.l1InfoTree = nil + return nil } diff --git a/synchronizer/actions/etrog/processor_l1_sequence_batches.go b/synchronizer/actions/etrog/processor_l1_sequence_batches.go index aa82c9c791..e1528594d9 100644 --- a/synchronizer/actions/etrog/processor_l1_sequence_batches.go +++ b/synchronizer/actions/etrog/processor_l1_sequence_batches.go @@ -391,7 +391,7 @@ func (p *ProcessorL1SequenceBatchesEtrog) checkTrustedState(ctx context.Context, reason := reorgReasons.String() if p.sync.IsTrustedSequencer() { - log.Errorf("TRUSTED REORG DETECTED! Batch: %d reson:%s", batch.BatchNumber, reason) + log.Errorf("TRUSTED REORG DETECTED! Batch: %d reason:%s", batch.BatchNumber, reason) // Halt function never have to return! it must blocks the process p.halt(ctx, fmt.Errorf("TRUSTED REORG DETECTED! Batch: %d", batch.BatchNumber)) log.Errorf("CRITICAL!!!: Never have to execute this code. Halt function never have to return! it must blocks the process") diff --git a/synchronizer/common/syncinterfaces/mocks/state_full_interface.go b/synchronizer/common/syncinterfaces/mocks/state_full_interface.go index f41e906728..1559654641 100644 --- a/synchronizer/common/syncinterfaces/mocks/state_full_interface.go +++ b/synchronizer/common/syncinterfaces/mocks/state_full_interface.go @@ -2343,6 +2343,53 @@ func (_c *StateFullInterface_ResetForkID_Call) RunAndReturn(run func(context.Con return _c } +// ResetL1InfoTree provides a mock function with given fields: ctx, dbTx +func (_m *StateFullInterface) ResetL1InfoTree(ctx context.Context, dbTx pgx.Tx) error { + ret := _m.Called(ctx, dbTx) + + if len(ret) == 0 { + panic("no return value specified for ResetL1InfoTree") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, pgx.Tx) error); ok { + r0 = rf(ctx, dbTx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// StateFullInterface_ResetL1InfoTree_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ResetL1InfoTree' +type StateFullInterface_ResetL1InfoTree_Call struct { + *mock.Call +} + +// ResetL1InfoTree is a helper method to define mock.On call +// - ctx context.Context +// - dbTx pgx.Tx +func (_e *StateFullInterface_Expecter) ResetL1InfoTree(ctx interface{}, dbTx interface{}) *StateFullInterface_ResetL1InfoTree_Call { + return &StateFullInterface_ResetL1InfoTree_Call{Call: _e.mock.On("ResetL1InfoTree", ctx, dbTx)} +} + +func (_c *StateFullInterface_ResetL1InfoTree_Call) Run(run func(ctx context.Context, dbTx pgx.Tx)) *StateFullInterface_ResetL1InfoTree_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(pgx.Tx)) + }) + return _c +} + +func (_c *StateFullInterface_ResetL1InfoTree_Call) Return(_a0 error) *StateFullInterface_ResetL1InfoTree_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *StateFullInterface_ResetL1InfoTree_Call) RunAndReturn(run func(context.Context, pgx.Tx) error) *StateFullInterface_ResetL1InfoTree_Call { + _c.Call.Return(run) + return _c +} + // ResetTrustedState provides a mock function with given fields: ctx, batchNumber, dbTx func (_m *StateFullInterface) ResetTrustedState(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error { ret := _m.Called(ctx, batchNumber, dbTx) diff --git a/synchronizer/l2_sync/l2_sync_etrog/executor_trusted_batch_sync.go b/synchronizer/l2_sync/l2_sync_etrog/executor_trusted_batch_sync.go index bb1a0798fa..7c89494441 100644 --- a/synchronizer/l2_sync/l2_sync_etrog/executor_trusted_batch_sync.go +++ b/synchronizer/l2_sync/l2_sync_etrog/executor_trusted_batch_sync.go @@ -136,13 +136,13 @@ func (b *SyncTrustedBatchExecutorForEtrog) FullProcess(ctx context.Context, data return nil, err } - leafs, l1InfoRoot, _, err := b.state.GetL1InfoTreeDataFromBatchL2Data(ctx, data.TrustedBatch.BatchL2Data, dbTx) + leaves, l1InfoRoot, _, err := b.state.GetL1InfoTreeDataFromBatchL2Data(ctx, data.TrustedBatch.BatchL2Data, dbTx) if err != nil { log.Errorf("%s error getting GetL1InfoTreeDataFromBatchL2Data: %v. Error:%w", data.DebugPrefix, l1InfoRoot, err) return nil, err } debugStr := data.DebugPrefix - processBatchResp, err := b.processAndStoreTxs(ctx, b.getProcessRequest(data, leafs, l1InfoRoot), dbTx, debugStr) + processBatchResp, err := b.processAndStoreTxs(ctx, b.getProcessRequest(data, leaves, l1InfoRoot), dbTx, debugStr) if err != nil { log.Error("%s error procesingAndStoringTxs. Error: ", debugStr, err) return nil, err @@ -197,7 +197,7 @@ func (b *SyncTrustedBatchExecutorForEtrog) IncrementalProcess(ctx context.Contex return nil, err } - leafs, l1InfoRoot, _, err := b.state.GetL1InfoTreeDataFromBatchL2Data(ctx, PartialBatchL2Data, dbTx) + leaves, l1InfoRoot, _, err := b.state.GetL1InfoTreeDataFromBatchL2Data(ctx, PartialBatchL2Data, dbTx) if err != nil { log.Errorf("%s error getting GetL1InfoTreeDataFromBatchL2Data: %v. Error:%w", data.DebugPrefix, l1InfoRoot, err) // TODO: Need to refine, depending of the response of GetL1InfoTreeDataFromBatchL2Data @@ -205,7 +205,7 @@ func (b *SyncTrustedBatchExecutorForEtrog) IncrementalProcess(ctx context.Contex return nil, syncinterfaces.ErrMissingSyncFromL1 } debugStr := fmt.Sprintf("%s: Batch %d:", data.Mode, uint64(data.TrustedBatch.Number)) - processReq := b.getProcessRequest(data, leafs, l1InfoRoot) + processReq := b.getProcessRequest(data, leaves, l1InfoRoot) processReq.Transactions = PartialBatchL2Data processBatchResp, err := b.processAndStoreTxs(ctx, processReq, dbTx, debugStr) if err != nil { diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index afc3da324f..7d129ac82a 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -535,6 +535,14 @@ func (s *ClientSynchronizer) syncBlocksParallel(lastEthBlockSynced *state.Block) // This function syncs the node from a specific block to the latest func (s *ClientSynchronizer) syncBlocksSequential(lastEthBlockSynced *state.Block) (*state.Block, error) { + // Call the blockchain to retrieve data + header, err := s.etherMan.HeaderByNumber(s.ctx, nil) + if err != nil { + log.Error("error getting header of the latest block in L1. Error: ", err) + return lastEthBlockSynced, err + } + lastKnownBlock := header.Number + // This function will read events fromBlockNum to latestEthBlock. Check reorg to be sure that everything is ok. block, err := s.checkReorg(lastEthBlockSynced) if err != nil { @@ -550,13 +558,6 @@ func (s *ClientSynchronizer) syncBlocksSequential(lastEthBlockSynced *state.Bloc return block, nil } - // Call the blockchain to retrieve data - header, err := s.etherMan.HeaderByNumber(s.ctx, nil) - if err != nil { - return lastEthBlockSynced, err - } - lastKnownBlock := header.Number - var fromBlock uint64 if lastEthBlockSynced.BlockNumber > 0 { fromBlock = lastEthBlockSynced.BlockNumber + 1 @@ -577,6 +578,22 @@ func (s *ClientSynchronizer) syncBlocksSequential(lastEthBlockSynced *state.Bloc if err != nil { return lastEthBlockSynced, err } + + // Check reorg again to be sure that the chain has not changed between the previous checkReorg and the call GetRollupInfoByBlockRange + block, err := s.checkReorg(lastEthBlockSynced) + if err != nil { + log.Errorf("error checking reorgs. Retrying... Err: %v", err) + return lastEthBlockSynced, fmt.Errorf("error checking reorgs") + } + if block != nil { + err = s.resetState(block.BlockNumber) + if err != nil { + log.Errorf("error resetting the state to a previous block. Retrying... Err: %v", err) + return lastEthBlockSynced, fmt.Errorf("error resetting the state to a previous block") + } + return block, nil + } + start = time.Now() err = s.blockRangeProcessor.ProcessBlockRange(s.ctx, blocks, order) metrics.ProcessL1DataTime(time.Since(start)) @@ -693,26 +710,27 @@ hash and has parent. This operation has to be done until a match is found. func (s *ClientSynchronizer) checkReorg(latestBlock *state.Block) (*state.Block, error) { // This function only needs to worry about reorgs if some of the reorganized blocks contained rollup info. latestEthBlockSynced := *latestBlock + reorgedBlock := *latestBlock var depth uint64 for { - block, err := s.etherMan.EthBlockByNumber(s.ctx, latestBlock.BlockNumber) + block, err := s.etherMan.EthBlockByNumber(s.ctx, reorgedBlock.BlockNumber) if err != nil { - log.Errorf("error getting latest block synced from blockchain. Block: %d, error: %v", latestBlock.BlockNumber, err) + log.Errorf("error getting latest block synced from blockchain. Block: %d, error: %v", reorgedBlock.BlockNumber, err) return nil, err } - if block.NumberU64() != latestBlock.BlockNumber { + if block.NumberU64() != reorgedBlock.BlockNumber { err = fmt.Errorf("wrong ethereum block retrieved from blockchain. Block numbers don't match. BlockNumber stored: %d. BlockNumber retrieved: %d", - latestBlock.BlockNumber, block.NumberU64()) + reorgedBlock.BlockNumber, block.NumberU64()) log.Error("error: ", err) return nil, err } // Compare hashes - if (block.Hash() != latestBlock.BlockHash || block.ParentHash() != latestBlock.ParentHash) && latestBlock.BlockNumber > s.genesis.BlockNumber { - log.Infof("checkReorg: Bad block %d hashOk %t parentHashOk %t", latestBlock.BlockNumber, block.Hash() == latestBlock.BlockHash, block.ParentHash() == latestBlock.ParentHash) - log.Debug("[checkReorg function] => latestBlockNumber: ", latestBlock.BlockNumber) - log.Debug("[checkReorg function] => latestBlockHash: ", latestBlock.BlockHash) - log.Debug("[checkReorg function] => latestBlockHashParent: ", latestBlock.ParentHash) - log.Debug("[checkReorg function] => BlockNumber: ", latestBlock.BlockNumber, block.NumberU64()) + if (block.Hash() != reorgedBlock.BlockHash || block.ParentHash() != reorgedBlock.ParentHash) && reorgedBlock.BlockNumber > s.genesis.BlockNumber { + log.Infof("checkReorg: Bad block %d hashOk %t parentHashOk %t", reorgedBlock.BlockNumber, block.Hash() == reorgedBlock.BlockHash, block.ParentHash() == reorgedBlock.ParentHash) + log.Debug("[checkReorg function] => latestBlockNumber: ", reorgedBlock.BlockNumber) + log.Debug("[checkReorg function] => latestBlockHash: ", reorgedBlock.BlockHash) + log.Debug("[checkReorg function] => latestBlockHashParent: ", reorgedBlock.ParentHash) + log.Debug("[checkReorg function] => BlockNumber: ", reorgedBlock.BlockNumber, block.NumberU64()) log.Debug("[checkReorg function] => BlockHash: ", block.Hash()) log.Debug("[checkReorg function] => BlockHashParent: ", block.ParentHash()) depth++ @@ -723,7 +741,7 @@ func (s *ClientSynchronizer) checkReorg(latestBlock *state.Block) (*state.Block, log.Errorf("error creating db transaction to get prevoius blocks") return nil, err } - latestBlock, err = s.state.GetPreviousBlock(s.ctx, depth, dbTx) + lb, err := s.state.GetPreviousBlock(s.ctx, depth, dbTx) errC := dbTx.Commit(s.ctx) if errC != nil { log.Errorf("error committing dbTx, err: %v", errC) @@ -739,16 +757,21 @@ func (s *ClientSynchronizer) checkReorg(latestBlock *state.Block) (*state.Block, log.Warn("error checking reorg: previous block not found in db: ", err) return &state.Block{}, nil } else if err != nil { + log.Error("error getting previousBlock from db. Error: ", err) return nil, err } + reorgedBlock = *lb } else { + log.Debugf("checkReorg: Block %d hashOk %t parentHashOk %t", reorgedBlock.BlockNumber, block.Hash() == reorgedBlock.BlockHash, block.ParentHash() == reorgedBlock.ParentHash) break } } - if latestEthBlockSynced.BlockHash != latestBlock.BlockHash { + if latestEthBlockSynced.BlockHash != reorgedBlock.BlockHash { + latestBlock = &reorgedBlock log.Info("Reorg detected in block: ", latestEthBlockSynced.BlockNumber, " last block OK: ", latestBlock.BlockNumber) return latestBlock, nil } + log.Debugf("No reorg detected in block: %d. BlockHash: %s", latestEthBlockSynced.BlockNumber, latestEthBlockSynced.BlockHash.String()) return nil, nil } diff --git a/synchronizer/synchronizer_test.go b/synchronizer/synchronizer_test.go index 4486be1f46..7e5bf285bb 100644 --- a/synchronizer/synchronizer_test.go +++ b/synchronizer/synchronizer_test.go @@ -266,6 +266,11 @@ func TestForcedBatchEtrog(t *testing.T) { Return(blocks, order, nil). Once() + m.Etherman. + On("EthBlockByNumber", ctx, lastBlock.BlockNumber). + Return(ethBlock, nil). + Once() + m.ZKEVMClient. On("BatchNumber", ctx). Return(uint64(1), nil) @@ -519,6 +524,11 @@ func TestSequenceForcedBatchIncaberry(t *testing.T) { Return(blocks, order, nil). Once() + m.Etherman. + On("EthBlockByNumber", ctx, lastBlock.BlockNumber). + Return(ethBlock, nil). + Once() + m.State. On("BeginStateTransaction", ctx). Return(m.DbTx, nil). diff --git a/test/e2e/forced_batches_vector_shared.go b/test/e2e/forced_batches_vector_shared.go index e7680bfc74..b84f660fcd 100644 --- a/test/e2e/forced_batches_vector_shared.go +++ b/test/e2e/forced_batches_vector_shared.go @@ -93,7 +93,7 @@ func LaunchTestForcedBatchesVectorFilesGroup(t *testing.T, vectorFilesDir string } log.Info("#######################") - log.Info("# Verifying new leafs #") + log.Info("# Verifying new leaves #") log.Info("#######################") merkleTree := opsman.State().GetTree() for _, expectedNewLeaf := range testCase.ExpectedNewLeafs { diff --git a/test/e2e/state_test.go b/test/e2e/state_test.go index e921597077..20a652547a 100644 --- a/test/e2e/state_test.go +++ b/test/e2e/state_test.go @@ -82,7 +82,7 @@ func TestStateTransition(t *testing.T) { st := opsman.State() - // Check leafs + // Check leaves l2Block, err := st.GetLastL2Block(ctx, nil) require.NoError(t, err) for addrStr, leaf := range testCase.ExpectedNewLeafs { diff --git a/tools/genesis/genesisparser/genesisparser.go b/tools/genesis/genesisparser/genesisparser.go index 27a037ebe0..d6109ff969 100644 --- a/tools/genesis/genesisparser/genesisparser.go +++ b/tools/genesis/genesisparser/genesisparser.go @@ -16,32 +16,32 @@ type GenesisAccountTest struct { // GenesisTest2Actions change format from testvector to the used internaly func GenesisTest2Actions(accounts []GenesisAccountTest) []*state.GenesisAction { - leafs := make([]*state.GenesisAction, 0) + leaves := make([]*state.GenesisAction, 0) for _, acc := range accounts { if len(acc.Balance) != 0 && acc.Balance != "0" { - leafs = append(leafs, &state.GenesisAction{ + leaves = append(leaves, &state.GenesisAction{ Address: acc.Address, Type: int(merkletree.LeafTypeBalance), Value: acc.Balance, }) } if len(acc.Nonce) != 0 && acc.Nonce != "0" { - leafs = append(leafs, &state.GenesisAction{ + leaves = append(leaves, &state.GenesisAction{ Address: acc.Address, Type: int(merkletree.LeafTypeNonce), Value: acc.Nonce, }) } if len(acc.Bytecode) != 0 { - leafs = append(leafs, &state.GenesisAction{ + leaves = append(leaves, &state.GenesisAction{ Address: acc.Address, Type: int(merkletree.LeafTypeCode), Bytecode: acc.Bytecode, }) } for key, value := range acc.Storage { - leafs = append(leafs, &state.GenesisAction{ + leaves = append(leaves, &state.GenesisAction{ Address: acc.Address, Type: int(merkletree.LeafTypeStorage), StoragePosition: key, @@ -49,5 +49,5 @@ func GenesisTest2Actions(accounts []GenesisAccountTest) []*state.GenesisAction { }) } } - return leafs + return leaves }