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.

223 lines
5.8 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 idx
  55. idx, err := parseIdx(c)
  56. if err != nil {
  57. retBadReq(err, c)
  58. return
  59. }
  60. // Get state
  61. state, err := parseQueryPoolL2TxState(c)
  62. if err != nil {
  63. retBadReq(err, c)
  64. return
  65. }
  66. // Fetch txs from l2DB
  67. txs, err := a.l2.GetPoolTxs(idx, state)
  68. if err != nil {
  69. retSQLErr(err, c)
  70. return
  71. }
  72. // Build successful response
  73. type txsResponse struct {
  74. Txs []*l2db.PoolTxAPI `json:"transactions"`
  75. }
  76. c.JSON(http.StatusOK, &txsResponse{
  77. Txs: txs,
  78. })
  79. }
  80. type receivedPoolTx struct {
  81. TxID common.TxID `json:"id" binding:"required"`
  82. Type common.TxType `json:"type" binding:"required"`
  83. TokenID common.TokenID `json:"tokenId"`
  84. FromIdx apitypes.StrHezIdx `json:"fromAccountIndex" binding:"required"`
  85. ToIdx *apitypes.StrHezIdx `json:"toAccountIndex"`
  86. ToEthAddr *apitypes.StrHezEthAddr `json:"toHezEthereumAddress"`
  87. ToBJJ *apitypes.StrHezBJJ `json:"toBjj"`
  88. Amount apitypes.StrBigInt `json:"amount" binding:"required"`
  89. Fee common.FeeSelector `json:"fee"`
  90. Nonce common.Nonce `json:"nonce"`
  91. Signature babyjub.SignatureComp `json:"signature" binding:"required"`
  92. RqFromIdx *apitypes.StrHezIdx `json:"requestFromAccountIndex"`
  93. RqToIdx *apitypes.StrHezIdx `json:"requestToAccountIndex"`
  94. RqToEthAddr *apitypes.StrHezEthAddr `json:"requestToHezEthereumAddress"`
  95. RqToBJJ *apitypes.StrHezBJJ `json:"requestToBjj"`
  96. RqTokenID *common.TokenID `json:"requestTokenId"`
  97. RqAmount *apitypes.StrBigInt `json:"requestAmount"`
  98. RqFee *common.FeeSelector `json:"requestFee"`
  99. RqNonce *common.Nonce `json:"requestNonce"`
  100. }
  101. func (tx *receivedPoolTx) toPoolL2TxWrite() *l2db.PoolL2TxWrite {
  102. f := new(big.Float).SetInt((*big.Int)(&tx.Amount))
  103. amountF, _ := f.Float64()
  104. return &l2db.PoolL2TxWrite{
  105. TxID: tx.TxID,
  106. FromIdx: common.Idx(tx.FromIdx),
  107. ToIdx: (*common.Idx)(tx.ToIdx),
  108. ToEthAddr: (*ethCommon.Address)(tx.ToEthAddr),
  109. ToBJJ: (*babyjub.PublicKeyComp)(tx.ToBJJ),
  110. TokenID: tx.TokenID,
  111. Amount: (*big.Int)(&tx.Amount),
  112. AmountFloat: amountF,
  113. Fee: tx.Fee,
  114. Nonce: tx.Nonce,
  115. State: common.PoolL2TxStatePending,
  116. Signature: tx.Signature,
  117. RqFromIdx: (*common.Idx)(tx.RqFromIdx),
  118. RqToIdx: (*common.Idx)(tx.RqToIdx),
  119. RqToEthAddr: (*ethCommon.Address)(tx.RqToEthAddr),
  120. RqToBJJ: (*babyjub.PublicKeyComp)(tx.RqToBJJ),
  121. RqTokenID: tx.RqTokenID,
  122. RqAmount: (*big.Int)(tx.RqAmount),
  123. RqFee: tx.RqFee,
  124. RqNonce: tx.RqNonce,
  125. Type: tx.Type,
  126. }
  127. }
  128. func (a *API) verifyPoolL2TxWrite(txw *l2db.PoolL2TxWrite) error {
  129. poolTx := common.PoolL2Tx{
  130. TxID: txw.TxID,
  131. FromIdx: txw.FromIdx,
  132. TokenID: txw.TokenID,
  133. Amount: txw.Amount,
  134. Fee: txw.Fee,
  135. Nonce: txw.Nonce,
  136. // State: txw.State,
  137. Signature: txw.Signature,
  138. RqAmount: txw.RqAmount,
  139. Type: txw.Type,
  140. }
  141. // ToIdx
  142. if txw.ToIdx != nil {
  143. poolTx.ToIdx = *txw.ToIdx
  144. }
  145. // ToEthAddr
  146. if txw.ToEthAddr == nil {
  147. poolTx.ToEthAddr = common.EmptyAddr
  148. } else {
  149. poolTx.ToEthAddr = *txw.ToEthAddr
  150. }
  151. // ToBJJ
  152. if txw.ToBJJ == nil {
  153. poolTx.ToBJJ = common.EmptyBJJComp
  154. } else {
  155. poolTx.ToBJJ = *txw.ToBJJ
  156. }
  157. // RqFromIdx
  158. if txw.RqFromIdx != nil {
  159. poolTx.RqFromIdx = *txw.RqFromIdx
  160. }
  161. // RqToIdx
  162. if txw.RqToIdx != nil {
  163. poolTx.RqToIdx = *txw.RqToIdx
  164. }
  165. // RqToEthAddr
  166. if txw.RqToEthAddr == nil {
  167. poolTx.RqToEthAddr = common.EmptyAddr
  168. } else {
  169. poolTx.RqToEthAddr = *txw.RqToEthAddr
  170. }
  171. // RqToBJJ
  172. if txw.RqToBJJ == nil {
  173. poolTx.RqToBJJ = common.EmptyBJJComp
  174. } else {
  175. poolTx.RqToBJJ = *txw.RqToBJJ
  176. }
  177. // RqTokenID
  178. if txw.RqTokenID != nil {
  179. poolTx.RqTokenID = *txw.RqTokenID
  180. }
  181. // RqFee
  182. if txw.RqFee != nil {
  183. poolTx.RqFee = *txw.RqFee
  184. }
  185. // RqNonce
  186. if txw.RqNonce != nil {
  187. poolTx.RqNonce = *txw.RqNonce
  188. }
  189. // Check type and id
  190. _, err := common.NewPoolL2Tx(&poolTx)
  191. if err != nil {
  192. return tracerr.Wrap(err)
  193. }
  194. // Validate feeAmount
  195. _, err = common.CalcFeeAmount(poolTx.Amount, poolTx.Fee)
  196. if err != nil {
  197. return tracerr.Wrap(err)
  198. }
  199. // Get public key
  200. account, err := a.h.GetCommonAccountAPI(poolTx.FromIdx)
  201. if err != nil {
  202. return tracerr.Wrap(fmt.Errorf("Error getting from account: %w", err))
  203. }
  204. // Validate TokenID
  205. if poolTx.TokenID != account.TokenID {
  206. return tracerr.Wrap(fmt.Errorf("tx.TokenID (%v) != account.TokenID (%v)",
  207. poolTx.TokenID, account.TokenID))
  208. }
  209. // Check signature
  210. if !poolTx.VerifySignature(a.chainID, account.BJJ) {
  211. return tracerr.Wrap(errors.New("wrong signature"))
  212. }
  213. return nil
  214. }