From 3a7a4f865f30ae43c47c9894676fe11223217dd7 Mon Sep 17 00:00:00 2001 From: Joan Esteban <129153821+joanestebanr@users.noreply.github.com> Date: Fri, 3 May 2024 13:38:07 +0200 Subject: [PATCH] fix #3613 timestamp needs to be greater or equal (#3614) syncrhonizer update the tstamp from table state.batch when the batch is sequenced --- state/interfaces.go | 1 + state/mocks/mock_storage.go | 49 +++++++++++++++++++ state/pgstatestorage/batch.go | 8 +++ .../etrog/processor_l1_sequence_batches.go | 14 +++++- .../processor_l1_sequence_batches_test.go | 10 +++- .../mocks/state_full_interface.go | 49 +++++++++++++++++++ synchronizer/common/syncinterfaces/state.go | 1 + synchronizer/synchronizer_test.go | 2 + 8 files changed, 132 insertions(+), 2 deletions(-) diff --git a/state/interfaces.go b/state/interfaces.go index cc1f0127a9..33e8bc01be 100644 --- a/state/interfaces.go +++ b/state/interfaces.go @@ -163,4 +163,5 @@ type storage interface { GetNotCheckedBatches(ctx context.Context, dbTx pgx.Tx) ([]*Batch, error) GetLastL2BlockByBatchNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*L2Block, error) GetPreviousBlockToBlockNumber(ctx context.Context, blockNumber uint64, dbTx pgx.Tx) (*Block, error) + UpdateBatchTimestamp(ctx context.Context, batchNumber uint64, timestamp time.Time, dbTx pgx.Tx) error } diff --git a/state/mocks/mock_storage.go b/state/mocks/mock_storage.go index 27964e0247..57b72a61f6 100644 --- a/state/mocks/mock_storage.go +++ b/state/mocks/mock_storage.go @@ -8333,6 +8333,55 @@ func (_c *StorageMock_UpdateBatchL2Data_Call) RunAndReturn(run func(context.Cont return _c } +// UpdateBatchTimestamp provides a mock function with given fields: ctx, batchNumber, timestamp, dbTx +func (_m *StorageMock) UpdateBatchTimestamp(ctx context.Context, batchNumber uint64, timestamp time.Time, dbTx pgx.Tx) error { + ret := _m.Called(ctx, batchNumber, timestamp, dbTx) + + if len(ret) == 0 { + panic("no return value specified for UpdateBatchTimestamp") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, time.Time, pgx.Tx) error); ok { + r0 = rf(ctx, batchNumber, timestamp, dbTx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// StorageMock_UpdateBatchTimestamp_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateBatchTimestamp' +type StorageMock_UpdateBatchTimestamp_Call struct { + *mock.Call +} + +// UpdateBatchTimestamp is a helper method to define mock.On call +// - ctx context.Context +// - batchNumber uint64 +// - timestamp time.Time +// - dbTx pgx.Tx +func (_e *StorageMock_Expecter) UpdateBatchTimestamp(ctx interface{}, batchNumber interface{}, timestamp interface{}, dbTx interface{}) *StorageMock_UpdateBatchTimestamp_Call { + return &StorageMock_UpdateBatchTimestamp_Call{Call: _e.mock.On("UpdateBatchTimestamp", ctx, batchNumber, timestamp, dbTx)} +} + +func (_c *StorageMock_UpdateBatchTimestamp_Call) Run(run func(ctx context.Context, batchNumber uint64, timestamp time.Time, dbTx pgx.Tx)) *StorageMock_UpdateBatchTimestamp_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(time.Time), args[3].(pgx.Tx)) + }) + return _c +} + +func (_c *StorageMock_UpdateBatchTimestamp_Call) Return(_a0 error) *StorageMock_UpdateBatchTimestamp_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *StorageMock_UpdateBatchTimestamp_Call) RunAndReturn(run func(context.Context, uint64, time.Time, pgx.Tx) error) *StorageMock_UpdateBatchTimestamp_Call { + _c.Call.Return(run) + return _c +} + // UpdateCheckedBlockByNumber provides a mock function with given fields: ctx, blockNumber, newCheckedStatus, dbTx func (_m *StorageMock) UpdateCheckedBlockByNumber(ctx context.Context, blockNumber uint64, newCheckedStatus bool, dbTx pgx.Tx) error { ret := _m.Called(ctx, blockNumber, newCheckedStatus, dbTx) diff --git a/state/pgstatestorage/batch.go b/state/pgstatestorage/batch.go index 843c725b12..b0b1aa6389 100644 --- a/state/pgstatestorage/batch.go +++ b/state/pgstatestorage/batch.go @@ -1092,3 +1092,11 @@ func (p *PostgresStorage) GetNotCheckedBatches(ctx context.Context, dbTx pgx.Tx) return batches, nil } + +// UpdateBatchTimestamp updates the timestamp of the state.batch with the given number. +func (p *PostgresStorage) UpdateBatchTimestamp(ctx context.Context, batchNumber uint64, timestamp time.Time, dbTx pgx.Tx) error { + const sql = "UPDATE state.batch SET timestamp = $1 WHERE batch_num = $2" + e := p.getExecQuerier(dbTx) + _, err := e.Exec(ctx, sql, timestamp.UTC(), batchNumber) + return err +} diff --git a/synchronizer/actions/etrog/processor_l1_sequence_batches.go b/synchronizer/actions/etrog/processor_l1_sequence_batches.go index 65e713137e..2f91d8a9d9 100644 --- a/synchronizer/actions/etrog/processor_l1_sequence_batches.go +++ b/synchronizer/actions/etrog/processor_l1_sequence_batches.go @@ -32,6 +32,7 @@ type stateProcessSequenceBatches interface { AddVirtualBatch(ctx context.Context, virtualBatch *state.VirtualBatch, dbTx pgx.Tx) error AddTrustedReorg(ctx context.Context, trustedReorg *state.TrustedReorg, dbTx pgx.Tx) error GetL1InfoTreeDataFromBatchL2Data(ctx context.Context, batchL2Data []byte, dbTx pgx.Tx) (map[uint32]state.L1DataV2, common.Hash, common.Hash, error) + UpdateBatchTimestamp(ctx context.Context, batchNumber uint64, timestamp time.Time, dbTx pgx.Tx) error } type syncProcessSequenceBatchesInterface interface { @@ -158,7 +159,7 @@ func (p *ProcessorL1SequenceBatchesEtrog) ProcessSequenceBatches(ctx context.Con SkipVerifyL1InfoRoot: 1, ClosingReason: state.SyncL1EventSequencedForcedBatchClosingReason, } - } else if sbatch.PolygonRollupBaseEtrogBatchData.ForcedTimestamp > 0 && sbatch.BatchNumber == 1 { + } else if sbatch.PolygonRollupBaseEtrogBatchData.ForcedTimestamp > 0 && sbatch.BatchNumber == 1 { // This is the initial batch (injected) log.Debug("Processing initial batch") batch.GlobalExitRoot = sbatch.PolygonRollupBaseEtrogBatchData.ForcedGlobalExitRoot var fBHL1 common.Hash = sbatch.PolygonRollupBaseEtrogBatchData.ForcedBlockHashL1 @@ -251,6 +252,17 @@ func (p *ProcessorL1SequenceBatchesEtrog) ProcessSequenceBatches(ctx context.Con return err } } else { + // Batch already exists + // We update the timestamp of the batch to match the timestamp + err := p.state.UpdateBatchTimestamp(ctx, batch.BatchNumber, *processCtx.Timestamp, dbTx) + if err != nil { + log.Errorf("error updating batch timestamp %s. BatchNumber: %d, BlockNumber: %d, error: %v", processCtx.Timestamp, batch.BatchNumber, blockNumber, err) + rollbackErr := dbTx.Rollback(ctx) + if rollbackErr != nil { + log.Errorf("error rolling back state because error updating batch timestamp. BatchNumber: %d, BlockNumber: %d, rollbackErr: %s, error : %v", batch.BatchNumber, blockNumber, rollbackErr.Error(), err) + return rollbackErr + } + } // Reprocess batch to compare the stateRoot with tBatch.StateRoot and get accInputHash batchRespose, err := p.state.ExecuteBatchV2(ctx, batch, processCtx.L1InfoRoot, leaves, *processCtx.Timestamp, false, processCtx.SkipVerifyL1InfoRoot, processCtx.ForcedBlockHashL1, dbTx) if err != nil { diff --git a/synchronizer/actions/etrog/processor_l1_sequence_batches_test.go b/synchronizer/actions/etrog/processor_l1_sequence_batches_test.go index 4d6a47e95f..3b30db1c50 100644 --- a/synchronizer/actions/etrog/processor_l1_sequence_batches_test.go +++ b/synchronizer/actions/etrog/processor_l1_sequence_batches_test.go @@ -101,9 +101,12 @@ func TestL1SequenceBatchesTrustedBatchSequencedThatAlreadyExistsHappyPath(t *tes expectationsPreExecution(t, mocks, ctx, batch, nil) executionResponse := newProcessBatchResponseV2(batch) expectationsForExecution(t, mocks, ctx, l1Block.SequencedBatches[1][0], l1Block.ReceivedAt, executionResponse) + mocks.State.EXPECT().UpdateBatchTimestamp(ctx, batch.BatchNumber, l1Block.ReceivedAt, mocks.DbTx).Return(nil) mocks.State.EXPECT().AddAccumulatedInputHash(ctx, executionResponse.NewBatchNum, common.BytesToHash(executionResponse.NewAccInputHash), mocks.DbTx).Return(nil) expectationsAddSequencedBatch(t, mocks, ctx, executionResponse) + err := sut.Process(ctx, etherman.Order{Pos: 1}, l1Block, mocks.DbTx) + require.NoError(t, err) } @@ -117,9 +120,12 @@ func TestL1SequenceBatchesPermissionlessBatchSequencedThatAlreadyExistsHappyPath expectationsPreExecution(t, mocks, ctx, batch, nil) executionResponse := newProcessBatchResponseV2(batch) expectationsForExecution(t, mocks, ctx, l1Block.SequencedBatches[1][0], l1Block.ReceivedAt, executionResponse) + mocks.State.EXPECT().UpdateBatchTimestamp(ctx, batch.BatchNumber, l1Block.ReceivedAt, mocks.DbTx).Return(nil) mocks.State.EXPECT().AddAccumulatedInputHash(ctx, executionResponse.NewBatchNum, common.BytesToHash(executionResponse.NewAccInputHash), mocks.DbTx).Return(nil) expectationsAddSequencedBatch(t, mocks, ctx, executionResponse) + err := sut.Process(ctx, etherman.Order{Pos: 1}, l1Block, mocks.DbTx) + require.NoError(t, err) } @@ -139,6 +145,7 @@ func TestL1SequenceBatchesPermissionlessBatchSequencedThatAlreadyExistsMismatch( executionResponse := newProcessBatchResponseV2(batch) executionResponse.NewStateRoot = common.HexToHash(hashExamplesValues[2]).Bytes() expectationsForExecution(t, mocks, ctx, l1Block.SequencedBatches[1][0], l1Block.ReceivedAt, executionResponse) + mocks.State.EXPECT().UpdateBatchTimestamp(ctx, batch.BatchNumber, l1Block.ReceivedAt, mocks.DbTx).Return(nil) mocks.State.EXPECT().AddAccumulatedInputHash(ctx, executionResponse.NewBatchNum, common.BytesToHash(executionResponse.NewAccInputHash), mocks.DbTx).Return(nil) mocks.Synchronizer.EXPECT().IsTrustedSequencer().Return(false) mocks.State.EXPECT().AddTrustedReorg(ctx, mock.Anything, mocks.DbTx).Return(nil) @@ -177,6 +184,7 @@ func TestL1SequenceBatchesTrustedBatchSequencedThatAlreadyExistsMismatch(t *test executionResponse := newProcessBatchResponseV2(batch) executionResponse.NewStateRoot = common.HexToHash(hashExamplesValues[2]).Bytes() expectationsForExecution(t, mocks, ctx, l1Block.SequencedBatches[1][0], l1Block.ReceivedAt, executionResponse) + mocks.State.EXPECT().UpdateBatchTimestamp(ctx, batch.BatchNumber, l1Block.ReceivedAt, mocks.DbTx).Return(nil) mocks.State.EXPECT().AddAccumulatedInputHash(ctx, executionResponse.NewBatchNum, common.BytesToHash(executionResponse.NewAccInputHash), mocks.DbTx).Return(nil) mocks.Synchronizer.EXPECT().IsTrustedSequencer().Return(true) @@ -295,7 +303,7 @@ func newL1Block(mocks *mocksEtrogProcessorL1, batch *state.Batch, l1InfoRoot com func newComposedL1Block(mocks *mocksEtrogProcessorL1, forcedBatch *etherman.SequencedBatch, l1InfoRoot common.Hash) *etherman.Block { l1Block := etherman.Block{ BlockNumber: 123, - ReceivedAt: mocks.TimeProvider.Now(), + ReceivedAt: time.Date(2024, 1, 1, 1, 0, 0, 0, time.UTC), SequencedBatches: [][]etherman.SequencedBatch{}, } l1Block.SequencedBatches = append(l1Block.SequencedBatches, []etherman.SequencedBatch{}) diff --git a/synchronizer/common/syncinterfaces/mocks/state_full_interface.go b/synchronizer/common/syncinterfaces/mocks/state_full_interface.go index 81fe9a430e..ec779c4854 100644 --- a/synchronizer/common/syncinterfaces/mocks/state_full_interface.go +++ b/synchronizer/common/syncinterfaces/mocks/state_full_interface.go @@ -2963,6 +2963,55 @@ func (_c *StateFullInterface_UpdateBatchL2Data_Call) RunAndReturn(run func(conte return _c } +// UpdateBatchTimestamp provides a mock function with given fields: ctx, batchNumber, timestamp, dbTx +func (_m *StateFullInterface) UpdateBatchTimestamp(ctx context.Context, batchNumber uint64, timestamp time.Time, dbTx pgx.Tx) error { + ret := _m.Called(ctx, batchNumber, timestamp, dbTx) + + if len(ret) == 0 { + panic("no return value specified for UpdateBatchTimestamp") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, time.Time, pgx.Tx) error); ok { + r0 = rf(ctx, batchNumber, timestamp, dbTx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// StateFullInterface_UpdateBatchTimestamp_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateBatchTimestamp' +type StateFullInterface_UpdateBatchTimestamp_Call struct { + *mock.Call +} + +// UpdateBatchTimestamp is a helper method to define mock.On call +// - ctx context.Context +// - batchNumber uint64 +// - timestamp time.Time +// - dbTx pgx.Tx +func (_e *StateFullInterface_Expecter) UpdateBatchTimestamp(ctx interface{}, batchNumber interface{}, timestamp interface{}, dbTx interface{}) *StateFullInterface_UpdateBatchTimestamp_Call { + return &StateFullInterface_UpdateBatchTimestamp_Call{Call: _e.mock.On("UpdateBatchTimestamp", ctx, batchNumber, timestamp, dbTx)} +} + +func (_c *StateFullInterface_UpdateBatchTimestamp_Call) Run(run func(ctx context.Context, batchNumber uint64, timestamp time.Time, dbTx pgx.Tx)) *StateFullInterface_UpdateBatchTimestamp_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(time.Time), args[3].(pgx.Tx)) + }) + return _c +} + +func (_c *StateFullInterface_UpdateBatchTimestamp_Call) Return(_a0 error) *StateFullInterface_UpdateBatchTimestamp_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *StateFullInterface_UpdateBatchTimestamp_Call) RunAndReturn(run func(context.Context, uint64, time.Time, pgx.Tx) error) *StateFullInterface_UpdateBatchTimestamp_Call { + _c.Call.Return(run) + return _c +} + // UpdateCheckedBlockByNumber provides a mock function with given fields: ctx, blockNumber, newCheckedStatus, dbTx func (_m *StateFullInterface) UpdateCheckedBlockByNumber(ctx context.Context, blockNumber uint64, newCheckedStatus bool, dbTx pgx.Tx) error { ret := _m.Called(ctx, blockNumber, newCheckedStatus, dbTx) diff --git a/synchronizer/common/syncinterfaces/state.go b/synchronizer/common/syncinterfaces/state.go index 0b3e248cbf..2fb9ea2f16 100644 --- a/synchronizer/common/syncinterfaces/state.go +++ b/synchronizer/common/syncinterfaces/state.go @@ -78,4 +78,5 @@ type StateFullInterface interface { GetL2BlockByNumber(ctx context.Context, blockNumber uint64, dbTx pgx.Tx) (*state.L2Block, error) GetUncheckedBlocks(ctx context.Context, fromBlockNumber uint64, toBlockNumber uint64, dbTx pgx.Tx) ([]*state.Block, error) GetPreviousBlockToBlockNumber(ctx context.Context, blockNumber uint64, dbTx pgx.Tx) (*state.Block, error) + UpdateBatchTimestamp(ctx context.Context, batchNumber uint64, timestamp time.Time, dbTx pgx.Tx) error } diff --git a/synchronizer/synchronizer_test.go b/synchronizer/synchronizer_test.go index 73bebe5a12..ed063cac1e 100644 --- a/synchronizer/synchronizer_test.go +++ b/synchronizer/synchronizer_test.go @@ -368,6 +368,8 @@ func TestForcedBatchEtrog(t *testing.T) { Return(nil). Once() + m.State.EXPECT().UpdateBatchTimestamp(ctx, sequencedBatch.BatchNumber, fb[0].ForcedAt, m.DbTx).Return(nil) + m.State. On("AddAccumulatedInputHash", ctx, sequencedBatch.BatchNumber, common.Hash{}, m.DbTx). Return(nil).