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