Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block coordinates on vm query #5447

Merged
44 changes: 20 additions & 24 deletions api/groups/vmValuesGroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/multiversx/mx-chain-core-go/core"
"github.com/multiversx/mx-chain-core-go/core/check"
apiData "github.com/multiversx/mx-chain-core-go/data/api"
"github.com/multiversx/mx-chain-core-go/data/vm"
"github.com/multiversx/mx-chain-go/api/errors"
"github.com/multiversx/mx-chain-go/api/shared"
Expand All @@ -26,7 +27,7 @@ const (

// vmValuesFacadeHandler defines the methods to be implemented by a facade for vm-values requests
type vmValuesFacadeHandler interface {
ExecuteSCQuery(*process.SCQuery) (*vm.VMOutputApi, error)
ExecuteSCQuery(*process.SCQuery) (*vm.VMOutputApi, apiData.BlockInfo, error)
DecodeAddressPubkey(pk string) ([]byte, error)
IsInterfaceNil() bool
}
Expand Down Expand Up @@ -102,7 +103,7 @@ func (vvg *vmValuesGroup) getInt(context *gin.Context) {
}

func (vvg *vmValuesGroup) doGetVMValue(context *gin.Context, asType vm.ReturnDataKind) {
vmOutput, execErrMsg, err := vvg.doExecuteQuery(context)
vmOutput, execErrMsg, blockInfo, err := vvg.doExecuteQuery(context)

if err != nil {
vvg.returnBadRequest(context, "doGetVMValue", err)
Expand All @@ -114,67 +115,62 @@ func (vvg *vmValuesGroup) doGetVMValue(context *gin.Context, asType vm.ReturnDat
execErrMsg += " " + err.Error()
}

vvg.returnOkResponse(context, returnData, execErrMsg)
vvg.returnOkResponse(context, returnData, execErrMsg, blockInfo)
}

// executeQuery returns the data as string
func (vvg *vmValuesGroup) executeQuery(context *gin.Context) {
vmOutput, execErrMsg, err := vvg.doExecuteQuery(context)
vmOutput, execErrMsg, blockInfo, err := vvg.doExecuteQuery(context)
if err != nil {
vvg.returnBadRequest(context, "executeQuery", err)
return
}

vvg.returnOkResponse(context, vmOutput, execErrMsg)
vvg.returnOkResponse(context, vmOutput, execErrMsg, blockInfo)
}

func (vvg *vmValuesGroup) doExecuteQuery(context *gin.Context) (*vm.VMOutputApi, string, error) {
func (vvg *vmValuesGroup) doExecuteQuery(context *gin.Context) (*vm.VMOutputApi, string, apiData.BlockInfo, error) {
request := VMValueRequest{}
err := context.ShouldBindJSON(&request)
if err != nil {
return nil, "", errors.ErrInvalidJSONRequest
return nil, "", apiData.BlockInfo{}, errors.ErrInvalidJSONRequest
}

command, err := vvg.createSCQuery(&request)
if err != nil {
return nil, "", err
return nil, "", apiData.BlockInfo{}, err
}

command.BlockNonce, command.BlockHash, command.BlockRootHash, err = extractBlockCoordinates(context)
command.BlockNonce, command.BlockHash, err = extractBlockCoordinates(context)
if err != nil {
return nil, "", err
return nil, "", apiData.BlockInfo{}, err
}

vmOutputApi, err := vvg.getFacade().ExecuteSCQuery(command)
vmOutputApi, blockInfo, err := vvg.getFacade().ExecuteSCQuery(command)
if err != nil {
return nil, "", err
return nil, "", apiData.BlockInfo{}, err
}

vmExecErrMsg := ""
if len(vmOutputApi.ReturnCode) > 0 && vmOutputApi.ReturnCode != vmcommon.Ok.String() {
vmExecErrMsg = vmOutputApi.ReturnCode + ":" + vmOutputApi.ReturnMessage
}

return vmOutputApi, vmExecErrMsg, nil
return vmOutputApi, vmExecErrMsg, blockInfo, nil
}

func extractBlockCoordinates(context *gin.Context) (core.OptionalUint64, []byte, []byte, error) {
func extractBlockCoordinates(context *gin.Context) (core.OptionalUint64, []byte, error) {
blockNonce, err := parseUint64UrlParam(context, urlParamBlockNonce)
if err != nil {
return core.OptionalUint64{}, nil, nil, err
return core.OptionalUint64{}, nil, fmt.Errorf("%w for block nonce", err)
}

blockHash, err := parseHexBytesUrlParam(context, urlParamBlockHash)
if err != nil {
return core.OptionalUint64{}, nil, nil, err
return core.OptionalUint64{}, nil, fmt.Errorf("%w for block hash", err)
}

blockRootHash, err := parseHexBytesUrlParam(context, urlParamBlockRootHash)
if err != nil {
return core.OptionalUint64{}, nil, nil, err
}

return blockNonce, blockHash, blockRootHash, nil
return blockNonce, blockHash, nil
}
bogdan-rosianu marked this conversation as resolved.
Show resolved Hide resolved

func (vvg *vmValuesGroup) createSCQuery(request *VMValueRequest) (*process.SCQuery, error) {
Expand Down Expand Up @@ -234,11 +230,11 @@ func (vvg *vmValuesGroup) returnBadRequest(context *gin.Context, errScope string
)
}

func (vvg *vmValuesGroup) returnOkResponse(context *gin.Context, data interface{}, errorMsg string) {
func (vvg *vmValuesGroup) returnOkResponse(context *gin.Context, data interface{}, errorMsg string, blockInfo apiData.BlockInfo) {
context.JSON(
http.StatusOK,
shared.GenericAPIResponse{
Data: gin.H{"data": data},
Data: gin.H{"data": data, "blockInfo": blockInfo},
Error: errorMsg,
Code: shared.ReturnCodeSuccess,
},
Expand Down
81 changes: 33 additions & 48 deletions api/groups/vmValuesGroup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/gin-gonic/gin"
"github.com/multiversx/mx-chain-core-go/core"
"github.com/multiversx/mx-chain-core-go/data/api"
"github.com/multiversx/mx-chain-core-go/data/vm"
apiErrors "github.com/multiversx/mx-chain-go/api/errors"
"github.com/multiversx/mx-chain-go/api/groups"
Expand Down Expand Up @@ -62,10 +63,10 @@ func TestGetHex_ShouldWork(t *testing.T) {
valueBuff, _ := hex.DecodeString("DEADBEEF")

facade := mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
return &vm.VMOutputApi{
ReturnData: [][]byte{valueBuff},
}, nil
}, api.BlockInfo{}, nil
},
}

Expand All @@ -89,10 +90,10 @@ func TestGetString_ShouldWork(t *testing.T) {
valueBuff := "DEADBEEF"

facade := mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
return &vm.VMOutputApi{
ReturnData: [][]byte{[]byte(valueBuff)},
}, nil
}, api.BlockInfo{}, nil
},
}

Expand All @@ -116,12 +117,12 @@ func TestGetInt_ShouldWork(t *testing.T) {
value := "1234567"

facade := mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
returnData := big.NewInt(0)
returnData.SetString(value, 10)
return &vm.VMOutputApi{
ReturnData: [][]byte{returnData.Bytes()},
}, nil
}, api.BlockInfo{}, nil
},
}

Expand All @@ -144,7 +145,6 @@ func TestQuery(t *testing.T) {

t.Run("invalid block nonce should error", testQueryShouldError("/vm-values/query?blockNonce=invalid_nonce"))
t.Run("invalid block hash should error", testQueryShouldError("/vm-values/query?blockHash=invalid_nonce"))
t.Run("invalid block root hash should error", testQueryShouldError("/vm-values/query?blockRootHash=invalid_nonce"))
t.Run("should work - block nonce", func(t *testing.T) {
t.Parallel()

Expand All @@ -153,11 +153,11 @@ func TestQuery(t *testing.T) {
HasValue: true,
}
facade := mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
require.Equal(t, providedBlockNonce, query.BlockNonce)
return &vm.VMOutputApi{
ReturnData: [][]byte{big.NewInt(42).Bytes()},
}, nil
}, api.BlockInfo{}, nil
},
}
url := fmt.Sprintf("/vm-values/query?blockNonce=%d", providedBlockNonce.Value)
Expand All @@ -168,40 +168,25 @@ func TestQuery(t *testing.T) {

providedBlockHash := []byte("provided hash")
facade := mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
require.Equal(t, providedBlockHash, query.BlockHash)
return &vm.VMOutputApi{
ReturnData: [][]byte{big.NewInt(42).Bytes()},
}, nil
}, api.BlockInfo{}, nil
},
}
url := fmt.Sprintf("/vm-values/query?blockHash=%s", hex.EncodeToString(providedBlockHash))
testQueryShouldWork(t, url, &facade)
})
t.Run("should work - block root hash", func(t *testing.T) {
t.Parallel()

providedBlockRootHash := []byte("provided root hash")
facade := mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
require.Equal(t, providedBlockRootHash, query.BlockRootHash)
return &vm.VMOutputApi{
ReturnData: [][]byte{big.NewInt(42).Bytes()},
}, nil
},
}
url := fmt.Sprintf("/vm-values/query?blockRootHash=%s", hex.EncodeToString(providedBlockRootHash))
testQueryShouldWork(t, url, &facade)
})
t.Run("should work - no block coordinates", func(t *testing.T) {
t.Parallel()

facade := mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {

return &vm.VMOutputApi{
ReturnData: [][]byte{big.NewInt(42).Bytes()},
}, nil
}, api.BlockInfo{}, nil
},
}
testQueryShouldWork(t, "/vm-values/query", &facade)
Expand Down Expand Up @@ -235,10 +220,10 @@ func testQueryShouldError(url string) func(t *testing.T) {
requestAsBytes, _ := json.Marshal(request)

facade := mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
return &vm.VMOutputApi{
ReturnData: [][]byte{big.NewInt(42).Bytes()},
}, nil
}, api.BlockInfo{}, nil
},
}

