Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhance(l1-info-sync): sync l1 info table from l2 rpc id an rpc node #1696

Open
wants to merge 2 commits into
base: zkevm
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,16 @@ var (
Name: "zkevm.genesis-config-path",
Usage: "File path for the zk config containing allocs, chainspec, and other zk specific configurations.",
}
L2InfoTreeUpdatesBatchSize = cli.Uint64Flag{
Name: "zkevm.l2-info-tree-updates-batch-size",
Usage: "Size of the batch of L2 info tree updates to retrieve at a time. L2 info tree updates must be enabled to use this.",
Value: 500,
}
L2InfoTreeUpdatesEnabled = cli.BoolFlag{
Name: "zkevm.l2-info-tree-updates-enabled",
Usage: "When enabled a RPC node can use the L2 to build the InfoTree.",
Value: false,
}
ACLPrintHistory = cli.IntFlag{
Name: "acl.print-history",
Usage: "Number of entries to print from the ACL history on node start up",
Expand Down
2 changes: 1 addition & 1 deletion eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,7 @@ func New(ctx context.Context, stack *node.Node, config *ethconfig.Config, logger
cfg.L1HighestBlockType,
)

l1InfoTreeUpdater := l1infotree.NewUpdater(cfg.Zk, l1InfoTreeSyncer)
l1InfoTreeUpdater := l1infotree.NewUpdater(cfg.Zk, l1InfoTreeSyncer, l1infotree.NewInfoTreeL2RpcSyncer(ctx, cfg.Zk))

var dataStreamServer server.DataStreamServer
if backend.streamServer != nil {
Expand Down
2 changes: 2 additions & 0 deletions eth/ethconfig/config_zkevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ type Zk struct {
BadTxAllowance uint64
BadTxStoreValue uint64
BadTxPurge bool
L2InfoTreeUpdatesBatchSize uint64
L2InfoTreeUpdatesEnabled bool
}

var DefaultZkConfig = &Zk{}
Expand Down
2 changes: 2 additions & 0 deletions turbo/cli/default_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ var DefaultFlags = []cli.Flag{
&utils.PanicOnReorg,
&utils.ShadowSequencer,
&utils.ZKGenesisConfigPathFlag,
&utils.L2InfoTreeUpdatesBatchSize,
&utils.L2InfoTreeUpdatesEnabled,

&utils.SilkwormExecutionFlag,
&utils.SilkwormRpcDaemonFlag,
Expand Down
2 changes: 2 additions & 0 deletions turbo/cli/flags_zkevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ func ApplyFlagsForZkConfig(ctx *cli.Context, cfg *ethconfig.Config) {
BadTxAllowance: ctx.Uint64(utils.BadTxAllowance.Name),
BadTxStoreValue: ctx.Uint64(utils.BadTxStoreValue.Name),
BadTxPurge: ctx.Bool(utils.BadTxPurge.Name),
L2InfoTreeUpdatesBatchSize: ctx.Uint64(utils.L2InfoTreeUpdatesBatchSize.Name),
L2InfoTreeUpdatesEnabled: ctx.Bool(utils.L2InfoTreeUpdatesEnabled.Name),
}

utils2.EnableTimer(cfg.DebugTimers)
Expand Down
15 changes: 13 additions & 2 deletions turbo/jsonrpc/zkevm_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ type ZkEvmAPI interface {
GetL2BlockInfoTree(ctx context.Context, blockNum rpc.BlockNumberOrHash) (json.RawMessage, error)
EstimateCounters(ctx context.Context, argsOrNil *zkevmRPCTransaction) (json.RawMessage, error)
GetBatchCountersByNumber(ctx context.Context, batchNumRpc rpc.BlockNumber) (res json.RawMessage, err error)
GetExitRootTable(ctx context.Context) ([]l1InfoTreeData, error)
GetExitRootTable(ctx context.Context, argsOrNil *zkevmRPCExitRootTableArgs) ([]l1InfoTreeData, error)
GetVersionHistory(ctx context.Context) (json.RawMessage, error)
GetForkId(ctx context.Context) (hexutil.Uint64, error)
GetForkById(ctx context.Context, forkId hexutil.Uint64) (res json.RawMessage, err error)
Expand Down Expand Up @@ -1246,6 +1246,11 @@ func (api *ZkEvmAPIImpl) GetVersionHistory(ctx context.Context) (json.RawMessage
return versionsJson, nil
}

type zkevmRPCExitRootTableArgs struct {
From *uint64 `json:"from"`
To *uint64 `json:"to"`
}

type l1InfoTreeData struct {
Index uint64 `json:"index"`
Ger common.Hash `json:"ger"`
Expand All @@ -1257,7 +1262,7 @@ type l1InfoTreeData struct {
BlockNumber uint64 `json:"block_number"`
}

func (api *ZkEvmAPIImpl) GetExitRootTable(ctx context.Context) ([]l1InfoTreeData, error) {
func (api *ZkEvmAPIImpl) GetExitRootTable(ctx context.Context, argsOrNil *zkevmRPCExitRootTableArgs) ([]l1InfoTreeData, error) {
tx, err := api.db.BeginRo(ctx)
if err != nil {
return nil, err
Expand All @@ -1274,6 +1279,9 @@ func (api *ZkEvmAPIImpl) GetExitRootTable(ctx context.Context) ([]l1InfoTreeData
var result []l1InfoTreeData

var idx uint64 = 1
if argsOrNil != nil && argsOrNil.From != nil {
idx = *argsOrNil.From
}
for {
info, err := hermezDb.GetL1InfoTreeUpdate(idx)
if err != nil {
Expand All @@ -1294,6 +1302,9 @@ func (api *ZkEvmAPIImpl) GetExitRootTable(ctx context.Context) ([]l1InfoTreeData
}
result = append(result, data)
idx++
if argsOrNil != nil && argsOrNil.To != nil && idx > *argsOrNil.To {
break
}
}

return result, nil
Expand Down
2 changes: 1 addition & 1 deletion turbo/jsonrpc/zkevm_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,7 @@ func TestGetExitRootTable(t *testing.T) {
}
tx.Commit()

exitRootEntries, err := zkEvmImpl.GetExitRootTable(ctx)
exitRootEntries, err := zkEvmImpl.GetExitRootTable(ctx, nil)
assert.NoError(err)
t.Logf("exitRootEntries: %+v", exitRootEntries)
for i, er := range exitRootEntries {
Expand Down
128 changes: 128 additions & 0 deletions zk/l1infotree/info_tree_l2_rpc_syncer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package l1infotree

import (
"context"
"encoding/json"
"github.com/ledgerwatch/erigon/eth/ethconfig"
zkTypes "github.com/ledgerwatch/erigon/zk/types"
jsonClient "github.com/ledgerwatch/erigon/zkevm/jsonrpc/client"
"github.com/ledgerwatch/log/v3"
"sync/atomic"
"time"
)

const (
ExitRootTable = "zkevm_getExitRootTable"
)

// InfoTreeL2RpcSyncer is a struct that is used to sync the Info Tree from an L2 Sequencer RPC.
type InfoTreeL2RpcSyncer struct {
ctx context.Context
zkCfg *ethconfig.Zk
isSyncStarted atomic.Bool
isSyncFinished atomic.Bool
infoTreeChan chan []zkTypes.L1InfoTreeUpdate
}

// NewInfoTreeL2RpcSyncer creates a new InfoTreeL2RpcSyncer.
// It is used to sync the Info Tree from an L2 Sequencer RPC.
// The sequencer must have the full Info Tree synced from the L1.
func NewInfoTreeL2RpcSyncer(ctx context.Context, zkCfg *ethconfig.Zk) *InfoTreeL2RpcSyncer {
return &InfoTreeL2RpcSyncer{
ctx: ctx,
zkCfg: zkCfg,
infoTreeChan: make(chan []zkTypes.L1InfoTreeUpdate),
}
}

func (s *InfoTreeL2RpcSyncer) IsSyncStarted() bool {
return s.isSyncStarted.Load()
}

func (s *InfoTreeL2RpcSyncer) IsSyncFinished() bool {
return s.isSyncFinished.Load()
}

func (s *InfoTreeL2RpcSyncer) GetInfoTreeChan() chan []zkTypes.L1InfoTreeUpdate {
return s.infoTreeChan
}

// ConsumeInfoTree consumes the Info Tree from the Info Tree chan.
func (s *InfoTreeL2RpcSyncer) ConsumeInfoTree() {
for {
select {
case <-s.ctx.Done():
return
case <-s.infoTreeChan:
default:
if !s.isSyncStarted.Load() {
return
}
time.Sleep(time.Second)
}
}
}

// RunSyncInfoTree runs the sync process for the Info Tree from an L2 Sequencer RPC and put the updates in the Info Tree chan.
func (s *InfoTreeL2RpcSyncer) RunSyncInfoTree() {
if s.isSyncStarted.Load() {
return
}
s.isSyncStarted.Store(true)
s.isSyncFinished.Store(false)

totalSynced := uint64(0)
batchSize := s.zkCfg.L2InfoTreeUpdatesBatchSize

go func() {
retry := 0
for {
select {
case <-s.ctx.Done():
s.isSyncFinished.Store(true)
break
default:
query := exitRootQuery{
From: totalSynced + 1,
To: totalSynced + batchSize,
}
infoTree, err := getExitRootTable(s.zkCfg.L2RpcUrl, query)
if err != nil {
log.Debug("getExitRootTable retry error", "err", err)
retry++
if retry > 5 {
return
}
time.Sleep(time.Duration(retry*2) * time.Second)
}

if len(infoTree) == 0 {
s.isSyncFinished.Store(true)
return
}

s.infoTreeChan <- infoTree
totalSynced = query.To
}
}
}()
}

type exitRootQuery struct {
From uint64 `json:"from"`
To uint64 `json:"to"`
}

func getExitRootTable(endpoint string, query exitRootQuery) ([]zkTypes.L1InfoTreeUpdate, error) {
res, err := jsonClient.JSONRPCCall(endpoint, ExitRootTable, query)
if err != nil {
return nil, err
}

var updates []zkTypes.L1InfoTreeUpdate
if err = json.Unmarshal(res.Result, &updates); err != nil {
return nil, err
}

return updates, nil
}
Loading
Loading