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.

376 lines
14 KiB

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 txselector
  2. import (
  3. "crypto/ecdsa"
  4. "fmt"
  5. "io/ioutil"
  6. "math/big"
  7. "os"
  8. "strconv"
  9. "testing"
  10. "time"
  11. ethCommon "github.com/ethereum/go-ethereum/common"
  12. ethCrypto "github.com/ethereum/go-ethereum/crypto"
  13. "github.com/hermeznetwork/hermez-node/common"
  14. dbUtils "github.com/hermeznetwork/hermez-node/db"
  15. "github.com/hermeznetwork/hermez-node/db/historydb"
  16. "github.com/hermeznetwork/hermez-node/db/l2db"
  17. "github.com/hermeznetwork/hermez-node/db/statedb"
  18. "github.com/hermeznetwork/hermez-node/log"
  19. "github.com/hermeznetwork/hermez-node/test"
  20. "github.com/hermeznetwork/hermez-node/test/til"
  21. "github.com/hermeznetwork/hermez-node/txprocessor"
  22. "github.com/iden3/go-iden3-crypto/babyjub"
  23. "github.com/jmoiron/sqlx"
  24. "github.com/stretchr/testify/assert"
  25. "github.com/stretchr/testify/require"
  26. )
  27. func initTest(t *testing.T, chainID uint16, hermezContractAddr ethCommon.Address, testSet string) (*TxSelector, *til.Context) {
  28. pass := os.Getenv("POSTGRES_PASS")
  29. db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
  30. require.NoError(t, err)
  31. l2DB := l2db.NewL2DB(db, 10, 100, 24*time.Hour)
  32. dir, err := ioutil.TempDir("", "tmpdb")
  33. require.NoError(t, err)
  34. defer assert.NoError(t, os.RemoveAll(dir))
  35. sdb, err := statedb.NewStateDB(dir, 128, statedb.TypeTxSelector, 0)
  36. require.NoError(t, err)
  37. txselDir, err := ioutil.TempDir("", "tmpTxSelDB")
  38. require.NoError(t, err)
  39. defer assert.NoError(t, os.RemoveAll(dir))
  40. // coordinator keys
  41. var ethSk ecdsa.PrivateKey
  42. ethSk.D = big.NewInt(int64(1)) // only for testing
  43. ethSk.PublicKey.X, ethSk.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(ethSk.D.Bytes())
  44. ethSk.Curve = ethCrypto.S256()
  45. addr := ethCrypto.PubkeyToAddress(ethSk.PublicKey)
  46. var bjj babyjub.PublicKeyComp
  47. err = bjj.UnmarshalText([]byte("c433f7a696b7aa3a5224efb3993baf0ccd9e92eecee0c29a3f6c8208a9e81d9e"))
  48. require.NoError(t, err)
  49. coordAccount := &CoordAccount{
  50. Addr: addr,
  51. BJJ: bjj,
  52. AccountCreationAuth: nil,
  53. }
  54. a := &common.AccountCreationAuth{
  55. EthAddr: addr,
  56. BJJ: bjj,
  57. }
  58. msg, err := a.HashToSign(chainID, hermezContractAddr)
  59. assert.NoError(t, err)
  60. sig, err := ethCrypto.Sign(msg, &ethSk)
  61. assert.NoError(t, err)
  62. sig[64] += 27
  63. coordAccount.AccountCreationAuth = sig
  64. txsel, err := NewTxSelector(coordAccount, txselDir, sdb, l2DB)
  65. require.NoError(t, err)
  66. test.WipeDB(txsel.l2db.DB())
  67. tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
  68. return txsel, tc
  69. }
  70. func addAccCreationAuth(t *testing.T, tc *til.Context, txsel *TxSelector, chainID uint16, hermezContractAddr ethCommon.Address, username string) []byte {
  71. user := tc.Users[username]
  72. a := &common.AccountCreationAuth{
  73. EthAddr: user.Addr,
  74. BJJ: user.BJJ.Public().Compress(),
  75. }
  76. msg, err := a.HashToSign(chainID, hermezContractAddr)
  77. assert.NoError(t, err)
  78. sig, err := ethCrypto.Sign(msg, user.EthSk)
  79. assert.NoError(t, err)
  80. sig[64] += 27
  81. a.Signature = sig
  82. err = txsel.l2db.AddAccountCreationAuth(a)
  83. assert.NoError(t, err)
  84. return a.Signature
  85. }
  86. func addL2Txs(t *testing.T, txsel *TxSelector, poolL2Txs []common.PoolL2Tx) {
  87. for i := 0; i < len(poolL2Txs); i++ {
  88. err := txsel.l2db.AddTxTest(&poolL2Txs[i])
  89. if err != nil {
  90. log.Error(err)
  91. }
  92. require.NoError(t, err)
  93. }
  94. }
  95. func addTokens(t *testing.T, tc *til.Context, db *sqlx.DB) {
  96. var tokens []common.Token
  97. for i := 0; i < int(tc.LastRegisteredTokenID); i++ {
  98. tokens = append(tokens, common.Token{
  99. TokenID: common.TokenID(i + 1),
  100. EthBlockNum: 1,
  101. EthAddr: ethCommon.BytesToAddress([]byte{byte(i + 1)}),
  102. Name: strconv.Itoa(i),
  103. Symbol: strconv.Itoa(i),
  104. Decimals: 18,
  105. })
  106. }
  107. hdb := historydb.NewHistoryDB(db)
  108. assert.NoError(t, hdb.AddBlock(&common.Block{
  109. Num: 1,
  110. }))
  111. assert.NoError(t, hdb.AddTokens(tokens))
  112. }
  113. func checkBalance(t *testing.T, tc *til.Context, txsel *TxSelector, username string, tokenid int, expected string) {
  114. // Accounts.Idx does not match with the TxSelector tests as we are not
  115. // using the Til L1CoordinatorTxs (as are generated by the TxSelector
  116. // itself when processing the txs, so the Idxs does not match the Til
  117. // idxs). But the Idx is obtained through StateDB.GetIdxByEthAddrBJJ
  118. user := tc.Users[username]
  119. idx, err := txsel.localAccountsDB.GetIdxByEthAddrBJJ(user.Addr, user.BJJ.Public().Compress(), common.TokenID(tokenid))
  120. require.NoError(t, err)
  121. checkBalanceByIdx(t, txsel, idx, expected)
  122. }
  123. func checkBalanceByIdx(t *testing.T, txsel *TxSelector, idx common.Idx, expected string) {
  124. acc, err := txsel.localAccountsDB.GetAccount(idx)
  125. require.NoError(t, err)
  126. assert.Equal(t, expected, acc.Balance.String())
  127. }
  128. // checkSortedByNonce takes as input testAccNonces map, and the array of
  129. // common.PoolL2Txs, and checks if the nonces correspond to the accumulated
  130. // values of the map. Also increases the Nonces computed on the map.
  131. func checkSortedByNonce(t *testing.T, testAccNonces map[common.Idx]common.Nonce, txs []common.PoolL2Tx) {
  132. for _, tx := range txs {
  133. assert.True(t, testAccNonces[tx.FromIdx] == tx.Nonce,
  134. fmt.Sprintf("Idx: %d, expected: %d, tx.Nonce: %d",
  135. tx.FromIdx, testAccNonces[tx.FromIdx], tx.Nonce))
  136. testAccNonces[tx.FromIdx] = testAccNonces[tx.FromIdx] + 1
  137. }
  138. }
  139. func TestGetL2TxSelectionMinimumFlow0(t *testing.T) {
  140. chainID := uint16(0)
  141. hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
  142. txsel, tc := initTest(t, chainID, hermezContractAddr, til.SetPool0)
  143. // generate test transactions, the L1CoordinatorTxs generated by Til
  144. // will be ignored at this test, as will be the TxSelector who
  145. // generates them when needed
  146. blocks, err := tc.GenerateBlocks(til.SetBlockchainMinimumFlow0)
  147. assert.NoError(t, err)
  148. // restart nonces of TilContext, as will be set by generating directly
  149. // the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
  150. tc.RestartNonces()
  151. testAccNonces := make(map[common.Idx]common.Nonce)
  152. // add tokens to HistoryDB to avoid breaking FK constrains
  153. addTokens(t, tc, txsel.l2db.DB())
  154. tpc := txprocessor.Config{
  155. NLevels: 16,
  156. MaxFeeTx: 10,
  157. MaxTx: 20,
  158. MaxL1Tx: 10,
  159. ChainID: chainID,
  160. }
  161. selectionConfig := &SelectionConfig{
  162. MaxL1UserTxs: 5,
  163. TxProcessorConfig: tpc,
  164. }
  165. // coordIdxs, accAuths, l1UserTxs, l1CoordTxs, l2Txs, err
  166. log.Debug("block:0 batch:0")
  167. l1UserTxs := []common.L1Tx{}
  168. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, err := txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  169. require.NoError(t, err)
  170. assert.Equal(t, 0, len(oL1UserTxs))
  171. assert.Equal(t, 0, len(oL1CoordTxs))
  172. assert.Equal(t, 0, len(oL2Txs))
  173. assert.Equal(t, common.BatchNum(1), txsel.localAccountsDB.CurrentBatch())
  174. assert.Equal(t, common.Idx(255), txsel.localAccountsDB.CurrentIdx())
  175. log.Debug("block:0 batch:1")
  176. l1UserTxs = []common.L1Tx{}
  177. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  178. require.NoError(t, err)
  179. assert.Equal(t, 0, len(oL1UserTxs))
  180. assert.Equal(t, 0, len(oL1CoordTxs))
  181. assert.Equal(t, 0, len(oL2Txs))
  182. assert.Equal(t, common.BatchNum(2), txsel.localAccountsDB.CurrentBatch())
  183. assert.Equal(t, common.Idx(255), txsel.localAccountsDB.CurrentIdx())
  184. log.Debug("block:0 batch:2")
  185. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[2].Batch.ForgeL1TxsNum])
  186. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  187. require.NoError(t, err)
  188. assert.Equal(t, 2, len(oL1UserTxs))
  189. assert.Equal(t, 0, len(oL1CoordTxs))
  190. assert.Equal(t, 0, len(oL2Txs))
  191. assert.Equal(t, common.BatchNum(3), txsel.localAccountsDB.CurrentBatch())
  192. assert.Equal(t, common.Idx(257), txsel.localAccountsDB.CurrentIdx())
  193. checkBalance(t, tc, txsel, "A", 0, "500")
  194. checkBalance(t, tc, txsel, "C", 1, "0")
  195. log.Debug("block:0 batch:3")
  196. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[3].Batch.ForgeL1TxsNum])
  197. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  198. require.NoError(t, err)
  199. assert.Equal(t, 1, len(oL1UserTxs))
  200. assert.Equal(t, 0, len(oL1CoordTxs))
  201. assert.Equal(t, 0, len(oL2Txs))
  202. assert.Equal(t, common.BatchNum(4), txsel.localAccountsDB.CurrentBatch())
  203. assert.Equal(t, common.Idx(258), txsel.localAccountsDB.CurrentIdx())
  204. checkBalance(t, tc, txsel, "A", 0, "500")
  205. checkBalance(t, tc, txsel, "A", 1, "500")
  206. checkBalance(t, tc, txsel, "C", 1, "0")
  207. log.Debug("block:0 batch:4")
  208. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[4].Batch.ForgeL1TxsNum])
  209. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  210. require.NoError(t, err)
  211. assert.Equal(t, 0, len(oL1UserTxs))
  212. assert.Equal(t, 0, len(oL1CoordTxs))
  213. assert.Equal(t, 0, len(oL2Txs))
  214. assert.Equal(t, common.BatchNum(5), txsel.localAccountsDB.CurrentBatch())
  215. assert.Equal(t, common.Idx(258), txsel.localAccountsDB.CurrentIdx())
  216. checkBalance(t, tc, txsel, "A", 0, "500")
  217. checkBalance(t, tc, txsel, "A", 1, "500")
  218. checkBalance(t, tc, txsel, "C", 1, "0")
  219. log.Debug("block:0 batch:5")
  220. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[5].Batch.ForgeL1TxsNum])
  221. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  222. require.NoError(t, err)
  223. assert.Equal(t, 1, len(oL1UserTxs))
  224. assert.Equal(t, 0, len(oL1CoordTxs))
  225. assert.Equal(t, 0, len(oL2Txs))
  226. assert.Equal(t, common.BatchNum(6), txsel.localAccountsDB.CurrentBatch())
  227. assert.Equal(t, common.Idx(259), txsel.localAccountsDB.CurrentIdx())
  228. checkBalance(t, tc, txsel, "A", 0, "600")
  229. checkBalance(t, tc, txsel, "A", 1, "500")
  230. checkBalance(t, tc, txsel, "B", 0, "400")
  231. checkBalance(t, tc, txsel, "C", 1, "0")
  232. log.Debug("block:0 batch:6")
  233. // simulate the PoolL2Txs of the batch6
  234. batchPoolL2 := `
  235. Type: PoolL2
  236. PoolTransfer(1) A-B: 200 (126)
  237. PoolTransfer(0) B-C: 100 (126)`
  238. poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
  239. require.NoError(t, err)
  240. // add AccountCreationAuths that will be used at the next batch
  241. accAuthSig0 := addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "B")
  242. accAuthSig1 := addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "C")
  243. // add ToEthAddr for the corresponent ToIdx, and remove ToIdx for Batches[6].L2Tx
  244. poolL2Txs[0].ToEthAddr = tc.Users["B"].Addr
  245. poolL2Txs[0].ToIdx = common.Idx(0)
  246. poolL2Txs[1].ToEthAddr = tc.Users["C"].Addr
  247. poolL2Txs[1].ToIdx = common.Idx(0)
  248. // add the PoolL2Txs to the l2DB
  249. addL2Txs(t, txsel, poolL2Txs)
  250. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[6].Batch.ForgeL1TxsNum])
  251. coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, err := txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  252. require.NoError(t, err)
  253. assert.Equal(t, []common.Idx{261, 262}, coordIdxs)
  254. assert.Equal(t, txsel.coordAccount.AccountCreationAuth, accAuths[0])
  255. assert.Equal(t, txsel.coordAccount.AccountCreationAuth, accAuths[1])
  256. assert.Equal(t, accAuthSig0, accAuths[2])
  257. assert.Equal(t, accAuthSig1, accAuths[3])
  258. assert.Equal(t, 1, len(oL1UserTxs))
  259. assert.Equal(t, 4, len(oL1CoordTxs))
  260. assert.Equal(t, 2, len(oL2Txs))
  261. assert.Equal(t, len(oL1CoordTxs), len(accAuths))
  262. assert.Equal(t, common.BatchNum(7), txsel.localAccountsDB.CurrentBatch())
  263. assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
  264. checkBalanceByIdx(t, txsel, 261, "20") // CoordIdx for TokenID=1
  265. checkBalanceByIdx(t, txsel, 262, "10") // CoordIdx for TokenID=0
  266. checkBalance(t, tc, txsel, "A", 0, "600")
  267. checkBalance(t, tc, txsel, "A", 1, "280")
  268. checkBalance(t, tc, txsel, "B", 0, "290")
  269. checkBalance(t, tc, txsel, "B", 1, "200")
  270. checkBalance(t, tc, txsel, "C", 0, "100")
  271. checkBalance(t, tc, txsel, "D", 0, "800")
  272. checkSortedByNonce(t, testAccNonces, oL2Txs)
  273. err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs), txsel.localAccountsDB.CurrentBatch())
  274. require.NoError(t, err)
  275. log.Debug("block:0 batch:7")
  276. // simulate the PoolL2Txs of the batch6
  277. batchPoolL2 = `
  278. Type: PoolL2
  279. PoolTransfer(0) A-B: 100 (126)
  280. PoolTransfer(0) C-A: 50 (126)
  281. PoolTransfer(1) B-C: 100 (126)
  282. PoolExit(0) A: 100 (126)`
  283. poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
  284. require.NoError(t, err)
  285. addL2Txs(t, txsel, poolL2Txs)
  286. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[7].Batch.ForgeL1TxsNum])
  287. coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  288. require.NoError(t, err)
  289. assert.Equal(t, []common.Idx{261, 262}, coordIdxs)
  290. assert.Equal(t, 0, len(accAuths))
  291. assert.Equal(t, 0, len(oL1UserTxs))
  292. assert.Equal(t, 0, len(oL1CoordTxs))
  293. assert.Equal(t, 4, len(oL2Txs))
  294. assert.Equal(t, len(oL1CoordTxs), len(accAuths))
  295. assert.Equal(t, common.BatchNum(8), txsel.localAccountsDB.CurrentBatch())
  296. assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
  297. checkBalanceByIdx(t, txsel, 261, "30")
  298. checkBalanceByIdx(t, txsel, 262, "35")
  299. checkBalance(t, tc, txsel, "A", 0, "430")
  300. checkBalance(t, tc, txsel, "A", 1, "280")
  301. checkBalance(t, tc, txsel, "B", 0, "390")
  302. checkBalance(t, tc, txsel, "B", 1, "90")
  303. checkBalance(t, tc, txsel, "C", 0, "45")
  304. checkBalance(t, tc, txsel, "C", 1, "100")
  305. checkBalance(t, tc, txsel, "D", 0, "800")
  306. checkSortedByNonce(t, testAccNonces, oL2Txs)
  307. err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs), txsel.localAccountsDB.CurrentBatch())
  308. require.NoError(t, err)
  309. log.Debug("block:1 batch:0")
  310. // simulate the PoolL2Txs of the batch6
  311. batchPoolL2 = `
  312. Type: PoolL2
  313. PoolTransfer(0) D-A: 300 (126)
  314. PoolTransfer(0) B-D: 100 (126)
  315. `
  316. poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
  317. require.NoError(t, err)
  318. addL2Txs(t, txsel, poolL2Txs)
  319. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[1].Rollup.Batches[0].Batch.ForgeL1TxsNum])
  320. coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  321. require.NoError(t, err)
  322. assert.Equal(t, []common.Idx{262}, coordIdxs)
  323. assert.Equal(t, 0, len(accAuths))
  324. assert.Equal(t, 4, len(oL1UserTxs))
  325. assert.Equal(t, 0, len(oL1CoordTxs))
  326. assert.Equal(t, 2, len(oL2Txs))
  327. assert.Equal(t, len(oL1CoordTxs), len(accAuths))
  328. assert.Equal(t, common.BatchNum(9), txsel.localAccountsDB.CurrentBatch())
  329. assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
  330. checkBalanceByIdx(t, txsel, 261, "30")
  331. checkBalanceByIdx(t, txsel, 262, "75")
  332. checkBalance(t, tc, txsel, "A", 0, "730")
  333. checkBalance(t, tc, txsel, "A", 1, "280")
  334. checkBalance(t, tc, txsel, "B", 0, "380")
  335. checkBalance(t, tc, txsel, "B", 1, "90")
  336. checkBalance(t, tc, txsel, "C", 0, "845")
  337. checkBalance(t, tc, txsel, "C", 1, "100")
  338. checkBalance(t, tc, txsel, "D", 0, "470")
  339. checkSortedByNonce(t, testAccNonces, oL2Txs)
  340. err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs), txsel.localAccountsDB.CurrentBatch())
  341. require.NoError(t, err)
  342. }