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

New API endpoints for managed keys #5357

Merged
merged 4 commits into from
Jun 20, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions api/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,9 @@ var ErrInvalidRole = errors.New("invalid role")

// ErrIsDataTrieMigrated signals that an error occurred while trying to verify the migration status of the data trie
var ErrIsDataTrieMigrated = errors.New("could not verify the migration status of the data trie")

// ErrGetEligibleManagedKeys signals that an error occurred while getting the eligible managed keys
var ErrGetEligibleManagedKeys = errors.New("error getting the eligible managed keys")

// ErrGetWaitingManagedKeys signals that an error occurred while getting the waiting managed keys
var ErrGetWaitingManagedKeys = errors.New("error getting the waiting managed keys")
70 changes: 70 additions & 0 deletions api/groups/nodeGroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const (
epochStartDataForEpoch = "/epoch-start/:epoch"
bootstrapStatusPath = "/bootstrapstatus"
connectedPeersRatingsPath = "/connected-peers-ratings"
managedKeysCount = "/managed-keys/count"
eligibleManagedKeys = "/managed-keys/eligible"
waitingManagedKeys = "/managed-keys/waiting"
)

// nodeFacadeHandler defines the methods to be implemented by a facade for node requests
Expand All @@ -37,6 +40,9 @@ type nodeFacadeHandler interface {
GetEpochStartDataAPI(epoch uint32) (*common.EpochStartDataAPI, error)
GetPeerInfo(pid string) ([]core.QueryP2PPeerInfo, error)
GetConnectedPeersRatings() string
GetManagedKeysCount() int
GetEligibleManagedKeys() ([]string, error)
GetWaitingManagedKeys() ([]string, error)
IsInterfaceNil() bool
}

Expand Down Expand Up @@ -109,6 +115,21 @@ func NewNodeGroup(facade nodeFacadeHandler) (*nodeGroup, error) {
Method: http.MethodGet,
Handler: ng.connectedPeersRatings,
},
{
Path: managedKeysCount,
Method: http.MethodGet,
Handler: ng.managedKeysCount,
},
{
Path: eligibleManagedKeys,
Method: http.MethodGet,
Handler: ng.managedKeysEligible,
},
{
Path: waitingManagedKeys,
Method: http.MethodGet,
Handler: ng.managedKeysWaiting,
},
}
ng.endpoints = endpoints

Expand Down Expand Up @@ -338,6 +359,55 @@ func (ng *nodeGroup) connectedPeersRatings(c *gin.Context) {
)
}

// managedKeysCount returns the node's number of managed keys
func (ng *nodeGroup) managedKeysCount(c *gin.Context) {
count := ng.getFacade().GetManagedKeysCount()
c.JSON(
http.StatusOK,
shared.GenericAPIResponse{
Data: gin.H{"count": count},
Error: "",
Code: shared.ReturnCodeSuccess,
},
)
}

// managedKeysEligible returns the node's eligible managed keys
func (ng *nodeGroup) managedKeysEligible(c *gin.Context) {
keys, err := ng.getFacade().GetEligibleManagedKeys()
if err != nil {
shared.RespondWithInternalError(c, errors.ErrGetEligibleManagedKeys, err)
return
}

c.JSON(
http.StatusOK,
shared.GenericAPIResponse{
Data: gin.H{"eligibleKeys": keys},
Error: "",
Code: shared.ReturnCodeSuccess,
},
)
}

// managedKeysWaiting returns the node's waiting managed keys
func (ng *nodeGroup) managedKeysWaiting(c *gin.Context) {
keys, err := ng.getFacade().GetWaitingManagedKeys()
if err != nil {
shared.RespondWithInternalError(c, errors.ErrGetWaitingManagedKeys, err)
return
}

c.JSON(
http.StatusOK,
shared.GenericAPIResponse{
Data: gin.H{"waitingKeys": keys},
Error: "",
Code: shared.ReturnCodeSuccess,
},
)
}

