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{}
 26var forceExits = map[int64][]common.ExitInfo{} // ForgeL1TxsNum -> []exit
 27var nonces = map[common.Idx]common.Nonce{}
 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
 39// Check Sync output and HistoryDB state against expected values generated by
 40// til
 41func checkSyncBlock(t *testing.T, s *Synchronizer, blockNum int, block, syncBlock *common.BlockData) {
 42	// Check Blocks
 43	dbBlocks, err := s.historyDB.GetAllBlocks()
 44	require.Nil(t, err)
 45	assert.Equal(t, blockNum, len(dbBlocks))
 46	assert.Equal(t, int64(blockNum), dbBlocks[blockNum-1].EthBlockNum)
 47	assert.NotEqual(t, dbBlocks[blockNum-1].Hash, dbBlocks[blockNum-2].Hash)
 48	assert.Greater(t, dbBlocks[blockNum-1].Timestamp.Unix(), dbBlocks[blockNum-2].Timestamp.Unix())
 49
 50	// Check Tokens
 51	assert.Equal(t, len(block.AddedTokens), len(syncBlock.AddedTokens))
 52	dbTokens, err := s.historyDB.GetAllTokens()
 53	require.Nil(t, err)
 54	for i, token := range block.AddedTokens {
 55		dbToken := dbTokens[i]
 56		syncToken := syncBlock.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.L1UserTxs), len(syncBlock.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.L1UserTxs {
 80		syncBlock.L1UserTxs[i].BatchNum = block.L1UserTxs[i].BatchNum
 81	}
 82	assert.Equal(t, block.L1UserTxs, syncBlock.L1UserTxs)
 83	for _, tx := range block.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.Batches), len(syncBlock.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	// fmt.Printf("DBG dbL1CoordinatorTxs: %+v\n", dbL1CoordinatorTxs)
105	dbL2Txs, err := s.historyDB.GetAllL2Txs()
106	require.Nil(t, err)
107	// fmt.Printf("DBG dbL2Txs: %+v\n", dbL2Txs)
108	dbExits, err := s.historyDB.GetAllExits()
109	require.Nil(t, err)
110	// dbL1CoordinatorTxs := []common.L1Tx{}
111	for i, batch := range block.Batches {
112		var dbBatch *common.Batch
113		// Find batch in DB output
114		for _, _dbBatch := range dbBatches {
115			if batch.Batch.BatchNum == _dbBatch.BatchNum {
116				dbBatch = new(common.Batch)
117				*dbBatch = _dbBatch
118				break
119			}
120		}
121		syncBatch := syncBlock.Batches[i]
122
123		// We don't care about TotalFeesUSD.  Use the syncBatch that
124		// has a TotalFeesUSD inserted by the HistoryDB
125		batch.Batch.TotalFeesUSD = syncBatch.Batch.TotalFeesUSD
126		batch.CreatedAccounts = syncBatch.CreatedAccounts // til doesn't output CreatedAccounts
127		batch.Batch.NumAccounts = len(batch.CreatedAccounts)
128
129		// Test field by field to facilitate debugging of errors
130		assert.Equal(t, batch.L1CoordinatorTxs, syncBatch.L1CoordinatorTxs)
131		assert.Equal(t, batch.L2Txs, syncBatch.L2Txs)
132		// In exit tree, we only check AccountIdx and Balance, because
133		// it's what we have precomputed before.
134		for j := range batch.ExitTree {
135			exit := &batch.ExitTree[j]
136			assert.Equal(t, exit.AccountIdx, syncBatch.ExitTree[j].AccountIdx)
137			assert.Equal(t, exit.Balance, syncBatch.ExitTree[j].Balance)
138			*exit = syncBatch.ExitTree[j]
139		}
140		// We are collecting fees after blockNum=2 in 2 idxs
141		if block.Block.EthBlockNum > 2 {
142			// fmt.Printf("DBG collectedFees: %+v\n", syncBatch.Batch.CollectedFees)
143			assert.Equal(t, 2, len(syncBatch.Batch.CollectedFees))
144		}
145		batch.Batch.CollectedFees = syncBatch.Batch.CollectedFees
146		assert.Equal(t, batch.Batch, syncBatch.Batch)
147		assert.Equal(t, batch, syncBatch)
148		assert.Equal(t, &batch.Batch, dbBatch) //nolint:gosec
149
150		// Check L1CoordinatorTxs from DB
151		for _, tx := range batch.L1CoordinatorTxs {
152			var dbTx *common.L1Tx
153			// Find tx in DB output
154			for _, _dbTx := range dbL1CoordinatorTxs {
155				if *tx.BatchNum == *_dbTx.BatchNum &&
156					tx.Position == _dbTx.Position {
157					dbTx = new(common.L1Tx)
158					*dbTx = _dbTx
159					break
160				}
161			}
162			assert.Equal(t, &tx, dbTx) //nolint:gosec
163		}
164
165		// Check L2Txs from DB
166		for _, tx := range batch.L2Txs {
167			var dbTx *common.L2Tx
168			// Find tx in DB output
169			for _, _dbTx := range dbL2Txs {
170				if tx.BatchNum == _dbTx.BatchNum &&
171					tx.Position == _dbTx.Position {
172					dbTx = new(common.L2Tx)
173					*dbTx = _dbTx
174					break
175				}
176			}
177			assert.Equal(t, &tx, dbTx) //nolint:gosec
178		}
179
180		// Check Exits from DB
181		for _, exit := range batch.ExitTree {
182			var dbExit *common.ExitInfo
183			// Find exit in DB output
184			for _, _dbExit := range dbExits {
185				if exit.BatchNum == _dbExit.BatchNum &&
186					exit.AccountIdx == _dbExit.AccountIdx {
187					dbExit = new(common.ExitInfo)
188					*dbExit = _dbExit
189					break
190				}
191			}
192			// Compare MerkleProof in JSON because unmarshaled 0
193			// big.Int leaves the internal big.Int array at nil,
194			// and gives trouble when comparing big.Int with
195			// internal big.Int array != nil but empty.
196			mtp, err := json.Marshal(exit.MerkleProof)
197			require.Nil(t, err)
198			dbMtp, err := json.Marshal(dbExit.MerkleProof)
199			require.Nil(t, err)
200			assert.Equal(t, mtp, dbMtp)
201			dbExit.MerkleProof = exit.MerkleProof
202			assert.Equal(t, &exit, dbExit) //nolint:gosec
203		}
204	}
205}
206
207func TestSync(t *testing.T) {
208	//
209	// Setup
210	//
211
212	ctx := context.Background()
213	// Int State DB
214	dir, err := ioutil.TempDir("", "tmpdb")
215	require.Nil(t, err)
216	defer assert.Nil(t, os.RemoveAll(dir))
217
218	stateDB, err := statedb.NewStateDB(dir, statedb.TypeSynchronizer, 32)
219	assert.Nil(t, err)
220
221	// Init History DB
222	pass := os.Getenv("POSTGRES_PASS")
223	db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
224	require.Nil(t, err)
225	historyDB := historydb.NewHistoryDB(db)
226	// Clear DB
227	err = historyDB.Reorg(-1)
228	assert.Nil(t, err)
229
230	// Init eth client
231	var timer timer
232	clientSetup := test.NewClientSetupExample()
233	bootCoordAddr := clientSetup.AuctionVariables.BootCoordinator
234	client := test.NewClient(true, &timer, &ethCommon.Address{}, clientSetup)
235
236	// Create Synchronizer
237	s, err := NewSynchronizer(client, historyDB, stateDB)
238	require.Nil(t, err)
239
240	//
241	// First Sync from an initial state
242	//
243
244	// Test Sync for rollup genesis block
245	syncBlock, discards, err := s.Sync2(ctx, nil)
246	require.Nil(t, err)
247	require.Nil(t, discards)
248	require.NotNil(t, syncBlock)
249	assert.Equal(t, int64(1), syncBlock.Block.EthBlockNum)
250	dbBlocks, err := s.historyDB.GetAllBlocks()
251	require.Nil(t, err)
252	assert.Equal(t, 1, len(dbBlocks))
253	assert.Equal(t, int64(1), dbBlocks[0].EthBlockNum)
254
255	// Sync again and expect no new blocks
256	syncBlock, discards, err = s.Sync2(ctx, nil)
257	require.Nil(t, err)
258	require.Nil(t, discards)
259	require.Nil(t, syncBlock)
260
261	//
262	// Generate blockchain and smart contract data, and fill the test smart contracts
263	//
264
265	// Generate blockchain data with til
266	set1 := `
267		Type: Blockchain
268
269		AddToken(1)
270		AddToken(2)
271		AddToken(3)
272
273		CreateAccountDeposit(1) C: 2000 // Idx=256+2=258
274		CreateAccountDeposit(2) A: 2000 // Idx=256+3=259
275		CreateAccountDeposit(1) D: 500  // Idx=256+4=260
276		CreateAccountDeposit(2) B: 500  // Idx=256+5=261
277		CreateAccountDeposit(2) C: 500  // Idx=256+6=262
278
279		CreateAccountCoordinator(1) A // Idx=256+0=256
280		CreateAccountCoordinator(1) B // Idx=256+1=257
281
282		> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{5}
283		> batchL1 // forge defined L1UserTxs{5}, freeze L1UserTxs{nil}
284		> block // blockNum=2
285
286		CreateAccountDepositTransfer(1) E-A: 1000, 200 // Idx=256+7=263
287		ForceExit(1) A: 100
288		ForceTransfer(1) A-D: 100
289
290		Transfer(1) C-A: 100 (200)
291		Exit(1) D: 30 (200)
292
293		> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{2}
294		> batchL1 // forge L1UserTxs{2}, freeze defined L1UserTxs{nil}
295		> block // blockNum=3
296
297	`
298	tc := til.NewContext(eth.RollupConstMaxL1UserTx)
299	blocks, err := tc.GenerateBlocks(set1)
300	require.Nil(t, err)
301	// Sanity check
302	require.Equal(t, 2, len(blocks))
303	// blocks 0 (blockNum=2)
304	i := 0
305	require.Equal(t, 2, int(blocks[i].Block.EthBlockNum))
306	require.Equal(t, 3, len(blocks[i].AddedTokens))
307	require.Equal(t, 5, len(blocks[i].L1UserTxs))
308	require.Equal(t, 2, len(blocks[i].Batches))
309	require.Equal(t, 2, len(blocks[i].Batches[0].L1CoordinatorTxs))
310	// blocks 1 (blockNum=3)
311	i = 1
312	require.Equal(t, 3, int(blocks[i].Block.EthBlockNum))
313	require.Equal(t, 3, len(blocks[i].L1UserTxs))
314	require.Equal(t, 2, len(blocks[i].Batches))
315	require.Equal(t, 2, len(blocks[i].Batches[0].L2Txs))
316
317	// Generate extra required data
318	for _, block := range blocks {
319		for _, token := range block.AddedTokens {
320			consts := eth.ERC20Consts{
321				Name:     fmt.Sprintf("Token %d", token.TokenID),
322				Symbol:   fmt.Sprintf("TK%d", token.TokenID),
323				Decimals: 18,
324			}
325			tokenConsts[token.TokenID] = consts
326			client.CtlAddERC20(token.EthAddr, consts)
327		}
328	}
329
330	// Add block data to the smart contracts
331	for _, block := range blocks {
332		for _, token := range block.AddedTokens {
333			_, err := client.RollupAddTokenSimple(token.EthAddr, clientSetup.RollupVariables.FeeAddToken)
334			require.Nil(t, err)
335		}
336		for _, tx := range block.L1UserTxs {
337			client.CtlSetAddr(tx.FromEthAddr)
338			_, err := client.RollupL1UserTxERC20ETH(tx.FromBJJ, int64(tx.FromIdx), tx.LoadAmount, tx.Amount,
339				uint32(tx.TokenID), int64(tx.ToIdx))
340			require.Nil(t, err)
341		}
342		client.CtlSetAddr(bootCoordAddr)
343		feeIdxCoordinator := []common.Idx{}
344		if block.Block.EthBlockNum > 2 {
345			// After blockNum=2 we have some accounts, use them as
346			// coordinator owned to receive fees.
347			feeIdxCoordinator = []common.Idx{common.Idx(256), common.Idx(259)}
348		}
349		for _, batch := range block.Batches {
350			_, err := client.RollupForgeBatch(&eth.RollupForgeBatchArgs{
351				NewLastIdx:            batch.Batch.LastIdx,
352				NewStRoot:             batch.Batch.StateRoot,
353				NewExitRoot:           batch.Batch.ExitRoot,
354				L1CoordinatorTxs:      batch.L1CoordinatorTxs,
355				L1CoordinatorTxsAuths: [][]byte{}, // Intentionally empty
356				L2TxsData:             batch.L2Txs,
357				FeeIdxCoordinator:     feeIdxCoordinator,
358				// Circuit selector
359				VerifierIdx: 0, // Intentionally empty
360				L1Batch:     batch.L1Batch,
361				ProofA:      [2]*big.Int{},    // Intentionally empty
362				ProofB:      [2][2]*big.Int{}, // Intentionally empty
363				ProofC:      [2]*big.Int{},    // Intentionally empty
364			})
365			require.Nil(t, err)
366		}
367		// Mine block and sync
368		client.CtlMineBlock()
369	}
370
371	// Fill extra fields not generated by til in til block
372	openToForge := int64(0)
373	toForgeL1TxsNum := int64(0)
374	l1UserTxsLen := map[int64]int{} // ForgeL1TxsNum -> len(L1UserTxs)
375	for i := range blocks {
376		block := &blocks[i]
377		// Count number of L1UserTxs in each queue, to figure out later
378		// position of L1CoordinatorTxs and L2Txs
379		for j := range block.L1UserTxs {
380			tx := &block.L1UserTxs[j]
381			l1UserTxsLen[*tx.ToForgeL1TxsNum]++
382			if tx.Type == common.TxTypeForceExit {
383				forceExits[*tx.ToForgeL1TxsNum] = append(forceExits[*tx.ToForgeL1TxsNum],
384					common.ExitInfo{
385						AccountIdx: tx.FromIdx,
386						Balance:    tx.Amount,
387					})
388			}
389		}
390		for j := range block.Batches {
391			batch := &block.Batches[j]
392			if batch.L1Batch {
393				// Set BatchNum for forged L1UserTxs to til blocks
394				bn := batch.Batch.BatchNum
395				for k := range blocks {
396					block := &blocks[k]
397					for l := range block.L1UserTxs {
398						tx := &block.L1UserTxs[l]
399						if *tx.ToForgeL1TxsNum == openToForge {
400							tx.BatchNum = &bn
401						}
402					}
403				}
404				openToForge++
405			}
406
407			batch.Batch.EthBlockNum = block.Block.EthBlockNum
408			batch.Batch.ForgerAddr = bootCoordAddr // til doesn't fill the batch forger addr
409			if batch.L1Batch {
410				toForgeL1TxsNumCpy := toForgeL1TxsNum
411				batch.Batch.ForgeL1TxsNum = &toForgeL1TxsNumCpy // til doesn't fill the ForgeL1TxsNum
412				toForgeL1TxsNum++
413			}
414
415			batchNum := batch.Batch.BatchNum
416			for k := range batch.L1CoordinatorTxs {
417				tx := &batch.L1CoordinatorTxs[k]
418				tx.BatchNum = &batchNum
419				tx.EthBlockNum = batch.Batch.EthBlockNum
420			}
421		}
422	}
423
424	// Fill expected positions in L1CoordinatorTxs and L2Txs
425	for i := range blocks {
426		block := &blocks[i]
427		for j := range block.Batches {
428			batch := &block.Batches[j]
429			position := 0
430			if batch.L1Batch {
431				position = l1UserTxsLen[*batch.Batch.ForgeL1TxsNum]
432			}
433			for k := range batch.L1CoordinatorTxs {
434				tx := &batch.L1CoordinatorTxs[k]
435				tx.Position = position
436				position++
437				nTx, err := common.NewL1Tx(tx)
438				require.Nil(t, err)
439				*tx = *nTx
440			}
441			for k := range batch.L2Txs {
442				tx := &batch.L2Txs[k]
443				tx.Position = position
444				position++
445				nonces[tx.FromIdx]++
446				tx.Nonce = nonces[tx.FromIdx]
447				nTx, err := common.NewL2Tx(tx)
448				require.Nil(t, err)
449				*tx = *nTx
450			}
451		}
452	}
453
454	// Fill ExitTree (only AccountIdx and Balance)
455	for i := range blocks {
456		block := &blocks[i]
457		for j := range block.Batches {
458			batch := &block.Batches[j]
459			if batch.L1Batch {
460				for forgeL1TxsNum, exits := range forceExits {
461					if forgeL1TxsNum == *batch.Batch.ForgeL1TxsNum {
462						batch.ExitTree = append(batch.ExitTree, exits...)
463					}
464				}
465			}
466			for k := range batch.L2Txs {
467				tx := &batch.L2Txs[k]
468				if tx.Type == common.TxTypeExit {
469					batch.ExitTree = append(batch.ExitTree, common.ExitInfo{
470						AccountIdx: tx.FromIdx,
471						Balance:    tx.Amount,
472					})
473				}
474			}
475		}
476	}
477
478	//
479	// Sync to synchronize the current state from the test smart contracts,
480	// and check the outcome
481	//
482
483	// Block 2
484
485	syncBlock, discards, err = s.Sync2(ctx, nil)
486	require.Nil(t, err)
487	require.Nil(t, discards)
488	require.NotNil(t, syncBlock)
489	assert.Equal(t, int64(2), syncBlock.Block.EthBlockNum)
490
491	checkSyncBlock(t, s, 2, &blocks[0], syncBlock)
492
493	// Block 3
494
495	syncBlock, discards, err = s.Sync2(ctx, nil)
496	require.Nil(t, err)
497	require.Nil(t, discards)
498	require.NotNil(t, syncBlock)
499	assert.Equal(t, int64(3), syncBlock.Block.EthBlockNum)
500
501	checkSyncBlock(t, s, 3, &blocks[1], syncBlock)
502
503	// TODO: Reorg will be properly tested once we have the mock ethClient implemented
504	/*
505		// Force a Reorg
506		lastSavedBlock, err := historyDB.GetLastBlock()
507		require.Nil(t, err)
508
509		lastSavedBlock.EthBlockNum++
510		err = historyDB.AddBlock(lastSavedBlock)
511		require.Nil(t, err)
512
513		lastSavedBlock.EthBlockNum++
514		err = historyDB.AddBlock(lastSavedBlock)
515		require.Nil(t, err)
516
517		log.Debugf("Wait for the blockchain to generate some blocks...")
518		time.Sleep(40 * time.Second)
519
520
521		err = s.Sync()
522		require.Nil(t, err)
523	*/
524}