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