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}