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