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		ForceTransfer(1) A-D: 100
291
292		Transfer(1) C-A: 100 (200)
293		Exit(1) D: 30 (200)
294
295		> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{2}
296		> batchL1 // forge L1UserTxs{2}, freeze defined L1UserTxs{nil}
297		> block // blockNum=3
298	`
299	tc := til.NewContext(common.RollupConstMaxL1UserTx)
300	tilCfgExtra := til.ConfigExtra{
301		BootCoordAddr: bootCoordAddr,
302		CoordUser:     "A",
303	}
304	blocks, err := tc.GenerateBlocks(set1)
305	require.Nil(t, err)
306	// Sanity check
307	require.Equal(t, 2, len(blocks))
308	// blocks 0 (blockNum=2)
309	i := 0
310	require.Equal(t, 2, int(blocks[i].Block.EthBlockNum))
311	require.Equal(t, 3, len(blocks[i].Rollup.AddedTokens))
312	require.Equal(t, 5, len(blocks[i].Rollup.L1UserTxs))
313	require.Equal(t, 2, len(blocks[i].Rollup.Batches))
314	require.Equal(t, 2, len(blocks[i].Rollup.Batches[0].L1CoordinatorTxs))
315	// blocks 1 (blockNum=3)
316	i = 1
317	require.Equal(t, 3, int(blocks[i].Block.EthBlockNum))
318	require.Equal(t, 3, len(blocks[i].Rollup.L1UserTxs))
319	require.Equal(t, 2, len(blocks[i].Rollup.Batches))
320	require.Equal(t, 2, len(blocks[i].Rollup.Batches[0].L2Txs))
321
322	// Generate extra required data
323	for _, block := range blocks {
324		for _, token := range block.Rollup.AddedTokens {
325			consts := eth.ERC20Consts{
326				Name:     fmt.Sprintf("Token %d", token.TokenID),
327				Symbol:   fmt.Sprintf("TK%d", token.TokenID),
328				Decimals: 18,
329			}
330			tokenConsts[token.TokenID] = consts
331			client.CtlAddERC20(token.EthAddr, consts)
332		}
333	}
334
335	err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
336	assert.Nil(t, err)
337
338	// Add block data to the smart contracts
339	for _, block := range blocks {
340		for _, token := range block.Rollup.AddedTokens {
341			_, err := client.RollupAddTokenSimple(token.EthAddr, clientSetup.RollupVariables.FeeAddToken)
342			require.Nil(t, err)
343		}
344		for _, tx := range block.Rollup.L1UserTxs {
345			client.CtlSetAddr(tx.FromEthAddr)
346			_, err := client.RollupL1UserTxERC20ETH(tx.FromBJJ, int64(tx.FromIdx), tx.LoadAmount, tx.Amount,
347				uint32(tx.TokenID), int64(tx.ToIdx))
348			require.Nil(t, err)
349		}
350		client.CtlSetAddr(bootCoordAddr)
351		// feeIdxCoordinator := []common.Idx{}
352		// if block.Block.EthBlockNum > 2 {
353		// 	// After blockNum=2 we have some accounts, use them as
354		// 	// coordinator owned to receive fees.
355		// 	feeIdxCoordinator = []common.Idx{common.Idx(256), common.Idx(259)}
356		// }
357		for _, batch := range block.Rollup.Batches {
358			_, err := client.RollupForgeBatch(&eth.RollupForgeBatchArgs{
359				NewLastIdx:            batch.Batch.LastIdx,
360				NewStRoot:             batch.Batch.StateRoot,
361				NewExitRoot:           batch.Batch.ExitRoot,
362				L1CoordinatorTxs:      batch.L1CoordinatorTxs,
363				L1CoordinatorTxsAuths: [][]byte{}, // Intentionally empty
364				L2TxsData:             batch.L2Txs,
365				FeeIdxCoordinator:     batch.Batch.FeeIdxsCoordinator,
366				// Circuit selector
367				VerifierIdx: 0, // Intentionally empty
368				L1Batch:     batch.L1Batch,
369				ProofA:      [2]*big.Int{},    // Intentionally empty
370				ProofB:      [2][2]*big.Int{}, // Intentionally empty
371				ProofC:      [2]*big.Int{},    // Intentionally empty
372			})
373			require.Nil(t, err)
374		}
375		// Mine block and sync
376		client.CtlMineBlock()
377	}
378
379	//
380	// Sync to synchronize the current state from the test smart contracts,
381	// and check the outcome
382	//
383
384	// Block 2
385
386	syncBlock, discards, err = s.Sync2(ctx, nil)
387	require.Nil(t, err)
388	require.Nil(t, discards)
389	require.NotNil(t, syncBlock)
390	assert.Equal(t, int64(2), syncBlock.Block.EthBlockNum)
391
392	checkSyncBlock(t, s, 2, &blocks[0], syncBlock)
393
394	// Block 3
395
396	syncBlock, discards, err = s.Sync2(ctx, nil)
397	require.Nil(t, err)
398	require.Nil(t, discards)
399	require.NotNil(t, syncBlock)
400	assert.Equal(t, int64(3), syncBlock.Block.EthBlockNum)
401
402	checkSyncBlock(t, s, 3, &blocks[1], syncBlock)
403
404	// TODO: Reorg will be properly tested once we have the mock ethClient implemented
405	/*
406		// Force a Reorg
407		lastSavedBlock, err := historyDB.GetLastBlock()
408		require.Nil(t, err)
409
410		lastSavedBlock.EthBlockNum++
411		err = historyDB.AddBlock(lastSavedBlock)
412		require.Nil(t, err)
413
414		lastSavedBlock.EthBlockNum++
415		err = historyDB.AddBlock(lastSavedBlock)
416		require.Nil(t, err)
417
418		log.Debugf("Wait for the blockchain to generate some blocks...")
419		time.Sleep(40 * time.Second)
420
421
422		err = s.Sync()
423		require.Nil(t, err)
424	*/
425}