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