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.

272 lines
7.0 KiB

  1. package synchronizer
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "math/big"
  7. "sync"
  8. ethCommon "github.com/ethereum/go-ethereum/common"
  9. "github.com/hermeznetwork/hermez-node/common"
  10. "github.com/hermeznetwork/hermez-node/db/historydb"
  11. "github.com/hermeznetwork/hermez-node/db/statedb"
  12. "github.com/hermeznetwork/hermez-node/eth"
  13. "github.com/hermeznetwork/hermez-node/log"
  14. )
  15. const (
  16. blocksToSync = 20 // TODO: This will be deleted once we can get the firstSavedBlock from the ethClient
  17. )
  18. var (
  19. // ErrNotAbleToSync is used when there is not possible to find a valid block to sync
  20. ErrNotAbleToSync = errors.New("it has not been possible to synchronize any block")
  21. )
  22. // BatchData contains information about Batches from the contracts
  23. //nolint:structcheck,unused
  24. type BatchData struct {
  25. l1txs []common.L1Tx
  26. l2txs []common.L2Tx
  27. registeredAccounts []common.Account
  28. exitTree []common.ExitInfo
  29. }
  30. // BlockData contains information about Blocks from the contracts
  31. //nolint:structcheck,unused
  32. type BlockData struct {
  33. block *common.Block
  34. // Rollup
  35. batches []BatchData
  36. withdrawals []common.ExitInfo
  37. registeredTokens []common.Token
  38. rollupVars *common.RollupVars
  39. // Auction
  40. bids []common.Bid
  41. coordinators []common.Coordinator
  42. auctionVars *common.AuctionVars
  43. }
  44. // Status is returned by the Status method
  45. type Status struct {
  46. CurrentBlock uint64
  47. CurrentBatch common.BatchNum
  48. CurrentForgerAddr ethCommon.Address
  49. NextForgerAddr ethCommon.Address
  50. Synchronized bool
  51. }
  52. // Synchronizer implements the Synchronizer type
  53. type Synchronizer struct {
  54. ethClient *eth.Client
  55. historyDB *historydb.HistoryDB
  56. stateDB *statedb.StateDB
  57. firstSavedBlock *common.Block
  58. mux sync.Mutex
  59. }
  60. // NewSynchronizer creates a new Synchronizer
  61. func NewSynchronizer(ethClient *eth.Client, historyDB *historydb.HistoryDB, stateDB *statedb.StateDB) *Synchronizer {
  62. s := &Synchronizer{
  63. ethClient: ethClient,
  64. historyDB: historyDB,
  65. stateDB: stateDB,
  66. }
  67. return s
  68. }
  69. // Sync updates History and State DB with information from the blockchain
  70. func (s *Synchronizer) Sync() error {
  71. // Avoid new sync while performing one
  72. s.mux.Lock()
  73. defer s.mux.Unlock()
  74. var lastStoredForgeL1TxsNum uint64
  75. // TODO: Get this information from ethClient once it's implemented
  76. // for the moment we will get the latestblock - 20 as firstSavedBlock
  77. latestBlock, err := s.ethClient.BlockByNumber(context.Background(), nil)
  78. if err != nil {
  79. return err
  80. }
  81. s.firstSavedBlock, err = s.ethClient.BlockByNumber(context.Background(), big.NewInt(int64(latestBlock.EthBlockNum-blocksToSync)))
  82. if err != nil {
  83. return err
  84. }
  85. // Get lastSavedBlock from History DB
  86. lastSavedBlock, err := s.historyDB.GetLastBlock()
  87. if err != nil && err != sql.ErrNoRows {
  88. return err
  89. }
  90. // Check if we got a block or nil
  91. // In case of nil we must do a full sync
  92. if lastSavedBlock == nil || lastSavedBlock.EthBlockNum == 0 {
  93. lastSavedBlock = s.firstSavedBlock
  94. } else {
  95. // Get the latest block we have in History DB from blockchain to detect a reorg
  96. ethBlock, err := s.ethClient.BlockByNumber(context.Background(), big.NewInt(int64(lastSavedBlock.EthBlockNum)))
  97. if err != nil {
  98. return err
  99. }
  100. if ethBlock.Hash != lastSavedBlock.Hash {
  101. // Reorg detected
  102. log.Debugf("Reorg Detected...")
  103. err := s.reorg(lastSavedBlock)
  104. if err != nil {
  105. return err
  106. }
  107. lastSavedBlock, err = s.historyDB.GetLastBlock()
  108. if err != nil {
  109. return err
  110. }
  111. }
  112. }
  113. log.Debugf("Syncing...")
  114. // Get latest blockNum in blockchain
  115. latestBlockNum, err := s.ethClient.CurrentBlock()
  116. if err != nil {
  117. return err
  118. }
  119. log.Debugf("Blocks to sync: %v (lastSavedBlock: %v, latestBlock: %v)", latestBlockNum.Uint64()-lastSavedBlock.EthBlockNum, lastSavedBlock.EthBlockNum, latestBlockNum)
  120. for lastSavedBlock.EthBlockNum < latestBlockNum.Uint64() {
  121. ethBlock, err := s.ethClient.BlockByNumber(context.Background(), big.NewInt(int64(lastSavedBlock.EthBlockNum+1)))
  122. if err != nil {
  123. return err
  124. }
  125. // Get data from the rollup contract
  126. blockData, batchData, err := s.rollupSync(ethBlock, lastStoredForgeL1TxsNum)
  127. if err != nil {
  128. return err
  129. }
  130. // Get data from the auction contract
  131. err = s.auctionSync(blockData, batchData)
  132. if err != nil {
  133. return err
  134. }
  135. // Add rollupData and auctionData once the method is updated
  136. err = s.historyDB.AddBlock(ethBlock)
  137. if err != nil {
  138. return err
  139. }
  140. // We get the block on every iteration
  141. lastSavedBlock, err = s.historyDB.GetLastBlock()
  142. if err != nil {
  143. return err
  144. }
  145. }
  146. return nil
  147. }
  148. // reorg manages a reorg, updating History and State DB as needed
  149. func (s *Synchronizer) reorg(uncleBlock *common.Block) error {
  150. var block *common.Block
  151. blockNum := uncleBlock.EthBlockNum
  152. found := false
  153. log.Debugf("Reorg first uncle block: %v", blockNum)
  154. // Iterate History DB and the blokchain looking for the latest valid block
  155. for !found && blockNum > s.firstSavedBlock.EthBlockNum {
  156. header, err := s.ethClient.HeaderByNumber(context.Background(), big.NewInt(int64(blockNum)))
  157. if err != nil {
  158. return err
  159. }
  160. block, err = s.historyDB.GetBlock(blockNum)
  161. if err != nil {
  162. return err
  163. }
  164. if block.Hash == header.Hash() {
  165. found = true
  166. log.Debugf("Found valid block: %v", blockNum)
  167. } else {
  168. log.Debugf("Discarding block: %v", blockNum)
  169. }
  170. blockNum--
  171. }
  172. if found {
  173. // Set History DB and State DB to the correct state
  174. err := s.historyDB.Reorg(block.EthBlockNum)
  175. if err != nil {
  176. return err
  177. }
  178. batchNum, err := s.historyDB.GetLastBatchNum()
  179. if err != nil && err != sql.ErrNoRows {
  180. return err
  181. }
  182. if batchNum != 0 {
  183. err = s.stateDB.Reset(batchNum)
  184. if err != nil {
  185. return err
  186. }
  187. }
  188. return nil
  189. }
  190. return ErrNotAbleToSync
  191. }
  192. // Status returns current status values from the Synchronizer
  193. func (s *Synchronizer) Status() (*Status, error) {
  194. // Avoid possible inconsistencies
  195. s.mux.Lock()
  196. defer s.mux.Unlock()
  197. var status *Status
  198. // Get latest block in History DB
  199. lastSavedBlock, err := s.historyDB.GetLastBlock()
  200. if err != nil {
  201. return nil, err
  202. }
  203. status.CurrentBlock = lastSavedBlock.EthBlockNum
  204. // Get latest batch in History DB
  205. lastSavedBatch, err := s.historyDB.GetLastBatchNum()
  206. if err != nil && err != sql.ErrNoRows {
  207. return nil, err
  208. }
  209. status.CurrentBatch = lastSavedBatch
  210. // Get latest blockNum in blockchain
  211. latestBlockNum, err := s.ethClient.CurrentBlock()
  212. if err != nil {
  213. return nil, err
  214. }
  215. // TODO: Get CurrentForgerAddr & NextForgerAddr
  216. // Check if Synchronizer is synchronized
  217. status.Synchronized = status.CurrentBlock == latestBlockNum.Uint64()
  218. return status, nil
  219. }
  220. // rollupSync gets information from the Rollup Contract
  221. func (s *Synchronizer) rollupSync(block *common.Block, lastStoredForgeL1TxsNum uint64) (*BlockData, []*BatchData, error) {
  222. // To be implemented
  223. return nil, nil, nil
  224. }
  225. // auctionSync gets information from the Auction Contract
  226. func (s *Synchronizer) auctionSync(blockData *BlockData, batchData []*BatchData) error {
  227. // To be implemented
  228. return nil
  229. }