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