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