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