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