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.

745 lines
27 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. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "strconv"
  7. "testing"
  8. "time"
  9. ethCommon "github.com/ethereum/go-ethereum/common"
  10. ethCrypto "github.com/ethereum/go-ethereum/crypto"
  11. "github.com/hermeznetwork/hermez-node/common"
  12. dbUtils "github.com/hermeznetwork/hermez-node/db"
  13. "github.com/hermeznetwork/hermez-node/db/historydb"
  14. "github.com/hermeznetwork/hermez-node/db/l2db"
  15. "github.com/hermeznetwork/hermez-node/db/statedb"
  16. "github.com/hermeznetwork/hermez-node/log"
  17. "github.com/hermeznetwork/hermez-node/test"
  18. "github.com/hermeznetwork/hermez-node/test/til"
  19. "github.com/hermeznetwork/hermez-node/test/txsets"
  20. "github.com/hermeznetwork/hermez-node/txprocessor"
  21. "github.com/jmoiron/sqlx"
  22. "github.com/stretchr/testify/assert"
  23. "github.com/stretchr/testify/require"
  24. )
  25. func initTest(t *testing.T, chainID uint16, hermezContractAddr ethCommon.Address,
  26. coordUser *til.User) *TxSelector {
  27. pass := os.Getenv("POSTGRES_PASS")
  28. db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
  29. require.NoError(t, err)
  30. l2DB := l2db.NewL2DB(db, db, 10, 100, 0.0, 24*time.Hour, nil)
  31. dir, err := ioutil.TempDir("", "tmpdb")
  32. require.NoError(t, err)
  33. defer assert.NoError(t, os.RemoveAll(dir))
  34. syncStateDB, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
  35. Type: statedb.TypeTxSelector, NLevels: 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. // use Til Coord keys for tests compatibility
  41. coordAccount := &CoordAccount{
  42. Addr: coordUser.Addr,
  43. BJJ: coordUser.BJJ.Public().Compress(),
  44. AccountCreationAuth: nil,
  45. }
  46. // fmt.Printf("%v\n", coordAccount)
  47. auth := common.AccountCreationAuth{
  48. EthAddr: coordUser.Addr,
  49. BJJ: coordUser.BJJ.Public().Compress(),
  50. }
  51. err = auth.Sign(func(hash []byte) ([]byte, error) {
  52. return ethCrypto.Sign(hash, coordUser.EthSk)
  53. }, chainID, hermezContractAddr)
  54. assert.NoError(t, err)
  55. coordAccount.AccountCreationAuth = auth.Signature
  56. txsel, err := NewTxSelector(coordAccount, txselDir, syncStateDB, l2DB)
  57. require.NoError(t, err)
  58. test.WipeDB(txsel.l2db.DB())
  59. return txsel
  60. }
  61. func addAccCreationAuth(t *testing.T, tc *til.Context, txsel *TxSelector, chainID uint16,
  62. hermezContractAddr ethCommon.Address, username string) []byte {
  63. user := tc.Users[username]
  64. auth := &common.AccountCreationAuth{
  65. EthAddr: user.Addr,
  66. BJJ: user.BJJ.Public().Compress(),
  67. }
  68. err := auth.Sign(func(hash []byte) ([]byte, error) {
  69. return ethCrypto.Sign(hash, user.EthSk)
  70. }, chainID, hermezContractAddr)
  71. assert.NoError(t, err)
  72. err = txsel.l2db.AddAccountCreationAuth(auth)
  73. assert.NoError(t, err)
  74. return auth.Signature
  75. }
  76. func addL2Txs(t *testing.T, txsel *TxSelector, poolL2Txs []common.PoolL2Tx) {
  77. for i := 0; i < len(poolL2Txs); i++ {
  78. err := txsel.l2db.AddTxTest(&poolL2Txs[i])
  79. if err != nil {
  80. log.Error(err)
  81. }
  82. require.NoError(t, err)
  83. }
  84. }
  85. func addTokens(t *testing.T, tc *til.Context, db *sqlx.DB) {
  86. var tokens []common.Token
  87. for i := 0; i < int(tc.LastRegisteredTokenID); i++ {
  88. tokens = append(tokens, common.Token{
  89. TokenID: common.TokenID(i + 1),
  90. EthBlockNum: 1,
  91. EthAddr: ethCommon.BytesToAddress([]byte{byte(i + 1)}),
  92. Name: strconv.Itoa(i),
  93. Symbol: strconv.Itoa(i),
  94. Decimals: 18,
  95. })
  96. }
  97. hdb := historydb.NewHistoryDB(db, db, nil)
  98. assert.NoError(t, hdb.AddBlock(&common.Block{
  99. Num: 1,
  100. }))
  101. assert.NoError(t, hdb.AddTokens(tokens))
  102. }
  103. func checkBalance(t *testing.T, tc *til.Context, txsel *TxSelector, username string,
  104. tokenid int, expected string) {
  105. // Accounts.Idx does not match with the TxSelector tests as we are not
  106. // using the Til L1CoordinatorTxs (as are generated by the TxSelector
  107. // itself when processing the txs, so the Idxs does not match the Til
  108. // idxs). But the Idx is obtained through StateDB.GetIdxByEthAddrBJJ
  109. user := tc.Users[username]
  110. idx, err := txsel.localAccountsDB.GetIdxByEthAddrBJJ(user.Addr, user.BJJ.Public().Compress(),
  111. common.TokenID(tokenid))
  112. require.NoError(t, err)
  113. checkBalanceByIdx(t, txsel, idx, expected)
  114. }
  115. func checkBalanceByIdx(t *testing.T, txsel *TxSelector, idx common.Idx, expected string) {
  116. acc, err := txsel.localAccountsDB.GetAccount(idx)
  117. require.NoError(t, err)
  118. assert.Equal(t, expected, acc.Balance.String())
  119. }
  120. // checkSortedByNonce takes as input testAccNonces map, and the array of
  121. // common.PoolL2Txs, and checks if the nonces correspond to the accumulated
  122. // values of the map. Also increases the Nonces computed on the map.
  123. func checkSortedByNonce(t *testing.T, testAccNonces map[common.Idx]common.Nonce,
  124. txs []common.PoolL2Tx) {
  125. for _, tx := range txs {
  126. assert.True(t, testAccNonces[tx.FromIdx] == tx.Nonce,
  127. fmt.Sprintf("Idx: %d, expected: %d, tx.Nonce: %d",
  128. tx.FromIdx, testAccNonces[tx.FromIdx], tx.Nonce))
  129. testAccNonces[tx.FromIdx] = testAccNonces[tx.FromIdx] + 1
  130. }
  131. }
  132. func TestGetL2TxSelectionMinimumFlow0(t *testing.T) {
  133. chainID := uint16(0)
  134. tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
  135. // generate test transactions, the L1CoordinatorTxs generated by Til
  136. // will be ignored at this test, as will be the TxSelector who
  137. // generates them when needed
  138. blocks, err := tc.GenerateBlocks(txsets.SetBlockchainMinimumFlow0)
  139. assert.NoError(t, err)
  140. hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
  141. txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
  142. // restart nonces of TilContext, as will be set by generating directly
  143. // the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
  144. tc.RestartNonces()
  145. testAccNonces := make(map[common.Idx]common.Nonce)
  146. // add tokens to HistoryDB to avoid breaking FK constrains
  147. addTokens(t, tc, txsel.l2db.DB())
  148. tpc := txprocessor.Config{
  149. NLevels: 16,
  150. MaxFeeTx: 10,
  151. MaxTx: 20,
  152. MaxL1Tx: 10,
  153. ChainID: chainID,
  154. }
  155. selectionConfig := &SelectionConfig{
  156. MaxL1UserTxs: 5,
  157. TxProcessorConfig: tpc,
  158. }
  159. // coordIdxs, accAuths, l1UserTxs, l1CoordTxs, l2Txs, err
  160. log.Debug("block:0 batch:1")
  161. l1UserTxs := []common.L1Tx{}
  162. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err :=
  163. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  164. require.NoError(t, err)
  165. assert.Equal(t, 0, len(oL1UserTxs))
  166. assert.Equal(t, 0, len(oL1CoordTxs))
  167. assert.Equal(t, 0, len(oL2Txs))
  168. assert.Equal(t, common.BatchNum(1), txsel.localAccountsDB.CurrentBatch())
  169. assert.Equal(t, common.Idx(255), txsel.localAccountsDB.CurrentIdx())
  170. log.Debug("block:0 batch:2")
  171. l1UserTxs = []common.L1Tx{}
  172. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
  173. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  174. require.NoError(t, err)
  175. assert.Equal(t, 0, len(oL1UserTxs))
  176. assert.Equal(t, 0, len(oL1CoordTxs))
  177. assert.Equal(t, 0, len(oL2Txs))
  178. assert.Equal(t, common.BatchNum(2), txsel.localAccountsDB.CurrentBatch())
  179. assert.Equal(t, common.Idx(255), txsel.localAccountsDB.CurrentIdx())
  180. log.Debug("block:0 batch:3")
  181. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[2].Batch.ForgeL1TxsNum])
  182. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
  183. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  184. require.NoError(t, err)
  185. assert.Equal(t, 2, len(oL1UserTxs))
  186. assert.Equal(t, 0, len(oL1CoordTxs))
  187. assert.Equal(t, 0, len(oL2Txs))
  188. assert.Equal(t, common.BatchNum(3), txsel.localAccountsDB.CurrentBatch())
  189. assert.Equal(t, common.Idx(257), txsel.localAccountsDB.CurrentIdx())
  190. checkBalance(t, tc, txsel, "A", 0, "500")
  191. checkBalance(t, tc, txsel, "C", 1, "0")
  192. log.Debug("block:0 batch:4")
  193. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[3].Batch.ForgeL1TxsNum])
  194. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
  195. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  196. require.NoError(t, err)
  197. assert.Equal(t, 1, len(oL1UserTxs))
  198. assert.Equal(t, 0, len(oL1CoordTxs))
  199. assert.Equal(t, 0, len(oL2Txs))
  200. assert.Equal(t, common.BatchNum(4), txsel.localAccountsDB.CurrentBatch())
  201. assert.Equal(t, common.Idx(258), txsel.localAccountsDB.CurrentIdx())
  202. checkBalance(t, tc, txsel, "A", 0, "500")
  203. checkBalance(t, tc, txsel, "A", 1, "500")
  204. checkBalance(t, tc, txsel, "C", 1, "0")
  205. log.Debug("block:0 batch:5")
  206. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[4].Batch.ForgeL1TxsNum])
  207. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
  208. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  209. require.NoError(t, err)
  210. assert.Equal(t, 0, len(oL1UserTxs))
  211. assert.Equal(t, 0, len(oL1CoordTxs))
  212. assert.Equal(t, 0, len(oL2Txs))
  213. assert.Equal(t, common.BatchNum(5), txsel.localAccountsDB.CurrentBatch())
  214. assert.Equal(t, common.Idx(258), txsel.localAccountsDB.CurrentIdx())
  215. checkBalance(t, tc, txsel, "A", 0, "500")
  216. checkBalance(t, tc, txsel, "A", 1, "500")
  217. checkBalance(t, tc, txsel, "C", 1, "0")
  218. log.Debug("block:0 batch:6")
  219. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[5].Batch.ForgeL1TxsNum])
  220. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
  221. 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:7")
  233. // simulate the PoolL2Txs of the batch7
  234. batchPoolL2 := `
  235. Type: PoolL2
  236. PoolTransferToEthAddr(1) A-B: 200 (126)
  237. PoolTransferToEthAddr(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 the PoolL2Txs to the l2DB
  244. addL2Txs(t, txsel, poolL2Txs)
  245. // check signatures of L2Txs from the L2DB (to check that the
  246. // parameters of the PoolL2Tx match the original parameters signed
  247. // before inserting it to the L2DB)
  248. l2TxsFromDB, err := txsel.l2db.GetPendingTxs()
  249. require.NoError(t, err)
  250. assert.True(t, l2TxsFromDB[0].VerifySignature(chainID, tc.Users["A"].BJJ.Public().Compress()))
  251. assert.True(t, l2TxsFromDB[1].VerifySignature(chainID, tc.Users["B"].BJJ.Public().Compress()))
  252. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[6].Batch.ForgeL1TxsNum])
  253. coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err :=
  254. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  255. require.NoError(t, err)
  256. assert.Equal(t, []common.Idx{261, 262}, coordIdxs)
  257. assert.Equal(t, txsel.coordAccount.AccountCreationAuth, accAuths[0])
  258. assert.Equal(t, txsel.coordAccount.AccountCreationAuth, accAuths[1])
  259. assert.Equal(t, accAuthSig0, accAuths[2])
  260. assert.Equal(t, accAuthSig1, accAuths[3])
  261. assert.Equal(t, 1, len(oL1UserTxs))
  262. assert.Equal(t, 4, len(oL1CoordTxs))
  263. assert.Equal(t, 2, len(oL2Txs))
  264. assert.Equal(t, len(oL1CoordTxs), len(accAuths))
  265. assert.Equal(t, common.BatchNum(7), txsel.localAccountsDB.CurrentBatch())
  266. assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
  267. checkBalanceByIdx(t, txsel, 261, "20") // CoordIdx for TokenID=1
  268. checkBalanceByIdx(t, txsel, 262, "10") // CoordIdx for TokenID=0
  269. checkBalance(t, tc, txsel, "A", 0, "600")
  270. checkBalance(t, tc, txsel, "A", 1, "280")
  271. checkBalance(t, tc, txsel, "B", 0, "290")
  272. checkBalance(t, tc, txsel, "B", 1, "200")
  273. checkBalance(t, tc, txsel, "C", 0, "100")
  274. checkBalance(t, tc, txsel, "D", 0, "800")
  275. checkSortedByNonce(t, testAccNonces, oL2Txs)
  276. err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs),
  277. txsel.localAccountsDB.CurrentBatch())
  278. require.NoError(t, err)
  279. log.Debug("block:0 batch:8")
  280. // simulate the PoolL2Txs of the batch8
  281. batchPoolL2 = `
  282. Type: PoolL2
  283. PoolTransfer(0) A-B: 100 (126)
  284. PoolTransfer(0) C-A: 50 (126)
  285. PoolTransfer(1) B-C: 100 (126)
  286. PoolExit(0) A: 100 (126)`
  287. poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
  288. require.NoError(t, err)
  289. addL2Txs(t, txsel, poolL2Txs)
  290. // check signatures of L2Txs from the L2DB (to check that the
  291. // parameters of the PoolL2Tx match the original parameters signed
  292. // before inserting it to the L2DB)
  293. l2TxsFromDB, err = txsel.l2db.GetPendingTxs()
  294. require.NoError(t, err)
  295. assert.True(t, l2TxsFromDB[0].VerifySignature(chainID, tc.Users["A"].BJJ.Public().Compress()))
  296. assert.True(t, l2TxsFromDB[1].VerifySignature(chainID, tc.Users["C"].BJJ.Public().Compress()))
  297. assert.True(t, l2TxsFromDB[2].VerifySignature(chainID, tc.Users["B"].BJJ.Public().Compress()))
  298. assert.True(t, l2TxsFromDB[3].VerifySignature(chainID, tc.Users["A"].BJJ.Public().Compress()))
  299. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[7].Batch.ForgeL1TxsNum])
  300. coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
  301. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  302. require.NoError(t, err)
  303. assert.Equal(t, []common.Idx{261, 262}, coordIdxs)
  304. assert.Equal(t, 0, len(accAuths))
  305. assert.Equal(t, 0, len(oL1UserTxs))
  306. assert.Equal(t, 0, len(oL1CoordTxs))
  307. assert.Equal(t, 4, len(oL2Txs))
  308. assert.Equal(t, len(oL1CoordTxs), len(accAuths))
  309. assert.Equal(t, common.BatchNum(8), txsel.localAccountsDB.CurrentBatch())
  310. assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
  311. checkBalanceByIdx(t, txsel, 261, "30")
  312. checkBalanceByIdx(t, txsel, 262, "35")
  313. checkBalance(t, tc, txsel, "A", 0, "430")
  314. checkBalance(t, tc, txsel, "A", 1, "280")
  315. checkBalance(t, tc, txsel, "B", 0, "390")
  316. checkBalance(t, tc, txsel, "B", 1, "90")
  317. checkBalance(t, tc, txsel, "C", 0, "45")
  318. checkBalance(t, tc, txsel, "C", 1, "100")
  319. checkBalance(t, tc, txsel, "D", 0, "800")
  320. checkSortedByNonce(t, testAccNonces, oL2Txs)
  321. err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs),
  322. txsel.localAccountsDB.CurrentBatch())
  323. require.NoError(t, err)
  324. log.Debug("(batch9)block:1 batch:1")
  325. // simulate the PoolL2Txs of the batch9
  326. batchPoolL2 = `
  327. Type: PoolL2
  328. PoolTransfer(0) D-A: 300 (126)
  329. PoolTransfer(0) B-D: 100 (126)
  330. `
  331. poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
  332. require.NoError(t, err)
  333. addL2Txs(t, txsel, poolL2Txs)
  334. // check signatures of L2Txs from the L2DB (to check that the
  335. // parameters of the PoolL2Tx match the original parameters signed
  336. // before inserting it to the L2DB)
  337. l2TxsFromDB, err = txsel.l2db.GetPendingTxs()
  338. require.NoError(t, err)
  339. assert.True(t, l2TxsFromDB[0].VerifySignature(chainID, tc.Users["D"].BJJ.Public().Compress()))
  340. assert.True(t, l2TxsFromDB[1].VerifySignature(chainID, tc.Users["B"].BJJ.Public().Compress()))
  341. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[1].Rollup.Batches[0].Batch.ForgeL1TxsNum])
  342. coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
  343. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  344. require.NoError(t, err)
  345. assert.Equal(t, []common.Idx{262}, coordIdxs)
  346. assert.Equal(t, 0, len(accAuths))
  347. assert.Equal(t, 4, len(oL1UserTxs))
  348. assert.Equal(t, 0, len(oL1CoordTxs))
  349. assert.Equal(t, 2, len(oL2Txs))
  350. assert.Equal(t, len(oL1CoordTxs), len(accAuths))
  351. assert.Equal(t, common.BatchNum(9), txsel.localAccountsDB.CurrentBatch())
  352. assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
  353. checkBalanceByIdx(t, txsel, 261, "30")
  354. checkBalanceByIdx(t, txsel, 262, "75")
  355. checkBalance(t, tc, txsel, "A", 0, "730")
  356. checkBalance(t, tc, txsel, "A", 1, "280")
  357. checkBalance(t, tc, txsel, "B", 0, "380")
  358. checkBalance(t, tc, txsel, "B", 1, "90")
  359. checkBalance(t, tc, txsel, "C", 0, "845")
  360. checkBalance(t, tc, txsel, "C", 1, "100")
  361. checkBalance(t, tc, txsel, "D", 0, "470")
  362. checkSortedByNonce(t, testAccNonces, oL2Txs)
  363. err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs),
  364. txsel.localAccountsDB.CurrentBatch())
  365. require.NoError(t, err)
  366. }
  367. func TestPoolL2TxsWithoutEnoughBalance(t *testing.T) {
  368. set := `
  369. Type: Blockchain
  370. CreateAccountDeposit(0) Coord: 0
  371. CreateAccountDeposit(0) A: 100
  372. CreateAccountDeposit(0) B: 100
  373. > batchL1 // freeze L1User{1}
  374. > batchL1 // forge L1User{1}
  375. > block
  376. `
  377. chainID := uint16(0)
  378. tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
  379. // generate test transactions, the L1CoordinatorTxs generated by Til
  380. // will be ignored at this test, as will be the TxSelector who
  381. // generates them when needed
  382. blocks, err := tc.GenerateBlocks(set)
  383. assert.NoError(t, err)
  384. hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
  385. txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
  386. // restart nonces of TilContext, as will be set by generating directly
  387. // the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
  388. tc.RestartNonces()
  389. tpc := txprocessor.Config{
  390. NLevels: 16,
  391. MaxFeeTx: 10,
  392. MaxTx: 20,
  393. MaxL1Tx: 10,
  394. ChainID: chainID,
  395. }
  396. selectionConfig := &SelectionConfig{
  397. MaxL1UserTxs: 5,
  398. TxProcessorConfig: tpc,
  399. }
  400. // batch1
  401. l1UserTxs := []common.L1Tx{}
  402. _, _, _, _, _, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  403. require.NoError(t, err)
  404. // 1st TransferToEthAddr
  405. expectedTxID0 := "0x028847b86613c0b70be18c8622119ed045b42e4e47d7938fa90bb3d1dc14928965"
  406. // 1st Exit
  407. expectedTxID1 := "0x0200b18773dcf56f770d65870fb02041cb59a088fd35b7c3f3df69f8a250b99a42"
  408. // 2nd TransferToEthAddr
  409. expectedTxID2 := "0x029720ff506153f970f120ac638cd7ee759eeff2c2012e7634a78e4fdc05c04a90"
  410. // batch2
  411. // prepare the PoolL2Txs
  412. batchPoolL2 := `
  413. Type: PoolL2
  414. PoolTransferToEthAddr(0) A-B: 100 (126)
  415. PoolExit(0) B: 100 (126)`
  416. poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
  417. require.NoError(t, err)
  418. // add the PoolL2Txs to the l2DB
  419. addL2Txs(t, txsel, poolL2Txs)
  420. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
  421. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
  422. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  423. require.NoError(t, err)
  424. assert.Equal(t, 3, len(oL1UserTxs))
  425. assert.Equal(t, 0, len(oL1CoordTxs))
  426. assert.Equal(t, 0, len(oL2Txs)) // should be 0 as the 2 PoolL2Txs does not have enough funds
  427. assert.Equal(t, 2, len(discardedL2Txs))
  428. assert.Equal(t, expectedTxID0, discardedL2Txs[0].TxID.String())
  429. assert.Equal(t, expectedTxID1, discardedL2Txs[1].TxID.String())
  430. err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
  431. txsel.localAccountsDB.CurrentBatch())
  432. require.NoError(t, err)
  433. // as the PoolL2Txs have not been really processed, restart nonces
  434. tc.RestartNonces()
  435. // batch3
  436. // NOTE: this batch will result with 1 L2Tx, as the PoolExit tx is not
  437. // possible, as the PoolTransferToEthAddr is not processed yet when
  438. // checking availability of PoolExit. This, in a near-future iteration
  439. // of the TxSelector will return the 2 transactions as valid and
  440. // selected, as the TxSelector will handle this kind of combinations.
  441. batchPoolL2 = `
  442. Type: PoolL2
  443. PoolTransferToEthAddr(0) A-B: 50 (126)`
  444. poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
  445. require.NoError(t, err)
  446. addL2Txs(t, txsel, poolL2Txs)
  447. l1UserTxs = []common.L1Tx{}
  448. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
  449. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  450. require.NoError(t, err)
  451. assert.Equal(t, 0, len(oL1UserTxs))
  452. assert.Equal(t, 0, len(oL1CoordTxs))
  453. assert.Equal(t, 1, len(oL2Txs)) // see 'NOTE' at the beginning of 'batch3' of this test
  454. assert.Equal(t, 2, len(discardedL2Txs))
  455. assert.Equal(t, expectedTxID2, oL2Txs[0].TxID.String())
  456. assert.Equal(t, expectedTxID0, discardedL2Txs[0].TxID.String())
  457. assert.Equal(t, expectedTxID1, discardedL2Txs[1].TxID.String())
  458. assert.Equal(t, common.TxTypeTransferToEthAddr, oL2Txs[0].Type)
  459. err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
  460. txsel.localAccountsDB.CurrentBatch())
  461. require.NoError(t, err)
  462. // batch4
  463. // make the selection of another batch, which should include the
  464. // initial PoolExit, which now is valid as B has enough Balance
  465. l1UserTxs = []common.L1Tx{}
  466. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
  467. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  468. require.NoError(t, err)
  469. assert.Equal(t, 0, len(oL1UserTxs))
  470. assert.Equal(t, 0, len(oL1CoordTxs))
  471. assert.Equal(t, 1, len(oL2Txs))
  472. assert.Equal(t, 1, len(discardedL2Txs))
  473. // the Exit that was not accepted at the batch2
  474. assert.Equal(t, expectedTxID1, oL2Txs[0].TxID.String())
  475. assert.Equal(t, expectedTxID0, discardedL2Txs[0].TxID.String())
  476. assert.Equal(t, common.TxTypeExit, oL2Txs[0].Type)
  477. err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
  478. txsel.localAccountsDB.CurrentBatch())
  479. require.NoError(t, err)
  480. }
  481. func TestTransferToBjj(t *testing.T) {
  482. set := `
  483. Type: Blockchain
  484. AddToken(1)
  485. CreateAccountDeposit(0) Coord: 0
  486. CreateAccountDeposit(0) A: 1000
  487. CreateAccountDeposit(0) B: 1000
  488. CreateAccountDeposit(1) B: 1000
  489. > batchL1 // freeze L1User{1}
  490. > batchL1 // forge L1User{1}
  491. > block
  492. `
  493. chainID := uint16(0)
  494. tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
  495. blocks, err := tc.GenerateBlocks(set)
  496. assert.NoError(t, err)
  497. hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
  498. txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
  499. // restart nonces of TilContext, as will be set by generating directly
  500. // the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
  501. tc.RestartNonces()
  502. addTokens(t, tc, txsel.l2db.DB())
  503. tpc := txprocessor.Config{
  504. NLevels: 16,
  505. MaxFeeTx: 10,
  506. MaxTx: 20,
  507. MaxL1Tx: 10,
  508. ChainID: chainID,
  509. }
  510. selectionConfig := &SelectionConfig{
  511. MaxL1UserTxs: 5,
  512. TxProcessorConfig: tpc,
  513. }
  514. // batch1 to freeze L1UserTxs that will create some accounts with
  515. // positive balance
  516. l1UserTxs := []common.L1Tx{}
  517. _, _, _, _, _, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  518. require.NoError(t, err)
  519. // Transfer is ToBJJ to a BJJ-only account that doesn't exist
  520. // and the coordinator will create it via L1CoordTx.
  521. batchPoolL2 := `
  522. Type: PoolL2
  523. PoolTransferToBJJ(0) A-B: 50 (126)
  524. `
  525. poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
  526. require.NoError(t, err)
  527. // add the PoolL2Txs to the l2DB
  528. addL2Txs(t, txsel, poolL2Txs)
  529. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
  530. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
  531. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  532. require.NoError(t, err)
  533. assert.Equal(t, 4, len(oL1UserTxs))
  534. // We expect the coordinator to add an L1CoordTx to create an account for the recipient of the l2tx
  535. require.Equal(t, 1, len(oL1CoordTxs))
  536. assert.Equal(t, poolL2Txs[0].ToEthAddr, oL1CoordTxs[0].FromEthAddr)
  537. assert.Equal(t, poolL2Txs[0].ToBJJ, oL1CoordTxs[0].FromBJJ)
  538. // fmt.Printf("DBG l1CoordTx[0]: %+v\n", oL1CoordTxs[0])
  539. assert.Equal(t, 1, len(oL2Txs))
  540. assert.Equal(t, 0, len(discardedL2Txs))
  541. err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
  542. txsel.localAccountsDB.CurrentBatch())
  543. require.NoError(t, err)
  544. // Now the BJJ-only account for B is already created, so the transfer
  545. // happens without an L1CoordTx that creates the user account.
  546. batchPoolL2 = `
  547. Type: PoolL2
  548. PoolTransferToBJJ(0) A-B: 50 (126)
  549. `
  550. poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
  551. require.NoError(t, err)
  552. addL2Txs(t, txsel, poolL2Txs)
  553. l1UserTxs = []common.L1Tx{}
  554. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
  555. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  556. require.NoError(t, err)
  557. assert.Equal(t, 0, len(oL1UserTxs))
  558. // Since the BJJ-only account B already exists, the coordinator doesn't add any L1CoordTxs
  559. assert.Equal(t, 0, len(oL1CoordTxs))
  560. assert.Equal(t, 1, len(oL2Txs))
  561. assert.Equal(t, 0, len(discardedL2Txs))
  562. err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
  563. txsel.localAccountsDB.CurrentBatch())
  564. require.NoError(t, err)
  565. // The transfer now is ToBJJ to a BJJ-only account that doesn't exist
  566. // and the coordinator will create it via L1CoordTx. Since it's a
  567. // transfer of a token for which the coordinator doesn't have a fee
  568. // account, another L1CoordTx will be created for the coordinator to
  569. // receive the fees.
  570. batchPoolL2 = `
  571. Type: PoolL2
  572. PoolTransferToBJJ(1) B-A: 50 (126)
  573. `
  574. poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
  575. require.NoError(t, err)
  576. addL2Txs(t, txsel, poolL2Txs)
  577. l1UserTxs = []common.L1Tx{}
  578. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
  579. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  580. require.NoError(t, err)
  581. assert.Equal(t, 0, len(oL1UserTxs))
  582. // We expect the coordinator to add an L1CoordTx to create an account
  583. // to receive the fees by the coordinator and another one for the
  584. // recipient of the l2tx
  585. assert.Equal(t, 2, len(oL1CoordTxs))
  586. // [0] Coordinator account cration for token 1
  587. assert.Equal(t, tc.Users["Coord"].Addr, oL1CoordTxs[0].FromEthAddr)
  588. // [1] User A BJJ-only account creation for token 1
  589. assert.Equal(t, poolL2Txs[0].ToEthAddr, oL1CoordTxs[1].FromEthAddr)
  590. assert.Equal(t, poolL2Txs[0].ToBJJ, oL1CoordTxs[1].FromBJJ)
  591. assert.Equal(t, common.TokenID(1), oL1CoordTxs[1].TokenID)
  592. assert.Equal(t, 1, len(oL2Txs))
  593. assert.Equal(t, 0, len(discardedL2Txs))
  594. err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
  595. txsel.localAccountsDB.CurrentBatch())
  596. require.NoError(t, err)
  597. }
  598. func TestTransferManyFromSameAccount(t *testing.T) {
  599. set := `
  600. Type: Blockchain
  601. CreateAccountDeposit(0) Coord: 0
  602. CreateAccountDeposit(0) A: 1000
  603. CreateAccountDeposit(0) B: 1000
  604. > batchL1 // freeze L1User{1}
  605. > batchL1 // forge L1User{1}
  606. > block
  607. `
  608. chainID := uint16(0)
  609. tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
  610. blocks, err := tc.GenerateBlocks(set)
  611. assert.NoError(t, err)
  612. hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
  613. txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
  614. // restart nonces of TilContext, as will be set by generating directly
  615. // the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
  616. tc.RestartNonces()
  617. tpc := txprocessor.Config{
  618. NLevels: 16,
  619. MaxFeeTx: 10,
  620. MaxTx: 10,
  621. MaxL1Tx: 10,
  622. ChainID: chainID,
  623. }
  624. selectionConfig := &SelectionConfig{
  625. MaxL1UserTxs: 5,
  626. TxProcessorConfig: tpc,
  627. }
  628. // batch1 to freeze L1UserTxs
  629. l1UserTxs := []common.L1Tx{}
  630. _, _, _, _, _, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  631. require.NoError(t, err)
  632. // 8 transfers from the same account
  633. batchPoolL2 := `
  634. Type: PoolL2
  635. PoolTransfer(0) A-B: 10 (126) // 1
  636. PoolTransfer(0) A-B: 10 (126) // 2
  637. PoolTransfer(0) A-B: 10 (126) // 3
  638. PoolTransfer(0) A-B: 10 (126) // 4
  639. PoolTransfer(0) A-B: 10 (126) // 5
  640. PoolTransfer(0) A-B: 10 (126) // 6
  641. PoolTransfer(0) A-B: 10 (126) // 7
  642. PoolTransfer(0) A-B: 10 (126) // 8
  643. PoolTransfer(0) A-B: 10 (126) // 9
  644. PoolTransfer(0) A-B: 10 (126) // 10
  645. PoolTransfer(0) A-B: 10 (126) // 11
  646. `
  647. poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
  648. require.NoError(t, err)
  649. require.Equal(t, 11, len(poolL2Txs))
  650. // reorder poolL2Txs so that nonces are not sorted
  651. poolL2Txs[0], poolL2Txs[7] = poolL2Txs[7], poolL2Txs[0]
  652. poolL2Txs[1], poolL2Txs[10] = poolL2Txs[10], poolL2Txs[1]
  653. // add the PoolL2Txs to the l2DB
  654. addL2Txs(t, txsel, poolL2Txs)
  655. // batch 2 to crate some accounts with positive balance, and do 8 L2Tx transfers from account A
  656. l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
  657. _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
  658. txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
  659. require.NoError(t, err)
  660. assert.Equal(t, 3, len(oL1UserTxs))
  661. require.Equal(t, 0, len(oL1CoordTxs))
  662. assert.Equal(t, 7, len(oL2Txs))
  663. assert.Equal(t, 1, len(discardedL2Txs))
  664. err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
  665. txsel.localAccountsDB.CurrentBatch())
  666. require.NoError(t, err)
  667. }