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