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