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 selectionConfig := &SelectionConfig{
178 MaxL1UserTxs: 5,
179 TxProcessorConfig: tpc,
180 }
181
182 // coordIdxs, accAuths, l1UserTxs, l1CoordTxs, l2Txs, err
183
184 log.Debug("block:0 batch:1")
185 l1UserTxs := []common.L1Tx{}
186 _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err :=
187 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
188 require.NoError(t, err)
189 assert.Equal(t, 0, len(oL1UserTxs))
190 assert.Equal(t, 0, len(oL1CoordTxs))
191 assert.Equal(t, 0, len(oL2Txs))
192 assert.Equal(t, common.BatchNum(1), txsel.localAccountsDB.CurrentBatch())
193 assert.Equal(t, common.Idx(255), txsel.localAccountsDB.CurrentIdx())
194
195 log.Debug("block:0 batch:2")
196 l1UserTxs = []common.L1Tx{}
197 _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
198 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
199 require.NoError(t, err)
200 assert.Equal(t, 0, len(oL1UserTxs))
201 assert.Equal(t, 0, len(oL1CoordTxs))
202 assert.Equal(t, 0, len(oL2Txs))
203 assert.Equal(t, common.BatchNum(2), txsel.localAccountsDB.CurrentBatch())
204 assert.Equal(t, common.Idx(255), txsel.localAccountsDB.CurrentIdx())
205
206 log.Debug("block:0 batch:3")
207 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[2].Batch.ForgeL1TxsNum])
208 _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
209 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
210 require.NoError(t, err)
211 assert.Equal(t, 2, len(oL1UserTxs))
212 assert.Equal(t, 0, len(oL1CoordTxs))
213 assert.Equal(t, 0, len(oL2Txs))
214 assert.Equal(t, common.BatchNum(3), txsel.localAccountsDB.CurrentBatch())
215 assert.Equal(t, common.Idx(257), txsel.localAccountsDB.CurrentIdx())
216 checkBalance(t, tc, txsel, "A", 0, "500")
217 checkBalance(t, tc, txsel, "C", 1, "0")
218
219 log.Debug("block:0 batch:4")
220 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[3].Batch.ForgeL1TxsNum])
221 _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
222 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
223 require.NoError(t, err)
224 assert.Equal(t, 1, len(oL1UserTxs))
225 assert.Equal(t, 0, len(oL1CoordTxs))
226 assert.Equal(t, 0, len(oL2Txs))
227 assert.Equal(t, common.BatchNum(4), txsel.localAccountsDB.CurrentBatch())
228 assert.Equal(t, common.Idx(258), txsel.localAccountsDB.CurrentIdx())
229 checkBalance(t, tc, txsel, "A", 0, "500")
230 checkBalance(t, tc, txsel, "A", 1, "500")
231 checkBalance(t, tc, txsel, "C", 1, "0")
232
233 log.Debug("block:0 batch:5")
234 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[4].Batch.ForgeL1TxsNum])
235 _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
236 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
237 require.NoError(t, err)
238 assert.Equal(t, 0, len(oL1UserTxs))
239 assert.Equal(t, 0, len(oL1CoordTxs))
240 assert.Equal(t, 0, len(oL2Txs))
241 assert.Equal(t, common.BatchNum(5), txsel.localAccountsDB.CurrentBatch())
242 assert.Equal(t, common.Idx(258), txsel.localAccountsDB.CurrentIdx())
243 checkBalance(t, tc, txsel, "A", 0, "500")
244 checkBalance(t, tc, txsel, "A", 1, "500")
245 checkBalance(t, tc, txsel, "C", 1, "0")
246
247 log.Debug("block:0 batch:6")
248 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[5].Batch.ForgeL1TxsNum])
249 _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
250 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
251 require.NoError(t, err)
252 assert.Equal(t, 1, len(oL1UserTxs))
253 assert.Equal(t, 0, len(oL1CoordTxs))
254 assert.Equal(t, 0, len(oL2Txs))
255 assert.Equal(t, common.BatchNum(6), txsel.localAccountsDB.CurrentBatch())
256 assert.Equal(t, common.Idx(259), txsel.localAccountsDB.CurrentIdx())
257 checkBalance(t, tc, txsel, "A", 0, "600")
258 checkBalance(t, tc, txsel, "A", 1, "500")
259 checkBalance(t, tc, txsel, "B", 0, "400")
260 checkBalance(t, tc, txsel, "C", 1, "0")
261
262 log.Debug("block:0 batch:7")
263 // simulate the PoolL2Txs of the batch7
264 batchPoolL2 := `
265 Type: PoolL2
266 PoolTransferToEthAddr(1) A-B: 200 (126)
267 PoolTransferToEthAddr(0) B-C: 100 (126)`
268 poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
269 require.NoError(t, err)
270 // add AccountCreationAuths that will be used at the next batch
271 accAuthSig0 := addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "B")
272 accAuthSig1 := addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "C")
273 // add the PoolL2Txs to the l2DB
274 addL2Txs(t, txsel, poolL2Txs)
275 // check signatures of L2Txs from the L2DB (to check that the
276 // parameters of the PoolL2Tx match the original parameters signed
277 // before inserting it to the L2DB)
278 l2TxsFromDB, err := txsel.l2db.GetPendingTxs()
279 require.NoError(t, err)
280 assert.True(t, l2TxsFromDB[0].VerifySignature(chainID, tc.Users["A"].BJJ.Public().Compress()))
281 assert.True(t, l2TxsFromDB[1].VerifySignature(chainID, tc.Users["B"].BJJ.Public().Compress()))
282 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[6].Batch.ForgeL1TxsNum])
283 coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err :=
284 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
285 require.NoError(t, err)
286 assert.Equal(t, []common.Idx{261, 262}, coordIdxs)
287 assert.Equal(t, txsel.coordAccount.AccountCreationAuth, accAuths[0])
288 assert.Equal(t, txsel.coordAccount.AccountCreationAuth, accAuths[1])
289 assert.Equal(t, accAuthSig0, accAuths[2])
290 assert.Equal(t, accAuthSig1, accAuths[3])
291 assert.Equal(t, 1, len(oL1UserTxs))
292 assert.Equal(t, 4, len(oL1CoordTxs))
293 assert.Equal(t, 2, len(oL2Txs))
294 assert.Equal(t, len(oL1CoordTxs), len(accAuths))
295 assert.Equal(t, common.BatchNum(7), txsel.localAccountsDB.CurrentBatch())
296 assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
297 checkBalanceByIdx(t, txsel, 261, "20") // CoordIdx for TokenID=1
298 checkBalanceByIdx(t, txsel, 262, "10") // CoordIdx for TokenID=0
299 checkBalance(t, tc, txsel, "A", 0, "600")
300 checkBalance(t, tc, txsel, "A", 1, "280")
301 checkBalance(t, tc, txsel, "B", 0, "290")
302 checkBalance(t, tc, txsel, "B", 1, "200")
303 checkBalance(t, tc, txsel, "C", 0, "100")
304 checkBalance(t, tc, txsel, "D", 0, "800")
305 checkSortedByNonce(t, testAccNonces, oL2Txs)
306 err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs),
307 txsel.localAccountsDB.CurrentBatch())
308 require.NoError(t, err)
309
310 log.Debug("block:0 batch:8")
311 // simulate the PoolL2Txs of the batch8
312 batchPoolL2 = `
313 Type: PoolL2
314 PoolTransfer(0) A-B: 100 (126)
315 PoolTransfer(0) C-A: 50 (126)
316 PoolTransfer(1) B-C: 100 (126)
317 PoolExit(0) A: 100 (126)`
318 poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
319 require.NoError(t, err)
320 addL2Txs(t, txsel, poolL2Txs)
321 // check signatures of L2Txs from the L2DB (to check that the
322 // parameters of the PoolL2Tx match the original parameters signed
323 // before inserting it to the L2DB)
324 l2TxsFromDB, err = txsel.l2db.GetPendingTxs()
325 require.NoError(t, err)
326 assert.True(t, l2TxsFromDB[0].VerifySignature(chainID, tc.Users["A"].BJJ.Public().Compress()))
327 assert.True(t, l2TxsFromDB[1].VerifySignature(chainID, tc.Users["C"].BJJ.Public().Compress()))
328 assert.True(t, l2TxsFromDB[2].VerifySignature(chainID, tc.Users["B"].BJJ.Public().Compress()))
329 assert.True(t, l2TxsFromDB[3].VerifySignature(chainID, tc.Users["A"].BJJ.Public().Compress()))
330 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[7].Batch.ForgeL1TxsNum])
331 coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
332 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
333 require.NoError(t, err)
334 assert.Equal(t, []common.Idx{261, 262}, coordIdxs)
335 assert.Equal(t, 0, len(accAuths))
336 assert.Equal(t, 0, len(oL1UserTxs))
337 assert.Equal(t, 0, len(oL1CoordTxs))
338 assert.Equal(t, 4, len(oL2Txs))
339 assert.Equal(t, len(oL1CoordTxs), len(accAuths))
340 assert.Equal(t, common.BatchNum(8), txsel.localAccountsDB.CurrentBatch())
341 assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
342 checkBalanceByIdx(t, txsel, 261, "30")
343 checkBalanceByIdx(t, txsel, 262, "35")
344 checkBalance(t, tc, txsel, "A", 0, "430")
345 checkBalance(t, tc, txsel, "A", 1, "280")
346 checkBalance(t, tc, txsel, "B", 0, "390")
347 checkBalance(t, tc, txsel, "B", 1, "90")
348 checkBalance(t, tc, txsel, "C", 0, "45")
349 checkBalance(t, tc, txsel, "C", 1, "100")
350 checkBalance(t, tc, txsel, "D", 0, "800")
351 checkSortedByNonce(t, testAccNonces, oL2Txs)
352 err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs),
353 txsel.localAccountsDB.CurrentBatch())
354 require.NoError(t, err)
355
356 log.Debug("(batch9)block:1 batch:1")
357 // simulate the PoolL2Txs of the batch9
358 batchPoolL2 = `
359 Type: PoolL2
360 PoolTransfer(0) D-A: 300 (126)
361 PoolTransfer(0) B-D: 100 (126)
362 `
363 poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
364 require.NoError(t, err)
365 addL2Txs(t, txsel, poolL2Txs)
366 // check signatures of L2Txs from the L2DB (to check that the
367 // parameters of the PoolL2Tx match the original parameters signed
368 // before inserting it to the L2DB)
369 l2TxsFromDB, err = txsel.l2db.GetPendingTxs()
370 require.NoError(t, err)
371 assert.True(t, l2TxsFromDB[0].VerifySignature(chainID, tc.Users["D"].BJJ.Public().Compress()))
372 assert.True(t, l2TxsFromDB[1].VerifySignature(chainID, tc.Users["B"].BJJ.Public().Compress()))
373 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[1].Rollup.Batches[0].Batch.ForgeL1TxsNum])
374 coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
375 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
376 require.NoError(t, err)
377 assert.Equal(t, []common.Idx{262}, coordIdxs)
378 assert.Equal(t, 0, len(accAuths))
379 assert.Equal(t, 4, len(oL1UserTxs))
380 assert.Equal(t, 0, len(oL1CoordTxs))
381 assert.Equal(t, 2, len(oL2Txs))
382 assert.Equal(t, len(oL1CoordTxs), len(accAuths))
383 assert.Equal(t, common.BatchNum(9), txsel.localAccountsDB.CurrentBatch())
384 assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
385 checkBalanceByIdx(t, txsel, 261, "30")
386 checkBalanceByIdx(t, txsel, 262, "75")
387 checkBalance(t, tc, txsel, "A", 0, "730")
388 checkBalance(t, tc, txsel, "A", 1, "280")
389 checkBalance(t, tc, txsel, "B", 0, "380")
390 checkBalance(t, tc, txsel, "B", 1, "90")
391 checkBalance(t, tc, txsel, "C", 0, "845")
392 checkBalance(t, tc, txsel, "C", 1, "100")
393 checkBalance(t, tc, txsel, "D", 0, "470")
394 checkSortedByNonce(t, testAccNonces, oL2Txs)
395 err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs),
396 txsel.localAccountsDB.CurrentBatch())
397 require.NoError(t, err)
398}
399
400func TestPoolL2TxsWithoutEnoughBalance(t *testing.T) {
401 set := `
402 Type: Blockchain
403
404 CreateAccountDeposit(0) Coord: 0
405 CreateAccountDeposit(0) A: 100
406 CreateAccountDeposit(0) B: 100
407
408 > batchL1 // freeze L1User{1}
409 > batchL1 // forge L1User{1}
410 > block
411 `
412
413 chainID := uint16(0)
414 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
415 // generate test transactions, the L1CoordinatorTxs generated by Til
416 // will be ignored at this test, as will be the TxSelector who
417 // generates them when needed
418 blocks, err := tc.GenerateBlocks(set)
419 assert.NoError(t, err)
420
421 hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
422 txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
423
424 // restart nonces of TilContext, as will be set by generating directly
425 // the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
426 tc.RestartNonces()
427
428 tpc := txprocessor.Config{
429 NLevels: 16,
430 MaxFeeTx: 10,
431 MaxTx: 20,
432 MaxL1Tx: 10,
433 ChainID: chainID,
434 }
435 selectionConfig := &SelectionConfig{
436 MaxL1UserTxs: 5,
437 TxProcessorConfig: tpc,
438 }
439 // batch1
440 l1UserTxs := []common.L1Tx{}
441 _, _, _, _, _, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
442 require.NoError(t, err)
443 // 1st TransferToEthAddr
444 expectedTxID0 := "0x028847b86613c0b70be18c8622119ed045b42e4e47d7938fa90bb3d1dc14928965"
445 // 1st Exit
446 expectedTxID1 := "0x0200b18773dcf56f770d65870fb02041cb59a088fd35b7c3f3df69f8a250b99a42"
447 // 2nd TransferToEthAddr
448 expectedTxID2 := "0x029720ff506153f970f120ac638cd7ee759eeff2c2012e7634a78e4fdc05c04a90"
449
450 // batch2
451 // prepare the PoolL2Txs
452 batchPoolL2 := `
453 Type: PoolL2
454 PoolTransferToEthAddr(0) A-B: 100 (126)
455 PoolExit(0) B: 100 (126)`
456 poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
457 require.NoError(t, err)
458 // add the PoolL2Txs to the l2DB
459 addL2Txs(t, txsel, poolL2Txs)
460
461 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
462 _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
463 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
464 require.NoError(t, err)
465 assert.Equal(t, 3, len(oL1UserTxs))
466 assert.Equal(t, 0, len(oL1CoordTxs))
467 assert.Equal(t, 0, len(oL2Txs)) // should be 0 as the 2 PoolL2Txs does not have enough funds
468 assert.Equal(t, 2, len(discardedL2Txs))
469 assert.Equal(t, expectedTxID0, discardedL2Txs[0].TxID.String())
470 assert.Equal(t, expectedTxID1, discardedL2Txs[1].TxID.String())
471 err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
472 txsel.localAccountsDB.CurrentBatch())
473 require.NoError(t, err)
474
475 // as the PoolL2Txs have not been really processed, restart nonces
476 tc.RestartNonces()
477
478 // batch3
479 // NOTE: this batch will result with 1 L2Tx, as the PoolExit tx is not
480 // possible, as the PoolTransferToEthAddr is not processed yet when
481 // checking availability of PoolExit. This, in a near-future iteration
482 // of the TxSelector will return the 2 transactions as valid and
483 // selected, as the TxSelector will handle this kind of combinations.
484 batchPoolL2 = `
485 Type: PoolL2
486 PoolTransferToEthAddr(0) A-B: 50 (126)`
487 poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
488 require.NoError(t, err)
489 addL2Txs(t, txsel, poolL2Txs)
490
491 l1UserTxs = []common.L1Tx{}
492 _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
493 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
494 require.NoError(t, err)
495 assert.Equal(t, 0, len(oL1UserTxs))
496 assert.Equal(t, 0, len(oL1CoordTxs))
497 assert.Equal(t, 1, len(oL2Txs)) // see 'NOTE' at the beginning of 'batch3' of this test
498 assert.Equal(t, 2, len(discardedL2Txs))
499 assert.Equal(t, expectedTxID2, oL2Txs[0].TxID.String())
500 assert.Equal(t, expectedTxID0, discardedL2Txs[0].TxID.String())
501 assert.Equal(t, expectedTxID1, discardedL2Txs[1].TxID.String())
502 assert.Equal(t, common.TxTypeTransferToEthAddr, oL2Txs[0].Type)
503 err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
504 txsel.localAccountsDB.CurrentBatch())
505 require.NoError(t, err)
506
507 // batch4
508 // make the selection of another batch, which should include the
509 // initial PoolExit, which now is valid as B has enough Balance
510 l1UserTxs = []common.L1Tx{}
511 _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
512 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
513 require.NoError(t, err)
514 assert.Equal(t, 0, len(oL1UserTxs))
515 assert.Equal(t, 0, len(oL1CoordTxs))
516 assert.Equal(t, 1, len(oL2Txs))
517 assert.Equal(t, 1, len(discardedL2Txs))
518 // the Exit that was not accepted at the batch2
519 assert.Equal(t, expectedTxID1, oL2Txs[0].TxID.String())
520 assert.Equal(t, expectedTxID0, discardedL2Txs[0].TxID.String())
521 assert.Equal(t, common.TxTypeExit, oL2Txs[0].Type)
522 err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
523 txsel.localAccountsDB.CurrentBatch())
524 require.NoError(t, err)
525}
526
527func TestTransferToBjj(t *testing.T) {
528 set := `
529 Type: Blockchain
530 AddToken(1)
531
532 CreateAccountDeposit(0) Coord: 0
533 CreateAccountDeposit(0) A: 1000
534 CreateAccountDeposit(0) B: 1000
535 CreateAccountDeposit(1) B: 1000
536
537 > batchL1 // freeze L1User{1}
538 > batchL1 // forge L1User{1}
539 > block
540 `
541
542 chainID := uint16(0)
543 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
544 blocks, err := tc.GenerateBlocks(set)
545 assert.NoError(t, err)
546
547 hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
548 txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
549
550 // restart nonces of TilContext, as will be set by generating directly
551 // the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
552 tc.RestartNonces()
553
554 addTokens(t, tc, txsel.l2db.DB())
555
556 tpc := txprocessor.Config{
557 NLevels: 16,
558 MaxFeeTx: 10,
559 MaxTx: 20,
560 MaxL1Tx: 10,
561 ChainID: chainID,
562 }
563 selectionConfig := &SelectionConfig{
564 MaxL1UserTxs: 5,
565 TxProcessorConfig: tpc,
566 }
567 // batch1 to freeze L1UserTxs that will create some accounts with
568 // positive balance
569 l1UserTxs := []common.L1Tx{}
570 _, _, _, _, _, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
571 require.NoError(t, err)
572
573 // Transfer is ToBJJ to a BJJ-only account that doesn't exist
574 // and the coordinator will create it via L1CoordTx.
575
576 batchPoolL2 := `
577 Type: PoolL2
578 PoolTransferToBJJ(0) A-B: 50 (126)
579 `
580 poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
581 require.NoError(t, err)
582
583 // add the PoolL2Txs to the l2DB
584 addL2Txs(t, txsel, poolL2Txs)
585
586 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
587 _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
588 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
589 require.NoError(t, err)
590 assert.Equal(t, 4, len(oL1UserTxs))
591 // We expect the coordinator to add an L1CoordTx to create an account for the recipient of the l2tx
592 require.Equal(t, 1, len(oL1CoordTxs))
593 assert.Equal(t, poolL2Txs[0].ToEthAddr, oL1CoordTxs[0].FromEthAddr)
594 assert.Equal(t, poolL2Txs[0].ToBJJ, oL1CoordTxs[0].FromBJJ)
595 // fmt.Printf("DBG l1CoordTx[0]: %+v\n", oL1CoordTxs[0])
596 assert.Equal(t, 1, len(oL2Txs))
597 assert.Equal(t, 0, len(discardedL2Txs))
598 err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
599 txsel.localAccountsDB.CurrentBatch())
600 require.NoError(t, err)
601
602 // Now the BJJ-only account for B is already created, so the transfer
603 // happens without an L1CoordTx that creates the user account.
604
605 batchPoolL2 = `
606 Type: PoolL2
607 PoolTransferToBJJ(0) A-B: 50 (126)
608 `
609
610 poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
611 require.NoError(t, err)
612 addL2Txs(t, txsel, poolL2Txs)
613
614 l1UserTxs = []common.L1Tx{}
615 _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
616 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
617 require.NoError(t, err)
618 assert.Equal(t, 0, len(oL1UserTxs))
619 // Since the BJJ-only account B already exists, the coordinator doesn't add any L1CoordTxs
620 assert.Equal(t, 0, len(oL1CoordTxs))
621 assert.Equal(t, 1, len(oL2Txs))
622 assert.Equal(t, 0, len(discardedL2Txs))
623 err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
624 txsel.localAccountsDB.CurrentBatch())
625 require.NoError(t, err)
626
627 // The transfer now is ToBJJ to a BJJ-only account that doesn't exist
628 // and the coordinator will create it via L1CoordTx. Since it's a
629 // transfer of a token for which the coordinator doesn't have a fee
630 // account, another L1CoordTx will be created for the coordinator to
631 // receive the fees.
632
633 batchPoolL2 = `
634 Type: PoolL2
635 PoolTransferToBJJ(1) B-A: 50 (126)
636 `
637
638 poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
639 require.NoError(t, err)
640 addL2Txs(t, txsel, poolL2Txs)
641
642 l1UserTxs = []common.L1Tx{}
643 _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
644 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
645 require.NoError(t, err)
646 assert.Equal(t, 0, len(oL1UserTxs))
647 // We expect the coordinator to add an L1CoordTx to create an account
648 // to receive the fees by the coordinator and another one for the
649 // recipient of the l2tx
650 assert.Equal(t, 2, len(oL1CoordTxs))
651 // [0] Coordinator account cration for token 1
652 assert.Equal(t, tc.Users["Coord"].Addr, oL1CoordTxs[0].FromEthAddr)
653 // [1] User A BJJ-only account creation for token 1
654 assert.Equal(t, poolL2Txs[0].ToEthAddr, oL1CoordTxs[1].FromEthAddr)
655 assert.Equal(t, poolL2Txs[0].ToBJJ, oL1CoordTxs[1].FromBJJ)
656 assert.Equal(t, common.TokenID(1), oL1CoordTxs[1].TokenID)
657
658 assert.Equal(t, 1, len(oL2Txs))
659 assert.Equal(t, 0, len(discardedL2Txs))
660 err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
661 txsel.localAccountsDB.CurrentBatch())
662 require.NoError(t, err)
663}
664
665func TestTransferManyFromSameAccount(t *testing.T) {
666 set := `
667 Type: Blockchain
668
669 CreateAccountDeposit(0) Coord: 0
670 CreateAccountDeposit(0) A: 1000
671 CreateAccountDeposit(0) B: 1000
672
673 > batchL1 // freeze L1User{1}
674 > batchL1 // forge L1User{1}
675 > block
676 `
677
678 chainID := uint16(0)
679 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
680 blocks, err := tc.GenerateBlocks(set)
681 assert.NoError(t, err)
682
683 hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
684 txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
685
686 // restart nonces of TilContext, as will be set by generating directly
687 // the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
688 tc.RestartNonces()
689
690 tpc := txprocessor.Config{
691 NLevels: 16,
692 MaxFeeTx: 10,
693 MaxTx: 10,
694 MaxL1Tx: 10,
695 ChainID: chainID,
696 }
697 selectionConfig := &SelectionConfig{
698 MaxL1UserTxs: 5,
699 TxProcessorConfig: tpc,
700 }
701 // batch1 to freeze L1UserTxs
702 l1UserTxs := []common.L1Tx{}
703 _, _, _, _, _, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
704 require.NoError(t, err)
705
706 // 8 transfers from the same account
707
708 batchPoolL2 := `
709 Type: PoolL2
710 PoolTransfer(0) A-B: 10 (126) // 1
711 PoolTransfer(0) A-B: 10 (126) // 2
712 PoolTransfer(0) A-B: 10 (126) // 3
713 PoolTransfer(0) A-B: 10 (126) // 4
714 PoolTransfer(0) A-B: 10 (126) // 5
715 PoolTransfer(0) A-B: 10 (126) // 6
716 PoolTransfer(0) A-B: 10 (126) // 7
717 PoolTransfer(0) A-B: 10 (126) // 8
718 PoolTransfer(0) A-B: 10 (126) // 9
719 PoolTransfer(0) A-B: 10 (126) // 10
720 PoolTransfer(0) A-B: 10 (126) // 11
721 `
722 poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
723 require.NoError(t, err)
724 require.Equal(t, 11, len(poolL2Txs))
725
726 // reorder poolL2Txs so that nonces are not sorted
727 poolL2Txs[0], poolL2Txs[7] = poolL2Txs[7], poolL2Txs[0]
728 poolL2Txs[1], poolL2Txs[10] = poolL2Txs[10], poolL2Txs[1]
729
730 // add the PoolL2Txs to the l2DB
731 addL2Txs(t, txsel, poolL2Txs)
732 // batch 2 to crate some accounts with positive balance, and do 8 L2Tx transfers from account A
733 l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
734 _, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
735 txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
736 require.NoError(t, err)
737 assert.Equal(t, 3, len(oL1UserTxs))
738 require.Equal(t, 0, len(oL1CoordTxs))
739 assert.Equal(t, 7, len(oL2Txs))
740 assert.Equal(t, 1, len(discardedL2Txs))
741
742 err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
743 txsel.localAccountsDB.CurrentBatch())
744 require.NoError(t, err)
745}