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