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.

229 lines
5.9 KiB

  1. package api
  2. import (
  3. "errors"
  4. "fmt"
  5. "math/big"
  6. "net/http"
  7. ethCommon "github.com/ethereum/go-ethereum/common"
  8. "github.com/gin-gonic/gin"
  9. "github.com/hermeznetwork/hermez-node/api/apitypes"
  10. "github.com/hermeznetwork/hermez-node/common"
  11. "github.com/hermeznetwork/hermez-node/db/l2db"
  12. "github.com/hermeznetwork/tracerr"
  13. "github.com/iden3/go-iden3-crypto/babyjub"
  14. )
  15. func (a *API) postPoolTx(c *gin.Context) {
  16. // Parse body
  17. var receivedTx receivedPoolTx
  18. if err := c.ShouldBindJSON(&receivedTx); err != nil {
  19. retBadReq(err, c)
  20. return
  21. }
  22. // Transform from received to insert format and validate
  23. writeTx := receivedTx.toPoolL2TxWrite()
  24. if err := a.verifyPoolL2TxWrite(writeTx); err != nil {
  25. retBadReq(err, c)
  26. return
  27. }
  28. writeTx.ClientIP = c.ClientIP()
  29. // Insert to DB
  30. if err := a.l2.AddTxAPI(writeTx); err != nil {
  31. retSQLErr(err, c)
  32. return
  33. }
  34. // Return TxID
  35. c.JSON(http.StatusOK, writeTx.TxID.String())
  36. }
  37. func (a *API) getPoolTx(c *gin.Context) {
  38. // Get TxID
  39. txID, err := parseParamTxID(c)
  40. if err != nil {
  41. retBadReq(err, c)
  42. return
  43. }
  44. // Fetch tx from l2DB
  45. tx, err := a.l2.GetTxAPI(txID)
  46. if err != nil {
  47. retSQLErr(err, c)
  48. return
  49. }
  50. // Build successful response
  51. c.JSON(http.StatusOK, tx)
  52. }
  53. func (a *API) getPoolTxs(c *gin.Context) {
  54. // Get from idx
  55. fromIdx, err := parseFromIdx(c)
  56. if err != nil {
  57. retBadReq(err, c)
  58. return
  59. }
  60. // Get to idx
  61. toIdx, err := parseToIdx(c)
  62. if err != nil {
  63. retBadReq(err, c)
  64. return
  65. }
  66. // Get state
  67. state, err := parseQueryPoolL2TxState(c)
  68. if err != nil {
  69. retBadReq(err, c)
  70. return
  71. }
  72. // Fetch txs from l2DB
  73. txs, err := a.l2.GetPoolTxs(fromIdx, toIdx, state)
  74. if err != nil {
  75. retSQLErr(err, c)
  76. return
  77. }
  78. // Build successful response
  79. type txsResponse struct {
  80. Txs []*l2db.PoolTxAPI `json:"transactions"`
  81. }
  82. c.JSON(http.StatusOK, &txsResponse{
  83. Txs: txs,
  84. })
  85. }
  86. type receivedPoolTx struct {
  87. TxID common.TxID `json:"id" binding:"required"`
  88. Type common.TxType `json:"type" binding:"required"`
  89. TokenID common.TokenID `json:"tokenId"`
  90. FromIdx apitypes.StrHezIdx `json:"fromAccountIndex" binding:"required"`
  91. ToIdx *apitypes.StrHezIdx `json:"toAccountIndex"`
  92. ToEthAddr *apitypes.StrHezEthAddr `json:"toHezEthereumAddress"`
  93. ToBJJ *apitypes.StrHezBJJ `json:"toBjj"`
  94. Amount apitypes.StrBigInt `json:"amount" binding:"required"`
  95. Fee common.FeeSelector `json:"fee"`
  96. Nonce common.Nonce `json:"nonce"`
  97. Signature babyjub.SignatureComp `json:"signature" binding:"required"`
  98. RqFromIdx *apitypes.StrHezIdx `json:"requestFromAccountIndex"`
  99. RqToIdx *apitypes.StrHezIdx `json:"requestToAccountIndex"`
  100. RqToEthAddr *apitypes.StrHezEthAddr `json:"requestToHezEthereumAddress"`
  101. RqToBJJ *apitypes.StrHezBJJ `json:"requestToBjj"`
  102. RqTokenID *common.TokenID `json:"requestTokenId"`
  103. RqAmount *apitypes.StrBigInt `json:"requestAmount"`
  104. RqFee *common.FeeSelector `json:"requestFee"`
  105. RqNonce *common.Nonce `json:"requestNonce"`
  106. }
  107. func (tx *receivedPoolTx) toPoolL2TxWrite() *l2db.PoolL2TxWrite {
  108. f := new(big.Float).SetInt((*big.Int)(&tx.Amount))
  109. amountF, _ := f.Float64()
  110. return &l2db.PoolL2TxWrite{
  111. TxID: tx.TxID,
  112. FromIdx: common.Idx(tx.FromIdx),
  113. ToIdx: (*common.Idx)(tx.ToIdx),
  114. ToEthAddr: (*ethCommon.Address)(tx.ToEthAddr),
  115. ToBJJ: (*babyjub.PublicKeyComp)(tx.ToBJJ),
  116. TokenID: tx.TokenID,
  117. Amount: (*big.Int)(&tx.Amount),
  118. AmountFloat: amountF,
  119. Fee: tx.Fee,
  120. Nonce: tx.Nonce,
  121. State: common.PoolL2TxStatePending,
  122. Signature: tx.Signature,
  123. RqFromIdx: (*common.Idx)(tx.RqFromIdx),
  124. RqToIdx: (*common.Idx)(tx.RqToIdx),
  125. RqToEthAddr: (*ethCommon.Address)(tx.RqToEthAddr),
  126. RqToBJJ: (*babyjub.PublicKeyComp)(tx.RqToBJJ),
  127. RqTokenID: tx.RqTokenID,
  128. RqAmount: (*big.Int)(tx.RqAmount),
  129. RqFee: tx.RqFee,
  130. RqNonce: tx.RqNonce,
  131. Type: tx.Type,
  132. }
  133. }
  134. func (a *API) verifyPoolL2TxWrite(txw *l2db.PoolL2TxWrite) error {
  135. poolTx := common.PoolL2Tx{
  136. TxID: txw.TxID,
  137. FromIdx: txw.FromIdx,
  138. TokenID: txw.TokenID,
  139. Amount: txw.Amount,
  140. Fee: txw.Fee,
  141. Nonce: txw.Nonce,
  142. // State: txw.State,
  143. Signature: txw.Signature,
  144. RqAmount: txw.RqAmount,
  145. Type: txw.Type,
  146. }
  147. // ToIdx
  148. if txw.ToIdx != nil {
  149. poolTx.ToIdx = *txw.ToIdx
  150. }
  151. // ToEthAddr
  152. if txw.ToEthAddr == nil {
  153. poolTx.ToEthAddr = common.EmptyAddr
  154. } else {
  155. poolTx.ToEthAddr = *txw.ToEthAddr
  156. }
  157. // ToBJJ
  158. if txw.ToBJJ == nil {
  159. poolTx.ToBJJ = common.EmptyBJJComp
  160. } else {
  161. poolTx.ToBJJ = *txw.ToBJJ
  162. }
  163. // RqFromIdx
  164. if txw.RqFromIdx != nil {
  165. poolTx.RqFromIdx = *txw.RqFromIdx
  166. }
  167. // RqToIdx
  168. if txw.RqToIdx != nil {
  169. poolTx.RqToIdx = *txw.RqToIdx
  170. }
  171. // RqToEthAddr
  172. if txw.RqToEthAddr == nil {
  173. poolTx.RqToEthAddr = common.EmptyAddr
  174. } else {
  175. poolTx.RqToEthAddr = *txw.RqToEthAddr
  176. }
  177. // RqToBJJ
  178. if txw.RqToBJJ == nil {
  179. poolTx.RqToBJJ = common.EmptyBJJComp
  180. } else {
  181. poolTx.RqToBJJ = *txw.RqToBJJ
  182. }
  183. // RqTokenID
  184. if txw.RqTokenID != nil {
  185. poolTx.RqTokenID = *txw.RqTokenID
  186. }
  187. // RqFee
  188. if txw.RqFee != nil {
  189. poolTx.RqFee = *txw.RqFee
  190. }
  191. // RqNonce
  192. if txw.RqNonce != nil {
  193. poolTx.RqNonce = *txw.RqNonce
  194. }
  195. // Check type and id
  196. _, err := common.NewPoolL2Tx(&poolTx)
  197. if err != nil {
  198. return tracerr.Wrap(err)
  199. }
  200. // Validate feeAmount
  201. _, err = common.CalcFeeAmount(poolTx.Amount, poolTx.Fee)
  202. if err != nil {
  203. return tracerr.Wrap(err)
  204. }
  205. // Get public key
  206. account, err := a.h.GetCommonAccountAPI(poolTx.FromIdx)
  207. if err != nil {
  208. return tracerr.Wrap(fmt.Errorf("Error getting from account: %w", err))
  209. }
  210. // Validate TokenID
  211. if poolTx.TokenID != account.TokenID {
  212. return tracerr.Wrap(fmt.Errorf("tx.TokenID (%v) != account.TokenID (%v)",
  213. poolTx.TokenID, account.TokenID))
  214. }
  215. // Check signature
  216. if !poolTx.VerifySignature(a.chainID, account.BJJ) {
  217. return tracerr.Wrap(errors.New("wrong signature"))
  218. }
  219. return nil
  220. }