arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/api/batch_test.go
api/batch_test.goRaw
  1package api
  2
  3import (
  4	"fmt"
  5	"strconv"
  6	"testing"
  7	"time"
  8
  9	ethCommon "github.com/ethereum/go-ethereum/common"
 10	"github.com/hermeznetwork/hermez-node/common"
 11	"github.com/hermeznetwork/hermez-node/db"
 12	"github.com/hermeznetwork/hermez-node/db/historydb"
 13	"github.com/mitchellh/copystructure"
 14	"github.com/stretchr/testify/assert"
 15)
 16
 17type testBatch struct {
 18	ItemID        int                       `json:"itemId"`
 19	BatchNum      common.BatchNum           `json:"batchNum"`
 20	EthBlockNum   int64                     `json:"ethereumBlockNum"`
 21	EthBlockHash  ethCommon.Hash            `json:"ethereumBlockHash"`
 22	Timestamp     time.Time                 `json:"timestamp"`
 23	ForgerAddr    ethCommon.Address         `json:"forgerAddr"`
 24	CollectedFees map[common.TokenID]string `json:"collectedFees"`
 25	TotalFeesUSD  *float64                  `json:"historicTotalCollectedFeesUSD"`
 26	StateRoot     string                    `json:"stateRoot"`
 27	NumAccounts   int                       `json:"numAccounts"`
 28	ExitRoot      string                    `json:"exitRoot"`
 29	ForgeL1TxsNum *int64                    `json:"forgeL1TransactionsNum"`
 30	SlotNum       int64                     `json:"slotNum"`
 31}
 32type testBatchesResponse struct {
 33	Batches    []testBatch    `json:"batches"`
 34	Pagination *db.Pagination `json:"pagination"`
 35}
 36
 37func (t testBatchesResponse) GetPagination() *db.Pagination {
 38	if t.Batches[0].ItemID < t.Batches[len(t.Batches)-1].ItemID {
 39		t.Pagination.FirstReturnedItem = t.Batches[0].ItemID
 40		t.Pagination.LastReturnedItem = t.Batches[len(t.Batches)-1].ItemID
 41	} else {
 42		t.Pagination.LastReturnedItem = t.Batches[0].ItemID
 43		t.Pagination.FirstReturnedItem = t.Batches[len(t.Batches)-1].ItemID
 44	}
 45	return t.Pagination
 46}
 47
 48func (t testBatchesResponse) Len() int {
 49	return len(t.Batches)
 50}
 51
 52type testFullBatch struct {
 53	Batch testBatch `json:"batch"`
 54	Txs   []testTx  `json:"transactions"`
 55}
 56
 57func genTestBatches(
 58	blocks []common.Block,
 59	cBatches []common.Batch,
 60	txs []testTx,
 61) ([]testBatch, []testFullBatch) {
 62	tBatches := []testBatch{}
 63	for _, cBatch := range cBatches {
 64		block := common.Block{}
 65		found := false
 66		for _, b := range blocks {
 67			if b.EthBlockNum == cBatch.EthBlockNum {
 68				block = b
 69				found = true
 70				break
 71			}
 72		}
 73		if !found {
 74			panic("block not found")
 75		}
 76		collectedFees := make(map[common.TokenID]string)
 77		for k, v := range cBatch.CollectedFees {
 78			collectedFees[k] = v.String()
 79		}
 80		tBatch := testBatch{
 81			BatchNum:      cBatch.BatchNum,
 82			EthBlockNum:   cBatch.EthBlockNum,
 83			EthBlockHash:  block.Hash,
 84			Timestamp:     block.Timestamp,
 85			ForgerAddr:    cBatch.ForgerAddr,
 86			CollectedFees: collectedFees,
 87			TotalFeesUSD:  cBatch.TotalFeesUSD,
 88			StateRoot:     cBatch.StateRoot.String(),
 89			NumAccounts:   cBatch.NumAccounts,
 90			ExitRoot:      cBatch.ExitRoot.String(),
 91			ForgeL1TxsNum: cBatch.ForgeL1TxsNum,
 92			SlotNum:       cBatch.SlotNum,
 93		}
 94		tBatches = append(tBatches, tBatch)
 95	}
 96	fullBatches := []testFullBatch{}
 97	for i := 0; i < len(tBatches); i++ {
 98		forgedTxs := []testTx{}
 99		for j := 0; j < len(txs); j++ {
100			if txs[j].BatchNum != nil && *txs[j].BatchNum == tBatches[i].BatchNum {
101				forgedTxs = append(forgedTxs, txs[j])
102			}
103		}
104		fullBatches = append(fullBatches, testFullBatch{
105			Batch: tBatches[i],
106			Txs:   forgedTxs,
107		})
108	}
109	return tBatches, fullBatches
110}
111
112func TestGetBatches(t *testing.T) {
113	endpoint := apiURL + "batches"
114	fetchedBatches := []testBatch{}
115	appendIter := func(intr interface{}) {
116		for i := 0; i < len(intr.(*testBatchesResponse).Batches); i++ {
117			tmp, err := copystructure.Copy(intr.(*testBatchesResponse).Batches[i])
118			if err != nil {
119				panic(err)
120			}
121			fetchedBatches = append(fetchedBatches, tmp.(testBatch))
122		}
123	}
124	// Get all (no filters)
125	limit := 3
126	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
127	err := doGoodReqPaginated(path, historydb.OrderAsc, &testBatchesResponse{}, appendIter)
128	assert.NoError(t, err)
129	assertBatches(t, tc.batches, fetchedBatches)
130
131	// minBatchNum
132	fetchedBatches = []testBatch{}
133	limit = 2
134	minBatchNum := tc.batches[len(tc.batches)/2].BatchNum
135	path = fmt.Sprintf("%s?minBatchNum=%d&limit=%d&fromItem=", endpoint, minBatchNum, limit)
136	err = doGoodReqPaginated(path, historydb.OrderAsc, &testBatchesResponse{}, appendIter)
137	assert.NoError(t, err)
138	minBatchNumBatches := []testBatch{}
139	for i := 0; i < len(tc.batches); i++ {
140		if tc.batches[i].BatchNum > minBatchNum {
141			minBatchNumBatches = append(minBatchNumBatches, tc.batches[i])
142		}
143	}
144	assertBatches(t, minBatchNumBatches, fetchedBatches)
145
146	// maxBatchNum
147	fetchedBatches = []testBatch{}
148	limit = 1
149	maxBatchNum := tc.batches[len(tc.batches)/2].BatchNum
150	path = fmt.Sprintf("%s?maxBatchNum=%d&limit=%d&fromItem=", endpoint, maxBatchNum, limit)
151	err = doGoodReqPaginated(path, historydb.OrderAsc, &testBatchesResponse{}, appendIter)
152	assert.NoError(t, err)
153	maxBatchNumBatches := []testBatch{}
154	for i := 0; i < len(tc.batches); i++ {
155		if tc.batches[i].BatchNum < maxBatchNum {
156			maxBatchNumBatches = append(maxBatchNumBatches, tc.batches[i])
157		}
158	}
159	assertBatches(t, maxBatchNumBatches, fetchedBatches)
160
161	// slotNum
162	fetchedBatches = []testBatch{}
163	limit = 5
164	slotNum := tc.batches[len(tc.batches)/2].SlotNum
165	path = fmt.Sprintf("%s?slotNum=%d&limit=%d&fromItem=", endpoint, slotNum, limit)
166	err = doGoodReqPaginated(path, historydb.OrderAsc, &testBatchesResponse{}, appendIter)
167	assert.NoError(t, err)
168	slotNumBatches := []testBatch{}
169	for i := 0; i < len(tc.batches); i++ {
170		if tc.batches[i].SlotNum == slotNum {
171			slotNumBatches = append(slotNumBatches, tc.batches[i])
172		}
173	}
174	assertBatches(t, slotNumBatches, fetchedBatches)
175
176	// forgerAddr
177	fetchedBatches = []testBatch{}
178	limit = 10
179	forgerAddr := tc.batches[len(tc.batches)/2].ForgerAddr
180	path = fmt.Sprintf("%s?forgerAddr=%s&limit=%d&fromItem=", endpoint, forgerAddr.String(), limit)
181	err = doGoodReqPaginated(path, historydb.OrderAsc, &testBatchesResponse{}, appendIter)
182	assert.NoError(t, err)
183	forgerAddrBatches := []testBatch{}
184	for i := 0; i < len(tc.batches); i++ {
185		if tc.batches[i].ForgerAddr == forgerAddr {
186			forgerAddrBatches = append(forgerAddrBatches, tc.batches[i])
187		}
188	}
189	assertBatches(t, forgerAddrBatches, fetchedBatches)
190
191	// All, in reverse order
192	fetchedBatches = []testBatch{}
193	limit = 6
194	path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
195	err = doGoodReqPaginated(path, historydb.OrderDesc, &testBatchesResponse{}, appendIter)
196	assert.NoError(t, err)
197	flippedBatches := []testBatch{}
198	for i := len(tc.batches) - 1; i >= 0; i-- {
199		flippedBatches = append(flippedBatches, tc.batches[i])
200	}
201	assertBatches(t, flippedBatches, fetchedBatches)
202
203	// Mixed filters
204	fetchedBatches = []testBatch{}
205	limit = 1
206	maxBatchNum = tc.batches[len(tc.batches)-len(tc.batches)/4].BatchNum
207	minBatchNum = tc.batches[len(tc.batches)/4].BatchNum
208	path = fmt.Sprintf("%s?minBatchNum=%d&maxBatchNum=%d&limit=%d&fromItem=", endpoint, minBatchNum, maxBatchNum, limit)
209	err = doGoodReqPaginated(path, historydb.OrderAsc, &testBatchesResponse{}, appendIter)
210	assert.NoError(t, err)
211	minMaxBatchNumBatches := []testBatch{}
212	for i := 0; i < len(tc.batches); i++ {
213		if tc.batches[i].BatchNum < maxBatchNum && tc.batches[i].BatchNum > minBatchNum {
214			minMaxBatchNumBatches = append(minMaxBatchNumBatches, tc.batches[i])
215		}
216	}
217	assertBatches(t, minMaxBatchNumBatches, fetchedBatches)
218
219	// 400
220	// Invalid minBatchNum
221	path = fmt.Sprintf("%s?minBatchNum=%d", endpoint, -2)
222	err = doBadReq("GET", path, nil, 400)
223	assert.NoError(t, err)
224	// Invalid forgerAddr
225	path = fmt.Sprintf("%s?forgerAddr=%s", endpoint, "0xG0000001")
226	err = doBadReq("GET", path, nil, 400)
227	assert.NoError(t, err)
228	// 404
229	path = fmt.Sprintf("%s?slotNum=%d&minBatchNum=%d", endpoint, 1, 25)
230	err = doBadReq("GET", path, nil, 404)
231	assert.NoError(t, err)
232}
233
234func TestGetBatch(t *testing.T) {
235	endpoint := apiURL + "batches/"
236	for _, batch := range tc.batches {
237		fetchedBatch := testBatch{}
238		assert.NoError(
239			t, doGoodReq(
240				"GET",
241				endpoint+strconv.Itoa(int(batch.BatchNum)),
242				nil, &fetchedBatch,
243			),
244		)
245		assertBatch(t, batch, fetchedBatch)
246	}
247	// 400
248	assert.NoError(t, doBadReq("GET", endpoint+"foo", nil, 400))
249	// 404
250	assert.NoError(t, doBadReq("GET", endpoint+"99999", nil, 404))
251}
252
253func TestGetFullBatch(t *testing.T) {
254	endpoint := apiURL + "full-batches/"
255	for _, fullBatch := range tc.fullBatches {
256		fetchedFullBatch := testFullBatch{}
257		assert.NoError(
258			t, doGoodReq(
259				"GET",
260				endpoint+strconv.Itoa(int(fullBatch.Batch.BatchNum)),
261				nil, &fetchedFullBatch,
262			),
263		)
264		assertBatch(t, fullBatch.Batch, fetchedFullBatch.Batch)
265		assertTxs(t, fullBatch.Txs, fetchedFullBatch.Txs)
266	}
267	// 400
268	assert.NoError(t, doBadReq("GET", endpoint+"foo", nil, 400))
269	// 404
270	assert.NoError(t, doBadReq("GET", endpoint+"99999", nil, 404))
271}
272
273func assertBatches(t *testing.T, expected, actual []testBatch) {
274	assert.Equal(t, len(expected), len(actual))
275	for i := 0; i < len(expected); i++ {
276		assertBatch(t, expected[i], actual[i])
277	}
278}
279
280func assertBatch(t *testing.T, expected, actual testBatch) {
281	assert.Equal(t, expected.Timestamp.Unix(), actual.Timestamp.Unix())
282	expected.Timestamp = actual.Timestamp
283	actual.ItemID = expected.ItemID
284	assert.Equal(t, expected, actual)
285}