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
 27
 28type timer struct {
 29	time int64
 30}
 31
 32func (t *timer) Time() int64 {
 33	currentTime := t.time
 34	t.time++
 35	return currentTime
 36}
 37
 38// Check Sync output and HistoryDB state against expected values generated by
 39// til
 40func checkSyncBlock(t *testing.T, s *Synchronizer, blockNum int, block, syncBlock *common.BlockData) {
 41	// Check Blocks
 42	dbBlocks, err := s.historyDB.GetAllBlocks()
 43	require.Nil(t, err)
 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.AddedTokens), len(syncBlock.AddedTokens))
 51	dbTokens, err := s.historyDB.GetAllTokens()
 52	require.Nil(t, err)
 53	for i, token := range block.AddedTokens {
 54		dbToken := dbTokens[i]
 55		syncToken := syncBlock.AddedTokens[i]
 56
 57		assert.Equal(t, block.Block.EthBlockNum, syncToken.EthBlockNum)
 58		assert.Equal(t, token.TokenID, syncToken.TokenID)
 59		assert.Equal(t, token.EthAddr, syncToken.EthAddr)
 60		tokenConst := tokenConsts[token.TokenID]
 61		assert.Equal(t, tokenConst.Name, syncToken.Name)
 62		assert.Equal(t, tokenConst.Symbol, syncToken.Symbol)
 63		assert.Equal(t, tokenConst.Decimals, syncToken.Decimals)
 64
 65		var tokenCpy historydb.TokenWithUSD
 66		//nolint:gosec
 67		require.Nil(t, copier.Copy(&tokenCpy, &token))      // copy common.Token to historydb.TokenWithUSD
 68		require.Nil(t, copier.Copy(&tokenCpy, &tokenConst)) // copy common.Token to historydb.TokenWithUSD
 69		tokenCpy.ItemID = dbToken.ItemID                    // we don't care about ItemID
 70		assert.Equal(t, tokenCpy, dbToken)
 71	}
 72
 73	// Check L1UserTxs
 74	assert.Equal(t, len(block.L1UserTxs), len(syncBlock.L1UserTxs))
 75	dbL1UserTxs, err := s.historyDB.GetAllL1UserTxs()
 76	require.Nil(t, err)
 77	// Ignore BatchNum in syncBlock.L1UserTxs because this value is set by the HistoryDB
 78	for i := range syncBlock.L1UserTxs {
 79		syncBlock.L1UserTxs[i].BatchNum = block.L1UserTxs[i].BatchNum
 80	}
 81	assert.Equal(t, block.L1UserTxs, syncBlock.L1UserTxs)
 82	for _, tx := range block.L1UserTxs {
 83		var dbTx *common.L1Tx
 84		// Find tx in DB output
 85		for _, _dbTx := range dbL1UserTxs {
 86			if *tx.ToForgeL1TxsNum == *_dbTx.ToForgeL1TxsNum &&
 87				tx.Position == _dbTx.Position {
 88				dbTx = new(common.L1Tx)
 89				*dbTx = _dbTx
 90				break
 91			}
 92		}
 93		assert.Equal(t, &tx, dbTx) //nolint:gosec
 94	}
 95
 96	// Check Batches
 97	assert.Equal(t, len(block.Batches), len(syncBlock.Batches))
 98	dbBatches, err := s.historyDB.GetAllBatches()
 99	require.Nil(t, err)
