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