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