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	// Generate fake bids
123	const nBids = 20
124	bids := test.GenBids(nBids, blocks, coords)
125	err := historyDB.addBids(bids)
126	assert.NoError(t, err)
127	// Fetch bids
128	fetchedBids, err := historyDB.GetBids()
129	assert.NoError(t, err)
130	// Compare fetched bids vs generated bids
131	for i, bid := range fetchedBids {
132		assert.Equal(t, bids[i], *bid)
133	}
134}
135
136func TestTokens(t *testing.T) {
137	const fromBlock int64 = 1
138	const toBlock int64 = 5
139	// Prepare blocks in the DB
140	blocks := setTestBlocks(fromBlock, toBlock)
141	// Generate fake tokens
142	const nTokens = 5
143	tokens := test.GenTokens(nTokens, blocks)
144	err := historyDB.AddTokens(tokens)
145	assert.NoError(t, err)
146	// Update price of generated tokens without price
147	for i := 0; i < len(tokens); i++ {
148		if tokens[i].USD == 0 {
149			value := 3.33 + float64(i)
150			tokens[i].USD = value
151			err := historyDB.UpdateTokenValue(tokens[i].TokenID, value)
152			assert.NoError(t, err)
153		}
154	}
155	// Fetch tokens
156	fetchedTokens, err := historyDB.GetTokens()
157	assert.NoError(t, err)
158	// Compare fetched tokens vs generated tokens
159	// All the tokens should have USDUpdate setted by the DB trigger
160	for i, token := range fetchedTokens {
161		assert.Equal(t, tokens[i].TokenID, token.TokenID)
162		assert.Equal(t, tokens[i].EthBlockNum, token.EthBlockNum)
163		assert.Equal(t, tokens[i].EthAddr, token.EthAddr)
164		assert.Equal(t, tokens[i].Name, token.Name)
165		assert.Equal(t, tokens[i].Symbol, token.Symbol)
166		assert.Equal(t, tokens[i].USD, token.USD)
167		assert.Greater(t, int64(1*time.Second), int64(time.Since(token.USDUpdate)))
168	}
169}
170
171func TestAccounts(t *testing.T) {
172	const fromBlock int64 = 1
173	const toBlock int64 = 5
174	// Prepare blocks in the DB
175	blocks := setTestBlocks(fromBlock, toBlock)
176	// Generate fake tokens
177	const nTokens = 5
178	tokens := test.GenTokens(nTokens, blocks)
179	err := historyDB.AddTokens(tokens)
180	assert.NoError(t, err)
181	// Generate fake batches
182	const nBatches = 10
183	batches := test.GenBatches(nBatches, blocks)
184	err = historyDB.AddBatches(batches)
185	assert.NoError(t, err)
186	// Generate fake accounts
187	const nAccounts = 3
188	accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
189	err = historyDB.AddAccounts(accs)
190	assert.NoError(t, err)
191	// Fetch accounts
192	fetchedAccs, err := historyDB.GetAccounts()
193	assert.NoError(t, err)
194	// Compare fetched accounts vs generated accounts
195	for i, acc := range fetchedAccs {
196		assert.Equal(t, accs[i], *acc)
197	}
198}
199
200func TestTxs(t *testing.T) {
201	const fromBlock int64 = 1
202	const toBlock int64 = 5
203	// Prepare blocks in the DB
204	blocks := setTestBlocks(fromBlock, toBlock)
205	// Generate fake tokens
206	const nTokens = 5
207	const tokenValue = 1.23456
208	tokens := test.GenTokens(nTokens, blocks)
209	for i := 0; i < len(tokens); i++ {
210		tokens[i].USD = tokenValue
211	}
212	err := historyDB.AddTokens(tokens)
213	assert.NoError(t, err)
214	// Generate fake batches
215	const nBatches = 10
216	batches := test.GenBatches(nBatches, blocks)
217	err = historyDB.AddBatches(batches)
218	assert.NoError(t, err)
219	// Generate fake accounts
220	const nAccounts = 3
221	accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
222	err = historyDB.AddAccounts(accs)
223	assert.NoError(t, err)
224	// Generate fake L1 txs
225	const nL1s = 30
226	_, l1txs := test.GenL1Txs(0, nL1s, 0, nil, accs, tokens, blocks, batches)
227	err = historyDB.AddL1Txs(l1txs)
228	assert.NoError(t, err)
229	// Generate fake L2 txs
230	const nL2s = 20
231	_, l2txs := test.GenL2Txs(0, nL2s, 0, nil, accs, tokens, blocks, batches)
232	err = historyDB.AddL2Txs(l2txs)
233	assert.NoError(t, err)
234	// Compare fetched txs vs generated txs.
235	for i := 0; i < len(l1txs); i++ {
236		tx := l1txs[i].Tx()
237		fetchedTx, err := historyDB.GetTx(tx.TxID)
238		assert.NoError(t, err)
239		tx.USD = tokenValue * tx.AmountFloat
240		if fetchedTx.USD > tx.USD {
241			assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
242		} else {
243			assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
244		}
245		tx.LoadAmountUSD = tokenValue * tx.LoadAmountFloat
246		if fetchedTx.LoadAmountUSD > tx.LoadAmountUSD {
247			assert.Less(t, 0.999, tx.LoadAmountUSD/fetchedTx.LoadAmountUSD)
248		} else {
249			assert.Less(t, 0.999, fetchedTx.LoadAmountUSD/tx.LoadAmountUSD)
250		}
251		tx.LoadAmountUSD = 0
252		tx.USD = 0
253		fetchedTx.LoadAmountUSD = 0
254		fetchedTx.USD = 0
255		assert.Equal(t, tx, fetchedTx)
256	}
257	for i := 0; i < len(l2txs); i++ {
258		tx := l2txs[i].Tx()
259		fetchedTx, err := historyDB.GetTx(tx.TxID)
260		assert.NoError(t, err)
261		tx.USD = tokenValue * tx.AmountFloat
262		if fetchedTx.USD > tx.USD {
263			assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
264		} else {
265			assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
266		}
267		tx.FeeUSD = tx.USD * tx.Fee.Percentage()
268		if fetchedTx.FeeUSD > tx.FeeUSD {
269			assert.Less(t, 0.999, tx.FeeUSD/fetchedTx.FeeUSD)
270		} else if fetchedTx.FeeUSD < tx.FeeUSD {
271			assert.Less(t, 0.999, fetchedTx.FeeUSD/tx.FeeUSD)
272		}
273		tx.FeeUSD = 0
274		tx.USD = 0
275		fetchedTx.FeeUSD = 0
276		fetchedTx.USD = 0
277		assert.Equal(t, tx, fetchedTx)
278	}
279	// Test trigger: L1 integrity
280	// from_eth_addr can't be null
281	l1txs[0].FromEthAddr = ethCommon.Address{}
282	err = historyDB.AddL1Txs(l1txs)
283	assert.Error(t, err)
284	l1txs[0].FromEthAddr = ethCommon.BigToAddress(big.NewInt(int64(5)))
285	// from_bjj can't be null
286	l1txs[0].FromBJJ = nil
287	err = historyDB.AddL1Txs(l1txs)
288	assert.Error(t, err)
289	privK := babyjub.NewRandPrivKey()
290	l1txs[0].FromBJJ = privK.Public()
291	// load_amount can't be null
292	l1txs[0].LoadAmount = nil
293	err = historyDB.AddL1Txs(l1txs)
294	assert.Error(t, err)
295	// Test trigger: L2 integrity
296	// batch_num can't be null
297	l2txs[0].BatchNum = 0
298	err = historyDB.AddL2Txs(l2txs)
299	assert.Error(t, err)
300	l2txs[0].BatchNum = 1
301	// nonce can't be null
302	l2txs[0].Nonce = 0
303	err = historyDB.AddL2Txs(l2txs)
304	assert.Error(t, err)
305}
306
307// setTestBlocks WARNING: this will delete the blocks and recreate them
308func setTestBlocks(from, to int64) []common.Block {
309	if err := cleanHistoryDB(); err != nil {
310		panic(err)
311	}
312	blocks := test.GenBlocks(from, to)
313	if err := historyDB.AddBlocks(blocks); err != nil {
314		panic(err)
315	}
316	return blocks
317}
318
319func cleanHistoryDB() error {
320	return historyDB.Reorg(0)
321}