arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/synchronizer/synchronizer.go
synchronizer/synchronizer.goRaw
   1package synchronizer
   2
   3import (
   4	"context"
   5	"database/sql"
   6	"fmt"
   7	"math/big"
   8	"sync"
   9	"time"
  10
  11	"github.com/ethereum/go-ethereum"
  12	"github.com/hermeznetwork/hermez-node/common"
  13	"github.com/hermeznetwork/hermez-node/db/historydb"
  14	"github.com/hermeznetwork/hermez-node/db/statedb"
  15	"github.com/hermeznetwork/hermez-node/eth"
  16	"github.com/hermeznetwork/hermez-node/log"
  17)
  18
  19var (
  20// ErrNotAbleToSync is used when there is not possible to find a valid block to sync
  21// ErrNotAbleToSync = errors.New("it has not been possible to synchronize any block")
  22)
  23
  24// // SyncronizerState describes the synchronization progress of the smart contracts
  25// type SyncronizerState struct {
  26// 	LastUpdate                time.Time // last time this information was updated
  27// 	CurrentBatchNum           BatchNum  // Last batch that was forged on the blockchain
  28// 	CurrentBlockNum           uint64    // Last block that was mined on Ethereum
  29// 	CurrentToForgeL1TxsNum    uint32
  30// 	LastSyncedBatchNum        BatchNum // last batch synchronized by the coordinator
  31// 	LastSyncedBlockNum        uint64   // last Ethereum block synchronized by the coordinator
  32// 	LastSyncedToForgeL1TxsNum uint32
  33// }
  34
  35// // SyncStatus is returned by the Status method of the Synchronizer
  36// type SyncStatus struct {
  37// 	CurrentBlock      int64
  38// 	CurrentBatch      BatchNum
  39// 	CurrentForgerAddr ethCommon.Address
  40// 	NextForgerAddr    ethCommon.Address
  41// 	Synchronized      bool
  42// }
  43
  44// Stats of the syncrhonizer
  45type Stats struct {
  46	Eth struct {
  47		RefreshPeriod time.Duration
  48		Updated       time.Time
  49		FirstBlock    int64
  50		LastBlock     int64
  51		LastBatch     int64
  52	}
  53	Sync struct {
  54		Updated   time.Time
  55		LastBlock int64
  56		LastBatch int64
  57		Auction   struct {
  58			CurrentSlot common.Slot
  59		}
  60	}
  61}
  62
  63// Synced returns true if the Synchronizer is up to date with the last ethereum block
  64func (s *Stats) Synced() bool {
  65	return s.Eth.LastBlock == s.Sync.LastBlock
  66}
  67
  68// TODO(Edu): Consider removing all the mutexes from StatsHolder, make
  69// Synchronizer.Stats not thread-safe, don't pass the synchronizer to the
  70// debugAPI, and have a copy of the Stats in the DebugAPI that the node passes
  71// when the Sync updates.
  72
  73// StatsHolder stores stats and that allows reading and writing them
  74// concurrently
  75type StatsHolder struct {
  76	Stats
  77	rw sync.RWMutex
  78}
  79
  80// NewStatsHolder creates a new StatsHolder
  81func NewStatsHolder(firstBlock int64, refreshPeriod time.Duration) *StatsHolder {
  82	stats := Stats{}
  83	stats.Eth.RefreshPeriod = refreshPeriod
  84	stats.Eth.FirstBlock = firstBlock
  85	return &StatsHolder{Stats: stats}
  86}
  87
  88// UpdateCurrentSlot updates the auction stats
  89func (s *StatsHolder) UpdateCurrentSlot(slot common.Slot) {
  90	s.rw.Lock()
  91	s.Sync.Auction.CurrentSlot = slot
  92	s.rw.Unlock()
  93}
  94
  95// UpdateSync updates the synchronizer stats
  96func (s *StatsHolder) UpdateSync(lastBlock int64, lastBatch *common.BatchNum) {
  97	now := time.Now()
  98	s.rw.Lock()
  99	s.Sync.LastBlock = lastBlock
 100	if lastBatch != nil {
 101		s.Sync.LastBatch = int64(*lastBatch)
 102	}
 103	s.Sync.Updated = now
 104	s.rw.Unlock()
 105}
 106
 107// UpdateEth updates the ethereum stats, only if the previous stats expired
 108func (s *StatsHolder) UpdateEth(ethClient eth.ClientInterface) error {
 109	now := time.Now()
 110	s.rw.RLock()
 111	elapsed := now.Sub(s.Eth.Updated)
 112	s.rw.RUnlock()
 113	if elapsed < s.Eth.RefreshPeriod {
 114		return nil
 115	}
 116
 117	lastBlock, err := ethClient.EthLastBlock()
 118	if err != nil {
 119		return err
 120	}
 121	lastBatch, err := ethClient.RollupLastForgedBatch()
 122	if err != nil {
 123		return err
 124	}
 125	s.rw.Lock()
 126	s.Eth.Updated = now
 127	s.Eth.LastBlock = lastBlock
 128	s.Eth.LastBatch = lastBatch
 129	s.rw.Unlock()
 130	return nil
 131}
 132
 133// CopyStats returns a copy of the inner Stats
 134func (s *StatsHolder) CopyStats() *Stats {
 135	s.rw.RLock()
 136	sCopy := s.Stats
 137	if s.Sync.Auction.CurrentSlot.BidValue != nil {
 138		sCopy.Sync.Auction.CurrentSlot.BidValue =
 139			common.CopyBigInt(s.Sync.Auction.CurrentSlot.BidValue)
 140	}
 141	s.rw.RUnlock()
 142	return &sCopy
 143}
 144
 145func (s *StatsHolder) blocksPerc() float64 {
 146	syncLastBlock := s.Sync.LastBlock
 147	if s.Sync.LastBlock == 0 {
 148		syncLastBlock = s.Eth.FirstBlock - 1
 149	}
 150	return float64(syncLastBlock-(s.Eth.FirstBlock-1)) * 100.0 /
 151		float64(s.Eth.LastBlock-(s.Eth.FirstBlock-1))
 152}
 153
 154func (s *StatsHolder) batchesPerc(batchNum int64) float64 {
 155	return float64(batchNum) * 100.0 /
 156		float64(s.Eth.LastBatch)
 157}
 158
 159// ConfigStartBlockNum sets the first block used to start tracking the smart
 160// contracts
 161type ConfigStartBlockNum struct {
 162	Rollup   int64 `validate:"required"`
 163	Auction  int64 `validate:"required"`
 164	WDelayer int64 `validate:"required"`
 165}
 166
 167// SCVariables joins all the smart contract variables in a single struct
 168type SCVariables struct {
 169	Rollup   common.RollupVariables   `validate:"required"`
 170	Auction  common.AuctionVariables  `validate:"required"`
 171	WDelayer common.WDelayerVariables `validate:"required"`
 172}
 173
 174// SCConsts joins all the smart contract constants in a single struct
 175type SCConsts struct {
 176	Rollup   common.RollupConstants
 177	Auction  common.AuctionConstants
 178	WDelayer common.WDelayerConstants
 179}
 180
 181// Config is the Synchronizer configuration
 182type Config struct {
 183	StartBlockNum      ConfigStartBlockNum
 184	InitialVariables   SCVariables
 185	StatsRefreshPeriod time.Duration
 186}
 187
 188// Synchronizer implements the Synchronizer type
 189type Synchronizer struct {
 190	ethClient eth.ClientInterface
 191	// auctionConstants  common.AuctionConstants
 192	// rollupConstants   common.RollupConstants
 193	// wDelayerConstants common.WDelayerConstants
 194	consts        SCConsts
 195	historyDB     *historydb.HistoryDB
 196	stateDB       *statedb.StateDB
 197	cfg           Config
 198	startBlockNum int64
 199	vars          SCVariables
 200	stats         *StatsHolder
 201	// firstSavedBlock  *common.Block
 202	// mux sync.Mutex
 203}
 204
 205// NewSynchronizer creates a new Synchronizer
 206func NewSynchronizer(ethClient eth.ClientInterface, historyDB *historydb.HistoryDB,
 207	stateDB *statedb.StateDB, cfg Config) (*Synchronizer, error) {
 208	auctionConstants, err := ethClient.AuctionConstants()
 209	if err != nil {
 210		log.Errorw("NewSynchronizer ethClient.AuctionConstants()", "err", err)
 211		return nil, err
 212	}
 213	rollupConstants, err := ethClient.RollupConstants()
 214	if err != nil {
 215		log.Errorw("NewSynchronizer ethClient.RollupConstants()", "err", err)
 216		return nil, err
 217	}
 218	wDelayerConstants, err := ethClient.WDelayerConstants()
 219	if err != nil {
 220		log.Errorw("NewSynchronizer ethClient.WDelayerConstants()", "err", err)
 221		return nil, err
 222	}
 223
 224	// Set startBlockNum to the minimum between Auction, Rollup and
 225	// WDelayer StartBlockNum
 226	startBlockNum := cfg.StartBlockNum.Auction
 227	if startBlockNum < cfg.StartBlockNum.Rollup {
 228		startBlockNum = cfg.StartBlockNum.Rollup
 229	}
 230	if startBlockNum < cfg.StartBlockNum.WDelayer {
 231		startBlockNum = cfg.StartBlockNum.WDelayer
 232	}
 233	stats := NewStatsHolder(startBlockNum, cfg.StatsRefreshPeriod)
 234	s := &Synchronizer{
 235		ethClient: ethClient,
 236		consts: SCConsts{
 237			Rollup:   *rollupConstants,
 238			Auction:  *auctionConstants,
 239			WDelayer: *wDelayerConstants,
 240		},
 241		historyDB:     historyDB,
 242		stateDB:       stateDB,
 243		cfg:           cfg,
 244		startBlockNum: startBlockNum,
 245		stats:         stats,
 246	}
 247	return s, s.init()
 248}
 249
 250// Stats returns a copy of the Synchronizer Stats.  It is safe to call Stats()
 251// during a Sync call
 252func (s *Synchronizer) Stats() *Stats {
 253	return s.stats.CopyStats()
 254}
 255
 256// AuctionConstants returns the AuctionConstants read from the smart contract
 257func (s *Synchronizer) AuctionConstants() *common.AuctionConstants {
 258	return &s.consts.Auction
 259}
 260
 261// RollupConstants returns the RollupConstants read from the smart contract
 262func (s *Synchronizer) RollupConstants() *common.RollupConstants {
 263	return &s.consts.Rollup
 264}
 265
 266// WDelayerConstants returns the WDelayerConstants read from the smart contract
 267func (s *Synchronizer) WDelayerConstants() *common.WDelayerConstants {
 268	return &s.consts.WDelayer
 269}
 270
 271// SCVars returns a copy of the Smart Contract Variables
 272func (s *Synchronizer) SCVars() (*common.RollupVariables, *common.AuctionVariables, *common.WDelayerVariables) {
 273	return s.vars.Rollup.Copy(), s.vars.Auction.Copy(), s.vars.WDelayer.Copy()
 274}
 275
 276func (s *Synchronizer) updateCurrentSlotIfSync(batchesLen int) error {
 277	slot := common.Slot{
 278		SlotNum:    s.stats.Sync.Auction.CurrentSlot.SlotNum,
 279		BatchesLen: int(s.stats.Sync.Auction.CurrentSlot.BatchesLen),
 280	}
 281	// We want the next block because the current one is already mined
 282	blockNum := s.stats.Sync.LastBlock + 1
 283	slotNum := s.consts.Auction.SlotNum(blockNum)
 284	if batchesLen == -1 {
 285		dbBatchesLen, err := s.historyDB.GetBatchesLen(slotNum)
 286		// fmt.Printf("DBG -1 from: %v, to: %v, len: %v\n", from, to, dbBatchesLen)
 287		if err != nil {
 288			log.Errorw("historyDB.GetBatchesLen", "err", err)
 289			return err
 290		}
 291		slot.BatchesLen = dbBatchesLen
 292	} else if slotNum > slot.SlotNum {
 293		// fmt.Printf("DBG batchesLen Reset len: %v (%v %v)\n", batchesLen, slotNum, slot.SlotNum)
 294		slot.BatchesLen = batchesLen
 295	} else {
 296		// fmt.Printf("DBG batchesLen add len: %v: %v\n", batchesLen, slot.BatchesLen+batchesLen)
 297		slot.BatchesLen += batchesLen
 298	}
 299	slot.SlotNum = slotNum
 300	slot.StartBlock, slot.EndBlock = s.consts.Auction.SlotBlocks(slot.SlotNum)
 301	// If Synced, update the current coordinator
 302	if s.stats.Synced() {
 303		bidCoord, err := s.historyDB.GetBestBidCoordinator(slot.SlotNum)
 304		if err != nil && err != sql.ErrNoRows {
 305			return err
 306		}
 307		if err == sql.ErrNoRows {
 308			slot.BootCoord = true
 309			slot.Forger = s.vars.Auction.BootCoordinator
 310			slot.URL = "???"
 311		} else if err == nil {
 312			slot.BidValue = bidCoord.BidValue
 313			defaultSlotBid := bidCoord.DefaultSlotSetBid[slot.SlotNum%6]
 314			if slot.BidValue.Cmp(defaultSlotBid) >= 0 {
 315				slot.Bidder = bidCoord.Bidder
 316				slot.Forger = bidCoord.Forger
 317				slot.URL = bidCoord.URL
 318			} else {
 319				slot.BootCoord = true
 320				slot.Forger = s.vars.Auction.BootCoordinator
 321				slot.URL = "???"
 322			}
 323		}
 324
 325		// TODO: Remove this SANITY CHECK once this code is tested enough
 326		// BEGIN SANITY CHECK
 327		canForge, err := s.ethClient.AuctionCanForge(slot.Forger, blockNum)
 328		if err != nil {
 329			return err
 330		}
 331		if !canForge {
 332			return fmt.Errorf("Synchronized value of forger address for closed slot "+
 333				"differs from smart contract: %+v", slot)
 334		}
 335		// END SANITY CHECK
 336	}
 337	s.stats.UpdateCurrentSlot(slot)
 338	return nil
 339}
 340
 341func (s *Synchronizer) init() error {
 342	// Update stats parameters so that they have valid values before the
 343	// first Sync call
 344	if err := s.stats.UpdateEth(s.ethClient); err != nil {
 345		return err
 346	}
 347	var lastBlockNum int64
 348	lastSavedBlock, err := s.historyDB.GetLastBlock()
 349	if err != nil && err != sql.ErrNoRows {
 350		return err
 351	}
 352	// If there's no block in the DB (or we only have the default block 0),
 353	// make sure that the stateDB is clean
 354	if err == sql.ErrNoRows || lastSavedBlock.EthBlockNum == 0 {
 355		if err := s.stateDB.Reset(0); err != nil {
 356			return err
 357		}
 358	} else {
 359		lastBlockNum = lastSavedBlock.EthBlockNum
 360	}
 361	if err := s.resetState(lastBlockNum); err != nil {
 362		return err
 363	}
 364
 365	log.Infow("Sync init block",
 366		"syncLastBlock", s.stats.Sync.LastBlock,
 367		"syncBlocksPerc", s.stats.blocksPerc(),
 368		"ethFirstBlock", s.stats.Eth.FirstBlock,
 369		"ethLastBlock", s.stats.Eth.LastBlock,
 370	)
 371	log.Infow("Sync init batch",
 372		"syncLastBatch", s.stats.Sync.LastBatch,
 373		"syncBatchesPerc", s.stats.batchesPerc(s.stats.Sync.LastBatch),
 374		"ethLastBatch", s.stats.Eth.LastBatch,
 375	)
 376	return nil
 377}
 378
 379// Sync2 attems to synchronize an ethereum block starting from lastSavedBlock.
 380// If lastSavedBlock is nil, the lastSavedBlock value is obtained from de DB.
 381// If a block is synched, it will be returned and also stored in the DB.  If a
 382// reorg is detected, the number of discarded blocks will be returned and no
 383// synchronization will be made.
 384// TODO: Be smart about locking: only lock during the read/write operations
 385func (s *Synchronizer) Sync2(ctx context.Context, lastSavedBlock *common.Block) (*common.BlockData, *int64, error) {
 386	var nextBlockNum int64 // next block number to sync
 387	if lastSavedBlock == nil {
 388		var err error
 389		// Get lastSavedBlock from History DB
 390		lastSavedBlock, err = s.historyDB.GetLastBlock()
 391		if err != nil && err != sql.ErrNoRows {
 392			return nil, nil, err
 393		}
 394		// If we don't have any stored block, we must do a full sync
 395		// starting from the startBlockNum
 396		if err == sql.ErrNoRows || lastSavedBlock.EthBlockNum == 0 {
 397			nextBlockNum = s.startBlockNum
 398			lastSavedBlock = nil
 399		}
 400	}
 401	if lastSavedBlock != nil {
 402		nextBlockNum = lastSavedBlock.EthBlockNum + 1
 403	}
 404
 405	ethBlock, err := s.ethClient.EthBlockByNumber(ctx, nextBlockNum)
 406	if err == ethereum.NotFound {
 407		return nil, nil, nil
 408	} else if err != nil {
 409		return nil, nil, err
 410	}
 411	log.Debugf("ethBlock: num: %v, parent: %v, hash: %v", ethBlock.EthBlockNum, ethBlock.ParentHash.String(), ethBlock.Hash.String())
 412
 413	if err := s.stats.UpdateEth(s.ethClient); err != nil {
 414		return nil, nil, err
 415	}
 416
 417	log.Debugw("Syncing...",
 418		"block", nextBlockNum,
 419		"ethLastBlock", s.stats.Eth.LastBlock,
 420	)
 421
 422	// Check that the obtianed ethBlock.ParentHash == prevEthBlock.Hash; if not, reorg!
 423	if lastSavedBlock != nil {
 424		if lastSavedBlock.Hash != ethBlock.ParentHash {
 425			// Reorg detected
 426			log.Debugw("Reorg Detected",
 427				"blockNum", ethBlock.EthBlockNum,
 428				"block.parent(got)", ethBlock.ParentHash, "parent.hash(exp)", lastSavedBlock.Hash)
 429			lastDBBlockNum, err := s.reorg(lastSavedBlock)
 430			if err != nil {
 431				return nil, nil, err
 432			}
 433			discarded := lastSavedBlock.EthBlockNum - lastDBBlockNum
 434			return nil, &discarded, nil
 435		}
 436	}
 437
 438	// Get data from the rollup contract
 439	rollupData, err := s.rollupSync(ethBlock)
 440	if err != nil {
 441		return nil, nil, err
 442	}
 443
 444	// Get data from the auction contract
 445	auctionData, err := s.auctionSync(ethBlock)
 446	if err != nil {
 447		return nil, nil, err
 448	}
 449
 450	// Get data from the WithdrawalDelayer contract
 451	wDelayerData, err := s.wdelayerSync(ethBlock)
 452	if err != nil {
 453		return nil, nil, err
 454	}
 455
 456	for i := range rollupData.Withdrawals {
 457		withdrawal := &rollupData.Withdrawals[i]
 458		if !withdrawal.InstantWithdraw {
 459			wDelayerTransfer, ok := wDelayerData.DepositsByTxHash[withdrawal.TxHash]
 460			if !ok {
 461				return nil, nil, fmt.Errorf("WDelayer deposit corresponding to " +
 462					"non-instant rollup withdrawal not found")
 463			}
 464			withdrawal.Owner = wDelayerTransfer.Owner
 465			withdrawal.Token = wDelayerTransfer.Token
 466		}
 467	}
 468
 469	// Group all the block data into the structs to save into HistoryDB
 470	blockData := common.BlockData{
 471		Block:    *ethBlock,
 472		Rollup:   *rollupData,
 473		Auction:  *auctionData,
 474		WDelayer: *wDelayerData,
 475	}
 476
 477	// log.Debugw("Sync()", "block", blockData)
 478	// err = s.historyDB.AddBlock(blockData.Block)
 479	// if err != nil {
 480	// 	return err
 481	// }
 482	err = s.historyDB.AddBlockSCData(&blockData)
 483	if err != nil {
 484		return nil, nil, err
 485	}
 486
 487	batchesLen := len(rollupData.Batches)
 488	if batchesLen == 0 {
 489		s.stats.UpdateSync(ethBlock.EthBlockNum, nil)
 490	} else {
 491		s.stats.UpdateSync(ethBlock.EthBlockNum,
 492			&rollupData.Batches[batchesLen-1].Batch.BatchNum)
 493	}
 494	if err := s.updateCurrentSlotIfSync(len(rollupData.Batches)); err != nil {
 495		return nil, nil, err
 496	}
 497	log.Debugw("Synced block",
 498		"syncLastBlock", s.stats.Sync.LastBlock,
 499		"syncBlocksPerc", s.stats.blocksPerc(),
 500		"ethLastBlock", s.stats.Eth.LastBlock,
 501	)
 502	for _, batchData := range rollupData.Batches {
 503		log.Debugw("Synced batch",
 504			"syncLastBatch", batchData.Batch.BatchNum,
 505			"syncBatchesPerc", s.stats.batchesPerc(int64(batchData.Batch.BatchNum)),
 506			"ethLastBatch", s.stats.Eth.LastBatch,
 507		)
 508	}
 509
 510	return &blockData, nil, nil
 511}
 512
 513// reorg manages a reorg, updating History and State DB as needed.  Keeps
 514// checking previous blocks from the HistoryDB against the blockchain until a
 515// block hash match is found.  All future blocks in the HistoryDB and
 516// corresponding batches in StateBD are discarded.  Returns the last valid
 517// blockNum from the HistoryDB.
 518func (s *Synchronizer) reorg(uncleBlock *common.Block) (int64, error) {
 519	blockNum := uncleBlock.EthBlockNum
 520
 521	for blockNum >= s.startBlockNum {
 522		ethBlock, err := s.ethClient.EthBlockByNumber(context.Background(), blockNum)
 523		if err != nil {
 524			log.Errorw("ethClient.EthBlockByNumber", "err", err)
 525			return 0, err
 526		}
 527
 528		block, err := s.historyDB.GetBlock(blockNum)
 529		if err != nil {
 530			log.Errorw("historyDB.GetBlock", "err", err)
 531			return 0, err
 532		}
 533		if block.Hash == ethBlock.Hash {
 534			log.Debugf("Found valid block: %v", blockNum)
 535			break
 536		}
 537		blockNum--
 538	}
 539	total := uncleBlock.EthBlockNum - blockNum
 540	log.Debugw("Discarding blocks", "total", total, "from", uncleBlock.EthBlockNum, "to", blockNum+1)
 541
 542	// Set History DB and State DB to the correct state
 543	err := s.historyDB.Reorg(blockNum)
 544	if err != nil {
 545		return 0, err
 546	}
 547
 548	if err := s.resetState(blockNum); err != nil {
 549		return 0, err
 550	}
 551
 552	return blockNum, nil
 553}
 554
 555func (s *Synchronizer) resetState(blockNum int64) error {
 556	rollup, auction, wDelayer, err := s.historyDB.GetSCVars()
 557	// If SCVars are not in the HistoryDB, this is probably the first run
 558	// of the Synchronizer: store the initial vars taken from config
 559	if err == sql.ErrNoRows {
 560		rollup = &s.cfg.InitialVariables.Rollup
 561		auction = &s.cfg.InitialVariables.Auction
 562		wDelayer = &s.cfg.InitialVariables.WDelayer
 563		log.Info("Setting initial SCVars in HistoryDB")
 564		if err = s.historyDB.SetInitialSCVars(rollup, auction, wDelayer); err != nil {
 565			log.Errorw("historyDB.SetInitialSCVars", "err", err)
 566			return err
 567		}
 568	}
 569	s.vars.Rollup = *rollup
 570	s.vars.Auction = *auction
 571	s.vars.WDelayer = *wDelayer
 572
 573	batchNum, err := s.historyDB.GetLastBatchNum()
 574	if err != nil && err != sql.ErrNoRows {
 575		log.Errorw("historyDB.GetLastBatchNum", "err", err)
 576		return err
 577	}
 578	if err == sql.ErrNoRows {
 579		batchNum = 0
 580	}
 581	err = s.stateDB.Reset(batchNum)
 582	if err != nil {
 583		log.Errorw("stateDB.Reset", "err", err)
 584		return err
 585	}
 586
 587	s.stats.UpdateSync(blockNum, &batchNum)
 588
 589	if err := s.updateCurrentSlotIfSync(-1); err != nil {
 590		return err
 591	}
 592	return nil
 593}
 594
 595// TODO: Figure out who will use the Status output, and only return what's strictly need
 596/*
 597// Status returns current status values from the Synchronizer
 598func (s *Synchronizer) Status() (*common.SyncStatus, error) {
 599	// Avoid possible inconsistencies
 600	s.mux.Lock()
 601	defer s.mux.Unlock()
 602
 603	var status *common.SyncStatus
 604
 605	// TODO: Join all queries to the DB into a single transaction so that
 606	// we can remove the mutex locking here:
 607	// - HistoryDB.GetLastBlock
 608	// - HistoryDB.GetLastBatchNum
 609	// - HistoryDB.GetCurrentForgerAddr
 610	// - HistoryDB.GetNextForgerAddr
 611
 612	// Get latest block in History DB
 613	lastSavedBlock, err := s.historyDB.GetLastBlock()
 614	if err != nil {
 615		return nil, err
 616	}
 617	status.CurrentBlock = lastSavedBlock.EthBlockNum
 618
 619	// Get latest batch in History DB
 620	lastSavedBatch, err := s.historyDB.GetLastBatchNum()
 621	if err != nil && err != sql.ErrNoRows {
 622		return nil, err
 623	}
 624	status.CurrentBatch = lastSavedBatch
 625
 626	// Get latest blockNum in blockchain
 627	latestBlockNum, err := s.ethClient.EthLastBlock()
 628	if err != nil {
 629		return nil, err
 630	}
 631
 632	// TODO: Get CurrentForgerAddr & NextForgerAddr from the Auction SC / Or from the HistoryDB
 633
 634	// Check if Synchronizer is synchronized
 635	status.Synchronized = status.CurrentBlock == latestBlockNum
 636	return status, nil
 637}
 638*/
 639
 640// rollupSync retreives all the Rollup Smart Contract Data that happened at
 641// ethBlock.blockNum with ethBlock.Hash.
 642func (s *Synchronizer) rollupSync(ethBlock *common.Block) (*common.RollupData, error) {
 643	blockNum := ethBlock.EthBlockNum
 644	var rollupData = common.NewRollupData()
 645	// var forgeL1TxsNum int64
 646
 647	// Get rollup events in the block, and make sure the block hash matches
 648	// the expected one.
 649	rollupEvents, blockHash, err := s.ethClient.RollupEventsByBlock(blockNum)
 650	if err != nil {
 651		return nil, err
 652	}
 653	// No events in this block
 654	if blockHash == nil {
 655		return &rollupData, nil
 656	}
 657	if *blockHash != ethBlock.Hash {
 658		log.Errorw("Block hash mismatch in Rollup events", "expected", ethBlock.Hash.String(),
 659			"got", blockHash.String())
 660		return nil, eth.ErrBlockHashMismatchEvent
 661	}
 662
 663	var nextForgeL1TxsNum int64 // forgeL1TxsNum for the next L1Batch
 664	nextForgeL1TxsNumPtr, err := s.historyDB.GetLastL1TxsNum()
 665	if err != nil {
 666		return nil, err
 667	}
 668	if nextForgeL1TxsNumPtr != nil {
 669		nextForgeL1TxsNum = *nextForgeL1TxsNumPtr + 1
 670	} else {
 671		nextForgeL1TxsNum = 0
 672	}
 673
 674	// Get L1UserTX
 675	rollupData.L1UserTxs, err = getL1UserTx(rollupEvents.L1UserTx, blockNum)
 676	if err != nil {
 677		return nil, err
 678	}
 679
 680	// Get ForgeBatch events to get the L1CoordinatorTxs
 681	for _, evtForgeBatch := range rollupEvents.ForgeBatch {
 682		batchData := common.NewBatchData()
 683		position := 0
 684
 685		// Get the input for each Tx
 686		forgeBatchArgs, sender, err := s.ethClient.RollupForgeBatchArgs(evtForgeBatch.EthTxHash)
 687		if err != nil {
 688			return nil, err
 689		}
 690
 691		batchNum := common.BatchNum(evtForgeBatch.BatchNum)
 692		var l1UserTxs []common.L1Tx
 693		// Check if this is a L1Batch to get L1 Tx from it
 694		if forgeBatchArgs.L1Batch {
 695			// Get L1UserTxs with toForgeL1TxsNum, which correspond
 696			// to the L1UserTxs that are forged in this batch, so
 697			// that stateDB can process them.
 698
 699			// First try to find them in HistoryDB.
 700			l1UserTxs, err = s.historyDB.GetL1UserTxs(nextForgeL1TxsNum)
 701			if err != nil {
 702				return nil, err
 703			}
 704			// Apart from the DB, try to find them in this block.
 705			// This could happen because in a block there could be
 706			// multiple batches with L1Batch = true (although it's
 707			// a very rare case).  If not found in the DB and the
 708			// block doesn't contain the l1UserTxs, it means that
 709			// the L1UserTxs queue with toForgeL1TxsNum was closed
 710			// empty, so we leave `l1UserTxs` as an empty slice.
 711			for _, l1UserTx := range rollupData.L1UserTxs {
 712				if *l1UserTx.ToForgeL1TxsNum == nextForgeL1TxsNum {
 713					l1UserTxs = append(l1UserTxs, l1UserTx)
 714				}
 715			}
 716
 717			position = len(l1UserTxs)
 718		}
 719		// Get L1 Coordinator Txs
 720		for i := range forgeBatchArgs.L1CoordinatorTxs {
 721			l1CoordinatorTx := forgeBatchArgs.L1CoordinatorTxs[i]
 722			l1CoordinatorTx.Position = position
 723			// l1CoordinatorTx.ToForgeL1TxsNum = &forgeL1TxsNum
 724			l1CoordinatorTx.UserOrigin = false
 725			l1CoordinatorTx.EthBlockNum = blockNum
 726			l1CoordinatorTx.BatchNum = &batchNum
 727			l1Tx, err := common.NewL1Tx(&l1CoordinatorTx)
 728			if err != nil {
 729				return nil, err
 730			}
 731
 732			batchData.L1CoordinatorTxs = append(batchData.L1CoordinatorTxs, *l1Tx)
 733			position++
 734			// fmt.Println("DGB l1coordtx")
 735		}
 736
 737		// Insert all the txs forged in this batch (l1UserTxs,
 738		// L1CoordinatorTxs, PoolL2Txs) into stateDB so that they are
 739		// processed.
 740		poolL2Txs := common.L2TxsToPoolL2Txs(forgeBatchArgs.L2TxsData) // NOTE: This is a big ugly, find a better way
 741
 742		// ProcessTxs updates poolL2Txs adding: Nonce (and also TokenID, but we don't use it).
 743		//nolint:gomnd
 744		ptc := statedb.ProcessTxsConfig{ // TODO TMP
 745			NLevels:  32,
 746			MaxFeeTx: 64,
 747			MaxTx:    512,
 748			MaxL1Tx:  64,
 749		}
 750		processTxsOut, err := s.stateDB.ProcessTxs(ptc, forgeBatchArgs.FeeIdxCoordinator, l1UserTxs,
 751			batchData.L1CoordinatorTxs, poolL2Txs)
 752		if err != nil {
 753			return nil, err
 754		}
 755
 756		// Set batchNum in exits
 757		for i := range processTxsOut.ExitInfos {
 758			exit := &processTxsOut.ExitInfos[i]
 759			exit.BatchNum = batchNum
 760		}
 761		batchData.ExitTree = processTxsOut.ExitInfos
 762
 763		l2Txs, err := common.PoolL2TxsToL2Txs(poolL2Txs) // NOTE: This is a big uggly, find a better way
 764		if err != nil {
 765			return nil, err
 766		}
 767
 768		for i := range l2Txs {
 769			tx := &l2Txs[i]
 770			tx.Position = position
 771			tx.EthBlockNum = blockNum
 772			tx.BatchNum = batchNum
 773			nTx, err := common.NewL2Tx(tx)
 774			if err != nil {
 775				return nil, err
 776			}
 777
 778			batchData.L2Txs = append(batchData.L2Txs, *nTx)
 779			position++
 780		}
 781
 782		for i := range processTxsOut.CreatedAccounts {
 783			createdAccount := &processTxsOut.CreatedAccounts[i]
 784			createdAccount.Nonce = 0
 785			createdAccount.Balance = big.NewInt(0)
 786			createdAccount.BatchNum = batchNum
 787		}
 788		batchData.CreatedAccounts = processTxsOut.CreatedAccounts
 789
 790		slotNum := int64(0)
 791		if ethBlock.EthBlockNum >= s.consts.Auction.GenesisBlockNum {
 792			slotNum = (ethBlock.EthBlockNum - s.consts.Auction.GenesisBlockNum) /
 793				int64(s.consts.Auction.BlocksPerSlot)
 794		}
 795
 796		// Get Batch information
 797		batch := common.Batch{
 798			BatchNum:           batchNum,
 799			EthBlockNum:        blockNum,
 800			ForgerAddr:         *sender,
 801			CollectedFees:      processTxsOut.CollectedFees,
 802			FeeIdxsCoordinator: forgeBatchArgs.FeeIdxCoordinator,
 803			StateRoot:          forgeBatchArgs.NewStRoot,
 804			NumAccounts:        len(batchData.CreatedAccounts),
 805			LastIdx:            forgeBatchArgs.NewLastIdx,
 806			ExitRoot:           forgeBatchArgs.NewExitRoot,
 807			SlotNum:            slotNum,
 808		}
 809		nextForgeL1TxsNumCpy := nextForgeL1TxsNum
 810		if forgeBatchArgs.L1Batch {
 811			batch.ForgeL1TxsNum = &nextForgeL1TxsNumCpy
 812			batchData.L1Batch = true
 813			nextForgeL1TxsNum++
 814		}
 815		batchData.Batch = batch
 816		rollupData.Batches = append(rollupData.Batches, *batchData)
 817	}
 818
 819	// Get Registered Tokens
 820	for _, evtAddToken := range rollupEvents.AddToken {
 821		var token common.Token
 822
 823		token.TokenID = common.TokenID(evtAddToken.TokenID)
 824		token.EthAddr = evtAddToken.TokenAddress
 825		token.EthBlockNum = blockNum
 826
 827		if consts, err := s.ethClient.EthERC20Consts(evtAddToken.TokenAddress); err != nil {
 828			log.Warnw("Error retreiving ERC20 token constants", "addr", evtAddToken.TokenAddress)
 829			token.Name = "ERC20_ETH_ERROR"
 830			token.Symbol = "ERROR"
 831			token.Decimals = 1
 832		} else {
 833			token.Name = cutStringMax(consts.Name, 20)
 834			token.Symbol = cutStringMax(consts.Symbol, 10)
 835			token.Decimals = consts.Decimals
 836		}
 837
 838		rollupData.AddedTokens = append(rollupData.AddedTokens, token)
 839	}
 840
 841	varsUpdate := false
 842
 843	for _, evtUpdateForgeL1L2BatchTimeout := range rollupEvents.UpdateForgeL1L2BatchTimeout {
 844		s.vars.Rollup.ForgeL1L2BatchTimeout = evtUpdateForgeL1L2BatchTimeout.NewForgeL1L2BatchTimeout
 845		varsUpdate = true
 846	}
 847
 848	for _, evtUpdateFeeAddToken := range rollupEvents.UpdateFeeAddToken {
 849		s.vars.Rollup.FeeAddToken = evtUpdateFeeAddToken.NewFeeAddToken
 850		varsUpdate = true
 851	}
 852
 853	// NOTE: WithdrawDelay update doesn't have event, so we can't track changes
 854
 855	// NOTE: Buckets update dones't have event, so we can't track changes
 856
 857	for _, evtWithdraw := range rollupEvents.Withdraw {
 858		rollupData.Withdrawals = append(rollupData.Withdrawals, common.WithdrawInfo{
 859			Idx:             common.Idx(evtWithdraw.Idx),
 860			NumExitRoot:     common.BatchNum(evtWithdraw.NumExitRoot),
 861			InstantWithdraw: evtWithdraw.InstantWithdraw,
 862			TxHash:          evtWithdraw.TxHash,
 863		})
 864	}
 865
 866	if varsUpdate {
 867		s.vars.Rollup.EthBlockNum = blockNum
 868		rollupData.Vars = s.vars.Rollup.Copy()
 869	}
 870
 871	return &rollupData, nil
 872}
 873
 874func cutStringMax(s string, max int) string {
 875	if len(s) > max {
 876		return s[:max]
 877	}
 878	return s
 879}
 880
 881// auctionSync gets information from the Auction Contract
 882func (s *Synchronizer) auctionSync(ethBlock *common.Block) (*common.AuctionData, error) {
 883	blockNum := ethBlock.EthBlockNum
 884	var auctionData = common.NewAuctionData()
 885
 886	// Get auction events in the block
 887	auctionEvents, blockHash, err := s.ethClient.AuctionEventsByBlock(blockNum)
 888	if err != nil {
 889		return nil, err
 890	}
 891	// No events in this block
 892	if blockHash == nil {
 893		return &auctionData, nil
 894	}
 895	if *blockHash != ethBlock.Hash {
 896		log.Errorw("Block hash mismatch in Auction events", "expected", ethBlock.Hash.String(),
 897			"got", blockHash.String())
 898		return nil, eth.ErrBlockHashMismatchEvent
 899	}
 900
 901	// Get bids
 902	for _, evtNewBid := range auctionEvents.NewBid {
 903		bid := common.Bid{
 904			SlotNum:     evtNewBid.Slot,
 905			BidValue:    evtNewBid.BidAmount,
 906			Bidder:      evtNewBid.Bidder,
 907			EthBlockNum: blockNum,
 908		}
 909		auctionData.Bids = append(auctionData.Bids, bid)
 910	}
 911
 912	// Get Coordinators
 913	for _, evtSetCoordinator := range auctionEvents.SetCoordinator {
 914		coordinator := common.Coordinator{
 915			Bidder: evtSetCoordinator.BidderAddress,
 916			Forger: evtSetCoordinator.ForgerAddress,
 917			URL:    evtSetCoordinator.CoordinatorURL,
 918		}
 919		auctionData.Coordinators = append(auctionData.Coordinators, coordinator)
 920	}
 921
 922	varsUpdate := false
 923
 924	for _, evt := range auctionEvents.NewSlotDeadline {
 925		s.vars.Auction.SlotDeadline = evt.NewSlotDeadline
 926		varsUpdate = true
 927	}
 928	for _, evt := range auctionEvents.NewClosedAuctionSlots {
 929		s.vars.Auction.ClosedAuctionSlots = evt.NewClosedAuctionSlots
 930		varsUpdate = true
 931	}
 932	for _, evt := range auctionEvents.NewOutbidding {
 933		s.vars.Auction.Outbidding = evt.NewOutbidding
 934		varsUpdate = true
 935	}
 936	for _, evt := range auctionEvents.NewDonationAddress {
 937		s.vars.Auction.DonationAddress = evt.NewDonationAddress
 938		varsUpdate = true
 939	}
 940	for _, evt := range auctionEvents.NewBootCoordinator {
 941		s.vars.Auction.BootCoordinator = evt.NewBootCoordinator
 942		varsUpdate = true
 943	}
 944	for _, evt := range auctionEvents.NewOpenAuctionSlots {
 945		s.vars.Auction.OpenAuctionSlots = evt.NewOpenAuctionSlots
 946		varsUpdate = true
 947	}
 948	for _, evt := range auctionEvents.NewAllocationRatio {
 949		s.vars.Auction.AllocationRatio = evt.NewAllocationRatio
 950		varsUpdate = true
 951	}
 952	for _, evt := range auctionEvents.NewDefaultSlotSetBid {
 953		if evt.SlotSet > 6 { //nolint:gomnd
 954			return nil, fmt.Errorf("unexpected SlotSet in "+
 955				"auctionEvents.NewDefaultSlotSetBid: %v", evt.SlotSet)
 956		}
 957		s.vars.Auction.DefaultSlotSetBid[evt.SlotSet] = evt.NewInitialMinBid
 958		s.vars.Auction.DefaultSlotSetBidSlotNum = s.consts.Auction.SlotNum(blockNum) + int64(s.vars.Auction.ClosedAuctionSlots) + 1
 959		varsUpdate = true
 960	}
 961
 962	// NOTE: We ignore NewForgeAllocated
 963	// NOTE: We ignore NewForge because we're already tracking ForgeBatch event from Rollup
 964	// NOTE: We ignore HEZClaimed
 965
 966	if varsUpdate {
 967		s.vars.Auction.EthBlockNum = blockNum
 968		auctionData.Vars = s.vars.Auction.Copy()
 969	}
 970
 971	return &auctionData, nil
 972}
 973
 974// wdelayerSync gets information from the Withdrawal Delayer Contract
 975func (s *Synchronizer) wdelayerSync(ethBlock *common.Block) (*common.WDelayerData, error) {
 976	blockNum := ethBlock.EthBlockNum
 977	wDelayerData := common.NewWDelayerData()
 978
 979	// Get wDelayer events in the block
 980	wDelayerEvents, blockHash, err := s.ethClient.WDelayerEventsByBlock(blockNum)
 981	if err != nil {
 982		return nil, err
 983	}
 984	// No events in this block
 985	if blockHash == nil {
 986		return &wDelayerData, nil
 987	}
 988	if *blockHash != ethBlock.Hash {
 989		log.Errorw("Block hash mismatch in WDelayer events", "expected", ethBlock.Hash.String(),
 990			"got", blockHash.String())
 991		return nil, eth.ErrBlockHashMismatchEvent
 992	}
 993
 994	for _, evt := range wDelayerEvents.Deposit {
 995		wDelayerData.Deposits = append(wDelayerData.Deposits, common.WDelayerTransfer{
 996			Owner:  evt.Owner,
 997			Token:  evt.Token,
 998			Amount: evt.Amount,
 999		})
1000		wDelayerData.DepositsByTxHash[evt.TxHash] =
1001			&wDelayerData.Deposits[len(wDelayerData.Deposits)-1]
1002	}
1003	for _, evt := range wDelayerEvents.Withdraw {
1004		wDelayerData.Withdrawals = append(wDelayerData.Withdrawals, common.WDelayerTransfer{
1005			Owner:  evt.Owner,
1006			Token:  evt.Token,
1007			Amount: evt.Amount,
1008		})
1009	}
1010
1011	varsUpdate := false
1012
1013	// TODO EscapeHatchWithdrawal
1014	for range wDelayerEvents.EmergencyModeEnabled {
1015		s.vars.WDelayer.EmergencyMode = true
1016		varsUpdate = true
1017	}
1018	for _, evt := range wDelayerEvents.NewWithdrawalDelay {
1019		s.vars.WDelayer.WithdrawalDelay = evt.WithdrawalDelay
1020		varsUpdate = true
1021	}
1022	for _, evt := range wDelayerEvents.NewHermezKeeperAddress {
1023		s.vars.WDelayer.HermezKeeperAddress = evt.NewHermezKeeperAddress
1024		varsUpdate = true
1025	}
1026	for _, evt := range wDelayerEvents.NewWhiteHackGroupAddress {
1027		s.vars.WDelayer.WhiteHackGroupAddress = evt.NewWhiteHackGroupAddress
1028		varsUpdate = true
1029	}
1030	for _, evt := range wDelayerEvents.NewHermezGovernanceDAOAddress {
1031		s.vars.WDelayer.HermezGovernanceDAOAddress = evt.NewHermezGovernanceDAOAddress
1032		varsUpdate = true
1033	}
1034
1035	if varsUpdate {
1036		s.vars.WDelayer.EthBlockNum = blockNum
1037		wDelayerData.Vars = s.vars.WDelayer.Copy()
1038	}
1039
1040	return &wDelayerData, nil
1041}
1042
1043func getL1UserTx(eventsL1UserTx []eth.RollupEventL1UserTx, blockNum int64) ([]common.L1Tx, error) {
1044	l1Txs := make([]common.L1Tx, len(eventsL1UserTx))
1045	for i := range eventsL1UserTx {
1046		eventsL1UserTx[i].L1UserTx.EthBlockNum = blockNum
1047		// Check validity of L1UserTx
1048		l1Tx, err := common.NewL1Tx(&eventsL1UserTx[i].L1UserTx)
1049		if err != nil {
1050			return nil, err
1051		}
1052		l1Txs[i] = *l1Tx
1053	}
1054	return l1Txs, nil
1055}