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