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