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