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