100
101	dbL1CoordinatorTxs, err := s.historyDB.GetAllL1CoordinatorTxs()
102	require.Nil(t, err)
103	// fmt.Printf("DBG dbL1CoordinatorTxs: %+v\n", dbL1CoordinatorTxs)
104	dbL2Txs, err := s.historyDB.GetAllL2Txs()
105	require.Nil(t, err)
106	// fmt.Printf("DBG dbL2Txs: %+v\n", dbL2Txs)
107	dbExits, err := s.historyDB.GetAllExits()
108	require.Nil(t, err)
109	// dbL1CoordinatorTxs := []common.L1Tx{}
110	for i, batch := range block.Batches {
111		var dbBatch *common.Batch
112		// Find batch in DB output
113		for _, _dbBatch := range dbBatches {
114			if batch.Batch.BatchNum == _dbBatch.BatchNum {
115				dbBatch = new(common.Batch)
116				*dbBatch = _dbBatch
117				break
118			}
119		}
120		syncBatch := syncBlock.Batches[i]
121
122		// We don't care about TotalFeesUSD.  Use the syncBatch that
123		// has a TotalFeesUSD inserted by the HistoryDB
124		batch.Batch.TotalFeesUSD = syncBatch.Batch.TotalFeesUSD
125		batch.CreatedAccounts = syncBatch.CreatedAccounts // til doesn't output 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	err = historyDB.Reorg(-1)
220	assert.Nil(t, err)
221
222	// Init eth client
223	var timer timer
224	clientSetup := test.NewClientSetupExample()
225	bootCoordAddr := clientSetup.AuctionVariables.BootCoordinator
226	client := test.NewClient(true, &timer, &ethCommon.Address{}, clientSetup)
227
228	// Create Synchronizer
229	s, err := NewSynchronizer(client, historyDB, stateDB)
230	require.Nil(t, err)
231
232	//
233	// First Sync from an initial state
234	//
235
236	// Test Sync for rollup genesis block
237	syncBlock, discards, err := s.Sync2(ctx, nil)
238	require.Nil(t, err)
239	require.Nil(t, discards)
240	require.NotNil(t, syncBlock)
241	assert.Equal(t, int64(1), syncBlock.Block.EthBlockNum)
242	dbBlocks, err := s.historyDB.GetAllBlocks()
243	require.Nil(t, err)
244	assert.Equal(t, 1, len(dbBlocks))
245	assert.Equal(t, int64(1), dbBlocks[0].EthBlockNum)
246
247	// Sync again and expect no new blocks
248	syncBlock, discards, err = s.Sync2(ctx, nil)
249	require.Nil(t, err)
250	require.Nil(t, discards)
251	require.Nil(t, syncBlock)
252
253	//
254	// Generate blockchain and smart contract data, and fill the test smart contracts
255	//
256
257	// TODO: Test CreateAccountDepositTransfer
258	// TODO: Test ForceTransfer
259	// TODO: Test ForceExit
260
261	// Generate blockchain data with til
262	set1 := `
263		Type: Blockchain
264
265		AddToken(1)
266		AddToken(2)
267		AddToken(3)
268
269		CreateAccountDeposit(1) C: 20 // Idx=256+2=258
270		CreateAccountDeposit(2) A: 20 // Idx=256+3=259
271		CreateAccountDeposit(1) D: 5  // Idx=256+4=260
272		CreateAccountDeposit(2) B: 5  // Idx=256+5=261
273		CreateAccountDeposit(2) C: 5  // Idx=256+6=262
274
275		CreateAccountCoordinator(1) A // Idx=256+0=256
276		CreateAccountCoordinator(1) B // Idx=256+1=257
277
278		> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs
279		> batchL1 // forge defined L1UserTxs, freeze L1UserTxs{nil}
280		> block // blockNum=2
281
282		Transfer(1) C-A: 10 (10)
283		Exit(1) D: 3 (5)
284
285		> batch
286		> block // blockNum=3
287
288	`
289	tc := til.NewContext(eth.RollupConstMaxL1UserTx)
290	blocks, err := tc.GenerateBlocks(set1)
291	require.Nil(t, err)
292	// Sanity check
293	require.Equal(t, 2, len(blocks))
294	// blocks 0 (blockNum=2)
295	i := 0
296	require.Equal(t, 2, int(blocks[i].Block.EthBlockNum))
297	require.Equal(t, 3, len(blocks[i].AddedTokens))
298	require.Equal(t, 5, len(blocks[i].L1UserTxs))
299	require.Equal(t, 2, len(blocks[i].Batches))
300	require.Equal(t, 2, len(blocks[i].Batches[0].L1CoordinatorTxs))
301	// blocks 1 (blockNum=3)
302	i = 1
303	require.Equal(t, 3, int(blocks[i].Block.EthBlockNum))
304	require.Equal(t, 1, len(blocks[i].Batches))
305	require.Equal(t, 2, len(blocks[i].Batches[0].L2Txs))
306
307	// Generate extra required data
308	for _, block := range blocks {
309		for _, token := range block.AddedTokens {
310			consts := eth.ERC20Consts{
311				Name:     fmt.Sprintf("Token %d", token.TokenID),
312				Symbol:   fmt.Sprintf("TK%d", token.TokenID),
313				Decimals: 18,
314			}
315			tokenConsts[token.TokenID] = consts
316			client.CtlAddERC20(token.EthAddr, consts)
317		}
318	}
319
320	// Add block data to the smart contracts
321	for _, block := range blocks {
322		for _, token := range block.AddedTokens {
323			_, err := client.RollupAddTokenSimple(token.EthAddr, clientSetup.RollupVariables.FeeAddToken)
324			require.Nil(t, err)
325		}
326		for _, tx := range block.L1UserTxs {
327			client.CtlSetAddr(tx.FromEthAddr)
328			_, err := client.RollupL1UserTxERC20ETH(tx.FromBJJ, int64(tx.FromIdx), tx.LoadAmount, tx.Amount,
329				uint32(tx.TokenID), int64(tx.ToIdx))
330			require.Nil(t, err)
331		}
332		client.CtlSetAddr(bootCoordAddr)
333		for _, batch := range block.Batches {
334			_, err := client.RollupForgeBatch(&eth.RollupForgeBatchArgs{
335				NewLastIdx:            batch.Batch.LastIdx,
336				NewStRoot:             batch.Batch.StateRoot,
337				NewExitRoot:           batch.Batch.ExitRoot,
338				L1CoordinatorTxs:      batch.L1CoordinatorTxs,
339				L1CoordinatorTxsAuths: [][]byte{}, // Intentionally empty
340				L2TxsData:             batch.L2Txs,
341				FeeIdxCoordinator:     []common.Idx{}, // TODO
342				// Circuit selector
343				VerifierIdx: 0, // Intentionally empty
344				L1Batch:     batch.L1Batch,
345				ProofA:      [2]*big.Int{},    // Intentionally empty
346				ProofB:      [2][2]*big.Int{}, // Intentionally empty
347				ProofC:      [2]*big.Int{},    // Intentionally empty
348			})
349			require.Nil(t, err)
350		}
351		// Mine block and sync
352		client.CtlMineBlock()
353	}
354
355	// Fill extra fields not generated by til in til block
356	openToForge := int64(0)
357	toForgeL1TxsNum := int64(0)
358	l1UserTxsLen := map[int64]int{} // ForgeL1TxsNum -> len(L1UserTxs)
359	for i := range blocks {
360		block := &blocks[i]
361		// Count number of L1UserTxs in each queue, to figure out later
362		// position of L1CoordinatorTxs and L2Txs
363		for j := range block.L1UserTxs {
364			tx := &block.L1UserTxs[j]
365			l1UserTxsLen[*tx.ToForgeL1TxsNum]++
366			if tx.Type == common.TxTypeForceExit {
367				forceExits[*tx.ToForgeL1TxsNum] = append(forceExits[*tx.ToForgeL1TxsNum],
368					common.ExitInfo{
369						AccountIdx: tx.FromIdx,
370						Balance:    tx.Amount,
371					})
372			}
373		}
374		for j := range block.Batches {
375			batch := &block.Batches[j]
376			if batch.L1Batch {
377				// Set BatchNum for forged L1UserTxs to til blocks
378				bn := batch.Batch.BatchNum
379				for k := range blocks {
380					block := &blocks[k]
381					for l := range block.L1UserTxs {
382						tx := &block.L1UserTxs[l]
383						if *tx.ToForgeL1TxsNum == openToForge {
384							tx.BatchNum = &bn
385						}
386					}
387				}
388				openToForge++
389			}
390
391			batch.Batch.EthBlockNum = block.Block.EthBlockNum
392			batch.Batch.ForgerAddr = bootCoordAddr // til doesn't fill the batch forger addr
393			if batch.L1Batch {
394				toForgeL1TxsNumCpy := toForgeL1TxsNum
395				batch.Batch.ForgeL1TxsNum = &toForgeL1TxsNumCpy // til doesn't fill the ForgeL1TxsNum
396				toForgeL1TxsNum++
397			}
398
399			batchNum := batch.Batch.BatchNum
400			for k := range batch.L1CoordinatorTxs {
401				tx := &batch.L1CoordinatorTxs[k]
402				tx.BatchNum = &batchNum
403				tx.EthBlockNum = batch.Batch.EthBlockNum
404			}
405		}
406	}
407
408	// Fill expected positions in L1CoordinatorTxs and L2Txs
409	for i := range blocks {
410		block := &blocks[i]
411		for j := range block.Batches {
412			batch := &block.Batches[j]
413			position := 0
414			if batch.L1Batch {
415				position = l1UserTxsLen[*batch.Batch.ForgeL1TxsNum]
416			}
417			for k := range batch.L1CoordinatorTxs {
418				tx := &batch.L1CoordinatorTxs[k]
419				tx.Position = position
420				position++
421				nTx, err := common.NewL1Tx(tx)
422				require.Nil(t, err)
423				*tx = *nTx
424			}
425			for k := range batch.L2Txs {
426				tx := &batch.L2Txs[k]
427				tx.Position = position
428				position++
429				nTx, err := common.NewL2Tx(tx)
430				require.Nil(t, err)
431				*tx = *nTx
432			}
433		}
434	}
435
436	// Fill ExitTree (only AccountIdx and Balance)
437	for i := range blocks {
438		block := &blocks[i]
439		for j := range block.Batches {
440			batch := &block.Batches[j]
441			if batch.L1Batch {
442				for forgeL1TxsNum, exits := range forceExits {
443					if forgeL1TxsNum == *batch.Batch.ForgeL1TxsNum {
444						batch.ExitTree = append(batch.ExitTree, exits...)
445					}
446				}
447			}
448			for k := range batch.L2Txs {
449				tx := &batch.L2Txs[k]
450				if tx.Type == common.TxTypeExit {
451					batch.ExitTree = append(batch.ExitTree, common.ExitInfo{
452						AccountIdx: tx.FromIdx,
453						Balance:    tx.Amount,
454					})
455				}
456			}
457		}
458	}
459
460	//
461	// Sync to synchronize the current state from the test smart contracts,
462	// and check the outcome
463	//
464
465	// Block 2
466
467	syncBlock, discards, err = s.Sync2(ctx, nil)
468	require.Nil(t, err)
469	require.Nil(t, discards)
470	require.NotNil(t, syncBlock)
471	assert.Equal(t, int64(2), syncBlock.Block.EthBlockNum)
472
473	checkSyncBlock(t, s, 2, &blocks[0], syncBlock)
474
475	// Block 3
476
477	syncBlock, discards, err = s.Sync2(ctx, nil)
478	require.Nil(t, err)
479	require.Nil(t, discards)
480	require.NotNil(t, syncBlock)
481	assert.Equal(t, int64(3), syncBlock.Block.EthBlockNum)
482
483	checkSyncBlock(t, s, 3, &blocks[1], syncBlock)
484
485	// TODO: Reorg will be properly tested once we have the mock ethClient implemented
486	/*
487		// Force a Reorg
488		lastSavedBlock, err := historyDB.GetLastBlock()
489		require.Nil(t, err)
490
491		lastSavedBlock.EthBlockNum++
492		err = historyDB.AddBlock(lastSavedBlock)
493		require.Nil(t, err)
494
495		lastSavedBlock.EthBlockNum++
496		err = historyDB.AddBlock(lastSavedBlock)
497		require.Nil(t, err)
498
499		log.Debugf("Wait for the blockchain to generate some blocks...")
500		time.Sleep(40 * time.Second)
501
502
503		err = s.Sync()
504		require.Nil(t, err)
505	*/
506}