arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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 succesfull 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	// Get public key
175	account, err := a.s.LastGetAccount(poolTx.FromIdx)
176	if err != nil {
177		return tracerr.Wrap(err)
178	}
179	// Validate feeAmount
180	_, err = common.CalcFeeAmount(poolTx.Amount, poolTx.Fee)
181	if err != nil {
182		return tracerr.Wrap(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}