Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/shard-id-for-system-account' int…
Browse files Browse the repository at this point in the history
…o shard-id-for-system-account
  • Loading branch information
miiu96 committed Mar 8, 2024
2 parents 3e46a52 + 126965d commit a269948
Show file tree
Hide file tree
Showing 16 changed files with 306 additions and 7 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ For more details, go [here](https://docs.multiversx.com/sdk-and-tools/proxy/).
### validator

- `/v1.0/validator/statistics` (GET) --> returns the validator statistics data from an observer from any shard. Has a cache to avoid many requests
- `/v1.0/validator/auction` (GET) --> returns the validator auction list data from an observer from metachain. It doesn't have a cache mechanism, since there is already one in place at the node level

### block

Expand Down
11 changes: 11 additions & 0 deletions api/groups/baseValidatorGroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func NewValidatorGroup(facadeHandler data.FacadeHandler) (*validatorGroup, error

baseRoutesHandlers := []*data.EndpointHandlerData{
{Path: "/statistics", Handler: vg.statistics, Method: http.MethodGet},
{Path: "/auction", Handler: vg.auctionList, Method: http.MethodGet},
}
vg.baseGroup.endpoints = baseRoutesHandlers

Expand All @@ -43,3 +44,13 @@ func (group *validatorGroup) statistics(c *gin.Context) {

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

func (group *validatorGroup) auctionList(c *gin.Context) {
auctionList, err := group.facade.AuctionList()
if err != nil {
shared.RespondWith(c, http.StatusBadRequest, nil, err.Error(), data.ReturnCodeRequestError)
return
}

shared.RespondWith(c, http.StatusOK, gin.H{"auctionList": auctionList}, "", data.ReturnCodeSuccess)
}
70 changes: 70 additions & 0 deletions api/groups/baseValidatorGroup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,73 @@ func TestValidatorStatistics_ShouldWork(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.Code)
assert.Equal(t, response.Data.Statistics["statistics"], valStatsMap["statistics"])
}

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

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

auctionList := []*data.AuctionListValidatorAPIResponse{
{
Owner: "owner",
NumStakedNodes: 1,
TotalTopUp: "100",
TopUpPerNode: "100",
QualifiedTopUp: "50",
},
}
facade := &mock.FacadeStub{
AuctionListHandler: func() ([]*data.AuctionListValidatorAPIResponse, error) {
return auctionList, nil
},
}

validatorGroup, _ := groups.NewValidatorGroup(facade)
ws := startProxyServer(validatorGroup, validatorPath)

req, _ := http.NewRequest("GET", "/validator/auction", nil)
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

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

require.Equal(t, http.StatusOK, resp.Code)
require.Equal(t, data.AuctionListAPIResponse{
Data: data.AuctionListResponse{
AuctionListValidators: auctionList,
},
Error: "",
Code: string(data.ReturnCodeSuccess),
}, response)
})

