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