Skip to content

Commit

Permalink
Merge pull request #47 from EspressoSystems/feat/preconfirmations
Browse files Browse the repository at this point in the history
Configure zkEVM node to work with HotShot preconfirmations
  • Loading branch information
jbearer authored Aug 3, 2023
2 parents 99aaf2f + 17f0731 commit 1d2dab2
Show file tree
Hide file tree
Showing 9 changed files with 407 additions and 71 deletions.
243 changes: 175 additions & 68 deletions etherman/etherman.go

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions etherman/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ import (
"github.com/ethereum/go-ethereum/common"
)

// Block of L2 transactions produced by the HotShot sequencer
type SequencerBlock struct {
Timestamp uint64 `json:"timestamp"`
Height uint64 `json:"height"`
L1Block uint64 `json:"l1_block"`
// Transactions are a blob of data encoded as hex
Transactions string `json:"transactions"`
}

// Block struct
type Block struct {
BlockNumber uint64
Expand Down
6 changes: 5 additions & 1 deletion state/pgstatestorage.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ const maxTopics = 4
const (
addGlobalExitRootSQL = "INSERT INTO state.exit_root (block_num, timestamp, mainnet_exit_root, rollup_exit_root, global_exit_root) VALUES ($1, $2, $3, $4, $5)"
getLatestExitRootBlockNumSQL = "SELECT block_num FROM state.exit_root ORDER BY id DESC LIMIT 1"
addBlockSQL = "INSERT INTO state.block (block_num, block_hash, parent_hash, received_at) VALUES ($1, $2, $3, $4)"
// When using preconfirmations, there are two streams that might add an L1 block: the L1
// synchronizer and the preconfirmations synchronizer. We need this insert statement not to fail
// when one stream tries to insert a block that the other stream has already added. Hence, the
// `WHERE NOT EXISTS` clause, to silently do nothing when the block already exists.
addBlockSQL = "INSERT INTO state.block (block_num, block_hash, parent_hash, received_at) SELECT $1, $2, $3, $4 WHERE NOT EXISTS (SELECT block_num FROM state.block WHERE block_num = $1)"
getLastBlockSQL = "SELECT block_num, block_hash, parent_hash, received_at FROM state.block ORDER BY block_num DESC LIMIT 1"
getPreviousBlockSQL = "SELECT block_num, block_hash, parent_hash, received_at FROM state.block ORDER BY block_num DESC LIMIT 1 OFFSET $1"
getLastBatchNumberSQL = "SELECT batch_num FROM state.batch ORDER BY batch_num DESC LIMIT 1"
Expand Down
2 changes: 2 additions & 0 deletions synchronizer/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
type Config struct {
// SyncInterval is the delay interval between reading new rollup information
SyncInterval types.Duration `mapstructure:"SyncInterval"`
// PreconfirmationsSyncInterval is the delay interval between reading new preconfirmations from the sequencer
PreconfirmationsSyncInterval types.Duration `mapstructure:"PreconfirmationsSyncInterval"`

// SyncChunkSize is the number of blocks to sync on each chunk
SyncChunkSize uint64 `mapstructure:"SyncChunkSize"`
Expand Down
3 changes: 2 additions & 1 deletion synchronizer/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import (
// ethermanInterface contains the methods required to interact with ethereum.
type ethermanInterface interface {
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
GetRollupInfoByBlockRange(ctx context.Context, fromBlock uint64, toBlock *uint64) ([]etherman.Block, map[common.Hash][]etherman.Order, error)
GetRollupInfoByBlockRange(ctx context.Context, fromBlock uint64, toBlock *uint64, usePreconfirmations bool) ([]etherman.Block, map[common.Hash][]etherman.Order, error)
EthBlockByNumber(ctx context.Context, blockNumber uint64) (*types.Block, error)
GetLatestBatchNumber() (uint64, error)
GetTrustedSequencerURL() (string, error)
VerifyGenBlockNumber(ctx context.Context, genBlockNumber uint64) (bool, error)
GetPreconfirmations(ctx context.Context, fromL2Block uint64) ([]etherman.Block, map[common.Hash][]etherman.Order, error)
}

// stateInterface gathers the methods required to interact with the state.
Expand Down
50 changes: 49 additions & 1 deletion synchronizer/synchronizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,21 @@ func (s *ClientSynchronizer) Sync() error {
log.Fatalf("error committing dbTx, err: %w", err)
}

if s.usePreconfirmations() {
go func() {
for {
select {
case <-s.ctx.Done():
return
case <-time.After(s.cfg.PreconfirmationsSyncInterval.Duration):
if err = s.syncPreconfirmations(); err != nil {
log.Warn("error syncing preconfirmations: ", err)
}
}
}
}()
}

for {
select {
case <-s.ctx.Done():
Expand Down Expand Up @@ -161,6 +176,10 @@ func (s *ClientSynchronizer) Sync() error {
}
}

func (s *ClientSynchronizer) usePreconfirmations() bool {
return s.cfg.PreconfirmationsSyncInterval.Duration != 0
}

// This function syncs the node from a specific block to the latest
func (s *ClientSynchronizer) syncBlocks(lastEthBlockSynced *state.Block) (*state.Block, error) {
// This function will read events fromBlockNum to latestEthBlock. Check reorg to be sure that everything is ok.
Expand Down Expand Up @@ -199,7 +218,7 @@ func (s *ClientSynchronizer) syncBlocks(lastEthBlockSynced *state.Block) (*state
// Name can be defferent in the order struct. For instance: Batches or Name:NewSequencers. This name is an identifier to check
// if the next info that must be stored in the db is a new sequencer or a batch. The value pos (position) tells what is the
// array index where this value is.
blocks, order, err := s.etherMan.GetRollupInfoByBlockRange(s.ctx, fromBlock, &toBlock)
blocks, order, err := s.etherMan.GetRollupInfoByBlockRange(s.ctx, fromBlock, &toBlock, s.usePreconfirmations())
if err != nil {
return lastEthBlockSynced, err
}
Expand Down Expand Up @@ -254,6 +273,35 @@ func (s *ClientSynchronizer) syncBlocks(lastEthBlockSynced *state.Block) (*state
return lastEthBlockSynced, nil
}

func (s *ClientSynchronizer) syncPreconfirmations() error {
for {
// Figure out where to start from: what is the first L2 block we haven't synchronized yet?
// This is the same as the last synchronized batch number, since L2 block numbers and batch
// numbers are offset by 1.
latestSyncedBatch, err := s.state.GetLastBatchNumber(s.ctx, nil)
if err != nil {
log.Warn("error getting latest batch synced. Error: ", err)
return err
}

// Fetch new preconfirmed blocks from the sequencer.
blocks, order, err := s.etherMan.GetPreconfirmations(s.ctx, latestSyncedBatch)
if err != nil {
log.Warn("error getting preconfirmations. Error: ", err)
return err
}
if len(blocks) == 0 {
// If there are no new blocks, exit so we can sleep and retry a bit later.
return nil
}

err = s.processBlockRange(blocks, order)
if err != nil {
return err
}
}
}

// syncTrustedState synchronizes information from the trusted sequencer
// related to the trusted state when the node has all the information from
// l1 synchronized
Expand Down
1 change: 1 addition & 0 deletions test/config/test.node.config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ DefaultSenderAddress = "0x1111111111111111111111111111111111111111"

[Synchronizer]
SyncInterval = "1s"
PreconfirmationsSyncInterval = "0s" # 0 turns preconfirmations off
SyncChunkSize = 100
GenBlockNumber = 63
IgnoreGenBlockNumberCheck = false
Expand Down
82 changes: 82 additions & 0 deletions test/config/test.prover.1.preconf.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"runExecutorServer": true,
"runExecutorClient": false,
"runExecutorClientMultithread": false,

"runStateDBServer": true,
"runStateDBTest": false,

"runAggregatorServer": false,
"runAggregatorClient": false,
"runAggregatorClientMock": true,
"aggregatorClientMockTimeout": 1000,
"proverName": "test-prover",

"runFileGenBatchProof": false,
"runFileGenAggregatedProof": false,
"runFileGenFinalProof": false,
"runFileProcessBatch": false,
"runFileProcessBatchMultithread": false,

"runKeccakScriptGenerator": false,
"runKeccakTest": false,
"runStorageSMTest": false,
"runBinarySMTest": false,
"runMemAlignSMTest": false,
"runSHA256Test": false,
"runBlakeTest": false,

"executeInParallel": true,
"useMainExecGenerated": false,
"saveRequestToFile": false,
"saveInputToFile": false,
"saveDbReadsToFile": false,
"saveDbReadsToFileOnChange": false,
"saveOutputToFile": true,
"saveProofToFile": true,
"saveResponseToFile": false,
"loadDBToMemCache": true,
"opcodeTracer": false,
"logRemoteDbReads": false,
"logExecutorServerResponses": false,

"proverServerPort": 50051,
"proverServerMockPort": 50052,
"proverServerMockTimeout": 10000000,
"proverClientPort": 50051,
"proverClientHost": "127.0.0.1",

"executorServerPort": 50071,
"executorROMLineTraces": false,
"executorClientPort": 50071,
"executorClientHost": "127.0.0.1",

"stateDBServerPort": 50061,
"stateDBURL": "local",

"aggregatorServerPort": 50081,
"aggregatorClientPort": 50081,
"aggregatorClientHost": "zkevm-1-aggregator",

"mapConstPolsFile": false,
"mapConstantsTreeFile": false,

"inputFile": "input_executor_0.json",
"inputFile2": "input_executor_1.json",

"keccakScriptFile": "config/scripts/keccak_script.json",
"storageRomFile": "config/scripts/storage_sm_rom.json",

"outputPath": "output",

"databaseURL": "postgresql://prover_user:prover_pass@zkevm-1-preconfirmations-state-db:5432/prover_db",
"dbNodesTableName": "state.nodes",
"dbProgramTableName": "state.program",
"dbAsyncWrite": false,
"cleanerPollingPeriod": 600,
"requestsPersistence": 3600,
"maxExecutorThreads": 20,
"maxProverThreads": 8,
"maxStateDBThreads": 8
}

82 changes: 82 additions & 0 deletions test/config/test.prover.2.preconf.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"runExecutorServer": true,
"runExecutorClient": false,
"runExecutorClientMultithread": false,

"runStateDBServer": true,
"runStateDBTest": false,

"runAggregatorServer": false,
"runAggregatorClient": false,
"runAggregatorClientMock": true,
"aggregatorClientMockTimeout": 1000,
"proverName": "test-prover",

"runFileGenBatchProof": false,
"runFileGenAggregatedProof": false,
"runFileGenFinalProof": false,
"runFileProcessBatch": false,
"runFileProcessBatchMultithread": false,

"runKeccakScriptGenerator": false,
"runKeccakTest": false,
"runStorageSMTest": false,
"runBinarySMTest": false,
"runMemAlignSMTest": false,
"runSHA256Test": false,
"runBlakeTest": false,

"executeInParallel": true,
"useMainExecGenerated": false,
"saveRequestToFile": false,
"saveInputToFile": false,
"saveDbReadsToFile": false,
"saveDbReadsToFileOnChange": false,
"saveOutputToFile": true,
"saveProofToFile": true,
"saveResponseToFile": false,
"loadDBToMemCache": true,
"opcodeTracer": false,
"logRemoteDbReads": false,
"logExecutorServerResponses": false,

"proverServerPort": 50051,
"proverServerMockPort": 50052,
"proverServerMockTimeout": 10000000,
"proverClientPort": 50051,
"proverClientHost": "127.0.0.1",

"executorServerPort": 50071,
"executorROMLineTraces": false,
"executorClientPort": 50071,
"executorClientHost": "127.0.0.1",

"stateDBServerPort": 50061,
"stateDBURL": "local",

"aggregatorServerPort": 50081,
"aggregatorClientPort": 50081,
"aggregatorClientHost": "zkevm-2-aggregator",

"mapConstPolsFile": false,
"mapConstantsTreeFile": false,

"inputFile": "input_executor_0.json",
"inputFile2": "input_executor_1.json",

"keccakScriptFile": "config/scripts/keccak_script.json",
"storageRomFile": "config/scripts/storage_sm_rom.json",

"outputPath": "output",

"databaseURL": "postgresql://prover_user:prover_pass@zkevm-2-preconfirmations-state-db:5432/prover_db",
"dbNodesTableName": "state.nodes",
"dbProgramTableName": "state.program",
"dbAsyncWrite": false,
"cleanerPollingPeriod": 600,
"requestsPersistence": 3600,
"maxExecutorThreads": 20,
"maxProverThreads": 8,
"maxStateDBThreads": 8
}

0 comments on commit 1d2dab2

Please sign in to comment.