package api import ( "errors" "fmt" "math/big" "net/http" ethCommon "github.com/ethereum/go-ethereum/common" "github.com/gin-gonic/gin" "github.com/hermeznetwork/hermez-node/api/apitypes" "github.com/hermeznetwork/hermez-node/common" "github.com/hermeznetwork/hermez-node/db/l2db" "github.com/hermeznetwork/tracerr" "github.com/iden3/go-iden3-crypto/babyjub" ) func (a *API) postPoolTx(c *gin.Context) { // Parse body var receivedTx receivedPoolTx if err := c.ShouldBindJSON(&receivedTx); err != nil { retBadReq(err, c) return } // Transform from received to insert format and validate writeTx := receivedTx.toPoolL2TxWrite() if err := a.verifyPoolL2TxWrite(writeTx); err != nil { retBadReq(err, c) return } writeTx.ClientIP = c.ClientIP() // Insert to DB if err := a.l2.AddTxAPI(writeTx); err != nil { retSQLErr(err, c) return } // Return TxID c.JSON(http.StatusOK, writeTx.TxID.String()) } func (a *API) getPoolTx(c *gin.Context) { // Get TxID txID, err := parseParamTxID(c) if err != nil { retBadReq(err, c) return } // Fetch tx from l2DB tx, err := a.l2.GetTxAPI(txID) if err != nil { retSQLErr(err, c) return } // Build successful response c.JSON(http.StatusOK, tx) } func (a *API) getPoolTxs(c *gin.Context) { // Get idx idx, err := parseIdx(c) if err != nil { retBadReq(err, c) return } // Get state state, err := parseQueryPoolL2TxState(c) if err != nil { retBadReq(err, c) return } // Fetch txs from l2DB txs, err := a.l2.GetPoolTxs(idx, state) if err != nil { retSQLErr(err, c) return } // Build successful response type txsResponse struct { Txs []*l2db.PoolTxAPI `json:"transactions"` } c.JSON(http.StatusOK, &txsResponse{ Txs: txs, }) } type receivedPoolTx struct { TxID common.TxID `json:"id" binding:"required"` Type common.TxType `json:"type" binding:"required"` TokenID common.TokenID `json:"tokenId"` FromIdx apitypes.StrHezIdx `json:"fromAccountIndex" binding:"required"` ToIdx *apitypes.StrHezIdx `json:"toAccountIndex"` ToEthAddr *apitypes.StrHezEthAddr `json:"toHezEthereumAddress"` ToBJJ *apitypes.StrHezBJJ `json:"toBjj"` Amount apitypes.StrBigInt `json:"amount" binding:"required"` Fee common.FeeSelector `json:"fee"` Nonce common.Nonce `json:"nonce"` Signature babyjub.SignatureComp `json:"signature" binding:"required"` RqFromIdx *apitypes.StrHezIdx `json:"requestFromAccountIndex"` RqToIdx *apitypes.StrHezIdx `json:"requestToAccountIndex"` RqToEthAddr *apitypes.StrHezEthAddr `json:"requestToHezEthereumAddress"` RqToBJJ *apitypes.StrHezBJJ `json:"requestToBjj"` RqTokenID *common.TokenID `json:"requestTokenId"` RqAmount *apitypes.StrBigInt `json:"requestAmount"` RqFee *common.FeeSelector `json:"requestFee"` RqNonce *common.Nonce `json:"requestNonce"` } func (tx *receivedPoolTx) toPoolL2TxWrite() *l2db.PoolL2TxWrite { f := new(big.Float).SetInt((*big.Int)(&tx.Amount)) amountF, _ := f.Float64() return &l2db.PoolL2TxWrite{ TxID: tx.TxID, FromIdx: common.Idx(tx.FromIdx), ToIdx: (*common.Idx)(tx.ToIdx), ToEthAddr: (*ethCommon.Address)(tx.ToEthAddr), ToBJJ: (*babyjub.PublicKeyComp)(tx.ToBJJ), TokenID: tx.TokenID, Amount: (*big.Int)(&tx.Amount), AmountFloat: amountF, Fee: tx.Fee, Nonce: tx.Nonce, State: common.PoolL2TxStatePending, Signature: tx.Signature, RqFromIdx: (*common.Idx)(tx.RqFromIdx), RqToIdx: (*common.Idx)(tx.RqToIdx), RqToEthAddr: (*ethCommon.Address)(tx.RqToEthAddr), RqToBJJ: (*babyjub.PublicKeyComp)(tx.RqToBJJ), RqTokenID: tx.RqTokenID, RqAmount: (*big.Int)(tx.RqAmount), RqFee: tx.RqFee, RqNonce: tx.RqNonce, Type: tx.Type, } } func (a *API) verifyPoolL2TxWrite(txw *l2db.PoolL2TxWrite) error { poolTx := common.PoolL2Tx{ TxID: txw.TxID, FromIdx: txw.FromIdx, TokenID: txw.TokenID, Amount: txw.Amount, Fee: txw.Fee, Nonce: txw.Nonce, // State: txw.State, Signature: txw.Signature, RqAmount: txw.RqAmount, Type: txw.Type, } // ToIdx if txw.ToIdx != nil { poolTx.ToIdx = *txw.ToIdx } // ToEthAddr if txw.ToEthAddr == nil { poolTx.ToEthAddr = common.EmptyAddr } else { poolTx.ToEthAddr = *txw.ToEthAddr } // ToBJJ if txw.ToBJJ == nil { poolTx.ToBJJ = common.EmptyBJJComp } else { poolTx.ToBJJ = *txw.ToBJJ } // RqFromIdx if txw.RqFromIdx != nil { poolTx.RqFromIdx = *txw.RqFromIdx } // RqToIdx if txw.RqToIdx != nil { poolTx.RqToIdx = *txw.RqToIdx } // RqToEthAddr if txw.RqToEthAddr == nil { poolTx.RqToEthAddr = common.EmptyAddr } else { poolTx.RqToEthAddr = *txw.RqToEthAddr } // RqToBJJ if txw.RqToBJJ == nil { poolTx.RqToBJJ = common.EmptyBJJComp } else { poolTx.RqToBJJ = *txw.RqToBJJ } // RqTokenID if txw.RqTokenID != nil { poolTx.RqTokenID = *txw.RqTokenID } // RqFee if txw.RqFee != nil { poolTx.RqFee = *txw.RqFee } // RqNonce if txw.RqNonce != nil { poolTx.RqNonce = *txw.RqNonce } // Check type and id _, err := common.NewPoolL2Tx(&poolTx) if err != nil { return tracerr.Wrap(err) } // Validate feeAmount _, err = common.CalcFeeAmount(poolTx.Amount, poolTx.Fee) if err != nil { return tracerr.Wrap(err) } // Get public key account, err := a.h.GetCommonAccountAPI(poolTx.FromIdx) if err != nil { return tracerr.Wrap(fmt.Errorf("Error getting from account: %w", err)) } // Validate TokenID if poolTx.TokenID != account.TokenID { return tracerr.Wrap(fmt.Errorf("tx.TokenID (%v) != account.TokenID (%v)", poolTx.TokenID, account.TokenID)) } // Check signature if !poolTx.VerifySignature(a.chainID, account.BJJ) { return tracerr.Wrap(errors.New("wrong signature")) } return nil }