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}