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