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.

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