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