arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/txselector/txselector_test.go
txselector/txselector_test.goRaw
  1package txselector
  2
  3import (
  4	"crypto/ecdsa"
  5	"fmt"
  6	"io/ioutil"
  7	"math/big"
  8	"os"
  9	"strconv"
 10	"testing"
 11	"time"
 12
 13	ethCommon "github.com/ethereum/go-ethereum/common"
 14	ethCrypto "github.com/ethereum/go-ethereum/crypto"
 15	"github.com/hermeznetwork/hermez-node/common"
 16	dbUtils "github.com/hermeznetwork/hermez-node/db"
 17	"github.com/hermeznetwork/hermez-node/db/historydb"
 18	"github.com/hermeznetwork/hermez-node/db/l2db"
 19	"github.com/hermeznetwork/hermez-node/db/statedb"
 20	"github.com/hermeznetwork/hermez-node/log"
 21	"github.com/hermeznetwork/hermez-node/test"
 22	"github.com/hermeznetwork/hermez-node/test/til"
 23	"github.com/hermeznetwork/hermez-node/txprocessor"
 24	"github.com/iden3/go-iden3-crypto/babyjub"
 25	"github.com/jmoiron/sqlx"
 26	"github.com/stretchr/testify/assert"
 27	"github.com/stretchr/testify/require"
 28)
 29
 30func initTest(t *testing.T, chainID uint16, hermezContractAddr ethCommon.Address, testSet string) (*TxSelector, *til.Context) {
 31	pass := os.Getenv("POSTGRES_PASS")
 32	db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
 33	require.NoError(t, err)
 34	l2DB := l2db.NewL2DB(db, 10, 100, 24*time.Hour)
 35
 36	dir, err := ioutil.TempDir("", "tmpdb")
 37	require.NoError(t, err)
 38	defer assert.NoError(t, os.RemoveAll(dir))
 39	sdb, err := statedb.NewStateDB(dir, 128, statedb.TypeTxSelector, 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	// coordinator keys
 47	var ethSk ecdsa.PrivateKey
 48	ethSk.D = big.NewInt(int64(1)) // only for testing
 49	ethSk.PublicKey.X, ethSk.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(ethSk.D.Bytes())
 50	ethSk.Curve = ethCrypto.S256()
 51	addr := ethCrypto.PubkeyToAddress(ethSk.PublicKey)
 52	var bjj babyjub.PublicKeyComp
 53	err = bjj.UnmarshalText([]byte("c433f7a696b7aa3a5224efb3993baf0ccd9e92eecee0c29a3f6c8208a9e81d9e"))
 54	require.NoError(t, err)
 55	coordAccount := &CoordAccount{
 56		Addr:                addr,
 57		BJJ:                 bjj,
 58		AccountCreationAuth: nil,
 59	}
 60	a := &common.AccountCreationAuth{
 61		EthAddr: addr,
 62		BJJ:     bjj,
 63	}
 64	msg, err := a.HashToSign(chainID, hermezContractAddr)
 65	assert.NoError(t, err)
 66	sig, err := ethCrypto.Sign(msg, &ethSk)
 67	assert.NoError(t, err)
 68	sig[64] += 27
 69	coordAccount.AccountCreationAuth = sig
 70
 71	txsel, err := NewTxSelector(coordAccount, txselDir, sdb, l2DB)
 72	require.NoError(t, err)
 73
 74	test.WipeDB(txsel.l2db.DB())
 75
 76	tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
 77
 78	return txsel, tc
 79}
 80
 81func addAccCreationAuth(t *testing.T, tc *til.Context, txsel *TxSelector, chainID uint16, hermezContractAddr ethCommon.Address, username string) []byte {
 82	user := tc.Users[username]
 83	a := &common.AccountCreationAuth{
 84		EthAddr: user.Addr,
 85		BJJ:     user.BJJ.Public().Compress(),
 86	}
 87	msg, err := a.HashToSign(chainID, hermezContractAddr)
 88	assert.NoError(t, err)
 89	sig, err := ethCrypto.Sign(msg, user.EthSk)
 90	assert.NoError(t, err)
 91	sig[64] += 27
 92	a.Signature = sig
 93
 94	err = txsel.l2db.AddAccountCreationAuth(a)
 95	assert.NoError(t, err)
 96	return a.Signature
 97}
 98
 99func addL2Txs(t *testing.T, txsel *TxSelector, poolL2Txs []common.PoolL2Tx) {
100	for i := 0; i < len(poolL2Txs); i++ {
101		err := txsel.l2db.AddTxTest(&poolL2Txs[i])
102		if err != nil {
103			log.Error(err)
104		}
105		require.NoError(t, err)
106	}
107}
108
109func addTokens(t *testing.T, tc *til.Context, db *sqlx.DB) {
110	var tokens []common.Token
111	for i := 0; i < int(tc.LastRegisteredTokenID); i++ {
112		tokens = append(tokens, common.Token{
113			TokenID:     common.TokenID(i + 1),
114			EthBlockNum: 1,
115			EthAddr:     ethCommon.BytesToAddress([]byte{byte(i + 1)}),
116			Name:        strconv.Itoa(i),
117			Symbol:      strconv.Itoa(i),
118			Decimals:    18,
119		})
120	}
121
122	hdb := historydb.NewHistoryDB(db)
123	assert.NoError(t, hdb.AddBlock(&common.Block{
124		Num: 1,
125	}))
126	assert.NoError(t, hdb.AddTokens(tokens))
127}
128
129func checkBalance(t *testing.T, tc *til.Context, txsel *TxSelector, username string, tokenid int, expected string) {
130	// Accounts.Idx does not match with the TxSelector tests as we are not
131	// using the Til L1CoordinatorTxs (as are generated by the TxSelector
132	// itself when processing the txs, so the Idxs does not match the Til
133	// idxs). But the Idx is obtained through StateDB.GetIdxByEthAddrBJJ
134	user := tc.Users[username]
135	idx, err := txsel.localAccountsDB.GetIdxByEthAddrBJJ(user.Addr, user.BJJ.Public().Compress(), common.TokenID(tokenid))
136	require.NoError(t, err)
137	checkBalanceByIdx(t, txsel, idx, expected)
138}
139
140func checkBalanceByIdx(t *testing.T, txsel *TxSelector, idx common.Idx, expected string) {
141	acc, err := txsel.localAccountsDB.GetAccount(idx)
142	require.NoError(t, err)
143	assert.Equal(t, expected, acc.Balance.String())
144}
145
146// checkSortedByNonce takes as input testAccNonces map, and the array of
147// common.PoolL2Txs, and checks if the nonces correspond to the accumulated
148// values of the map. Also increases the Nonces computed on the map.
149func checkSortedByNonce(t *testing.T, testAccNonces map[common.Idx]common.Nonce, txs []common.PoolL2Tx) {
150	for _, tx := range txs {
151		assert.True(t, testAccNonces[tx.FromIdx] == tx.Nonce,
152			fmt.Sprintf("Idx: %d, expected: %d, tx.Nonce: %d",
153				tx.FromIdx, testAccNonces[tx.FromIdx], tx.Nonce))
154		testAccNonces[tx.FromIdx] = testAccNonces[tx.FromIdx] + 1
155	}
156}
157
158func TestGetL2TxSelectionMinimumFlow0(t *testing.T) {
159	chainID := uint16(0)
160	hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
161	txsel, tc := initTest(t, chainID, hermezContractAddr, til.SetPool0)
162
163	// generate test transactions, the L1CoordinatorTxs generated by Til
164	// will be ignored at this test, as will be the TxSelector who
165	// generates them when needed
166	blocks, err := tc.GenerateBlocks(til.SetBlockchainMinimumFlow0)
167	assert.NoError(t, err)
168
169	// restart nonces of TilContext, as will be set by generating directly
170	// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
171	tc.RestartNonces()
172	testAccNonces := make(map[common.Idx]common.Nonce)
173
174	// add tokens to HistoryDB to avoid breaking FK constrains
175	addTokens(t, tc, txsel.l2db.DB())
176
177	tpc := txprocessor.Config{
178		NLevels:  16,
179		MaxFeeTx: 10,
180		MaxTx:    20,
181		MaxL1Tx:  10,
182		ChainID:  chainID,
183	}
184	selectionConfig := &SelectionConfig{
185		MaxL1UserTxs:      5,
186		TxProcessorConfig: tpc,
187	}
188
189	// coordIdxs, accAuths, l1UserTxs, l1CoordTxs, l2Txs, err
190
191	log.Debug("block:0 batch:0")
192	l1UserTxs := []common.L1Tx{}
193	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, err := txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
194	require.NoError(t, err)
195	assert.Equal(t, 0, len(oL1UserTxs))
196	assert.Equal(t, 0, len(oL1CoordTxs))
197	assert.Equal(t, 0, len(oL2Txs))
198	assert.Equal(t, common.BatchNum(1), txsel.localAccountsDB.CurrentBatch())
199	assert.Equal(t, common.Idx(255), txsel.localAccountsDB.CurrentIdx())
200
201	log.Debug("block:0 batch:1")
202	l1UserTxs = []common.L1Tx{}
203	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
204	require.NoError(t, err)
205	assert.Equal(t, 0, len(oL1UserTxs))
206	assert.Equal(t, 0, len(oL1CoordTxs))
207	assert.Equal(t, 0, len(oL2Txs))
208	assert.Equal(t, common.BatchNum(2), txsel.localAccountsDB.CurrentBatch())
209	assert.Equal(t, common.Idx(255), txsel.localAccountsDB.CurrentIdx())
210
211	log.Debug("block:0 batch:2")
212	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[2].Batch.ForgeL1TxsNum])
213	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
214	require.NoError(t, err)
215	assert.Equal(t, 2, len(oL1UserTxs))
216	assert.Equal(t, 0, len(oL1CoordTxs))
217	assert.Equal(t, 0, len(oL2Txs))
218	assert.Equal(t, common.BatchNum(3), txsel.localAccountsDB.CurrentBatch())
219	assert.Equal(t, common.Idx(257), txsel.localAccountsDB.CurrentIdx())
220	checkBalance(t, tc, txsel, "A", 0, "500")
221	checkBalance(t, tc, txsel, "C", 1, "0")
222
223	log.Debug("block:0 batch:3")
224	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[3].Batch.ForgeL1TxsNum])
225	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
226	require.NoError(t, err)
227	assert.Equal(t, 1, len(oL1UserTxs))
228	assert.Equal(t, 0, len(oL1CoordTxs))
229	assert.Equal(t, 0, len(oL2Txs))
230	assert.Equal(t, common.BatchNum(4), txsel.localAccountsDB.CurrentBatch())
231	assert.Equal(t, common.Idx(258), txsel.localAccountsDB.CurrentIdx())
232	checkBalance(t, tc, txsel, "A", 0, "500")
233	checkBalance(t, tc, txsel, "A", 1, "500")
234	checkBalance(t, tc, txsel, "C", 1, "0")
235
236	log.Debug("block:0 batch:4")
237	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[4].Batch.ForgeL1TxsNum])
238	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
239	require.NoError(t, err)
240	assert.Equal(t, 0, len(oL1UserTxs))
241	assert.Equal(t, 0, len(oL1CoordTxs))
242	assert.Equal(t, 0, len(oL2Txs))
243	assert.Equal(t, common.BatchNum(5), txsel.localAccountsDB.CurrentBatch())
244	assert.Equal(t, common.Idx(258), txsel.localAccountsDB.CurrentIdx())
245	checkBalance(t, tc, txsel, "A", 0, "500")
246	checkBalance(t, tc, txsel, "A", 1, "500")
247	checkBalance(t, tc, txsel, "C", 1, "0")
248
249	log.Debug("block:0 batch:5")
250	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[5].Batch.ForgeL1TxsNum])
251	_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
252	require.NoError(t, err)
253	assert.Equal(t, 1, len(oL1UserTxs))
254	assert.Equal(t, 0, len(oL1CoordTxs))
255	assert.Equal(t, 0, len(oL2Txs))
256	assert.Equal(t, common.BatchNum(6), txsel.localAccountsDB.CurrentBatch())
257	assert.Equal(t, common.Idx(259), txsel.localAccountsDB.CurrentIdx())
258	checkBalance(t, tc, txsel, "A", 0, "600")
259	checkBalance(t, tc, txsel, "A", 1, "500")
260	checkBalance(t, tc, txsel, "B", 0, "400")
261	checkBalance(t, tc, txsel, "C", 1, "0")
262
263	log.Debug("block:0 batch:6")
264	// simulate the PoolL2Txs of the batch6
265	batchPoolL2 := `
266	Type: PoolL2
267	PoolTransfer(1) A-B: 200 (126)
268	PoolTransfer(0) B-C: 100 (126)`
269	poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
270	require.NoError(t, err)
271	// add AccountCreationAuths that will be used at the next batch
272	accAuthSig0 := addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "B")
273	accAuthSig1 := addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "C")
274	// add ToEthAddr for the corresponent ToIdx, and remove ToIdx for Batches[6].L2Tx
275	poolL2Txs[0].ToEthAddr = tc.Users["B"].Addr
276	poolL2Txs[0].ToIdx = common.Idx(0)
277	poolL2Txs[1].ToEthAddr = tc.Users["C"].Addr
278	poolL2Txs[1].ToIdx = common.Idx(0)
279	// add the PoolL2Txs to the l2DB
280	addL2Txs(t, txsel, poolL2Txs)
281
282	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[6].Batch.ForgeL1TxsNum])
283	coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, err := txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
284	require.NoError(t, err)
285	assert.Equal(t, []common.Idx{261, 262}, coordIdxs)
286	assert.Equal(t, txsel.coordAccount.AccountCreationAuth, accAuths[0])
287	assert.Equal(t, txsel.coordAccount.AccountCreationAuth, accAuths[1])
288	assert.Equal(t, accAuthSig0, accAuths[2])
289	assert.Equal(t, accAuthSig1, accAuths[3])
290	assert.Equal(t, 1, len(oL1UserTxs))
291	assert.Equal(t, 4, len(oL1CoordTxs))
292	assert.Equal(t, 2, len(oL2Txs))
293	assert.Equal(t, len(oL1CoordTxs), len(accAuths))
294	assert.Equal(t, common.BatchNum(7), txsel.localAccountsDB.CurrentBatch())
295	assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
296	checkBalanceByIdx(t, txsel, 261, "20") // CoordIdx for TokenID=1
297	checkBalanceByIdx(t, txsel, 262, "10") // CoordIdx for TokenID=0
298	checkBalance(t, tc, txsel, "A", 0, "600")
299	checkBalance(t, tc, txsel, "A", 1, "280")
300	checkBalance(t, tc, txsel, "B", 0, "290")
301	checkBalance(t, tc, txsel, "B", 1, "200")
302	checkBalance(t, tc, txsel, "C", 0, "100")
303	checkBalance(t, tc, txsel, "D", 0, "800")
304	checkSortedByNonce(t, testAccNonces, oL2Txs)
305	err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs), txsel.localAccountsDB.CurrentBatch())
306	require.NoError(t, err)
307
308	log.Debug("block:0 batch:7")
309	// simulate the PoolL2Txs of the batch6
310	batchPoolL2 = `
311	Type: PoolL2
312	PoolTransfer(0) A-B: 100 (126)
313	PoolTransfer(0) C-A: 50 (126)
314	PoolTransfer(1) B-C: 100 (126)
315	PoolExit(0) A: 100 (126)`
316	poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
317	require.NoError(t, err)
318	addL2Txs(t, txsel, poolL2Txs)
319	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[7].Batch.ForgeL1TxsNum])
320	coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
321	require.NoError(t, err)
322	assert.Equal(t, []common.Idx{261, 262}, coordIdxs)
323	assert.Equal(t, 0, len(accAuths))
324	assert.Equal(t, 0, len(oL1UserTxs))
325	assert.Equal(t, 0, len(oL1CoordTxs))
326	assert.Equal(t, 4, len(oL2Txs))
327	assert.Equal(t, len(oL1CoordTxs), len(accAuths))
328	assert.Equal(t, common.BatchNum(8), txsel.localAccountsDB.CurrentBatch())
329	assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
330	checkBalanceByIdx(t, txsel, 261, "30")
331	checkBalanceByIdx(t, txsel, 262, "35")
332	checkBalance(t, tc, txsel, "A", 0, "430")
333	checkBalance(t, tc, txsel, "A", 1, "280")
334	checkBalance(t, tc, txsel, "B", 0, "390")
335	checkBalance(t, tc, txsel, "B", 1, "90")
336	checkBalance(t, tc, txsel, "C", 0, "45")
337	checkBalance(t, tc, txsel, "C", 1, "100")
338	checkBalance(t, tc, txsel, "D", 0, "800")
339	checkSortedByNonce(t, testAccNonces, oL2Txs)
340	err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs), txsel.localAccountsDB.CurrentBatch())
341	require.NoError(t, err)
342
343	log.Debug("block:1 batch:0")
344	// simulate the PoolL2Txs of the batch6
345	batchPoolL2 = `
346	Type: PoolL2
347	PoolTransfer(0) D-A: 300 (126)
348	PoolTransfer(0) B-D: 100 (126)
349	`
350	poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
351	require.NoError(t, err)
352	addL2Txs(t, txsel, poolL2Txs)
353	l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[1].Rollup.Batches[0].Batch.ForgeL1TxsNum])
354	coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, err = txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs)
355	require.NoError(t, err)
356	assert.Equal(t, []common.Idx{262}, coordIdxs)
357	assert.Equal(t, 0, len(accAuths))
358	assert.Equal(t, 4, len(oL1UserTxs))
359	assert.Equal(t, 0, len(oL1CoordTxs))
360	assert.Equal(t, 2, len(oL2Txs))
361	assert.Equal(t, len(oL1CoordTxs), len(accAuths))
362	assert.Equal(t, common.BatchNum(9), txsel.localAccountsDB.CurrentBatch())
363	assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
364	checkBalanceByIdx(t, txsel, 261, "30")
365	checkBalanceByIdx(t, txsel, 262, "75")
366	checkBalance(t, tc, txsel, "A", 0, "730")
367	checkBalance(t, tc, txsel, "A", 1, "280")
368	checkBalance(t, tc, txsel, "B", 0, "380")
369	checkBalance(t, tc, txsel, "B", 1, "90")
370	checkBalance(t, tc, txsel, "C", 0, "845")
371	checkBalance(t, tc, txsel, "C", 1, "100")
372	checkBalance(t, tc, txsel, "D", 0, "470")
373	checkSortedByNonce(t, testAccNonces, oL2Txs)
374	err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs), txsel.localAccountsDB.CurrentBatch())
375	require.NoError(t, err)
376}