func (ng *nodeGroup) getFacade() nodeFacadeHandler {
ng.mutFacade.RLock()
defer ng.mutFacade.RUnlock()
Expand Down
169 changes: 169 additions & 0 deletions api/groups/nodeGroup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,27 @@ type epochStartResponse struct {
generalResponse
}

type managedKeysCountResponse struct {
Data struct {
Count int `json:"count"`
} `json:"data"`
generalResponse
}

type managedEligibleKeysResponse struct {
Data struct {
Keys []string `json:"eligibleKeys"`
} `json:"data"`
generalResponse
}

type managedWaitingKeysResponse struct {
Data struct {
Keys []string `json:"waitingKeys"`
} `json:"data"`
generalResponse
}

func init() {
gin.SetMode(gin.TestMode)
}
Expand Down Expand Up @@ -651,6 +672,151 @@ func TestPrometheusMetrics_ShouldWork(t *testing.T) {
assert.True(t, keyAndValueFoundInResponse)
}

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

providedCount := 1000
facade := mock.FacadeStub{
GetManagedKeysCountCalled: func() int {
return providedCount
},
}

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

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

req, _ := http.NewRequest("GET", "/node/managed-keys/count", nil)
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

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

assert.Equal(t, http.StatusOK, resp.Code)
assert.Equal(t, "", response.Error)
assert.Equal(t, providedCount, response.Data.Count)
}

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

t.Run("facade error should error", func(t *testing.T) {
t.Parallel()

facade := mock.FacadeStub{
GetEligibleManagedKeysCalled: func() ([]string, error) {
return nil, expectedErr
},
}

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

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

req, _ := http.NewRequest("GET", "/node/managed-keys/eligible", 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()))
})
t.Run("should work", func(t *testing.T) {
t.Parallel()

providedKeys := []string{
"key1",
"key2",
"key3",
}
facade := mock.FacadeStub{
GetEligibleManagedKeysCalled: func() ([]string, error) {
return providedKeys, nil
},
}

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

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

req, _ := http.NewRequest("GET", "/node/managed-keys/eligible", nil)
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

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

assert.Equal(t, http.StatusOK, resp.Code)
assert.Equal(t, "", response.Error)
assert.Equal(t, providedKeys, response.Data.Keys)
})
}

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

t.Run("facade error should error", func(t *testing.T) {
t.Parallel()

facade := mock.FacadeStub{
GetWaitingManagedKeysCalled: func() ([]string, error) {
return nil, expectedErr
},
}

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

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

req, _ := http.NewRequest("GET", "/node/managed-keys/waiting", 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()))
})
t.Run("should work", func(t *testing.T) {
t.Parallel()

providedKeys := []string{
"key1",
"key2",
"key3",
}
facade := mock.FacadeStub{
GetWaitingManagedKeysCalled: func() ([]string, error) {
return providedKeys, nil
},
}

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

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

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

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

assert.Equal(t, http.StatusOK, resp.Code)
assert.Equal(t, "", response.Error)
assert.Equal(t, providedKeys, response.Data.Keys)
})
}

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

Expand Down Expand Up @@ -758,6 +924,9 @@ func getNodeRoutesConfig() config.ApiRoutesConfig {
{Name: "/epoch-start/:epoch", Open: true},
{Name: "/bootstrapstatus", Open: true},
{Name: "/connected-peers-ratings", Open: true},
{Name: "/managed-keys/count", Open: true},
{Name: "/managed-keys/eligible", Open: true},
{Name: "/managed-keys/waiting", Open: true},
},
},
},
Expand Down
27 changes: 27 additions & 0 deletions api/mock/facadeStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ type FacadeStub struct {
PprofEnabledCalled func() bool
DecodeAddressPubkeyCalled func(pk string) ([]byte, error)
IsDataTrieMigratedCalled func(address string, options api.AccountQueryOptions) (bool, error)
GetManagedKeysCountCalled func() int
GetEligibleManagedKeysCalled func() ([]string, error)
GetWaitingManagedKeysCalled func() ([]string, error)
}

