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