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