Skip to content

Commit

Permalink
Merge pull request #4505 from ElrondNetwork/EN-13187-oldest-epoch-met…
Browse files Browse the repository at this point in the history
…ric-plus-epoch-start

EN-13187: add epoch start data endpoint
  • Loading branch information
gabi-vuls authored Sep 26, 2022
2 parents e7da853 + d6260e4 commit f992a46
Show file tree
Hide file tree
Showing 27 changed files with 464 additions and 115 deletions.
9 changes: 3 additions & 6 deletions api/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ var ErrGetValueForKey = errors.New("get value for key error")
// ErrGetKeyValuePairs signals an error in getting the key-value pairs of a key for an account
var ErrGetKeyValuePairs = errors.New("get key-value pairs error")

// ErrGetESDTTokens signals an error in getting esdt tokens for a given address
var ErrGetESDTTokens = errors.New("get esdt tokens for account error")

// ErrGetESDTBalance signals an error in getting esdt balance for given address
var ErrGetESDTBalance = errors.New("get esdt balance for account error")

Expand Down Expand Up @@ -73,9 +70,6 @@ var ErrInvalidEpoch = errors.New("invalid epoch parameter")
// ErrValidationEmptyBlockHash signals an empty block hash was provided
var ErrValidationEmptyBlockHash = errors.New("block hash is empty")

// ErrValidationEmptyToken signals that an empty token was provided
var ErrValidationEmptyToken = errors.New("token is empty")

// ErrGetTransaction signals an error happening when trying to fetch a transaction
var ErrGetTransaction = errors.New("getting transaction failed")

Expand All @@ -88,6 +82,9 @@ var ErrQueryError = errors.New("query error")
// ErrGetPidInfo signals that an error occurred while getting peer ID info
var ErrGetPidInfo = errors.New("error getting peer id info")

// ErrGetEpochStartData signals that an error occurred while getting the epoch start data for a provided epoch
var ErrGetEpochStartData = errors.New("error getting epoch start data for epoch")

// ErrTooManyRequests signals that too many requests were simultaneously received
var ErrTooManyRequests = errors.New("too many requests")

Expand Down
39 changes: 32 additions & 7 deletions api/groups/nodeGroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,30 @@ import (
"github.com/ElrondNetwork/elrond-go-core/core/check"
"github.com/ElrondNetwork/elrond-go/api/errors"
"github.com/ElrondNetwork/elrond-go/api/shared"
"github.com/ElrondNetwork/elrond-go/common"
"github.com/ElrondNetwork/elrond-go/debug"
"github.com/ElrondNetwork/elrond-go/heartbeat/data"
"github.com/ElrondNetwork/elrond-go/node/external"
"github.com/gin-gonic/gin"
)

const (
pidQueryParam = "pid"
debugPath = "/debug"
heartbeatStatusPath = "/heartbeatstatus"
metricsPath = "/metrics"
p2pStatusPath = "/p2pstatus"
peerInfoPath = "/peerinfo"
statusPath = "/status"
pidQueryParam = "pid"
debugPath = "/debug"
heartbeatStatusPath = "/heartbeatstatus"
metricsPath = "/metrics"
p2pStatusPath = "/p2pstatus"
peerInfoPath = "/peerinfo"
statusPath = "/status"
epochStartDataForEpoch = "/epoch-start/:epoch"
)

// nodeFacadeHandler defines the methods to be implemented by a facade for node requests
type nodeFacadeHandler interface {
GetHeartbeats() ([]data.PubKeyHeartbeat, error)
StatusMetrics() external.StatusMetricsHandler
GetQueryHandler(name string) (debug.QueryHandler, error)
GetEpochStartDataAPI(epoch uint32) (*common.EpochStartDataAPI, error)
GetPeerInfo(pid string) ([]core.QueryP2PPeerInfo, error)
IsInterfaceNil() bool
}
Expand Down Expand Up @@ -88,6 +91,11 @@ func NewNodeGroup(facade nodeFacadeHandler) (*nodeGroup, error) {
Method: http.MethodGet,
Handler: ng.peerInfo,
},
{
Path: epochStartDataForEpoch,
Method: http.MethodGet,
Handler: ng.epochStartDataForEpoch,
},
}
ng.endpoints = endpoints

Expand Down Expand Up @@ -241,6 +249,23 @@ func (ng *nodeGroup) peerInfo(c *gin.Context) {
)
}

// epochStartDataForEpoch returns epoch start data for the provided epoch
func (ng *nodeGroup) epochStartDataForEpoch(c *gin.Context) {
epoch, err := getQueryParamEpoch(c)
if err != nil {
shared.RespondWithValidationError(c, errors.ErrValidation, errors.ErrBadUrlParams)
return
}

epochStartData, err := ng.getFacade().GetEpochStartDataAPI(epoch)
if err != nil {
shared.RespondWithInternalError(c, errors.ErrGetEpochStartData, err)
return
}

shared.RespondWithSuccess(c, gin.H{"epochStart": epochStartData})
}

