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.

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