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.

279 lines
9.4 KiB

  1. package api
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "math/big"
  6. "testing"
  7. "time"
  8. "github.com/hermeznetwork/hermez-node/common"
  9. "github.com/hermeznetwork/hermez-node/db/historydb"
  10. "github.com/iden3/go-iden3-crypto/babyjub"
  11. "github.com/stretchr/testify/assert"
  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 genTestPoolTx(accs []common.Account, privKs []babyjub.PrivateKey, tokens []historydb.TokenWithUSD) (poolTxsToSend []testPoolTxSend, poolTxsToReceive []testPoolTxReceive) {
  65. // Generate common.PoolL2Tx
  66. // WARNING: this should be replaced once til is ready
  67. poolTxs := []common.PoolL2Tx{}
  68. amount := new(big.Int)
  69. amount, ok := amount.SetString("100000000000000", 10)
  70. if !ok {
  71. panic("bad amount")
  72. }
  73. poolTx := common.PoolL2Tx{
  74. FromIdx: accs[0].Idx,
  75. ToIdx: accs[1].Idx,
  76. Amount: amount,
  77. TokenID: accs[0].TokenID,
  78. Nonce: 6,
  79. }
  80. if _, err := common.NewPoolL2Tx(&poolTx); err != nil {
  81. panic(err)
  82. }
  83. h, err := poolTx.HashToSign()
  84. if err != nil {
  85. panic(err)
  86. }
  87. poolTx.Signature = privKs[0].SignPoseidon(h).Compress()
  88. poolTxs = append(poolTxs, poolTx)
  89. // Transform to API formats
  90. poolTxsToSend = []testPoolTxSend{}
  91. poolTxsToReceive = []testPoolTxReceive{}
  92. for _, poolTx := range poolTxs {
  93. // common.PoolL2Tx ==> testPoolTxSend
  94. token := getTokenByID(poolTx.TokenID, tokens)
  95. genSendTx := testPoolTxSend{
  96. TxID: poolTx.TxID,
  97. Type: poolTx.Type,
  98. TokenID: poolTx.TokenID,
  99. FromIdx: idxToHez(poolTx.FromIdx, token.Symbol),
  100. Amount: poolTx.Amount.String(),
  101. Fee: poolTx.Fee,
  102. Nonce: poolTx.Nonce,
  103. Signature: poolTx.Signature,
  104. RqFee: &poolTx.RqFee,
  105. RqNonce: &poolTx.RqNonce,
  106. }
  107. // common.PoolL2Tx ==> testPoolTxReceive
  108. genReceiveTx := testPoolTxReceive{
  109. TxID: poolTx.TxID,
  110. Type: poolTx.Type,
  111. FromIdx: idxToHez(poolTx.FromIdx, token.Symbol),
  112. Amount: poolTx.Amount.String(),
  113. Fee: poolTx.Fee,
  114. Nonce: poolTx.Nonce,
  115. State: poolTx.State,
  116. Signature: poolTx.Signature,
  117. Timestamp: poolTx.Timestamp,
  118. // BatchNum: poolTx.BatchNum,
  119. RqFee: &poolTx.RqFee,
  120. RqNonce: &poolTx.RqNonce,
  121. Token: token,
  122. }
  123. fromAcc := getAccountByIdx(poolTx.ToIdx, accs)
  124. fromAddr := ethAddrToHez(fromAcc.EthAddr)
  125. genReceiveTx.FromEthAddr = &fromAddr
  126. fromBjj := bjjToString(fromAcc.PublicKey)
  127. genReceiveTx.FromBJJ = &fromBjj
  128. if poolTx.ToIdx != 0 {
  129. toIdx := idxToHez(poolTx.ToIdx, token.Symbol)
  130. genSendTx.ToIdx = &toIdx
  131. genReceiveTx.ToIdx = &toIdx
  132. }
  133. if poolTx.ToEthAddr != common.EmptyAddr {
  134. toEth := ethAddrToHez(poolTx.ToEthAddr)
  135. genSendTx.ToEthAddr = &toEth
  136. genReceiveTx.ToEthAddr = &toEth
  137. } else if poolTx.ToIdx > 255 {
  138. acc := getAccountByIdx(poolTx.ToIdx, accs)
  139. addr := ethAddrToHez(acc.EthAddr)
  140. genReceiveTx.ToEthAddr = &addr
  141. }
  142. if poolTx.ToBJJ != nil {
  143. toBJJ := bjjToString(poolTx.ToBJJ)
  144. genSendTx.ToBJJ = &toBJJ
  145. genReceiveTx.ToBJJ = &toBJJ
  146. } else if poolTx.ToIdx > 255 {
  147. acc := getAccountByIdx(poolTx.ToIdx, accs)
  148. bjj := bjjToString(acc.PublicKey)
  149. genReceiveTx.ToBJJ = &bjj
  150. }
  151. if poolTx.RqFromIdx != 0 {
  152. rqFromIdx := idxToHez(poolTx.RqFromIdx, token.Symbol)
  153. genSendTx.RqFromIdx = &rqFromIdx
  154. genReceiveTx.RqFromIdx = &rqFromIdx
  155. genSendTx.RqTokenID = &token.TokenID
  156. genReceiveTx.RqTokenID = &token.TokenID
  157. rqAmount := poolTx.RqAmount.String()
  158. genSendTx.RqAmount = &rqAmount
  159. genReceiveTx.RqAmount = &rqAmount
  160. if poolTx.RqToIdx != 0 {
  161. rqToIdx := idxToHez(poolTx.RqToIdx, token.Symbol)
  162. genSendTx.RqToIdx = &rqToIdx
  163. genReceiveTx.RqToIdx = &rqToIdx
  164. }
  165. if poolTx.RqToEthAddr != common.EmptyAddr {
  166. rqToEth := ethAddrToHez(poolTx.RqToEthAddr)
  167. genSendTx.RqToEthAddr = &rqToEth
  168. genReceiveTx.RqToEthAddr = &rqToEth
  169. }
  170. if poolTx.RqToBJJ != nil {
  171. rqToBJJ := bjjToString(poolTx.RqToBJJ)
  172. genSendTx.RqToBJJ = &rqToBJJ
  173. genReceiveTx.RqToBJJ = &rqToBJJ
  174. }
  175. }
  176. poolTxsToSend = append(poolTxsToSend, genSendTx)
  177. poolTxsToReceive = append(poolTxsToReceive, genReceiveTx)
  178. }
  179. return poolTxsToSend, poolTxsToReceive
  180. }
  181. func TestPoolTxs(t *testing.T) {
  182. // POST
  183. endpoint := apiURL + "transactions-pool"
  184. fetchedTxID := common.TxID{}
  185. for _, tx := range tc.poolTxsToSend {
  186. jsonTxBytes, err := json.Marshal(tx)
  187. assert.NoError(t, err)
  188. jsonTxReader := bytes.NewReader(jsonTxBytes)
  189. assert.NoError(
  190. t, doGoodReq(
  191. "POST",
  192. endpoint,
  193. jsonTxReader, &fetchedTxID,
  194. ),
  195. )
  196. assert.Equal(t, tx.TxID, fetchedTxID)
  197. }
  198. // 400
  199. // Wrong fee
  200. badTx := tc.poolTxsToSend[0]
  201. badTx.Amount = "99999999999999999999999"
  202. badTx.Fee = 255
  203. jsonTxBytes, err := json.Marshal(badTx)
  204. assert.NoError(t, err)
  205. jsonTxReader := bytes.NewReader(jsonTxBytes)
  206. err = doBadReq("POST", endpoint, jsonTxReader, 400)
  207. assert.NoError(t, err)
  208. // Wrong signature
  209. badTx = tc.poolTxsToSend[0]
  210. badTx.FromIdx = "hez:foo:1000"
  211. jsonTxBytes, err = json.Marshal(badTx)
  212. assert.NoError(t, err)
  213. jsonTxReader = bytes.NewReader(jsonTxBytes)
  214. err = doBadReq("POST", endpoint, jsonTxReader, 400)
  215. assert.NoError(t, err)
  216. // Wrong to
  217. badTx = tc.poolTxsToSend[0]
  218. ethAddr := "hez:0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
  219. badTx.ToEthAddr = &ethAddr
  220. badTx.ToIdx = nil
  221. jsonTxBytes, err = json.Marshal(badTx)
  222. assert.NoError(t, err)
  223. jsonTxReader = bytes.NewReader(jsonTxBytes)
  224. err = doBadReq("POST", endpoint, jsonTxReader, 400)
  225. assert.NoError(t, err)
  226. // Wrong rq
  227. badTx = tc.poolTxsToSend[0]
  228. rqFromIdx := "hez:foo:30"
  229. badTx.RqFromIdx = &rqFromIdx
  230. jsonTxBytes, err = json.Marshal(badTx)
  231. assert.NoError(t, err)
  232. jsonTxReader = bytes.NewReader(jsonTxBytes)
  233. err = doBadReq("POST", endpoint, jsonTxReader, 400)
  234. assert.NoError(t, err)
  235. // GET
  236. endpoint += "/"
  237. for _, tx := range tc.poolTxsToReceive {
  238. fetchedTx := testPoolTxReceive{}
  239. assert.NoError(
  240. t, doGoodReq(
  241. "GET",
  242. endpoint+tx.TxID.String(),
  243. nil, &fetchedTx,
  244. ),
  245. )
  246. assertPoolTx(t, tx, fetchedTx)
  247. }
  248. // 400
  249. err = doBadReq("GET", endpoint+"0xG20000000156660000000090", nil, 400)
  250. assert.NoError(t, err)
  251. // 404
  252. err = doBadReq("GET", endpoint+"0x020000000156660000000090", nil, 404)
  253. assert.NoError(t, err)
  254. }
  255. func assertPoolTx(t *testing.T, expected, actual testPoolTxReceive) {
  256. // state should be pending
  257. assert.Equal(t, common.PoolL2TxStatePending, actual.State)
  258. expected.State = actual.State
  259. actual.Token.ItemID = 0
  260. // timestamp should be very close to now
  261. assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
  262. expected.Timestamp = actual.Timestamp
  263. // token timestamp
  264. if expected.Token.USDUpdate == nil {
  265. assert.Equal(t, expected.Token.USDUpdate, actual.Token.USDUpdate)
  266. } else {
  267. assert.Equal(t, expected.Token.USDUpdate.Unix(), actual.Token.USDUpdate.Unix())
  268. expected.Token.USDUpdate = actual.Token.USDUpdate
  269. }
  270. assert.Equal(t, expected, actual)
  271. }