// prometheusMetrics is the endpoint which will return the data in the way that prometheus expects them
func (ng *nodeGroup) prometheusMetrics(c *gin.Context) {
metrics, err := ng.getFacade().StatusMetrics().StatusMetricsWithoutP2PPrometheusString()
Expand Down
69 changes: 69 additions & 0 deletions api/groups/nodeGroup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/ElrondNetwork/elrond-go/api/groups"
"github.com/ElrondNetwork/elrond-go/api/mock"
"github.com/ElrondNetwork/elrond-go/api/shared"
"github.com/ElrondNetwork/elrond-go/common"
"github.com/ElrondNetwork/elrond-go/config"
"github.com/ElrondNetwork/elrond-go/debug"
"github.com/ElrondNetwork/elrond-go/heartbeat/data"
Expand Down Expand Up @@ -60,6 +61,13 @@ type queryResponse struct {
Result []string `json:"result"`
}

type epochStartResponse struct {
Data struct {
common.EpochStartDataAPI `json:"epochStart"`
} `json:"data"`
generalResponse
}

func init() {
gin.SetMode(gin.TestMode)
}
Expand Down Expand Up @@ -387,6 +395,66 @@ func TestPeerInfo_PeerInfoShouldWork(t *testing.T) {
assert.NotNil(t, responseInfo["info"])
}

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

expectedErr := errors.New("expected error")
facade := mock.FacadeStub{
GetEpochStartDataAPICalled: func(epoch uint32) (*common.EpochStartDataAPI, error) {
return nil, expectedErr
},
}

nodeGroup, err := groups.NewNodeGroup(&facade)
require.NoError(t, err)

ws := startWebServer(nodeGroup, "node", getNodeRoutesConfig())

req, _ := http.NewRequest("GET", "/node/epoch-start/4", nil)
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

response := &shared.GenericAPIResponse{}
loadResponse(resp.Body, response)

assert.Equal(t, http.StatusInternalServerError, resp.Code)
assert.True(t, strings.Contains(response.Error, expectedErr.Error()))
}

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

expectedEpochStartData := &common.EpochStartDataAPI{
Nonce: 1,
Round: 2,
Shard: 3,
Timestamp: 4,
}

facade := mock.FacadeStub{
GetEpochStartDataAPICalled: func(epoch uint32) (*common.EpochStartDataAPI, error) {
return expectedEpochStartData, nil
},
}

nodeGroup, err := groups.NewNodeGroup(&facade)
require.NoError(t, err)

ws := startWebServer(nodeGroup, "node", getNodeRoutesConfig())

req, _ := http.NewRequest("GET", "/node/epoch-start/3", nil)
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

response := &epochStartResponse{}
loadResponse(resp.Body, response)

assert.Equal(t, http.StatusOK, resp.Code)
assert.Equal(t, "", response.Error)

require.Equal(t, *expectedEpochStartData, response.Data.EpochStartDataAPI)
}

