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.

272 lines
9.1 KiB

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