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