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