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

Support for new endpoint /node/waiting-epochs-left/:key #407

Merged
merged 4 commits into from
Nov 23, 2023
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
12 changes: 12 additions & 0 deletions api/groups/baseNodeGroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func NewNodeGroup(facadeHandler data.FacadeHandler) (*nodeGroup, error) {
baseRoutesHandlers := []*data.EndpointHandlerData{
{Path: "/heartbeatstatus", Handler: ng.getHeartbeatData, Method: http.MethodGet},
{Path: "/old-storage-token/:token/nonce/:nonce", Handler: ng.isOldStorageForToken, Method: http.MethodGet},
{Path: "/waiting-epochs-left/:key", Handler: ng.waitingEpochsLeft, Method: http.MethodGet},
}
ng.baseGroup.endpoints = baseRoutesHandlers

Expand Down Expand Up @@ -69,3 +70,14 @@ func (group *nodeGroup) isOldStorageForToken(c *gin.Context) {

shared.RespondWith(c, http.StatusOK, gin.H{"isOldStorage": isOldStorage}, "", data.ReturnCodeSuccess)
}

func (group *nodeGroup) waitingEpochsLeft(c *gin.Context) {
publicKey := c.Param("key")
response, err := group.facade.GetWaitingEpochsLeftForPublicKey(publicKey)
if err != nil {
shared.RespondWith(c, http.StatusInternalServerError, nil, err.Error(), data.ReturnCodeInternalError)
return
}

shared.RespondWith(c, http.StatusOK, response.Data, "", data.ReturnCodeSuccess)
}
50 changes: 50 additions & 0 deletions api/groups/baseNodeGroup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,53 @@ func TestNodeGroup_IsOldStorageToken(t *testing.T) {
assert.Equal(t, "map[isOldStorage:true]", fmt.Sprintf("%v", result.Data))
})
}

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

