Skip to content

Commit

Permalink
Merge pull request #4473 from ElrondNetwork/merge-rc1.4-feat-elrond-g…
Browse files Browse the repository at this point in the history
…o-storage

Merge rc1.4 feat elrond go storage
  • Loading branch information
gabi-vuls authored Sep 19, 2022
2 parents 013b2cf + 28a9823 commit f33c98a
Show file tree
Hide file tree
Showing 401 changed files with 16,194 additions and 4,322 deletions.
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
93 changes: 56 additions & 37 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 @@ -30,12 +31,17 @@ const (
getESDTNFTDataPath = "/:address/nft/:tokenIdentifier/nonce/:nonce"
urlParamOnFinalBlock = "onFinalBlock"
urlParamOnStartOfEpoch = "onStartOfEpoch"
urlParamBlockNonce = "blockNonce"
urlParamBlockHash = "blockHash"
urlParamBlockRootHash = "blockRootHash"
urlParamHintEpoch = "hintEpoch"
)

// addressFacadeHandler defines the methods to be implemented by a facade for handling address requests
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 @@ -99,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 @@ -153,9 +164,9 @@ func (ag *addressGroup) getAccount(c *gin.Context) {
return
}

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

Expand All @@ -177,9 +188,9 @@ func (ag *addressGroup) getBalance(c *gin.Context) {
return
}

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

Expand All @@ -200,9 +211,9 @@ func (ag *addressGroup) getUsername(c *gin.Context) {
return
}

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

Expand All @@ -215,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 All @@ -223,9 +257,9 @@ func (ag *addressGroup) getValueForKey(c *gin.Context) {
return
}

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

Expand All @@ -252,9 +286,9 @@ func (ag *addressGroup) getKeyValuePairs(c *gin.Context) {
return
}

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

Expand All @@ -275,9 +309,9 @@ func (ag *addressGroup) getESDTBalance(c *gin.Context) {
return
}

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

Expand Down Expand Up @@ -310,9 +344,9 @@ func (ag *addressGroup) getESDTsRoles(c *gin.Context) {
return
}

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

Expand All @@ -333,9 +367,9 @@ func (ag *addressGroup) getESDTTokensWithRole(c *gin.Context) {
return
}

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

Expand Down Expand Up @@ -367,9 +401,9 @@ func (ag *addressGroup) getNFTTokenIDsRegisteredByAddress(c *gin.Context) {
return
}

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

Expand All @@ -390,9 +424,9 @@ func (ag *addressGroup) getESDTNFTData(c *gin.Context) {
return
}

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

Expand Down Expand Up @@ -432,9 +466,9 @@ func (ag *addressGroup) getAllESDTData(c *gin.Context) {
return
}

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

