diff --git a/config/config_test.go b/config/config_test.go index f2e01575b8..cf918c4afe 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -60,6 +60,11 @@ func Test_Defaults(t *testing.T) { path: "Synchronizer.L2Synchronization.ReprocessFullBatchOnClose", expectedValue: false, }, + { + path: "Synchronizer.L2Synchronization.CheckLastL2BlockHashOnCloseBatch", + expectedValue: true, + }, + { path: "Sequencer.DeletePoolTxsL1BlockConfirmations", expectedValue: uint64(100), diff --git a/config/default.go b/config/default.go index b28076b251..c8db9dbc2d 100644 --- a/config/default.go +++ b/config/default.go @@ -119,6 +119,7 @@ L1SynchronizationMode = "sequential" [Synchronizer.L2Synchronization] AcceptEmptyClosedBatches = false ReprocessFullBatchOnClose = false + CheckLastL2BlockHashOnCloseBatch = true [Sequencer] DeletePoolTxsL1BlockConfirmations = 100 diff --git a/docs/config-file/node-config-doc.html b/docs/config-file/node-config-doc.html index d76299260d..6b95d5c91b 100644 --- a/docs/config-file/node-config-doc.html +++ b/docs/config-file/node-config-doc.html @@ -28,7 +28,7 @@
"300ms"
 

Default: "5s"Type: string

RollupInfoRetriesSpacing is the minimum time between retries to request rollup info (it will sleep for fulfill this time) to avoid spamming L1


Examples:

"1m"
 
"300ms"
-

Default: falseType: boolean

FallbackToSequentialModeOnSynchronized if true switch to sequential mode if the system is synchronized


L2Synchronization Configuration for L2 synchronization
Default: falseType: boolean

AcceptEmptyClosedBatches is a flag to enable or disable the acceptance of empty batches.
if true, the synchronizer will accept empty batches and process them.


Default: falseType: boolean

ReprocessFullBatchOnClose if is true when a batch is closed is force to reprocess again


Configuration of the sequencer service
Default: 100Type: integer

DeletePoolTxsL1BlockConfirmations is blocks amount after which txs will be deleted from the pool


Default: "12h0m0s"Type: string

DeletePoolTxsCheckInterval is frequency with which txs will be checked for deleting


Examples:

"1m"
+

Default: falseType: boolean

FallbackToSequentialModeOnSynchronized if true switch to sequential mode if the system is synchronized


L2Synchronization Configuration for L2 synchronization
Default: falseType: boolean

AcceptEmptyClosedBatches is a flag to enable or disable the acceptance of empty batches.
if true, the synchronizer will accept empty batches and process them.


Default: trueType: boolean

ReprocessFullBatchOnClose if is true when a batch is closed is force to reprocess again


Configuration of the sequencer service
Default: 100Type: integer

DeletePoolTxsL1BlockConfirmations is blocks amount after which txs will be deleted from the pool


Default: "12h0m0s"Type: string

DeletePoolTxsCheckInterval is frequency with which txs will be checked for deleting


Examples:

"1m"
 
"300ms"
 

Default: "10m0s"Type: string

TxLifetimeCheckInterval is the time the sequencer waits to check txs lifetime


Examples:

"1m"
 
"300ms"
diff --git a/docs/config-file/node-config-doc.md b/docs/config-file/node-config-doc.md
index 5ca12d7e8b..0d70da8e62 100644
--- a/docs/config-file/node-config-doc.md
+++ b/docs/config-file/node-config-doc.md
@@ -1683,10 +1683,11 @@ FallbackToSequentialModeOnSynchronized=false
 **Type:** : `object`
 **Description:** L2Synchronization Configuration for L2 synchronization
 
-| Property                                                                                  | Pattern | Type    | Deprecated | Definition | Title/Description                                                                                                                                                   |
-| ----------------------------------------------------------------------------------------- | ------- | ------- | ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| - [AcceptEmptyClosedBatches](#Synchronizer_L2Synchronization_AcceptEmptyClosedBatches )   | No      | boolean | No         | -          | AcceptEmptyClosedBatches is a flag to enable or disable the acceptance of empty batches.
if true, the synchronizer will accept empty batches and process them. | -| - [ReprocessFullBatchOnClose](#Synchronizer_L2Synchronization_ReprocessFullBatchOnClose ) | No | boolean | No | - | ReprocessFullBatchOnClose if is true when a batch is closed is force to reprocess again | +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| ------------------------------------------------------------------------------------------------------- | ------- | ------- | ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| - [AcceptEmptyClosedBatches](#Synchronizer_L2Synchronization_AcceptEmptyClosedBatches ) | No | boolean | No | - | AcceptEmptyClosedBatches is a flag to enable or disable the acceptance of empty batches.
if true, the synchronizer will accept empty batches and process them. | +| - [ReprocessFullBatchOnClose](#Synchronizer_L2Synchronization_ReprocessFullBatchOnClose ) | No | boolean | No | - | ReprocessFullBatchOnClose if is true when a batch is closed is force to reprocess again | +| - [CheckLastL2BlockHashOnCloseBatch](#Synchronizer_L2Synchronization_CheckLastL2BlockHashOnCloseBatch ) | No | boolean | No | - | CheckLastL2BlockHashOnCloseBatch if is true when a batch is closed is force to check the last L2Block hash | #### 9.6.1. `Synchronizer.L2Synchronization.AcceptEmptyClosedBatches` @@ -1717,6 +1718,20 @@ AcceptEmptyClosedBatches=false ReprocessFullBatchOnClose=false ``` +#### 9.6.3. `Synchronizer.L2Synchronization.CheckLastL2BlockHashOnCloseBatch` + +**Type:** : `boolean` + +**Default:** `true` + +**Description:** CheckLastL2BlockHashOnCloseBatch if is true when a batch is closed is force to check the last L2Block hash + +**Example setting the default value** (true): +``` +[Synchronizer.L2Synchronization] +CheckLastL2BlockHashOnCloseBatch=true +``` + ## 10. `[Sequencer]` **Type:** : `object` diff --git a/docs/config-file/node-config-schema.json b/docs/config-file/node-config-schema.json index 3d8842e926..867558ea25 100644 --- a/docs/config-file/node-config-schema.json +++ b/docs/config-file/node-config-schema.json @@ -635,7 +635,7 @@ "ReprocessFullBatchOnClose": { "type": "boolean", "description": "ReprocessFullBatchOnClose if is true when a batch is closed is force to reprocess again", - "default": false + "default": true } }, "additionalProperties": false, diff --git a/synchronizer/l2_sync/config.go b/synchronizer/l2_sync/config.go index 7166765b88..7781c7bd6c 100644 --- a/synchronizer/l2_sync/config.go +++ b/synchronizer/l2_sync/config.go @@ -8,4 +8,7 @@ type Config struct { // ReprocessFullBatchOnClose if is true when a batch is closed is force to reprocess again ReprocessFullBatchOnClose bool `mapstructure:"ReprocessFullBatchOnClose"` + + // CheckLastL2BlockHashOnCloseBatch if is true when a batch is closed is force to check the last L2Block hash + CheckLastL2BlockHashOnCloseBatch bool `mapstructure:"CheckLastL2BlockHashOnCloseBatch"` } diff --git a/synchronizer/l2_sync/l2_shared/post_closed_batch_check_l2block.go b/synchronizer/l2_sync/l2_shared/post_closed_batch_check_l2block.go new file mode 100644 index 0000000000..e42842aa8a --- /dev/null +++ b/synchronizer/l2_sync/l2_shared/post_closed_batch_check_l2block.go @@ -0,0 +1,63 @@ +package l2_shared + +import ( + "context" + "fmt" + "math/big" + + "github.com/0xPolygonHermez/zkevm-node/log" + "github.com/0xPolygonHermez/zkevm-node/state" + "github.com/jackc/pgx/v4" +) + +// Implements PostClosedBatchChecker + +type statePostClosedBatchCheckL2Block interface { + GetLastL2BlockByBatchNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.L2Block, error) +} + +// PostClosedBatchCheckL2Block is a struct that implements the PostClosedBatchChecker interface and check the las L2Block hash on close batch +type PostClosedBatchCheckL2Block struct { + state statePostClosedBatchCheckL2Block +} + +// NewPostClosedBatchCheckL2Block creates a new PostClosedBatchCheckL2Block +func NewPostClosedBatchCheckL2Block(state statePostClosedBatchCheckL2Block) *PostClosedBatchCheckL2Block { + return &PostClosedBatchCheckL2Block{ + state: state, + } +} + +// CheckPostClosedBatch checks the last L2Block hash on close batch +func (p *PostClosedBatchCheckL2Block) CheckPostClosedBatch(ctx context.Context, processData ProcessData, dbTx pgx.Tx) error { + if processData.TrustedBatch == nil { + log.Warnf("%s trusted batch is nil", processData.DebugPrefix) + return nil + } + if len(processData.TrustedBatch.Blocks) == 0 { + log.Infof("%s trusted batch have no Blocks, so nothing to check", processData.DebugPrefix) + return nil + } + + // Get last L2Block from the database + statelastL2Block, err := p.state.GetLastL2BlockByBatchNumber(ctx, processData.BatchNumber, dbTx) + if err != nil { + return err + } + if statelastL2Block == nil { + return fmt.Errorf("last L2Block in the database is nil") + } + trustedLastL2Block := processData.TrustedBatch.Blocks[len(processData.TrustedBatch.Blocks)-1].Block + log.Info(trustedLastL2Block) + if statelastL2Block.Number().Cmp(big.NewInt(int64(trustedLastL2Block.Number))) != 0 { + return fmt.Errorf("last L2Block in the database %s and the trusted batch %d are different", statelastL2Block.Number().String(), trustedLastL2Block.Number) + } + + if statelastL2Block.Hash() != *trustedLastL2Block.Hash { + return fmt.Errorf("last L2Block %s in the database %s and the trusted batch %s are different", statelastL2Block.Number().String(), statelastL2Block.Hash().String(), trustedLastL2Block.Hash.String()) + } + log.Infof("%s last L2Block in the database %s and the trusted batch %s are the same", processData.DebugPrefix, statelastL2Block.Number().String(), trustedLastL2Block.Number) + // Compare the two blocks + + return nil +} diff --git a/synchronizer/l2_sync/l2_shared/processor_trusted_batch_sync.go b/synchronizer/l2_sync/l2_shared/processor_trusted_batch_sync.go index 94535ebe4e..db4ddd15e0 100644 --- a/synchronizer/l2_sync/l2_shared/processor_trusted_batch_sync.go +++ b/synchronizer/l2_sync/l2_shared/processor_trusted_batch_sync.go @@ -132,16 +132,22 @@ type L1SyncGlobalExitRootChecker interface { CheckL1SyncGlobalExitRootEnoughToProcessBatch(ctx context.Context, batchNumber uint64, globalExitRoot common.Hash, dbTx pgx.Tx) error } +// PostClosedBatchChecker is the interface to implement a checker post closed batch +type PostClosedBatchChecker interface { + CheckPostClosedBatch(ctx context.Context, processData ProcessData, dbTx pgx.Tx) error +} + // ProcessorTrustedBatchSync is a template to sync trusted state. It classify what kind of update is needed and call to SyncTrustedStateBatchExecutorSteps // // that is the one that execute the sync process // // the real implementation of the steps is in the SyncTrustedStateBatchExecutorSteps interface that known how to process a batch type ProcessorTrustedBatchSync struct { - Steps SyncTrustedBatchExecutor - timeProvider syncCommon.TimeProvider - l1SyncChecker L1SyncGlobalExitRootChecker - Cfg l2_sync.Config + Steps SyncTrustedBatchExecutor + timeProvider syncCommon.TimeProvider + l1SyncChecker L1SyncGlobalExitRootChecker + postClosedCheckers []PostClosedBatchChecker + Cfg l2_sync.Config } // NewProcessorTrustedBatchSync creates a new SyncTrustedStateBatchExecutorTemplate @@ -155,6 +161,14 @@ func NewProcessorTrustedBatchSync(steps SyncTrustedBatchExecutor, } } +// AddPostChecker add a post closed batch checker +func (s *ProcessorTrustedBatchSync) AddPostChecker(checker PostClosedBatchChecker) { + if s.postClosedCheckers == nil { + s.postClosedCheckers = make([]PostClosedBatchChecker, 0) + } + s.postClosedCheckers = append(s.postClosedCheckers, checker) +} + // ProcessTrustedBatch processes a trusted batch and return the new state func (s *ProcessorTrustedBatchSync) ProcessTrustedBatch(ctx context.Context, trustedBatch *types.Batch, status TrustedState, dbTx pgx.Tx, debugPrefix string) (*TrustedState, error) { log.Debugf("%s Processing trusted batch: %v", debugPrefix, trustedBatch.Number) @@ -241,6 +255,15 @@ func (s *ProcessorTrustedBatchSync) ExecuteProcessBatch(ctx context.Context, pro log.Error("%s error verifying batch result! Error: ", processMode.DebugPrefix, err) return nil, err } + if s.postClosedCheckers != nil && len(s.postClosedCheckers) > 0 { + for _, checker := range s.postClosedCheckers { + err := checker.CheckPostClosedBatch(ctx, *processMode, dbTx) + if err != nil { + log.Errorf("%s error checking post closed batch. Error: ", processMode.DebugPrefix, err) + return nil, err + } + } + } } return processBatchResp, err } 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 377825bf56..bb1a0798fa 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 @@ -11,16 +11,11 @@ import ( "github.com/0xPolygonHermez/zkevm-node/state" syncCommon "github.com/0xPolygonHermez/zkevm-node/synchronizer/common" "github.com/0xPolygonHermez/zkevm-node/synchronizer/common/syncinterfaces" - "github.com/0xPolygonHermez/zkevm-node/synchronizer/l2_sync" "github.com/0xPolygonHermez/zkevm-node/synchronizer/l2_sync/l2_shared" "github.com/ethereum/go-ethereum/common" "github.com/jackc/pgx/v4" ) -const ( - timeOfLiveBatchOnCache = 5 * time.Minute -) - var ( // ErrNotImplemented is returned when a method is not implemented ErrNotImplemented = errors.New("not implemented") @@ -54,19 +49,13 @@ type SyncTrustedBatchExecutorForEtrog struct { sync syncinterfaces.SynchronizerFlushIDManager } -// NewSyncTrustedBatchExecutorForEtrog creates a new prcessor for sync with L2 batches -func NewSyncTrustedBatchExecutorForEtrog(zkEVMClient syncinterfaces.ZKEVMClientTrustedBatchesGetter, - state l2_shared.StateInterface, stateBatchExecutor StateInterface, - sync syncinterfaces.SynchronizerFlushIDManager, timeProvider syncCommon.TimeProvider, l1SyncChecker l2_shared.L1SyncGlobalExitRootChecker, - cfg l2_sync.Config) *l2_shared.TrustedBatchesRetrieve { - executorSteps := &SyncTrustedBatchExecutorForEtrog{ +// NewSyncTrustedBatchExecutorForEtrog creates a new SyncTrustedBatchExecutorForEtrog +func NewSyncTrustedBatchExecutorForEtrog(stateBatchExecutor StateInterface, + sync syncinterfaces.SynchronizerFlushIDManager) *SyncTrustedBatchExecutorForEtrog { + return &SyncTrustedBatchExecutorForEtrog{ state: stateBatchExecutor, sync: sync, } - - executor := l2_shared.NewProcessorTrustedBatchSync(executorSteps, timeProvider, l1SyncChecker, cfg) - a := l2_shared.NewTrustedBatchesRetrieve(executor, zkEVMClient, state, sync, *l2_shared.NewTrustedStateManager(timeProvider, timeOfLiveBatchOnCache)) - return a } // NothingProcess process a batch that is already on database and no new L2batchData, so it is not going to be processed again. diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index 591b2f4357..8029ec36a1 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -29,8 +29,9 @@ const ( // ParallelMode is the value for L1SynchronizationMode to run in parallel mode ParallelMode = "parallel" // SequentialMode is the value for L1SynchronizationMode to run in sequential mode - SequentialMode = "sequential" - maxBatchNumber = ^uint64(0) + SequentialMode = "sequential" + maxBatchNumber = ^uint64(0) + timeOfLiveBatchOnCache = 5 * time.Minute ) // Synchronizer connects L1 and L2 @@ -113,10 +114,17 @@ func NewSynchronizer( if !isTrustedSequencer { log.Info("Permissionless: creating and Initializing L2 synchronization components") L1SyncChecker := l2_sync_etrog.NewCheckSyncStatusToProcessBatch(res.zkEVMClient, res.state) + sync := &res + //syncTrustedStateEtrog := l2_sync_etrog.NewSyncTrustedBatchExecutorForEtrog(res.zkEVMClient, res.state, res.state, res, + // syncCommon.DefaultTimeProvider{}, L1SyncChecker, cfg.L2Synchronization) + executorSteps := l2_sync_etrog.NewSyncTrustedBatchExecutorForEtrog(res.state, *sync) + executor := l2_shared.NewProcessorTrustedBatchSync(executorSteps, syncCommon.DefaultTimeProvider{}, L1SyncChecker, cfg.L2Synchronization) + if cfg.L2Synchronization.CheckLastL2BlockHashOnCloseBatch { + log.Infof("Adding check of L2Block hash on close batch when sync from trusted node") + executor.AddPostChecker(l2_shared.NewPostClosedBatchCheckL2Block(res.state)) + } - syncTrustedStateEtrog := l2_sync_etrog.NewSyncTrustedBatchExecutorForEtrog(res.zkEVMClient, res.state, res.state, res, - syncCommon.DefaultTimeProvider{}, L1SyncChecker, cfg.L2Synchronization) - + syncTrustedStateEtrog := l2_shared.NewTrustedBatchesRetrieve(executor, zkEVMClient, res.state, *sync, *l2_shared.NewTrustedStateManager(syncCommon.DefaultTimeProvider{}, timeOfLiveBatchOnCache)) res.syncTrustedStateExecutor = l2_shared.NewSyncTrustedStateExecutorSelector(map[uint64]syncinterfaces.SyncTrustedStateExecutor{ uint64(state.FORKID_ETROG): syncTrustedStateEtrog, uint64(state.FORKID_ELDERBERRY): syncTrustedStateEtrog,