arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/synchronizer/synchronizer_test.go
synchronizer/synchronizer_test.goRaw
  1package synchronizer
  2
  3import (
  4	"context"
  5	"encoding/json"
  6	"fmt"
  7	"io/ioutil"
  8	"math/big"
  9	"os"
 10	"sort"
 11	"testing"
 12	"time"
 13
 14	ethCommon "github.com/ethereum/go-ethereum/common"
 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/statedb"
 19	"github.com/hermeznetwork/hermez-node/eth"
 20	"github.com/hermeznetwork/hermez-node/test"
 21	"github.com/hermeznetwork/hermez-node/test/til"
 22	"github.com/jinzhu/copier"
 23	"github.com/stretchr/testify/assert"
 24	"github.com/stretchr/testify/require"
 25)
 26
 27var tokenConsts = map[common.TokenID]eth.ERC20Consts{}
 28
 29type timer struct {
 30	time int64
 31}
 32
 33func (t *timer) Time() int64 {
 34	currentTime := t.time
 35	t.time++
 36	return currentTime
 37}
 38
 39func accountsCmp(accounts []common.Account) func(i, j int) bool {
 40	return func(i, j int) bool { return accounts[i].Idx < accounts[j].Idx }
 41}
 42
 43// Check Sync output and HistoryDB state against expected values generated by
 44// til
 45func checkSyncBlock(t *testing.T, s *Synchronizer, blockNum int, block,
 46	syncBlock *common.BlockData) {
 47	// Check Blocks
 48	dbBlocks, err := s.historyDB.GetAllBlocks()
 49	require.NoError(t, err)
 50	dbBlocks = dbBlocks[1:] // ignore block 0, added by default in the DB
 51	assert.Equal(t, blockNum, len(dbBlocks))
 52	assert.Equal(t, int64(blockNum), dbBlocks[blockNum-1].Num)
 53	assert.NotEqual(t, dbBlocks[blockNum-1].Hash, dbBlocks[blockNum-2].Hash)
 54	assert.Greater(t, dbBlocks[blockNum-1].Timestamp.Unix(), dbBlocks[blockNum-2].Timestamp.Unix())
 55
 56	// Check Tokens
 57	assert.Equal(t, len(block.Rollup.AddedTokens), len(syncBlock.Rollup.AddedTokens))
 58	dbTokens, err := s.historyDB.GetAllTokens()
 59	require.NoError(t, err)
 60	dbTokens = dbTokens[1:] // ignore token 0, added by default in the DB
 61	for i, token := range block.Rollup.AddedTokens {
 62		dbToken := dbTokens[i]
 63		syncToken := syncBlock.Rollup.AddedTokens[i]
 64
 65		assert.Equal(t, block.Block.Num, syncToken.EthBlockNum)
 66		assert.Equal(t, token.TokenID, syncToken.TokenID)
 67		assert.Equal(t, token.EthAddr, syncToken.EthAddr)
 68		tokenConst := tokenConsts[token.TokenID]
 69		assert.Equal(t, tokenConst.Name, syncToken.Name)
 70		assert.Equal(t, tokenConst.Symbol, syncToken.Symbol)
 71		assert.Equal(t, tokenConst.Decimals, syncToken.Decimals)
 72
 73		var tokenCpy historydb.TokenWithUSD
 74		//nolint:gosec
 75		require.Nil(t, copier.Copy(&tokenCpy, &token))      // copy common.Token to historydb.TokenWithUSD
 76		require.Nil(t, copier.Copy(&tokenCpy, &tokenConst)) // copy common.Token to historydb.TokenWithUSD
 77		tokenCpy.ItemID = dbToken.ItemID                    // we don't care about ItemID
 78		assert.Equal(t, tokenCpy, dbToken)
 79	}
 80
 81	// Check submitted L1UserTxs
 82	assert.Equal(t, len(block.Rollup.L1UserTxs), len(syncBlock.Rollup.L1UserTxs))
 83	dbL1UserTxs, err := s.historyDB.GetAllL1UserTxs()
 84	require.NoError(t, err)
 85	// Ignore BatchNum in syncBlock.L1UserTxs because this value is set by
 86	// the HistoryDB. Also ignore EffectiveAmount & EffectiveDepositAmount
 87	// because this value is set by StateDB.ProcessTxs.
 88	for i := range syncBlock.Rollup.L1UserTxs {
 89		syncBlock.Rollup.L1UserTxs[i].BatchNum = block.Rollup.L1UserTxs[i].BatchNum
 90		assert.Nil(t, syncBlock.Rollup.L1UserTxs[i].EffectiveDepositAmount)
 91		assert.Nil(t, syncBlock.Rollup.L1UserTxs[i].EffectiveAmount)
 92	}
 93	assert.Equal(t, block.Rollup.L1UserTxs, syncBlock.Rollup.L1UserTxs)
 94	for _, tx := range block.Rollup.L1UserTxs {
 95		var dbTx *common.L1Tx
 96		// Find tx in DB output
 97		for _, _dbTx := range dbL1UserTxs {
 98			if *tx.ToForgeL1TxsNum == *_dbTx.ToForgeL1TxsNum &&
 99				tx.Position == _dbTx.Position {
100				dbTx = new(common.L1Tx)
101				*dbTx = _dbTx
102				// NOTE: Overwrite EffectiveFromIdx in L1UserTx
103				// from db because we don't expect
104				// EffectiveFromIdx to be set yet, as this tx
105				// is not in yet forged
106				dbTx.EffectiveFromIdx = 0
107				break
108			}
109		}
110		// If the tx has been forged in this block, this will be
111		// reflected in the DB, and so the Effective values will be
112		// already set
113		if dbTx.BatchNum != nil {
114			tx.EffectiveAmount = tx.Amount
115			tx.EffectiveDepositAmount = tx.DepositAmount
116		}
117		assert.Equal(t, &tx, dbTx) //nolint:gosec
118	}
119
120	// Check Batches
121	assert.Equal(t, len(block.Rollup.Batches), len(syncBlock.Rollup.Batches))
122	dbBatches, err := s.historyDB.GetAllBatches()
123	require.NoError(t, err)
124
125	dbL1CoordinatorTxs, err := s.historyDB.GetAllL1CoordinatorTxs()
126	require.NoError(t, err)
127	dbL2Txs, err := s.historyDB.GetAllL2Txs()
128	require.NoError(t, err)
129	dbExits, err := s.historyDB.GetAllExits()
130	require.NoError(t, err)
131	// dbL1CoordinatorTxs := []common.L1Tx{}
132	for i, batch := range block.Rollup.Batches {
133		var dbBatch *common.Batch
134		// Find batch in DB output
135		for _, _dbBatch := range dbBatches {
136			if batch.Batch.BatchNum == _dbBatch.BatchNum {
137				dbBatch = new(common.Batch)
138				*dbBatch = _dbBatch
139				break
140			}
141		}
142		syncBatch := syncBlock.Rollup.Batches[i]
143
144		// We don't care about TotalFeesUSD.  Use the syncBatch that
145		// has a TotalFeesUSD inserted by the HistoryDB
146		batch.Batch.TotalFeesUSD = syncBatch.Batch.TotalFeesUSD
147		assert.Equal(t, batch.CreatedAccounts, syncBatch.CreatedAccounts)
148		batch.Batch.NumAccounts = len(batch.CreatedAccounts)
149
150		// Test field by field to facilitate debugging of errors
151		assert.Equal(t, len(batch.L1UserTxs), len(syncBatch.L1UserTxs))
152		// NOTE: EffectiveFromIdx is set to til L1UserTxs in
153		// `FillBlocksForgedL1UserTxs` function
154		for j := range syncBatch.L1UserTxs {
155			assert.NotEqual(t, 0, syncBatch.L1UserTxs[j].EffectiveFromIdx)
156		}
157		assert.Equal(t, batch.L1UserTxs, syncBatch.L1UserTxs)
158		// NOTE: EffectiveFromIdx is set to til L1CoordinatorTxs in
159		// `FillBlocksExtra` function
160		for j := range syncBatch.L1CoordinatorTxs {
161			assert.NotEqual(t, 0, syncBatch.L1CoordinatorTxs[j].EffectiveFromIdx)
162		}
163		assert.Equal(t, batch.L1CoordinatorTxs, syncBatch.L1CoordinatorTxs)
164		assert.Equal(t, batch.L2Txs, syncBatch.L2Txs)
165		// In exit tree, we only check AccountIdx and Balance, because
166		// it's what we have precomputed before.
167		require.Equal(t, len(batch.ExitTree), len(syncBatch.ExitTree))
168		for j := range batch.ExitTree {
169			exit := &batch.ExitTree[j]
170			assert.Equal(t, exit.AccountIdx, syncBatch.ExitTree[j].AccountIdx)
171			assert.Equal(t, exit.Balance, syncBatch.ExitTree[j].Balance)
172			*exit = syncBatch.ExitTree[j]
173		}
174		assert.Equal(t, batch.Batch, syncBatch.Batch)
175		// Ignore updated accounts
176		syncBatch.UpdatedAccounts = nil
177		assert.Equal(t, batch, syncBatch)
178		assert.Equal(t, &batch.Batch, dbBatch) //nolint:gosec
179
180		// Check forged L1UserTxs from DB, and check effective values
181		// in sync output
182		for j, tx := range batch.L1UserTxs {
183			var dbTx *common.L1Tx
184			// Find tx in DB output
185			for _, _dbTx := range dbL1UserTxs {
186				if *tx.BatchNum == *_dbTx.BatchNum &&
187					tx.Position == _dbTx.Position {
188					dbTx = new(common.L1Tx)
189					*dbTx = _dbTx
190					break
191				}
192			}
193			assert.Equal(t, &tx, dbTx) //nolint:gosec
194
195			syncTx := &syncBlock.Rollup.Batches[i].L1UserTxs[j]
196			assert.Equal(t, syncTx.DepositAmount, syncTx.EffectiveDepositAmount)
197			assert.Equal(t, syncTx.Amount, syncTx.EffectiveAmount)
198		}
199
200		// Check L1CoordinatorTxs from DB
201		for _, tx := range batch.L1CoordinatorTxs {
202			var dbTx *common.L1Tx
203			// Find tx in DB output
204			for _, _dbTx := range dbL1CoordinatorTxs {
205				if *tx.BatchNum == *_dbTx.BatchNum &&
206					tx.Position == _dbTx.Position {
207					dbTx = new(common.L1Tx)
208					*dbTx = _dbTx
209					break
210				}
211			}
212			assert.Equal(t, &tx, dbTx) //nolint:gosec
213		}
214
215		// Check L2Txs from DB
216		for _, tx := range batch.L2Txs {
217			var dbTx *common.L2Tx
218			// Find tx in DB output
219			for _, _dbTx := range dbL2Txs {
220				if tx.BatchNum == _dbTx.BatchNum &&
221					tx.Position == _dbTx.Position {
222					dbTx = new(common.L2Tx)
223					*dbTx = _dbTx
224					break
225				}
226			}
227			assert.Equal(t, &tx, dbTx) //nolint:gosec
228		}
229
230		// Check Exits from DB
231		for _, exit := range batch.ExitTree {
232			var dbExit *common.ExitInfo
233			// Find exit in DB output
234			for _, _dbExit := range dbExits {
235				if exit.BatchNum == _dbExit.BatchNum &&
236					exit.AccountIdx == _dbExit.AccountIdx {
237					dbExit = new(common.ExitInfo)
238					*dbExit = _dbExit
239					break
240				}
241			}
242			// Compare MerkleProof in JSON because unmarshaled 0
243			// big.Int leaves the internal big.Int array at nil,
244			// and gives trouble when comparing big.Int with
245			// internal big.Int array != nil but empty.
246			mtp, err := json.Marshal(exit.MerkleProof)
247			require.NoError(t, err)
248			dbMtp, err := json.Marshal(dbExit.MerkleProof)
249			require.NoError(t, err)
250			assert.Equal(t, mtp, dbMtp)
251			dbExit.MerkleProof = exit.MerkleProof
252			assert.Equal(t, &exit, dbExit) //nolint:gosec
253		}
254	}
255
256	// Compare accounts from HistoryDB with StateDB (they should match)
257	dbAccounts, err := s.historyDB.GetAllAccounts()
258	require.NoError(t, err)
259	sdbAccounts, err := s.stateDB.TestGetAccounts()
260	require.NoError(t, err)
261	assertEqualAccountsHistoryDBStateDB(t, dbAccounts, sdbAccounts)
262}
263
264func assertEqualAccountsHistoryDBStateDB(t *testing.T, hdbAccs, sdbAccs []common.Account) {
265	assert.Equal(t, len(hdbAccs), len(sdbAccs))
266	sort.SliceStable(hdbAccs, accountsCmp(hdbAccs))
267	sort.SliceStable(sdbAccs, accountsCmp(sdbAccs))
268	for i := range hdbAccs {
269		hdbAcc := hdbAccs[i]
270		sdbAcc := sdbAccs[i]
271		assert.Equal(t, hdbAcc.Idx, sdbAcc.Idx)
272		assert.Equal(t, hdbAcc.TokenID, sdbAcc.TokenID)
273		assert.Equal(t, hdbAcc.EthAddr, sdbAcc.EthAddr)
274		assert.Equal(t, hdbAcc.BJJ, sdbAcc.BJJ)
275	}
276}
277
278// ethAddTokens adds the tokens from the blocks to the blockchain
279func ethAddTokens(blocks []common.BlockData, client *test.Client) {
280	for _, block := range blocks {
281		for _, token := range block.Rollup.AddedTokens {
282			consts := eth.ERC20Consts{
283				Name:     fmt.Sprintf("Token %d", token.TokenID),
284				Symbol:   fmt.Sprintf("TK%d", token.TokenID),
285				Decimals: 18,
286			}
287			tokenConsts[token.TokenID] = consts
288			client.CtlAddERC20(token.EthAddr, consts)
289		}
290	}
291}
292
293var chainID uint16 = 0
294var deleteme = []string{}
295
296func TestMain(m *testing.M) {
297	exitVal := m.Run()
298	for _, dir := range deleteme {
299		if err := os.RemoveAll(dir); err != nil {
300			panic(err)
301		}
302	}
303	os.Exit(exitVal)
304}
305
306func newTestModules(t *testing.T) (*statedb.StateDB, *historydb.HistoryDB) {
307	// Int State DB
308	dir, err := ioutil.TempDir("", "tmpdb")
309	require.NoError(t, err)
310	deleteme = append(deleteme, dir)
311
312	stateDB, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
313		Type: statedb.TypeSynchronizer, NLevels: 32})
314	require.NoError(t, err)
315
316	// Init History DB
317	pass := os.Getenv("POSTGRES_PASS")
318	db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
319	require.NoError(t, err)
320	historyDB := historydb.NewHistoryDB(db, db, nil)
321	// Clear DB
322	test.WipeDB(historyDB.DB())
323
324	return stateDB, historyDB
325}
326
327func newBigInt(s string) *big.Int {
328	v, ok := new(big.Int).SetString(s, 10)
329	if !ok {
330		panic(fmt.Errorf("Can't set big.Int from %s", s))
331	}
332	return v
333}
334
335func TestSyncGeneral(t *testing.T) {
336	//
337	// Setup
338	//
339
340	stateDB, historyDB := newTestModules(t)
341
342	// Init eth client
343	var timer timer
344	clientSetup := test.NewClientSetupExample()
345	clientSetup.ChainID = big.NewInt(int64(chainID))
346	bootCoordAddr := clientSetup.AuctionVariables.BootCoordinator
347	client := test.NewClient(true, &timer, &ethCommon.Address{}, clientSetup)
348
349	// Create Synchronizer
350	s, err := NewSynchronizer(client, historyDB, stateDB, Config{
351		StatsRefreshPeriod: 0 * time.Second,
352	})
353	require.NoError(t, err)
354
355	ctx := context.Background()
356
357	//
358	// First Sync from an initial state
359	//
360	stats := s.Stats()
361	assert.Equal(t, false, stats.Synced())
362
363	// Test Sync for rollup genesis block
364	syncBlock, discards, err := s.Sync(ctx, nil)
365	require.NoError(t, err)
366	require.Nil(t, discards)
367	require.NotNil(t, syncBlock)
368	require.Nil(t, syncBlock.Rollup.Vars)
369	require.Nil(t, syncBlock.Auction.Vars)
370	require.Nil(t, syncBlock.WDelayer.Vars)
371	assert.Equal(t, int64(1), syncBlock.Block.Num)
372	stats = s.Stats()
373	assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
374	assert.Equal(t, int64(1), stats.Eth.LastBlock.Num)
375	assert.Equal(t, int64(1), stats.Sync.LastBlock.Num)
376	vars := s.SCVars()
377	assert.Equal(t, clientSetup.RollupVariables, vars.Rollup)
378	assert.Equal(t, clientSetup.AuctionVariables, vars.Auction)
379	assert.Equal(t, clientSetup.WDelayerVariables, vars.WDelayer)
380
381	dbBlocks, err := s.historyDB.GetAllBlocks()
382	require.NoError(t, err)
383	assert.Equal(t, 2, len(dbBlocks))
384	assert.Equal(t, int64(1), dbBlocks[1].Num)
385
386	// Sync again and expect no new blocks
387	syncBlock, discards, err = s.Sync(ctx, nil)
388	require.NoError(t, err)
389	require.Nil(t, discards)
390	require.Nil(t, syncBlock)
391
392	//
393	// Generate blockchain and smart contract data, and fill the test smart contracts
394	//
395
396	// Generate blockchain data with til
397	set1 := `
398		Type: Blockchain
399
400		AddToken(1)
401		AddToken(2)
402		AddToken(3)
403
404		CreateAccountDeposit(1) C: 2000 // Idx=256+2=258
405		CreateAccountDeposit(2) A: 2000 // Idx=256+3=259
406		CreateAccountDeposit(1) D: 500  // Idx=256+4=260
407		CreateAccountDeposit(2) B: 500  // Idx=256+5=261
408		CreateAccountDeposit(2) C: 500  // Idx=256+6=262
409
410		CreateAccountCoordinator(1) A // Idx=256+0=256
411		CreateAccountCoordinator(1) B // Idx=256+1=257
412
413		> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{5}
414		> batchL1 // forge defined L1UserTxs{5}, freeze L1UserTxs{nil}
415		> block // blockNum=2
416
417		CreateAccountDepositTransfer(1) E-A: 1000, 200 // Idx=256+7=263
418		ForceTransfer(1) C-B: 80
419		ForceExit(1) A: 100
420		ForceExit(1) B: 80
421		ForceTransfer(1) A-D: 100
422
423		Transfer(1) C-A: 100 (126)
424		Exit(1) C: 50 (100)
425		Exit(1) D: 30 (100)
426
427		> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{3}
428		> batchL1 // forge L1UserTxs{3}, freeze defined L1UserTxs{nil}
429		> block // blockNum=3
430	`
431	tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
432	tilCfgExtra := til.ConfigExtra{
433		BootCoordAddr: bootCoordAddr,
434		CoordUser:     "A",
435	}
436	blocks, err := tc.GenerateBlocks(set1)
437	require.NoError(t, err)
438	// Sanity check
439	require.Equal(t, 2, len(blocks))
440	// blocks 0 (blockNum=2)
441	i := 0
442	require.Equal(t, 2, int(blocks[i].Block.Num))
443	require.Equal(t, 3, len(blocks[i].Rollup.AddedTokens))
444	require.Equal(t, 5, len(blocks[i].Rollup.L1UserTxs))
445	require.Equal(t, 2, len(blocks[i].Rollup.Batches))
446	require.Equal(t, 2, len(blocks[i].Rollup.Batches[0].L1CoordinatorTxs))
447	// Set StateRoots for batches manually (til doesn't set it)
448	blocks[i].Rollup.Batches[0].Batch.StateRoot =
449		newBigInt("18906357591508007884273218035694076596537737437965299189312069102730480717391")
450	blocks[i].Rollup.Batches[1].Batch.StateRoot =
451		newBigInt("9513185123401321669660637227182204000277156839501731093239187625486561933297")
452	// blocks 1 (blockNum=3)
453	i = 1
454	require.Equal(t, 3, int(blocks[i].Block.Num))
455	require.Equal(t, 5, len(blocks[i].Rollup.L1UserTxs))
456	require.Equal(t, 2, len(blocks[i].Rollup.Batches))
457	require.Equal(t, 3, len(blocks[i].Rollup.Batches[0].L2Txs))
458	// Set StateRoots for batches manually (til doesn't set it)
459	blocks[i].Rollup.Batches[0].Batch.StateRoot =
460		newBigInt("13060270878200012606074130020925677466793317216609491464427188889005039616594")
461	blocks[i].Rollup.Batches[1].Batch.StateRoot =
462		newBigInt("21427104994652624302859637783375978708867165042357535792408500519060088086054")
463
464	// Generate extra required data
465	ethAddTokens(blocks, client)
466
467	err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
468	require.NoError(t, err)
469	tc.FillBlocksL1UserTxsBatchNum(blocks)
470	err = tc.FillBlocksForgedL1UserTxs(blocks)
471	require.NoError(t, err)
472
473	// Add block data to the smart contracts
474	err = client.CtlAddBlocks(blocks)
475	require.NoError(t, err)
476
477	//
478	// Sync to synchronize the current state from the test smart contracts,
479	// and check the outcome
480	//
481
482	// Block 2
483
484	syncBlock, discards, err = s.Sync(ctx, nil)
485	require.NoError(t, err)
486	require.Nil(t, discards)
487	require.NotNil(t, syncBlock)
488	assert.Nil(t, syncBlock.Rollup.Vars)
489	assert.Nil(t, syncBlock.Auction.Vars)
490	assert.Nil(t, syncBlock.WDelayer.Vars)
491	assert.Equal(t, int64(2), syncBlock.Block.Num)
492	stats = s.Stats()
493	assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
494	assert.Equal(t, int64(3), stats.Eth.LastBlock.Num)
495	assert.Equal(t, int64(2), stats.Sync.LastBlock.Num)
496
497	checkSyncBlock(t, s, 2, &blocks[0], syncBlock)
498
499	// Block 3
500
501	syncBlock, discards, err = s.Sync(ctx, nil)
502	assert.NoError(t, err)
503	require.NoError(t, err)
504	require.Nil(t, discards)
505	require.NotNil(t, syncBlock)
506	assert.Nil(t, syncBlock.Rollup.Vars)
507	assert.Nil(t, syncBlock.Auction.Vars)
508	assert.Nil(t, syncBlock.WDelayer.Vars)
509	assert.Equal(t, int64(3), syncBlock.Block.Num)
510	stats = s.Stats()
511	assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
512	assert.Equal(t, int64(3), stats.Eth.LastBlock.Num)
513	assert.Equal(t, int64(3), stats.Sync.LastBlock.Num)
514
515	checkSyncBlock(t, s, 3, &blocks[1], syncBlock)
516
517	// Block 4
518	// Generate 2 withdraws manually
519	_, err = client.RollupWithdrawMerkleProof(tc.Users["A"].BJJ.Public().Compress(), 1, 4, 256,
520		big.NewInt(100), []*big.Int{}, true)
521	require.NoError(t, err)
522	_, err = client.RollupWithdrawMerkleProof(tc.Users["C"].BJJ.Public().Compress(), 1, 3, 258,
523		big.NewInt(50), []*big.Int{}, false)
524	require.NoError(t, err)
525	client.CtlMineBlock()
526
527	syncBlock, discards, err = s.Sync(ctx, nil)
528	require.NoError(t, err)
529	require.Nil(t, discards)
530	require.NotNil(t, syncBlock)
531	assert.Nil(t, syncBlock.Rollup.Vars)
532	assert.Nil(t, syncBlock.Auction.Vars)
533	assert.Nil(t, syncBlock.WDelayer.Vars)
534	assert.Equal(t, int64(4), syncBlock.Block.Num)
535	stats = s.Stats()
536	assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
537	assert.Equal(t, int64(4), stats.Eth.LastBlock.Num)
538	assert.Equal(t, int64(4), stats.Sync.LastBlock.Num)
539	vars = s.SCVars()
540	assert.Equal(t, clientSetup.RollupVariables, vars.Rollup)
541	assert.Equal(t, clientSetup.AuctionVariables, vars.Auction)
542	assert.Equal(t, clientSetup.WDelayerVariables, vars.WDelayer)
543
544	dbExits, err := s.historyDB.GetAllExits()
545	require.NoError(t, err)
546	foundA1, foundC1 := false, false
547	for _, exit := range dbExits {
548		if exit.AccountIdx == 256 && exit.BatchNum == 4 {
549			foundA1 = true
550			assert.Equal(t, int64(4), *exit.InstantWithdrawn)
551		}
552		if exit.AccountIdx == 258 && exit.BatchNum == 3 {
553			foundC1 = true
554			assert.Equal(t, int64(4), *exit.DelayedWithdrawRequest)
555		}
556	}
557	assert.True(t, foundA1)
558	assert.True(t, foundC1)
559
560	// Block 5
561	// Update variables manually
562	rollupVars, auctionVars, wDelayerVars, err := s.historyDB.GetSCVars()
563	require.NoError(t, err)
564	rollupVars.ForgeL1L2BatchTimeout = 42
565	_, err = client.RollupUpdateForgeL1L2BatchTimeout(rollupVars.ForgeL1L2BatchTimeout)
566	require.NoError(t, err)
567
568	auctionVars.OpenAuctionSlots = 17
569	_, err = client.AuctionSetOpenAuctionSlots(auctionVars.OpenAuctionSlots)
570	require.NoError(t, err)
571
572	wDelayerVars.WithdrawalDelay = 99
573	_, err = client.WDelayerChangeWithdrawalDelay(wDelayerVars.WithdrawalDelay)
574	require.NoError(t, err)
575
576	client.CtlMineBlock()
577
578	syncBlock, discards, err = s.Sync(ctx, nil)
579	require.NoError(t, err)
580	require.Nil(t, discards)
581	require.NotNil(t, syncBlock)
582	assert.NotNil(t, syncBlock.Rollup.Vars)
583	assert.NotNil(t, syncBlock.Auction.Vars)
584	assert.NotNil(t, syncBlock.WDelayer.Vars)
585	assert.Equal(t, int64(5), syncBlock.Block.Num)
586	stats = s.Stats()
587	assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
588	assert.Equal(t, int64(5), stats.Eth.LastBlock.Num)
589	assert.Equal(t, int64(5), stats.Sync.LastBlock.Num)
590	vars = s.SCVars()
591	assert.NotEqual(t, clientSetup.RollupVariables, vars.Rollup)
592	assert.NotEqual(t, clientSetup.AuctionVariables, vars.Auction)
593	assert.NotEqual(t, clientSetup.WDelayerVariables, vars.WDelayer)
594
595	dbRollupVars, dbAuctionVars, dbWDelayerVars, err := s.historyDB.GetSCVars()
596	require.NoError(t, err)
597	// Set EthBlockNum for Vars to the blockNum in which they were updated (should be 5)
598	rollupVars.EthBlockNum = syncBlock.Block.Num
599	auctionVars.EthBlockNum = syncBlock.Block.Num
600	wDelayerVars.EthBlockNum = syncBlock.Block.Num
601	assert.Equal(t, rollupVars, dbRollupVars)
602	assert.Equal(t, auctionVars, dbAuctionVars)
603	assert.Equal(t, wDelayerVars, dbWDelayerVars)
604
605	//
606	// Reorg test
607	//
608
609	// Redo blocks 2-5 (as a reorg) only leaving:
610	// - 2 create account transactions
611	// - 2 add tokens
612	// We add a 6th block so that the synchronizer can detect the reorg
613	set2 := `
614		Type: Blockchain
615
616		AddToken(1)
617		AddToken(2)
618
619		CreateAccountDeposit(1) C: 2000 // Idx=256+1=257
620
621		CreateAccountCoordinator(1) A // Idx=256+0=256
622
623		> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{1}
624		> batchL1 // forge defined L1UserTxs{1}, freeze L1UserTxs{nil}
625		> block // blockNum=2
626		> block // blockNum=3
627		> block // blockNum=4
628		> block // blockNum=5
629		> block // blockNum=6
630	`
631	tc = til.NewContext(chainID, common.RollupConstMaxL1UserTx)
632	tilCfgExtra = til.ConfigExtra{
633		BootCoordAddr: bootCoordAddr,
634		CoordUser:     "A",
635	}
636	blocks, err = tc.GenerateBlocks(set2)
637	require.NoError(t, err)
638
639	// Set StateRoots for batches manually (til doesn't set it)
640	blocks[0].Rollup.Batches[0].Batch.StateRoot =
641		newBigInt("11218510534825843475100588932060366395781087435899915642332104464234485046683")
642	blocks[0].Rollup.Batches[1].Batch.StateRoot =
643		newBigInt("20283020730369146334077598087403837297563965802277806438205710455191646998983")
644
645	for i := 0; i < 4; i++ {
646		client.CtlRollback()
647	}
648	block := client.CtlLastBlock()
649	require.Equal(t, int64(1), block.Num)
650
651	// Generate extra required data
652	ethAddTokens(blocks, client)
653
654	err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
655	require.NoError(t, err)
656	tc.FillBlocksL1UserTxsBatchNum(blocks)
657
658	// Add block data to the smart contracts
659	err = client.CtlAddBlocks(blocks)
660	require.NoError(t, err)
661
662	// First sync detects the reorg and discards 4 blocks
663	syncBlock, discards, err = s.Sync(ctx, nil)
664	require.NoError(t, err)
665	expetedDiscards := int64(4)
666	require.Equal(t, &expetedDiscards, discards)
667	require.Nil(t, syncBlock)
668	stats = s.Stats()
669	assert.Equal(t, false, stats.Synced())
670	assert.Equal(t, int64(6), stats.Eth.LastBlock.Num)
671	vars = s.SCVars()
672	assert.Equal(t, clientSetup.RollupVariables, vars.Rollup)
673	assert.Equal(t, clientSetup.AuctionVariables, vars.Auction)
674	assert.Equal(t, clientSetup.WDelayerVariables, vars.WDelayer)
675
676	// At this point, the DB only has data up to block 1
677	dbBlock, err := s.historyDB.GetLastBlock()
678	require.NoError(t, err)
679	assert.Equal(t, int64(1), dbBlock.Num)
680
681	// Accounts in HistoryDB and StateDB must be empty
682	dbAccounts, err := s.historyDB.GetAllAccounts()
683	require.NoError(t, err)
684	sdbAccounts, err := s.stateDB.TestGetAccounts()
685	require.NoError(t, err)
686	assert.Equal(t, 0, len(dbAccounts))
687	assertEqualAccountsHistoryDBStateDB(t, dbAccounts, sdbAccounts)
688
689	// Sync blocks 2-6
690	for i := 0; i < 5; i++ {
691		syncBlock, discards, err = s.Sync(ctx, nil)
692		require.NoError(t, err)
693		require.Nil(t, discards)
694		require.NotNil(t, syncBlock)
695		assert.Nil(t, syncBlock.Rollup.Vars)
696		assert.Nil(t, syncBlock.Auction.Vars)
697		assert.Nil(t, syncBlock.WDelayer.Vars)
698		assert.Equal(t, int64(2+i), syncBlock.Block.Num)
699
700		stats = s.Stats()
701		assert.Equal(t, int64(1), stats.Eth.FirstBlockNum)
702		assert.Equal(t, int64(6), stats.Eth.LastBlock.Num)
703		assert.Equal(t, int64(2+i), stats.Sync.LastBlock.Num)
704		if i == 4 {
705			assert.Equal(t, true, stats.Synced())
706		} else {
707			assert.Equal(t, false, stats.Synced())
708		}
709
710		vars = s.SCVars()
711		assert.Equal(t, clientSetup.RollupVariables, vars.Rollup)
712		assert.Equal(t, clientSetup.AuctionVariables, vars.Auction)
713		assert.Equal(t, clientSetup.WDelayerVariables, vars.WDelayer)
714	}
715
716	dbBlock, err = s.historyDB.GetLastBlock()
717	require.NoError(t, err)
718	assert.Equal(t, int64(6), dbBlock.Num)
719
720	// Accounts in HistoryDB and StateDB is only 2 entries
721	dbAccounts, err = s.historyDB.GetAllAccounts()
722	require.NoError(t, err)
723	sdbAccounts, err = s.stateDB.TestGetAccounts()
724	require.NoError(t, err)
725	assert.Equal(t, 2, len(dbAccounts))
726	assertEqualAccountsHistoryDBStateDB(t, dbAccounts, sdbAccounts)
727}
728
729func TestSyncForgerCommitment(t *testing.T) {
730	stateDB, historyDB := newTestModules(t)
731
732	// Init eth client
733	var timer timer
734	clientSetup := test.NewClientSetupExample()
735	clientSetup.ChainID = big.NewInt(int64(chainID))
736	clientSetup.AuctionConstants.GenesisBlockNum = 2
737	clientSetup.AuctionConstants.BlocksPerSlot = 4
738	clientSetup.AuctionVariables.SlotDeadline = 2
739	bootCoordAddr := clientSetup.AuctionVariables.BootCoordinator
740	client := test.NewClient(true, &timer, &ethCommon.Address{}, clientSetup)
741
742	// Create Synchronizer
743	s, err := NewSynchronizer(client, historyDB, stateDB, Config{
744		StatsRefreshPeriod: 0 * time.Second,
745	})
746	require.NoError(t, err)
747
748	ctx := context.Background()
749
750	set := `
751		Type: Blockchain
752
753		// Slot = 0
754
755		> block // 2
756		> block // 3
757		> block // 4
758		> block // 5
759
760		// Slot = 1
761
762		> block // 6
763		> batch
764		> block // 7
765		> block // 8
766		> block // 9
767
768		// Slot = 2
769
770		> block // 10
771		> block // 11
772		> batch
773		> block // 12
774		> block // 13
775
776	`
777	// For each block, true when the slot that belongs to the following
778	// block has forgerCommitment
779	commitment := map[int64]bool{
780		2: false,
781		3: false,
782		4: false,
783		5: false,
784
785		6: false,
786		7: true,
787		8: true,
788		9: false,
789
790		10: false,
791		11: false,
792		12: false,
793		13: false,
794	}
795	tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
796	blocks, err := tc.GenerateBlocks(set)
797	assert.NoError(t, err)
798
799	tilCfgExtra := til.ConfigExtra{
800		BootCoordAddr: bootCoordAddr,
801		CoordUser:     "A",
802	}
803	err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
804	require.NoError(t, err)
805
806	// for i := range blocks {
807	// 	for j := range blocks[i].Rollup.Batches {
808	// 		blocks[i].Rollup.Batches[j].Batch.SlotNum = int64(i) / 4
809	// 	}
810	// }
811
812	// be in sync
813	for {
814		syncBlock, discards, err := s.Sync(ctx, nil)
815		require.NoError(t, err)
816		require.Nil(t, discards)
817		if syncBlock == nil {
818			break
819		}
820	}
821	stats := s.Stats()
822	require.Equal(t, int64(1), stats.Sync.LastBlock.Num)
823
824	// Store ForgerComitmnent observed at every block by the live synchronizer
825	syncCommitment := map[int64]bool{}
826	// Store ForgerComitmnent observed at every block by a syncrhonizer that is restarted
827	syncRestartedCommitment := map[int64]bool{}
828	for _, block := range blocks {
829		// Add block data to the smart contracts
830		err = client.CtlAddBlocks([]common.BlockData{block})
831		require.NoError(t, err)
832
833		syncBlock, discards, err := s.Sync(ctx, nil)
834		require.NoError(t, err)
835		require.Nil(t, discards)
836		if syncBlock == nil {
837			break
838		}
839		stats := s.Stats()
840		require.True(t, stats.Synced())
841		syncCommitment[syncBlock.Block.Num] = stats.Sync.Auction.CurrentSlot.ForgerCommitment
842
843		s2, err := NewSynchronizer(client, historyDB, stateDB, Config{
844			StatsRefreshPeriod: 0 * time.Second,
845		})
846		require.NoError(t, err)
847		stats = s2.Stats()
848		require.True(t, stats.Synced())
849		syncRestartedCommitment[syncBlock.Block.Num] = stats.Sync.Auction.CurrentSlot.ForgerCommitment
850	}
851	assert.Equal(t, commitment, syncCommitment)
852	assert.Equal(t, commitment, syncRestartedCommitment)
853}