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