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.

896 lines
25 KiB

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