t.Run("facade returns bad request", func(t *testing.T) {
t.Parallel()

facade := &mock.FacadeStub{
GetWaitingEpochsLeftForPublicKeyCalled: func(publicKey string) (*data.WaitingEpochsLeftApiResponse, error) {
return nil, errors.New("bad request")
},
}
nodeGroup, err := groups.NewNodeGroup(facade)
require.NoError(t, err)
ws := startProxyServer(nodeGroup, nodePath)

req, _ := http.NewRequest("GET", "/node/waiting-epochs-left/key", nil)
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

assert.Equal(t, http.StatusInternalServerError, resp.Code)
})
t.Run("facade returns bad request", func(t *testing.T) {
t.Parallel()

providedData := data.WaitingEpochsLeftResponse{
EpochsLeft: 10,
}
facade := &mock.FacadeStub{
GetWaitingEpochsLeftForPublicKeyCalled: func(publicKey string) (*data.WaitingEpochsLeftApiResponse, error) {
return &data.WaitingEpochsLeftApiResponse{
Data: providedData,
}, nil
},
}
nodeGroup, err := groups.NewNodeGroup(facade)
require.NoError(t, err)
ws := startProxyServer(nodeGroup, nodePath)

req, _ := http.NewRequest("GET", "/node/waiting-epochs-left/key", nil)
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

assert.Equal(t, http.StatusOK, resp.Code)

var result data.WaitingEpochsLeftApiResponse
loadResponse(resp.Body, &result)
assert.Equal(t, providedData, result.Data)
})
}
1 change: 1 addition & 0 deletions api/groups/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ type NetworkFacadeHandler interface {
type NodeFacadeHandler interface {
GetHeartbeatData() (*data.HeartbeatResponse, error)
IsOldStorageForToken(tokenID string, nonce uint64) (bool, error)
GetWaitingEpochsLeftForPublicKey(publicKey string) (*data.WaitingEpochsLeftApiResponse, error)
}

// StatusFacadeHandler interface defines methods that can be used from the facade
Expand Down
9 changes: 9 additions & 0 deletions api/mock/facadeStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ type FacadeStub struct {
GetCodeHashCalled func(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error)
GetGuardianDataCalled func(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error)
IsDataTrieMigratedCalled func(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error)
GetWaitingEpochsLeftForPublicKeyCalled func(publicKey string) (*data.WaitingEpochsLeftApiResponse, error)
}

// GetProof -
Expand Down Expand Up @@ -554,6 +555,14 @@ func (f *FacadeStub) IsDataTrieMigrated(address string, options common.AccountQu
return &data.GenericAPIResponse{}, nil
}

// GetWaitingEpochsLeftForPublicKey -
func (f *FacadeStub) GetWaitingEpochsLeftForPublicKey(publicKey string) (*data.WaitingEpochsLeftApiResponse, error) {
if f.GetWaitingEpochsLeftForPublicKeyCalled != nil {
return f.GetWaitingEpochsLeftForPublicKeyCalled(publicKey)
}
return &data.WaitingEpochsLeftApiResponse{}, nil
}

// WrongFacade is a struct that can be used as a wrong implementation of the node router handler
type WrongFacade struct {
}
3 changes: 2 additions & 1 deletion cmd/proxy/config/apiConfig/v1_0.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ Routes = [
[APIPackages.node]
Routes = [
{ Name = "/heartbeatstatus", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/old-storage-token/:token/nonce/:nonce", Open = true, Secured = false, RateLimit = 0}
{ Name = "/old-storage-token/:token/nonce/:nonce", Open = true, Secured = false, RateLimit = 0},
{ Name = "/waiting-epochs-left/:key", Open = true, Secured = false, RateLimit = 0}
]

[APIPackages.address]
Expand Down
3 changes: 2 additions & 1 deletion cmd/proxy/config/apiConfig/v_next.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ Routes = [
[APIPackages.node]
Routes = [
{ Name = "/heartbeatstatus", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/old-storage-token/:token/nonce/:nonce", Open = true, Secured = false, RateLimit = 0}
{ Name = "/old-storage-token/:token/nonce/:nonce", Open = true, Secured = false, RateLimit = 0},
{ Name = "/waiting-epochs-left/:key", Open = true, Secured = false, RateLimit = 0}
]

[APIPackages.address]
Expand Down
34 changes: 34 additions & 0 deletions cmd/proxy/config/swagger/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1406,6 +1406,40 @@
}
}
},
"/node/waiting-epochs-left/{key}": {
"get": {
"tags": [
"node"
],
"summary": "will return the number of epochs left for the public key until it becomes eligible",
"parameters": [
{
"name": "key",
"in": "path",
"description": "the public key to look after",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GenericResponse"
}
}
}
},
"400": {
"description": "validation error"
}
}
}
},
"/proof/root-hash/{roothash}/address/{address}": {
"get": {
"tags": [
Expand Down
12 changes: 12 additions & 0 deletions data/nodeStatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,15 @@ type TrieStatisticsAPIResponse struct {
Error string `json:"error"`
Code string `json:"code"`
}

// WaitingEpochsLeftResponse matches the output structure the data field for a waiting epochs left response
type WaitingEpochsLeftResponse struct {
EpochsLeft uint32 `json:"epochsLeft"`
}

// WaitingEpochsLeftApiResponse matches the output of an observer's waiting epochs left endpoint
type WaitingEpochsLeftApiResponse struct {
Data WaitingEpochsLeftResponse `json:"data"`
Error string `json:"error"`
Code string `json:"code"`
}
5 changes: 5 additions & 0 deletions facade/baseFacade.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,11 @@ func (epf *ProxyFacade) GetInternalStartOfEpochValidatorsInfo(epoch uint32) (*da
return epf.blockProc.GetInternalStartOfEpochValidatorsInfo(epoch)
}

// GetWaitingEpochsLeftForPublicKey returns the number of epochs left for the public key until it becomes eligible
func (epf *ProxyFacade) GetWaitingEpochsLeftForPublicKey(publicKey string) (*data.WaitingEpochsLeftApiResponse, error) {
return epf.nodeGroupProc.GetWaitingEpochsLeftForPublicKey(publicKey)
}

// IsDataTrieMigrated returns true if the data trie for the given address is migrated
func (epf *ProxyFacade) IsDataTrieMigrated(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) {
return epf.accountProc.IsDataTrieMigrated(address, options)
Expand Down
35 changes: 35 additions & 0 deletions facade/baseFacade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,41 @@ func TestProxyFacade_GetGasConfigs(t *testing.T) {
assert.Equal(t, expectedResult, actualResult)
}

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

expectedResults := &data.WaitingEpochsLeftApiResponse{
Data: data.WaitingEpochsLeftResponse{
EpochsLeft: 10,
},
}
epf, _ := facade.NewProxyFacade(
&mock.ActionsProcessorStub{},
&mock.AccountProcessorStub{},
&mock.TransactionProcessorStub{},
&mock.SCQueryServiceStub{},
&mock.NodeGroupProcessorStub{
GetWaitingEpochsLeftForPublicKeyCalled: func(publicKey string) (*data.WaitingEpochsLeftApiResponse, error) {
return expectedResults, nil
},
},
&mock.ValidatorStatisticsProcessorStub{},
&mock.FaucetProcessorStub{},
&mock.NodeStatusProcessorStub{},
&mock.BlockProcessorStub{},
&mock.BlocksProcessorStub{},
&mock.ProofProcessorStub{},
publicKeyConverter,
&mock.ESDTSuppliesProcessorStub{},
&mock.StatusProcessorStub{},
&mock.AboutInfoProcessorStub{},
)

actualResult, _ := epf.GetWaitingEpochsLeftForPublicKey("key")

assert.Equal(t, expectedResults, actualResult)
}

func getPrivKey() crypto.PrivateKey {
keyGen := signing.NewKeyGenerator(ed25519.NewEd25519())
sk, _ := keyGen.GeneratePair()
Expand Down
1 change: 1 addition & 0 deletions facade/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type SCQueryService interface {
type NodeGroupProcessor interface {
GetHeartbeatData() (*data.HeartbeatResponse, error)
IsOldStorageForToken(tokenID string, nonce uint64) (bool, error)
GetWaitingEpochsLeftForPublicKey(publicKey string) (*data.WaitingEpochsLeftApiResponse, error)
}

// ValidatorStatisticsProcessor defines what a validator statistics processor should do
Expand Down
13 changes: 11 additions & 2 deletions facade/mock/nodeGroupProcessorStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import "github.com/multiversx/mx-chain-proxy-go/data"

// NodeGroupProcessorStub represents a stub implementation of a NodeGroupProcessor
type NodeGroupProcessorStub struct {
GetHeartbeatDataCalled func() (*data.HeartbeatResponse, error)
IsOldStorageForTokenCalled func(tokenID string, nonce uint64) (bool, error)
GetHeartbeatDataCalled func() (*data.HeartbeatResponse, error)
IsOldStorageForTokenCalled func(tokenID string, nonce uint64) (bool, error)
GetWaitingEpochsLeftForPublicKeyCalled func(publicKey string) (*data.WaitingEpochsLeftApiResponse, error)
}

// IsOldStorageForToken -
Expand All @@ -17,3 +18,11 @@ func (hbps *NodeGroupProcessorStub) IsOldStorageForToken(tokenID string, nonce u
func (hbps *NodeGroupProcessorStub) GetHeartbeatData() (*data.HeartbeatResponse, error) {
return hbps.GetHeartbeatDataCalled()
}

// GetWaitingEpochsLeftForPublicKey -
func (hbps *NodeGroupProcessorStub) GetWaitingEpochsLeftForPublicKey(publicKey string) (*data.WaitingEpochsLeftApiResponse, error) {
if hbps.GetWaitingEpochsLeftForPublicKeyCalled != nil {
return hbps.GetWaitingEpochsLeftForPublicKeyCalled(publicKey)
}
return &data.WaitingEpochsLeftApiResponse{}, nil
}
3 changes: 3 additions & 0 deletions process/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,6 @@ var ErrEmptyAppVersionString = errors.New("empty app version string")

// ErrEmptyCommitString signals than an empty commit id string has been provided
var ErrEmptyCommitString = errors.New("empty commit id string")

// ErrEmptyPubKey signals that an empty public key has been provided
var ErrEmptyPubKey = errors.New("public key is empty")
Loading
Loading