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