hermez-node/synchronizer/synchronizer.go
synchronizer/synchronizer.goRaw
1package synchronizer
2
3import (
4 "context"
5 "database/sql"
6 "fmt"
7 "math/big"
8 "sync"
9 "time"
10
11 "github.com/ethereum/go-ethereum"
12 "github.com/hermeznetwork/hermez-node/common"
13 "github.com/hermeznetwork/hermez-node/db/historydb"
14 "github.com/hermeznetwork/hermez-node/db/statedb"
15 "github.com/hermeznetwork/hermez-node/eth"
16 "github.com/hermeznetwork/hermez-node/log"
17 "github.com/hermeznetwork/hermez-node/txprocessor"
18 "github.com/hermeznetwork/tracerr"
19)
20
21// Stats of the syncrhonizer
22type Stats struct {
23 Eth struct {
24 RefreshPeriod time.Duration
25 Updated time.Time
26 FirstBlockNum int64
27 LastBlock common.Block
28 LastBatch int64
29 }
30 Sync struct {
31 Updated time.Time
32 LastBlock common.Block
33 LastBatch int64
34 // LastL1BatchBlock is the last ethereum block in which an
35 // l1Batch was forged
36 LastL1BatchBlock int64
37 LastForgeL1TxsNum int64
38 Auction struct {
39 CurrentSlot common.Slot
40 }
41 }
42}
43
44// Synced returns true if the Synchronizer is up to date with the last ethereum block
45func (s *Stats) Synced() bool {
46 return s.Eth.LastBlock.Num == s.Sync.LastBlock.Num
47}
48
49// TODO(Edu): Consider removing all the mutexes from StatsHolder, make
50// Synchronizer.Stats not thread-safe, don't pass the synchronizer to the
51// debugAPI, and have a copy of the Stats in the DebugAPI that the node passes
52// when the Sync updates.
53
54// StatsHolder stores stats and that allows reading and writing them
55// concurrently
56type StatsHolder struct {
57 Stats
58 rw sync.RWMutex
59}
60
61// NewStatsHolder creates a new StatsHolder
62func NewStatsHolder(firstBlockNum int64, refreshPeriod time.Duration) *StatsHolder {
63 stats := Stats{}
64 stats.Eth.RefreshPeriod = refreshPeriod
65 stats.Eth.FirstBlockNum = firstBlockNum
66 stats.Sync.LastForgeL1TxsNum = -1
67 return &StatsHolder{Stats: stats}
68}
69
70// UpdateCurrentSlot updates the auction stats
71func (s *StatsHolder) UpdateCurrentSlot(slot common.Slot) {
72 s.rw.Lock()
73 s.Sync.Auction.CurrentSlot = slot
74 s.rw.Unlock()
75}
76
77// UpdateSync updates the synchronizer stats
78func (s *StatsHolder) UpdateSync(lastBlock *common.Block, lastBatch *common.BatchNum,
79 lastL1BatchBlock *int64, lastForgeL1TxsNum *int64) {
80 now := time.Now()
81 s.rw.Lock()
82 s.Sync.LastBlock = *lastBlock
83 if lastBatch != nil {
84 s.Sync.LastBatch = int64(*lastBatch)
85 }
86 if lastL1BatchBlock != nil {
87 s.Sync.LastL1BatchBlock = *lastL1BatchBlock
88 s.Sync.LastForgeL1TxsNum = *lastForgeL1TxsNum
89 }
90 s.Sync.Updated = now
91 s.rw.Unlock()
92}
93
94// UpdateEth updates the ethereum stats, only if the previous stats expired
95func (s *StatsHolder) UpdateEth(ethClient eth.ClientInterface) error {
96 now := time.Now()
97 s.rw.RLock()
98 elapsed := now.Sub(s.Eth.Updated)
99 s.rw.RUnlock()
100 if elapsed < s.Eth.RefreshPeriod {
101 return nil
102 }
103
104 lastBlock, err := ethClient.EthBlockByNumber(context.TODO(), -1)
105 if err != nil {
106 return tracerr.Wrap(err)
107 }
108 lastBatch, err := ethClient.RollupLastForgedBatch()
109 if err != nil {
110 return tracerr.Wrap(err)
111 }
112 s.rw.Lock()
113 s.Eth.Updated = now
114 s.Eth.LastBlock = *lastBlock
115 s.Eth.LastBatch = lastBatch
116 s.rw.Unlock()
117 return nil
118}
119
120// CopyStats returns a copy of the inner Stats
121func (s *StatsHolder) CopyStats() *Stats {
122 s.rw.RLock()
123 sCopy := s.Stats
124 if s.Sync.Auction.CurrentSlot.BidValue != nil {
125 sCopy.Sync.Auction.CurrentSlot.BidValue =
126 common.CopyBigInt(s.Sync.Auction.CurrentSlot.BidValue)
127 }
128 if s.Sync.Auction.CurrentSlot.DefaultSlotBid != nil {
129 sCopy.Sync.Auction.CurrentSlot.DefaultSlotBid =
130 common.CopyBigInt(s.Sync.Auction.CurrentSlot.DefaultSlotBid)
131 }
132 s.rw.RUnlock()
133 return &sCopy
134}
135
136func (s *StatsHolder) blocksPerc() float64 {
137 syncLastBlockNum := s.Sync.LastBlock.Num
138 if s.Sync.LastBlock.Num == 0 {
139 syncLastBlockNum = s.Eth.FirstBlockNum - 1
140 }
141 return float64(syncLastBlockNum-(s.Eth.FirstBlockNum-1)) * 100.0 /
142 float64(s.Eth.LastBlock.Num-(s.Eth.FirstBlockNum-1))
143}
144
145func (s *StatsHolder) batchesPerc(batchNum int64) float64 {
146 return float64(batchNum) * 100.0 /
147 float64(s.Eth.LastBatch)
148}
149
150// StartBlockNums sets the first block used to start tracking the smart
151// contracts
152type StartBlockNums struct {
153 Rollup int64
154 Auction int64
155 WDelayer int64
156}
157
158// SCVariables joins all the smart contract variables in a single struct
159type SCVariables struct {
160 Rollup common.RollupVariables `validate:"required"`
161 Auction common.AuctionVariables `validate:"required"`
162 WDelayer common.WDelayerVariables `validate:"required"`
163}
164
165// SCVariablesPtr joins all the smart contract variables as pointers in a single
166// struct
167type SCVariablesPtr struct {
168 Rollup *common.RollupVariables `validate:"required"`
169 Auction *common.AuctionVariables `validate:"required"`
170 WDelayer *common.WDelayerVariables `validate:"required"`
171}
172
173// SCConsts joins all the smart contract constants in a single struct
174type SCConsts struct {
175 Rollup common.RollupConstants
176 Auction common.AuctionConstants
177 WDelayer common.WDelayerConstants
178}
179
180// Config is the Synchronizer configuration
181type Config struct {
182 StatsRefreshPeriod time.Duration
183 ChainID uint16
184}
185
186// Synchronizer implements the Synchronizer type
187type Synchronizer struct {
188 ethClient eth.ClientInterface
189 consts SCConsts
190 historyDB *historydb.HistoryDB
191 stateDB *statedb.StateDB
192 cfg Config
193 initVars SCVariables
194 startBlockNum int64
195 vars SCVariables
196 stats *StatsHolder
197}
198
199// NewSynchronizer creates a new Synchronizer
200func NewSynchronizer(ethClient eth.ClientInterface, historyDB *historydb.HistoryDB,
201 stateDB *statedb.StateDB, cfg Config) (*Synchronizer, error) {
202 auctionConstants, err := ethClient.AuctionConstants()
203 if err != nil {
204 return nil, tracerr.Wrap(fmt.Errorf("NewSynchronizer ethClient.AuctionConstants(): %w",
205 err))
206 }
207 rollupConstants, err := ethClient.RollupConstants()
208 if err != nil {
209 return nil, tracerr.Wrap(fmt.Errorf("NewSynchronizer ethClient.RollupConstants(): %w",
210 err))
211 }
212 wDelayerConstants, err := ethClient.WDelayerConstants()
213 if err != nil {
214 return nil, tracerr.Wrap(fmt.Errorf("NewSynchronizer ethClient.WDelayerConstants(): %w",
215 err))
216 }
217 consts := SCConsts{
218 Rollup: *rollupConstants,
219 Auction: *auctionConstants,
220 WDelayer: *wDelayerConstants,
221 }
222
223 initVars, startBlockNums, err := getInitialVariables(ethClient, &consts)
224 if err != nil {
225 return nil, tracerr.Wrap(err)
226 }
227 log.Infow("Synchronizer syncing from smart contract blocks",
228 "rollup", startBlockNums.Rollup,
229 "auction", startBlockNums.Auction,
230 "wdelayer", startBlockNums.WDelayer,
231 )
232 // Set startBlockNum to the minimum between Auction, Rollup and
233 // WDelayer StartBlockNum
234 startBlockNum := startBlockNums.Auction
235 if startBlockNums.Rollup < startBlockNum {
236 startBlockNum = startBlockNums.Rollup
237 }
238 if startBlockNums.WDelayer < startBlockNum {
239 startBlockNum = startBlockNums.WDelayer
240 }
241 stats := NewStatsHolder(startBlockNum, cfg.StatsRefreshPeriod)
242 s := &Synchronizer{
243 ethClient: ethClient,
244 consts: consts,
245 historyDB: historyDB,
246 stateDB: stateDB,
247 cfg: cfg,
248 initVars: *initVars,
249 startBlockNum: startBlockNum,
250 stats: stats,
251 }
252 return s, s.init()
253}
254
255// Stats returns a copy of the Synchronizer Stats. It is safe to call Stats()
256// during a Sync call
257func (s *Synchronizer) Stats() *Stats {
258 return s.stats.CopyStats()
259}
260
261// AuctionConstants returns the AuctionConstants read from the smart contract
262func (s *Synchronizer) AuctionConstants() *common.AuctionConstants {
263 return &s.consts.Auction
264}
265
266// RollupConstants returns the RollupConstants read from the smart contract
267func (s *Synchronizer) RollupConstants() *common.RollupConstants {
268 return &s.consts.Rollup
269}
270
271// WDelayerConstants returns the WDelayerConstants read from the smart contract
272func (s *Synchronizer) WDelayerConstants() *common.WDelayerConstants {
273 return &s.consts.WDelayer
274}
275
276// SCVars returns a copy of the Smart Contract Variables
277func (s *Synchronizer) SCVars() SCVariablesPtr {
278 return SCVariablesPtr{
279 Rollup: s.vars.Rollup.Copy(),
280 Auction: s.vars.Auction.Copy(),
281 WDelayer: s.vars.WDelayer.Copy(),
282 }
283}
284
285// firstBatchBlockNum is the blockNum of first batch in that block, if any
286func (s *Synchronizer) updateCurrentSlotIfSync(reset bool, firstBatchBlockNum *int64) error {
287 slot := common.Slot{
288 SlotNum: s.stats.Sync.Auction.CurrentSlot.SlotNum,
289 ForgerCommitment: s.stats.Sync.Auction.CurrentSlot.ForgerCommitment,
290 }
291 // We want the next block because the current one is already mined
292 blockNum := s.stats.Sync.LastBlock.Num + 1
293 slotNum := s.consts.Auction.SlotNum(blockNum)
294 if reset {
295 dbFirstBatchBlockNum, err := s.historyDB.GetFirstBatchBlockNumBySlot(slotNum)
296 if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
297 return tracerr.Wrap(fmt.Errorf("historyDB.GetFirstBatchBySlot: %w", err))
298 } else if tracerr.Unwrap(err) == sql.ErrNoRows {
299 firstBatchBlockNum = nil
300 } else {
301 firstBatchBlockNum = &dbFirstBatchBlockNum
302 }
303 slot.ForgerCommitment = false
304 } else if slotNum > slot.SlotNum {
305 // We are in a new slotNum, start from default values
306 slot.ForgerCommitment = false
307 }
308 slot.SlotNum = slotNum
309 slot.StartBlock, slot.EndBlock = s.consts.Auction.SlotBlocks(slot.SlotNum)
310 // If Synced, update the current coordinator
311 if s.stats.Synced() && blockNum >= s.consts.Auction.GenesisBlockNum {
312 bidCoord, err := s.historyDB.GetBestBidCoordinator(slot.SlotNum)
313 if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
314 return tracerr.Wrap(err)
315 }
316 if tracerr.Unwrap(err) == sql.ErrNoRows {
317 slot.BootCoord = true
318 slot.Forger = s.vars.Auction.BootCoordinator
319 slot.URL = s.vars.Auction.BootCoordinatorURL
320 } else if err == nil {
321 slot.BidValue = bidCoord.BidValue
322 slot.DefaultSlotBid = bidCoord.DefaultSlotSetBid[slot.SlotNum%6]
323 // Only if the highest bid value is greater/equal than
324 // the default slot bid, the bidder is the winner of
325 // the slot. Otherwise the boot coordinator is the
326 // winner.
327 if slot.BidValue.Cmp(slot.DefaultSlotBid) >= 0 {
328 slot.Bidder = bidCoord.Bidder
329 slot.Forger = bidCoord.Forger
330 slot.URL = bidCoord.URL
331 } else {
332 slot.BootCoord = true
333 slot.Forger = s.vars.Auction.BootCoordinator
334 slot.URL = s.vars.Auction.BootCoordinatorURL
335 }
336 }
337 if firstBatchBlockNum != nil &&
338 s.consts.Auction.RelativeBlock(*firstBatchBlockNum) <
339 int64(s.vars.Auction.SlotDeadline) {
340 slot.ForgerCommitment = true
341 }
342
343 // TODO: Remove this SANITY CHECK once this code is tested enough
344 // BEGIN SANITY CHECK
345 canForge, err := s.ethClient.AuctionCanForge(slot.Forger, blockNum)
346 if err != nil {
347 return tracerr.Wrap(err)
348 }
349 if !canForge {
350 return tracerr.Wrap(fmt.Errorf("Synchronized value of forger address for closed slot "+
351 "differs from smart contract: %+v", slot))
352 }
353 // END SANITY CHECK
354 }
355 s.stats.UpdateCurrentSlot(slot)
356 return nil
357}
358
359func (s *Synchronizer) init() error {
360 // Update stats parameters so that they have valid values before the
361 // first Sync call
362 if err := s.stats.UpdateEth(s.ethClient); err != nil {
363 return tracerr.Wrap(err)
364 }
365 lastBlock := &common.Block{}
366 lastSavedBlock, err := s.historyDB.GetLastBlock()
367 // `s.historyDB.GetLastBlock()` will never return `sql.ErrNoRows`
368 // because we always have the default block 0 in the DB
369 if err != nil {
370 return tracerr.Wrap(err)
371 }
372 // If we only have the default block 0,
373 // make sure that the stateDB is clean
374 if lastSavedBlock.Num == 0 {
375 if err := s.stateDB.Reset(0); err != nil {
376 return tracerr.Wrap(err)
377 }
378 } else {
379 lastBlock = lastSavedBlock
380 }
381 if err := s.resetState(lastBlock); err != nil {
382 return tracerr.Wrap(err)
383 }
384
385 log.Infow("Sync init block",
386 "syncLastBlock", s.stats.Sync.LastBlock,
387 "syncBlocksPerc", s.stats.blocksPerc(),
388 "ethFirstBlockNum", s.stats.Eth.FirstBlockNum,
389 "ethLastBlock", s.stats.Eth.LastBlock,
390 )
391 log.Infow("Sync init batch",
392 "syncLastBatch", s.stats.Sync.LastBatch,
393 "syncBatchesPerc", s.stats.batchesPerc(s.stats.Sync.LastBatch),
394 "ethLastBatch", s.stats.Eth.LastBatch,
395 )
396 return nil
397}
398
399// Sync2 attems to synchronize an ethereum block starting from lastSavedBlock.
400// If lastSavedBlock is nil, the lastSavedBlock value is obtained from de DB.
401// If a block is synched, it will be returned and also stored in the DB. If a
402// reorg is detected, the number of discarded blocks will be returned and no
403// synchronization will be made.
404// TODO: Be smart about locking: only lock during the read/write operations
405func (s *Synchronizer) Sync2(ctx context.Context, lastSavedBlock *common.Block) (*common.BlockData, *int64, error) {
406 var nextBlockNum int64 // next block number to sync
407 if lastSavedBlock == nil {
408 var err error
409 // Get lastSavedBlock from History DB
410 lastSavedBlock, err = s.historyDB.GetLastBlock()
411 if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
412 return nil, nil, tracerr.Wrap(err)
413 }
414 // If we don't have any stored block, we must do a full sync
415 // starting from the startBlockNum
416 if tracerr.Unwrap(err) == sql.ErrNoRows || lastSavedBlock.Num == 0 {
417 nextBlockNum = s.startBlockNum
418 lastSavedBlock = nil
419 }
420 }
421 if lastSavedBlock != nil {
422 nextBlockNum = lastSavedBlock.Num + 1
423 if lastSavedBlock.Num < s.startBlockNum {
424 return nil, nil, tracerr.Wrap(
425 fmt.Errorf("lastSavedBlock (%v) < startBlockNum (%v)",
426 lastSavedBlock.Num, s.startBlockNum))
427 }
428 }
429
430 ethBlock, err := s.ethClient.EthBlockByNumber(ctx, nextBlockNum)
431 if tracerr.Unwrap(err) == ethereum.NotFound {
432 return nil, nil, nil
433 } else if err != nil {
434 return nil, nil, tracerr.Wrap(err)
435 }
436 log.Debugf("ethBlock: num: %v, parent: %v, hash: %v",
437 ethBlock.Num, ethBlock.ParentHash.String(), ethBlock.Hash.String())
438
439 if err := s.stats.UpdateEth(s.ethClient); err != nil {
440 return nil, nil, tracerr.Wrap(err)
441 }
442
443 log.Debugw("Syncing...",
444 "block", nextBlockNum,
445 "ethLastBlock", s.stats.Eth.LastBlock,
446 )
447
448 // Check that the obtianed ethBlock.ParentHash == prevEthBlock.Hash; if not, reorg!
449 if lastSavedBlock != nil {
450 if lastSavedBlock.Hash != ethBlock.ParentHash {
451 // Reorg detected
452 log.Debugw("Reorg Detected",
453 "blockNum", ethBlock.Num,
454 "block.parent(got)", ethBlock.ParentHash, "parent.hash(exp)", lastSavedBlock.Hash)
455 lastDBBlockNum, err := s.reorg(lastSavedBlock)
456 if err != nil {
457 return nil, nil, tracerr.Wrap(err)
458 }
459 discarded := lastSavedBlock.Num - lastDBBlockNum
460 return nil, &discarded, nil
461 }
462 }
463
464 // Get data from the rollup contract
465 rollupData, err := s.rollupSync(ethBlock)
466 if err != nil {
467 return nil, nil, tracerr.Wrap(err)
468 }
469
470 // Get data from the auction contract
471 auctionData, err := s.auctionSync(ethBlock)
472 if err != nil {
473 return nil, nil, tracerr.Wrap(err)
474 }
475
476 // Get data from the WithdrawalDelayer contract
477 wDelayerData, err := s.wdelayerSync(ethBlock)
478 if err != nil {
479 return nil, nil, tracerr.Wrap(err)
480 }
481
482 for i := range rollupData.Withdrawals {
483 withdrawal := &rollupData.Withdrawals[i]
484 if !withdrawal.InstantWithdraw {
485 wDelayerTransfers := wDelayerData.DepositsByTxHash[withdrawal.TxHash]
486 if len(wDelayerTransfers) == 0 {
487 return nil, nil, tracerr.Wrap(fmt.Errorf("WDelayer deposit corresponding to " +
488 "non-instant rollup withdrawal not found"))
489 }
490 // Pop the first wDelayerTransfer to consume them in chronological order
491 wDelayerTransfer := wDelayerTransfers[0]
492 wDelayerData.DepositsByTxHash[withdrawal.TxHash] =
493 wDelayerData.DepositsByTxHash[withdrawal.TxHash][1:]
494
495 withdrawal.Owner = wDelayerTransfer.Owner
496 withdrawal.Token = wDelayerTransfer.Token
497 }
498 }
499
500 // Group all the block data into the structs to save into HistoryDB
501 blockData := common.BlockData{
502 Block: *ethBlock,
503 Rollup: *rollupData,
504 Auction: *auctionData,
505 WDelayer: *wDelayerData,
506 }
507
508 err = s.historyDB.AddBlockSCData(&blockData)
509 if err != nil {
510 return nil, nil, tracerr.Wrap(err)
511 }
512
513 batchesLen := len(rollupData.Batches)
514 if batchesLen == 0 {
515 s.stats.UpdateSync(ethBlock, nil, nil, nil)
516 } else {
517 var lastL1BatchBlock *int64
518 var lastForgeL1TxsNum *int64
519 for _, batchData := range rollupData.Batches {
520 if batchData.L1Batch {
521 lastL1BatchBlock = &batchData.Batch.EthBlockNum
522 lastForgeL1TxsNum = batchData.Batch.ForgeL1TxsNum
523 }
524 }
525 s.stats.UpdateSync(ethBlock,
526 &rollupData.Batches[batchesLen-1].Batch.BatchNum,
527 lastL1BatchBlock, lastForgeL1TxsNum)
528 }
529 var firstBatchBlockNum *int64
530 if len(rollupData.Batches) > 0 {
531 firstBatchBlockNum = &rollupData.Batches[0].Batch.EthBlockNum
532 }
533 if err := s.updateCurrentSlotIfSync(false, firstBatchBlockNum); err != nil {
534 return nil, nil, tracerr.Wrap(err)
535 }
536
537 log.Debugw("Synced block",
538 "syncLastBlockNum", s.stats.Sync.LastBlock.Num,
539 "syncBlocksPerc", s.stats.blocksPerc(),
540 "ethLastBlockNum", s.stats.Eth.LastBlock.Num,
541 )
542 for _, batchData := range rollupData.Batches {
543 log.Debugw("Synced batch",
544 "syncLastBatch", batchData.Batch.BatchNum,
545 "syncBatchesPerc", s.stats.batchesPerc(int64(batchData.Batch.BatchNum)),
546 "ethLastBatch", s.stats.Eth.LastBatch,
547 )
548 }
549
550 return &blockData, nil, nil
551}
552
553// reorg manages a reorg, updating History and State DB as needed. Keeps
554// checking previous blocks from the HistoryDB against the blockchain until a
555// block hash match is found. All future blocks in the HistoryDB and
556// corresponding batches in StateBD are discarded. Returns the last valid
557// blockNum from the HistoryDB.
558func (s *Synchronizer) reorg(uncleBlock *common.Block) (int64, error) {
559 blockNum := uncleBlock.Num
560
561 var block *common.Block
562 for blockNum >= s.startBlockNum {
563 ethBlock, err := s.ethClient.EthBlockByNumber(context.Background(), blockNum)
564 if err != nil {
565 return 0, tracerr.Wrap(fmt.Errorf("ethClient.EthBlockByNumber: %w", err))
566 }
567
568 block, err = s.historyDB.GetBlock(blockNum)
569 if err != nil {
570 return 0, tracerr.Wrap(fmt.Errorf("historyDB.GetBlock: %w", err))
571 }
572 if block.Hash == ethBlock.Hash {
573 log.Debugf("Found valid block: %v", blockNum)
574 break
575 }
576 blockNum--
577 }
578 total := uncleBlock.Num - block.Num
579 log.Debugw("Discarding blocks", "total", total, "from", uncleBlock.Num, "to", block.Num+1)
580
581 // Set History DB and State DB to the correct state
582 err := s.historyDB.Reorg(block.Num)
583 if err != nil {
584 return 0, tracerr.Wrap(err)
585 }
586
587 if err := s.resetState(block); err != nil {
588 return 0, tracerr.Wrap(err)
589 }
590
591 return block.Num, nil
592}
593
594func getInitialVariables(ethClient eth.ClientInterface,
595 consts *SCConsts) (*SCVariables, *StartBlockNums, error) {
596 rollupInit, rollupInitBlock, err := ethClient.RollupEventInit()
597 if err != nil {
598 return nil, nil, tracerr.Wrap(err)
599 }
600 auctionInit, auctionInitBlock, err := ethClient.AuctionEventInit()
601 if err != nil {
602 return nil, nil, tracerr.Wrap(err)
603 }
604 wDelayerInit, wDelayerInitBlock, err := ethClient.WDelayerEventInit()
605 if err != nil {
606 return nil, nil, tracerr.Wrap(err)
607 }
608 rollupVars := rollupInit.RollupVariables()
609 auctionVars := auctionInit.AuctionVariables(consts.Auction.InitialMinimalBidding)
610 wDelayerVars := wDelayerInit.WDelayerVariables()
611 return &SCVariables{
612 Rollup: *rollupVars,
613 Auction: *auctionVars,
614 WDelayer: *wDelayerVars,
615 }, &StartBlockNums{
616 Rollup: rollupInitBlock,
617 Auction: auctionInitBlock,
618 WDelayer: wDelayerInitBlock,
619 }, nil
620}
621
622func (s *Synchronizer) resetState(block *common.Block) error {
623 rollup, auction, wDelayer, err := s.historyDB.GetSCVars()
624 // If SCVars are not in the HistoryDB, this is probably the first run
625 // of the Synchronizer: store the initial vars taken from config
626 if tracerr.Unwrap(err) == sql.ErrNoRows {
627 vars := s.initVars
628 log.Info("Setting initial SCVars in HistoryDB")
629 if err = s.historyDB.SetInitialSCVars(&vars.Rollup, &vars.Auction, &vars.WDelayer); err != nil {
630 return tracerr.Wrap(fmt.Errorf("historyDB.SetInitialSCVars: %w", err))
631 }
632 s.vars.Rollup = *vars.Rollup.Copy()
633 s.vars.Auction = *vars.Auction.Copy()
634 s.vars.WDelayer = *vars.WDelayer.Copy()
635 // Add initial boot coordinator to HistoryDB
636 if err := s.historyDB.AddCoordinators([]common.Coordinator{{
637 Forger: s.initVars.Auction.BootCoordinator,
638 URL: s.initVars.Auction.BootCoordinatorURL,
639 EthBlockNum: s.initVars.Auction.EthBlockNum,
640 }}); err != nil {
641 return tracerr.Wrap(err)
642 }
643 } else if err != nil {
644 return tracerr.Wrap(err)
645 } else {
646 s.vars.Rollup = *rollup
647 s.vars.Auction = *auction
648 s.vars.WDelayer = *wDelayer
649 }
650
651 batchNum, err := s.historyDB.GetLastBatchNum()
652 if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
653 return tracerr.Wrap(fmt.Errorf("historyDB.GetLastBatchNum: %w", err))
654 }
655 if tracerr.Unwrap(err) == sql.ErrNoRows {
656 batchNum = 0
657 }
658
659 lastL1BatchBlockNum, err := s.historyDB.GetLastL1BatchBlockNum()
660 if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
661 return tracerr.Wrap(fmt.Errorf("historyDB.GetLastL1BatchBlockNum: %w", err))
662 }
663 if tracerr.Unwrap(err) == sql.ErrNoRows {
664 lastL1BatchBlockNum = 0
665 }
666
667 lastForgeL1TxsNum, err := s.historyDB.GetLastL1TxsNum()
668 if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
669 return tracerr.Wrap(fmt.Errorf("historyDB.GetLastL1BatchBlockNum: %w", err))
670 }
671 if tracerr.Unwrap(err) == sql.ErrNoRows || lastForgeL1TxsNum == nil {
672 n := int64(-1)
673 lastForgeL1TxsNum = &n
674 }
675
676 err = s.stateDB.Reset(batchNum)
677 if err != nil {
678 return tracerr.Wrap(fmt.Errorf("stateDB.Reset: %w", err))
679 }
680
681 s.stats.UpdateSync(block, &batchNum, &lastL1BatchBlockNum, lastForgeL1TxsNum)
682
683 if err := s.updateCurrentSlotIfSync(true, nil); err != nil {
684 return tracerr.Wrap(err)
685 }
686 return nil
687}
688
689// rollupSync retreives all the Rollup Smart Contract Data that happened at
690// ethBlock.blockNum with ethBlock.Hash.
691func (s *Synchronizer) rollupSync(ethBlock *common.Block) (*common.RollupData, error) {
692 blockNum := ethBlock.Num
693 var rollupData = common.NewRollupData()
694 // var forgeL1TxsNum int64
695
696 // Get rollup events in the block, and make sure the block hash matches
697 // the expected one.
698 rollupEvents, blockHash, err := s.ethClient.RollupEventsByBlock(blockNum)
699 if err != nil {
700 return nil, tracerr.Wrap(err)
701 }
702 // No events in this block
703 if blockHash == nil {
704 return &rollupData, nil
705 }
706 if *blockHash != ethBlock.Hash {
707 log.Errorw("Block hash mismatch in Rollup events", "expected", ethBlock.Hash.String(),
708 "got", blockHash.String())
709 return nil, tracerr.Wrap(eth.ErrBlockHashMismatchEvent)
710 }
711
712 var nextForgeL1TxsNum int64 // forgeL1TxsNum for the next L1Batch
713 nextForgeL1TxsNumPtr, err := s.historyDB.GetLastL1TxsNum()
714 if err != nil {
715 return nil, tracerr.Wrap(err)
716 }
717 if nextForgeL1TxsNumPtr != nil {
718 nextForgeL1TxsNum = *nextForgeL1TxsNumPtr + 1
719 } else {
720 nextForgeL1TxsNum = 0
721 }
722
723 // Get L1UserTX
724 rollupData.L1UserTxs, err = getL1UserTx(rollupEvents.L1UserTx, blockNum)
725 if err != nil {
726 return nil, tracerr.Wrap(err)
727 }
728
729 // Get ForgeBatch events to get the L1CoordinatorTxs
730 for _, evtForgeBatch := range rollupEvents.ForgeBatch {
731 batchData := common.NewBatchData()
732 position := 0
733
734 // Get the input for each Tx
735 forgeBatchArgs, sender, err := s.ethClient.RollupForgeBatchArgs(evtForgeBatch.EthTxHash,
736 evtForgeBatch.L1UserTxsLen)
737 if err != nil {
738 return nil, tracerr.Wrap(err)
739 }
740
741 batchNum := common.BatchNum(evtForgeBatch.BatchNum)
742 var l1UserTxs []common.L1Tx
743 // Check if this is a L1Batch to get L1 Tx from it
744 if forgeBatchArgs.L1Batch {
745 // Get L1UserTxs with toForgeL1TxsNum, which correspond
746 // to the L1UserTxs that are forged in this batch, so
747 // that stateDB can process them.
748
749 // First try to find them in HistoryDB.
750 l1UserTxs, err = s.historyDB.GetUnforgedL1UserTxs(nextForgeL1TxsNum)
751 if err != nil {
752 return nil, tracerr.Wrap(err)
753 }
754 // Apart from the DB, try to find them in this block.
755 // This could happen because in a block there could be
756 // multiple batches with L1Batch = true (although it's
757 // a very rare case). If not found in the DB and the
758 // block doesn't contain the l1UserTxs, it means that
759 // the L1UserTxs queue with toForgeL1TxsNum was closed
760 // empty, so we leave `l1UserTxs` as an empty slice.
761 for _, l1UserTx := range rollupData.L1UserTxs {
762 if *l1UserTx.ToForgeL1TxsNum == nextForgeL1TxsNum {
763 l1UserTxs = append(l1UserTxs, l1UserTx)
764 }
765 }
766
767 position = len(l1UserTxs)
768 }
769 // Get L1 Coordinator Txs
770 for i := range forgeBatchArgs.L1CoordinatorTxs {
771 l1CoordinatorTx := forgeBatchArgs.L1CoordinatorTxs[i]
772 l1CoordinatorTx.Position = position
773 // l1CoordinatorTx.ToForgeL1TxsNum = &forgeL1TxsNum
774 l1CoordinatorTx.UserOrigin = false
775 l1CoordinatorTx.EthBlockNum = blockNum
776 l1CoordinatorTx.BatchNum = &batchNum
777 l1Tx, err := common.NewL1Tx(&l1CoordinatorTx)
778 if err != nil {
779 return nil, tracerr.Wrap(err)
780 }
781
782 batchData.L1CoordinatorTxs = append(batchData.L1CoordinatorTxs, *l1Tx)
783 position++
784 // fmt.Println("DGB l1coordtx")
785 }
786
787 // Insert all the txs forged in this batch (l1UserTxs,
788 // L1CoordinatorTxs, PoolL2Txs) into stateDB so that they are
789 // processed.
790
791 // Set TxType to the forged L2Txs
792 for i := range forgeBatchArgs.L2TxsData {
793 if err := forgeBatchArgs.L2TxsData[i].SetType(); err != nil {
794 return nil, tracerr.Wrap(err)
795 }
796 }
797
798 // Transform L2 txs to PoolL2Txs
799 poolL2Txs := common.L2TxsToPoolL2Txs(forgeBatchArgs.L2TxsData) // NOTE: This is a big ugly, find a better way
800
801 if int(forgeBatchArgs.VerifierIdx) >= len(s.consts.Rollup.Verifiers) {
802 return nil, tracerr.Wrap(fmt.Errorf("forgeBatchArgs.VerifierIdx (%v) >= "+
803 " len(s.consts.Rollup.Verifiers) (%v)",
804 forgeBatchArgs.VerifierIdx, len(s.consts.Rollup.Verifiers)))
805 }
806 tpc := txprocessor.Config{
807 NLevels: uint32(s.consts.Rollup.Verifiers[forgeBatchArgs.VerifierIdx].NLevels),
808 MaxTx: uint32(s.consts.Rollup.Verifiers[forgeBatchArgs.VerifierIdx].MaxTx),
809 ChainID: s.cfg.ChainID,
810 MaxFeeTx: common.RollupConstMaxFeeIdxCoordinator,
811 MaxL1Tx: common.RollupConstMaxL1Tx,
812 }
813 tp := txprocessor.NewTxProcessor(s.stateDB, tpc)
814
815 // ProcessTxs updates poolL2Txs adding: Nonce (and also TokenID, but we don't use it).
816 processTxsOut, err := tp.ProcessTxs(forgeBatchArgs.FeeIdxCoordinator,
817 l1UserTxs, batchData.L1CoordinatorTxs, poolL2Txs)
818 if err != nil {
819 return nil, tracerr.Wrap(err)
820 }
821
822 // Transform processed PoolL2 txs to L2 and store in BatchData
823 l2Txs, err := common.PoolL2TxsToL2Txs(poolL2Txs) // NOTE: This is a big uggly, find a better way
824 if err != nil {
825 return nil, tracerr.Wrap(err)
826 }
827
828 // Set TxID, BlockNum, BatchNum and Position to the forged L2Txs
829 for i := range l2Txs {
830 if err := l2Txs[i].SetID(); err != nil {
831 return nil, tracerr.Wrap(err)
832 }
833 l2Txs[i].EthBlockNum = blockNum
834 l2Txs[i].BatchNum = batchNum
835 l2Txs[i].Position = position
836 position++
837 }
838 batchData.L2Txs = l2Txs
839
840 // Set the BatchNum in the forged L1UserTxs
841 for i := range l1UserTxs {
842 l1UserTxs[i].BatchNum = &batchNum
843 }
844 batchData.L1UserTxs = l1UserTxs
845
846 // Set batchNum in exits
847 for i := range processTxsOut.ExitInfos {
848 exit := &processTxsOut.ExitInfos[i]
849 exit.BatchNum = batchNum
850 }
851 batchData.ExitTree = processTxsOut.ExitInfos
852
853 for i := range processTxsOut.CreatedAccounts {
854 createdAccount := &processTxsOut.CreatedAccounts[i]
855 createdAccount.Nonce = 0
856 createdAccount.Balance = big.NewInt(0)
857 createdAccount.BatchNum = batchNum
858 }
859 batchData.CreatedAccounts = processTxsOut.CreatedAccounts
860
861 slotNum := int64(0)
862 if ethBlock.Num >= s.consts.Auction.GenesisBlockNum {
863 slotNum = (ethBlock.Num - s.consts.Auction.GenesisBlockNum) /
864 int64(s.consts.Auction.BlocksPerSlot)
865 }
866
867 // Get Batch information
868 batch := common.Batch{
869 BatchNum: batchNum,
870 EthBlockNum: blockNum,
871 ForgerAddr: *sender,
872 CollectedFees: processTxsOut.CollectedFees,
873 FeeIdxsCoordinator: forgeBatchArgs.FeeIdxCoordinator,
874 StateRoot: forgeBatchArgs.NewStRoot,
875 NumAccounts: len(batchData.CreatedAccounts),
876 LastIdx: forgeBatchArgs.NewLastIdx,
877 ExitRoot: forgeBatchArgs.NewExitRoot,
878 SlotNum: slotNum,
879 }
880 nextForgeL1TxsNumCpy := nextForgeL1TxsNum
881 if forgeBatchArgs.L1Batch {
882 batch.ForgeL1TxsNum = &nextForgeL1TxsNumCpy
883 batchData.L1Batch = true
884 nextForgeL1TxsNum++
885 }
886 batchData.Batch = batch
887 rollupData.Batches = append(rollupData.Batches, *batchData)
888 }
889
890 // Get Registered Tokens
891 for _, evtAddToken := range rollupEvents.AddToken {
892 var token common.Token
893
894 token.TokenID = common.TokenID(evtAddToken.TokenID)
895 token.EthAddr = evtAddToken.TokenAddress
896 token.EthBlockNum = blockNum
897
898 if consts, err := s.ethClient.EthERC20Consts(evtAddToken.TokenAddress); err != nil {
899 log.Warnw("Error retreiving ERC20 token constants", "addr", evtAddToken.TokenAddress)
900 token.Name = "ERC20_ETH_ERROR"
901 token.Symbol = "ERROR"
902 token.Decimals = 1
903 } else {
904 token.Name = cutStringMax(consts.Name, 20)
905 token.Symbol = cutStringMax(consts.Symbol, 10)
906 token.Decimals = consts.Decimals
907 }
908
909 rollupData.AddedTokens = append(rollupData.AddedTokens, token)
910 }
911
912 for _, evt := range rollupEvents.UpdateBucketWithdraw {
913 rollupData.UpdateBucketWithdraw = append(rollupData.UpdateBucketWithdraw,
914 common.BucketUpdate{
915 EthBlockNum: blockNum,
916 NumBucket: evt.NumBucket,
917 BlockStamp: evt.BlockStamp,
918 Withdrawals: evt.Withdrawals,
919 })
920 }
921
922 for _, evt := range rollupEvents.Withdraw {
923 rollupData.Withdrawals = append(rollupData.Withdrawals, common.WithdrawInfo{
924 Idx: common.Idx(evt.Idx),
925 NumExitRoot: common.BatchNum(evt.NumExitRoot),
926 InstantWithdraw: evt.InstantWithdraw,
927 TxHash: evt.TxHash,
928 })
929 }
930
931 for _, evt := range rollupEvents.UpdateTokenExchange {
932 if len(evt.AddressArray) != len(evt.ValueArray) {
933 return nil, tracerr.Wrap(fmt.Errorf("in RollupEventUpdateTokenExchange "+
934 "len(AddressArray) != len(ValueArray) (%v != %v)",
935 len(evt.AddressArray), len(evt.ValueArray)))
936 }
937 for i := range evt.AddressArray {
938 rollupData.TokenExchanges = append(rollupData.TokenExchanges,
939 common.TokenExchange{
940 EthBlockNum: blockNum,
941 Address: evt.AddressArray[i],
942 ValueUSD: int64(evt.ValueArray[i]),
943 })
944 }
945 }
946
947 varsUpdate := false
948
949 for _, evt := range rollupEvents.UpdateForgeL1L2BatchTimeout {
950 s.vars.Rollup.ForgeL1L2BatchTimeout = evt.NewForgeL1L2BatchTimeout
951 varsUpdate = true
952 }
953
954 for _, evt := range rollupEvents.UpdateFeeAddToken {
955 s.vars.Rollup.FeeAddToken = evt.NewFeeAddToken
956 varsUpdate = true
957 }
958
959 for _, evt := range rollupEvents.UpdateWithdrawalDelay {
960 s.vars.Rollup.WithdrawalDelay = evt.NewWithdrawalDelay
961 varsUpdate = true
962 }
963
964 // NOTE: We skip the event rollupEvents.SafeMode because the
965 // implementation RollupEventsByBlock already inserts a non-existing
966 // RollupEventUpdateBucketsParameters into UpdateBucketsParameters with
967 // all the bucket values at 0 and SafeMode = true
968
969 for _, evt := range rollupEvents.UpdateBucketsParameters {
970 for i, bucket := range evt.ArrayBuckets {
971 s.vars.Rollup.Buckets[i] = common.BucketParams{
972 CeilUSD: bucket.CeilUSD,
973 Withdrawals: bucket.Withdrawals,
974 BlockWithdrawalRate: bucket.BlockWithdrawalRate,
975 MaxWithdrawals: bucket.MaxWithdrawals,
976 }
977 }
978 s.vars.Rollup.SafeMode = evt.SafeMode
979 varsUpdate = true
980 }
981
982 if varsUpdate {
983 s.vars.Rollup.EthBlockNum = blockNum
984 rollupData.Vars = s.vars.Rollup.Copy()
985 }
986
987 return &rollupData, nil
988}
989
990func cutStringMax(s string, max int) string {
991 if len(s) > max {
992 return s[:max]
993 }
994 return s
995}
996
997// auctionSync gets information from the Auction Contract
998func (s *Synchronizer) auctionSync(ethBlock *common.Block) (*common.AuctionData, error) {
999 blockNum := ethBlock.Num
1000 var auctionData = common.NewAuctionData()
1001
1002 // Get auction events in the block
1003 auctionEvents, blockHash, err := s.ethClient.AuctionEventsByBlock(blockNum)
1004 if err != nil {
1005 return nil, tracerr.Wrap(err)
1006 }
1007 // No events in this block
1008 if blockHash == nil {
1009 return &auctionData, nil
1010 }
1011 if *blockHash != ethBlock.Hash {
1012 log.Errorw("Block hash mismatch in Auction events", "expected", ethBlock.Hash.String(),
1013 "got", blockHash.String())
1014 return nil, tracerr.Wrap(eth.ErrBlockHashMismatchEvent)
1015 }
1016
1017 // Get bids
1018 for _, evt := range auctionEvents.NewBid {
1019 bid := common.Bid{
1020 SlotNum: evt.Slot,
1021 BidValue: evt.BidAmount,
1022 Bidder: evt.Bidder,
1023 EthBlockNum: blockNum,
1024 }
1025 auctionData.Bids = append(auctionData.Bids, bid)
1026 }
1027
1028 // Get Coordinators
1029 for _, evt := range auctionEvents.SetCoordinator {
1030 coordinator := common.Coordinator{
1031 Bidder: evt.BidderAddress,
1032 Forger: evt.ForgerAddress,
1033 URL: evt.CoordinatorURL,
1034 EthBlockNum: blockNum,
1035 }
1036 auctionData.Coordinators = append(auctionData.Coordinators, coordinator)
1037 }
1038
1039 varsUpdate := false
1040
1041 for _, evt := range auctionEvents.NewSlotDeadline {
1042 s.vars.Auction.SlotDeadline = evt.NewSlotDeadline
1043 varsUpdate = true
1044 }
1045 for _, evt := range auctionEvents.NewClosedAuctionSlots {
1046 s.vars.Auction.ClosedAuctionSlots = evt.NewClosedAuctionSlots
1047 varsUpdate = true
1048 }
1049 for _, evt := range auctionEvents.NewOutbidding {
1050 s.vars.Auction.Outbidding = evt.NewOutbidding
1051 varsUpdate = true
1052 }
1053 for _, evt := range auctionEvents.NewDonationAddress {
1054 s.vars.Auction.DonationAddress = evt.NewDonationAddress
1055 varsUpdate = true
1056 }
1057 for _, evt := range auctionEvents.NewBootCoordinator {
1058 s.vars.Auction.BootCoordinator = evt.NewBootCoordinator
1059 s.vars.Auction.BootCoordinatorURL = evt.NewBootCoordinatorURL
1060 varsUpdate = true
1061 // Add new boot coordinator
1062 auctionData.Coordinators = append(auctionData.Coordinators, common.Coordinator{
1063 Forger: evt.NewBootCoordinator,
1064 URL: evt.NewBootCoordinatorURL,
1065 EthBlockNum: blockNum,
1066 })
1067 }
1068 for _, evt := range auctionEvents.NewOpenAuctionSlots {
1069 s.vars.Auction.OpenAuctionSlots = evt.NewOpenAuctionSlots
1070 varsUpdate = true
1071 }
1072 for _, evt := range auctionEvents.NewAllocationRatio {
1073 s.vars.Auction.AllocationRatio = evt.NewAllocationRatio
1074 varsUpdate = true
1075 }
1076 for _, evt := range auctionEvents.NewDefaultSlotSetBid {
1077 if evt.SlotSet > 6 { //nolint:gomnd
1078 return nil, tracerr.Wrap(fmt.Errorf("unexpected SlotSet in "+
1079 "auctionEvents.NewDefaultSlotSetBid: %v", evt.SlotSet))
1080 }
1081 s.vars.Auction.DefaultSlotSetBid[evt.SlotSet] = evt.NewInitialMinBid
1082 s.vars.Auction.DefaultSlotSetBidSlotNum = s.consts.Auction.SlotNum(blockNum) +
1083 int64(s.vars.Auction.ClosedAuctionSlots) + 1
1084 varsUpdate = true
1085 }
1086
1087 // NOTE: We ignore NewForgeAllocated
1088 // NOTE: We ignore NewForge because we're already tracking ForgeBatch event from Rollup
1089 // NOTE: We ignore HEZClaimed
1090
1091 if varsUpdate {
1092 s.vars.Auction.EthBlockNum = blockNum
1093 auctionData.Vars = s.vars.Auction.Copy()
1094 }
1095
1096 return &auctionData, nil
1097}
1098
1099// wdelayerSync gets information from the Withdrawal Delayer Contract
1100func (s *Synchronizer) wdelayerSync(ethBlock *common.Block) (*common.WDelayerData, error) {
1101 blockNum := ethBlock.Num
1102 wDelayerData := common.NewWDelayerData()
1103
1104 // Get wDelayer events in the block
1105 wDelayerEvents, blockHash, err := s.ethClient.WDelayerEventsByBlock(blockNum)
1106 if err != nil {
1107 return nil, tracerr.Wrap(err)
1108 }
1109 // No events in this block
1110 if blockHash == nil {
1111 return &wDelayerData, nil
1112 }
1113 if *blockHash != ethBlock.Hash {
1114 log.Errorw("Block hash mismatch in WDelayer events", "expected", ethBlock.Hash.String(),
1115 "got", blockHash.String())
1116 return nil, tracerr.Wrap(eth.ErrBlockHashMismatchEvent)
1117 }
1118
1119 for _, evt := range wDelayerEvents.Deposit {
1120 wDelayerData.Deposits = append(wDelayerData.Deposits, common.WDelayerTransfer{
1121 Owner: evt.Owner,
1122 Token: evt.Token,
1123 Amount: evt.Amount,
1124 })
1125 wDelayerData.DepositsByTxHash[evt.TxHash] =
1126 append(wDelayerData.DepositsByTxHash[evt.TxHash],
1127 &wDelayerData.Deposits[len(wDelayerData.Deposits)-1])
1128 }
1129 for _, evt := range wDelayerEvents.Withdraw {
1130 wDelayerData.Withdrawals = append(wDelayerData.Withdrawals, common.WDelayerTransfer{
1131 Owner: evt.Owner,
1132 Token: evt.Token,
1133 Amount: evt.Amount,
1134 })
1135 }
1136 for _, evt := range wDelayerEvents.EscapeHatchWithdrawal {
1137 wDelayerData.EscapeHatchWithdrawals = append(wDelayerData.EscapeHatchWithdrawals,
1138 common.WDelayerEscapeHatchWithdrawal{
1139 EthBlockNum: blockNum,
1140 Who: evt.Who,
1141 To: evt.To,
1142 TokenAddr: evt.Token,
1143 Amount: evt.Amount,
1144 })
1145 }
1146
1147 varsUpdate := false
1148
1149 for range wDelayerEvents.EmergencyModeEnabled {
1150 s.vars.WDelayer.EmergencyMode = true
1151 s.vars.WDelayer.EmergencyModeStartingBlock = blockNum
1152 varsUpdate = true
1153 }
1154 for _, evt := range wDelayerEvents.NewWithdrawalDelay {
1155 s.vars.WDelayer.WithdrawalDelay = evt.WithdrawalDelay
1156 varsUpdate = true
1157 }
1158 for _, evt := range wDelayerEvents.NewEmergencyCouncil {
1159 s.vars.WDelayer.EmergencyCouncilAddress = evt.NewEmergencyCouncil
1160 varsUpdate = true
1161 }
1162 for _, evt := range wDelayerEvents.NewHermezGovernanceAddress {
1163 s.vars.WDelayer.HermezGovernanceAddress = evt.NewHermezGovernanceAddress
1164 varsUpdate = true
1165 }
1166
1167 if varsUpdate {
1168 s.vars.WDelayer.EthBlockNum = blockNum
1169 wDelayerData.Vars = s.vars.WDelayer.Copy()
1170 }
1171
1172 return &wDelayerData, nil
1173}
1174
1175func getL1UserTx(eventsL1UserTx []eth.RollupEventL1UserTx, blockNum int64) ([]common.L1Tx, error) {
1176 l1Txs := make([]common.L1Tx, len(eventsL1UserTx))
1177 for i := range eventsL1UserTx {
1178 eventsL1UserTx[i].L1UserTx.EthBlockNum = blockNum
1179 // Check validity of L1UserTx
1180 l1Tx, err := common.NewL1Tx(&eventsL1UserTx[i].L1UserTx)
1181 if err != nil {
1182 return nil, tracerr.Wrap(err)
1183 }
1184 l1Txs[i] = *l1Tx
1185 }
1186 return l1Txs, nil
1187}