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
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}