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