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)
18
19var (
20// ErrNotAbleToSync is used when there is not possible to find a valid block to sync
21// ErrNotAbleToSync = errors.New("it has not been possible to synchronize any block")
22)
23
24// // SyncronizerState describes the synchronization progress of the smart contracts
25// type SyncronizerState struct {
26// LastUpdate time.Time // last time this information was updated
27// CurrentBatchNum BatchNum // Last batch that was forged on the blockchain
28// CurrentBlockNum uint64 // Last block that was mined on Ethereum
29// CurrentToForgeL1TxsNum uint32
30// LastSyncedBatchNum BatchNum // last batch synchronized by the coordinator
31// LastSyncedBlockNum uint64 // last Ethereum block synchronized by the coordinator
32// LastSyncedToForgeL1TxsNum uint32
33// }
34
35// // SyncStatus is returned by the Status method of the Synchronizer
36// type SyncStatus struct {
37// CurrentBlock int64
38// CurrentBatch BatchNum
39// CurrentForgerAddr ethCommon.Address
40// NextForgerAddr ethCommon.Address
41// Synchronized bool
42// }
43
44// Stats of the syncrhonizer
45type Stats struct {
46 Eth struct {
47 RefreshPeriod time.Duration
48 Updated time.Time
49 FirstBlock int64
50 LastBlock int64
51 LastBatch int64
52 }
53 Sync struct {
54 Updated time.Time
55 LastBlock int64
56 LastBatch int64
57 Auction struct {
58 CurrentSlot common.Slot
59 }
60 }
61}
62
63// Synced returns true if the Synchronizer is up to date with the last ethereum block
64func (s *Stats) Synced() bool {
65 return s.Eth.LastBlock == s.Sync.LastBlock
66}
67
68// TODO(Edu): Consider removing all the mutexes from StatsHolder, make
69// Synchronizer.Stats not thread-safe, don't pass the synchronizer to the
70// debugAPI, and have a copy of the Stats in the DebugAPI that the node passes
71// when the Sync updates.
72
73// StatsHolder stores stats and that allows reading and writing them
74// concurrently
75type StatsHolder struct {
76 Stats
77 rw sync.RWMutex
78}
79
80// NewStatsHolder creates a new StatsHolder
81func NewStatsHolder(firstBlock int64, refreshPeriod time.Duration) *StatsHolder {
82 stats := Stats{}
83 stats.Eth.RefreshPeriod = refreshPeriod
84 stats.Eth.FirstBlock = firstBlock
85 return &StatsHolder{Stats: stats}
86}
87
88// UpdateCurrentSlot updates the auction stats
89func (s *StatsHolder) UpdateCurrentSlot(slot common.Slot) {
90 s.rw.Lock()
91 s.Sync.Auction.CurrentSlot = slot
92 s.rw.Unlock()
93}
94
95// UpdateSync updates the synchronizer stats
96func (s *StatsHolder) UpdateSync(lastBlock int64, lastBatch *common.BatchNum) {
97 now := time.Now()
98 s.rw.Lock()
99 s.Sync.LastBlock = lastBlock
100 if lastBatch != nil {
101 s.Sync.LastBatch = int64(*lastBatch)
102 }
103 s.Sync.Updated = now
104 s.rw.Unlock()
105}
106
107// UpdateEth updates the ethereum stats, only if the previous stats expired
108func (s *StatsHolder) UpdateEth(ethClient eth.ClientInterface) error {
109 now := time.Now()
110 s.rw.RLock()
111 elapsed := now.Sub(s.Eth.Updated)
112 s.rw.RUnlock()
113 if elapsed < s.Eth.RefreshPeriod {
114 return nil
115 }
116
117 lastBlock, err := ethClient.EthLastBlock()
118 if err != nil {
119 return err
120 }
121 lastBatch, err := ethClient.RollupLastForgedBatch()
122 if err != nil {
123 return err
124 }
125 s.rw.Lock()
126 s.Eth.Updated = now
127 s.Eth.LastBlock = lastBlock
128 s.Eth.LastBatch = lastBatch
129 s.rw.Unlock()
130 return nil
131}
132
133// CopyStats returns a copy of the inner Stats
134func (s *StatsHolder) CopyStats() *Stats {
135 s.rw.RLock()
136 sCopy := s.Stats
137 if s.Sync.Auction.CurrentSlot.BidValue != nil {
138 sCopy.Sync.Auction.CurrentSlot.BidValue =
139 common.CopyBigInt(s.Sync.Auction.CurrentSlot.BidValue)
140 }
141 s.rw.RUnlock()
142 return &sCopy
143}
144
145func (s *StatsHolder) blocksPerc() float64 {
146 syncLastBlock := s.Sync.LastBlock
147 if s.Sync.LastBlock == 0 {
148 syncLastBlock = s.Eth.FirstBlock - 1
149 }
150 return float64(syncLastBlock-(s.Eth.FirstBlock-1)) * 100.0 /
151 float64(s.Eth.LastBlock-(s.Eth.FirstBlock-1))
152}
153
154func (s *StatsHolder) batchesPerc(batchNum int64) float64 {
155 return float64(batchNum) * 100.0 /
156 float64(s.Eth.LastBatch)
157}
158
159// ConfigStartBlockNum sets the first block used to start tracking the smart
160// contracts
161type ConfigStartBlockNum struct {
162 Rollup int64 `validate:"required"`
163 Auction int64 `validate:"required"`
164 WDelayer int64 `validate:"required"`
165}
166
167// SCVariables joins all the smart contract variables in a single struct
168type SCVariables struct {
169 Rollup common.RollupVariables `validate:"required"`
170 Auction common.AuctionVariables `validate:"required"`
171 WDelayer common.WDelayerVariables `validate:"required"`
172}
173
174// SCConsts joins all the smart contract constants in a single struct
175type SCConsts struct {
176 Rollup common.RollupConstants
177 Auction common.AuctionConstants
178 WDelayer common.WDelayerConstants
179}
180
181// Config is the Synchronizer configuration
182type Config struct {
183 StartBlockNum ConfigStartBlockNum
184 InitialVariables SCVariables
185 StatsRefreshPeriod time.Duration
186}
187
188// Synchronizer implements the Synchronizer type
189type Synchronizer struct {
190 ethClient eth.ClientInterface
191 // auctionConstants common.AuctionConstants
192 // rollupConstants common.RollupConstants
193 // wDelayerConstants common.WDelayerConstants
194 consts SCConsts
195 historyDB *historydb.HistoryDB
196 stateDB *statedb.StateDB
197 cfg Config
198 startBlockNum int64
199 vars SCVariables
200 stats *StatsHolder
201 // firstSavedBlock *common.Block
202 // mux sync.Mutex
203}
204
205// NewSynchronizer creates a new Synchronizer
206func NewSynchronizer(ethClient eth.ClientInterface, historyDB *historydb.HistoryDB,
207 stateDB *statedb.StateDB, cfg Config) (*Synchronizer, error) {
208 auctionConstants, err := ethClient.AuctionConstants()
209 if err != nil {
210 log.Errorw("NewSynchronizer ethClient.AuctionConstants()", "err", err)
211 return nil, err
212 }
213 rollupConstants, err := ethClient.RollupConstants()
214 if err != nil {
215 log.Errorw("NewSynchronizer ethClient.RollupConstants()", "err", err)
216 return nil, err
217 }
218 wDelayerConstants, err := ethClient.WDelayerConstants()
219 if err != nil {
220 log.Errorw("NewSynchronizer ethClient.WDelayerConstants()", "err", err)
221 return nil, err
222 }
223
224 // Set startBlockNum to the minimum between Auction, Rollup and
225 // WDelayer StartBlockNum
226 startBlockNum := cfg.StartBlockNum.Auction
227 if startBlockNum < cfg.StartBlockNum.Rollup {
228 startBlockNum = cfg.StartBlockNum.Rollup
229 }
230 if startBlockNum < cfg.StartBlockNum.WDelayer {
231 startBlockNum = cfg.StartBlockNum.WDelayer
232 }
233 stats := NewStatsHolder(startBlockNum, cfg.StatsRefreshPeriod)
234 s := &Synchronizer{
235 ethClient: ethClient,
236 consts: SCConsts{
237 Rollup: *rollupConstants,
238 Auction: *auctionConstants,
239 WDelayer: *wDelayerConstants,
240 },
241 historyDB: historyDB,
242 stateDB: stateDB,
243 cfg: cfg,
244 startBlockNum: startBlockNum,
245 stats: stats,
246 }
247 return s, s.init()
248}
249
250// Stats returns a copy of the Synchronizer Stats. It is safe to call Stats()
251// during a Sync call
252func (s *Synchronizer) Stats() *Stats {
253 return s.stats.CopyStats()
254}
255
256// AuctionConstants returns the AuctionConstants read from the smart contract
257func (s *Synchronizer) AuctionConstants() *common.AuctionConstants {
258 return &s.consts.Auction
259}
260
261// RollupConstants returns the RollupConstants read from the smart contract
262func (s *Synchronizer) RollupConstants() *common.RollupConstants {
263 return &s.consts.Rollup
264}
265
266// WDelayerConstants returns the WDelayerConstants read from the smart contract
267func (s *Synchronizer) WDelayerConstants() *common.WDelayerConstants {
268 return &s.consts.WDelayer
269}
270
271// SCVars returns a copy of the Smart Contract Variables
272func (s *Synchronizer) SCVars() (*common.RollupVariables, *common.AuctionVariables, *common.WDelayerVariables) {
273 return s.vars.Rollup.Copy(), s.vars.Auction.Copy(), s.vars.WDelayer.Copy()
274}
275
276func (s *Synchronizer) updateCurrentSlotIfSync(batchesLen int) error {
277 slot := common.Slot{
278 SlotNum: s.stats.Sync.Auction.CurrentSlot.SlotNum,
279 BatchesLen: int(s.stats.Sync.Auction.CurrentSlot.BatchesLen),
280 }
281 // We want the next block because the current one is already mined
282 blockNum := s.stats.Sync.LastBlock + 1
283 slotNum := s.consts.Auction.SlotNum(blockNum)
284 if batchesLen == -1 {
285 dbBatchesLen, err := s.historyDB.GetBatchesLen(slotNum)
286 // fmt.Printf("DBG -1 from: %v, to: %v, len: %v\n", from, to, dbBatchesLen)
287 if err != nil {
288 log.Errorw("historyDB.GetBatchesLen", "err", err)
289 return err
290 }
291 slot.BatchesLen = dbBatchesLen
292 } else if slotNum > slot.SlotNum {
293 // fmt.Printf("DBG batchesLen Reset len: %v (%v %v)\n", batchesLen, slotNum, slot.SlotNum)
294 slot.BatchesLen = batchesLen
295 } else {
296 // fmt.Printf("DBG batchesLen add len: %v: %v\n", batchesLen, slot.BatchesLen+batchesLen)
297 slot.BatchesLen += batchesLen
298 }
299 slot.SlotNum = slotNum
300 slot.StartBlock, slot.EndBlock = s.consts.Auction.SlotBlocks(slot.SlotNum)
301 // If Synced, update the current coordinator
302 if s.stats.Synced() {
303 bidCoord, err := s.historyDB.GetBestBidCoordinator(slot.SlotNum)
304 if err != nil && err != sql.ErrNoRows {
305 return err
306 }
307 if err == sql.ErrNoRows {
308 slot.BootCoord = true
309 slot.Forger = s.vars.Auction.BootCoordinator
310 slot.URL = "???"
311 } else if err == nil {
312 slot.BidValue = bidCoord.BidValue
313 defaultSlotBid := bidCoord.DefaultSlotSetBid[slot.SlotNum%6]
314 if slot.BidValue.Cmp(defaultSlotBid) >= 0 {
315 slot.Bidder = bidCoord.Bidder
316 slot.Forger = bidCoord.Forger
317 slot.URL = bidCoord.URL
318 } else {
319 slot.BootCoord = true
320 slot.Forger = s.vars.Auction.BootCoordinator
321 slot.URL = "???"
322 }
323 }
324
325 // TODO: Remove this SANITY CHECK once this code is tested enough
326 // BEGIN SANITY CHECK
327 canForge, err := s.ethClient.AuctionCanForge(slot.Forger, blockNum)
328 if err != nil {
329 return err
330 }
331 if !canForge {
332 return fmt.Errorf("Synchronized value of forger address for closed slot "+
333 "differs from smart contract: %+v", slot)
334 }
335 // END SANITY CHECK
336 }
337 s.stats.UpdateCurrentSlot(slot)
338 return nil
339}
340
341func (s *Synchronizer) init() error {
342 // Update stats parameters so that they have valid values before the
343 // first Sync call
344 if err := s.stats.UpdateEth(s.ethClient); err != nil {
345 return err
346 }
347 var lastBlockNum int64
348 lastSavedBlock, err := s.historyDB.GetLastBlock()
349 if err != nil && err != sql.ErrNoRows {
350 return err
351 }
352 // If there's no block in the DB (or we only have the default block 0),
353 // make sure that the stateDB is clean
354 if err == sql.ErrNoRows || lastSavedBlock.EthBlockNum == 0 {
355 if err := s.stateDB.Reset(0); err != nil {
356 return err
357 }
358 } else {
359 lastBlockNum = lastSavedBlock.EthBlockNum
360 }
361 if err := s.resetState(lastBlockNum); err != nil {
362 return err
363 }
364
365 log.Infow("Sync init block",
366 "syncLastBlock", s.stats.Sync.LastBlock,
367 "syncBlocksPerc", s.stats.blocksPerc(),
368 "ethFirstBlock", s.stats.Eth.FirstBlock,
369 "ethLastBlock", s.stats.Eth.LastBlock,
370 )
371 log.Infow("Sync init batch",
372 "syncLastBatch", s.stats.Sync.LastBatch,
373 "syncBatchesPerc", s.stats.batchesPerc(s.stats.Sync.LastBatch),
374 "ethLastBatch", s.stats.Eth.LastBatch,
375 )
376 return nil
377}
378
379// Sync2 attems to synchronize an ethereum block starting from lastSavedBlock.
380// If lastSavedBlock is nil, the lastSavedBlock value is obtained from de DB.
381// If a block is synched, it will be returned and also stored in the DB. If a
382// reorg is detected, the number of discarded blocks will be returned and no
383// synchronization will be made.
384// TODO: Be smart about locking: only lock during the read/write operations
385func (s *Synchronizer) Sync2(ctx context.Context, lastSavedBlock *common.Block) (*common.BlockData, *int64, error) {
386 var nextBlockNum int64 // next block number to sync
387 if lastSavedBlock == nil {
388 var err error
389 // Get lastSavedBlock from History DB
390 lastSavedBlock, err = s.historyDB.GetLastBlock()
391 if err != nil && err != sql.ErrNoRows {
392 return nil, nil, err
393 }
394 // If we don't have any stored block, we must do a full sync
395 // starting from the startBlockNum
396 if err == sql.ErrNoRows || lastSavedBlock.EthBlockNum == 0 {
397 nextBlockNum = s.startBlockNum
398 lastSavedBlock = nil
399 }
400 }
401 if lastSavedBlock != nil {
402 nextBlockNum = lastSavedBlock.EthBlockNum + 1
403 }
404
405 ethBlock, err := s.ethClient.EthBlockByNumber(ctx, nextBlockNum)
406 if err == ethereum.NotFound {
407 return nil, nil, nil
408 } else if err != nil {
409 return nil, nil, err
410 }
411 log.Debugf("ethBlock: num: %v, parent: %v, hash: %v", ethBlock.EthBlockNum, ethBlock.ParentHash.String(), ethBlock.Hash.String())
412
413 if err := s.stats.UpdateEth(s.ethClient); err != nil {
414 return nil, nil, err
415 }
416
417 log.Debugw("Syncing...",
418 "block", nextBlockNum,
419 "ethLastBlock", s.stats.Eth.LastBlock,
420 )
421
422 // Check that the obtianed ethBlock.ParentHash == prevEthBlock.Hash; if not, reorg!
423 if lastSavedBlock != nil {
424 if lastSavedBlock.Hash != ethBlock.ParentHash {
425 // Reorg detected
426 log.Debugw("Reorg Detected",
427 "blockNum", ethBlock.EthBlockNum,
428 "block.parent(got)", ethBlock.ParentHash, "parent.hash(exp)", lastSavedBlock.Hash)
429 lastDBBlockNum, err := s.reorg(lastSavedBlock)
430 if err != nil {
431 return nil, nil, err
432 }
433 discarded := lastSavedBlock.EthBlockNum - lastDBBlockNum
434 return nil, &discarded, nil
435 }
436 }
437
438 // Get data from the rollup contract
439 rollupData, err := s.rollupSync(ethBlock)
440 if err != nil {
441 return nil, nil, err
442 }
443
444 // Get data from the auction contract
445 auctionData, err := s.auctionSync(ethBlock)
446 if err != nil {
447 return nil, nil, err
448 }
449
450 // Get data from the WithdrawalDelayer contract
451 wDelayerData, err := s.wdelayerSync(ethBlock)
452 if err != nil {
453 return nil, nil, err
454 }
455
456 for i := range rollupData.Withdrawals {
457 withdrawal := &rollupData.Withdrawals[i]
458 if !withdrawal.InstantWithdraw {
459 wDelayerTransfer, ok := wDelayerData.DepositsByTxHash[withdrawal.TxHash]
460 if !ok {
461 return nil, nil, fmt.Errorf("WDelayer deposit corresponding to " +
462 "non-instant rollup withdrawal not found")
463 }
464 withdrawal.Owner = wDelayerTransfer.Owner
465 withdrawal.Token = wDelayerTransfer.Token
466 }
467 }
468
469 // Group all the block data into the structs to save into HistoryDB
470 blockData := common.BlockData{
471 Block: *ethBlock,
472 Rollup: *rollupData,
473 Auction: *auctionData,
474 WDelayer: *wDelayerData,
475 }
476
477 // log.Debugw("Sync()", "block", blockData)
478 // err = s.historyDB.AddBlock(blockData.Block)
479 // if err != nil {
480 // return err
481 // }
482 err = s.historyDB.AddBlockSCData(&blockData)
483 if err != nil {
484 return nil, nil, err
485 }
486
487 batchesLen := len(rollupData.Batches)
488 if batchesLen == 0 {
489 s.stats.UpdateSync(ethBlock.EthBlockNum, nil)
490 } else {
491 s.stats.UpdateSync(ethBlock.EthBlockNum,
492 &rollupData.Batches[batchesLen-1].Batch.BatchNum)
493 }
494 if err := s.updateCurrentSlotIfSync(len(rollupData.Batches)); err != nil {
495 return nil, nil, err
496 }
497 log.Debugw("Synced block",
498 "syncLastBlock", s.stats.Sync.LastBlock,
499 "syncBlocksPerc", s.stats.blocksPerc(),
500 "ethLastBlock", s.stats.Eth.LastBlock,
501 )
502 for _, batchData := range rollupData.Batches {
503 log.Debugw("Synced batch",
504 "syncLastBatch", batchData.Batch.BatchNum,
505 "syncBatchesPerc", s.stats.batchesPerc(int64(batchData.Batch.BatchNum)),
506 "ethLastBatch", s.stats.Eth.LastBatch,
507 )
508 }
509
510 return &blockData, nil, nil
511}
512
513// reorg manages a reorg, updating History and State DB as needed. Keeps
514// checking previous blocks from the HistoryDB against the blockchain until a
515// block hash match is found. All future blocks in the HistoryDB and
516// corresponding batches in StateBD are discarded. Returns the last valid
517// blockNum from the HistoryDB.
518func (s *Synchronizer) reorg(uncleBlock *common.Block) (int64, error) {
519 blockNum := uncleBlock.EthBlockNum
520
521 for blockNum >= s.startBlockNum {
522 ethBlock, err := s.ethClient.EthBlockByNumber(context.Background(), blockNum)
523 if err != nil {
524 log.Errorw("ethClient.EthBlockByNumber", "err", err)
525 return 0, err
526 }
527
528 block, err := s.historyDB.GetBlock(blockNum)
529 if err != nil {
530 log.Errorw("historyDB.GetBlock", "err", err)
531 return 0, err
532 }
533 if block.Hash == ethBlock.Hash {
534 log.Debugf("Found valid block: %v", blockNum)
535 break
536 }
537 blockNum--
538 }
539 total := uncleBlock.EthBlockNum - blockNum
540 log.Debugw("Discarding blocks", "total", total, "from", uncleBlock.EthBlockNum, "to", blockNum+1)
541
542 // Set History DB and State DB to the correct state
543 err := s.historyDB.Reorg(blockNum)
544 if err != nil {
545 return 0, err
546 }
547
548 if err := s.resetState(blockNum); err != nil {
549 return 0, err
550 }
551
552 return blockNum, nil
553}
554
555func (s *Synchronizer) resetState(blockNum int64) error {
556 rollup, auction, wDelayer, err := s.historyDB.GetSCVars()
557 // If SCVars are not in the HistoryDB, this is probably the first run
558 // of the Synchronizer: store the initial vars taken from config
559 if err == sql.ErrNoRows {
560 rollup = &s.cfg.InitialVariables.Rollup
561 auction = &s.cfg.InitialVariables.Auction
562 wDelayer = &s.cfg.InitialVariables.WDelayer
563 log.Info("Setting initial SCVars in HistoryDB")
564 if err = s.historyDB.SetInitialSCVars(rollup, auction, wDelayer); err != nil {
565 log.Errorw("historyDB.SetInitialSCVars", "err", err)
566 return err
567 }
568 }
569 s.vars.Rollup = *rollup
570 s.vars.Auction = *auction
571 s.vars.WDelayer = *wDelayer
572
573 batchNum, err := s.historyDB.GetLastBatchNum()
574 if err != nil && err != sql.ErrNoRows {
575 log.Errorw("historyDB.GetLastBatchNum", "err", err)
576 return err
577 }
578 if err == sql.ErrNoRows {
579 batchNum = 0
580 }
581 err = s.stateDB.Reset(batchNum)
582 if err != nil {
583 log.Errorw("stateDB.Reset", "err", err)
584 return err
585 }
586
587 s.stats.UpdateSync(blockNum, &batchNum)
588
589 if err := s.updateCurrentSlotIfSync(-1); err != nil {
590 return err
591 }
592 return nil
593}
594
595// TODO: Figure out who will use the Status output, and only return what's strictly need
596/*
597// Status returns current status values from the Synchronizer
598func (s *Synchronizer) Status() (*common.SyncStatus, error) {
599 // Avoid possible inconsistencies
600 s.mux.Lock()
601 defer s.mux.Unlock()
602
603 var status *common.SyncStatus
604
605 // TODO: Join all queries to the DB into a single transaction so that
606 // we can remove the mutex locking here:
607 // - HistoryDB.GetLastBlock
608 // - HistoryDB.GetLastBatchNum
609 // - HistoryDB.GetCurrentForgerAddr
610 // - HistoryDB.GetNextForgerAddr
611
612 // Get latest block in History DB
613 lastSavedBlock, err := s.historyDB.GetLastBlock()
614 if err != nil {
615 return nil, err
616 }
617 status.CurrentBlock = lastSavedBlock.EthBlockNum
618
619 // Get latest batch in History DB
620 lastSavedBatch, err := s.historyDB.GetLastBatchNum()
621 if err != nil && err != sql.ErrNoRows {
622 return nil, err
623 }
624 status.CurrentBatch = lastSavedBatch
625
626 // Get latest blockNum in blockchain
627 latestBlockNum, err := s.ethClient.EthLastBlock()
628 if err != nil {
629 return nil, err
630 }
631
632 // TODO: Get CurrentForgerAddr & NextForgerAddr from the Auction SC / Or from the HistoryDB
633
634 // Check if Synchronizer is synchronized
635 status.Synchronized = status.CurrentBlock == latestBlockNum
636 return status, nil
637}
638*/
639
640// rollupSync retreives all the Rollup Smart Contract Data that happened at
641// ethBlock.blockNum with ethBlock.Hash.
642func (s *Synchronizer) rollupSync(ethBlock *common.Block) (*common.RollupData, error) {
643 blockNum := ethBlock.EthBlockNum
644 var rollupData = common.NewRollupData()
645 // var forgeL1TxsNum int64
646
647 // Get rollup events in the block, and make sure the block hash matches
648 // the expected one.
649 rollupEvents, blockHash, err := s.ethClient.RollupEventsByBlock(blockNum)
650 if err != nil {
651 return nil, err
652 }
653 // No events in this block
654 if blockHash == nil {
655 return &rollupData, nil
656 }
657 if *blockHash != ethBlock.Hash {
658 log.Errorw("Block hash mismatch in Rollup events", "expected", ethBlock.Hash.String(),
659 "got", blockHash.String())
660 return nil, eth.ErrBlockHashMismatchEvent
661 }
662
663 var nextForgeL1TxsNum int64 // forgeL1TxsNum for the next L1Batch
664 nextForgeL1TxsNumPtr, err := s.historyDB.GetLastL1TxsNum()
665 if err != nil {
666 return nil, err
667 }
668 if nextForgeL1TxsNumPtr != nil {
669 nextForgeL1TxsNum = *nextForgeL1TxsNumPtr + 1
670 } else {
671 nextForgeL1TxsNum = 0
672 }
673
674 // Get L1UserTX
675 rollupData.L1UserTxs, err = getL1UserTx(rollupEvents.L1UserTx, blockNum)
676 if err != nil {
677 return nil, err
678 }
679
680 // Get ForgeBatch events to get the L1CoordinatorTxs
681 for _, evtForgeBatch := range rollupEvents.ForgeBatch {
682 batchData := common.NewBatchData()
683 position := 0
684
685 // Get the input for each Tx
686 forgeBatchArgs, sender, err := s.ethClient.RollupForgeBatchArgs(evtForgeBatch.EthTxHash)
687 if err != nil {
688 return nil, err
689 }
690
691 batchNum := common.BatchNum(evtForgeBatch.BatchNum)
692 var l1UserTxs []common.L1Tx
693 // Check if this is a L1Batch to get L1 Tx from it
694 if forgeBatchArgs.L1Batch {
695 // Get L1UserTxs with toForgeL1TxsNum, which correspond
696 // to the L1UserTxs that are forged in this batch, so
697 // that stateDB can process them.
698
699 // First try to find them in HistoryDB.
700 l1UserTxs, err = s.historyDB.GetL1UserTxs(nextForgeL1TxsNum)
701 if err != nil {
702 return nil, err
703 }
704 // Apart from the DB, try to find them in this block.
705 // This could happen because in a block there could be
706 // multiple batches with L1Batch = true (although it's
707 // a very rare case). If not found in the DB and the
708 // block doesn't contain the l1UserTxs, it means that
709 // the L1UserTxs queue with toForgeL1TxsNum was closed
710 // empty, so we leave `l1UserTxs` as an empty slice.
711 for _, l1UserTx := range rollupData.L1UserTxs {
712 if *l1UserTx.ToForgeL1TxsNum == nextForgeL1TxsNum {
713 l1UserTxs = append(l1UserTxs, l1UserTx)
714 }
715 }
716
717 position = len(l1UserTxs)
718 }
719 // Get L1 Coordinator Txs
720 for i := range forgeBatchArgs.L1CoordinatorTxs {
721 l1CoordinatorTx := forgeBatchArgs.L1CoordinatorTxs[i]
722 l1CoordinatorTx.Position = position
723 // l1CoordinatorTx.ToForgeL1TxsNum = &forgeL1TxsNum
724 l1CoordinatorTx.UserOrigin = false
725 l1CoordinatorTx.EthBlockNum = blockNum
726 l1CoordinatorTx.BatchNum = &batchNum
727 l1Tx, err := common.NewL1Tx(&l1CoordinatorTx)
728 if err != nil {
729 return nil, err
730 }
731
732 batchData.L1CoordinatorTxs = append(batchData.L1CoordinatorTxs, *l1Tx)
733 position++
734 // fmt.Println("DGB l1coordtx")
735 }
736
737 // Insert all the txs forged in this batch (l1UserTxs,
738 // L1CoordinatorTxs, PoolL2Txs) into stateDB so that they are
739 // processed.
740 poolL2Txs := common.L2TxsToPoolL2Txs(forgeBatchArgs.L2TxsData) // NOTE: This is a big ugly, find a better way
741
742 // ProcessTxs updates poolL2Txs adding: Nonce (and also TokenID, but we don't use it).
743 //nolint:gomnd
744 ptc := statedb.ProcessTxsConfig{ // TODO TMP
745 NLevels: 32,
746 MaxFeeTx: 64,
747 MaxTx: 512,
748 MaxL1Tx: 64,
749 }
750 processTxsOut, err := s.stateDB.ProcessTxs(ptc, forgeBatchArgs.FeeIdxCoordinator, l1UserTxs,
751 batchData.L1CoordinatorTxs, poolL2Txs)
752 if err != nil {
753 return nil, err
754 }
755
756 // Set batchNum in exits
757 for i := range processTxsOut.ExitInfos {
758 exit := &processTxsOut.ExitInfos[i]
759 exit.BatchNum = batchNum
760 }
761 batchData.ExitTree = processTxsOut.ExitInfos
762
763 l2Txs, err := common.PoolL2TxsToL2Txs(poolL2Txs) // NOTE: This is a big uggly, find a better way
764 if err != nil {
765 return nil, err
766 }
767
768 for i := range l2Txs {
769 tx := &l2Txs[i]
770 tx.Position = position
771 tx.EthBlockNum = blockNum
772 tx.BatchNum = batchNum
773 nTx, err := common.NewL2Tx(tx)
774 if err != nil {
775 return nil, err
776 }
777
778 batchData.L2Txs = append(batchData.L2Txs, *nTx)
779 position++
780 }
781
782 for i := range processTxsOut.CreatedAccounts {
783 createdAccount := &processTxsOut.CreatedAccounts[i]
784 createdAccount.Nonce = 0
785 createdAccount.Balance = big.NewInt(0)
786 createdAccount.BatchNum = batchNum
787 }
788 batchData.CreatedAccounts = processTxsOut.CreatedAccounts
789
790 slotNum := int64(0)
791 if ethBlock.EthBlockNum >= s.consts.Auction.GenesisBlockNum {
792 slotNum = (ethBlock.EthBlockNum - s.consts.Auction.GenesisBlockNum) /
793 int64(s.consts.Auction.BlocksPerSlot)
794 }
795
796 // Get Batch information
797 batch := common.Batch{
798 BatchNum: batchNum,
799 EthBlockNum: blockNum,
800 ForgerAddr: *sender,
801 CollectedFees: processTxsOut.CollectedFees,
802 FeeIdxsCoordinator: forgeBatchArgs.FeeIdxCoordinator,
803 StateRoot: forgeBatchArgs.NewStRoot,
804 NumAccounts: len(batchData.CreatedAccounts),
805 LastIdx: forgeBatchArgs.NewLastIdx,
806 ExitRoot: forgeBatchArgs.NewExitRoot,
807 SlotNum: slotNum,
808 }
809 nextForgeL1TxsNumCpy := nextForgeL1TxsNum
810 if forgeBatchArgs.L1Batch {
811 batch.ForgeL1TxsNum = &nextForgeL1TxsNumCpy
812 batchData.L1Batch = true
813 nextForgeL1TxsNum++
814 }
815 batchData.Batch = batch
816 rollupData.Batches = append(rollupData.Batches, *batchData)
817 }
818
819 // Get Registered Tokens
820 for _, evtAddToken := range rollupEvents.AddToken {
821 var token common.Token
822
823 token.TokenID = common.TokenID(evtAddToken.TokenID)
824 token.EthAddr = evtAddToken.TokenAddress
825 token.EthBlockNum = blockNum
826
827 if consts, err := s.ethClient.EthERC20Consts(evtAddToken.TokenAddress); err != nil {
828 log.Warnw("Error retreiving ERC20 token constants", "addr", evtAddToken.TokenAddress)
829 token.Name = "ERC20_ETH_ERROR"
830 token.Symbol = "ERROR"
831 token.Decimals = 1
832 } else {
833 token.Name = cutStringMax(consts.Name, 20)
834 token.Symbol = cutStringMax(consts.Symbol, 10)
835 token.Decimals = consts.Decimals
836 }
837
838 rollupData.AddedTokens = append(rollupData.AddedTokens, token)
839 }
840
841 varsUpdate := false
842
843 for _, evtUpdateForgeL1L2BatchTimeout := range rollupEvents.UpdateForgeL1L2BatchTimeout {
844 s.vars.Rollup.ForgeL1L2BatchTimeout = evtUpdateForgeL1L2BatchTimeout.NewForgeL1L2BatchTimeout
845 varsUpdate = true
846 }
847
848 for _, evtUpdateFeeAddToken := range rollupEvents.UpdateFeeAddToken {
849 s.vars.Rollup.FeeAddToken = evtUpdateFeeAddToken.NewFeeAddToken
850 varsUpdate = true
851 }
852
853 // NOTE: WithdrawDelay update doesn't have event, so we can't track changes
854
855 // NOTE: Buckets update dones't have event, so we can't track changes
856
857 for _, evtWithdraw := range rollupEvents.Withdraw {
858 rollupData.Withdrawals = append(rollupData.Withdrawals, common.WithdrawInfo{
859 Idx: common.Idx(evtWithdraw.Idx),
860 NumExitRoot: common.BatchNum(evtWithdraw.NumExitRoot),
861 InstantWithdraw: evtWithdraw.InstantWithdraw,
862 TxHash: evtWithdraw.TxHash,
863 })
864 }
865
866 if varsUpdate {
867 s.vars.Rollup.EthBlockNum = blockNum
868 rollupData.Vars = s.vars.Rollup.Copy()
869 }
870
871 return &rollupData, nil
872}
873
874func cutStringMax(s string, max int) string {
875 if len(s) > max {
876 return s[:max]
877 }
878 return s
879}
880
881// auctionSync gets information from the Auction Contract
882func (s *Synchronizer) auctionSync(ethBlock *common.Block) (*common.AuctionData, error) {
883 blockNum := ethBlock.EthBlockNum
884 var auctionData = common.NewAuctionData()
885
886 // Get auction events in the block
887 auctionEvents, blockHash, err := s.ethClient.AuctionEventsByBlock(blockNum)
888 if err != nil {
889 return nil, err
890 }
891 // No events in this block
892 if blockHash == nil {
893 return &auctionData, nil
894 }
895 if *blockHash != ethBlock.Hash {
896 log.Errorw("Block hash mismatch in Auction events", "expected", ethBlock.Hash.String(),
897 "got", blockHash.String())
898 return nil, eth.ErrBlockHashMismatchEvent
899 }
900
901 // Get bids
902 for _, evtNewBid := range auctionEvents.NewBid {
903 bid := common.Bid{
904 SlotNum: evtNewBid.Slot,
905 BidValue: evtNewBid.BidAmount,
906 Bidder: evtNewBid.Bidder,
907 EthBlockNum: blockNum,
908 }
909 auctionData.Bids = append(auctionData.Bids, bid)
910 }
911
912 // Get Coordinators
913 for _, evtSetCoordinator := range auctionEvents.SetCoordinator {
914 coordinator := common.Coordinator{
915 Bidder: evtSetCoordinator.BidderAddress,
916 Forger: evtSetCoordinator.ForgerAddress,
917 URL: evtSetCoordinator.CoordinatorURL,
918 }
919 auctionData.Coordinators = append(auctionData.Coordinators, coordinator)
920 }
921
922 varsUpdate := false
923
924 for _, evt := range auctionEvents.NewSlotDeadline {
925 s.vars.Auction.SlotDeadline = evt.NewSlotDeadline
926 varsUpdate = true
927 }
928 for _, evt := range auctionEvents.NewClosedAuctionSlots {
929 s.vars.Auction.ClosedAuctionSlots = evt.NewClosedAuctionSlots
930 varsUpdate = true
931 }
932 for _, evt := range auctionEvents.NewOutbidding {
933 s.vars.Auction.Outbidding = evt.NewOutbidding
934 varsUpdate = true
935 }
936 for _, evt := range auctionEvents.NewDonationAddress {
937 s.vars.Auction.DonationAddress = evt.NewDonationAddress
938 varsUpdate = true
939 }
940 for _, evt := range auctionEvents.NewBootCoordinator {
941 s.vars.Auction.BootCoordinator = evt.NewBootCoordinator
942 varsUpdate = true
943 }
944 for _, evt := range auctionEvents.NewOpenAuctionSlots {
945 s.vars.Auction.OpenAuctionSlots = evt.NewOpenAuctionSlots
946 varsUpdate = true
947 }
948 for _, evt := range auctionEvents.NewAllocationRatio {
949 s.vars.Auction.AllocationRatio = evt.NewAllocationRatio
950 varsUpdate = true
951 }
952 for _, evt := range auctionEvents.NewDefaultSlotSetBid {
953 if evt.SlotSet > 6 { //nolint:gomnd
954 return nil, fmt.Errorf("unexpected SlotSet in "+
955 "auctionEvents.NewDefaultSlotSetBid: %v", evt.SlotSet)
956 }
957 s.vars.Auction.DefaultSlotSetBid[evt.SlotSet] = evt.NewInitialMinBid
958 s.vars.Auction.DefaultSlotSetBidSlotNum = s.consts.Auction.SlotNum(blockNum) + int64(s.vars.Auction.ClosedAuctionSlots) + 1
959 varsUpdate = true
960 }
961
962 // NOTE: We ignore NewForgeAllocated
963 // NOTE: We ignore NewForge because we're already tracking ForgeBatch event from Rollup
964 // NOTE: We ignore HEZClaimed
965
966 if varsUpdate {
967 s.vars.Auction.EthBlockNum = blockNum
968 auctionData.Vars = s.vars.Auction.Copy()
969 }
970
971 return &auctionData, nil
972}
973
974// wdelayerSync gets information from the Withdrawal Delayer Contract
975func (s *Synchronizer) wdelayerSync(ethBlock *common.Block) (*common.WDelayerData, error) {
976 blockNum := ethBlock.EthBlockNum
977 wDelayerData := common.NewWDelayerData()
978
979 // Get wDelayer events in the block
980 wDelayerEvents, blockHash, err := s.ethClient.WDelayerEventsByBlock(blockNum)
981 if err != nil {
982 return nil, err
983 }
984 // No events in this block
985 if blockHash == nil {
986 return &wDelayerData, nil
987 }
988 if *blockHash != ethBlock.Hash {
989 log.Errorw("Block hash mismatch in WDelayer events", "expected", ethBlock.Hash.String(),
990 "got", blockHash.String())
991 return nil, eth.ErrBlockHashMismatchEvent
992 }
993
994 for _, evt := range wDelayerEvents.Deposit {
995 wDelayerData.Deposits = append(wDelayerData.Deposits, common.WDelayerTransfer{
996 Owner: evt.Owner,
997 Token: evt.Token,
998 Amount: evt.Amount,
999 })
1000 wDelayerData.DepositsByTxHash[evt.TxHash] =
1001 &wDelayerData.Deposits[len(wDelayerData.Deposits)-1]
1002 }
1003 for _, evt := range wDelayerEvents.Withdraw {
1004 wDelayerData.Withdrawals = append(wDelayerData.Withdrawals, common.WDelayerTransfer{
1005 Owner: evt.Owner,
1006 Token: evt.Token,
1007 Amount: evt.Amount,
1008 })
1009 }
1010
1011 varsUpdate := false
1012
1013 // TODO EscapeHatchWithdrawal
1014 for range wDelayerEvents.EmergencyModeEnabled {
1015 s.vars.WDelayer.EmergencyMode = true
1016 varsUpdate = true
1017 }
1018 for _, evt := range wDelayerEvents.NewWithdrawalDelay {
1019 s.vars.WDelayer.WithdrawalDelay = evt.WithdrawalDelay
1020 varsUpdate = true
1021 }
1022 for _, evt := range wDelayerEvents.NewHermezKeeperAddress {
1023 s.vars.WDelayer.HermezKeeperAddress = evt.NewHermezKeeperAddress
1024 varsUpdate = true
1025 }
1026 for _, evt := range wDelayerEvents.NewWhiteHackGroupAddress {
1027 s.vars.WDelayer.WhiteHackGroupAddress = evt.NewWhiteHackGroupAddress
1028 varsUpdate = true
1029 }
1030 for _, evt := range wDelayerEvents.NewHermezGovernanceDAOAddress {
1031 s.vars.WDelayer.HermezGovernanceDAOAddress = evt.NewHermezGovernanceDAOAddress
1032 varsUpdate = true
1033 }
1034
1035 if varsUpdate {
1036 s.vars.WDelayer.EthBlockNum = blockNum
1037 wDelayerData.Vars = s.vars.WDelayer.Copy()
1038 }
1039
1040 return &wDelayerData, nil
1041}
1042
1043func getL1UserTx(eventsL1UserTx []eth.RollupEventL1UserTx, blockNum int64) ([]common.L1Tx, error) {
1044 l1Txs := make([]common.L1Tx, len(eventsL1UserTx))
1045 for i := range eventsL1UserTx {
1046 eventsL1UserTx[i].L1UserTx.EthBlockNum = blockNum
1047 // Check validity of L1UserTx
1048 l1Tx, err := common.NewL1Tx(&eventsL1UserTx[i].L1UserTx)
1049 if err != nil {
1050 return nil, err
1051 }
1052 l1Txs[i] = *l1Tx
1053 }
1054 return l1Txs, nil
1055}