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