Expand Down Expand Up @@ -277,8 +262,8 @@ func TestAllRoutes_FacadeErrorsShouldErr(t *testing.T) {

errExpected := errors.New("some random error")
facade := mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
return nil, errExpected
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
return nil, api.BlockInfo{}, errExpected
},
}

Expand All @@ -296,8 +281,8 @@ func TestAllRoutes_WhenBadAddressShouldErr(t *testing.T) {

errExpected := errors.New("not a valid address")
facade := mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
return &vm.VMOutputApi{}, nil
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
return &vm.VMOutputApi{}, api.BlockInfo{}, nil
},
}

Expand All @@ -315,8 +300,8 @@ func TestAllRoutes_WhenBadArgumentsShouldErr(t *testing.T) {

errExpected := errors.New("not a valid hex string")
facade := mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
return &vm.VMOutputApi{}, nil
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
return &vm.VMOutputApi{}, api.BlockInfo{}, nil
},
}

Expand All @@ -334,8 +319,8 @@ func TestAllRoutes_WhenNoVMReturnDataShouldErr(t *testing.T) {

errExpected := errors.New("no return data")
facade := &mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
return &vm.VMOutputApi{}, nil
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
return &vm.VMOutputApi{}, api.BlockInfo{}, nil
},
}

Expand Down Expand Up @@ -365,8 +350,8 @@ func TestAllRoutes_WhenBadJsonShouldErr(t *testing.T) {
t.Parallel()

facade := mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
return &vm.VMOutputApi{}, nil
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
return &vm.VMOutputApi{}, api.BlockInfo{}, nil
},
}

