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