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