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