arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/db/historydb/historydb_test.go
db/historydb/historydb_test.goRaw
  1package historydb
  2
  3import (
  4	"math/big"
  5	"os"
  6	"testing"
  7	"time"
  8
  9	ethCommon "github.com/ethereum/go-ethereum/common"
 10	"github.com/hermeznetwork/hermez-node/common"
 11	dbUtils "github.com/hermeznetwork/hermez-node/db"
 12	"github.com/hermeznetwork/hermez-node/log"
 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	pass := os.Getenv("POSTGRES_PASS")
 31	db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
 32	if err != nil {
 33		panic(err)
 34	}
 35	historyDB = NewHistoryDB(db)
 36	if err != nil {
 37		panic(err)
 38	}
 39	// Run tests
 40	result := m.Run()
 41	// Close DB
 42	if err := db.Close(); err != nil {
 43		log.Error("Error closing the history DB:", err)
 44	}
 45	os.Exit(result)
 46}
 47
 48func TestBlocks(t *testing.T) {
 49	var fromBlock, toBlock int64
 50	fromBlock = 1
 51	toBlock = 5
 52	// Delete peviously created rows (clean previous test execs)
 53	assert.NoError(t, historyDB.Reorg(fromBlock-1))
 54	// Generate fake blocks
 55	blocks := test.GenBlocks(fromBlock, toBlock)
 56	// Insert blocks into DB
 57	for i := 0; i < len(blocks); i++ {
 58		err := historyDB.AddBlock(&blocks[i])
 59		assert.NoError(t, err)
 60	}
 61	// Get all blocks from DB
 62	fetchedBlocks, err := historyDB.GetBlocks(fromBlock, toBlock)
 63	assert.Equal(t, len(blocks), len(fetchedBlocks))
 64	// Compare generated vs getted blocks
 65	assert.NoError(t, err)
 66	for i, fetchedBlock := range fetchedBlocks {
 67		assertEqualBlock(t, &blocks[i], fetchedBlock)
 68	}
 69	// Get blocks from the DB one by one
 70	for i := fromBlock; i < toBlock; i++ {
 71		fetchedBlock, err := historyDB.GetBlock(i)
 72		assert.NoError(t, err)
 73		assertEqualBlock(t, &blocks[i-1], fetchedBlock)
 74	}
 75	// Get last block
 76	lastBlock, err := historyDB.GetLastBlock()
 77	assert.NoError(t, err)
 78	assertEqualBlock(t, &blocks[len(blocks)-1], lastBlock)
 79}
 80
 81func assertEqualBlock(t *testing.T, expected *common.Block, actual *common.Block) {
 82	assert.Equal(t, expected.EthBlockNum, actual.EthBlockNum)
 83	assert.Equal(t, expected.Hash, actual.Hash)
 84	assert.Equal(t, expected.Timestamp.Unix(), actual.Timestamp.Unix())
 85}
 86
 87func TestBatches(t *testing.T) {
 88	const fromBlock int64 = 1
 89	const toBlock int64 = 3
 90	// Prepare blocks in the DB
 91	blocks := setTestBlocks(fromBlock, toBlock)
 92	// Generate fake batches
 93	const nBatches = 9
 94	batches := test.GenBatches(nBatches, blocks)
 95	// Test GetLastL1TxsNum with no batches
 96	fetchedLastL1TxsNum, err := historyDB.GetLastL1TxsNum()
 97	assert.NoError(t, err)
 98	assert.Nil(t, fetchedLastL1TxsNum)
 99	// Add batches to the DB
100	err = historyDB.AddBatches(batches)
101	assert.NoError(t, err)
102	// Get batches from the DB
103	fetchedBatches, err := historyDB.GetBatches(0, common.BatchNum(nBatches))
104	assert.NoError(t, err)
105	for i, fetchedBatch := range fetchedBatches {
106		assert.Equal(t, batches[i], *fetchedBatch)
107	}
108	// Test GetLastBatchNum
109	fetchedLastBatchNum, err := historyDB.GetLastBatchNum()
110	assert.NoError(t, err)
111	assert.Equal(t, batches[len(batches)-1].BatchNum, fetchedLastBatchNum)
112	// Test GetLastL1TxsNum
113	fetchedLastL1TxsNum, err = historyDB.GetLastL1TxsNum()
114	assert.NoError(t, err)
115	assert.Equal(t, batches[nBatches-1].ForgeL1TxsNum, *fetchedLastL1TxsNum)
116}
117
118func TestBids(t *testing.T) {
119	const fromBlock int64 = 1
120	const toBlock int64 = 5
121	// Prepare blocks in the DB
122	blocks := setTestBlocks(fromBlock, toBlock)
123	// Generate fake coordinators
124	const nCoords = 5
125	coords := test.GenCoordinators(nCoords, blocks)
126	err := historyDB.AddCoordinators(coords)
127	assert.NoError(t, err)
128	// Generate fake bids
129	const nBids = 20
130	bids := test.GenBids(nBids, blocks, coords)
131	err = historyDB.AddBids(bids)
132	assert.NoError(t, err)
133	// Fetch bids
134	fetchedBids, err := historyDB.GetBids()
135	assert.NoError(t, err)
136	// Compare fetched bids vs generated bids
137	for i, bid := range fetchedBids {
138		assert.Equal(t, bids[i], *bid)
139	}
140}
141
142func TestTokens(t *testing.T) {
143	const fromBlock int64 = 1
144	const toBlock int64 = 5
145	// Prepare blocks in the DB
146	blocks := setTestBlocks(fromBlock, toBlock)
147	// Generate fake tokens
148	const nTokens = 5
149	tokens := test.GenTokens(nTokens, blocks)
150	err := historyDB.AddTokens(tokens)
151	assert.NoError(t, err)
152	// Fetch tokens
153	fetchedTokens, err := historyDB.GetTokens()
154	assert.NoError(t, err)
155	// Compare fetched tokens vs generated tokens
156	// All the tokens should have USDUpdate setted by the DB trigger
157	for i, token := range fetchedTokens {
158		assert.Equal(t, tokens[i].TokenID, token.TokenID)
159		assert.Equal(t, tokens[i].EthBlockNum, token.EthBlockNum)
160		assert.Equal(t, tokens[i].EthAddr, token.EthAddr)
161		assert.Equal(t, tokens[i].Name, token.Name)
162		assert.Equal(t, tokens[i].Symbol, token.Symbol)
163		assert.Equal(t, tokens[i].USD, token.USD)
164		if token.USDUpdate != nil {
165			assert.Greater(t, int64(1*time.Second), int64(time.Since(*token.USDUpdate)))
166		} else {
167			assert.Equal(t, tokens[i].USDUpdate, token.USDUpdate)
168		}
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, 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 = 500
208	tokens := test.GenTokens(nTokens, blocks)
209	err := historyDB.AddTokens(tokens)
210	assert.NoError(t, err)
211	// Generate fake batches
212	const nBatches = 10
213	batches := test.GenBatches(nBatches, blocks)
214	err = historyDB.AddBatches(batches)
215	assert.NoError(t, err)
216	// Generate fake accounts
217	const nAccounts = 3
218	accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
219	err = historyDB.AddAccounts(accs)
220	assert.NoError(t, err)
221	// Generate fake L1 txs
222	const nL1s = 64
223	_, l1txs := test.GenL1Txs(0, nL1s, 0, nil, accs, tokens, blocks, batches)
224	err = historyDB.AddL1Txs(l1txs)
225	assert.NoError(t, err)
226	// Generate fake L2 txs
227	const nL2s = 2048 - nL1s
228	_, l2txs := test.GenL2Txs(0, nL2s, 0, nil, accs, tokens, blocks, batches)
229	err = historyDB.AddL2Txs(l2txs)
230	assert.NoError(t, err)
231	// Compare fetched txs vs generated txs.
232	fetchAndAssertTxs(t, l1txs, l2txs)
233	// Test trigger: L1 integrity
234	// from_eth_addr can't be null
235	l1txs[0].FromEthAddr = ethCommon.Address{}
236	err = historyDB.AddL1Txs(l1txs)
237	assert.Error(t, err)
238	l1txs[0].FromEthAddr = ethCommon.BigToAddress(big.NewInt(int64(5)))
239	// from_bjj can't be null
240	l1txs[0].FromBJJ = nil
241	err = historyDB.AddL1Txs(l1txs)
242	assert.Error(t, err)
243	privK := babyjub.NewRandPrivKey()
244	l1txs[0].FromBJJ = privK.Public()
245	// load_amount can't be null
246	l1txs[0].LoadAmount = nil
247	err = historyDB.AddL1Txs(l1txs)
248	assert.Error(t, err)
249	// Test trigger: L2 integrity
250	// batch_num can't be null
251	l2txs[0].BatchNum = 0
252	err = historyDB.AddL2Txs(l2txs)
253	assert.Error(t, err)
254	l2txs[0].BatchNum = 1
255	// nonce can't be null
256	l2txs[0].Nonce = 0
257	err = historyDB.AddL2Txs(l2txs)
258	assert.Error(t, err)
259	// Test trigger: forge L1 txs
260	// add next batch to DB
261	batchNum, toForgeL1TxsNum := test.GetNextToForgeNumAndBatch(batches)
262	batch := batches[0]
263	batch.BatchNum = batchNum
264	batch.ForgeL1TxsNum = toForgeL1TxsNum
265	assert.NoError(t, historyDB.AddBatch(&batch)) // This should update nL1s / 2 rows
266	// Set batch num in txs that should have been marked as forged in the DB
267	for i := 0; i < len(l1txs); i++ {
268		fetchedTx, err := historyDB.GetTx(l1txs[i].TxID)
269		assert.NoError(t, err)
270		if l1txs[i].ToForgeL1TxsNum == toForgeL1TxsNum {
271			assert.Equal(t, batchNum, *fetchedTx.BatchNum)
272		} else {
273			if fetchedTx.BatchNum != nil {
274				assert.NotEqual(t, batchNum, *fetchedTx.BatchNum)
275			}
276		}
277	}
278
279	// Test helper functions for Synchronizer
280	// GetLastTxsPosition
281	expectedPosition := -1
282	var choosenToForgeL1TxsNum int64 = -1
283	for _, tx := range l1txs {
284		if choosenToForgeL1TxsNum == -1 && tx.ToForgeL1TxsNum > 0 {
285			choosenToForgeL1TxsNum = tx.ToForgeL1TxsNum
286			expectedPosition = tx.Position
287		} else if choosenToForgeL1TxsNum == tx.ToForgeL1TxsNum && expectedPosition < tx.Position {
288			expectedPosition = tx.Position
289		}
290	}
291	position, err := historyDB.GetLastTxsPosition(choosenToForgeL1TxsNum)
292	assert.NoError(t, err)
293	assert.Equal(t, expectedPosition, position)
294
295	// GetL1UserTxs: not needed? tests were broken
296	// txs, err := historyDB.GetL1UserTxs(2)
297	// assert.NoError(t, err)
298	// assert.NotZero(t, len(txs))
299	// assert.NoError(t, err)
300	// assert.Equal(t, 22, position)
301	// // Test Update L1 TX Batch_num
302	// assert.Equal(t, common.BatchNum(0), txs[0].BatchNum)
303	// txs[0].BatchNum = common.BatchNum(1)
304	// txs, err = historyDB.GetL1UserTxs(2)
305	// assert.NoError(t, err)
306	// assert.NotZero(t, len(txs))
307	// assert.Equal(t, common.BatchNum(1), txs[0].BatchNum)
308}
309
310func fetchAndAssertTxs(t *testing.T, l1txs []common.L1Tx, l2txs []common.L2Tx) {
311	for i := 0; i < len(l1txs); i++ {
312		tx := l1txs[i].Tx()
313		fetchedTx, err := historyDB.GetTx(tx.TxID)
314		assert.NoError(t, err)
315		test.AssertUSD(t, tx.USD, fetchedTx.USD)
316		test.AssertUSD(t, tx.LoadAmountUSD, fetchedTx.LoadAmountUSD)
317		assert.Equal(t, tx, fetchedTx)
318	}
319	for i := 0; i < len(l2txs); i++ {
320		tx := l2txs[i].Tx()
321		fetchedTx, err := historyDB.GetTx(tx.TxID)
322		tx.TokenID = fetchedTx.TokenID
323		assert.NoError(t, err)
324		test.AssertUSD(t, fetchedTx.USD, tx.USD)
325		test.AssertUSD(t, fetchedTx.FeeUSD, tx.FeeUSD)
326		assert.Equal(t, tx, fetchedTx)
327	}
328}
329
330func TestExitTree(t *testing.T) {
331	nBatches := 17
332	blocks := setTestBlocks(0, 10)
333	batches := test.GenBatches(nBatches, blocks)
334	err := historyDB.AddBatches(batches)
335	assert.NoError(t, err)
336
337	exitTree := test.GenExitTree(nBatches)
338	err = historyDB.AddExitTree(exitTree)
339	assert.NoError(t, err)
340}
341
342// setTestBlocks WARNING: this will delete the blocks and recreate them
343func setTestBlocks(from, to int64) []common.Block {
344	if err := cleanHistoryDB(); err != nil {
345		panic(err)
346	}
347	blocks := test.GenBlocks(from, to)
348	if err := historyDB.AddBlocks(blocks); err != nil {
349		panic(err)
350	}
351	return blocks
352}
353
354func cleanHistoryDB() error {
355	return historyDB.Reorg(-1)
356}