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