arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/db/historydb/historydb_test.go
db/historydb/historydb_test.goRaw
  1package historydb
  2
  3import (
  4	"database/sql"
  5	"math"
  6	"math/big"
  7	"os"
  8	"testing"
  9	"time"
 10
 11	ethCommon "github.com/ethereum/go-ethereum/common"
 12	"github.com/hermeznetwork/hermez-node/common"
 13	dbUtils "github.com/hermeznetwork/hermez-node/db"
 14	"github.com/hermeznetwork/hermez-node/log"
 15	"github.com/hermeznetwork/hermez-node/test"
 16	"github.com/hermeznetwork/hermez-node/test/til"
 17	"github.com/stretchr/testify/assert"
 18	"github.com/stretchr/testify/require"
 19)
 20
 21var historyDB *HistoryDB
 22
 23// In order to run the test you need to run a Posgres DB with
 24// a database named "history" that is accessible by
 25// user: "hermez"
 26// pass: set it using the env var POSTGRES_PASS
 27// This can be achieved by running: POSTGRES_PASS=your_strong_pass && sudo docker run --rm --name hermez-db-test -p 5432:5432 -e POSTGRES_DB=history -e POSTGRES_USER=hermez -e POSTGRES_PASSWORD=$POSTGRES_PASS -d postgres && sleep 2s && sudo docker exec -it hermez-db-test psql -a history -U hermez -c "CREATE DATABASE l2;"
 28// After running the test you can stop the container by running: sudo docker kill hermez-db-test
 29// If you already did that for the L2DB you don't have to do it again
 30
 31func TestMain(m *testing.M) {
 32	// init DB
 33	pass := os.Getenv("POSTGRES_PASS")
 34	db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
 35	if err != nil {
 36		panic(err)
 37	}
 38	historyDB = NewHistoryDB(db)
 39	if err != nil {
 40		panic(err)
 41	}
 42	// Run tests
 43	result := m.Run()
 44	// Close DB
 45	if err := db.Close(); err != nil {
 46		log.Error("Error closing the history DB:", err)
 47	}
 48	os.Exit(result)
 49}
 50
 51func TestBlocks(t *testing.T) {
 52	var fromBlock, toBlock int64
 53	fromBlock = 0
 54	toBlock = 7
 55	// Reset DB
 56	test.WipeDB(historyDB.DB())
 57	// Generate blocks using til
 58	set1 := `
 59		Type: Blockchain
 60		// block 0 is stored as default in the DB
 61		// block 1 does not exist
 62		> block // blockNum=2
 63		> block // blockNum=3 
 64		> block // blockNum=4
 65		> block // blockNum=5
 66		> block // blockNum=6
 67	`
 68	tc := til.NewContext(1)
 69	blocks, err := tc.GenerateBlocks(set1)
 70	require.NoError(t, err)
 71	// Save timestamp of a block with UTC and change it without UTC
 72	timestamp := time.Now().Add(time.Second * 13)
 73	blocks[fromBlock].Block.Timestamp = timestamp
 74	// Insert blocks into DB
 75	for i := 0; i < len(blocks); i++ {
 76		err := historyDB.AddBlock(&blocks[i].Block)
 77		assert.NoError(t, err)
 78	}
 79	// Add block 0 to the generated blocks
 80	blocks = append(
 81		[]common.BlockData{common.BlockData{Block: test.Block0}}, //nolint:gofmt
 82		blocks...,
 83	)
 84	// Get all blocks from DB
 85	fetchedBlocks, err := historyDB.GetBlocks(fromBlock, toBlock)
 86	assert.Equal(t, len(blocks), len(fetchedBlocks))
 87	// Compare generated vs getted blocks
 88	assert.NoError(t, err)
 89	for i := range fetchedBlocks {
 90		assertEqualBlock(t, &blocks[i].Block, &fetchedBlocks[i])
 91	}
 92	// Compare saved timestamp vs getted
 93	nameZoneUTC, offsetUTC := timestamp.UTC().Zone()
 94	zoneFetchedBlock, offsetFetchedBlock := fetchedBlocks[fromBlock].Timestamp.Zone()
 95	assert.Equal(t, nameZoneUTC, zoneFetchedBlock)
 96	assert.Equal(t, offsetUTC, offsetFetchedBlock)
 97	// Get blocks from the DB one by one
 98	for i := int64(2); i < toBlock; i++ { // avoid block 0 for simplicity
 99		fetchedBlock, err := historyDB.GetBlock(i)
100		assert.NoError(t, err)
101		assertEqualBlock(t, &blocks[i-1].Block, fetchedBlock)
102	}
103	// Get last block
104	lastBlock, err := historyDB.GetLastBlock()
105	assert.NoError(t, err)
106	assertEqualBlock(t, &blocks[len(blocks)-1].Block, lastBlock)
107}
108
109func assertEqualBlock(t *testing.T, expected *common.Block, actual *common.Block) {
110	assert.Equal(t, expected.EthBlockNum, actual.EthBlockNum)
111	assert.Equal(t, expected.Hash, actual.Hash)
112	assert.Equal(t, expected.Timestamp.Unix(), actual.Timestamp.Unix())
113}
114
115func TestBatches(t *testing.T) {
116	// Reset DB
117	test.WipeDB(historyDB.DB())
118	// Generate batches using til (and blocks for foreign key)
119	set := `
120		Type: Blockchain
121		
122		AddToken(1) // Will have value in USD
123		AddToken(2) // Will NOT have value in USD
124		CreateAccountDeposit(1) A: 2000
125		CreateAccountDeposit(2) A: 2000
126		CreateAccountDeposit(1) B: 1000
127		CreateAccountDeposit(2) B: 1000
128		> batchL1 
129		> batchL1 
130		Transfer(1) A-B: 100 (5)
131		Transfer(2) B-A: 100 (199)
132		> batch   // batchNum=2, L2 only batch, forges transfers (mixed case of with(out) USD value)
133		> block
134		Transfer(1) A-B: 100 (5)
135		> batch   // batchNum=3, L2 only batch, forges transfer (with USD value)
136		Transfer(2) B-A: 100 (199)
137		> batch   // batchNum=4, L2 only batch, forges transfer (without USD value)
138		> block
139	`
140	tc := til.NewContext(common.RollupConstMaxL1UserTx)
141	tilCfgExtra := til.ConfigExtra{
142		BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
143		CoordUser:     "A",
144	}
145	blocks, err := tc.GenerateBlocks(set)
146	require.Nil(t, err)
147	err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
148	assert.Nil(t, err)
149	// Insert to DB
150	batches := []common.Batch{}
151	tokensValue := make(map[common.TokenID]float64)
152	lastL1TxsNum := new(int64)
153	for _, block := range blocks {
154		// Insert block
155		assert.NoError(t, historyDB.AddBlock(&block.Block))
156		// Insert tokens
157		for i, token := range block.Rollup.AddedTokens {
158			assert.NoError(t, historyDB.AddToken(&token)) //nolint:gosec
159			if i%2 != 0 {
160				// Set value to the token
161				value := (float64(i) + 5) * 5.389329
162				assert.NoError(t, historyDB.UpdateTokenValue(token.Symbol, value))
163				tokensValue[token.TokenID] = value / math.Pow(10, float64(token.Decimals))
164			}
165		}
166		// Combine all generated batches into single array
167		for _, batch := range block.Rollup.Batches {
168			batches = append(batches, batch.Batch)
169			forgeTxsNum := batch.Batch.ForgeL1TxsNum
170			if forgeTxsNum != nil && (lastL1TxsNum == nil || *lastL1TxsNum < *forgeTxsNum) {
171				*lastL1TxsNum = *forgeTxsNum
172			}
173		}
174	}
175	// Insert batches
176	assert.NoError(t, historyDB.AddBatches(batches))
177	// Set expected total fee
178	for _, batch := range batches {
179		total := .0
180		for tokenID, amount := range batch.CollectedFees {
181			af := new(big.Float).SetInt(amount)
182			amountFloat, _ := af.Float64()
183			total += tokensValue[tokenID] * amountFloat
184		}
185		batch.TotalFeesUSD = &total
186	}
187	// Get batches from the DB
188	fetchedBatches, err := historyDB.GetBatches(0, common.BatchNum(len(batches)+1))
189	assert.NoError(t, err)
190	assert.Equal(t, len(batches), len(fetchedBatches))
191	for i, fetchedBatch := range fetchedBatches {
192		assert.Equal(t, batches[i], fetchedBatch)
193	}
194	// Test GetLastBatchNum
195	fetchedLastBatchNum, err := historyDB.GetLastBatchNum()
196	assert.NoError(t, err)
197	assert.Equal(t, batches[len(batches)-1].BatchNum, fetchedLastBatchNum)
198	// Test GetLastL1TxsNum
199	fetchedLastL1TxsNum, err := historyDB.GetLastL1TxsNum()
200	assert.NoError(t, err)
201	assert.Equal(t, lastL1TxsNum, fetchedLastL1TxsNum)
202}
203
204func TestBids(t *testing.T) {
205	const fromBlock int64 = 1
206	const toBlock int64 = 5
207	// Prepare blocks in the DB
208	blocks := setTestBlocks(fromBlock, toBlock)
209	// Generate fake coordinators
210	const nCoords = 5
211	coords := test.GenCoordinators(nCoords, blocks)
212	err := historyDB.AddCoordinators(coords)
213	assert.NoError(t, err)
214	// Generate fake bids
215	const nBids = 20
216	bids := test.GenBids(nBids, blocks, coords)
217	err = historyDB.AddBids(bids)
218	assert.NoError(t, err)
219	// Fetch bids
220	fetchedBids, err := historyDB.GetAllBids()
221	assert.NoError(t, err)
222	// Compare fetched bids vs generated bids
223	for i, bid := range fetchedBids {
224		assert.Equal(t, bids[i], bid)
225	}
226}
227
228func TestTokens(t *testing.T) {
229	const fromBlock int64 = 1
230	const toBlock int64 = 5
231	// Prepare blocks in the DB
232	blocks := setTestBlocks(fromBlock, toBlock)
233	// Generate fake tokens
234	const nTokens = 5
235	tokens, ethToken := test.GenTokens(nTokens, blocks)
236	err := historyDB.AddTokens(tokens)
237	assert.NoError(t, err)
238	tokens = append([]common.Token{ethToken}, tokens...)
239	limit := uint(10)
240	// Fetch tokens
241	fetchedTokens, _, err := historyDB.GetTokens(nil, nil, "", nil, &limit, OrderAsc)
242	assert.NoError(t, err)
243	// Compare fetched tokens vs generated tokens
244	// All the tokens should have USDUpdate setted by the DB trigger
245	for i, token := range fetchedTokens {
246		assert.Equal(t, tokens[i].TokenID, token.TokenID)
247		assert.Equal(t, tokens[i].EthBlockNum, token.EthBlockNum)
248		assert.Equal(t, tokens[i].EthAddr, token.EthAddr)
249		assert.Equal(t, tokens[i].Name, token.Name)
250		assert.Equal(t, tokens[i].Symbol, token.Symbol)
251		assert.Nil(t, token.USD)
252		assert.Nil(t, token.USDUpdate)
253	}
254
255	// Update token value
256	for i, token := range tokens {
257		value := 1.01 * float64(i)
258		assert.NoError(t, historyDB.UpdateTokenValue(token.Symbol, value))
259	}
260	// Fetch tokens
261	fetchedTokens, _, err = historyDB.GetTokens(nil, nil, "", nil, &limit, OrderAsc)
262	assert.NoError(t, err)
263	// Compare fetched tokens vs generated tokens
264	// All the tokens should have USDUpdate setted by the DB trigger
265	for i, token := range fetchedTokens {
266		value := 1.01 * float64(i)
267		assert.Equal(t, value, *token.USD)
268		nameZone, offset := token.USDUpdate.Zone()
269		assert.Equal(t, "UTC", nameZone)
270		assert.Equal(t, 0, offset)
271	}
272}
273
274func TestAccounts(t *testing.T) {
275	const fromBlock int64 = 1
276	const toBlock int64 = 5
277	// Prepare blocks in the DB
278	blocks := setTestBlocks(fromBlock, toBlock)
279	// Generate fake tokens
280	const nTokens = 5
281	tokens, ethToken := test.GenTokens(nTokens, blocks)
282	err := historyDB.AddTokens(tokens)
283	assert.NoError(t, err)
284	tokens = append([]common.Token{ethToken}, tokens...)
285	// Generate fake batches
286	const nBatches = 10
287	batches := test.GenBatches(nBatches, blocks)
288	err = historyDB.AddBatches(batches)
289	assert.NoError(t, err)
290	// Generate fake accounts
291	const nAccounts = 3
292	accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
293	err = historyDB.AddAccounts(accs)
294	assert.NoError(t, err)
295	// Fetch accounts
296	fetchedAccs, err := historyDB.GetAllAccounts()
297	assert.NoError(t, err)
298	// Compare fetched accounts vs generated accounts
299	for i, acc := range fetchedAccs {
300		accs[i].Balance = nil
301		assert.Equal(t, accs[i], acc)
302	}
303}
304
305func TestTxs(t *testing.T) {
306	// Reset DB
307	test.WipeDB(historyDB.DB())
308	// TODO: Generate batches using til (and blocks for foreign key)
309	set := `
310		Type: Blockchain
311
312		// Things to test:
313		// One tx of each type
314		// batches that forge user L1s
315		// historic USD is not set if USDUpdate is too old (24h)
316	`
317	tc := til.NewContext(common.RollupConstMaxL1UserTx)
318	tilCfgExtra := til.ConfigExtra{
319		BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
320		CoordUser:     "A",
321	}
322	blocks, err := tc.GenerateBlocks(set)
323	require.Nil(t, err)
324	err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
325	assert.Nil(t, err)
326
327	/*
328
329		OLD TEST
330
331
332		const fromBlock int64 = 1
333		const toBlock int64 = 5
334		// Prepare blocks in the DB
335		blocks := setTestBlocks(fromBlock, toBlock)
336		// Generate fake tokens
337		const nTokens = 500
338		tokens, ethToken := test.GenTokens(nTokens, blocks)
339		err := historyDB.AddTokens(tokens)
340		assert.NoError(t, err)
341		tokens = append([]common.Token{ethToken}, tokens...)
342		// Generate fake batches
343		const nBatches = 10
344		batches := test.GenBatches(nBatches, blocks)
345		err = historyDB.AddBatches(batches)
346		assert.NoError(t, err)
347		// Generate fake accounts
348		const nAccounts = 3
349		accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
350		err = historyDB.AddAccounts(accs)
351		assert.NoError(t, err)
352
353			Uncomment once the transaction generation is fixed
354			!! test that batches that forge user L1s !!
355			!! Missing tests to check that  !!
356
357			// Generate fake L1 txs
358			const nL1s = 64
359			_, l1txs := test.GenL1Txs(256, nL1s, 0, nil, accs, tokens, blocks, batches)
360			err = historyDB.AddL1Txs(l1txs)
361			assert.NoError(t, err)
362			// Generate fake L2 txs
363			const nL2s = 2048 - nL1s
364			_, l2txs := test.GenL2Txs(256, nL2s, 0, nil, accs, tokens, blocks, batches)
365			err = historyDB.AddL2Txs(l2txs)
366			assert.NoError(t, err)
367			// Compare fetched txs vs generated txs.
368			fetchAndAssertTxs(t, l1txs, l2txs)
369			// Test trigger: L1 integrity
370			// from_eth_addr can't be null
371			l1txs[0].FromEthAddr = ethCommon.Address{}
372			err = historyDB.AddL1Txs(l1txs)
373			assert.Error(t, err)
374			l1txs[0].FromEthAddr = ethCommon.BigToAddress(big.NewInt(int64(5)))
375			// from_bjj can't be null
376			l1txs[0].FromBJJ = nil
377			err = historyDB.AddL1Txs(l1txs)
378			assert.Error(t, err)
379			privK := babyjub.NewRandPrivKey()
380			l1txs[0].FromBJJ = privK.Public()
381			// load_amount can't be null
382			l1txs[0].LoadAmount = nil
383			err = historyDB.AddL1Txs(l1txs)
384			assert.Error(t, err)
385			// Test trigger: L2 integrity
386			// batch_num can't be null
387			l2txs[0].BatchNum = 0
388			err = historyDB.AddL2Txs(l2txs)
389			assert.Error(t, err)
390			l2txs[0].BatchNum = 1
391			// nonce can't be null
392			l2txs[0].Nonce = 0
393			err = historyDB.AddL2Txs(l2txs)
394			assert.Error(t, err)
395			// Test trigger: forge L1 txs
396			// add next batch to DB
397			batchNum, toForgeL1TxsNum := test.GetNextToForgeNumAndBatch(batches)
398			batch := batches[0]
399			batch.BatchNum = batchNum
400			batch.ForgeL1TxsNum = toForgeL1TxsNum
401			assert.NoError(t, historyDB.AddBatch(&batch)) // This should update nL1s / 2 rows
402			// Set batch num in txs that should have been marked as forged in the DB
403			for i := 0; i < len(l1txs); i++ {
404				fetchedTx, err := historyDB.GetTx(l1txs[i].TxID)
405				assert.NoError(t, err)
406				if l1txs[i].ToForgeL1TxsNum == toForgeL1TxsNum {
407					assert.Equal(t, batchNum, *fetchedTx.BatchNum)
408				} else {
409					if fetchedTx.BatchNum != nil {
410						assert.NotEqual(t, batchNum, *fetchedTx.BatchNum)
411					}
412				}
413			}
414
415			// Test helper functions for Synchronizer
416			// GetLastTxsPosition
417			expectedPosition := -1
418			var choosenToForgeL1TxsNum int64 = -1
419			for _, tx := range l1txs {
420				if choosenToForgeL1TxsNum == -1 && tx.ToForgeL1TxsNum > 0 {
421					choosenToForgeL1TxsNum = tx.ToForgeL1TxsNum
422					expectedPosition = tx.Position
423				} else if choosenToForgeL1TxsNum == tx.ToForgeL1TxsNum && expectedPosition < tx.Position {
424					expectedPosition = tx.Position
425				}
426			}
427			position, err := historyDB.GetLastTxsPosition(choosenToForgeL1TxsNum)
428			assert.NoError(t, err)
429			assert.Equal(t, expectedPosition, position)
430
431			// GetL1UserTxs: not needed? tests were broken
432			// txs, err := historyDB.GetL1UserTxs(2)
433			// assert.NoError(t, err)
434			// assert.NotZero(t, len(txs))
435			// assert.NoError(t, err)
436			// assert.Equal(t, 22, position)
437			// // Test Update L1 TX Batch_num
438			// assert.Equal(t, common.BatchNum(0), txs[0].BatchNum)
439			// txs[0].BatchNum = common.BatchNum(1)
440			// txs, err = historyDB.GetL1UserTxs(2)
441			// assert.NoError(t, err)
442			// assert.NotZero(t, len(txs))
443			// assert.Equal(t, common.BatchNum(1), txs[0].BatchNum)
444	*/
445}
446
447/*
448func fetchAndAssertTxs(t *testing.T, l1txs []common.L1Tx, l2txs []common.L2Tx) {
449	for i := 0; i < len(l1txs); i++ {
450		tx := l1txs[i].Tx()
451		fmt.Println("ASDF", i, tx.TxID)
452		fetchedTx, err := historyDB.GetTx(tx.TxID)
453		require.NoError(t, err)
454		test.AssertUSD(t, tx.USD, fetchedTx.USD)
455		test.AssertUSD(t, tx.LoadAmountUSD, fetchedTx.LoadAmountUSD)
456		assert.Equal(t, tx, fetchedTx)
457	}
458	for i := 0; i < len(l2txs); i++ {
459		tx := l2txs[i].Tx()
460		fetchedTx, err := historyDB.GetTx(tx.TxID)
461		tx.TokenID = fetchedTx.TokenID
462		assert.NoError(t, err)
463		test.AssertUSD(t, fetchedTx.USD, tx.USD)
464		test.AssertUSD(t, fetchedTx.FeeUSD, tx.FeeUSD)
465		assert.Equal(t, tx, fetchedTx)
466	}
467}
468*/
469
470func TestExitTree(t *testing.T) {
471	nBatches := 17
472	blocks := setTestBlocks(1, 10)
473	batches := test.GenBatches(nBatches, blocks)
474	err := historyDB.AddBatches(batches)
475	assert.NoError(t, err)
476	const nTokens = 50
477	tokens, ethToken := test.GenTokens(nTokens, blocks)
478	err = historyDB.AddTokens(tokens)
479	assert.NoError(t, err)
480	tokens = append([]common.Token{ethToken}, tokens...)
481	const nAccounts = 3
482	accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
483	assert.NoError(t, historyDB.AddAccounts(accs))
484	exitTree := test.GenExitTree(nBatches, batches, accs, blocks)
485	err = historyDB.AddExitTree(exitTree)
486	assert.NoError(t, err)
487}
488
489func TestGetL1UserTxs(t *testing.T) {
490	test.WipeDB(historyDB.DB())
491
492	set := `
493		Type: Blockchain
494		AddToken(1)
495		AddToken(2)
496		AddToken(3)
497
498		CreateAccountDeposit(1) A: 20
499		CreateAccountDeposit(2) A: 20
500		CreateAccountDeposit(1) B: 5
501		CreateAccountDeposit(1) C: 5
502		CreateAccountDeposit(1) D: 5
503
504		> block
505	`
506	tc := til.NewContext(128)
507	blocks, err := tc.GenerateBlocks(set)
508	require.Nil(t, err)
509	// Sanity check
510	require.Equal(t, 1, len(blocks))
511	require.Equal(t, 5, len(blocks[0].Rollup.L1UserTxs))
512
513	toForgeL1TxsNum := int64(1)
514
515	for i := range blocks {
516		err = historyDB.AddBlockSCData(&blocks[i])
517		require.Nil(t, err)
518	}
519
520	l1UserTxs, err := historyDB.GetL1UserTxs(toForgeL1TxsNum)
521	require.Nil(t, err)
522	assert.Equal(t, 5, len(l1UserTxs))
523	assert.Equal(t, blocks[0].Rollup.L1UserTxs, l1UserTxs)
524
525	// No l1UserTxs for this toForgeL1TxsNum
526	l1UserTxs, err = historyDB.GetL1UserTxs(2)
527	require.Nil(t, err)
528	assert.Equal(t, 0, len(l1UserTxs))
529}
530
531func TestSetInitialSCVars(t *testing.T) {
532	test.WipeDB(historyDB.DB())
533	_, _, _, err := historyDB.GetSCVars()
534	assert.Equal(t, sql.ErrNoRows, err)
535
536	//nolint:govet
537	rollup := &common.RollupVariables{
538		0,
539		big.NewInt(10),
540		12,
541		13,
542		[5]common.Bucket{},
543	}
544	//nolint:govet
545	auction := &common.AuctionVariables{
546		0,
547		ethCommon.BigToAddress(big.NewInt(2)),
548		ethCommon.BigToAddress(big.NewInt(3)),
549		[6]*big.Int{
550			big.NewInt(1), big.NewInt(2), big.NewInt(3),
551			big.NewInt(4), big.NewInt(5), big.NewInt(6),
552		},
553		2,
554		4320,
555		[3]uint16{10, 11, 12},
556		1000,
557		20,
558	}
559	//nolint:govet
560	wDelayer := &common.WDelayerVariables{
561		0,
562		ethCommon.BigToAddress(big.NewInt(2)),
563		ethCommon.BigToAddress(big.NewInt(3)),
564		ethCommon.BigToAddress(big.NewInt(4)),
565		13,
566		14,
567		false,
568	}
569	err = historyDB.SetInitialSCVars(rollup, auction, wDelayer)
570	require.Nil(t, err)
571	dbRollup, dbAuction, dbWDelayer, err := historyDB.GetSCVars()
572	assert.Nil(t, err)
573	require.Equal(t, rollup, dbRollup)
574	require.Equal(t, auction, dbAuction)
575	require.Equal(t, wDelayer, dbWDelayer)
576}
577
578func TestUpdateExitTree(t *testing.T) {
579	test.WipeDB(historyDB.DB())
580
581	set := `
582		Type: Blockchain
583
584		AddToken(1)
585
586		CreateAccountDeposit(1) C: 2000 // Idx=256+2=258
587		CreateAccountDeposit(1) D: 500  // Idx=256+3=259
588
589		CreateAccountCoordinator(1) A // Idx=256+0=256
590		CreateAccountCoordinator(1) B // Idx=256+1=257
591
592		> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{5}
593		> batchL1 // forge defined L1UserTxs{5}, freeze L1UserTxs{nil}
594		> block // blockNum=2
595
596		ForceExit(1) A: 100
597		ForceExit(1) B: 80
598
599		Exit(1) C: 50 (172)
600		Exit(1) D: 30 (172)
601
602		> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{3}
603		> batchL1 // forge L1UserTxs{3}, freeze defined L1UserTxs{nil}
604		> block // blockNum=3
605
606		> block // blockNum=4 (empty block)
607		> block // blockNum=5 (empty block)
608	`
609
610	tc := til.NewContext(common.RollupConstMaxL1UserTx)
611	tilCfgExtra := til.ConfigExtra{
612		BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
613		CoordUser:     "A",
614	}
615	blocks, err := tc.GenerateBlocks(set)
616	require.Nil(t, err)
617	err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
618	assert.Nil(t, err)
619
620	// Add all blocks except for the last two
621	for i := range blocks[:len(blocks)-2] {
622		err = historyDB.AddBlockSCData(&blocks[i])
623		require.Nil(t, err)
624	}
625
626	// Add withdraws to the second-to-last block, and insert block into the DB
627	block := &blocks[len(blocks)-2]
628	require.Equal(t, int64(4), block.Block.EthBlockNum)
629	tokenAddr := blocks[0].Rollup.AddedTokens[0].EthAddr
630	// block.WDelayer.Deposits = append(block.WDelayer.Deposits,
631	// 	common.WDelayerTransfer{Owner: tc.UsersByIdx[257].Addr, Token: tokenAddr, Amount: big.NewInt(80)}, // 257
632	// 	common.WDelayerTransfer{Owner: tc.UsersByIdx[259].Addr, Token: tokenAddr, Amount: big.NewInt(15)}, // 259
633	// )
634	block.Rollup.Withdrawals = append(block.Rollup.Withdrawals,
635		common.WithdrawInfo{Idx: 256, NumExitRoot: 4, InstantWithdraw: true},
636		common.WithdrawInfo{Idx: 257, NumExitRoot: 4, InstantWithdraw: false,
637			Owner: tc.UsersByIdx[257].Addr, Token: tokenAddr},
638		common.WithdrawInfo{Idx: 258, NumExitRoot: 3, InstantWithdraw: true},
639		common.WithdrawInfo{Idx: 259, NumExitRoot: 3, InstantWithdraw: false,
640			Owner: tc.UsersByIdx[259].Addr, Token: tokenAddr},
641	)
642	err = historyDB.addBlock(historyDB.db, &block.Block)
643	require.Nil(t, err)
644
645	err = historyDB.updateExitTree(historyDB.db, block.Block.EthBlockNum,
646		block.Rollup.Withdrawals, block.WDelayer.Withdrawals)
647	require.Nil(t, err)
648
649	// Check that exits in DB match with the expected values
650	dbExits, err := historyDB.GetAllExits()
651	require.Nil(t, err)
652	assert.Equal(t, 4, len(dbExits))
653	dbExitsByIdx := make(map[common.Idx]common.ExitInfo)
654	for _, dbExit := range dbExits {
655		dbExitsByIdx[dbExit.AccountIdx] = dbExit
656	}
657	for _, withdraw := range block.Rollup.Withdrawals {
658		assert.Equal(t, withdraw.NumExitRoot, dbExitsByIdx[withdraw.Idx].BatchNum)
659		if withdraw.InstantWithdraw {
660			assert.Equal(t, &block.Block.EthBlockNum, dbExitsByIdx[withdraw.Idx].InstantWithdrawn)
661		} else {
662			assert.Equal(t, &block.Block.EthBlockNum, dbExitsByIdx[withdraw.Idx].DelayedWithdrawRequest)
663		}
664	}
665
666	// Add delayed withdraw to the last block, and insert block into the DB
667	block = &blocks[len(blocks)-1]
668	require.Equal(t, int64(5), block.Block.EthBlockNum)
669	block.WDelayer.Withdrawals = append(block.WDelayer.Withdrawals,
670		common.WDelayerTransfer{
671			Owner:  tc.UsersByIdx[257].Addr,
672			Token:  tokenAddr,
673			Amount: big.NewInt(80),
674		})
675	err = historyDB.addBlock(historyDB.db, &block.Block)
676	require.Nil(t, err)
677
678	err = historyDB.updateExitTree(historyDB.db, block.Block.EthBlockNum,
679		block.Rollup.Withdrawals, block.WDelayer.Withdrawals)
680	require.Nil(t, err)
681
682	// Check that delayed withdrawn has been set
683	dbExits, err = historyDB.GetAllExits()
684	require.Nil(t, err)
685	for _, dbExit := range dbExits {
686		dbExitsByIdx[dbExit.AccountIdx] = dbExit
687	}
688	require.Equal(t, &block.Block.EthBlockNum, dbExitsByIdx[257].DelayedWithdrawn)
689}
690
691// setTestBlocks WARNING: this will delete the blocks and recreate them
692func setTestBlocks(from, to int64) []common.Block {
693	test.WipeDB(historyDB.DB())
694	blocks := test.GenBlocks(from, to)
695	if err := historyDB.AddBlocks(blocks); err != nil {
696		panic(err)
697	}
698	return blocks
699}