func TestPrometheusMetrics_ShouldReturnErrorIfFacadeReturnsError(t *testing.T) {
expectedErr := errors.New("i am an error")

Expand Down Expand Up @@ -466,6 +534,7 @@ func getNodeRoutesConfig() config.ApiRoutesConfig {
{Name: "/p2pstatus", Open: true},
{Name: "/debug", Open: true},
{Name: "/peerinfo", Open: true},
{Name: "/epoch-start/:epoch", Open: true},
},
},
},
Expand Down
6 changes: 6 additions & 0 deletions api/mock/facadeStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type FacadeStub struct {
GetQueryHandlerCalled func(name string) (debug.QueryHandler, error)
GetValueForKeyCalled func(address string, key string, options api.AccountQueryOptions) (string, api.BlockInfo, error)
GetPeerInfoCalled func(pid string) ([]core.QueryP2PPeerInfo, error)
GetEpochStartDataAPICalled func(epoch uint32) (*common.EpochStartDataAPI, error)
GetThrottlerForEndpointCalled func(endpoint string) (core.Throttler, bool)
GetUsernameCalled func(address string, options api.AccountQueryOptions) (string, api.BlockInfo, error)
GetKeyValuePairsCalled func(address string, options api.AccountQueryOptions) (map[string]string, api.BlockInfo, error)
Expand Down Expand Up @@ -347,6 +348,11 @@ func (f *FacadeStub) GetPeerInfo(pid string) ([]core.QueryP2PPeerInfo, error) {
return f.GetPeerInfoCalled(pid)
}

// GetEpochStartDataAPI -
func (f *FacadeStub) GetEpochStartDataAPI(epoch uint32) (*common.EpochStartDataAPI, error) {
return f.GetEpochStartDataAPICalled(epoch)
}

// GetBlockByNonce -
func (f *FacadeStub) GetBlockByNonce(nonce uint64, options api.BlockQueryOptions) (*api.Block, error) {
return f.GetBlockByNonceCalled(nonce, options)
Expand Down
1 change: 1 addition & 0 deletions api/shared/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ type FacadeHandler interface {
GetAllIssuedESDTs(tokenType string) ([]string, error)
GetHeartbeats() ([]data.PubKeyHeartbeat, error)
GetQueryHandler(name string) (debug.QueryHandler, error)
GetEpochStartDataAPI(epoch uint32) (*common.EpochStartDataAPI, error)
GetPeerInfo(pid string) ([]core.QueryP2PPeerInfo, error)
GetProof(rootHash string, address string) (*common.GetProofResponse, error)
GetProofDataTrie(rootHash string, address string, key string) (*common.GetProofResponse, *common.GetProofResponse, error)
Expand Down
5 changes: 4 additions & 1 deletion cmd/node/config/api.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
{ Name = "/debug", Open = true },

# /node/peerinfo will return the p2p peer info of the provided pid
{ Name = "/peerinfo", Open = true }
{ Name = "/peerinfo", Open = true },

# /node/epoch-start/:epoch will return the epoch start data for a given epoch
{ Name = "/epoch-start/:epoch", Open = true }
]

[APIPackages.address]
Expand Down
14 changes: 14 additions & 0 deletions common/dtos.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,17 @@ type InitialAccountAPI struct {
StakingValue string `json:"stakingvalue"`
Delegation DelegationDataAPI `json:"delegation"`
}

// EpochStartDataAPI holds fields from the first block in a given epoch
type EpochStartDataAPI struct {
Nonce uint64 `json:"nonce"`
Round uint64 `json:"round"`
Timestamp int64 `json:"timestamp"`
Epoch uint32 `json:"epoch"`
Shard uint32 `json:"shard"`
PrevBlockHash string `json:"prevBlockHash"`
StateRootHash string `json:"stateRootHash"`
ScheduledRootHash string `json:"scheduledRootHash"`
AccumulatedFees string `json:"accumulatedFees,omitempty"`
DeveloperFees string `json:"developerFees,omitempty"`
}
18 changes: 10 additions & 8 deletions epochStart/bootstrap/metaStorageHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,16 @@ func NewMetaStorageHandler(
) (*metaStorageHandler, error) {
epochStartNotifier := &disabled.EpochStartNotifier{}
storageFactory, err := factory.NewStorageServiceFactory(
&generalConfig,
&prefsConfig,
shardCoordinator,
pathManagerHandler,
epochStartNotifier,
nodeTypeProvider,
currentEpoch,
false,
factory.StorageServiceFactoryArgs{
Config: generalConfig,
PrefsConfig: prefsConfig,
ShardCoordinator: shardCoordinator,
PathManager: pathManagerHandler,
EpochStartNotifier: epochStartNotifier,
NodeTypeProvider: nodeTypeProvider,
CurrentEpoch: currentEpoch,
CreateTrieEpochRootHashStorer: false,
},
)
if err != nil {
return nil, err
Expand Down
1 change: 0 additions & 1 deletion epochStart/bootstrap/metaStorageHandler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ func TestNewMetaStorageHandler_CreateForMetaErr(t *testing.T) {
hasher := &hashingMocks.HasherMock{}
uit64Cvt := &mock.Uint64ByteSliceConverterMock{}
nodeTypeProvider := &nodeTypeProviderMock.NodeTypeProviderStub{}

mtStrHandler, err := NewMetaStorageHandler(gCfg, prefsConfig, coordinator, pathManager, marshalizer, hasher, 1, uit64Cvt, nodeTypeProvider)
assert.False(t, check.IfNil(mtStrHandler))
assert.Nil(t, err)
Expand Down
19 changes: 10 additions & 9 deletions epochStart/bootstrap/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -1080,15 +1080,16 @@ func (e *epochStartBootstrap) createStorageService(
targetShardId uint32,
) (dataRetriever.StorageService, error) {
storageServiceCreator, err := storageFactory.NewStorageServiceFactory(
&e.generalConfig,
&e.prefsConfig,
shardCoordinator,
pathManager,
epochStartNotifier,
e.coreComponentsHolder.NodeTypeProvider(),
startEpoch,
createTrieEpochRootHashStorer,
)
storageFactory.StorageServiceFactoryArgs{
Config: e.generalConfig,
PrefsConfig: e.prefsConfig,
ShardCoordinator: shardCoordinator,
PathManager: pathManager,
EpochStartNotifier: epochStartNotifier,
NodeTypeProvider: e.coreComponentsHolder.NodeTypeProvider(),
CurrentEpoch: startEpoch,
CreateTrieEpochRootHashStorer: createTrieEpochRootHashStorer,
})
if err != nil {
return nil, err
}
Expand Down
3 changes: 3 additions & 0 deletions epochStart/bootstrap/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ func createComponentsForEpochStart() (*mock.CoreComponentsMock, *mock.CryptoComp
NodeTypeProviderField: &nodeTypeProviderMock.NodeTypeProviderStub{},
ProcessStatusHandlerInstance: &testscommon.ProcessStatusHandlerStub{},
HardforkTriggerPubKeyField: []byte("provided hardfork pub key"),
StatusHandlerCalled: func() core.AppStatusHandler {
return &statusHandlerMock.AppStatusHandlerStub{}
},
},
&mock.CryptoComponentsMock{
PubKey: &cryptoMocks.PublicKeyStub{},
Expand Down
18 changes: 10 additions & 8 deletions epochStart/bootstrap/shardStorageHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,16 @@ func NewShardStorageHandler(
) (*shardStorageHandler, error) {
epochStartNotifier := &disabled.EpochStartNotifier{}
storageFactory, err := factory.NewStorageServiceFactory(
&generalConfig,
&prefsConfig,
shardCoordinator,
pathManagerHandler,
epochStartNotifier,
nodeTypeProvider,
currentEpoch,
false,
factory.StorageServiceFactoryArgs{
Config: generalConfig,
PrefsConfig: prefsConfig,
ShardCoordinator: shardCoordinator,
PathManager: pathManagerHandler,
EpochStartNotifier: epochStartNotifier,
NodeTypeProvider: nodeTypeProvider,
CurrentEpoch: currentEpoch,
CreateTrieEpochRootHashStorer: false,
},
)
if err != nil {
return nil, err
Expand Down
5 changes: 5 additions & 0 deletions facade/initial/initialNodeFacade.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ func (inf *initialNodeFacade) GetPeerInfo(_ string) ([]core.QueryP2PPeerInfo, er
return nil, errNodeStarting
}

// GetEpochStartDataAPI returns nil and error
func (inf *initialNodeFacade) GetEpochStartDataAPI(_ uint32) (*common.EpochStartDataAPI, error) {
return nil, errNodeStarting
}

// GetThrottlerForEndpoint returns nil and false
func (inf *initialNodeFacade) GetThrottlerForEndpoint(_ string) (core.Throttler, bool) {
return nil, false
Expand Down
2 changes: 2 additions & 0 deletions facade/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ type NodeHandler interface {
GetQueryHandler(name string) (debug.QueryHandler, error)
GetPeerInfo(pid string) ([]core.QueryP2PPeerInfo, error)

GetEpochStartDataAPI(epoch uint32) (*common.EpochStartDataAPI, error)

GetProof(rootHash string, key string) (*common.GetProofResponse, error)
GetProofDataTrie(rootHash string, address string, key string) (*common.GetProofResponse, *common.GetProofResponse, error)
VerifyProof(rootHash string, address string, proof [][]byte) (bool, 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 @@ -37,6 +37,7 @@ type NodeStub struct {
GetQueryHandlerCalled func(name string) (debug.QueryHandler, error)
GetValueForKeyCalled func(address string, key string, options api.AccountQueryOptions) (string, api.BlockInfo, error)
GetPeerInfoCalled func(pid string) ([]core.QueryP2PPeerInfo, error)
GetEpochStartDataAPICalled func(epoch uint32) (*common.EpochStartDataAPI, error)
GetUsernameCalled func(address string, options api.AccountQueryOptions) (string, 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)
Expand Down Expand Up @@ -193,6 +194,15 @@ func (ns *NodeStub) GetPeerInfo(pid string) ([]core.QueryP2PPeerInfo, error) {
return make([]core.QueryP2PPeerInfo, 0), nil
}

// GetEpochStartDataAPI -
func (ns *NodeStub) GetEpochStartDataAPI(epoch uint32) (*common.EpochStartDataAPI, error) {
if ns.GetEpochStartDataAPICalled != nil {
return ns.GetEpochStartDataAPICalled(epoch)
}

return &common.EpochStartDataAPI{}, nil
}

// GetESDTData -
func (ns *NodeStub) GetESDTData(address, tokenID string, nonce uint64, options api.AccountQueryOptions) (*esdt.ESDigitalToken, api.BlockInfo, error) {
if ns.GetESDTDataCalled != nil {
Expand Down
Loading

0 comments on commit f992a46

Please sign in to comment.