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