Skip to content

Commit

Permalink
Merge pull request #4403 from ElrondNetwork/external-codehash-endpoint
Browse files Browse the repository at this point in the history
External codehash endpoint
  • Loading branch information
gabi-vuls authored Aug 30, 2022
2 parents ae87e01 + 60dc80c commit afd0d66
Show file tree
Hide file tree
Showing 15 changed files with 197 additions and 1 deletion.
3 changes: 3 additions & 0 deletions api/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ var ErrGetBalance = errors.New("get balance error")
// ErrGetUsername signals an error in getting the username for an account
var ErrGetUsername = errors.New("get username error")

// ErrGetCodeHash signals an error in getting the code hash for an account
var ErrGetCodeHash = errors.New("get code hash error")

// ErrGetValueForKey signals an error in getting the value of a key for an account
var ErrGetValueForKey = errors.New("get value for key error")

Expand Down
30 changes: 30 additions & 0 deletions api/groups/addressGroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const (
getAccountPath = "/:address"
getBalancePath = "/:address/balance"
getUsernamePath = "/:address/username"
getCodeHashPath = "/:address/code-hash"
getKeysPath = "/:address/keys"
getKeyPath = "/:address/key/:key"
getESDTTokensPath = "/:address/esdt"
Expand All @@ -40,6 +41,7 @@ const (
type addressFacadeHandler interface {
GetBalance(address string, options api.AccountQueryOptions) (*big.Int, api.BlockInfo, error)
GetUsername(address string, options api.AccountQueryOptions) (string, api.BlockInfo, error)
GetCodeHash(address string, options api.AccountQueryOptions) ([]byte, api.BlockInfo, error)
GetValueForKey(address string, key string, options api.AccountQueryOptions) (string, api.BlockInfo, error)
GetAccount(address string, options api.AccountQueryOptions) (api.AccountResponse, api.BlockInfo, error)
GetESDTData(address string, key string, nonce uint64, options api.AccountQueryOptions) (*esdt.ESDigitalToken, api.BlockInfo, error)
Expand Down Expand Up @@ -103,6 +105,11 @@ func NewAddressGroup(facade addressFacadeHandler) (*addressGroup, error) {
Method: http.MethodGet,
Handler: ag.getUsername,
},
{
Path: getCodeHashPath,
Method: http.MethodGet,
Handler: ag.getCodeHash,
},
{
Path: getKeyPath,
Method: http.MethodGet,
Expand Down Expand Up @@ -219,6 +226,29 @@ func (ag *addressGroup) getUsername(c *gin.Context) {
shared.RespondWithSuccess(c, gin.H{"username": userName, "blockInfo": blockInfo})
}

// getCodeHash returns the code hash for the address parameter
func (ag *addressGroup) getCodeHash(c *gin.Context) {
addr := c.Param("address")
if addr == "" {
shared.RespondWithValidationError(c, errors.ErrGetCodeHash, errors.ErrEmptyAddress)
return
}

options, err := parseAccountQueryOptions(c)
if err != nil {
shared.RespondWithValidationError(c, errors.ErrGetCodeHash, errors.ErrBadUrlParams)
return
}

codeHash, blockInfo, err := ag.getFacade().GetCodeHash(addr, options)
if err != nil {
shared.RespondWithInternalError(c, errors.ErrGetCodeHash, err)
return
}

shared.RespondWithSuccess(c, gin.H{"codeHash": codeHash, "blockInfo": blockInfo})
}

// getValueForKey returns the value for the given address and key
func (ag *addressGroup) getValueForKey(c *gin.Context) {
addr := c.Param("address")
Expand Down
65 changes: 65 additions & 0 deletions api/groups/addressGroup_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package groups_test

import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -139,6 +140,16 @@ type usernameResponse struct {
Code string `json:"code"`
}

type codeHashResponseData struct {
CodeHash string `json:"codeHash"`
}

type codeHashResponse struct {
Data codeHashResponseData `json:"data"`
Error string `json:"error"`
Code string `json:"code"`
}

func TestNewAddressGroup(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -395,6 +406,59 @@ func TestGetUsername_ShouldWork(t *testing.T) {
assert.Equal(t, testUsername, usernameResponseObj.Data.Username)
}

func TestGetCodeHash_NodeFailsShouldError(t *testing.T) {
t.Parallel()

testAddress := "address"
expectedErr := errors.New("expected error")
facade := mock.FacadeStub{
GetCodeHashCalled: func(_ string, _ api.AccountQueryOptions) ([]byte, api.BlockInfo, error) {
return nil, api.BlockInfo{}, expectedErr
},
}

addrGroup, err := groups.NewAddressGroup(&facade)
require.NoError(t, err)

ws := startWebServer(addrGroup, "address", getAddressRoutesConfig())

req, _ := http.NewRequest("GET", fmt.Sprintf("/address/%s/code-hash", testAddress), nil)
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

codeHashResponseObj := codeHashResponse{}
loadResponse(resp.Body, &codeHashResponseObj)
assert.Equal(t, http.StatusInternalServerError, resp.Code)
assert.True(t, strings.Contains(codeHashResponseObj.Error, expectedErr.Error()))
}

func TestGetCodeHash_ShouldWork(t *testing.T) {
t.Parallel()

testAddress := "address"
testCodeHash := []byte("value")
expectedResponseCodeHash := base64.StdEncoding.EncodeToString(testCodeHash)
facade := mock.FacadeStub{
GetCodeHashCalled: func(_ string, _ api.AccountQueryOptions) ([]byte, api.BlockInfo, error) {
return testCodeHash, api.BlockInfo{}, nil
},
}

addrGroup, err := groups.NewAddressGroup(&facade)
require.NoError(t, err)

ws := startWebServer(addrGroup, "address", getAddressRoutesConfig())

req, _ := http.NewRequest("GET", fmt.Sprintf("/address/%s/code-hash", testAddress), nil)
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

codeHashResponseObj := codeHashResponse{}
loadResponse(resp.Body, &codeHashResponseObj)
assert.Equal(t, http.StatusOK, resp.Code)
assert.Equal(t, expectedResponseCodeHash, codeHashResponseObj.Data.CodeHash)
}

func TestGetAccount_FailWhenFacadeStubGetAccountFails(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -1034,6 +1098,7 @@ func getAddressRoutesConfig() config.ApiRoutesConfig {
{Name: "/:address", Open: true},
{Name: "/:address/balance", Open: true},
{Name: "/:address/username", Open: true},
{Name: "/:address/code-hash", Open: true},
{Name: "/:address/keys", Open: true},
{Name: "/:address/key/:key", Open: true},
{Name: "/:address/esdt", Open: true},
Expand Down
10 changes: 10 additions & 0 deletions api/mock/facadeStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type FacadeStub struct {
GetPeerInfoCalled func(pid string) ([]core.QueryP2PPeerInfo, error)
GetThrottlerForEndpointCalled func(endpoint string) (core.Throttler, bool)
GetUsernameCalled func(address string, options api.AccountQueryOptions) (string, api.BlockInfo, error)
GetCodeHashCalled func(address string, options api.AccountQueryOptions) ([]byte, api.BlockInfo, error)
GetKeyValuePairsCalled func(address string, options api.AccountQueryOptions) (map[string]string, api.BlockInfo, error)
SimulateTransactionExecutionHandler func(tx *transaction.Transaction) (*txSimData.SimulationResults, error)
GetESDTDataCalled func(address string, key string, nonce uint64, options api.AccountQueryOptions) (*esdt.ESDigitalToken, api.BlockInfo, error)
Expand Down Expand Up @@ -132,6 +133,15 @@ func (f *FacadeStub) GetUsername(address string, options api.AccountQueryOptions
return "", api.BlockInfo{}, nil
}

// GetCodeHash -
func (f *FacadeStub) GetCodeHash(address string, options api.AccountQueryOptions) ([]byte, api.BlockInfo, error) {
if f.GetCodeHashCalled != nil {
return f.GetCodeHashCalled(address, options)
}

return nil, api.BlockInfo{}, nil
}

// GetThrottlerForEndpoint -
func (f *FacadeStub) GetThrottlerForEndpoint(endpoint string) (core.Throttler, bool) {
if f.GetThrottlerForEndpointCalled != nil {
Expand Down
1 change: 1 addition & 0 deletions api/shared/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type GroupHandler interface {
type FacadeHandler interface {
GetBalance(address string, options api.AccountQueryOptions) (*big.Int, api.BlockInfo, error)
GetUsername(address string, options api.AccountQueryOptions) (string, api.BlockInfo, error)
GetCodeHash(address string, options api.AccountQueryOptions) ([]byte, api.BlockInfo, error)
GetValueForKey(address string, key string, options api.AccountQueryOptions) (string, api.BlockInfo, error)
GetAccount(address string, options api.AccountQueryOptions) (api.AccountResponse, api.BlockInfo, error)
GetESDTData(address string, key string, nonce uint64, options api.AccountQueryOptions) (*esdt.ESDigitalToken, api.BlockInfo, error)
Expand Down
3 changes: 3 additions & 0 deletions cmd/node/config/api.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
# /address/:address/username will return the username of a given account
{ Name = "/:address/username", Open = true },

# /address/:address/code-hash will return the code hash of a given account
{ Name = "/:address/code-hash", Open = true },

# /address/:address/keys will return all the key-value pairs of a given account
{ Name = "/:address/keys", Open = true },

Expand Down
5 changes: 5 additions & 0 deletions facade/initial/initialNodeFacade.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ func (inf *initialNodeFacade) GetUsername(_ string, _ api.AccountQueryOptions) (
return emptyString, api.BlockInfo{}, errNodeStarting
}

// GetCodeHash returns empty string and error
func (inf *initialNodeFacade) GetCodeHash(_ string, _ api.AccountQueryOptions) ([]byte, api.BlockInfo, error) {
return nil, api.BlockInfo{}, errNodeStarting
}

// GetValueForKey returns an empty string and error
func (inf *initialNodeFacade) GetValueForKey(_ string, _ string, _ api.AccountQueryOptions) (string, api.BlockInfo, error) {
return emptyString, api.BlockInfo{}, errNodeStarting
Expand Down
3 changes: 3 additions & 0 deletions facade/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ type NodeHandler interface {
// GetUsername returns the username for a specific address
GetUsername(address string, options api.AccountQueryOptions) (string, api.BlockInfo, error)

// GetCodeHash returns the username for a specific address
GetCodeHash(address string, options api.AccountQueryOptions) ([]byte, api.BlockInfo, error)

// GetValueForKey returns the value of a key from a given account
GetValueForKey(address string, key string, options api.AccountQueryOptions) (string, api.BlockInfo, error)

Expand Down
10 changes: 10 additions & 0 deletions facade/mock/nodeStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type NodeStub struct {
GetValueForKeyCalled func(address string, key string, options api.AccountQueryOptions) (string, api.BlockInfo, error)
GetPeerInfoCalled func(pid string) ([]core.QueryP2PPeerInfo, error)
GetUsernameCalled func(address string, options api.AccountQueryOptions) (string, api.BlockInfo, error)
GetCodeHashCalled func(address string, options api.AccountQueryOptions) ([]byte, api.BlockInfo, error)
GetESDTDataCalled func(address string, key string, nonce uint64, options api.AccountQueryOptions) (*esdt.ESDigitalToken, api.BlockInfo, error)
GetAllESDTTokensCalled func(address string, options api.AccountQueryOptions, ctx context.Context) (map[string]*esdt.ESDigitalToken, api.BlockInfo, error)
GetNFTTokenIDsRegisteredByAddressCalled func(address string, options api.AccountQueryOptions, ctx context.Context) ([]string, api.BlockInfo, error)
Expand Down Expand Up @@ -86,6 +87,15 @@ func (ns *NodeStub) GetUsername(address string, options api.AccountQueryOptions)
return "", api.BlockInfo{}, nil
}

// GetCodeHash -
func (ns *NodeStub) GetCodeHash(address string, options api.AccountQueryOptions) ([]byte, api.BlockInfo, error) {
if ns.GetCodeHashCalled != nil {
return ns.GetCodeHashCalled(address, options)
}

return nil, api.BlockInfo{}, nil
}

// GetKeyValuePairs -
func (ns *NodeStub) GetKeyValuePairs(address string, options api.AccountQueryOptions, ctx context.Context) (map[string]string, api.BlockInfo, error) {
if ns.GetKeyValuePairsCalled != nil {
Expand Down
5 changes: 5 additions & 0 deletions facade/nodeFacade.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ func (nf *nodeFacade) GetUsername(address string, options apiData.AccountQueryOp
return nf.node.GetUsername(address, options)
}

// GetCodeHash gets the code hash for a specified address
func (nf *nodeFacade) GetCodeHash(address string, options apiData.AccountQueryOptions) ([]byte, apiData.BlockInfo, error) {
return nf.node.GetCodeHash(address, options)
}

// GetValueForKey gets the value for a key in a given address
func (nf *nodeFacade) GetValueForKey(address string, key string, options apiData.AccountQueryOptions) (string, apiData.BlockInfo, error) {
return nf.node.GetValueForKey(address, key, options)
Expand Down
18 changes: 18 additions & 0 deletions facade/nodeFacade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,24 @@ func TestNodeFacade_GetUsername(t *testing.T) {
assert.Equal(t, expectedUsername, username)
}

func TestNodeFacade_GetCodeHash(t *testing.T) {
t.Parallel()

expectedCodeHash := []byte("hash")
node := &mock.NodeStub{}
node.GetCodeHashCalled = func(address string, _ api.AccountQueryOptions) ([]byte, api.BlockInfo, error) {
return expectedCodeHash, api.BlockInfo{}, nil
}

arg := createMockArguments()
arg.Node = node
nf, _ := NewNodeFacade(arg)

codeHash, _, err := nf.GetCodeHash("test", api.AccountQueryOptions{})
assert.NoError(t, err)
assert.Equal(t, expectedCodeHash, codeHash)
}

func TestNodeFacade_GetHeartbeatsReturnsNilShouldErr(t *testing.T) {
t.Parallel()

Expand Down
1 change: 1 addition & 0 deletions integrationTests/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type NetworkShardingUpdater interface {
type Facade interface {
GetBalance(address string, options api.AccountQueryOptions) (*big.Int, api.BlockInfo, error)
GetUsername(address string, options api.AccountQueryOptions) (string, api.BlockInfo, error)
GetCodeHash(address string, options api.AccountQueryOptions) ([]byte, api.BlockInfo, error)
GetValueForKey(address string, key string, options api.AccountQueryOptions) (string, api.BlockInfo, error)
GetAccount(address string, options api.AccountQueryOptions) (dataApi.AccountResponse, api.BlockInfo, error)
GetESDTData(address string, key string, nonce uint64, options api.AccountQueryOptions) (*esdt.ESDigitalToken, api.BlockInfo, error)
Expand Down
2 changes: 1 addition & 1 deletion integrationTests/testProcessorNodeWithTestWebServer.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func createFacadeArg(tpn *TestProcessorNode) nodeFacade.ArgNodeFacade {
func createTestApiConfig() config.ApiRoutesConfig {
routes := map[string][]string{
"node": {"/status", "/metrics", "/heartbeatstatus", "/statistics", "/p2pstatus", "/debug", "/peerinfo"},
"address": {"/:address", "/:address/balance", "/:address/username", "/:address/key/:key", "/:address/esdt", "/:address/esdt/:tokenIdentifier"},
"address": {"/:address", "/:address/balance", "/:address/username", "/:address/code-hash", "/:address/key/:key", "/:address/esdt", "/:address/esdt/:tokenIdentifier"},
"hardfork": {"/trigger"},
"network": {"/status", "/total-staked", "/economics", "/config"},
"log": {"/log"},
Expand Down
11 changes: 11 additions & 0 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,17 @@ func (n *Node) GetUsername(address string, options api.AccountQueryOptions) (str
return string(username), blockInfo, nil
}

// GetCodeHash gets the code hash for a specific address
func (n *Node) GetCodeHash(address string, options api.AccountQueryOptions) ([]byte, api.BlockInfo, error) {
userAccount, blockInfo, err := n.loadUserAccountHandlerByAddress(address, options)
if err != nil {
return nil, api.BlockInfo{}, err
}

codeHash := userAccount.GetCodeHash()
return codeHash, blockInfo, nil
}

// GetAllIssuedESDTs returns all the issued esdt tokens, works only on metachain
func (n *Node) GetAllIssuedESDTs(tokenType string, ctx context.Context) ([]string, error) {
if n.processComponents.ShardCoordinator().SelfId() != core.MetachainShardId {
Expand Down
31 changes: 31 additions & 0 deletions node/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,37 @@ func TestGetUsername(t *testing.T) {
assert.Equal(t, string(expectedUsername), username)
}

func TestGetCodeHash(t *testing.T) {
expectedCodeHash := []byte("hash")

testAccount, _ := state.NewUserAccount(testscommon.TestPubKeyAlice)
testAccount.CodeHash = expectedCodeHash
accountsRepository := &stateMock.AccountsRepositoryStub{
GetAccountWithBlockInfoCalled: func(address []byte, options api.AccountQueryOptions) (vmcommon.AccountHandler, common.BlockInfo, error) {
return testAccount, nil, nil
},
}

coreComponents := getDefaultCoreComponents()
coreComponents.IntMarsh = getMarshalizer()
coreComponents.VmMarsh = getMarshalizer()
coreComponents.Hash = getHasher()

dataComponents := getDefaultDataComponents()
stateComponents := getDefaultStateComponents()
stateComponents.AccountsRepo = accountsRepository

n, _ := node.NewNode(
node.WithDataComponents(dataComponents),
node.WithCoreComponents(coreComponents),
node.WithStateComponents(stateComponents),
)

codeHash, _, err := n.GetCodeHash(testscommon.TestAddressAlice, api.AccountQueryOptions{})
assert.Nil(t, err)
assert.Equal(t, expectedCodeHash, codeHash)
}

func TestNode_GetKeyValuePairs(t *testing.T) {
acc, _ := state.NewUserAccount([]byte("newaddress"))

Expand Down

0 comments on commit afd0d66

Please sign in to comment.