t.Run("cannot get auction list from facade, should return error", func(t *testing.T) {
t.Parallel()

errFacade := errors.New("error getting auction list")
facade := &mock.FacadeStub{
AuctionListHandler: func() ([]*data.AuctionListValidatorAPIResponse, error) {
return nil, errFacade
},
}

validatorGroup, _ := groups.NewValidatorGroup(facade)
ws := startProxyServer(validatorGroup, validatorPath)

req, _ := http.NewRequest("GET", "/validator/auction", nil)
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

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

require.Equal(t, http.StatusBadRequest, resp.Code)
require.Equal(t, data.GenericAPIResponse{
Data: nil,
Error: errFacade.Error(),
Code: data.ReturnCodeRequestError,
}, response)
})
}
1 change: 1 addition & 0 deletions api/groups/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ type ProofFacadeHandler interface {
// ValidatorFacadeHandler interface defines methods that can be used from the facade
type ValidatorFacadeHandler interface {
ValidatorStatistics() (map[string]*data.ValidatorApiResponse, error)
AuctionList() ([]*data.AuctionListValidatorAPIResponse, error)
}

// VmValuesFacadeHandler interface defines methods that can be used from the facade
Expand Down
16 changes: 15 additions & 1 deletion api/mock/facadeStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type FacadeStub struct {
ExecuteSCQueryHandler func(query *data.SCQuery) (*vm.VMOutputApi, data.BlockInfo, error)
GetHeartbeatDataHandler func() (*data.HeartbeatResponse, error)
ValidatorStatisticsHandler func() (map[string]*data.ValidatorApiResponse, error)
AuctionListHandler func() ([]*data.AuctionListValidatorAPIResponse, error)
TransactionCostRequestHandler func(tx *data.Transaction) (*data.TxCostResponseData, error)
GetTransactionStatusHandler func(txHash string, sender string) (string, error)
GetProcessedTransactionStatusHandler func(txHash string) (string, error)
Expand Down Expand Up @@ -250,7 +251,20 @@ func (f *FacadeStub) GetESDTSupply(token string) (*data.ESDTSupplyResponse, erro

// ValidatorStatistics -
func (f *FacadeStub) ValidatorStatistics() (map[string]*data.ValidatorApiResponse, error) {
return f.ValidatorStatisticsHandler()
if f.ValidatorStatisticsHandler != nil {
return f.ValidatorStatisticsHandler()
}

return nil, nil
}

// AuctionList -
func (f *FacadeStub) AuctionList() ([]*data.AuctionListValidatorAPIResponse, error) {
if f.AuctionListHandler != nil {
return f.AuctionListHandler()
}

return nil, nil
}

// GetAccount -
Expand Down
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 @@ -78,7 +78,8 @@ Routes = [

[APIPackages.validator]
Routes = [
{ Name = "/statistics", Open = true, Secured = false, RateLimit = 0 }
{ Name = "/statistics", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/auction", Open = true, Secured = false, RateLimit = 0 }
]

[APIPackages.vm-values]
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 @@ -78,7 +78,8 @@ Routes = [

[APIPackages.validator]
Routes = [
{ Name = "/statistics", Open = true, Secured = false, RateLimit = 0 }
{ Name = "/statistics", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/auction", Open = true, Secured = false, RateLimit = 0 }
]

[APIPackages.vm-values]
Expand Down
29 changes: 29 additions & 0 deletions data/auctionList.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package data

// AuctionNode holds data needed for a node in auction to respond to API calls
type AuctionNode struct {
BlsKey string `json:"blsKey"`
Qualified bool `json:"qualified"`
}

// AuctionListValidatorAPIResponse holds the data needed for an auction node validator for responding to API calls
type AuctionListValidatorAPIResponse struct {
Owner string `json:"owner"`
NumStakedNodes int64 `json:"numStakedNodes"`
TotalTopUp string `json:"totalTopUp"`
TopUpPerNode string `json:"topUpPerNode"`
QualifiedTopUp string `json:"qualifiedTopUp"`
Nodes []*AuctionNode `json:"nodes"`
}

// AuctionListResponse respects the format the auction list api response received from the observers
type AuctionListResponse struct {
AuctionListValidators []*AuctionListValidatorAPIResponse `json:"auctionList"`
}

// AuctionListAPIResponse respects the format the auction list received from the observers
type AuctionListAPIResponse struct {
Data AuctionListResponse `json:"data"`
Error string `json:"error"`
Code string `json:"code"`
}
10 changes: 10 additions & 0 deletions facade/baseFacade.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,16 @@ func (pf *ProxyFacade) ValidatorStatistics() (map[string]*data.ValidatorApiRespo
return valStats.Statistics, nil
}

// AuctionList will return the auction list
func (epf *ProxyFacade) AuctionList() ([]*data.AuctionListValidatorAPIResponse, error) {
auctionList, err := epf.valStatsProc.GetAuctionList()
if err != nil {
return nil, err
}

return auctionList.AuctionListValidators, nil
}

// GetAtlasBlockByShardIDAndNonce returns block by shardID and nonce in a BlockAtlas-friendly-format
func (pf *ProxyFacade) GetAtlasBlockByShardIDAndNonce(shardID uint32, nonce uint64) (data.AtlasBlock, error) {
return pf.blockProc.GetAtlasBlockByShardIDAndNonce(shardID, nonce)
Expand Down
1 change: 1 addition & 0 deletions facade/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type NodeGroupProcessor interface {
// ValidatorStatisticsProcessor defines what a validator statistics processor should do
type ValidatorStatisticsProcessor interface {
GetValidatorStatistics() (*data.ValidatorStatisticsResponse, error)
GetAuctionList() (*data.AuctionListResponse, error)
}

// ESDTSupplyProcessor defines what an esdt supply processor should do
Expand Down
5 changes: 5 additions & 0 deletions facade/mock/accountProccessorStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,8 @@ func (aps *AccountProcessorStub) IsDataTrieMigrated(address string, options comm

return &data.GenericAPIResponse{}, nil
}

// AuctionList -
func (aps *AccountProcessorStub) AuctionList() ([]*data.AuctionListValidatorAPIResponse, error) {
return nil, nil
}
5 changes: 5 additions & 0 deletions facade/mock/validatorStatisticsProcessorStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ type ValidatorStatisticsProcessorStub struct {
func (v *ValidatorStatisticsProcessorStub) GetValidatorStatistics() (*data.ValidatorStatisticsResponse, error) {
return v.GetValidatorStatisticsCalled()
}

// GetAuctionList -
func (v *ValidatorStatisticsProcessorStub) GetAuctionList() (*data.AuctionListResponse, error) {
return nil, nil
}
5 changes: 4 additions & 1 deletion process/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ var ErrNilValidatorStatisticsCacher = errors.New("nil validator statistics cache
var ErrNilEconomicMetricsCacher = errors.New("nil economic metrics cacher")

// ErrValidatorStatisticsNotAvailable signals that the validator statistics data is not found
var ErrValidatorStatisticsNotAvailable = errors.New("validator statistics data not found at any observer")
var ErrValidatorStatisticsNotAvailable = errors.New("validator statistics data not found on any observer")

// ErrAuctionListNotAvailable signals that the auction list data is not found
var ErrAuctionListNotAvailable = errors.New("auction list data not found on any observer")

// ErrInvalidCacheValidityDuration signals that the given validity duration for cache data is invalid
var ErrInvalidCacheValidityDuration = errors.New("invalid cache validity duration")
Expand Down
27 changes: 27 additions & 0 deletions process/validatorAuctionProcessor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package process

import (
"github.com/multiversx/mx-chain-core-go/core"
"github.com/multiversx/mx-chain-proxy-go/data"
)

// GetAuctionList returns the auction list from a metachain observer node
func (vsp *ValidatorStatisticsProcessor) GetAuctionList() (*data.AuctionListResponse, error) {
observers, errFetchObs := vsp.proc.GetObservers(core.MetachainShardId, data.AvailabilityRecent)
if errFetchObs != nil {
return nil, errFetchObs
}

var valStatsResponse data.AuctionListAPIResponse
for _, observer := range observers {
_, err := vsp.proc.CallGetRestEndPoint(observer.Address, auctionListPath, &valStatsResponse)
if err == nil {
log.Info("auction list fetched from API", "observer", observer.Address)
return &valStatsResponse.Data, nil
}

log.Error("getAuctionListFromApi", "observer", observer.Address, "error", err)
}

return nil, ErrAuctionListNotAvailable
}
118 changes: 118 additions & 0 deletions process/validatorAuctionProcessor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package process

import (
"encoding/json"
"errors"
"sync/atomic"
"testing"
"time"

"github.com/multiversx/mx-chain-core-go/core"
"github.com/multiversx/mx-chain-proxy-go/data"
"github.com/multiversx/mx-chain-proxy-go/process/mock"
"github.com/stretchr/testify/require"
)

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

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

node := &data.NodeData{
Address: "addr",
ShardId: core.MetachainShardId,
}
expectedResp := &data.AuctionListAPIResponse{
Data: data.AuctionListResponse{
AuctionListValidators: []*data.AuctionListValidatorAPIResponse{
{
Owner: "owner",
NumStakedNodes: 4,
TotalTopUp: "100",
TopUpPerNode: "50",
QualifiedTopUp: "50",
},
},
},
Error: "",
Code: "ok",
}
expectedRespMarshalled, err := json.Marshal(expectedResp)
require.Nil(t, err)

processor := &mock.ProcessorStub{
GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) {
require.Equal(t, core.MetachainShardId, shardId)

return []*data.NodeData{node}, nil
},
CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) {
require.Equal(t, node.Address, address)
require.Equal(t, auctionListPath, path)

err = json.Unmarshal(expectedRespMarshalled, value)
require.Nil(t, err)
return 0, nil
},
}
vsp, _ := NewValidatorStatisticsProcessor(processor, &mock.ValStatsCacherMock{}, time.Second)
resp, err := vsp.GetAuctionList()
require.Nil(t, err)
require.Equal(t, expectedResp.Data, *resp)
})

t.Run("get observers failed, should return error", func(t *testing.T) {
t.Parallel()

errGetObservers := errors.New("err getting observers")
callGetRestEndPointCalledCt := int32(0)

processor := &mock.ProcessorStub{
GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) {
require.Equal(t, core.MetachainShardId, shardId)
return nil, errGetObservers
},
CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) {
atomic.AddInt32(&callGetRestEndPointCalledCt, 1)

return 0, nil
},
}
vsp, _ := NewValidatorStatisticsProcessor(processor, &mock.ValStatsCacherMock{}, time.Second)

resp, err := vsp.GetAuctionList()
require.Equal(t, errGetObservers, err)
require.Nil(t, resp)
require.Equal(t, int32(0), callGetRestEndPointCalledCt)
})

t.Run("could not get auction list from observer, should return error", func(t *testing.T) {
t.Parallel()

node := &data.NodeData{
Address: "addr",
ShardId: core.MetachainShardId,
}

errCallEndpoint := errors.New("error call endpoint")
processor := &mock.ProcessorStub{
GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) {
require.Equal(t, core.MetachainShardId, shardId)

return []*data.NodeData{node}, nil
},
CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) {
require.Equal(t, node.Address, address)
require.Equal(t, auctionListPath, path)

return 0, errCallEndpoint
},
}
vsp, _ := NewValidatorStatisticsProcessor(processor, &mock.ValStatsCacherMock{}, time.Second)

resp, err := vsp.GetAuctionList()
require.Equal(t, ErrAuctionListNotAvailable, err)
require.Nil(t, resp)
})
}
Loading

0 comments on commit a269948

Please sign in to comment.