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