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	"testing"
 11
 12	ethCommon "github.com/ethereum/go-ethereum/common"
 13	"github.com/hermeznetwork/hermez-node/common"
 14	dbUtils "github.com/hermeznetwork/hermez-node/db"
 15	"github.com/hermeznetwork/hermez-node/db/historydb"
 16	"github.com/hermeznetwork/hermez-node/db/statedb"
 17	"github.com/hermeznetwork/hermez-node/eth"
 18	"github.com/hermeznetwork/hermez-node/test"
 19	"github.com/hermeznetwork/hermez-node/test/til"
 20	"github.com/jinzhu/copier"
 21	"github.com/stretchr/testify/assert"
 22	"github.com/stretchr/testify/require"
 23)
 24
 25var tokenConsts = map[common.TokenID]eth.ERC20Consts{}
 26
 27type timer struct {
 28	time int64
 29}
 30
 31func (t *timer) Time() int64 {
 32	currentTime := t.time
 33	t.time++
 34	return currentTime
 35}
 36
 37// Check Sync output and HistoryDB state against expected values generated by
 38// til
 39func checkSyncBlock(t *testing.T, s *Synchronizer, blockNum int, block, syncBlock *common.BlockData) {
 40	// Check Blocks
 41	dbBlocks, err := s.historyDB.GetAllBlocks()
 42	require.Nil(t, err)
 43	dbBlocks = dbBlocks[1:] // ignore block 0, added by default in the DB
 44	assert.Equal(t, blockNum, len(dbBlocks))
 45	assert.Equal(t, int64(blockNum), dbBlocks[blockNum-1].EthBlockNum)
 46	assert.NotEqual(t, dbBlocks[blockNum-1].Hash, dbBlocks[blockNum-2].Hash)
 47	assert.Greater(t, dbBlocks[blockNum-1].Timestamp.Unix(), dbBlocks[blockNum-2].Timestamp.Unix())
 48
 49	// Check Tokens
 50	assert.Equal(t, len(block.Rollup.AddedTokens), len(syncBlock.Rollup.AddedTokens))
 51	dbTokens, err := s.historyDB.GetAllTokens()
 52	require.Nil(t, err)
 53	dbTokens = dbTokens[1:] // ignore token 0, added by default in the DB
 54	for i, token := range block.Rollup.AddedTokens {
 55		dbToken := dbTokens[i]
 56		syncToken := syncBlock.Rollup.AddedTokens[i]
 57
 58		assert.Equal(t, block.Block.EthBlockNum, syncToken.EthBlockNum)
 59		assert.Equal(t, token.TokenID, syncToken.TokenID)
 60		assert.Equal(t, token.EthAddr, syncToken.EthAddr)
 61		tokenConst := tokenConsts[token.TokenID]
 62		assert.Equal(t, tokenConst.Name, syncToken.Name)
 63		assert.Equal(t, tokenConst.Symbol, syncToken.Symbol)
 64		assert.Equal(t, tokenConst.Decimals, syncToken.Decimals)
 65
 66		var tokenCpy historydb.TokenWithUSD
 67		//nolint:gosec
 68		require.Nil(t, copier.Copy(&tokenCpy, &token))      // copy common.Token to historydb.TokenWithUSD
 69		require.Nil(t, copier.Copy(&tokenCpy, &tokenConst)) // copy common.Token to historydb.TokenWithUSD
 70		tokenCpy.ItemID = dbToken.ItemID                    // we don't care about ItemID
 71		assert.Equal(t, tokenCpy, dbToken)
 72	}
 73
 74	// Check L1UserTxs
 75	assert.Equal(t, len(block.Rollup.L1UserTxs), len(syncBlock.Rollup.L1UserTxs))
 76	dbL1UserTxs, err := s.historyDB.GetAllL1UserTxs()
 77	require.Nil(t, err)
 78	// Ignore BatchNum in syncBlock.L1UserTxs because this value is set by the HistoryDB
 79	for i := range syncBlock.Rollup.L1UserTxs {
 80		syncBlock.Rollup.L1UserTxs[i].BatchNum = block.Rollup.L1UserTxs[i].BatchNum
 81	}
 82	assert.Equal(t, block.Rollup.L1UserTxs, syncBlock.Rollup.L1UserTxs)
 83	for _, tx := range block.Rollup.L1UserTxs {
 84		var dbTx *common.L1Tx
 85		// Find tx in DB output
 86		for _, _dbTx := range dbL1UserTxs {
 87			if *tx.ToForgeL1TxsNum == *_dbTx.ToForgeL1TxsNum &&
 88				tx.Position == _dbTx.Position {
 89				dbTx = new(common.L1Tx)
 90				*dbTx = _dbTx
 91				break
 92			}
 93		}
 94		assert.Equal(t, &tx, dbTx) //nolint:gosec
 95	}
 96
 97	// Check Batches
 98	assert.Equal(t, len(block.Rollup.Batches), len(syncBlock.Rollup.Batches))
 99	dbBatches, err := s.historyDB.GetAllBatches()
100	require.Nil(t, err)
101
102	dbL1CoordinatorTxs, err := s.historyDB.GetAllL1CoordinatorTxs()
103	require.Nil(t, err)
104	dbL2Txs, err := s.historyDB.GetAllL2Txs()
105	require.Nil(t, err)
106	dbExits, err := s.historyDB.GetAllExits()
107	require.Nil(t, err)
108	// dbL1CoordinatorTxs := []common.L1Tx{}
109	for i, batch := range block.Rollup.Batches {
110		var dbBatch *common.Batch
111		// Find batch in DB output
112		for _, _dbBatch := range dbBatches {
113			if batch.Batch.BatchNum == _dbBatch.BatchNum {
114				dbBatch = new(common.Batch)
115				*dbBatch = _dbBatch
116				break
117			}
118		}
119		syncBatch := syncBlock.Rollup.Batches[i]
120
121		// We don't care about TotalFeesUSD.  Use the syncBatch that
122		// has a TotalFeesUSD inserted by the HistoryDB
123		batch.Batch.TotalFeesUSD = syncBatch.Batch.TotalFeesUSD
124		assert.Equal(t, batch.CreatedAccounts, syncBatch.CreatedAccounts)
125		batch.Batch.NumAccounts = len(batch.CreatedAccounts)
126
127		// Test field by field to facilitate debugging of errors
128		assert.Equal(t, batch.L1CoordinatorTxs, syncBatch.L1CoordinatorTxs)
129		assert.Equal(t, batch.L2Txs, syncBatch.L2Txs)
130		// In exit tree, we only check AccountIdx and Balance, because
131		// it's what we have precomputed before.
132		for j := range batch.ExitTree {
133			exit := &batch.ExitTree[j]
134			assert.Equal(t, exit.AccountIdx, syncBatch.ExitTree[j].AccountIdx)
135			assert.Equal(t, exit.Balance, syncBatch.ExitTree[j].Balance)
136			*exit = syncBatch.ExitTree[j]
137		}
138		assert.Equal(t, batch.Batch, syncBatch.Batch)
139		assert.Equal(t, batch, syncBatch)
140		assert.Equal(t, &batch.Batch, dbBatch) //nolint:gosec
141
142		// Check L1CoordinatorTxs from DB
143		for _, tx := range batch.L1CoordinatorTxs {
144			var dbTx *common.L1Tx
145			// Find tx in DB output
146			for _, _dbTx := range dbL1CoordinatorTxs {
147				if *tx.BatchNum == *_dbTx.BatchNum &&
148					tx.Position == _dbTx.Position {
149					dbTx = new(common.L1Tx)
150					*dbTx = _dbTx
151					break
152				}
153			}
154			assert.Equal(t, &tx, dbTx) //nolint:gosec
155		}
156
157		// Check L2Txs from DB
158		for _, tx := range batch.L2Txs {
159			var dbTx *common.L2Tx
160			// Find tx in DB output
161			for _, _dbTx := range dbL2Txs {
162				if tx.BatchNum == _dbTx.BatchNum &&
163					tx.Position == _dbTx.Position {
164					dbTx = new(common.L2Tx)
165					*dbTx = _dbTx
166					break
167				}
168			}
169			assert.Equal(t, &tx, dbTx) //nolint:gosec
170		}
171
172		// Check Exits from DB
173		for _, exit := range batch.ExitTree {
174			var dbExit *common.ExitInfo
175			// Find exit in DB output
176			for _, _dbExit := range dbExits {
177				if exit.BatchNum == _dbExit.BatchNum &&
178					exit.AccountIdx == _dbExit.AccountIdx {
179					dbExit = new(common.ExitInfo)
180					*dbExit = _dbExit
181					break
182				}
183			}
184			// Compare MerkleProof in JSON because unmarshaled 0
185			// big.Int leaves the internal big.Int array at nil,
186			// and gives trouble when comparing big.Int with
187			// internal big.Int array != nil but empty.
188			mtp, err := json.Marshal(exit.MerkleProof)
189			require.Nil(t, err)
190			dbMtp, err := json.Marshal(dbExit.MerkleProof)
191			require.Nil(t, err)
192			assert.Equal(t, mtp, dbMtp)
193			dbExit.MerkleProof = exit.MerkleProof
194			assert.Equal(t, &exit, dbExit) //nolint:gosec
195		}
196	}
197}
198
199func TestSync(t *testing.T) {
200	//
201	// Setup
202	//
203
204	ctx := context.Background()
205	// Int State DB
206	dir, err := ioutil.TempDir("", "tmpdb")
207	require.Nil(t, err)
208	defer assert.Nil(t, os.RemoveAll(dir))
209
210	stateDB, err := statedb.NewStateDB(dir, statedb.TypeSynchronizer, 32)
211	assert.Nil(t, err)
212
213	// Init History DB
214	pass := os.Getenv("POSTGRES_PASS")
215	db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
216	require.Nil(t, err)
217	historyDB := historydb.NewHistoryDB(db)
218	// Clear DB
219	test.WipeDB(historyDB.DB())
220
221	// Init eth client
222	var timer timer
223	clientSetup := test.NewClientSetupExample()
224	bootCoordAddr := clientSetup.AuctionVariables.BootCoordinator
225	client := test.NewClient(true, &timer, &ethCommon.Address{}, clientSetup)
226
227	// Create Synchronizer
228	s, err := NewSynchronizer(client, historyDB, stateDB, Config{
229		StartBlockNum: ConfigStartBlockNum{
230			Rollup:   1,
231			Auction:  1,
232			WDelayer: 1,
233		},
234		InitialVariables: SCVariables{
235			Rollup:   *clientSetup.RollupVariables,
236			Auction:  *clientSetup.AuctionVariables,
237			WDelayer: *clientSetup.WDelayerVariables,
238		},
239	})
240	require.Nil(t, err)
241
242	//
243	// First Sync from an initial state
244	//
245
246	// Test Sync for rollup genesis block
247	syncBlock, discards, err := s.Sync2(ctx, nil)
248	require.Nil(t, err)
249	require.Nil(t, discards)
250	require.NotNil(t, syncBlock)
251	assert.Equal(t, int64(1), syncBlock.Block.EthBlockNum)
252	dbBlocks, err := s.historyDB.GetAllBlocks()
253	require.Nil(t, err)
254	assert.Equal(t, 2, len(dbBlocks))
255	assert.Equal(t, int64(1), dbBlocks[1].EthBlockNum)
256
257	// Sync again and expect no new blocks
258	syncBlock, discards, err = s.Sync2(ctx, nil)
259	require.Nil(t, err)
260	require.Nil(t, discards)
261	require.Nil(t, syncBlock)
262
263	//
264	// Generate blockchain and smart contract data, and fill the test smart contracts
265	//
266
267	// Generate blockchain data with til
268	set1 := `
269		Type: Blockchain
270
271		AddToken(1)
272		AddToken(2)
273		AddToken(3)
274
275		CreateAccountDeposit(1) C: 2000 // Idx=256+2=258
276		CreateAccountDeposit(2) A: 2000 // Idx=256+3=259
277		CreateAccountDeposit(1) D: 500  // Idx=256+4=260
278		CreateAccountDeposit(2) B: 500  // Idx=256+5=261
279		CreateAccountDeposit(2) C: 500  // Idx=256+6=262
280
281		CreateAccountCoordinator(1) A // Idx=256+0=256
282		CreateAccountCoordinator(1) B // Idx=256+1=257
283
284		> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{5}
285		> batchL1 // forge defined L1UserTxs{5}, freeze L1UserTxs{nil}
286		> block // blockNum=2
287
288		CreateAccountDepositTransfer(1) E-A: 1000, 200 // Idx=256+7=263
289		ForceExit(1) A: 100
290		ForceExit(1) B: 80
291		ForceTransfer(1) A-D: 100
292
293		Transfer(1) C-A: 100 (200)
294		Exit(1) C: 50 (200)
295		Exit(1) D: 30 (200)
296
297		> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{3}
298		> batchL1 // forge L1UserTxs{3}, freeze defined L1UserTxs{nil}
299		> block // blockNum=3
300	`
301	tc := til.NewContext(common.RollupConstMaxL1UserTx)
302	tilCfgExtra := til.ConfigExtra{
303		BootCoordAddr: bootCoordAddr,
304		CoordUser:     "A",
305	}
306	blocks, err := tc.GenerateBlocks(set1)
307	require.Nil(t, err)
308	// Sanity check
309	require.Equal(t, 2, len(blocks))
310	// blocks 0 (blockNum=2)
311	i := 0
312	require.Equal(t, 2, int(blocks[i].Block.EthBlockNum))
313	require.Equal(t, 3, len(blocks[i].Rollup.AddedTokens))
314	require.Equal(t, 5, len(blocks[i].Rollup.L1UserTxs))
315	require.Equal(t, 2, len(blocks[i].Rollup.Batches))
316	require.Equal(t, 2, len(blocks[i].Rollup.Batches[0].L1CoordinatorTxs))
317	// blocks 1 (blockNum=3)
318	i = 1
319	require.Equal(t, 3, int(blocks[i].Block.EthBlockNum))
320	require.Equal(t, 4, len(blocks[i].Rollup.L1UserTxs))
321	require.Equal(t, 2, len(blocks[i].Rollup.Batches))
322	require.Equal(t, 3, len(blocks[i].Rollup.Batches[0].L2Txs))
323
324	// Generate extra required data
325	for _, block := range blocks {
326		for _, token := range block.Rollup.AddedTokens {
327			consts := eth.ERC20Consts{
328				Name:     fmt.Sprintf("Token %d", token.TokenID),
329				Symbol:   fmt.Sprintf("TK%d", token.TokenID),
330				Decimals: 18,
331			}
332			tokenConsts[token.TokenID] = consts
333			client.CtlAddERC20(token.EthAddr, consts)
334		}
335	}
336
337	err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
338	assert.Nil(t, err)
339	tc.FillBlocksL1UserTxsBatchNum(blocks)
340
341	// Add block data to the smart contracts
342	for _, block := range blocks {
343		for _, token := range block.Rollup.AddedTokens {
344			_, err := client.RollupAddTokenSimple(token.EthAddr, clientSetup.RollupVariables.FeeAddToken)
345			require.Nil(t, err)
346		}
347		for _, tx := range block.Rollup.L1UserTxs {
348			client.CtlSetAddr(tx.FromEthAddr)
349			_, err := client.RollupL1UserTxERC20ETH(tx.FromBJJ, int64(tx.FromIdx), tx.LoadAmount, tx.Amount,
350				uint32(tx.TokenID), int64(tx.ToIdx))
351			require.Nil(t, err)
352		}
353		client.CtlSetAddr(bootCoordAddr)
354		// feeIdxCoordinator := []common.Idx{}
355		// if block.Block.EthBlockNum > 2 {
356		// 	// After blockNum=2 we have some accounts, use them as
357		// 	// coordinator owned to receive fees.
358		// 	feeIdxCoordinator = []common.Idx{common.Idx(256), common.Idx(259)}
359		// }
360		for _, batch := range block.Rollup.Batches {
361			_, err := client.RollupForgeBatch(&eth.RollupForgeBatchArgs{
362				NewLastIdx:            batch.Batch.LastIdx,
363				NewStRoot:             batch.Batch.StateRoot,
364				NewExitRoot:           batch.Batch.ExitRoot,
365				L1CoordinatorTxs:      batch.L1CoordinatorTxs,
366				L1CoordinatorTxsAuths: [][]byte{}, // Intentionally empty
367				L2TxsData:             batch.L2Txs,
368				FeeIdxCoordinator:     batch.Batch.FeeIdxsCoordinator,
369				// Circuit selector
370				VerifierIdx: 0, // Intentionally empty
371				L1Batch:     batch.L1Batch,
372				ProofA:      [2]*big.Int{},    // Intentionally empty
373				ProofB:      [2][2]*big.Int{}, // Intentionally empty
374				ProofC:      [2]*big.Int{},    // Intentionally empty
375			})
376			require.Nil(t, err)
377		}
378		// Mine block and sync
379		client.CtlMineBlock()
380	}
381
382	//
383	// Sync to synchronize the current state from the test smart contracts,
384	// and check the outcome
385	//
386
387	// Block 2
388
389	syncBlock, discards, err = s.Sync2(ctx, nil)
390	require.Nil(t, err)
391	require.Nil(t, discards)
392	require.NotNil(t, syncBlock)
393	assert.Equal(t, int64(2), syncBlock.Block.EthBlockNum)
394
395	checkSyncBlock(t, s, 2, &blocks[0], syncBlock)
396
397	// Block 3
398
399	syncBlock, discards, err = s.Sync2(ctx, nil)
400	require.Nil(t, err)
401	require.Nil(t, discards)
402	require.NotNil(t, syncBlock)
403	assert.Equal(t, int64(3), syncBlock.Block.EthBlockNum)
404
405	checkSyncBlock(t, s, 3, &blocks[1], syncBlock)
406
407	// Block 4
408	// Generate 2 withdraws manually
409	_, err = client.RollupWithdrawMerkleProof(tc.Users["A"].BJJ.Public(), 1, 4, 256, big.NewInt(100), []*big.Int{}, true)
410	require.Nil(t, err)
411	_, err = client.RollupWithdrawMerkleProof(tc.Users["C"].BJJ.Public(), 1, 3, 258, big.NewInt(50), []*big.Int{}, false)
412	require.Nil(t, err)
413	client.CtlMineBlock()
414
415	syncBlock, discards, err = s.Sync2(ctx, nil)
416	require.Nil(t, err)
417	require.Nil(t, discards)
418	require.NotNil(t, syncBlock)
419	assert.Equal(t, int64(4), syncBlock.Block.EthBlockNum)
420	dbExits, err := s.historyDB.GetAllExits()
421	require.Nil(t, err)
422	foundA1, foundC1 := false, false
423	for _, exit := range dbExits {
424		if exit.AccountIdx == 256 && exit.BatchNum == 4 {
425			foundA1 = true
426			assert.Equal(t, int64(4), *exit.InstantWithdrawn)
427		}
428		if exit.AccountIdx == 258 && exit.BatchNum == 3 {
429			foundC1 = true
430			assert.Equal(t, int64(4), *exit.DelayedWithdrawRequest)
431		}
432	}
433	assert.True(t, foundA1)
434	assert.True(t, foundC1)
435
436	// Block 5
437	// Update variables manually
438	rollupVars, auctionVars, wDelayerVars, err := s.historyDB.GetSCVars()
439	require.Nil(t, err)
440	rollupVars.ForgeL1L2BatchTimeout = 42
441	_, err = client.RollupUpdateForgeL1L2BatchTimeout(rollupVars.ForgeL1L2BatchTimeout)
442	require.Nil(t, err)
443
444	auctionVars.OpenAuctionSlots = 17
445	_, err = client.AuctionSetOpenAuctionSlots(auctionVars.OpenAuctionSlots)
446	require.Nil(t, err)
447
448	wDelayerVars.WithdrawalDelay = 99
449	_, err = client.WDelayerChangeWithdrawalDelay(wDelayerVars.WithdrawalDelay)
450	require.Nil(t, err)
451
452	client.CtlMineBlock()
453
454	syncBlock, discards, err = s.Sync2(ctx, nil)
455	require.Nil(t, err)
456	require.Nil(t, discards)
457	require.NotNil(t, syncBlock)
458	assert.Equal(t, int64(5), syncBlock.Block.EthBlockNum)
459
460	dbRollupVars, dbAuctionVars, dbWDelayerVars, err := s.historyDB.GetSCVars()
461	require.Nil(t, err)
462	// Set EthBlockNum for Vars to the blockNum in which they were updated (should be 5)
463	rollupVars.EthBlockNum = syncBlock.Block.EthBlockNum
464	auctionVars.EthBlockNum = syncBlock.Block.EthBlockNum
465	wDelayerVars.EthBlockNum = syncBlock.Block.EthBlockNum
466	assert.Equal(t, rollupVars, dbRollupVars)
467	assert.Equal(t, auctionVars, dbAuctionVars)
468	assert.Equal(t, wDelayerVars, dbWDelayerVars)
469
470	// TODO: Reorg will be properly tested once we have the mock ethClient implemented
471	/*
472		// Force a Reorg
473		lastSavedBlock, err := historyDB.GetLastBlock()
474		require.Nil(t, err)
475
476		lastSavedBlock.EthBlockNum++
477		err = historyDB.AddBlock(lastSavedBlock)
478		require.Nil(t, err)
479
480		lastSavedBlock.EthBlockNum++
481		err = historyDB.AddBlock(lastSavedBlock)
482		require.Nil(t, err)
483
484		log.Debugf("Wait for the blockchain to generate some blocks...")
485		time.Sleep(40 * time.Second)
486
487
488		err = s.Sync()
489		require.Nil(t, err)
490	*/
491}