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