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.PublicKey)
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 != nil {
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.PublicKey)
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 != nil {
155 rqToBJJ := bjjToString(poolTx.RqToBJJ)
156 genSendTx.RqToBJJ = &rqToBJJ
157 genReceiveTx.RqToBJJ = &rqToBJJ
158 }
159 }
160 poolTxsToSend = append(poolTxsToSend, genSendTx)
161 poolTxsToReceive = append(poolTxsToReceive, genReceiveTx)
162 }
163 return poolTxsToSend, poolTxsToReceive
164}
165
166func TestPoolTxs(t *testing.T) {
167 // POST
168 endpoint := apiURL + "transactions-pool"
169 fetchedTxID := common.TxID{}
170 for _, tx := range tc.poolTxsToSend {
171 jsonTxBytes, err := json.Marshal(tx)
172 assert.NoError(t, err)
173 jsonTxReader := bytes.NewReader(jsonTxBytes)
174 assert.NoError(
175 t, doGoodReq(
176 "POST",
177 endpoint,
178 jsonTxReader, &fetchedTxID,
179 ),
180 )
181 assert.Equal(t, tx.TxID, fetchedTxID)
182 }
183 // 400
184 // Wrong fee
185 badTx := tc.poolTxsToSend[0]
186 badTx.Amount = "99999999999999999999999"
187 badTx.Fee = 255
188 jsonTxBytes, err := json.Marshal(badTx)
189 assert.NoError(t, err)
190 jsonTxReader := bytes.NewReader(jsonTxBytes)
191 err = doBadReq("POST", endpoint, jsonTxReader, 400)
192 assert.NoError(t, err)
193 // Wrong signature
194 badTx = tc.poolTxsToSend[0]
195 badTx.FromIdx = "hez:foo:1000"
196 jsonTxBytes, err = json.Marshal(badTx)
197 assert.NoError(t, err)
198 jsonTxReader = bytes.NewReader(jsonTxBytes)
199 err = doBadReq("POST", endpoint, jsonTxReader, 400)
200 assert.NoError(t, err)
201 // Wrong to
202 badTx = tc.poolTxsToSend[0]
203 ethAddr := "hez:0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
204 badTx.ToEthAddr = ðAddr
205 badTx.ToIdx = nil
206 jsonTxBytes, err = json.Marshal(badTx)
207 assert.NoError(t, err)
208 jsonTxReader = bytes.NewReader(jsonTxBytes)
209 err = doBadReq("POST", endpoint, jsonTxReader, 400)
210 assert.NoError(t, err)
211 // Wrong rq
212 badTx = tc.poolTxsToSend[0]
213 rqFromIdx := "hez:foo:30"
214 badTx.RqFromIdx = &rqFromIdx
215 jsonTxBytes, err = json.Marshal(badTx)
216 assert.NoError(t, err)
217 jsonTxReader = bytes.NewReader(jsonTxBytes)
218 err = doBadReq("POST", endpoint, jsonTxReader, 400)
219 assert.NoError(t, err)
220 // GET
221 endpoint += "/"
222 for _, tx := range tc.poolTxsToReceive {
223 fetchedTx := testPoolTxReceive{}
224 assert.NoError(
225 t, doGoodReq(
226 "GET",
227 endpoint+tx.TxID.String(),
228 nil, &fetchedTx,
229 ),
230 )
231 assertPoolTx(t, tx, fetchedTx)
232 }
233 // 400
234 err = doBadReq("GET", endpoint+"0xG20000000156660000000090", nil, 400)
235 assert.NoError(t, err)
236 // 404
237 err = doBadReq("GET", endpoint+"0x020000000156660000000090", nil, 404)
238 assert.NoError(t, err)
239}
240
241func assertPoolTx(t *testing.T, expected, actual testPoolTxReceive) {
242 // state should be pending
243 assert.Equal(t, common.PoolL2TxStatePending, actual.State)
244 expected.State = actual.State
245 actual.Token.ItemID = 0
246 // timestamp should be very close to now
247 assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
248 expected.Timestamp = actual.Timestamp
249 // token timestamp
250 if expected.Token.USDUpdate == nil {
251 assert.Equal(t, expected.Token.USDUpdate, actual.Token.USDUpdate)
252 } else {
253 assert.Equal(t, expected.Token.USDUpdate.Unix(), actual.Token.USDUpdate.Unix())
254 expected.Token.USDUpdate = actual.Token.USDUpdate
255 }
256 assert.Equal(t, expected, actual)
257}