Skip to content

Commit

Permalink
Merge pull request #24 from ElrondNetwork/EN-5385-vmvalues
Browse files Browse the repository at this point in the history
En 5385 vmvalues
  • Loading branch information
iulianpascalau authored Jan 15, 2020
2 parents 5a48c75 + abdf5b3 commit 8fdbed8
Show file tree
Hide file tree
Showing 20 changed files with 625 additions and 584 deletions.
3 changes: 3 additions & 0 deletions api/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import (
// ErrInvalidAppContext signals an invalid context passed to the routing system
var ErrInvalidAppContext = errors.New("invalid app context")

// ErrInvalidJSONRequest signals an error in json request formatting
var ErrInvalidJSONRequest = errors.New("invalid json request")

// ErrValidation signals an error in validation
var ErrValidation = errors.New("validation error")

Expand Down
10 changes: 6 additions & 4 deletions api/mock/facade.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package mock
import (
"math/big"

"github.com/ElrondNetwork/elrond-go/process"
"github.com/ElrondNetwork/elrond-proxy-go/data"
vmcommon "github.com/ElrondNetwork/elrond-vm-common"
)

// Facade is the mock implementation of a node's router handler
Expand All @@ -12,7 +14,7 @@ type Facade struct {
SendTransactionHandler func(tx *data.Transaction) (int, string, error)
SendMultipleTransactionsHandler func(txs []*data.Transaction) (uint64, error)
SendUserFundsCalled func(receiver string, value *big.Int) error
GetVmValueHandler func(resType string, address string, funcName string, argsBuff ...[]byte) ([]byte, error)
ExecuteSCQueryHandler func(query *process.SCQuery) (*vmcommon.VMOutput, error)
GetHeartbeatDataHandler func() (*data.HeartbeatResponse, error)
ValidatorStatisticsHandler func() (map[string]*data.ValidatorApiResponse, error)
}
Expand Down Expand Up @@ -42,9 +44,9 @@ func (f *Facade) SendUserFunds(receiver string, value *big.Int) error {
return f.SendUserFundsCalled(receiver, value)
}

// GetVmValue is the mock implementation of a handler's GetVmValue method
func (f *Facade) GetVmValue(resType string, address string, funcName string, argsBuff ...[]byte) ([]byte, error) {
return f.GetVmValueHandler(resType, address, funcName, argsBuff...)
// ExecuteSCQuery is a mock implementation.
func (f *Facade) ExecuteSCQuery(query *process.SCQuery) (*vmcommon.VMOutput, error) {
return f.ExecuteSCQueryHandler(query)
}

// GetHeartbeatData is the mock implementation of a handler's GetHeartbeatData method
Expand Down
7 changes: 6 additions & 1 deletion api/vmValues/interface.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package vmValues

import (
"github.com/ElrondNetwork/elrond-go/process"
vmcommon "github.com/ElrondNetwork/elrond-vm-common"
)

// FacadeHandler interface defines methods that can be used from `elrondFacade` context variable
type FacadeHandler interface {
GetVmValue(resType string, address string, funcName string, argsBuff ...[]byte) ([]byte, error)
ExecuteSCQuery(*process.SCQuery) (*vmcommon.VMOutput, error)
}
130 changes: 80 additions & 50 deletions api/vmValues/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,96 +2,126 @@ package vmValues

import (
"encoding/hex"
"errors"
"fmt"
"math/big"
"net/http"

"github.com/ElrondNetwork/elrond-go/process"
apiErrors "github.com/ElrondNetwork/elrond-proxy-go/api/errors"
vmcommon "github.com/ElrondNetwork/elrond-vm-common"
"github.com/gin-gonic/gin"
)

// VmValueRequest represents the structure on which user input for generating a new transaction will validate against
type VmValueRequest struct {
// VMValueRequest represents the structure on which user input for generating a new transaction will validate against
type VMValueRequest struct {
ScAddress string `form:"scAddress" json:"scAddress"`
FuncName string `form:"funcName" json:"funcName"`
Args []string `form:"args" json:"args"`
}

// Routes defines address related routes
func Routes(router *gin.RouterGroup) {
router.POST("/hex", GetVmValueAsHexBytes)
router.POST("/string", GetVmValueAsString)
router.POST("/int", GetVmValueAsBigInt)
router.POST("/hex", getHex)
router.POST("/string", getString)
router.POST("/int", getInt)
router.POST("/query", executeQuery)
}

func vmValueFromAccount(c *gin.Context, resType string) ([]byte, int, error) {
ef, ok := c.MustGet("elrondProxyFacade").(FacadeHandler)
if !ok {
return nil, http.StatusInternalServerError, apiErrors.ErrInvalidAppContext
}
// getHex returns the data as bytes, hex-encoded
func getHex(context *gin.Context) {
doGetVMValue(context, vmcommon.AsHex)
}

var gval = VmValueRequest{}
err := c.ShouldBindJSON(&gval)
if err != nil {
return nil, http.StatusBadRequest, err
}
// getString returns the data as string
func getString(context *gin.Context) {
doGetVMValue(context, vmcommon.AsString)
}

argsBuff := make([][]byte, 0)
for _, arg := range gval.Args {
buff, err := hex.DecodeString(arg)
if err != nil {
return nil,
http.StatusBadRequest,
errors.New(fmt.Sprintf("'%s' is not a valid hex string: %s", arg, err.Error()))
}
// getInt returns the data as big int
func getInt(context *gin.Context) {
doGetVMValue(context, vmcommon.AsBigIntString)
}

argsBuff = append(argsBuff, buff)
func doGetVMValue(context *gin.Context, asType vmcommon.ReturnDataKind) {
vmOutput, err := doExecuteQuery(context)

if err != nil {
returnBadRequest(context, "doGetVMValue", err)
return
}

returnedData, err := ef.GetVmValue(resType, gval.ScAddress, gval.FuncName, argsBuff...)
returnData, err := vmOutput.GetFirstReturnData(asType)
if err != nil {
return nil, http.StatusBadRequest, err
returnBadRequest(context, "doGetVMValue", err)
return
}

return returnedData, http.StatusOK, nil
returnOkResponse(context, returnData)
}

// GetVmValueAsHexBytes returns the data as byte slice
func GetVmValueAsHexBytes(c *gin.Context) {
data, status, err := vmValueFromAccount(c, "hex")
// executeQuery returns the data as string
func executeQuery(context *gin.Context) {
vmOutput, err := doExecuteQuery(context)
if err != nil {
c.JSON(status, gin.H{"error": fmt.Sprintf("get vm value as hex bytes: %s", err)})
returnBadRequest(context, "executeQuery", err)
return
}

c.JSON(http.StatusOK, gin.H{"data": hex.EncodeToString(data)})
returnOkResponse(context, vmOutput)
}

// GetVmValueAsString returns the data as string
func GetVmValueAsString(c *gin.Context) {
data, status, err := vmValueFromAccount(c, "string")
func doExecuteQuery(context *gin.Context) (*vmcommon.VMOutput, error) {
facade, ok := context.MustGet("elrondProxyFacade").(FacadeHandler)
if !ok {
return nil, apiErrors.ErrInvalidAppContext
}

request := VMValueRequest{}
err := context.ShouldBindJSON(&request)
if err != nil {
c.JSON(status, gin.H{"error": fmt.Sprintf("get vm value as string: %s", err)})
return
return nil, apiErrors.ErrInvalidJSONRequest
}

c.JSON(http.StatusOK, gin.H{"data": string(data)})
command, err := createSCQuery(&request)
if err != nil {
return nil, err
}

vmOutput, err := facade.ExecuteSCQuery(command)
if err != nil {
return nil, err
}

return vmOutput, nil
}

// GetVmValueAsBigInt returns the data as big int
func GetVmValueAsBigInt(c *gin.Context) {
data, status, err := vmValueFromAccount(c, "int")
func createSCQuery(request *VMValueRequest) (*process.SCQuery, error) {
decodedAddress, err := hex.DecodeString(request.ScAddress)
if err != nil {
c.JSON(status, gin.H{"error": fmt.Sprintf("get vm value as big int: %s", err)})
return
return nil, fmt.Errorf("'%s' is not a valid hex string: %s", request.ScAddress, err.Error())
}

value, ok := big.NewInt(0).SetString(string(data), 10)
if !ok {
c.JSON(status, gin.H{"error": fmt.Sprintf("value %s could not be converted to a big int", string(data))})
return
arguments := make([][]byte, len(request.Args))
for i, arg := range request.Args {
argBytes, err := hex.DecodeString(arg)
if err != nil {
return nil, fmt.Errorf("'%s' is not a valid hex string: %s", arg, err.Error())
}

arguments[i] = append(arguments[i], argBytes...)
}

c.JSON(http.StatusOK, gin.H{"data": value.String()})
return &process.SCQuery{
ScAddress: decodedAddress,
FuncName: request.FuncName,
Arguments: arguments,
}, nil
}

func returnBadRequest(context *gin.Context, errScope string, err error) {
message := fmt.Sprintf("%s: %s", errScope, err)
context.JSON(http.StatusBadRequest, gin.H{"error": message})
}

func returnOkResponse(context *gin.Context, data interface{}) {
context.JSON(http.StatusOK, gin.H{"data": data})
}
Loading

0 comments on commit 8fdbed8

Please sign in to comment.