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