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.

329 lines
10 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"
  8. "github.com/ethereum/go-ethereum/accounts"
  9. "github.com/ethereum/go-ethereum/accounts/abi/bind"
  10. ethKeystore "github.com/ethereum/go-ethereum/accounts/keystore"
  11. ethCommon "github.com/ethereum/go-ethereum/common"
  12. "github.com/ethereum/go-ethereum/core/types"
  13. "github.com/ethereum/go-ethereum/ethclient"
  14. "github.com/hermeznetwork/hermez-node/common"
  15. HEZ "github.com/hermeznetwork/hermez-node/eth/contracts/tokenHEZ"
  16. "github.com/hermeznetwork/hermez-node/log"
  17. "github.com/hermeznetwork/tracerr"
  18. )
  19. // ERC20Consts are the constants defined in a particular ERC20 Token instance
  20. type ERC20Consts struct {
  21. Name string
  22. Symbol string
  23. Decimals uint64
  24. }
  25. // EthereumInterface is the interface to Ethereum
  26. type EthereumInterface interface {
  27. EthLastBlock() (int64, error)
  28. // EthHeaderByNumber(context.Context, *big.Int) (*types.Header, error)
  29. EthBlockByNumber(context.Context, int64) (*common.Block, error)
  30. EthAddress() (*ethCommon.Address, error)
  31. EthTransactionReceipt(context.Context, ethCommon.Hash) (*types.Receipt, error)
  32. EthERC20Consts(ethCommon.Address) (*ERC20Consts, error)
  33. EthChainID() (*big.Int, error)
  34. EthPendingNonceAt(ctx context.Context, account ethCommon.Address) (uint64, error)
  35. EthNonceAt(ctx context.Context, account ethCommon.Address, blockNumber *big.Int) (uint64, error)
  36. EthSuggestGasPrice(ctx context.Context) (*big.Int, error)
  37. EthKeyStore() *ethKeystore.KeyStore
  38. EthCall(ctx context.Context, tx *types.Transaction, blockNum *big.Int) ([]byte, error)
  39. }
  40. var (
  41. // ErrAccountNil is used when the calls can not be made because the account is nil
  42. ErrAccountNil = fmt.Errorf("Authorized calls can't be made when the account is nil")
  43. // ErrBlockHashMismatchEvent is used when there's a block hash mismatch
  44. // beetween different events of the same block
  45. ErrBlockHashMismatchEvent = fmt.Errorf("block hash mismatch in event log")
  46. )
  47. const (
  48. // default values
  49. defaultCallGasLimit = 300000
  50. defaultGasPriceDiv = 100
  51. )
  52. // EthereumConfig defines the configuration parameters of the EthereumClient
  53. type EthereumConfig struct {
  54. CallGasLimit uint64
  55. GasPriceDiv uint64
  56. }
  57. // EthereumClient is an ethereum client to call Smart Contract methods and check blockchain information.
  58. type EthereumClient struct {
  59. client *ethclient.Client
  60. chainID *big.Int
  61. account *accounts.Account
  62. ks *ethKeystore.KeyStore
  63. config *EthereumConfig
  64. opts *bind.CallOpts
  65. }
  66. // NewEthereumClient creates a EthereumClient instance. The account is not mandatory (it can
  67. // be nil). If the account is nil, CallAuth will fail with ErrAccountNil.
  68. func NewEthereumClient(client *ethclient.Client, account *accounts.Account, ks *ethKeystore.KeyStore, config *EthereumConfig) (*EthereumClient, error) {
  69. if config == nil {
  70. config = &EthereumConfig{
  71. CallGasLimit: defaultCallGasLimit,
  72. GasPriceDiv: defaultGasPriceDiv,
  73. }
  74. }
  75. c := &EthereumClient{
  76. client: client,
  77. account: account,
  78. ks: ks,
  79. config: config,
  80. opts: newCallOpts(),
  81. }
  82. chainID, err := c.EthChainID()
  83. if err != nil {
  84. return nil, tracerr.Wrap(err)
  85. }
  86. c.chainID = chainID
  87. return c, nil
  88. }
  89. // EthChainID returns the ChainID of the ethereum network
  90. func (c *EthereumClient) EthChainID() (*big.Int, error) {
  91. chainID, err := c.client.ChainID(context.Background())
  92. if err != nil {
  93. return nil, tracerr.Wrap(err)
  94. }
  95. return chainID, nil
  96. }
  97. // BalanceAt retieves information about the default account
  98. func (c *EthereumClient) BalanceAt(addr ethCommon.Address) (*big.Int, error) {
  99. return c.client.BalanceAt(context.TODO(), addr, nil)
  100. }
  101. // Account returns the underlying ethereum account
  102. func (c *EthereumClient) Account() *accounts.Account {
  103. return c.account
  104. }
  105. // EthAddress returns the ethereum address of the account loaded into the EthereumClient
  106. func (c *EthereumClient) EthAddress() (*ethCommon.Address, error) {
  107. if c.account == nil {
  108. return nil, tracerr.Wrap(ErrAccountNil)
  109. }
  110. return &c.account.Address, nil
  111. }
  112. // EthSuggestGasPrice retrieves the currently suggested gas price to allow a
  113. // timely execution of a transaction.
  114. func (c *EthereumClient) EthSuggestGasPrice(ctx context.Context) (*big.Int, error) {
  115. return c.client.SuggestGasPrice(ctx)
  116. }
  117. // EthKeyStore returns the keystore in the EthereumClient
  118. func (c *EthereumClient) EthKeyStore() *ethKeystore.KeyStore {
  119. return c.ks
  120. }
  121. // NewAuth builds a new auth object to make a transaction
  122. func (c *EthereumClient) NewAuth() (*bind.TransactOpts, error) {
  123. if c.account == nil {
  124. return nil, tracerr.Wrap(ErrAccountNil)
  125. }
  126. gasPrice, err := c.client.SuggestGasPrice(context.Background())
  127. if err != nil {
  128. return nil, tracerr.Wrap(err)
  129. }
  130. inc := new(big.Int).Set(gasPrice)
  131. inc.Div(inc, new(big.Int).SetUint64(c.config.GasPriceDiv))
  132. gasPrice.Add(gasPrice, inc)
  133. log.Debugw("Transaction metadata", "gasPrice", gasPrice)
  134. auth, err := bind.NewKeyStoreTransactorWithChainID(c.ks, *c.account, c.chainID)
  135. if err != nil {
  136. return nil, tracerr.Wrap(err)
  137. }
  138. auth.Value = big.NewInt(0) // in wei
  139. auth.GasLimit = c.config.CallGasLimit
  140. auth.GasPrice = gasPrice
  141. return auth, nil
  142. }
  143. // CallAuth performs a Smart Contract method call that requires authorization.
  144. // This call requires a valid account with Ether that can be spend during the
  145. // call.
  146. func (c *EthereumClient) CallAuth(gasLimit uint64,
  147. fn func(*ethclient.Client, *bind.TransactOpts) (*types.Transaction, error)) (*types.Transaction, error) {
  148. if c.account == nil {
  149. return nil, tracerr.Wrap(ErrAccountNil)
  150. }
  151. gasPrice, err := c.client.SuggestGasPrice(context.Background())
  152. if err != nil {
  153. return nil, tracerr.Wrap(err)
  154. }
  155. inc := new(big.Int).Set(gasPrice)
  156. inc.Div(inc, new(big.Int).SetUint64(c.config.GasPriceDiv))
  157. gasPrice.Add(gasPrice, inc)
  158. log.Debugw("Transaction metadata", "gasPrice", gasPrice)
  159. auth, err := bind.NewKeyStoreTransactorWithChainID(c.ks, *c.account, c.chainID)
  160. if err != nil {
  161. return nil, tracerr.Wrap(err)
  162. }
  163. auth.Value = big.NewInt(0) // in wei
  164. if gasLimit == 0 {
  165. auth.GasLimit = c.config.CallGasLimit // in units
  166. } else {
  167. auth.GasLimit = gasLimit // in units
  168. }
  169. auth.GasPrice = gasPrice
  170. tx, err := fn(c.client, auth)
  171. if tx != nil {
  172. log.Debugw("Transaction", "tx", tx.Hash().Hex(), "nonce", tx.Nonce())
  173. }
  174. return tx, tracerr.Wrap(err)
  175. }
  176. // ContractData contains the contract data
  177. type ContractData struct {
  178. Address ethCommon.Address
  179. Tx *types.Transaction
  180. Receipt *types.Receipt
  181. }
  182. // Call performs a read only Smart Contract method call.
  183. func (c *EthereumClient) Call(fn func(*ethclient.Client) error) error {
  184. return fn(c.client)
  185. }
  186. // EthTransactionReceipt returns the transaction receipt of the given txHash
  187. func (c *EthereumClient) EthTransactionReceipt(ctx context.Context, txHash ethCommon.Hash) (*types.Receipt, error) {
  188. return c.client.TransactionReceipt(ctx, txHash)
  189. }
  190. // EthLastBlock returns the last block number in the blockchain
  191. func (c *EthereumClient) EthLastBlock() (int64, error) {
  192. ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
  193. defer cancel()
  194. header, err := c.client.HeaderByNumber(ctx, nil)
  195. if err != nil {
  196. return 0, tracerr.Wrap(err)
  197. }
  198. return header.Number.Int64(), nil
  199. }
  200. // EthHeaderByNumber internally calls ethclient.Client HeaderByNumber
  201. // func (c *EthereumClient) EthHeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
  202. // return c.client.HeaderByNumber(ctx, number)
  203. // }
  204. // EthBlockByNumber internally calls ethclient.Client BlockByNumber and returns
  205. // *common.Block. If number == -1, the latests known block is returned.
  206. func (c *EthereumClient) EthBlockByNumber(ctx context.Context, number int64) (*common.Block, error) {
  207. blockNum := big.NewInt(number)
  208. if number == -1 {
  209. blockNum = nil
  210. }
  211. block, err := c.client.BlockByNumber(ctx, blockNum)
  212. if err != nil {
  213. return nil, tracerr.Wrap(err)
  214. }
  215. b := &common.Block{
  216. Num: block.Number().Int64(),
  217. Timestamp: time.Unix(int64(block.Time()), 0),
  218. ParentHash: block.ParentHash(),
  219. Hash: block.Hash(),
  220. }
  221. return b, nil
  222. }
  223. // EthERC20Consts returns the constants defined for a particular ERC20 Token instance.
  224. func (c *EthereumClient) EthERC20Consts(tokenAddress ethCommon.Address) (*ERC20Consts, error) {
  225. // We use the HEZ token smart contract interfacehere because it's an
  226. // ERC20, which allows us to access the standard ERC20 constants.
  227. instance, err := HEZ.NewHEZ(tokenAddress, c.client)
  228. if err != nil {
  229. return nil, tracerr.Wrap(err)
  230. }
  231. name, err := instance.Name(c.opts)
  232. if err != nil {
  233. return nil, tracerr.Wrap(err)
  234. }
  235. symbol, err := instance.Symbol(c.opts)
  236. if err != nil {
  237. return nil, tracerr.Wrap(err)
  238. }
  239. decimals, err := instance.Decimals(c.opts)
  240. if err != nil {
  241. return nil, tracerr.Wrap(err)
  242. }
  243. return &ERC20Consts{
  244. Name: name,
  245. Symbol: symbol,
  246. Decimals: uint64(decimals),
  247. }, nil
  248. }
  249. // Client returns the internal ethclient.Client
  250. func (c *EthereumClient) Client() *ethclient.Client {
  251. return c.client
  252. }
  253. // newCallOpts returns a CallOpts to be used in ethereum calls with a non-zero
  254. // From address. This is a workaround for a bug in ethereumjs-vm that shows up
  255. // in ganache: https://github.com/hermeznetwork/hermez-node/issues/317
  256. func newCallOpts() *bind.CallOpts {
  257. return &bind.CallOpts{
  258. From: ethCommon.HexToAddress("0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"),
  259. }
  260. }
  261. // EthPendingNonceAt returns the account nonce of the given account in the pending
  262. // state. This is the nonce that should be used for the next transaction.
  263. func (c *EthereumClient) EthPendingNonceAt(ctx context.Context,
  264. account ethCommon.Address) (uint64, error) {
  265. return c.client.PendingNonceAt(ctx, account)
  266. }
  267. // EthNonceAt returns the account nonce of the given account. The block number can
  268. // be nil, in which case the nonce is taken from the latest known block.
  269. func (c *EthereumClient) EthNonceAt(ctx context.Context,
  270. account ethCommon.Address, blockNumber *big.Int) (uint64, error) {
  271. return c.client.NonceAt(ctx, account, blockNumber)
  272. }
  273. // EthCall runs the transaction as a call (without paying) in the local node at
  274. // blockNum.
  275. func (c *EthereumClient) EthCall(ctx context.Context, tx *types.Transaction,
  276. blockNum *big.Int) ([]byte, error) {
  277. if c.account == nil {
  278. return nil, tracerr.Wrap(ErrAccountNil)
  279. }
  280. msg := ethereum.CallMsg{
  281. From: c.account.Address,
  282. To: tx.To(),
  283. Gas: tx.Gas(),
  284. GasPrice: tx.GasPrice(),
  285. Value: tx.Value(),
  286. Data: tx.Data(),
  287. }
  288. result, err := c.client.CallContract(ctx, msg, blockNum)
  289. return result, tracerr.Wrap(err)
  290. }