Expand Down Expand Up @@ -501,18 +535,3 @@ func (ag *addressGroup) UpdateFacade(newFacade interface{}) error {
func (ag *addressGroup) IsInterfaceNil() bool {
return ag == nil
}

func parseAccountQueryOptions(c *gin.Context) (api.AccountQueryOptions, error) {
onFinalBlock, err := parseBoolUrlParam(c, urlParamOnFinalBlock)
if err != nil {
return api.AccountQueryOptions{}, err
}

onStartOfEpoch, err := parseUintUrlParam(c, urlParamOnStartOfEpoch)
if err != nil {
return api.AccountQueryOptions{}, err
}

options := api.AccountQueryOptions{OnFinalBlock: onFinalBlock, OnStartOfEpoch: uint32(onStartOfEpoch)}
return options, nil
}
95 changes: 95 additions & 0 deletions api/groups/addressGroupOptions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package groups

import (
"errors"
"fmt"

"github.com/ElrondNetwork/elrond-go-core/data/api"
customErrors "github.com/ElrondNetwork/elrond-go/api/errors"
"github.com/gin-gonic/gin"
)

func extractAccountQueryOptions(c *gin.Context) (api.AccountQueryOptions, error) {
options, err := parseAccountQueryOptions(c)
if err != nil {
return api.AccountQueryOptions{}, fmt.Errorf("%w: %v", customErrors.ErrBadUrlParams, err)
}

err = checkAccountQueryOptions(options)
if err != nil {
return api.AccountQueryOptions{}, fmt.Errorf("%w: %v", customErrors.ErrBadUrlParams, err)
}

return options, nil
}

func parseAccountQueryOptions(c *gin.Context) (api.AccountQueryOptions, error) {
onFinalBlock, err := parseBoolUrlParam(c, urlParamOnFinalBlock)
if err != nil {
return api.AccountQueryOptions{}, err
}

onStartOfEpoch, err := parseUint32UrlParam(c, urlParamOnStartOfEpoch)
if err != nil {
return api.AccountQueryOptions{}, err
}

blockNonce, err := parseUint64UrlParam(c, urlParamBlockNonce)
if err != nil {
return api.AccountQueryOptions{}, err
}

blockHash, err := parseHexBytesUrlParam(c, urlParamBlockHash)
if err != nil {
return api.AccountQueryOptions{}, err
}

blockRootHash, err := parseHexBytesUrlParam(c, urlParamBlockRootHash)
if err != nil {
return api.AccountQueryOptions{}, err
}

hintEpoch, err := parseUint32UrlParam(c, urlParamHintEpoch)
if err != nil {
return api.AccountQueryOptions{}, err
}

options := api.AccountQueryOptions{
OnFinalBlock: onFinalBlock,
OnStartOfEpoch: onStartOfEpoch,
BlockNonce: blockNonce,
BlockHash: blockHash,
BlockRootHash: blockRootHash,
HintEpoch: hintEpoch,
}
return options, nil
}

func checkAccountQueryOptions(options api.AccountQueryOptions) error {
numSpecifiedBlockCoordinates := 0

if options.BlockNonce.HasValue {
numSpecifiedBlockCoordinates++
}
if len(options.BlockHash) > 0 {
numSpecifiedBlockCoordinates++
}
if len(options.BlockRootHash) > 0 {
numSpecifiedBlockCoordinates++
}

if numSpecifiedBlockCoordinates > 1 {
return errors.New("only one block coordinate (blockNonce vs. blockHash vs. blockRootHash) can be specified at a time")
}
if options.OnFinalBlock && numSpecifiedBlockCoordinates > 0 {
return errors.New("onFinalBlock is not compatible with any other block coordinates")
}
if options.OnStartOfEpoch.HasValue && numSpecifiedBlockCoordinates > 0 {
return errors.New("onStartOfEpoch is not compatible with any other block coordinates")
}
if options.HintEpoch.HasValue && len(options.BlockRootHash) == 0 {
return errors.New("hintEpoch is optional, but only compatible with blockRootHash")
}

return nil
}
66 changes: 66 additions & 0 deletions api/groups/addressGroupOptions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package groups

import (
"testing"

"github.com/ElrondNetwork/elrond-go-core/core"
"github.com/ElrondNetwork/elrond-go-core/data/api"
"github.com/ElrondNetwork/elrond-go/testscommon"
"github.com/stretchr/testify/require"
)

func TestExtractAccountQueryOptions(t *testing.T) {
t.Run("good options", func(t *testing.T) {
options, err := extractAccountQueryOptions(testscommon.CreateGinContextWithRawQuery("onFinalBlock=true"))
require.Nil(t, err)
require.True(t, options.OnFinalBlock)

options, err = extractAccountQueryOptions(testscommon.CreateGinContextWithRawQuery("onStartOfEpoch=7"))
require.Nil(t, err)
require.Equal(t, core.OptionalUint32{Value: 7, HasValue: true}, options.OnStartOfEpoch)

options, err = extractAccountQueryOptions(testscommon.CreateGinContextWithRawQuery("blockNonce=42"))
require.Nil(t, err)
require.Equal(t, core.OptionalUint64{Value: 42, HasValue: true}, options.BlockNonce)

options, err = extractAccountQueryOptions(testscommon.CreateGinContextWithRawQuery("blockHash=aaaa"))
require.Nil(t, err)
require.Equal(t, []byte{0xaa, 0xaa}, options.BlockHash)

options, err = extractAccountQueryOptions(testscommon.CreateGinContextWithRawQuery("blockHash=aaaa"))
require.Nil(t, err)
require.Equal(t, []byte{0xaa, 0xaa}, options.BlockHash)

options, err = extractAccountQueryOptions(testscommon.CreateGinContextWithRawQuery("blockRootHash=bbbb&hintEpoch=7"))
require.Nil(t, err)
require.Equal(t, []byte{0xbb, 0xbb}, options.BlockRootHash)
require.Equal(t, uint32(7), options.HintEpoch.Value)
})

t.Run("bad options", func(t *testing.T) {
options, err := extractAccountQueryOptions(testscommon.CreateGinContextWithRawQuery("blockNonce=42&blockHash=aaaa"))
require.ErrorContains(t, err, "only one block coordinate")
require.Equal(t, api.AccountQueryOptions{}, options)

options, err = extractAccountQueryOptions(testscommon.CreateGinContextWithRawQuery("blockHash=aaaa&blockRootHash=bbbb"))
require.ErrorContains(t, err, "only one block coordinate")
require.Equal(t, api.AccountQueryOptions{}, options)

options, err = extractAccountQueryOptions(testscommon.CreateGinContextWithRawQuery("onFinalBlock=true&blockHash=aaaa"))
require.ErrorContains(t, err, "onFinalBlock is not compatible")
require.Equal(t, api.AccountQueryOptions{}, options)

options, err = extractAccountQueryOptions(testscommon.CreateGinContextWithRawQuery("onStartOfEpoch=7&blockRootHash=bbbb"))
require.ErrorContains(t, err, "onStartOfEpoch is not compatible")
require.Equal(t, api.AccountQueryOptions{}, options)

options, err = extractAccountQueryOptions(testscommon.CreateGinContextWithRawQuery("onFinalBlock=true&hintEpoch=7"))
require.ErrorContains(t, err, "hintEpoch is optional, but only compatible with blockRootHash")
require.Equal(t, api.AccountQueryOptions{}, options)

options, err = extractAccountQueryOptions(testscommon.CreateGinContextWithRawQuery("blockHash=aaaa&hintEpoch=7"))
require.ErrorContains(t, err, "hintEpoch is optional, but only compatible with blockRootHash")
require.Equal(t, api.AccountQueryOptions{}, options)

})
}
Loading

0 comments on commit f33c98a

Please sign in to comment.