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

External codehash endpoint #4403

Merged
merged 7 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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