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.GetAccounts()
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)
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	// fmt.Printf("DBG Blocks: %+v\n", blocks)
513
514	toForgeL1TxsNum := int64(1)
515
516	for i := range blocks {
517		err = historyDB.AddBlockSCData(&blocks[i])
518		require.Nil(t, err)
519	}
520
521	l1UserTxs, err := historyDB.GetL1UserTxs(toForgeL1TxsNum)
522	require.Nil(t, err)
523	assert.Equal(t, 5, len(l1UserTxs))
524	assert.Equal(t, blocks[0].Rollup.L1UserTxs, l1UserTxs)
525
526	// No l1UserTxs for this toForgeL1TxsNum
527	l1UserTxs, err = historyDB.GetL1UserTxs(2)
528	require.Nil(t, err)
529	assert.Equal(t, 0, len(l1UserTxs))
530}
531
532func TestSetInitialSCVars(t *testing.T) {
533	test.WipeDB(historyDB.DB())
534	_, _, _, err := historyDB.GetSCVars()
535	assert.Equal(t, sql.ErrNoRows, err)
536
537	//nolint:govet
538	rollup := &common.RollupVariables{
539		0,
540		big.NewInt(10),
541		12,
542		13,
543		[5]common.Bucket{},
544	}
545	//nolint:govet
546	auction := &common.AuctionVariables{
547		0,
548		ethCommon.BigToAddress(big.NewInt(2)),
549		ethCommon.BigToAddress(big.NewInt(3)),
550		[6]*big.Int{
551			big.NewInt(1), big.NewInt(2), big.NewInt(3),
552			big.NewInt(4), big.NewInt(5), big.NewInt(6),
553		},
554		2,
555		4320,
556		[3]uint16{10, 11, 12},
557		1000,
558		20,
559	}
560	//nolint:govet
561	wDelayer := &common.WDelayerVariables{
562		0,
563		ethCommon.BigToAddress(big.NewInt(2)),
564		ethCommon.BigToAddress(big.NewInt(3)),
565		ethCommon.BigToAddress(big.NewInt(4)),
566		13,
567		14,
568		false,
569	}
570	err = historyDB.SetInitialSCVars(rollup, auction, wDelayer)
571	require.Nil(t, err)
572	dbRollup, dbAuction, dbWDelayer, err := historyDB.GetSCVars()
573	assert.Nil(t, err)
574	require.Equal(t, rollup, dbRollup)
575	require.Equal(t, auction, dbAuction)
576	require.Equal(t, wDelayer, dbWDelayer)
577}
578
579func TestUpdateExitTree(t *testing.T) {
580	test.WipeDB(historyDB.DB())
581
582	set := `
583		Type: Blockchain
584
585		AddToken(1)
586
587		CreateAccountDeposit(1) C: 2000 // Idx=256+2=258
588		CreateAccountDeposit(1) D: 500  // Idx=256+3=259
589
590		CreateAccountCoordinator(1) A // Idx=256+0=256
591		CreateAccountCoordinator(1) B // Idx=256+1=257
592
593		> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{5}
594		> batchL1 // forge defined L1UserTxs{5}, freeze L1UserTxs{nil}
595		> block // blockNum=2
596
597		ForceExit(1) A: 100
598		ForceExit(1) B: 80
599
600		Exit(1) C: 50 (200)
601		Exit(1) D: 30 (200)
602
603		> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{3}
604		> batchL1 // forge L1UserTxs{3}, freeze defined L1UserTxs{nil}
605		> block // blockNum=3
606
607		> block // blockNum=4 (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 one
621	for i := range blocks[:len(blocks)-1] {
622		err = historyDB.AddBlockSCData(&blocks[i])
623		require.Nil(t, err)
624	}
625
626	// Add withdraws to the last block, and insert block into the DB
627	block := &blocks[len(blocks)-1]
628	require.Equal(t, int64(4), block.Block.EthBlockNum)
629	block.Rollup.Withdrawals = append(block.Rollup.Withdrawals,
630		common.WithdrawInfo{Idx: 256, NumExitRoot: 4, InstantWithdraw: true},
631		common.WithdrawInfo{Idx: 257, NumExitRoot: 4, InstantWithdraw: false},
632		common.WithdrawInfo{Idx: 258, NumExitRoot: 3, InstantWithdraw: true},
633		common.WithdrawInfo{Idx: 259, NumExitRoot: 3, InstantWithdraw: false},
634	)
635	err = historyDB.addBlock(historyDB.db, &block.Block)
636	require.Nil(t, err)
637
638	// update exit trees in DB
639	instantWithdrawn := []exitID{}
640	delayedWithdrawRequest := []exitID{}
641	for _, withdraw := range block.Rollup.Withdrawals {
642		exitID := exitID{
643			batchNum: int64(withdraw.NumExitRoot),
644			idx:      int64(withdraw.Idx),
645		}
646		if withdraw.InstantWithdraw {
647			instantWithdrawn = append(instantWithdrawn, exitID)
648		} else {
649			delayedWithdrawRequest = append(delayedWithdrawRequest, exitID)
650		}
651	}
652	err = historyDB.updateExitTree(historyDB.db, block.Block.EthBlockNum, instantWithdrawn, delayedWithdrawRequest)
653	require.Nil(t, err)
654
655	// Check that exits in DB match with the expected values
656	dbExits, err := historyDB.GetAllExits()
657	require.Nil(t, err)
658	assert.Equal(t, 4, len(dbExits))
659	dbExitsByIdx := make(map[common.Idx]common.ExitInfo)
660	for _, dbExit := range dbExits {
661		dbExitsByIdx[dbExit.AccountIdx] = dbExit
662	}
663	for _, withdraw := range block.Rollup.Withdrawals {
664		assert.Equal(t, withdraw.NumExitRoot, dbExitsByIdx[withdraw.Idx].BatchNum)
665		if withdraw.InstantWithdraw {
666			assert.Equal(t, &block.Block.EthBlockNum, dbExitsByIdx[withdraw.Idx].InstantWithdrawn)
667		} else {
668			assert.Equal(t, &block.Block.EthBlockNum, dbExitsByIdx[withdraw.Idx].DelayedWithdrawRequest)
669		}
670	}
671}
672
673// setTestBlocks WARNING: this will delete the blocks and recreate them
674func setTestBlocks(from, to int64) []common.Block {
675	test.WipeDB(historyDB.DB())
676	blocks := test.GenBlocks(from, to)
677	if err := historyDB.AddBlocks(blocks); err != nil {
678		panic(err)
679	}
680	return blocks
681}