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.

881 lines
25 KiB

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