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.

932 lines
26 KiB

4 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
4 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.
4 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.
4 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
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.
4 years ago
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
3 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
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.
4 years ago
Allow serving API only via new cli command - Add new command to the cli/node: `serveapi` that alows serving the API just by connecting to the PostgreSQL database. The mode flag should me passed in order to select whether we are connecting to a synchronizer database or a coordinator database. If `coord` is chosen as mode, the coordinator endpoints can be activated in order to allow inserting l2txs and authorizations into the L2DB. Summary of the implementation details - New SQL table with 3 columns (plus `item_id` pk). The table only contains a single row with `item_id` = 1. Columns: - state: historydb.StateAPI in JSON. This is the struct that is served via the `/state` API endpoint. The node will periodically update this struct and store it int he DB. The api server will query it from the DB to serve it. - config: historydb.NodeConfig in JSON. This struct contains node configuration parameters that the API needs to be aware of. It's updated once every time the node starts. - constants: historydb.Constants in JSON. This struct contains all the hermez network constants gathered via the ethereum client by the node. It's written once every time the node starts. - The HistoryDB contains methods to get and update each one of these columns individually. - The HistoryDB contains all methods that query the DB and prepare objects that will appear in the StateAPI endpoint. - The configuration used in for the `serveapi` cli/node command is defined in `config.APIServer`, and is a subset of `node.Config` in order to allow reusing the same configuration file of the node if desired. - A new object is introduced in the api: `StateAPIUpdater`, which contains all the necessary information to update the StateAPI in the DB periodically by the node. - Moved the types `SCConsts`, `SCVariables` and `SCVariablesPtr` from `syncrhonizer` to `common` for convenience.
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.
4 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.
4 years ago
  1. package api
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "math/big"
  10. "net"
  11. "net/http"
  12. "os"
  13. "strconv"
  14. "sync"
  15. "testing"
  16. "time"
  17. ethCommon "github.com/ethereum/go-ethereum/common"
  18. swagger "github.com/getkin/kin-openapi/openapi3filter"
  19. "github.com/gin-gonic/gin"
  20. "github.com/hermeznetwork/hermez-node/api/stateapiupdater"
  21. "github.com/hermeznetwork/hermez-node/common"
  22. "github.com/hermeznetwork/hermez-node/db"
  23. "github.com/hermeznetwork/hermez-node/db/historydb"
  24. "github.com/hermeznetwork/hermez-node/db/l2db"
  25. "github.com/hermeznetwork/hermez-node/log"
  26. "github.com/hermeznetwork/hermez-node/test"
  27. "github.com/hermeznetwork/hermez-node/test/til"
  28. "github.com/hermeznetwork/hermez-node/test/txsets"
  29. "github.com/hermeznetwork/tracerr"
  30. "github.com/stretchr/testify/require"
  31. )
  32. // Pendinger is an interface that allows getting last returned item ID and PendingItems to be used for building fromItem
  33. // when testing paginated endpoints.
  34. type Pendinger interface {
  35. GetPending() (pendingItems, lastItemID uint64)
  36. Len() int
  37. New() Pendinger
  38. }
  39. const (
  40. apiPort = "4010"
  41. apiIP = "http://localhost:"
  42. apiURL = apiIP + apiPort + "/v1/"
  43. )
  44. var SetBlockchain = `
  45. Type: Blockchain
  46. AddToken(1)
  47. AddToken(2)
  48. AddToken(3)
  49. AddToken(4)
  50. AddToken(5)
  51. AddToken(6)
  52. AddToken(7)
  53. AddToken(8)
  54. > block
  55. // Coordinator accounts, Idxs: 256, 257
  56. CreateAccountCoordinator(0) Coord
  57. CreateAccountCoordinator(1) Coord
  58. // close Block:0, Batch:1
  59. > batch
  60. CreateAccountDeposit(0) A: 11100000000000000
  61. CreateAccountDeposit(1) C: 22222222200000000000
  62. CreateAccountCoordinator(0) C
  63. // close Block:0, Batch:2
  64. > batchL1
  65. // Expected balances:
  66. // Coord(0): 0, Coord(1): 0
  67. // C(0): 0
  68. CreateAccountDeposit(1) A: 33333333300000000000
  69. // close Block:0, Batch:3
  70. > batchL1
  71. // close Block:0, Batch:4
  72. > batchL1
  73. CreateAccountDepositTransfer(0) B-A: 44444444400000000000, 123444444400000000000
  74. // close Block:0, Batch:5
  75. > batchL1
  76. CreateAccountDeposit(0) D: 55555555500000000000
  77. // close Block:0, Batch:6
  78. > batchL1
  79. CreateAccountCoordinator(1) B
  80. Transfer(1) A-B: 11100000000000000 (2)
  81. Transfer(0) B-C: 22200000000000000 (3)
  82. // close Block:0, Batch:7
  83. > batchL1 // forge L1User{1}, forge L1Coord{2}, forge L2{2}
  84. Deposit(0) C: 66666666600000000000
  85. DepositTransfer(0) C-D: 77777777700000000000, 12377777700000000000
  86. Transfer(0) A-B: 33350000000000000 (111)
  87. Transfer(0) C-A: 44450000000000000 (222)
  88. Transfer(1) B-C: 55550000000000000 (123)
  89. Exit(0) A: 66650000000000000 (44)
  90. ForceTransfer(0) D-B: 77777700000000000
  91. ForceExit(0) B: 88888800000000000
  92. // close Block:0, Batch:8
  93. > batchL1
  94. > block
  95. Transfer(0) D-A: 99950000000000000 (77)
  96. Transfer(0) B-D: 12300000000000000 (55)
  97. // close Block:1, Batch:1
  98. > batchL1
  99. CreateAccountCoordinator(0) F
  100. CreateAccountCoordinator(0) G
  101. CreateAccountCoordinator(0) H
  102. CreateAccountCoordinator(0) I
  103. CreateAccountCoordinator(0) J
  104. CreateAccountCoordinator(0) K
  105. CreateAccountCoordinator(0) L
  106. CreateAccountCoordinator(0) M
  107. CreateAccountCoordinator(0) N
  108. CreateAccountCoordinator(0) O
  109. CreateAccountCoordinator(0) P
  110. CreateAccountCoordinator(5) G
  111. CreateAccountCoordinator(5) H
  112. CreateAccountCoordinator(5) I
  113. CreateAccountCoordinator(5) J
  114. CreateAccountCoordinator(5) K
  115. CreateAccountCoordinator(5) L
  116. CreateAccountCoordinator(5) M
  117. CreateAccountCoordinator(5) N
  118. CreateAccountCoordinator(5) O
  119. CreateAccountCoordinator(5) P
  120. CreateAccountCoordinator(2) G
  121. CreateAccountCoordinator(2) H
  122. CreateAccountCoordinator(2) I
  123. CreateAccountCoordinator(2) J
  124. CreateAccountCoordinator(2) K
  125. CreateAccountCoordinator(2) L
  126. CreateAccountCoordinator(2) M
  127. CreateAccountCoordinator(2) N
  128. CreateAccountCoordinator(2) O
  129. CreateAccountCoordinator(2) P
  130. > batch
  131. > block
  132. > batch
  133. > block
  134. > batch
  135. > block
  136. `
  137. type testCommon struct {
  138. blocks []common.Block
  139. tokens []historydb.TokenWithUSD
  140. batches []testBatch
  141. fullBatches []testFullBatch
  142. coordinators []historydb.CoordinatorAPI
  143. accounts []testAccount
  144. txs []testTx
  145. exits []testExit
  146. poolTxsToSend []testPoolTxSend
  147. poolTxsToReceive []testPoolTxReceive
  148. auths []testAuth
  149. router *swagger.Router
  150. bids []testBid
  151. slots []testSlot
  152. auctionVars common.AuctionVariables
  153. rollupVars common.RollupVariables
  154. wdelayerVars common.WDelayerVariables
  155. nextForgers []historydb.NextForgerAPI
  156. }
  157. var tc testCommon
  158. var config configAPI
  159. var api *API
  160. var stateAPIUpdater *stateapiupdater.Updater
  161. // TestMain initializes the API server, and fill HistoryDB and StateDB with fake data,
  162. // emulating the task of the synchronizer in order to have data to be returned
  163. // by the API endpoints that will be tested
  164. func TestMain(m *testing.M) {
  165. // Initializations
  166. // Swagger
  167. router := swagger.NewRouter().WithSwaggerFromFile("./swagger.yml")
  168. // HistoryDB
  169. pass := os.Getenv("POSTGRES_PASS")
  170. database, err := db.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
  171. if err != nil {
  172. panic(err)
  173. }
  174. apiConnCon := db.NewAPIConnectionController(1, time.Second)
  175. hdb := historydb.NewHistoryDB(database, database, apiConnCon)
  176. if err != nil {
  177. panic(err)
  178. }
  179. // L2DB
  180. l2DB := l2db.NewL2DB(database, database, 10, 1000, 0.0, 1000.0, 24*time.Hour, apiConnCon)
  181. test.WipeDB(l2DB.DB()) // this will clean HistoryDB and L2DB
  182. // Config (smart contract constants)
  183. chainID := uint16(0)
  184. _config := getConfigTest(chainID)
  185. config = configAPI{
  186. ChainID: chainID,
  187. RollupConstants: *newRollupConstants(_config.RollupConstants),
  188. AuctionConstants: _config.AuctionConstants,
  189. WDelayerConstants: _config.WDelayerConstants,
  190. }
  191. // API
  192. apiGin := gin.Default()
  193. // Reset DB
  194. test.WipeDB(hdb.DB())
  195. constants := &historydb.Constants{
  196. SCConsts: common.SCConsts{
  197. Rollup: _config.RollupConstants,
  198. Auction: _config.AuctionConstants,
  199. WDelayer: _config.WDelayerConstants,
  200. },
  201. ChainID: chainID,
  202. HermezAddress: _config.HermezAddress,
  203. }
  204. if err := hdb.SetConstants(constants); err != nil {
  205. panic(err)
  206. }
  207. nodeConfig := &historydb.NodeConfig{
  208. MaxPoolTxs: 10,
  209. MinFeeUSD: 0,
  210. MaxFeeUSD: 10000000000,
  211. }
  212. if err := hdb.SetNodeConfig(nodeConfig); err != nil {
  213. panic(err)
  214. }
  215. api, err = NewAPI(
  216. true,
  217. true,
  218. apiGin,
  219. hdb,
  220. l2DB,
  221. )
  222. if err != nil {
  223. log.Error(err)
  224. panic(err)
  225. }
  226. // Start server
  227. listener, err := net.Listen("tcp", ":"+apiPort) //nolint:gosec
  228. if err != nil {
  229. panic(err)
  230. }
  231. server := &http.Server{Handler: apiGin}
  232. go func() {
  233. if err := server.Serve(listener); err != nil &&
  234. tracerr.Unwrap(err) != http.ErrServerClosed {
  235. panic(err)
  236. }
  237. }()
  238. // Generate blockchain data with til
  239. tcc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
  240. tilCfgExtra := til.ConfigExtra{
  241. BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
  242. CoordUser: "Coord",
  243. }
  244. blocksData, err := tcc.GenerateBlocks(SetBlockchain)
  245. if err != nil {
  246. panic(err)
  247. }
  248. err = tcc.FillBlocksExtra(blocksData, &tilCfgExtra)
  249. if err != nil {
  250. panic(err)
  251. }
  252. err = tcc.FillBlocksForgedL1UserTxs(blocksData)
  253. if err != nil {
  254. panic(err)
  255. }
  256. AddAditionalInformation(blocksData)
  257. // Generate L2 Txs with til
  258. commonPoolTxs, err := tcc.GeneratePoolL2Txs(txsets.SetPoolL2MinimumFlow0)
  259. if err != nil {
  260. panic(err)
  261. }
  262. // Extract til generated data, and add it to HistoryDB
  263. var commonBlocks []common.Block
  264. var commonBatches []common.Batch
  265. var commonAccounts []common.Account
  266. var commonExitTree []common.ExitInfo
  267. var commonL1Txs []common.L1Tx
  268. var commonL2Txs []common.L2Tx
  269. // Add ETH token at the beginning of the array
  270. testTokens := []historydb.TokenWithUSD{}
  271. ethUSD := float64(500)
  272. ethNow := time.Now()
  273. testTokens = append(testTokens, historydb.TokenWithUSD{
  274. TokenID: test.EthToken.TokenID,
  275. EthBlockNum: test.EthToken.EthBlockNum,
  276. EthAddr: test.EthToken.EthAddr,
  277. Name: test.EthToken.Name,
  278. Symbol: test.EthToken.Symbol,
  279. Decimals: test.EthToken.Decimals,
  280. USD: &ethUSD,
  281. USDUpdate: &ethNow,
  282. })
  283. err = api.h.UpdateTokenValue(common.EmptyAddr, ethUSD)
  284. if err != nil {
  285. panic(err)
  286. }
  287. for _, block := range blocksData {
  288. // Insert block into HistoryDB
  289. // nolint reason: block is used as read only in the function
  290. if err := api.h.AddBlockSCData(&block); err != nil { //nolint:gosec
  291. log.Error(err)
  292. panic(err)
  293. }
  294. // Extract data
  295. commonBlocks = append(commonBlocks, block.Block)
  296. for i, tkn := range block.Rollup.AddedTokens {
  297. token := historydb.TokenWithUSD{
  298. TokenID: tkn.TokenID,
  299. EthBlockNum: tkn.EthBlockNum,
  300. EthAddr: tkn.EthAddr,
  301. Name: tkn.Name,
  302. Symbol: tkn.Symbol,
  303. Decimals: tkn.Decimals,
  304. }
  305. value := float64(i + 423)
  306. now := time.Now().UTC()
  307. token.USD = &value
  308. token.USDUpdate = &now
  309. // Set value in DB
  310. err = api.h.UpdateTokenValue(token.EthAddr, value)
  311. if err != nil {
  312. panic(err)
  313. }
  314. testTokens = append(testTokens, token)
  315. }
  316. // Set USD value for tokens in DB
  317. for _, batch := range block.Rollup.Batches {
  318. commonL2Txs = append(commonL2Txs, batch.L2Txs...)
  319. for i := range batch.CreatedAccounts {
  320. batch.CreatedAccounts[i].Nonce = common.Nonce(i)
  321. commonAccounts = append(commonAccounts, batch.CreatedAccounts[i])
  322. }
  323. commonBatches = append(commonBatches, batch.Batch)
  324. commonExitTree = append(commonExitTree, batch.ExitTree...)
  325. commonL1Txs = append(commonL1Txs, batch.L1UserTxs...)
  326. commonL1Txs = append(commonL1Txs, batch.L1CoordinatorTxs...)
  327. }
  328. }
  329. // Generate Coordinators and add them to HistoryDB
  330. const nCoords = 10
  331. commonCoords := test.GenCoordinators(nCoords, commonBlocks)
  332. // Update one coordinator to test behaviour when bidder address is repeated
  333. updatedCoordBlock := commonCoords[len(commonCoords)-1].EthBlockNum
  334. commonCoords = append(commonCoords, common.Coordinator{
  335. Bidder: commonCoords[0].Bidder,
  336. Forger: commonCoords[0].Forger,
  337. EthBlockNum: updatedCoordBlock,
  338. URL: commonCoords[0].URL + ".new",
  339. })
  340. if err := api.h.AddCoordinators(commonCoords); err != nil {
  341. panic(err)
  342. }
  343. // Test next forgers
  344. // Set auction vars
  345. // Slots 3 and 6 will have bids that will be invalidated because of minBid update
  346. // Slots 4 and 7 will have valid bids, the rest will be cordinator slots
  347. var slot3MinBid int64 = 3
  348. var slot4MinBid int64 = 4
  349. var slot6MinBid int64 = 6
  350. var slot7MinBid int64 = 7
  351. // First update will indicate how things behave from slot 0
  352. var defaultSlotSetBid [6]*big.Int = [6]*big.Int{
  353. big.NewInt(10), // Slot 0 min bid
  354. big.NewInt(10), // Slot 1 min bid
  355. big.NewInt(10), // Slot 2 min bid
  356. big.NewInt(slot3MinBid), // Slot 3 min bid
  357. big.NewInt(slot4MinBid), // Slot 4 min bid
  358. big.NewInt(10), // Slot 5 min bid
  359. }
  360. auctionVars := common.AuctionVariables{
  361. EthBlockNum: int64(2),
  362. DonationAddress: ethCommon.HexToAddress("0x1111111111111111111111111111111111111111"),
  363. DefaultSlotSetBid: defaultSlotSetBid,
  364. DefaultSlotSetBidSlotNum: 0,
  365. Outbidding: uint16(1),
  366. SlotDeadline: uint8(20),
  367. BootCoordinator: ethCommon.HexToAddress("0x1111111111111111111111111111111111111111"),
  368. BootCoordinatorURL: "https://boot.coordinator.io",
  369. ClosedAuctionSlots: uint16(10),
  370. OpenAuctionSlots: uint16(20),
  371. }
  372. if err := api.h.AddAuctionVars(&auctionVars); err != nil {
  373. panic(err)
  374. }
  375. // Last update in auction vars will indicate how things will behave from slot 5
  376. defaultSlotSetBid = [6]*big.Int{
  377. big.NewInt(10), // Slot 5 min bid
  378. big.NewInt(slot6MinBid), // Slot 6 min bid
  379. big.NewInt(slot7MinBid), // Slot 7 min bid
  380. big.NewInt(10), // Slot 8 min bid
  381. big.NewInt(10), // Slot 9 min bid
  382. big.NewInt(10), // Slot 10 min bid
  383. }
  384. auctionVars = common.AuctionVariables{
  385. EthBlockNum: int64(3),
  386. DonationAddress: ethCommon.HexToAddress("0x1111111111111111111111111111111111111111"),
  387. DefaultSlotSetBid: defaultSlotSetBid,
  388. DefaultSlotSetBidSlotNum: 5,
  389. Outbidding: uint16(1),
  390. SlotDeadline: uint8(20),
  391. BootCoordinator: ethCommon.HexToAddress("0x1111111111111111111111111111111111111111"),
  392. BootCoordinatorURL: "https://boot.coordinator.io",
  393. ClosedAuctionSlots: uint16(10),
  394. OpenAuctionSlots: uint16(20),
  395. }
  396. if err := api.h.AddAuctionVars(&auctionVars); err != nil {
  397. panic(err)
  398. }
  399. // Generate Bids and add them to HistoryDB
  400. bids := []common.Bid{}
  401. // Slot 1 and 2, no bids, wins boot coordinator
  402. // Slot 3, below what's going to be the minimum (wins boot coordinator)
  403. bids = append(bids, common.Bid{
  404. SlotNum: 3,
  405. BidValue: big.NewInt(slot3MinBid - 1),
  406. EthBlockNum: commonBlocks[0].Num,
  407. Bidder: commonCoords[0].Bidder,
  408. })
  409. // Slot 4, valid bid (wins bidder)
  410. bids = append(bids, common.Bid{
  411. SlotNum: 4,
  412. BidValue: big.NewInt(slot4MinBid),
  413. EthBlockNum: commonBlocks[0].Num,
  414. Bidder: commonCoords[0].Bidder,
  415. })
  416. // Slot 5 no bids, wins boot coordinator
  417. // Slot 6, below what's going to be the minimum (wins boot coordinator)
  418. bids = append(bids, common.Bid{
  419. SlotNum: 6,
  420. BidValue: big.NewInt(slot6MinBid - 1),
  421. EthBlockNum: commonBlocks[0].Num,
  422. Bidder: commonCoords[0].Bidder,
  423. })
  424. // Slot 7, valid bid (wins bidder)
  425. bids = append(bids, common.Bid{
  426. SlotNum: 7,
  427. BidValue: big.NewInt(slot7MinBid),
  428. EthBlockNum: commonBlocks[0].Num,
  429. Bidder: commonCoords[0].Bidder,
  430. })
  431. if err = api.h.AddBids(bids); err != nil {
  432. panic(err)
  433. }
  434. bootForger := historydb.NextForgerAPI{
  435. Coordinator: historydb.CoordinatorAPI{
  436. Forger: auctionVars.BootCoordinator,
  437. URL: auctionVars.BootCoordinatorURL,
  438. },
  439. }
  440. // Set next forgers: set all as boot coordinator then replace the non boot coordinators
  441. nextForgers := []historydb.NextForgerAPI{}
  442. var initBlock int64 = 140
  443. var deltaBlocks int64 = 40
  444. for i := 1; i < int(auctionVars.ClosedAuctionSlots)+2; i++ {
  445. fromBlock := initBlock + deltaBlocks*int64(i-1)
  446. bootForger.Period = historydb.Period{
  447. SlotNum: int64(i),
  448. FromBlock: fromBlock,
  449. ToBlock: fromBlock + deltaBlocks - 1,
  450. }
  451. nextForgers = append(nextForgers, bootForger)
  452. }
  453. // Set next forgers that aren't the boot coordinator
  454. nonBootForger := historydb.CoordinatorAPI{
  455. Bidder: commonCoords[0].Bidder,
  456. Forger: commonCoords[0].Forger,
  457. URL: commonCoords[0].URL + ".new",
  458. }
  459. // Slot 4
  460. nextForgers[3].Coordinator = nonBootForger
  461. // Slot 7
  462. nextForgers[6].Coordinator = nonBootForger
  463. var buckets [common.RollupConstNumBuckets]common.BucketParams
  464. for i := range buckets {
  465. buckets[i].CeilUSD = big.NewInt(int64(i) * 10)
  466. buckets[i].Withdrawals = big.NewInt(int64(i) * 100)
  467. buckets[i].BlockWithdrawalRate = big.NewInt(int64(i) * 1000)
  468. buckets[i].MaxWithdrawals = big.NewInt(int64(i) * 10000)
  469. }
  470. // Generate SC vars and add them to HistoryDB (if needed)
  471. rollupVars := common.RollupVariables{
  472. EthBlockNum: int64(3),
  473. FeeAddToken: big.NewInt(100),
  474. ForgeL1L2BatchTimeout: int64(44),
  475. WithdrawalDelay: uint64(3000),
  476. Buckets: buckets,
  477. SafeMode: false,
  478. }
  479. wdelayerVars := common.WDelayerVariables{
  480. WithdrawalDelay: uint64(3000),
  481. }
  482. stateAPIUpdater, err = stateapiupdater.NewUpdater(hdb, nodeConfig, &common.SCVariables{
  483. Rollup: rollupVars,
  484. Auction: auctionVars,
  485. WDelayer: wdelayerVars,
  486. }, constants, &stateapiupdater.RecommendedFeePolicy{
  487. PolicyType: stateapiupdater.RecommendedFeePolicyTypeAvgLastHour,
  488. })
  489. if err != nil {
  490. panic(err)
  491. }
  492. // Generate test data, as expected to be received/sended from/to the API
  493. testCoords := genTestCoordinators(commonCoords)
  494. testBids := genTestBids(commonBlocks, testCoords, bids)
  495. testExits := genTestExits(commonExitTree, testTokens, commonAccounts)
  496. testTxs := genTestTxs(commonL1Txs, commonL2Txs, commonAccounts, testTokens, commonBlocks)
  497. testBatches, testFullBatches := genTestBatches(commonBlocks, commonBatches, testTxs)
  498. poolTxsToSend, poolTxsToReceive := genTestPoolTxs(commonPoolTxs, testTokens, commonAccounts)
  499. // Add balance and nonce to historyDB
  500. accounts := genTestAccounts(commonAccounts, testTokens)
  501. accUpdates := []common.AccountUpdate{}
  502. for i := 0; i < len(accounts); i++ {
  503. balance := new(big.Int)
  504. balance.SetString(string(*accounts[i].Balance), 10)
  505. idx, err := stringToIdx(string(accounts[i].Idx), "foo")
  506. if err != nil {
  507. panic(err)
  508. }
  509. accUpdates = append(accUpdates, common.AccountUpdate{
  510. EthBlockNum: 0,
  511. BatchNum: 1,
  512. Idx: *idx,
  513. Nonce: 0,
  514. Balance: balance,
  515. })
  516. accUpdates = append(accUpdates, common.AccountUpdate{
  517. EthBlockNum: 0,
  518. BatchNum: 1,
  519. Idx: *idx,
  520. Nonce: accounts[i].Nonce,
  521. Balance: balance,
  522. })
  523. }
  524. if err := api.h.AddAccountUpdates(accUpdates); err != nil {
  525. panic(err)
  526. }
  527. tc = testCommon{
  528. blocks: commonBlocks,
  529. tokens: testTokens,
  530. batches: testBatches,
  531. fullBatches: testFullBatches,
  532. coordinators: testCoords,
  533. accounts: accounts,
  534. txs: testTxs,
  535. exits: testExits,
  536. poolTxsToSend: poolTxsToSend,
  537. poolTxsToReceive: poolTxsToReceive,
  538. auths: genTestAuths(test.GenAuths(5, _config.ChainID, _config.HermezAddress)),
  539. router: router,
  540. bids: testBids,
  541. slots: api.genTestSlots(
  542. 20,
  543. commonBlocks[len(commonBlocks)-1].Num,
  544. testBids,
  545. auctionVars,
  546. ),
  547. auctionVars: auctionVars,
  548. rollupVars: rollupVars,
  549. wdelayerVars: wdelayerVars,
  550. nextForgers: nextForgers,
  551. }
  552. // Run tests
  553. result := m.Run()
  554. // Fake server
  555. if os.Getenv("FAKE_SERVER") == "yes" {
  556. for {
  557. log.Info("Running fake server at " + apiURL + " until ^C is received")
  558. time.Sleep(30 * time.Second)
  559. }
  560. }
  561. // Stop server
  562. if err := server.Shutdown(context.Background()); err != nil {
  563. panic(err)
  564. }
  565. if err := database.Close(); err != nil {
  566. panic(err)
  567. }
  568. os.Exit(result)
  569. }
  570. func TestTimeout(t *testing.T) {
  571. pass := os.Getenv("POSTGRES_PASS")
  572. databaseTO, err := db.ConnectSQLDB(5432, "localhost", "hermez", pass, "hermez")
  573. require.NoError(t, err)
  574. apiConnConTO := db.NewAPIConnectionController(1, 100*time.Millisecond)
  575. hdbTO := historydb.NewHistoryDB(databaseTO, databaseTO, apiConnConTO)
  576. require.NoError(t, err)
  577. // L2DB
  578. l2DBTO := l2db.NewL2DB(databaseTO, databaseTO, 10, 1000, 1.0, 1000.0, 24*time.Hour, apiConnConTO)
  579. // API
  580. apiGinTO := gin.Default()
  581. finishWait := make(chan interface{})
  582. startWait := make(chan interface{})
  583. apiGinTO.GET("/v1/wait", func(c *gin.Context) {
  584. cancel, err := apiConnConTO.Acquire()
  585. defer cancel()
  586. require.NoError(t, err)
  587. defer apiConnConTO.Release()
  588. startWait <- nil
  589. <-finishWait
  590. })
  591. // Start server
  592. serverTO := &http.Server{Handler: apiGinTO}
  593. listener, err := net.Listen("tcp", ":4444") //nolint:gosec
  594. require.NoError(t, err)
  595. go func() {
  596. if err := serverTO.Serve(listener); err != nil &&
  597. tracerr.Unwrap(err) != http.ErrServerClosed {
  598. require.NoError(t, err)
  599. }
  600. }()
  601. _, err = NewAPI(
  602. true,
  603. true,
  604. apiGinTO,
  605. hdbTO,
  606. l2DBTO,
  607. )
  608. require.NoError(t, err)
  609. client := &http.Client{}
  610. httpReq, err := http.NewRequest("GET", "http://localhost:4444/v1/tokens", nil)
  611. require.NoError(t, err)
  612. httpReqWait, err := http.NewRequest("GET", "http://localhost:4444/v1/wait", nil)
  613. require.NoError(t, err)
  614. // Request that will get timed out
  615. var wg sync.WaitGroup
  616. wg.Add(1)
  617. go func() {
  618. // Request that will make the API busy
  619. _, err = client.Do(httpReqWait)
  620. require.NoError(t, err)
  621. wg.Done()
  622. }()
  623. <-startWait
  624. resp, err := client.Do(httpReq)
  625. require.NoError(t, err)
  626. require.Equal(t, http.StatusServiceUnavailable, resp.StatusCode)
  627. defer resp.Body.Close() //nolint
  628. body, err := ioutil.ReadAll(resp.Body)
  629. require.NoError(t, err)
  630. // Unmarshal body into return struct
  631. msg := &errorMsg{}
  632. err = json.Unmarshal(body, msg)
  633. require.NoError(t, err)
  634. // Check that the error was the expected down
  635. require.Equal(t, errSQLTimeout, msg.Message)
  636. finishWait <- nil
  637. // Stop server
  638. wg.Wait()
  639. require.NoError(t, serverTO.Shutdown(context.Background()))
  640. require.NoError(t, databaseTO.Close())
  641. }
  642. func doGoodReqPaginated(
  643. path, order string,
  644. iterStruct Pendinger,
  645. appendIter func(res interface{}),
  646. ) error {
  647. var next uint64
  648. firstIte := true
  649. expectedTotal := 0
  650. totalReceived := 0
  651. for {
  652. // Calculate fromItem
  653. iterPath := path
  654. if !firstIte {
  655. iterPath += "&fromItem=" + strconv.Itoa(int(next))
  656. }
  657. // Call API to get this iteration items
  658. iterStruct = iterStruct.New()
  659. if err := doGoodReq(
  660. "GET", iterPath+"&order="+order, nil,
  661. iterStruct,
  662. ); err != nil {
  663. return tracerr.Wrap(err)
  664. }
  665. appendIter(iterStruct)
  666. // Keep iterating?
  667. remaining, lastID := iterStruct.GetPending()
  668. if remaining == 0 {
  669. break
  670. }
  671. if order == historydb.OrderDesc {
  672. next = lastID - 1
  673. } else {
  674. next = lastID + 1
  675. }
  676. // Check that the expected amount of items is consistent across iterations
  677. totalReceived += iterStruct.Len()
  678. if firstIte {
  679. firstIte = false
  680. expectedTotal = totalReceived + int(remaining)
  681. }
  682. if expectedTotal != totalReceived+int(remaining) {
  683. panic(fmt.Sprintf(
  684. "pagination error, totalReceived + remaining should be %d, but is %d",
  685. expectedTotal, totalReceived+int(remaining),
  686. ))
  687. }
  688. }
  689. return nil
  690. }
  691. func doGoodReq(method, path string, reqBody io.Reader, returnStruct interface{}) error {
  692. ctx := context.Background()
  693. client := &http.Client{}
  694. httpReq, err := http.NewRequest(method, path, reqBody)
  695. if err != nil {
  696. return tracerr.Wrap(err)
  697. }
  698. if reqBody != nil {
  699. httpReq.Header.Add("Content-Type", "application/json")
  700. }
  701. route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
  702. if err != nil {
  703. return tracerr.Wrap(err)
  704. }
  705. // Validate request against swagger spec
  706. requestValidationInput := &swagger.RequestValidationInput{
  707. Request: httpReq,
  708. PathParams: pathParams,
  709. Route: route,
  710. }
  711. if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
  712. return tracerr.Wrap(err)
  713. }
  714. // Do API call
  715. resp, err := client.Do(httpReq)
  716. if err != nil {
  717. return tracerr.Wrap(err)
  718. }
  719. if resp.Body == nil && returnStruct != nil {
  720. return tracerr.Wrap(errors.New("Nil body"))
  721. }
  722. //nolint
  723. defer resp.Body.Close()
  724. body, err := ioutil.ReadAll(resp.Body)
  725. if err != nil {
  726. return tracerr.Wrap(err)
  727. }
  728. if resp.StatusCode != 200 {
  729. return tracerr.Wrap(fmt.Errorf("%d response. Body: %s", resp.StatusCode, string(body)))
  730. }
  731. if returnStruct == nil {
  732. return nil
  733. }
  734. // Unmarshal body into return struct
  735. if err := json.Unmarshal(body, returnStruct); err != nil {
  736. log.Error("invalid json: " + string(body))
  737. log.Error(err)
  738. return tracerr.Wrap(err)
  739. }
  740. // log.Info(string(body))
  741. // Validate response against swagger spec
  742. responseValidationInput := &swagger.ResponseValidationInput{
  743. RequestValidationInput: requestValidationInput,
  744. Status: resp.StatusCode,
  745. Header: resp.Header,
  746. }
  747. responseValidationInput = responseValidationInput.SetBodyBytes(body)
  748. return swagger.ValidateResponse(ctx, responseValidationInput)
  749. }
  750. func doBadReq(method, path string, reqBody io.Reader, expectedResponseCode int) error {
  751. ctx := context.Background()
  752. client := &http.Client{}
  753. httpReq, _ := http.NewRequest(method, path, reqBody)
  754. route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
  755. if err != nil {
  756. return tracerr.Wrap(err)
  757. }
  758. // Validate request against swagger spec
  759. requestValidationInput := &swagger.RequestValidationInput{
  760. Request: httpReq,
  761. PathParams: pathParams,
  762. Route: route,
  763. }
  764. if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
  765. if expectedResponseCode != 400 {
  766. return tracerr.Wrap(err)
  767. }
  768. log.Warn("The request does not match the API spec")
  769. }
  770. // Do API call
  771. resp, err := client.Do(httpReq)
  772. if err != nil {
  773. return tracerr.Wrap(err)
  774. }
  775. if resp.Body == nil {
  776. return tracerr.Wrap(errors.New("Nil body"))
  777. }
  778. //nolint
  779. defer resp.Body.Close()
  780. body, err := ioutil.ReadAll(resp.Body)
  781. if err != nil {
  782. return tracerr.Wrap(err)
  783. }
  784. if resp.StatusCode != expectedResponseCode {
  785. return tracerr.Wrap(fmt.Errorf("Unexpected response code: %d. Body: %s", resp.StatusCode, string(body)))
  786. }
  787. // Validate response against swagger spec
  788. responseValidationInput := &swagger.ResponseValidationInput{
  789. RequestValidationInput: requestValidationInput,
  790. Status: resp.StatusCode,
  791. Header: resp.Header,
  792. }
  793. responseValidationInput = responseValidationInput.SetBodyBytes(body)
  794. return swagger.ValidateResponse(ctx, responseValidationInput)
  795. }
  796. func doSimpleReq(method, endpoint string) (string, error) {
  797. client := &http.Client{}
  798. httpReq, err := http.NewRequest(method, endpoint, nil)
  799. if err != nil {
  800. return "", tracerr.Wrap(err)
  801. }
  802. resp, err := client.Do(httpReq)
  803. if err != nil {
  804. return "", tracerr.Wrap(err)
  805. }
  806. //nolint
  807. defer resp.Body.Close()
  808. body, err := ioutil.ReadAll(resp.Body)
  809. if err != nil {
  810. return "", tracerr.Wrap(err)
  811. }
  812. return string(body), nil
  813. }
  814. // test helpers
  815. func getTimestamp(blockNum int64, blocks []common.Block) time.Time {
  816. for i := 0; i < len(blocks); i++ {
  817. if blocks[i].Num == blockNum {
  818. return blocks[i].Timestamp
  819. }
  820. }
  821. panic("timesamp not found")
  822. }
  823. func getTokenByID(id common.TokenID, tokens []historydb.TokenWithUSD) historydb.TokenWithUSD {
  824. for i := 0; i < len(tokens); i++ {
  825. if tokens[i].TokenID == id {
  826. return tokens[i]
  827. }
  828. }
  829. panic("token not found")
  830. }
  831. func getTokenByIdx(idx common.Idx, tokens []historydb.TokenWithUSD, accs []common.Account) historydb.TokenWithUSD {
  832. for _, acc := range accs {
  833. if idx == acc.Idx {
  834. return getTokenByID(acc.TokenID, tokens)
  835. }
  836. }
  837. panic("token not found")
  838. }
  839. func getAccountByIdx(idx common.Idx, accs []common.Account) *common.Account {
  840. for _, acc := range accs {
  841. if acc.Idx == idx {
  842. return &acc
  843. }
  844. }
  845. panic("account not found")
  846. }
  847. func getBlockByNum(ethBlockNum int64, blocks []common.Block) common.Block {
  848. for _, b := range blocks {
  849. if b.Num == ethBlockNum {
  850. return b
  851. }
  852. }
  853. panic("block not found")
  854. }
  855. func getCoordinatorByBidder(bidder ethCommon.Address, coordinators []historydb.CoordinatorAPI) historydb.CoordinatorAPI {
  856. var coordLastUpdate historydb.CoordinatorAPI
  857. found := false
  858. for _, c := range coordinators {
  859. if c.Bidder == bidder {
  860. coordLastUpdate = c
  861. found = true
  862. }
  863. }
  864. if !found {
  865. panic("coordinator not found")
  866. }
  867. return coordLastUpdate
  868. }