// GetTokenSupply -
Expand Down Expand Up @@ -573,6 +576,30 @@ func (f *FacadeStub) IsSelfTrigger() bool {
return false
}

// GetManagedKeysCount -
func (f *FacadeStub) GetManagedKeysCount() int {
if f.GetManagedKeysCountCalled != nil {
return f.GetManagedKeysCountCalled()
}
return 0
}

// GetEligibleManagedKeys -
func (f *FacadeStub) GetEligibleManagedKeys() ([]string, error) {
if f.GetEligibleManagedKeysCalled != nil {
return f.GetEligibleManagedKeysCalled()
}
return make([]string, 0), nil
}

// GetWaitingManagedKeys -
func (f *FacadeStub) GetWaitingManagedKeys() ([]string, error) {
if f.GetWaitingManagedKeysCalled != nil {
return f.GetWaitingManagedKeysCalled()
}
return make([]string, 0), nil
}

// Close -
func (f *FacadeStub) Close() error {
return nil
Expand Down
3 changes: 3 additions & 0 deletions api/shared/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,5 +127,8 @@ type FacadeHandler interface {
GetLastPoolNonceForSender(sender string) (uint64, error)
GetTransactionsPoolNonceGapsForSender(sender string) (*common.TransactionsPoolNonceGapsForSenderApiResponse, error)
IsDataTrieMigrated(address string, options api.AccountQueryOptions) (bool, error)
GetManagedKeysCount() int
GetEligibleManagedKeys() ([]string, error)
GetWaitingManagedKeys() ([]string, error)
IsInterfaceNil() bool
}
11 changes: 10 additions & 1 deletion cmd/node/config/api.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,16 @@
{ Name = "/bootstrapstatus", Open = true },

# /node/connected-peers-ratings will return the peers ratings
{ Name = "/connected-peers-ratings", Open = true }
{ Name = "/connected-peers-ratings", Open = true },

# /node/managed-keys/count will return the number of keys managed by the node
{ Name = "/managed-keys/count", Open = true },

# /node/managed-keys/eligible will return the eligible keys managed by the node on the current epoch
{ Name = "/managed-keys/eligible", Open = true },

# /node/managed-keys/waiting will return the waiting keys managed by the node on the current epoch
{ Name = "/managed-keys/waiting", Open = true }
]

[APIPackages.address]
Expand Down
4 changes: 2 additions & 2 deletions common/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ type StateSyncNotifierSubscriber interface {
// ManagedPeersMonitor defines the operations of an entity that monitors the managed peers holder
type ManagedPeersMonitor interface {
GetManagedKeysCount() int
GetEligibleManagedKeys(epoch uint32) ([][]byte, error)
GetWaitingManagedKeys(epoch uint32) ([][]byte, error)
GetEligibleManagedKeys() ([][]byte, error)
GetWaitingManagedKeys() ([][]byte, error)
IsInterfaceNil() bool
}
4 changes: 2 additions & 2 deletions errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,8 +532,8 @@ var ErrNilGenesisNodesSetupHandler = errors.New("nil genesis nodes setup handler
// ErrNilManagedPeersHolder signals that a nil managed peers holder has been provided
var ErrNilManagedPeersHolder = errors.New("nil managed peers holder")

// ErrEmptyPeerID signals that an empty peer ID has been provided
var ErrEmptyPeerID = errors.New("empty peer ID")
// ErrNilManagedPeersMonitor signals that a nil managed peers monitor has been provided
var ErrNilManagedPeersMonitor = errors.New("nil managed peers monitor")

// ErrNilPeersRatingHandler signals that a nil peers rating handler implementation has been provided
var ErrNilPeersRatingHandler = errors.New("nil peers rating handler")
Expand Down