hermez-node/api/txspool_test.go
api/txspool_test.goRaw
1package api
2
3import (
4 "bytes"
5 "encoding/json"
6 "testing"
7 "time"
8
9 "github.com/hermeznetwork/hermez-node/common"
10 "github.com/hermeznetwork/hermez-node/db/historydb"
11 "github.com/iden3/go-iden3-crypto/babyjub"
12 "github.com/stretchr/testify/assert"
13)
14
15// testPoolTxReceive is a struct to be used to assert the response
16// of GET /transactions-pool/:id
17type testPoolTxReceive struct {
18 TxID common.TxID `json:"id"`
19 Type common.TxType `json:"type"`
20 FromIdx string `json:"fromAccountIndex"`
21 FromEthAddr *string `json:"fromHezEthereumAddress"`
22 FromBJJ *string `json:"fromBJJ"`
23 ToIdx *string `json:"toAccountIndex"`
24 ToEthAddr *string `json:"toHezEthereumAddress"`
25 ToBJJ *string `json:"toBjj"`
26 Amount string `json:"amount"`
27 Fee common.FeeSelector `json:"fee"`
28 Nonce common.Nonce `json:"nonce"`
29 State common.PoolL2TxState `json:"state"`
30 Signature babyjub.SignatureComp `json:"signature"`
31 RqFromIdx *string `json:"requestFromAccountIndex"`
32 RqToIdx *string `json:"requestToAccountIndex"`
33 RqToEthAddr *string `json:"requestToHezEthereumAddress"`
34 RqToBJJ *string `json:"requestToBJJ"`
35 RqTokenID *common.TokenID `json:"requestTokenId"`
36 RqAmount *string `json:"requestAmount"`
37 RqFee *common.FeeSelector `json:"requestFee"`
38 RqNonce *common.Nonce `json:"requestNonce"`
39 BatchNum *common.BatchNum `json:"batchNum"`
40 Timestamp time.Time `json:"timestamp"`
41 Token historydb.TokenWithUSD `json:"token"`
42}
43
44// testPoolTxSend is a struct to be used as a JSON body
45// when testing POST /transactions-pool
46type testPoolTxSend struct {
47 TxID common.TxID `json:"id" binding:"required"`
48 Type common.TxType `json:"type" binding:"required"`
49 TokenID common.TokenID `json:"tokenId"`
50 FromIdx string `json:"fromAccountIndex" binding:"required"`
51 ToIdx *string `json:"toAccountIndex"`
52 ToEthAddr *string `json:"toHezEthereumAddress"`
53 ToBJJ *string `json:"toBjj"`
54 Amount string `json:"amount" binding:"required"`
55 Fee common.FeeSelector `json:"fee"`
56 Nonce common.Nonce `json:"nonce"`
57 Signature babyjub.SignatureComp `json:"signature" binding:"required"`
58 RqFromIdx *string `json:"requestFromAccountIndex"`
59 RqToIdx *string `json:"requestToAccountIndex"`
60 RqToEthAddr *string `json:"requestToHezEthereumAddress"`
61 RqToBJJ *string `json:"requestToBjj"`
62 RqTokenID *common.TokenID `json:"requestTokenId"`
63 RqAmount *string `json:"requestAmount"`
64 RqFee *common.FeeSelector `json:"requestFee"`
65 RqNonce *common.Nonce `json:"requestNonce"`
66}
67
68func genTestPoolTxs(
69 poolTxs []common.PoolL2Tx,
70 tokens []historydb.TokenWithUSD,
71 accs []common.Account,
72) (poolTxsToSend []testPoolTxSend, poolTxsToReceive []testPoolTxReceive) {
73 poolTxsToSend = []testPoolTxSend{}
74 poolTxsToReceive = []testPoolTxReceive{}
75 for _, poolTx := range poolTxs {
76 // common.PoolL2Tx ==> testPoolTxSend
77 token := getTokenByID(poolTx.TokenID, tokens)
78 genSendTx := testPoolTxSend{
79 TxID: poolTx.TxID,
80 Type: poolTx.Type,
81 TokenID: poolTx.TokenID,
82 FromIdx: idxToHez(poolTx.FromIdx, token.Symbol),
83 Amount: poolTx.Amount.String(),
84 Fee: poolTx.Fee,
85 Nonce: poolTx.Nonce,
86 Signature: poolTx.Signature,
87 RqFee: &poolTx.RqFee,
88 RqNonce: &poolTx.RqNonce,
89 }
90 // common.PoolL2Tx ==> testPoolTxReceive
91 genReceiveTx := testPoolTxReceive{
92 TxID: poolTx.TxID,
93 Type: poolTx.Type,
94 FromIdx: idxToHez(poolTx.FromIdx, token.Symbol),
95 Amount: poolTx.Amount.String(),
96 Fee: poolTx.Fee,
97 Nonce: poolTx.Nonce,
98 State: poolTx.State,
99 Signature: poolTx.Signature,
100 Timestamp: poolTx.Timestamp,
101 // BatchNum: poolTx.BatchNum,
102 RqFee: &poolTx.RqFee,
103 RqNonce: &poolTx.RqNonce,
104 Token: token,
105 }
106 fromAcc := getAccountByIdx(poolTx.FromIdx, accs)
107 fromAddr := ethAddrToHez(fromAcc.EthAddr)
108 genReceiveTx.FromEthAddr = &fromAddr
109 fromBjj := bjjToString(fromAcc.BJJ)
110 genReceiveTx.FromBJJ = &fromBjj
111 if poolTx.ToIdx != 0 {
112 toIdx := idxToHez(poolTx.ToIdx, token.Symbol)
113 genSendTx.ToIdx = &toIdx
114 genReceiveTx.ToIdx = &toIdx
115 }
116 if poolTx.ToEthAddr != common.EmptyAddr {
117 toEth := ethAddrToHez(poolTx.ToEthAddr)
118 genSendTx.ToEthAddr = &toEth
119 genReceiveTx.ToEthAddr = &toEth
120 } else if poolTx.ToIdx > 255 {
121 acc := getAccountByIdx(poolTx.ToIdx, accs)
122 addr := ethAddrToHez(acc.EthAddr)
123 genReceiveTx.ToEthAddr = &addr
124 }
125 if poolTx.ToBJJ != common.EmptyBJJComp {
126 toBJJ := bjjToString(poolTx.ToBJJ)
127 genSendTx.ToBJJ = &toBJJ
128 genReceiveTx.ToBJJ = &toBJJ
129 } else if poolTx.ToIdx > 255 {
130 acc := getAccountByIdx(poolTx.ToIdx, accs)
131 bjj := bjjToString(acc.BJJ)
132 genReceiveTx.ToBJJ = &bjj
133 }
134 if poolTx.RqFromIdx != 0 {
135 rqFromIdx := idxToHez(poolTx.RqFromIdx, token.Symbol)
136 genSendTx.RqFromIdx = &rqFromIdx
137 genReceiveTx.RqFromIdx = &rqFromIdx
138 genSendTx.RqTokenID = &token.TokenID
139 genReceiveTx.RqTokenID = &token.TokenID
140 rqAmount := poolTx.RqAmount.String()
141 genSendTx.RqAmount = &rqAmount
142 genReceiveTx.RqAmount = &rqAmount
143
144 if poolTx.RqToIdx != 0 {
145 rqToIdx := idxToHez(poolTx.RqToIdx, token.Symbol)
146 genSendTx.RqToIdx = &rqToIdx
147 genReceiveTx.RqToIdx = &rqToIdx
148 }
149 if poolTx.RqToEthAddr != common.EmptyAddr {
150 rqToEth := ethAddrToHez(poolTx.RqToEthAddr)
151 genSendTx.RqToEthAddr = &rqToEth
152 genReceiveTx.RqToEthAddr = &rqToEth
153 }
154 if poolTx.RqToBJJ != common.EmptyBJJComp {
155 rqToBJJ := bjjToString(poolTx.RqToBJJ)
156 genSendTx.RqToBJJ = &rqToBJJ
157 genReceiveTx.RqToBJJ = &rqToBJJ
158 }
159 }
160
161 poolTxsToSend = append(poolTxsToSend, genSendTx)
162 poolTxsToReceive = append(poolTxsToReceive, genReceiveTx)
163 }
164 return poolTxsToSend, poolTxsToReceive
165}
166
167func TestPoolTxs(t *testing.T) {
168 // POST
169 endpoint := apiURL + "transactions-pool"
170 fetchedTxID := common.TxID{}
171 for _, tx := range tc.poolTxsToSend {
172 jsonTxBytes, err := json.Marshal(tx)
173 assert.NoError(t, err)
174 jsonTxReader := bytes.NewReader(jsonTxBytes)
175 assert.NoError(
176 t, doGoodReq(
177 "POST",
178 endpoint,
179 jsonTxReader, &fetchedTxID,
180 ),
181 )
182 assert.Equal(t, tx.TxID, fetchedTxID)
183 }
184 // 400
185 // Wrong fee
186 badTx := tc.poolTxsToSend[0]
187 badTx.Amount = "99999999999999999999999"
188 badTx.Fee = 255
189 jsonTxBytes, err := json.Marshal(badTx)
190 assert.NoError(t, err)
191 jsonTxReader := bytes.NewReader(jsonTxBytes)
192 err = doBadReq("POST", endpoint, jsonTxReader, 400)
193 assert.NoError(t, err)
194 // Wrong signature
195 badTx = tc.poolTxsToSend[0]
196 badTx.FromIdx = "hez:foo:1000"
197 jsonTxBytes, err = json.Marshal(badTx)
198 assert.NoError(t, err)
199 jsonTxReader = bytes.NewReader(jsonTxBytes)
200 err = doBadReq("POST", endpoint, jsonTxReader, 400)
201 assert.NoError(t, err)
202 // Wrong to
203 badTx = tc.poolTxsToSend[0]
204 ethAddr := "hez:0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
205 badTx.ToEthAddr = ðAddr
206 badTx.ToIdx = nil
207 jsonTxBytes, err = json.Marshal(badTx)
208 assert.NoError(t, err)
209 jsonTxReader = bytes.NewReader(jsonTxBytes)
210 err = doBadReq("POST", endpoint, jsonTxReader, 400)
211 assert.NoError(t, err)
212 // Wrong rq
213 badTx = tc.poolTxsToSend[0]
214 rqFromIdx := "hez:foo:30"
215 badTx.RqFromIdx = &rqFromIdx
216 jsonTxBytes, err = json.Marshal(badTx)
217 assert.NoError(t, err)
218 jsonTxReader = bytes.NewReader(jsonTxBytes)
219 err = doBadReq("POST", endpoint, jsonTxReader, 400)
220 assert.NoError(t, err)
221 // GET
222 endpoint += "/"
223 for _, tx := range tc.poolTxsToReceive {
224 fetchedTx := testPoolTxReceive{}
225 assert.NoError(
226 t, doGoodReq(
227 "GET",
228 endpoint+tx.TxID.String(),
229 nil, &fetchedTx,
230 ),
231 )
232 assertPoolTx(t, tx, fetchedTx)
233 }
234 // 400
235 err = doBadReq("GET", endpoint+"0xG20000000156660000000090", nil, 400)
236 assert.NoError(t, err)
237 // 404
238 err = doBadReq("GET", endpoint+"0x020000000156660000000090", nil, 404)
239 assert.NoError(t, err)
240}
241
242func assertPoolTx(t *testing.T, expected, actual testPoolTxReceive) {
243 // state should be pending
244 assert.Equal(t, common.PoolL2TxStatePending, actual.State)
245 expected.State = actual.State
246 actual.Token.ItemID = 0
247 // timestamp should be very close to now
248 assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
249 expected.Timestamp = actual.Timestamp
250 // token timestamp
251 if expected.Token.USDUpdate == nil {
252 assert.Equal(t, expected.Token.USDUpdate, actual.Token.USDUpdate)
253 } else {
254 assert.Equal(t, expected.Token.USDUpdate.Unix(), actual.Token.USDUpdate.Unix())
255 expected.Token.USDUpdate = actual.Token.USDUpdate
256 }
257 assert.Equal(t, expected, actual)
258}