You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

259 lines
8.9 KiB

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