arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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}