arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/db/historydb/historydb_test.go
db/historydb/historydb_test.goRaw
  1package historydb
  2
  3import (
  4	"fmt"
  5	"math/big"
  6	"os"
  7	"testing"
  8	"time"
  9
 10	ethCommon "github.com/ethereum/go-ethereum/common"
 11	"github.com/hermeznetwork/hermez-node/common"
 12	"github.com/hermeznetwork/hermez-node/test"
 13	"github.com/iden3/go-iden3-crypto/babyjub"
 14	"github.com/stretchr/testify/assert"
 15)
 16
 17var historyDB *HistoryDB
 18
 19// In order to run the test you need to run a Posgres DB with
 20// a database named "history" that is accessible by
 21// user: "hermez"
 22// pass: set it using the env var POSTGRES_PASS
 23// This can be achieved by running: POSTGRES_PASS=your_strong_pass && sudo docker run --rm --name hermez-db-test -p 5432:5432 -e POSTGRES_DB=history -e POSTGRES_USER=hermez -e POSTGRES_PASSWORD=$POSTGRES_PASS -d postgres && sleep 2s && sudo docker exec -it hermez-db-test psql -a history -U hermez -c "CREATE DATABASE l2;"
 24// After running the test you can stop the container by running: sudo docker kill hermez-db-test
 25// If you already did that for the L2DB you don't have to do it again
 26
 27func TestMain(m *testing.M) {
 28	// init DB
 29	var err error
 30	pass := os.Getenv("POSTGRES_PASS")
 31	historyDB, err = NewHistoryDB(5432, "localhost", "hermez", pass, "history")
 32	if err != nil {
 33		panic(err)
 34	}
 35	// Run tests
 36	result := m.Run()
 37	// Close DB
 38	if err := historyDB.Close(); err != nil {
 39		fmt.Println("Error closing the history DB:", err)
 40	}
 41	os.Exit(result)
 42}
 43
 44func TestBlocks(t *testing.T) {
 45	var fromBlock, toBlock int64
 46	fromBlock = 1
 47	toBlock = 5
 48	// Delete peviously created rows (clean previous test execs)
 49	assert.NoError(t, historyDB.Reorg(fromBlock-1))
 50	// Generate fake blocks
 51	blocks := test.GenBlocks(fromBlock, toBlock)
 52	// Insert blocks into DB
 53	for i := 0; i < len(blocks); i++ {
 54		err := historyDB.AddBlock(&blocks[i])
 55		assert.NoError(t, err)
 56	}
 57	// Get all blocks from DB
 58	fetchedBlocks, err := historyDB.GetBlocks(fromBlock, toBlock)
 59	assert.Equal(t, len(blocks), len(fetchedBlocks))
 60	// Compare generated vs getted blocks
 61	assert.NoError(t, err)
 62	for i, fetchedBlock := range fetchedBlocks {
 63		assertEqualBlock(t, &blocks[i], fetchedBlock)
 64	}
 65	// Get blocks from the DB one by one
 66	for i := fromBlock; i < toBlock; i++ {
 67		fetchedBlock, err := historyDB.GetBlock(i)
 68		assert.NoError(t, err)
 69		assertEqualBlock(t, &blocks[i-1], fetchedBlock)
 70	}
 71	// Get last block
 72	lastBlock, err := historyDB.GetLastBlock()
 73	assert.NoError(t, err)
 74	assertEqualBlock(t, &blocks[len(blocks)-1], lastBlock)
 75}
 76
 77func assertEqualBlock(t *testing.T, expected *common.Block, actual *common.Block) {
 78	assert.Equal(t, expected.EthBlockNum, actual.EthBlockNum)
 79	assert.Equal(t, expected.Hash, actual.Hash)
 80	assert.Equal(t, expected.Timestamp.Unix(), actual.Timestamp.Unix())
 81}
 82
 83func TestBatches(t *testing.T) {
 84	const fromBlock int64 = 1
 85	const toBlock int64 = 3
 86	// Prepare blocks in the DB
 87	blocks := setTestBlocks(fromBlock, toBlock)
 88	// Generate fake batches
 89	const nBatches = 9
 90	batches := test.GenBatches(nBatches, blocks)
 91	// Test GetLastL1TxsNum with no batches
 92	fetchedLastL1TxsNum, err := historyDB.GetLastL1TxsNum()
 93	assert.NoError(t, err)
 94	assert.Nil(t, fetchedLastL1TxsNum)
 95	// Add batches to the DB
 96	err = historyDB.AddBatches(batches)
 97	assert.NoError(t, err)
 98	// Get batches from the DB
 99	fetchedBatches, err := historyDB.GetBatches(0, common.BatchNum(nBatches))
100	assert.NoError(t, err)
101	for i, fetchedBatch := range fetchedBatches {
102		assert.Equal(t, batches[i], *fetchedBatch)
103	}
104	// Test GetLastBatchNum
105	fetchedLastBatchNum, err := historyDB.GetLastBatchNum()
106	assert.NoError(t, err)
107	assert.Equal(t, batches[len(batches)-1].BatchNum, fetchedLastBatchNum)
108	// Test GetLastL1TxsNum
109	fetchedLastL1TxsNum, err = historyDB.GetLastL1TxsNum()
110	assert.NoError(t, err)
111	assert.Equal(t, batches[nBatches-1].ForgeL1TxsNum, *fetchedLastL1TxsNum)
112}
113
114func TestBids(t *testing.T) {
115	const fromBlock int64 = 1
116	const toBlock int64 = 5
117	// Prepare blocks in the DB
118	blocks := setTestBlocks(fromBlock, toBlock)
119	// Generate fake coordinators
120	const nCoords = 5
121	coords := test.GenCoordinators(nCoords, blocks)
122	err := historyDB.AddCoordinators(coords)
123	assert.NoError(t, err)
124	// Generate fake bids
125	const nBids = 20
126	bids := test.GenBids(nBids, blocks, coords)
127	err = historyDB.AddBids(bids)
128	assert.NoError(t, err)
129	// Fetch bids
130	fetchedBids, err := historyDB.GetBids()
131	assert.NoError(t, err)
132	// Compare fetched bids vs generated bids
133	for i, bid := range fetchedBids {
134		assert.Equal(t, bids[i], *bid)
135	}
136}
137
138func TestTokens(t *testing.T) {
139	const fromBlock int64 = 1
140	const toBlock int64 = 5
141	// Prepare blocks in the DB
142	blocks := setTestBlocks(fromBlock, toBlock)
143	// Generate fake tokens
144	const nTokens = 5
145	tokens := test.GenTokens(nTokens, blocks)
146	err := historyDB.AddTokens(tokens)
147	assert.NoError(t, err)
148	// Update price of generated tokens without price
149	for i := 0; i < len(tokens); i++ {
150		if tokens[i].USD == 0 {
151			value := 3.33 + float64(i)
152			tokens[i].USD = value
153			err := historyDB.UpdateTokenValue(tokens[i].TokenID, value)
154			assert.NoError(t, err)
155		}
156	}
157	// Fetch tokens
158	fetchedTokens, err := historyDB.GetTokens()
159	assert.NoError(t, err)
160	// Compare fetched tokens vs generated tokens
161	// All the tokens should have USDUpdate setted by the DB trigger
162	for i, token := range fetchedTokens {
163		assert.Equal(t, tokens[i].TokenID, token.TokenID)
164		assert.Equal(t, tokens[i].EthBlockNum, token.EthBlockNum)
165		assert.Equal(t, tokens[i].EthAddr, token.EthAddr)
166		assert.Equal(t, tokens[i].Name, token.Name)
167		assert.Equal(t, tokens[i].Symbol, token.Symbol)
168		assert.Equal(t, tokens[i].USD, token.USD)
169		assert.Greater(t, int64(1*time.Second), int64(time.Since(token.USDUpdate)))
170	}
171}
172
173func TestAccounts(t *testing.T) {
174	const fromBlock int64 = 1
175	const toBlock int64 = 5
176	// Prepare blocks in the DB
177	blocks := setTestBlocks(fromBlock, toBlock)
178	// Generate fake tokens
179	const nTokens = 5
180	tokens := test.GenTokens(nTokens, blocks)
181	err := historyDB.AddTokens(tokens)
182	assert.NoError(t, err)
183	// Generate fake batches
184	const nBatches = 10
185	batches := test.GenBatches(nBatches, blocks)
186	err = historyDB.AddBatches(batches)
187	assert.NoError(t, err)
188	// Generate fake accounts
189	const nAccounts = 3
190	accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
191	err = historyDB.AddAccounts(accs)
192	assert.NoError(t, err)
193	// Fetch accounts
194	fetchedAccs, err := historyDB.GetAccounts()
195	assert.NoError(t, err)
196	// Compare fetched accounts vs generated accounts
197	for i, acc := range fetchedAccs {
198		assert.Equal(t, accs[i], *acc)
199	}
200}
201
202func TestTxs(t *testing.T) {
203	const fromBlock int64 = 1
204	const toBlock int64 = 5
205	// Prepare blocks in the DB
206	blocks := setTestBlocks(fromBlock, toBlock)
207	// Generate fake tokens
208	const nTokens = 5
209	const tokenValue = 1.23456
210	tokens := test.GenTokens(nTokens, blocks)
211	for i := 0; i < len(tokens); i++ {
212		tokens[i].USD = tokenValue
213	}
214	err := historyDB.AddTokens(tokens)
215	assert.NoError(t, err)
216	// Generate fake batches
217	const nBatches = 10
218	batches := test.GenBatches(nBatches, blocks)
219	err = historyDB.AddBatches(batches)
220	assert.NoError(t, err)
221	// Generate fake accounts
222	const nAccounts = 3
223	accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
224	err = historyDB.AddAccounts(accs)
225	assert.NoError(t, err)
226	// Generate fake L1 txs
227	const nL1s = 30
228	_, l1txs := test.GenL1Txs(0, nL1s, 0, nil, accs, tokens, blocks, batches)
229	err = historyDB.AddL1Txs(l1txs)
230	assert.NoError(t, err)
231	// Generate fake L2 txs
232	const nL2s = 20
233	_, l2txs := test.GenL2Txs(0, nL2s, 0, nil, accs, tokens, blocks, batches)
234	err = historyDB.AddL2Txs(l2txs)
235	assert.NoError(t, err)
236	// Compare fetched txs vs generated txs.
237	for i := 0; i < len(l1txs); i++ {
238		tx := l1txs[i].Tx()
239		fetchedTx, err := historyDB.GetTx(tx.TxID)
240		assert.NoError(t, err)
241		tx.USD = tokenValue * tx.AmountFloat
242		if fetchedTx.USD > tx.USD {
243			assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
244		} else {
245			assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
246		}
247		tx.LoadAmountUSD = tokenValue * tx.LoadAmountFloat
248		if fetchedTx.LoadAmountUSD > tx.LoadAmountUSD {
249			assert.Less(t, 0.999, tx.LoadAmountUSD/fetchedTx.LoadAmountUSD)
250		} else {
251			assert.Less(t, 0.999, fetchedTx.LoadAmountUSD/tx.LoadAmountUSD)
252		}
253		tx.LoadAmountUSD = 0
254		tx.USD = 0
255		fetchedTx.LoadAmountUSD = 0
256		fetchedTx.USD = 0
257		assert.Equal(t, tx, fetchedTx)
258	}
259	for i := 0; i < len(l2txs); i++ {
260		tx := l2txs[i].Tx()
261		fetchedTx, err := historyDB.GetTx(tx.TxID)
262		assert.NoError(t, err)
263		tx.USD = tokenValue * tx.AmountFloat
264		if fetchedTx.USD > tx.USD {
265			assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
266		} else {
267			assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
268		}
269		tx.FeeUSD = tx.USD * tx.Fee.Percentage()
270		if fetchedTx.FeeUSD > tx.FeeUSD {
271			assert.Less(t, 0.999, tx.FeeUSD/fetchedTx.FeeUSD)
272		} else if fetchedTx.FeeUSD < tx.FeeUSD {
273			assert.Less(t, 0.999, fetchedTx.FeeUSD/tx.FeeUSD)
274		}
275		tx.FeeUSD = 0
276		tx.USD = 0
277		fetchedTx.FeeUSD = 0
278		fetchedTx.USD = 0
279		assert.Equal(t, tx, fetchedTx)
280	}
281	// Test trigger: L1 integrity
282	// from_eth_addr can't be null
283	l1txs[0].FromEthAddr = ethCommon.Address{}
284	err = historyDB.AddL1Txs(l1txs)
285	assert.Error(t, err)
286	l1txs[0].FromEthAddr = ethCommon.BigToAddress(big.NewInt(int64(5)))
287	// from_bjj can't be null
288	l1txs[0].FromBJJ = nil
289	err = historyDB.AddL1Txs(l1txs)
290	assert.Error(t, err)
291	privK := babyjub.NewRandPrivKey()
292	l1txs[0].FromBJJ = privK.Public()
293	// load_amount can't be null
294	l1txs[0].LoadAmount = nil
295	err = historyDB.AddL1Txs(l1txs)
296	assert.Error(t, err)
297	// Test trigger: L2 integrity
298	// batch_num can't be null
299	l2txs[0].BatchNum = 0
300	err = historyDB.AddL2Txs(l2txs)
301	assert.Error(t, err)
302	l2txs[0].BatchNum = 1
303	// nonce can't be null
304	l2txs[0].Nonce = 0
305	err = historyDB.AddL2Txs(l2txs)
306	assert.Error(t, err)
307	// Test helper functions for Synchronizer
308	txs, err := historyDB.GetL1UserTxs(2)
309	assert.NoError(t, err)
310	assert.NotZero(t, len(txs))
311	position, err := historyDB.GetLastTxsPosition(2)
312	assert.NoError(t, err)
313	assert.Equal(t, 22, position)
314	// Test Update L1 TX Batch_num
315	assert.Equal(t, common.BatchNum(0), txs[0].BatchNum)
316	txs[0].BatchNum = common.BatchNum(1)
317	// err = historyDB.UpdateTxsBatchNum(txs)
318	err = historyDB.SetBatchNumL1UserTxs(2, 1)
319	assert.NoError(t, err)
320	txs, err = historyDB.GetL1UserTxs(2)
321	assert.NoError(t, err)
322	assert.NotZero(t, len(txs))
323	assert.Equal(t, common.BatchNum(1), txs[0].BatchNum)
324}
325
326func TestExitTree(t *testing.T) {
327	nBatches := 17
328	blocks := setTestBlocks(0, 10)
329	batches := test.GenBatches(nBatches, blocks)
330	err := historyDB.AddBatches(batches)
331	assert.NoError(t, err)
332
333	exitTree := test.GenExitTree(nBatches)
334	err = historyDB.AddExitTree(exitTree)
335	assert.NoError(t, err)
336}
337
338// setTestBlocks WARNING: this will delete the blocks and recreate them
339func setTestBlocks(from, to int64) []common.Block {
340	if err := cleanHistoryDB(); err != nil {
341		panic(err)
342	}
343	blocks := test.GenBlocks(from, to)
344	if err := historyDB.AddBlocks(blocks); err != nil {
345		panic(err)
346	}
347	return blocks
348}
349
350func cleanHistoryDB() error {
351	return historyDB.Reorg(-1)
352}