Expand All @@ -385,8 +370,8 @@ func TestAllRoutes_DecodeAddressPubkeyFailsShouldErr(t *testing.T) {
}
return hex.DecodeString(pk)
},
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
return &vm.VMOutputApi{}, nil
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
return &vm.VMOutputApi{}, api.BlockInfo{}, nil
},
}

Expand All @@ -403,8 +388,8 @@ func TestAllRoutes_SetStringFailsShouldErr(t *testing.T) {
t.Parallel()

facade := mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
return &vm.VMOutputApi{}, nil
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
return &vm.VMOutputApi{}, api.BlockInfo{}, nil
},
}

Expand Down Expand Up @@ -444,12 +429,12 @@ func TestVMValuesGroup_UpdateFacade(t *testing.T) {

valueBuff, _ := hex.DecodeString("DEADBEEF")
facade := &mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {

return &vm.VMOutputApi{
ReturnData: [][]byte{valueBuff},
ReturnCode: "NOK", // coverage
}, nil
}, api.BlockInfo{}, nil
},
}

Expand Down Expand Up @@ -480,11 +465,11 @@ func TestVMValuesGroup_UpdateFacade(t *testing.T) {
require.Equal(t, hex.EncodeToString(valueBuff), response.Data)

newFacade := &mock.FacadeStub{
ExecuteSCQueryHandler: func(query *process.SCQuery) (vmOutput *vm.VMOutputApi, e error) {
ExecuteSCQueryHandler: func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {

return &vm.VMOutputApi{
ReturnData: nil,
}, expectedErr
}, api.BlockInfo{}, expectedErr
bogdan-rosianu marked this conversation as resolved.
Show resolved Hide resolved
},
}

Expand Down
4 changes: 2 additions & 2 deletions api/mock/facadeStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type FacadeStub struct {
ValidateTransactionHandler func(tx *transaction.Transaction) error
ValidateTransactionForSimulationHandler func(tx *transaction.Transaction, bypassSignature bool) error
SendBulkTransactionsHandler func(txs []*transaction.Transaction) (uint64, error)
ExecuteSCQueryHandler func(query *process.SCQuery) (*vm.VMOutputApi, error)
ExecuteSCQueryHandler func(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error)
StatusMetricsHandler func() external.StatusMetricsHandler
ValidatorStatisticsHandler func() (map[string]*accounts.ValidatorApiResponse, error)
ComputeTransactionGasLimitHandler func(tx *transaction.Transaction) (*transaction.CostResponse, error)
Expand Down Expand Up @@ -331,7 +331,7 @@ func (f *FacadeStub) ValidatorStatisticsApi() (map[string]*accounts.ValidatorApi
}

// ExecuteSCQuery is a mock implementation.
func (f *FacadeStub) ExecuteSCQuery(query *process.SCQuery) (*vm.VMOutputApi, error) {
func (f *FacadeStub) ExecuteSCQuery(query *process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error) {
return f.ExecuteSCQueryHandler(query)
}

Expand Down
2 changes: 1 addition & 1 deletion api/shared/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ type FacadeHandler interface {
ComputeTransactionGasLimit(tx *transaction.Transaction) (*transaction.CostResponse, error)
EncodeAddressPubkey(pk []byte) (string, error)
ValidatorStatisticsApi() (map[string]*accounts.ValidatorApiResponse, error)
ExecuteSCQuery(*process.SCQuery) (*vm.VMOutputApi, error)
ExecuteSCQuery(*process.SCQuery) (*vm.VMOutputApi, api.BlockInfo, error)
DecodeAddressPubkey(pk string) ([]byte, error)
RestApiInterface() string
RestAPIServerDebugMode() bool
Expand Down