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