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