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.

252 lines
7.7 KiB

Redo coordinator structure, connect API to node - API: - Modify the constructor so that hardcoded rollup constants don't need to be passed (introduce a `Config` and use `configAPI` internally) - Common: - Update rollup constants with proper *big.Int when required - Add BidCoordinator and Slot structs used by the HistoryDB and Synchronizer. - Add helper methods to AuctionConstants - AuctionVariables: Add column `DefaultSlotSetBidSlotNum` (in the SQL table: `default_slot_set_bid_slot_num`), which indicates at which slotNum does the `DefaultSlotSetBid` specified starts applying. - Config: - Move coordinator exclusive configuration from the node config to the coordinator config - Coordinator: - Reorganize the code towards having the goroutines started and stopped from the coordinator itself instead of the node. - Remove all stop and stopped channels, and use context.Context and sync.WaitGroup instead. - Remove BatchInfo setters and assing variables directly - In ServerProof and ServerProofPool use context instead stop channel. - Use message passing to notify the coordinator about sync updates and reorgs - Introduce the Pipeline, which can be started and stopped by the Coordinator - Introduce the TxManager, which manages ethereum transactions (the TxManager is also in charge of making the forge call to the rollup smart contract). The TxManager keeps ethereum transactions and: 1. Waits for the transaction to be accepted 2. Waits for the transaction to be confirmed for N blocks - In forge logic, first prepare a batch and then wait for an available server proof to have all work ready once the proof server is ready. - Remove the `isForgeSequence` method which was querying the smart contract, and instead use notifications sent by the Synchronizer to figure out if it's forging time. - Update test (which is a minimal test to manually see if the coordinator starts) - HistoryDB: - Add method to get the number of batches in a slot (used to detect when a slot has passed the bid winner forging deadline) - Add method to get the best bid and associated coordinator of a slot (used to detect the forgerAddress that can forge the slot) - General: - Rename some instances of `currentBlock` to `lastBlock` to be more clear. - Node: - Connect the API to the node and call the methods to update cached state when the sync advances blocks. - Call methods to update Coordinator state when the sync advances blocks and finds reorgs. - Synchronizer: - Add Auction field in the Stats, which contain the current slot with info about highest bidder and other related info required to know who can forge in the current block. - Better organization of cached state: - On Sync, update the internal cached state - On Init or Reorg, load the state from HistoryDB into the internal cached state.
3 years ago
Redo coordinator structure, connect API to node - API: - Modify the constructor so that hardcoded rollup constants don't need to be passed (introduce a `Config` and use `configAPI` internally) - Common: - Update rollup constants with proper *big.Int when required - Add BidCoordinator and Slot structs used by the HistoryDB and Synchronizer. - Add helper methods to AuctionConstants - AuctionVariables: Add column `DefaultSlotSetBidSlotNum` (in the SQL table: `default_slot_set_bid_slot_num`), which indicates at which slotNum does the `DefaultSlotSetBid` specified starts applying. - Config: - Move coordinator exclusive configuration from the node config to the coordinator config - Coordinator: - Reorganize the code towards having the goroutines started and stopped from the coordinator itself instead of the node. - Remove all stop and stopped channels, and use context.Context and sync.WaitGroup instead. - Remove BatchInfo setters and assing variables directly - In ServerProof and ServerProofPool use context instead stop channel. - Use message passing to notify the coordinator about sync updates and reorgs - Introduce the Pipeline, which can be started and stopped by the Coordinator - Introduce the TxManager, which manages ethereum transactions (the TxManager is also in charge of making the forge call to the rollup smart contract). The TxManager keeps ethereum transactions and: 1. Waits for the transaction to be accepted 2. Waits for the transaction to be confirmed for N blocks - In forge logic, first prepare a batch and then wait for an available server proof to have all work ready once the proof server is ready. - Remove the `isForgeSequence` method which was querying the smart contract, and instead use notifications sent by the Synchronizer to figure out if it's forging time. - Update test (which is a minimal test to manually see if the coordinator starts) - HistoryDB: - Add method to get the number of batches in a slot (used to detect when a slot has passed the bid winner forging deadline) - Add method to get the best bid and associated coordinator of a slot (used to detect the forgerAddress that can forge the slot) - General: - Rename some instances of `currentBlock` to `lastBlock` to be more clear. - Node: - Connect the API to the node and call the methods to update cached state when the sync advances blocks. - Call methods to update Coordinator state when the sync advances blocks and finds reorgs. - Synchronizer: - Add Auction field in the Stats, which contain the current slot with info about highest bidder and other related info required to know who can forge in the current block. - Better organization of cached state: - On Sync, update the internal cached state - On Init or Reorg, load the state from HistoryDB into the internal cached state.
3 years ago
Update coordinator, call all api update functions - Common: - Rename Block.EthBlockNum to Block.Num to avoid unneeded repetition - API: - Add UpdateNetworkInfoBlock to update just block information, to be used when the node is not yet synchronized - Node: - Call API.UpdateMetrics and UpdateRecommendedFee in a loop, with configurable time intervals - Synchronizer: - When mapping events by TxHash, use an array to support the possibility of multiple calls of the same function happening in the same transaction (for example, a smart contract in a single transaction could call withdraw with delay twice, which would generate 2 withdraw events, and 2 deposit events). - In Stats, keep entire LastBlock instead of just the blockNum - In Stats, add lastL1BatchBlock - Test Stats and SCVars - Coordinator: - Enable writing the BatchInfo in every step of the pipeline to disk (with JSON text files) for debugging purposes. - Move the Pipeline functionality from the Coordinator to its own struct (Pipeline) - Implement shouldL1lL2Batch - In TxManager, implement logic to perform several attempts when doing ethereum node RPC calls before considering the error. (Both for calls to forgeBatch and transaction receipt) - In TxManager, reorganize the flow and note the specific points in which actions are made when err != nil - HistoryDB: - Implement GetLastL1BatchBlockNum: returns the blockNum of the latest forged l1Batch, to help the coordinator decide when to forge an L1Batch. - EthereumClient and test.Client: - Update EthBlockByNumber to return the last block when the passed number is -1.
3 years ago
Update coordinator, call all api update functions - Common: - Rename Block.EthBlockNum to Block.Num to avoid unneeded repetition - API: - Add UpdateNetworkInfoBlock to update just block information, to be used when the node is not yet synchronized - Node: - Call API.UpdateMetrics and UpdateRecommendedFee in a loop, with configurable time intervals - Synchronizer: - When mapping events by TxHash, use an array to support the possibility of multiple calls of the same function happening in the same transaction (for example, a smart contract in a single transaction could call withdraw with delay twice, which would generate 2 withdraw events, and 2 deposit events). - In Stats, keep entire LastBlock instead of just the blockNum - In Stats, add lastL1BatchBlock - Test Stats and SCVars - Coordinator: - Enable writing the BatchInfo in every step of the pipeline to disk (with JSON text files) for debugging purposes. - Move the Pipeline functionality from the Coordinator to its own struct (Pipeline) - Implement shouldL1lL2Batch - In TxManager, implement logic to perform several attempts when doing ethereum node RPC calls before considering the error. (Both for calls to forgeBatch and transaction receipt) - In TxManager, reorganize the flow and note the specific points in which actions are made when err != nil - HistoryDB: - Implement GetLastL1BatchBlockNum: returns the blockNum of the latest forged l1Batch, to help the coordinator decide when to forge an L1Batch. - EthereumClient and test.Client: - Update EthBlockByNumber to return the last block when the passed number is -1.
3 years ago
Update coordinator, call all api update functions - Common: - Rename Block.EthBlockNum to Block.Num to avoid unneeded repetition - API: - Add UpdateNetworkInfoBlock to update just block information, to be used when the node is not yet synchronized - Node: - Call API.UpdateMetrics and UpdateRecommendedFee in a loop, with configurable time intervals - Synchronizer: - When mapping events by TxHash, use an array to support the possibility of multiple calls of the same function happening in the same transaction (for example, a smart contract in a single transaction could call withdraw with delay twice, which would generate 2 withdraw events, and 2 deposit events). - In Stats, keep entire LastBlock instead of just the blockNum - In Stats, add lastL1BatchBlock - Test Stats and SCVars - Coordinator: - Enable writing the BatchInfo in every step of the pipeline to disk (with JSON text files) for debugging purposes. - Move the Pipeline functionality from the Coordinator to its own struct (Pipeline) - Implement shouldL1lL2Batch - In TxManager, implement logic to perform several attempts when doing ethereum node RPC calls before considering the error. (Both for calls to forgeBatch and transaction receipt) - In TxManager, reorganize the flow and note the specific points in which actions are made when err != nil - HistoryDB: - Implement GetLastL1BatchBlockNum: returns the blockNum of the latest forged l1Batch, to help the coordinator decide when to forge an L1Batch. - EthereumClient and test.Client: - Update EthBlockByNumber to return the last block when the passed number is -1.
3 years ago
  1. package eth
  2. import (
  3. "context"
  4. "fmt"
  5. "math/big"
  6. "time"
  7. "github.com/ethereum/go-ethereum/accounts"
  8. "github.com/ethereum/go-ethereum/accounts/abi/bind"
  9. ethKeystore "github.com/ethereum/go-ethereum/accounts/keystore"
  10. ethCommon "github.com/ethereum/go-ethereum/common"
  11. "github.com/ethereum/go-ethereum/core/types"
  12. "github.com/ethereum/go-ethereum/ethclient"
  13. "github.com/hermeznetwork/hermez-node/common"
  14. HEZ "github.com/hermeznetwork/hermez-node/eth/contracts/tokenHEZ"
  15. "github.com/hermeznetwork/hermez-node/log"
  16. "github.com/hermeznetwork/tracerr"
  17. )
  18. // ERC20Consts are the constants defined in a particular ERC20 Token instance
  19. type ERC20Consts struct {
  20. Name string
  21. Symbol string
  22. Decimals uint64
  23. }
  24. // EthereumInterface is the interface to Ethereum
  25. type EthereumInterface interface {
  26. EthLastBlock() (int64, error)
  27. // EthHeaderByNumber(context.Context, *big.Int) (*types.Header, error)
  28. EthBlockByNumber(context.Context, int64) (*common.Block, error)
  29. EthAddress() (*ethCommon.Address, error)
  30. EthTransactionReceipt(context.Context, ethCommon.Hash) (*types.Receipt, error)
  31. EthERC20Consts(ethCommon.Address) (*ERC20Consts, error)
  32. EthChainID() (*big.Int, error)
  33. }
  34. var (
  35. // ErrAccountNil is used when the calls can not be made because the account is nil
  36. ErrAccountNil = fmt.Errorf("Authorized calls can't be made when the account is nil")
  37. // ErrBlockHashMismatchEvent is used when there's a block hash mismatch
  38. // beetween different events of the same block
  39. ErrBlockHashMismatchEvent = fmt.Errorf("block hash mismatch in event log")
  40. )
  41. const (
  42. // default values
  43. defaultCallGasLimit = 300000
  44. defaultGasPriceDiv = 100
  45. )
  46. // EthereumConfig defines the configuration parameters of the EthereumClient
  47. type EthereumConfig struct {
  48. CallGasLimit uint64
  49. GasPriceDiv uint64
  50. }
  51. // EthereumClient is an ethereum client to call Smart Contract methods and check blockchain information.
  52. type EthereumClient struct {
  53. client *ethclient.Client
  54. chainID *big.Int
  55. account *accounts.Account
  56. ks *ethKeystore.KeyStore
  57. config *EthereumConfig
  58. opts *bind.CallOpts
  59. }
  60. // NewEthereumClient creates a EthereumClient instance. The account is not mandatory (it can
  61. // be nil). If the account is nil, CallAuth will fail with ErrAccountNil.
  62. func NewEthereumClient(client *ethclient.Client, account *accounts.Account, ks *ethKeystore.KeyStore, config *EthereumConfig) (*EthereumClient, error) {
  63. if config == nil {
  64. config = &EthereumConfig{
  65. CallGasLimit: defaultCallGasLimit,
  66. GasPriceDiv: defaultGasPriceDiv,
  67. }
  68. }
  69. c := &EthereumClient{
  70. client: client,
  71. account: account,
  72. ks: ks,
  73. config: config,
  74. opts: newCallOpts(),
  75. }
  76. chainID, err := c.EthChainID()
  77. if err != nil {
  78. return nil, tracerr.Wrap(err)
  79. }
  80. c.chainID = chainID
  81. return c, nil
  82. }
  83. // EthChainID returns the ChainID of the ethereum network
  84. func (c *EthereumClient) EthChainID() (*big.Int, error) {
  85. chainID, err := c.client.ChainID(context.Background())
  86. if err != nil {
  87. return nil, tracerr.Wrap(err)
  88. }
  89. return chainID, nil
  90. }
  91. // BalanceAt retieves information about the default account
  92. func (c *EthereumClient) BalanceAt(addr ethCommon.Address) (*big.Int, error) {
  93. return c.client.BalanceAt(context.TODO(), addr, nil)
  94. }
  95. // Account returns the underlying ethereum account
  96. func (c *EthereumClient) Account() *accounts.Account {
  97. return c.account
  98. }
  99. // EthAddress returns the ethereum address of the account loaded into the EthereumClient
  100. func (c *EthereumClient) EthAddress() (*ethCommon.Address, error) {
  101. if c.account == nil {
  102. return nil, tracerr.Wrap(ErrAccountNil)
  103. }
  104. return &c.account.Address, nil
  105. }
  106. // CallAuth performs a Smart Contract method call that requires authorization.
  107. // This call requires a valid account with Ether that can be spend during the
  108. // call.
  109. func (c *EthereumClient) CallAuth(gasLimit uint64,
  110. fn func(*ethclient.Client, *bind.TransactOpts) (*types.Transaction, error)) (*types.Transaction, error) {
  111. if c.account == nil {
  112. return nil, tracerr.Wrap(ErrAccountNil)
  113. }
  114. gasPrice, err := c.client.SuggestGasPrice(context.Background())
  115. if err != nil {
  116. return nil, tracerr.Wrap(err)
  117. }
  118. inc := new(big.Int).Set(gasPrice)
  119. inc.Div(inc, new(big.Int).SetUint64(c.config.GasPriceDiv))
  120. gasPrice.Add(gasPrice, inc)
  121. log.Debugw("Transaction metadata", "gasPrice", gasPrice)
  122. auth, err := bind.NewKeyStoreTransactorWithChainID(c.ks, *c.account, c.chainID)
  123. if err != nil {
  124. return nil, tracerr.Wrap(err)
  125. }
  126. auth.Value = big.NewInt(0) // in wei
  127. if gasLimit == 0 {
  128. auth.GasLimit = c.config.CallGasLimit // in units
  129. } else {
  130. auth.GasLimit = gasLimit // in units
  131. }
  132. auth.GasPrice = gasPrice
  133. tx, err := fn(c.client, auth)
  134. if tx != nil {
  135. log.Debugw("Transaction", "tx", tx.Hash().Hex(), "nonce", tx.Nonce())
  136. }
  137. return tx, tracerr.Wrap(err)
  138. }
  139. // ContractData contains the contract data
  140. type ContractData struct {
  141. Address ethCommon.Address
  142. Tx *types.Transaction
  143. Receipt *types.Receipt
  144. }
  145. // Call performs a read only Smart Contract method call.
  146. func (c *EthereumClient) Call(fn func(*ethclient.Client) error) error {
  147. return fn(c.client)
  148. }
  149. // EthTransactionReceipt returns the transaction receipt of the given txHash
  150. func (c *EthereumClient) EthTransactionReceipt(ctx context.Context, txHash ethCommon.Hash) (*types.Receipt, error) {
  151. return c.client.TransactionReceipt(ctx, txHash)
  152. }
  153. // EthLastBlock returns the last block number in the blockchain
  154. func (c *EthereumClient) EthLastBlock() (int64, error) {
  155. ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
  156. defer cancel()
  157. header, err := c.client.HeaderByNumber(ctx, nil)
  158. if err != nil {
  159. return 0, tracerr.Wrap(err)
  160. }
  161. return header.Number.Int64(), nil
  162. }
  163. // EthHeaderByNumber internally calls ethclient.Client HeaderByNumber
  164. // func (c *EthereumClient) EthHeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
  165. // return c.client.HeaderByNumber(ctx, number)
  166. // }
  167. // EthBlockByNumber internally calls ethclient.Client BlockByNumber and returns
  168. // *common.Block. If number == -1, the latests known block is returned.
  169. func (c *EthereumClient) EthBlockByNumber(ctx context.Context, number int64) (*common.Block, error) {
  170. blockNum := big.NewInt(number)
  171. if number == -1 {
  172. blockNum = nil
  173. }
  174. block, err := c.client.BlockByNumber(ctx, blockNum)
  175. if err != nil {
  176. return nil, tracerr.Wrap(err)
  177. }
  178. b := &common.Block{
  179. Num: block.Number().Int64(),
  180. Timestamp: time.Unix(int64(block.Time()), 0),
  181. ParentHash: block.ParentHash(),
  182. Hash: block.Hash(),
  183. }
  184. return b, nil
  185. }
  186. // EthERC20Consts returns the constants defined for a particular ERC20 Token instance.
  187. func (c *EthereumClient) EthERC20Consts(tokenAddress ethCommon.Address) (*ERC20Consts, error) {
  188. // We use the HEZ token smart contract interfacehere because it's an
  189. // ERC20, which allows us to access the standard ERC20 constants.
  190. instance, err := HEZ.NewHEZ(tokenAddress, c.client)
  191. if err != nil {
  192. return nil, tracerr.Wrap(err)
  193. }
  194. name, err := instance.Name(c.opts)
  195. if err != nil {
  196. return nil, tracerr.Wrap(err)
  197. }
  198. symbol, err := instance.Symbol(c.opts)
  199. if err != nil {
  200. return nil, tracerr.Wrap(err)
  201. }
  202. decimals, err := instance.Decimals(c.opts)
  203. if err != nil {
  204. return nil, tracerr.Wrap(err)
  205. }
  206. return &ERC20Consts{
  207. Name: name,
  208. Symbol: symbol,
  209. Decimals: uint64(decimals),
  210. }, nil
  211. }
  212. // Client returns the internal ethclient.Client
  213. func (c *EthereumClient) Client() *ethclient.Client {
  214. return c.client
  215. }
  216. // newCallOpts returns a CallOpts to be used in ethereum calls with a non-zero
  217. // From address. This is a workaround for a bug in ethereumjs-vm that shows up
  218. // in ganache: https://github.com/hermeznetwork/hermez-node/issues/317
  219. func newCallOpts() *bind.CallOpts {
  220. return &bind.CallOpts{
  221. From: ethCommon.HexToAddress("0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"),
  222. }
  223. }