You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

579 lines
17 KiB

  1. package synchronizer
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "strconv"
  7. "sync"
  8. "github.com/hermeznetwork/hermez-node/common"
  9. "github.com/hermeznetwork/hermez-node/db/historydb"
  10. "github.com/hermeznetwork/hermez-node/db/statedb"
  11. "github.com/hermeznetwork/hermez-node/eth"
  12. "github.com/hermeznetwork/hermez-node/log"
  13. )
  14. var (
  15. // ErrNotAbleToSync is used when there is not possible to find a valid block to sync
  16. ErrNotAbleToSync = errors.New("it has not been possible to synchronize any block")
  17. )
  18. // rollupData contains information returned by the Rollup SC
  19. type rollupData struct {
  20. l1Txs []*common.L1Tx
  21. batches []*BatchData
  22. // withdrawals []*common.ExitInfo
  23. registeredTokens []*common.Token
  24. rollupVars *common.RollupVars
  25. }
  26. // NewRollupData creates an empty rollupData with the slices initialized.
  27. func newRollupData() rollupData {
  28. return rollupData{
  29. l1Txs: make([]*common.L1Tx, 0),
  30. batches: make([]*BatchData, 0),
  31. // withdrawals: make([]*common.ExitInfo, 0),
  32. registeredTokens: make([]*common.Token, 0),
  33. }
  34. }
  35. // auctionData contains information returned by the Action SC
  36. type auctionData struct {
  37. bids []*common.Bid
  38. coordinators []*common.Coordinator
  39. auctionVars *common.AuctionVars
  40. }
  41. // newAuctionData creates an empty auctionData with the slices initialized.
  42. func newAuctionData() *auctionData {
  43. return &auctionData{
  44. bids: make([]*common.Bid, 0),
  45. coordinators: make([]*common.Coordinator, 0),
  46. }
  47. }
  48. // BatchData contains information about Batches from the contracts
  49. type BatchData struct {
  50. l1UserTxs []*common.L1Tx
  51. l1CoordinatorTxs []*common.L1Tx
  52. l2Txs []*common.L2Tx
  53. createdAccounts []*common.Account
  54. exitTree []*common.ExitInfo
  55. batch *common.Batch
  56. }
  57. // NewBatchData creates an empty BatchData with the slices initialized.
  58. func NewBatchData() *BatchData {
  59. return &BatchData{
  60. l1UserTxs: make([]*common.L1Tx, 0),
  61. l1CoordinatorTxs: make([]*common.L1Tx, 0),
  62. l2Txs: make([]*common.L2Tx, 0),
  63. createdAccounts: make([]*common.Account, 0),
  64. exitTree: make([]*common.ExitInfo, 0),
  65. }
  66. }
  67. // BlockData contains information about Blocks from the contracts
  68. type BlockData struct {
  69. block *common.Block
  70. // Rollup
  71. l1Txs []*common.L1Tx // TODO: Answer: User? Coordinator? Both?
  72. batches []*BatchData // TODO: Also contains L1Txs!
  73. // withdrawals []*common.ExitInfo // TODO
  74. registeredTokens []*common.Token
  75. rollupVars *common.RollupVars
  76. // Auction
  77. bids []*common.Bid
  78. coordinators []*common.Coordinator
  79. auctionVars *common.AuctionVars
  80. // WithdrawalDelayer
  81. withdrawalDelayerVars *common.WithdrawalDelayerVars
  82. }
  83. // Synchronizer implements the Synchronizer type
  84. type Synchronizer struct {
  85. ethClient *eth.Client
  86. historyDB *historydb.HistoryDB
  87. stateDB *statedb.StateDB
  88. firstSavedBlock *common.Block
  89. mux sync.Mutex
  90. }
  91. // NewSynchronizer creates a new Synchronizer
  92. func NewSynchronizer(ethClient *eth.Client, historyDB *historydb.HistoryDB, stateDB *statedb.StateDB) *Synchronizer {
  93. s := &Synchronizer{
  94. ethClient: ethClient,
  95. historyDB: historyDB,
  96. stateDB: stateDB,
  97. }
  98. return s
  99. }
  100. // TODO: Be smart about locking: only lock during the read/write operations
  101. // Sync updates History and State DB with information from the blockchain
  102. // TODO: Return true if a new block was processed
  103. // TODO: Add argument: maximum number of blocks to process
  104. // TODO: Check reorgs in the middle of syncing a block. Probably make
  105. // rollupSync, auctionSync and withdrawalSync return the block hash.
  106. func (s *Synchronizer) Sync() error {
  107. // Avoid new sync while performing one
  108. s.mux.Lock()
  109. defer s.mux.Unlock()
  110. var nextBlockNum int64 // next block number to sync
  111. // Get lastSavedBlock from History DB
  112. lastSavedBlock, err := s.historyDB.GetLastBlock()
  113. if err != nil && err != sql.ErrNoRows {
  114. return err
  115. }
  116. // If we don't have any stored block, we must do a full sync starting from the rollup genesis block
  117. if err == sql.ErrNoRows {
  118. // TODO: Query rollup constants and genesis information, store them
  119. nextBlockNum = 1234 // TODO: Replace this with genesisBlockNum
  120. } else {
  121. // Get the latest block we have in History DB from blockchain to detect a reorg
  122. ethBlock, err := s.ethClient.EthBlockByNumber(context.Background(), lastSavedBlock.EthBlockNum)
  123. if err != nil {
  124. return err
  125. }
  126. if ethBlock.Hash != lastSavedBlock.Hash {
  127. // Reorg detected
  128. log.Debugf("Reorg Detected...")
  129. _, err := s.reorg(lastSavedBlock)
  130. if err != nil {
  131. return err
  132. }
  133. lastSavedBlock, err = s.historyDB.GetLastBlock()
  134. if err != nil {
  135. return err
  136. }
  137. }
  138. nextBlockNum = lastSavedBlock.EthBlockNum + 1
  139. }
  140. log.Debugf("Syncing...")
  141. // Get latest blockNum in blockchain
  142. latestBlockNum, err := s.ethClient.EthCurrentBlock()
  143. if err != nil {
  144. return err
  145. }
  146. log.Debugf("Blocks to sync: %v (firstBlockToSync: %v, latestBlock: %v)", latestBlockNum-nextBlockNum+1, nextBlockNum, latestBlockNum)
  147. for nextBlockNum < latestBlockNum {
  148. ethBlock, err := s.ethClient.EthBlockByNumber(context.Background(), nextBlockNum)
  149. if err != nil {
  150. return err
  151. }
  152. // TODO: Check that the obtianed ethBlock.ParentHash == prevEthBlock.Hash; if not, reorg!
  153. // TODO: Send the ethHash in rollupSync(), auctionSync() and
  154. // wdelayerSync() and make sure they all use the same block
  155. // hash.
  156. // Get data from the rollup contract
  157. rollupData, err := s.rollupSync(nextBlockNum)
  158. if err != nil {
  159. return err
  160. }
  161. // Get data from the auction contract
  162. auctionData, err := s.auctionSync(nextBlockNum)
  163. if err != nil {
  164. return err
  165. }
  166. // Get data from the WithdrawalDelayer contract
  167. wdelayerData, err := s.wdelayerSync(nextBlockNum)
  168. if err != nil {
  169. return err
  170. }
  171. // Group all the block data into the structs to save into HistoryDB
  172. var blockData BlockData
  173. blockData.block = ethBlock
  174. if rollupData != nil {
  175. blockData.l1Txs = rollupData.l1Txs
  176. blockData.batches = rollupData.batches
  177. // blockData.withdrawals = rollupData.withdrawals // TODO
  178. blockData.registeredTokens = rollupData.registeredTokens
  179. blockData.rollupVars = rollupData.rollupVars
  180. }
  181. if auctionData != nil {
  182. blockData.bids = auctionData.bids
  183. blockData.coordinators = auctionData.coordinators
  184. blockData.auctionVars = auctionData.auctionVars
  185. }
  186. if wdelayerData != nil {
  187. blockData.withdrawalDelayerVars = wdelayerData
  188. }
  189. // Add rollupData and auctionData once the method is updated
  190. // TODO: Save Whole Struct -> AddBlockSCData(blockData)
  191. err = s.historyDB.AddBlock(blockData.block)
  192. if err != nil {
  193. return err
  194. }
  195. }
  196. return nil
  197. }
  198. // reorg manages a reorg, updating History and State DB as needed. Keeps
  199. // checking previous blocks from the HistoryDB against the blockchain until a
  200. // block hash match is found. All future blocks in the HistoryDB and
  201. // corresponding batches in StateBD are discarded. Returns the last valid
  202. // blockNum from the HistoryDB.
  203. func (s *Synchronizer) reorg(uncleBlock *common.Block) (int64, error) {
  204. var block *common.Block
  205. blockNum := uncleBlock.EthBlockNum
  206. found := false
  207. log.Debugf("Reorg first uncle block: %v", blockNum)
  208. // Iterate History DB and the blokchain looking for the latest valid block
  209. for !found && blockNum > s.firstSavedBlock.EthBlockNum {
  210. ethBlock, err := s.ethClient.EthBlockByNumber(context.Background(), blockNum)
  211. if err != nil {
  212. return 0, err
  213. }
  214. block, err = s.historyDB.GetBlock(blockNum)
  215. if err != nil {
  216. return 0, err
  217. }
  218. if block.Hash == ethBlock.Hash {
  219. found = true
  220. log.Debugf("Found valid block: %v", blockNum)
  221. } else {
  222. log.Debugf("Discarding block: %v", blockNum)
  223. }
  224. blockNum--
  225. }
  226. if found {
  227. // Set History DB and State DB to the correct state
  228. err := s.historyDB.Reorg(block.EthBlockNum)
  229. if err != nil {
  230. return 0, err
  231. }
  232. batchNum, err := s.historyDB.GetLastBatchNum()
  233. if err != nil && err != sql.ErrNoRows {
  234. return 0, err
  235. }
  236. if batchNum != 0 {
  237. err = s.stateDB.Reset(batchNum)
  238. if err != nil {
  239. return 0, err
  240. }
  241. }
  242. return block.EthBlockNum, nil
  243. }
  244. return 0, ErrNotAbleToSync
  245. }
  246. // Status returns current status values from the Synchronizer
  247. func (s *Synchronizer) Status() (*common.SyncStatus, error) {
  248. // Avoid possible inconsistencies
  249. s.mux.Lock()
  250. defer s.mux.Unlock()
  251. var status *common.SyncStatus
  252. // TODO: Join all queries to the DB into a single transaction so that
  253. // we can remove the mutex locking here:
  254. // - HistoryDB.GetLastBlock
  255. // - HistoryDB.GetLastBatchNum
  256. // - HistoryDB.GetCurrentForgerAddr
  257. // - HistoryDB.GetNextForgerAddr
  258. // Get latest block in History DB
  259. lastSavedBlock, err := s.historyDB.GetLastBlock()
  260. if err != nil {
  261. return nil, err
  262. }
  263. status.CurrentBlock = lastSavedBlock.EthBlockNum
  264. // Get latest batch in History DB
  265. lastSavedBatch, err := s.historyDB.GetLastBatchNum()
  266. if err != nil && err != sql.ErrNoRows {
  267. return nil, err
  268. }
  269. status.CurrentBatch = lastSavedBatch
  270. // Get latest blockNum in blockchain
  271. latestBlockNum, err := s.ethClient.EthCurrentBlock()
  272. if err != nil {
  273. return nil, err
  274. }
  275. // TODO: Get CurrentForgerAddr & NextForgerAddr from the Auction SC / Or from the HistoryDB
  276. // Check if Synchronizer is synchronized
  277. status.Synchronized = status.CurrentBlock == latestBlockNum
  278. return status, nil
  279. }
  280. // rollupSync gets information from the Rollup Contract
  281. func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
  282. var rollupData = newRollupData()
  283. // var forgeL1TxsNum int64
  284. var numAccounts int
  285. // Get rollup events in the block
  286. rollupEvents, _, err := s.ethClient.RollupEventsByBlock(blockNum)
  287. if err != nil {
  288. return nil, err
  289. }
  290. // TODO: Replace GetLastL1TxsNum by GetNextL1TxsNum
  291. nextForgeL1TxsNum := int64(0)
  292. nextForgeL1TxsNumPtr, err := s.historyDB.GetLastL1TxsNum()
  293. if err != nil {
  294. return nil, err
  295. }
  296. if nextForgeL1TxsNumPtr != nil {
  297. nextForgeL1TxsNum = *nextForgeL1TxsNumPtr + 1
  298. }
  299. // Get newLastIdx that will be used to complete the accounts
  300. // idx, err := s.getIdx(rollupEvents)
  301. // if err != nil {
  302. // return nil, err
  303. // }
  304. // Get L1UserTX
  305. rollupData.l1Txs = getL1UserTx(rollupEvents.L1UserTx, blockNum)
  306. // Get ForgeBatch events to get the L1CoordinatorTxs
  307. for _, fbEvent := range rollupEvents.ForgeBatch {
  308. batchData := NewBatchData()
  309. position := 0
  310. // Get the input for each Tx
  311. forgeBatchArgs, err := s.ethClient.RollupForgeBatchArgs(fbEvent.EthTxHash)
  312. if err != nil {
  313. return nil, err
  314. }
  315. forgeL1TxsNum := int64(0)
  316. // Check if this is a L1Batch to get L1 Tx from it
  317. if forgeBatchArgs.L1Batch {
  318. forgeL1TxsNum = nextForgeL1TxsNum
  319. // Get L1 User Txs from History DB
  320. // TODO: Get L1TX from HistoryDB filtered by toforgeL1txNum & fromidx = 0 and
  321. // update batch number and add accounts to createdAccounts updating idx
  322. // l1UserTxs, err := s.historyDB.GetL1UserTxs(nextForgeL1TxsNum)
  323. // If HistoryDB doesn't have L1UserTxs at
  324. // nextForgeL1TxsNum, check if they exist in
  325. // rollupData.l1Txs. This could happen because in a
  326. // block there could be multiple batches with L1Batch =
  327. // true (although it's a very rare case). If the
  328. // L1UserTxs are not in rollupData.l1Txs, use an empty
  329. // array (this happens when the L1UserTxs queue is
  330. // frozen but didn't store any tx).
  331. l1UserTxs := []common.L1Tx{}
  332. position = len(l1UserTxs)
  333. // Get L1 Coordinator Txs
  334. for _, l1CoordinatorTx := range forgeBatchArgs.L1CoordinatorTxs {
  335. l1CoordinatorTx.Position = position
  336. l1CoordinatorTx.ToForgeL1TxsNum = nextForgeL1TxsNum
  337. l1CoordinatorTx.TxID = common.TxID(common.Hash([]byte("0x01" + strconv.FormatInt(int64(nextForgeL1TxsNum), 10) + strconv.FormatInt(int64(l1CoordinatorTx.Position), 10) + "00")))
  338. l1CoordinatorTx.UserOrigin = false
  339. l1CoordinatorTx.EthBlockNum = blockNum
  340. l1CoordinatorTx.BatchNum = common.BatchNum(fbEvent.BatchNum)
  341. batchData.l1CoordinatorTxs = append(batchData.l1CoordinatorTxs, l1CoordinatorTx)
  342. // Check if we have to register an account
  343. // if l1CoordinatorTx.FromIdx == 0 {
  344. // account := common.Account{
  345. // // TODO: Uncommnent when common.account has IDx
  346. // // IDx: common.Idx(idx),
  347. // TokenID: l1CoordinatorTx.TokenID,
  348. // Nonce: 0,
  349. // Balance: l1CoordinatorTx.LoadAmount,
  350. // PublicKey: l1CoordinatorTx.FromBJJ,
  351. // EthAddr: l1CoordinatorTx.FromEthAddr,
  352. // }
  353. // idx++
  354. // batchData.createdAccounts = append(batchData.createdAccounts, &account)
  355. // numAccounts++
  356. // }
  357. position++
  358. }
  359. nextForgeL1TxsNum++
  360. }
  361. // Get L2Txs
  362. poolL2Txs := common.L2TxsToPoolL2Txs(forgeBatchArgs.L2Txs) // TODO: This is a big uggly, find a better way
  363. // Get exitTree
  364. // TODO: Get createdAccounts from ProcessTxs()
  365. // TODO: Get CollectedFees from ProcessTxs()
  366. // TODO: Pass forgeBatchArgs.FeeIdxCoordinator to ProcessTxs()
  367. _, exitInfo, err := s.stateDB.ProcessTxs(true, false, batchData.l1UserTxs, batchData.l1CoordinatorTxs, poolL2Txs)
  368. if err != nil {
  369. return nil, err
  370. }
  371. l2Txs := common.PoolL2TxsToL2Txs(poolL2Txs) // TODO: This is a big uggly, find a better way
  372. batchData.l2Txs = append(batchData.l2Txs, l2Txs...)
  373. batchData.exitTree = exitInfo
  374. // Get Batch information
  375. batch := &common.Batch{
  376. BatchNum: common.BatchNum(fbEvent.BatchNum),
  377. EthBlockNum: blockNum,
  378. // ForgerAddr: , TODO: Get it from ethClient -> Add ForgerAddr to RollupEventForgeBatch
  379. // CollectedFees: , TODO: Clarify where to get them if they are still needed
  380. StateRoot: common.Hash(forgeBatchArgs.NewStRoot.Bytes()),
  381. NumAccounts: numAccounts,
  382. ExitRoot: common.Hash(forgeBatchArgs.NewExitRoot.Bytes()),
  383. ForgeL1TxsNum: forgeL1TxsNum,
  384. // SlotNum: TODO: Calculate once ethClient provides the info // calculate from blockNum + ethClient Constants
  385. }
  386. batchData.batch = batch
  387. rollupData.batches = append(rollupData.batches, batchData)
  388. }
  389. // Get Registered Tokens
  390. for _, eAddToken := range rollupEvents.AddToken {
  391. var token *common.Token
  392. token.TokenID = common.TokenID(eAddToken.TokenID)
  393. token.EthAddr = eAddToken.Address
  394. token.EthBlockNum = blockNum
  395. // TODO: Add external information consulting SC about it using Address
  396. rollupData.registeredTokens = append(rollupData.registeredTokens, token)
  397. }
  398. // TODO: rollupEvents.UpdateForgeL1L2BatchTimeout
  399. // TODO: rollupEvents.UpdateFeeAddToken
  400. // TODO: rollupEvents.WithdrawEvent
  401. // TODO: Emergency Mechanism
  402. // TODO: Variables
  403. // TODO: Constants
  404. return &rollupData, nil
  405. }
  406. // auctionSync gets information from the Auction Contract
  407. func (s *Synchronizer) auctionSync(blockNum int64) (*auctionData, error) {
  408. var auctionData = newAuctionData()
  409. // Get auction events in the block
  410. auctionEvents, _, err := s.ethClient.AuctionEventsByBlock(blockNum)
  411. if err != nil {
  412. return nil, err
  413. }
  414. // Get bids
  415. for _, eNewBid := range auctionEvents.NewBid {
  416. bid := &common.Bid{
  417. SlotNum: common.SlotNum(eNewBid.Slot),
  418. BidValue: eNewBid.BidAmount,
  419. ForgerAddr: eNewBid.CoordinatorForger,
  420. EthBlockNum: blockNum,
  421. }
  422. auctionData.bids = append(auctionData.bids, bid)
  423. }
  424. // Get Coordinators
  425. for _, eNewCoordinator := range auctionEvents.NewCoordinator {
  426. coordinator := &common.Coordinator{
  427. Forger: eNewCoordinator.ForgerAddress,
  428. Withdraw: eNewCoordinator.WithdrawalAddress,
  429. URL: eNewCoordinator.URL,
  430. }
  431. auctionData.coordinators = append(auctionData.coordinators, coordinator)
  432. }
  433. // TODO: NewSlotDeadline
  434. // TODO: NewClosedAuctionSlots
  435. // TODO: NewOutbidding
  436. // TODO: NewDonationAddress
  437. // TODO: NewBootCoordinator
  438. // TODO: NewOpenAuctionSlots
  439. // TODO: NewAllocationRatio
  440. // TODO: NewForgeAllocated
  441. // TODO: NewDefaultSlotSetBid
  442. // TODO: NewForge
  443. // TODO: HEZClaimed
  444. // TODO: Think about separating new coordinaors from coordinator updated
  445. // Get Coordinators from updates
  446. for _, eCoordinatorUpdated := range auctionEvents.CoordinatorUpdated {
  447. coordinator := &common.Coordinator{
  448. Forger: eCoordinatorUpdated.ForgerAddress,
  449. Withdraw: eCoordinatorUpdated.WithdrawalAddress,
  450. URL: eCoordinatorUpdated.URL,
  451. }
  452. auctionData.coordinators = append(auctionData.coordinators, coordinator)
  453. }
  454. // TODO: VARS
  455. // TODO: CONSTANTS
  456. return auctionData, nil
  457. }
  458. // wdelayerSync gets information from the Withdrawal Delayer Contract
  459. func (s *Synchronizer) wdelayerSync(blockNum int64) (*common.WithdrawalDelayerVars, error) {
  460. // TODO: VARS
  461. // TODO: CONSTANTS
  462. return nil, nil
  463. }
  464. // func (s *Synchronizer) getIdx(rollupEvents *eth.RollupEvents) (int64, error) {
  465. // // TODO: FIXME: There will be an error here when `len(rollupEvents.ForgeBatch) == 0`
  466. // lastForgeBatch := rollupEvents.ForgeBatch[len(rollupEvents.ForgeBatch)-1]
  467. //
  468. // // TODO: RollupForgeBatchArgs is already called in `rollupSync`.
  469. // // Ideally it should not need to be called twice for the same batch.
  470. // // Get the input for forgeBatch
  471. // forgeBatchArgs, err := s.ethClient.RollupForgeBatchArgs(lastForgeBatch.EthTxHash)
  472. // if err != nil {
  473. // return 0, err
  474. // }
  475. //
  476. // return forgeBatchArgs.NewLastIdx + 1, nil
  477. // }
  478. func getL1UserTx(l1UserTxEvents []eth.RollupEventL1UserTx, blockNum int64) []*common.L1Tx {
  479. l1Txs := make([]*common.L1Tx, 0)
  480. for _, eL1UserTx := range l1UserTxEvents {
  481. // Fill aditional Tx fields
  482. eL1UserTx.L1Tx.TxID = common.TxID(common.Hash([]byte("0x00" + strconv.FormatInt(int64(eL1UserTx.ToForgeL1TxsNum), 10) + strconv.FormatInt(int64(eL1UserTx.Position), 10) + "00")))
  483. eL1UserTx.L1Tx.ToForgeL1TxsNum = eL1UserTx.ToForgeL1TxsNum
  484. eL1UserTx.L1Tx.Position = eL1UserTx.Position
  485. eL1UserTx.L1Tx.UserOrigin = true
  486. eL1UserTx.L1Tx.EthBlockNum = blockNum
  487. eL1UserTx.L1Tx.BatchNum = 0
  488. l1Txs = append(l1Txs, &eL1UserTx.L1Tx)
  489. }
  490. return l1Txs
  491. }