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, 10, 100, 24*time.Hour, nil)
 33
 34	dir, err := ioutil.TempDir("", "tmpdb")
 35	require.NoError(t, err)
 36	defer assert.NoError(t, os.RemoveAll(dir))
 37	syncStateDB, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
 38		Type: statedb.TypeTxSelector, NLevels: 0})
 39	require.NoError(t, err)
 40
 41	txselDir, err := ioutil.TempDir("", "tmpTxSelDB")
 42	require.NoError(t, err)
 43	defer assert.NoError(t, os.RemoveAll(dir))
 44
 45	// use Til Coord keys for tests compatibility
 46	coordAccount := &CoordAccount{
 47		Addr:                coordUser.Addr,
 48		BJJ:                 coordUser.BJJ.Public().Compress(),
 49		AccountCreationAuth: nil,
 50	}
 51	// fmt.Printf("%v\n", coordAccount)
 52	auth := common.AccountCreationAuth{
 53		EthAddr: coordUser.Addr,
 54		BJJ:     coordUser.BJJ.Public().Compress(),
 55	}
 56	err = auth.Sign(func(hash []byte) ([]byte, error) {
 57		return ethCrypto.Sign(hash, coordUser.EthSk)
 58	}, chainID, hermezContractAddr)
 59	assert.NoError(t, err)
 60	coordAccount.AccountCreationAuth = auth.Signature
 61
 62	txsel, err := NewTxSelector(coordAccount, txselDir, syncStateDB, l2DB)
 63	require.NoError(t, err)
 64
 65	test.WipeDB(txsel.l2db.DB())
 66
 67	return txsel
 68}
 69
 70func addAccCreationAuth(t *testing.T, tc *til.Context, txsel *TxSelector, chainID uint16, hermezContractAddr ethCommon.Address, username string) []byte {
 71	user := tc.Users[username]
 72	auth := &common.AccountCreationAuth{
 73		EthAddr: user.Addr,
 74		BJJ:     user.BJJ.Public().Compress(),
 75	}
 76	err := auth.Sign(func(hash []byte) ([]byte, error) {
 77		return ethCrypto.Sign(hash, user.EthSk)
 78	}, chainID, hermezContractAddr)
 79	assert.NoError(t, err)
 80
 81	err = txsel.l2db.AddAccountCreationAuth(auth)
 82	assert.NoError(t, err)
 83	return auth.Signature
 84}
 85
 86func addL2Txs(t *testing.T, txsel *TxSelector, poolL2Txs []common.PoolL2Tx) {
 87	for i := 0; i < len(poolL2Txs); i++ {
 88		err := txsel.l2db.AddTxTest(&poolL2Txs[i])
 89		if err != nil {
 90			log.Error(err)
 91		}
 92		require.NoError(t, err)
 93	}
 94}
 95
 96func addTokens(t *testing.T, tc *til.Context, db *sqlx.DB) {
 97	var tokens []common.Token
 98	for i := 0; i < int(tc.LastRegisteredTokenID); i++ {
 99		tokens = append(tokens, common.Token{
100			TokenID:     common.TokenID(i + 1),
101			EthBlockNum: 1,
102			EthAddr:     ethCommon.BytesToAddress([]byte{byte(i + 1)}),
103			Name:        strconv.Itoa(i),
104			Symbol:      strconv.Itoa(i),
105			Decimals:    18,
106		})
107	}
108
109	hdb := historydb.NewHistoryDB(db, nil)
110	assert.NoError(t, hdb.AddBlock(&common.Block{
111		Num: 1,
112	}))
113	assert.NoError(t, hdb.AddTokens(tokens))
114}
115
116func checkBalance(t *testing.T, tc *til.Context, txsel *TxSelector, username string, tokenid int, expected string) {
117	// Accounts.Idx does not match with the TxSelector tests as we are not
118	// using the Til L1CoordinatorTxs (as are generated by the TxSelector
119	// itself when processing the txs, so the Idxs does not match the Til
120	// idxs). But the Idx is obtained through StateDB.GetIdxByEthAddrBJJ
121	user := tc.Users[username]
122	idx, err := txsel.localAccountsDB.GetIdxByEthAddrBJJ(user.Addr, user.BJJ.Public().Compress(), common.TokenID(tokenid))
123	require.NoError(t, err)
124	checkBalanceByIdx(t, txsel, idx, expected)
125}
126
127func checkBalanceByIdx(t *testing.T, txsel *TxSelector, idx common.Idx, expected string) {
128	acc, err := txsel.localAccountsDB.GetAccount(idx)
129	require.NoError(t, err)
130	assert.Equal(t, expected, acc.Balance.String())
131}
132
133// checkSortedByNonce takes as input testAccNonces map, and the array of
134// common.PoolL2Txs, and checks if the nonces correspond to the accumulated
135// values of the map. Also increases the Nonces computed on the map.
136func checkSortedByNonce(t *testing.T, testAccNonces map[common.Idx]common.Nonce, txs []common.PoolL2Tx) {
137	for _, tx := range txs {
138		assert.True(t, testAccNonces[tx.FromIdx] == tx.Nonce,
139			fmt.Sprintf("Idx: %d, expected: %d, tx.Nonce: %d",
140				tx.FromIdx, testAccNonces[tx.FromIdx], tx.Nonce))
141		testAccNonces[tx.FromIdx] = testAccNonces[tx.FromIdx] + 1
142	}
143}
144
145func TestGetL2TxSelectionMinimumFlow0(t *testing.T) {
146	chainID := uint16(0)
147	tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
148	// generate test transactions, the L1CoordinatorTxs generated by Til
149	// will be ignored at this test, as will be the TxSelector who
150	// generates them when needed
151	blocks, err := tc.GenerateBlocks(txsets.SetBlockchainMinimumFlow0)
152	assert.NoError(t, err)
153
154	hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
155	txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
156
157	// restart nonces of TilContext, as will be set by generating directly
158	// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
159	tc.RestartNonces()
160	testAccNonces := make(map[common.Idx]common.Nonce)
161
162	// add tokens to HistoryDB to avoid breaking FK constrains
163	addTokens(t, tc, txsel.l2db.DB())
164
165	tpc := txprocessor.Config{
166		NLevels:  16,
167		MaxFeeTx: 10,
168		MaxTx:    20,
169		MaxL1Tx:  10,
170		ChainID:  chainID,
171	}
172	selectionConfig := &SelectionConfig{
173		MaxL1UserTxs:      5,
174		TxProcessorConfig: tpc,
175	}
176
177	// coordIdxs, accAuths, l1UserTxs, l1CoordTxs, l2Txs, err
178
179	log.Debug("block:0 batch:1")
180	l1UserTxs := []common.L1Tx{}
181	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err := txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
182	require.NoError(t, err)
183	assert.Equal(t, 0, len(oL1UserTxs))
184	assert.Equal(t, 0, len(oL1CoordTxs))
185	assert.Equal(t, 0, len(oL2Txs))
186	assert.Equal(t, common.BatchNum(1), txsel.localAccountsDB.CurrentBatch())
187	assert.Equal(t, common.Idx(255), txsel.localAccountsDB.CurrentIdx())
188
189	log.Debug("block:0 batch:2")
190	l1UserTxs = []common.L1Tx{}
191	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
192	require.NoError(t, err)
193	assert.Equal(t, 0, len(oL1UserTxs))
194	assert.Equal(t, 0, len(oL1CoordTxs))
195	assert.Equal(t, 0, len(oL2Txs))
196	assert.Equal(t, common.BatchNum(2), txsel.localAccountsDB.CurrentBatch())
197	assert.Equal(t, common.Idx(255), txsel.localAccountsDB.CurrentIdx())
198
199	log.Debug("block:0 batch:3")
200	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[2].Batch.ForgeL1TxsNum])
201	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
202	require.NoError(t, err)
203	assert.Equal(t, 2, len(oL1UserTxs))
204	assert.Equal(t, 0, len(oL1CoordTxs))
205	assert.Equal(t, 0, len(oL2Txs))
206	assert.Equal(t, common.BatchNum(3), txsel.localAccountsDB.CurrentBatch())
207	assert.Equal(t, common.Idx(257), txsel.localAccountsDB.CurrentIdx())
208	checkBalance(t, tc, txsel, "A", 0, "500")
209	checkBalance(t, tc, txsel, "C", 1, "0")
210
211	log.Debug("block:0 batch:4")
212	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[3].Batch.ForgeL1TxsNum])
213	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
214	require.NoError(t, err)
215	assert.Equal(t, 1, len(oL1UserTxs))
216	assert.Equal(t, 0, len(oL1CoordTxs))
217	assert.Equal(t, 0, len(oL2Txs))
218	assert.Equal(t, common.BatchNum(4), txsel.localAccountsDB.CurrentBatch())
219	assert.Equal(t, common.Idx(258), txsel.localAccountsDB.CurrentIdx())
220	checkBalance(t, tc, txsel, "A", 0, "500")
221	checkBalance(t, tc, txsel, "A", 1, "500")
222	checkBalance(t, tc, txsel, "C", 1, "0")
223
224	log.Debug("block:0 batch:5")
225	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[4].Batch.ForgeL1TxsNum])
226	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
227	require.NoError(t, err)
228	assert.Equal(t, 0, len(oL1UserTxs))
229	assert.Equal(t, 0, len(oL1CoordTxs))
230	assert.Equal(t, 0, len(oL2Txs))
231	assert.Equal(t, common.BatchNum(5), txsel.localAccountsDB.CurrentBatch())
232	assert.Equal(t, common.Idx(258), txsel.localAccountsDB.CurrentIdx())
233	checkBalance(t, tc, txsel, "A", 0, "500")
234	checkBalance(t, tc, txsel, "A", 1, "500")
235	checkBalance(t, tc, txsel, "C", 1, "0")
236
237	log.Debug("block:0 batch:6")
238	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[5].Batch.ForgeL1TxsNum])
239	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
240	require.NoError(t, err)
241	assert.Equal(t, 1, len(oL1UserTxs))
242	assert.Equal(t, 0, len(oL1CoordTxs))
243	assert.Equal(t, 0, len(oL2Txs))
244	assert.Equal(t, common.BatchNum(6), txsel.localAccountsDB.CurrentBatch())
245	assert.Equal(t, common.Idx(259), txsel.localAccountsDB.CurrentIdx())
246	checkBalance(t, tc, txsel, "A", 0, "600")
247	checkBalance(t, tc, txsel, "A", 1, "500")
248	checkBalance(t, tc, txsel, "B", 0, "400")
249	checkBalance(t, tc, txsel, "C", 1, "0")
250
251	log.Debug("block:0 batch:7")
252	// simulate the PoolL2Txs of the batch7
253	batchPoolL2 := `
254	Type: PoolL2
255	PoolTransferToEthAddr(1) A-B: 200 (126)
256	PoolTransferToEthAddr(0) B-C: 100 (126)`
257	poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
258	require.NoError(t, err)
259	// add AccountCreationAuths that will be used at the next batch
260	accAuthSig0 := addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "B")
261	accAuthSig1 := addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "C")
262	// add the PoolL2Txs to the l2DB
263	addL2Txs(t, txsel, poolL2Txs)
264	// check signatures of L2Txs from the L2DB (to check that the
265	// parameters of the PoolL2Tx match the original parameters signed
266	// before inserting it to the L2DB)
267	l2TxsFromDB, err := txsel.l2db.GetPendingTxs()
268	require.NoError(t, err)
269	assert.True(t, l2TxsFromDB[0].VerifySignature(chainID, tc.Users["A"].BJJ.Public().Compress()))
270	assert.True(t, l2TxsFromDB[1].VerifySignature(chainID, tc.Users["B"].BJJ.Public().Compress()))
271	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[6].Batch.ForgeL1TxsNum])
272	coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err := txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
273	require.NoError(t, err)
274	assert.Equal(t, []common.Idx{261, 262}, coordIdxs)
275	assert.Equal(t, txsel.coordAccount.AccountCreationAuth, accAuths[0])
276	assert.Equal(t, txsel.coordAccount.AccountCreationAuth, accAuths[1])
277	assert.Equal(t, accAuthSig0, accAuths[2])
278	assert.Equal(t, accAuthSig1, accAuths[3])
279	assert.Equal(t, 1, len(oL1UserTxs))
280	assert.Equal(t, 4, len(oL1CoordTxs))
281	assert.Equal(t, 2, len(oL2Txs))
282	assert.Equal(t, len(oL1CoordTxs), len(accAuths))
283	assert.Equal(t, common.BatchNum(7), txsel.localAccountsDB.CurrentBatch())
284	assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
285	checkBalanceByIdx(t, txsel, 261, "20") // CoordIdx for TokenID=1
286	checkBalanceByIdx(t, txsel, 262, "10") // CoordIdx for TokenID=0
287	checkBalance(t, tc, txsel, "A", 0, "600")
288	checkBalance(t, tc, txsel, "A", 1, "280")
289	checkBalance(t, tc, txsel, "B", 0, "290")
290	checkBalance(t, tc, txsel, "B", 1, "200")
291	checkBalance(t, tc, txsel, "C", 0, "100")
292	checkBalance(t, tc, txsel, "D", 0, "800")
293	checkSortedByNonce(t, testAccNonces, oL2Txs)
294	err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs), txsel.localAccountsDB.CurrentBatch())
295	require.NoError(t, err)
296
297	log.Debug("block:0 batch:8")
298	// simulate the PoolL2Txs of the batch8
299	batchPoolL2 = `
300	Type: PoolL2
301	PoolTransfer(0) A-B: 100 (126)
302	PoolTransfer(0) C-A: 50 (126)
303	PoolTransfer(1) B-C: 100 (126)
304	PoolExit(0) A: 100 (126)`
305	poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
306	require.NoError(t, err)
307	addL2Txs(t, txsel, poolL2Txs)
308	// check signatures of L2Txs from the L2DB (to check that the
309	// parameters of the PoolL2Tx match the original parameters signed
310	// before inserting it to the L2DB)
311	l2TxsFromDB, err = txsel.l2db.GetPendingTxs()
312	require.NoError(t, err)
313	assert.True(t, l2TxsFromDB[0].VerifySignature(chainID, tc.Users["A"].BJJ.Public().Compress()))
314	assert.True(t, l2TxsFromDB[1].VerifySignature(chainID, tc.Users["C"].BJJ.Public().Compress()))
315	assert.True(t, l2TxsFromDB[2].VerifySignature(chainID, tc.Users["B"].BJJ.Public().Compress()))
316	assert.True(t, l2TxsFromDB[3].VerifySignature(chainID, tc.Users["A"].BJJ.Public().Compress()))
317	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[7].Batch.ForgeL1TxsNum])
318	coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
319	require.NoError(t, err)
320	assert.Equal(t, []common.Idx{261, 262}, coordIdxs)
321	assert.Equal(t, 0, len(accAuths))
322	assert.Equal(t, 0, len(oL1UserTxs))
323	assert.Equal(t, 0, len(oL1CoordTxs))
324	assert.Equal(t, 4, len(oL2Txs))
325	assert.Equal(t, len(oL1CoordTxs), len(accAuths))
326	assert.Equal(t, common.BatchNum(8), txsel.localAccountsDB.CurrentBatch())
327	assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
328	checkBalanceByIdx(t, txsel, 261, "30")
329	checkBalanceByIdx(t, txsel, 262, "35")
330	checkBalance(t, tc, txsel, "A", 0, "430")
331	checkBalance(t, tc, txsel, "A", 1, "280")
332	checkBalance(t, tc, txsel, "B", 0, "390")
333	checkBalance(t, tc, txsel, "B", 1, "90")
334	checkBalance(t, tc, txsel, "C", 0, "45")
335	checkBalance(t, tc, txsel, "C", 1, "100")
336	checkBalance(t, tc, txsel, "D", 0, "800")
337	checkSortedByNonce(t, testAccNonces, oL2Txs)
338	err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs), txsel.localAccountsDB.CurrentBatch())
339	require.NoError(t, err)
340
341	log.Debug("(batch9)block:1 batch:1")
342	// simulate the PoolL2Txs of the batch9
343	batchPoolL2 = `
344	Type: PoolL2
345	PoolTransfer(0) D-A: 300 (126)
346	PoolTransfer(0) B-D: 100 (126)
347	`
348	poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
349	require.NoError(t, err)
350	addL2Txs(t, txsel, poolL2Txs)
351	// check signatures of L2Txs from the L2DB (to check that the
352	// parameters of the PoolL2Tx match the original parameters signed
353	// before inserting it to the L2DB)
354	l2TxsFromDB, err = txsel.l2db.GetPendingTxs()
355	require.NoError(t, err)
356	assert.True(t, l2TxsFromDB[0].VerifySignature(chainID, tc.Users["D"].BJJ.Public().Compress()))
357	assert.True(t, l2TxsFromDB[1].VerifySignature(chainID, tc.Users["B"].BJJ.Public().Compress()))
358	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[1].Rollup.Batches[0].Batch.ForgeL1TxsNum])
359	coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
360	require.NoError(t, err)
361	assert.Equal(t, []common.Idx{262}, coordIdxs)
362	assert.Equal(t, 0, len(accAuths))
363	assert.Equal(t, 4, len(oL1UserTxs))
364	assert.Equal(t, 0, len(oL1CoordTxs))
365	assert.Equal(t, 2, len(oL2Txs))
366	assert.Equal(t, len(oL1CoordTxs), len(accAuths))
367	assert.Equal(t, common.BatchNum(9), txsel.localAccountsDB.CurrentBatch())
368	assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
369	checkBalanceByIdx(t, txsel, 261, "30")
370	checkBalanceByIdx(t, txsel, 262, "75")
371	checkBalance(t, tc, txsel, "A", 0, "730")
372	checkBalance(t, tc, txsel, "A", 1, "280")
373	checkBalance(t, tc, txsel, "B", 0, "380")
374	checkBalance(t, tc, txsel, "B", 1, "90")
375	checkBalance(t, tc, txsel, "C", 0, "845")
376	checkBalance(t, tc, txsel, "C", 1, "100")
377	checkBalance(t, tc, txsel, "D", 0, "470")
378	checkSortedByNonce(t, testAccNonces, oL2Txs)
379	err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs), txsel.localAccountsDB.CurrentBatch())
380	require.NoError(t, err)
381}
382
383func TestPoolL2TxsWithoutEnoughBalance(t *testing.T) {
384	set := `
385		Type: Blockchain
386
387		CreateAccountDeposit(0) Coord: 0
388		CreateAccountDeposit(0) A: 100
389		CreateAccountDeposit(0) B: 100
390
391		> batchL1 // freeze L1User{1}
392		> batchL1 // forge L1User{1}
393		> block
394	`
395
396	chainID := uint16(0)
397	tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
398	// generate test transactions, the L1CoordinatorTxs generated by Til
399	// will be ignored at this test, as will be the TxSelector who
400	// generates them when needed
401	blocks, err := tc.GenerateBlocks(set)
402	assert.NoError(t, err)
403
404	hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
405	txsel := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
406
407	// restart nonces of TilContext, as will be set by generating directly
408	// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
409	tc.RestartNonces()
410
411	tpc := txprocessor.Config{
412		NLevels:  16,
413		MaxFeeTx: 10,
414		MaxTx:    20,
415		MaxL1Tx:  10,
416		ChainID:  chainID,
417	}
418	selectionConfig := &SelectionConfig{
419		MaxL1UserTxs:      5,
420		TxProcessorConfig: tpc,
421	}
422	// batch1
423	l1UserTxs := []common.L1Tx{}
424	_, _, _, _, _, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
425	require.NoError(t, err)
426
427	expectedTxID0 := "0x0248bae02b5c8c3847d312bfac3a33ae790616e888f2f711f22aeaff007cde92c2" // 1st TransferToEthAddr
428	expectedTxID1 := "0x0249af018311a393c337ab9174ca2466cba489e49942b4ca4e5c530903671c4aef" // 1st Exit
429	expectedTxID2 := "0x0228b93a261a0cdc62f35588c03bd179d31a0807c28afffdb6a7aaf0c4f017e4cf" // 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 create some accounts with positive balance
542	l1UserTxs := []common.L1Tx{}
543	_, _, _, _, _, _, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
544	require.NoError(t, err)
545
546	// Transfer is ToBJJ to a BJJ-only account that doesn't exist
547	// and the coordinator will create it via L1CoordTx.
548
549	batchPoolL2 := `
550	Type: PoolL2
551	PoolTransferToBJJ(0) A-B: 50 (126)
552	`
553	poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
554	require.NoError(t, err)
555
556	// add the PoolL2Txs to the l2DB
557	addL2Txs(t, txsel, poolL2Txs)
558
559	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
560	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err := txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
561	require.NoError(t, err)
562	assert.Equal(t, 4, len(oL1UserTxs))
563	// We expect the coordinator to add an L1CoordTx to create an account for the recipient of the l2tx
564	require.Equal(t, 1, len(oL1CoordTxs))
565	assert.Equal(t, poolL2Txs[0].ToEthAddr, oL1CoordTxs[0].FromEthAddr)
566	assert.Equal(t, poolL2Txs[0].ToBJJ, oL1CoordTxs[0].FromBJJ)
567	// fmt.Printf("DBG l1CoordTx[0]: %+v\n", oL1CoordTxs[0])
568	assert.Equal(t, 1, len(oL2Txs))
569	assert.Equal(t, 0, len(discardedL2Txs))
570	err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs), txsel.localAccountsDB.CurrentBatch())
571	require.NoError(t, err)
572
573	// Now the BJJ-only account for B is already created, so the transfer
574	// happens without an L1CoordTx that creates the user account.
575
576	batchPoolL2 = `
577	Type: PoolL2
578	PoolTransferToBJJ(0) A-B: 50 (126)
579	`
580
581	poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
582	require.NoError(t, err)
583	addL2Txs(t, txsel, poolL2Txs)
584
585	l1UserTxs = []common.L1Tx{}
586	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
587	require.NoError(t, err)
588	assert.Equal(t, 0, len(oL1UserTxs))
589	// Since the BJJ-only account B already exists, the coordinator doesn't add any L1CoordTxs
590	assert.Equal(t, 0, len(oL1CoordTxs))
591	assert.Equal(t, 1, len(oL2Txs))
592	assert.Equal(t, 0, len(discardedL2Txs))
593	err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs), txsel.localAccountsDB.CurrentBatch())
594	require.NoError(t, err)
595
596	// The transfer now is ToBJJ to a BJJ-only account that doesn't exist
597	// and the coordinator will create it via L1CoordTx.   Since it's a
598	// transfer of a token for which the coordinator doesn't have a fee
599	// account, another L1CoordTx will be created for the coordinator to
600	// receive the fees.
601
602	batchPoolL2 = `
603	Type: PoolL2
604	PoolTransferToBJJ(1) B-A: 50 (126)
605	`
606
607	poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
608	require.NoError(t, err)
609	addL2Txs(t, txsel, poolL2Txs)
610
611	l1UserTxs = []common.L1Tx{}
612	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
613	require.NoError(t, err)
614	assert.Equal(t, 0, len(oL1UserTxs))
615	// We expect the coordinator to add an L1CoordTx to create an account
616	// to receive the fees by the coordinator and another one for the
617	// recipient of the l2tx
618	assert.Equal(t, 2, len(oL1CoordTxs))
619	// [0] Coordinator account cration for token 1
620	assert.Equal(t, tc.Users["Coord"].Addr, oL1CoordTxs[0].FromEthAddr)
621	// [1] User A BJJ-only account creation for token 1
622	assert.Equal(t, poolL2Txs[0].ToEthAddr, oL1CoordTxs[1].FromEthAddr)
623	assert.Equal(t, poolL2Txs[0].ToBJJ, oL1CoordTxs[1].FromBJJ)
624	assert.Equal(t, common.TokenID(1), oL1CoordTxs[1].TokenID)
625
626	assert.Equal(t, 1, len(oL2Txs))
627	assert.Equal(t, 0, len(discardedL2Txs))
628	err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs), txsel.localAccountsDB.CurrentBatch())
629	require.NoError(t, err)
630}