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