Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
22 changes: 22 additions & 0 deletions api/groups/baseTransactionGroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func NewTransactionGroup(facadeHandler data.FacadeHandler) (*transactionGroup, e
{Path: "/:txhash/process-status", Handler: tg.getProcessedTransactionStatus, Method: http.MethodGet},
{Path: "/:txhash", Handler: tg.getTransaction, Method: http.MethodGet},
{Path: "/pool", Handler: tg.getTransactionsPool, Method: http.MethodGet},
{Path: "/pool/count", Handler: tg.getTransactionsPoolCount, Method: http.MethodGet},
}
tg.baseGroup.endpoints = baseRoutesHandlers

Expand Down Expand Up @@ -413,3 +414,24 @@ func getTxPoolForSender(c *gin.Context, ef TransactionFacadeHandler, sender, fie

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

func getTxPoolCount(c *gin.Context, ef TransactionFacadeHandler, shardID uint32) {
txPoolCount, err := ef.GetTransactionsPoolCount(shardID)
if err != nil {
shared.RespondWith(c, http.StatusInternalServerError, nil, err.Error(), data.ReturnCodeInternalError)
return
}

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

// getTransactionsPoolCount will return the number of transactions currently in the pool for the given shard
func (group *transactionGroup) getTransactionsPoolCount(c *gin.Context) {
shardIDParam, err := parseUint32UrlParam(c, common.UrlParameterShardID)
if err != nil || !shardIDParam.HasValue {
shared.RespondWith(c, http.StatusBadRequest, nil, errors.ErrBadUrlParams.Error(), data.ReturnCodeRequestError)
return
}

getTxPoolCount(c, group.facade, shardIDParam.Value)
}
103 changes: 103 additions & 0 deletions api/groups/baseTransactionGroup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ type nonceGapsResp struct {
Data nonceGaps
}

type txPoolCount struct {
TxPoolCount uint64 `json:"txPoolCount"`
}

type txPoolCountResp struct {
GeneralResponse
Data txPoolCount
}

type txProcessedStatusResp struct {
GeneralResponse
Data struct {
Expand Down Expand Up @@ -751,6 +760,100 @@ func TestGetTransactionsPoolPoolNonceGapsForSender_ReturnsSuccessfully(t *testin
assert.Equal(t, providedNonceGaps, &response.Data.NonceGaps)
}

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

t.Run("missing shard-id param should err", func(t *testing.T) {
t.Parallel()

transactionsGroup, err := groups.NewTransactionGroup(&mock.FacadeStub{})
require.NoError(t, err)
ws := startProxyServer(transactionsGroup, transactionsPath)

req, _ := http.NewRequest("GET", "/transaction/pool/count", nil)

resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

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

assert.Equal(t, http.StatusBadRequest, resp.Code)
assert.Equal(t, apiErrors.ErrBadUrlParams.Error(), response.Error)
})
t.Run("invalid shard-id param should err", func(t *testing.T) {
t.Parallel()

transactionsGroup, err := groups.NewTransactionGroup(&mock.FacadeStub{})
require.NoError(t, err)
ws := startProxyServer(transactionsGroup, transactionsPath)

req, _ := http.NewRequest("GET", "/transaction/pool/count?shard-id=invalid", nil)

resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

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

assert.Equal(t, http.StatusBadRequest, resp.Code)
assert.Equal(t, apiErrors.ErrBadUrlParams.Error(), response.Error)
})
t.Run("facade error should err", func(t *testing.T) {
t.Parallel()

expectedErr := errors.New("facade error")
facade := &mock.FacadeStub{
GetTransactionsPoolCountHandler: func(shardID uint32) (uint64, error) {
assert.Equal(t, uint32(0), shardID)
return 0, expectedErr
},
}

transactionsGroup, err := groups.NewTransactionGroup(facade)
require.NoError(t, err)
ws := startProxyServer(transactionsGroup, transactionsPath)

req, _ := http.NewRequest("GET", "/transaction/pool/count?shard-id=0", nil)

resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

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

assert.Equal(t, http.StatusInternalServerError, resp.Code)
assert.Equal(t, expectedErr.Error(), response.Error)
})
t.Run("should work", func(t *testing.T) {
t.Parallel()

providedCount := uint64(42)
facade := &mock.FacadeStub{
GetTransactionsPoolCountHandler: func(shardID uint32) (uint64, error) {
assert.Equal(t, uint32(1), shardID)
return providedCount, nil
},
}

transactionsGroup, err := groups.NewTransactionGroup(facade)
require.NoError(t, err)
ws := startProxyServer(transactionsGroup, transactionsPath)

req, _ := http.NewRequest("GET", "/transaction/pool/count?shard-id=1", nil)

resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

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

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

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

Expand Down
1 change: 1 addition & 0 deletions api/groups/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ type TransactionFacadeHandler interface {
GetTransactionsPoolForSender(sender, fields string) (*data.TransactionsPoolForSender, error)
GetLastPoolNonceForSender(sender string) (uint64, error)
GetTransactionsPoolNonceGapsForSender(sender string) (*data.TransactionsPoolNonceGaps, error)
GetTransactionsPoolCount(shardID uint32) (uint64, error)
}

// ProofFacadeHandler interface defines methods that can be used from the facade
Expand Down
10 changes: 10 additions & 0 deletions api/mock/facadeStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type FacadeStub struct {
GetTransactionsPoolForSenderHandler func(sender, fields string) (*data.TransactionsPoolForSender, error)
GetLastPoolNonceForSenderHandler func(sender string) (uint64, error)
GetTransactionsPoolNonceGapsForSenderHandler func(sender string) (*data.TransactionsPoolNonceGaps, error)
GetTransactionsPoolCountHandler func(shardID uint32) (uint64, error)
SendTransactionHandler func(tx *data.Transaction) (int, string, error)
SendMultipleTransactionsHandler func(txs []*data.Transaction) (data.MultipleTransactionsResponseData, error)
SimulateTransactionHandler func(tx *data.Transaction, checkSignature bool) (*data.GenericAPIResponse, error)
Expand Down Expand Up @@ -405,6 +406,15 @@ func (f *FacadeStub) GetTransactionsPoolNonceGapsForSender(sender string) (*data
return nil, nil
}

// GetTransactionsPoolCount -
func (f *FacadeStub) GetTransactionsPoolCount(shardID uint32) (uint64, error) {
if f.GetTransactionsPoolCountHandler != nil {
return f.GetTransactionsPoolCountHandler(shardID)
}

return 0, nil
}

// SendTransaction -
func (f *FacadeStub) SendTransaction(tx *data.Transaction) (int, string, error) {
return f.SendTransactionHandler(tx)
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 @@ -102,7 +102,8 @@ Routes = [
{ Name = "/:txhash", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/:txhash/status", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/:txhash/process-status", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/pool", Open = true, Secured = false, RateLimit = 0 }
{ Name = "/pool", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/pool/count", Open = true, Secured = false, RateLimit = 0 }
]

[APIPackages.block]
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 @@ -102,7 +102,8 @@ Routes = [
{ Name = "/:txhash", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/:txhash/status", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/:txhash/process-status", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/pool", Open = true, Secured = false, RateLimit = 0 }
{ Name = "/pool", Open = true, Secured = false, RateLimit = 0 },
{ Name = "/pool/count", Open = true, Secured = false, RateLimit = 0 }
]

[APIPackages.block]
Expand Down
5 changes: 5 additions & 0 deletions facade/baseFacade.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,11 @@ func (pf *ProxyFacade) GetTriesStatistics(shardID uint32) (*data.TrieStatisticsA
return pf.nodeStatusProc.GetTriesStatistics(shardID)
}

// GetTransactionsPoolCount will return the number of transactions currently in the pool for the given shard
func (pf *ProxyFacade) GetTransactionsPoolCount(shardID uint32) (uint64, error) {
return pf.nodeStatusProc.GetTransactionsPoolCount(shardID)
}

// GetEpochStartData retrieves epoch start data for the provides epoch and shard ID
func (pf *ProxyFacade) GetEpochStartData(epoch uint32, shardID uint32) (*data.GenericAPIResponse, error) {
return pf.nodeStatusProc.GetEpochStartData(epoch, shardID)
Expand Down
1 change: 1 addition & 0 deletions facade/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ type NodeStatusProcessor interface {
GetGasConfigs() (*data.GenericAPIResponse, error)
GetTriesStatistics(shardID uint32) (*data.TrieStatisticsAPIResponse, error)
GetEpochStartData(epoch uint32, shardID uint32) (*data.GenericAPIResponse, error)
GetTransactionsPoolCount(shardID uint32) (uint64, error)
}

// BlocksProcessor defines what a blocks processor should do
Expand Down
9 changes: 9 additions & 0 deletions facade/mock/nodeStatusProcessorStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type NodeStatusProcessorStub struct {
GetGasConfigsCalled func() (*data.GenericAPIResponse, error)
GetTriesStatisticsCalled func(shardID uint32) (*data.TrieStatisticsAPIResponse, error)
GetEpochStartDataCalled func(epoch uint32, shardID uint32) (*data.GenericAPIResponse, error)
GetTransactionsPoolCountCalled func(shardID uint32) (uint64, error)
}

// GetNetworkConfigMetrics --
Expand Down Expand Up @@ -154,3 +155,11 @@ func (stub *NodeStatusProcessorStub) GetTriesStatistics(shardID uint32) (*data.T
}
return &data.TrieStatisticsAPIResponse{}, nil
}

// GetTransactionsPoolCount -
func (stub *NodeStatusProcessorStub) GetTransactionsPoolCount(shardID uint32) (uint64, error) {
if stub.GetTransactionsPoolCountCalled != nil {
return stub.GetTransactionsPoolCountCalled(shardID)
}
return 0, nil
}
22 changes: 22 additions & 0 deletions process/nodeStatusProcessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ const (

// MetricNonce is the metric for monitoring the nonce of a node
MetricNonce = "erd_nonce"

// MetricTxPoolLoad is the metric for monitoring the number of transactions currently in the pool
MetricTxPoolLoad = "erd_tx_pool_load"
)

// NodeStatusProcessor handles the action needed for fetching data related to status metrics from nodes
Expand Down Expand Up @@ -373,6 +376,16 @@ func (nsp *NodeStatusProcessor) GetTriesStatistics(shardID uint32) (*data.TrieSt
return getTrieStatistics(nodeStatusResponse.Data)
}

// GetTransactionsPoolCount will return the number of transactions currently in the pool for the given shard
func (nsp *NodeStatusProcessor) GetTransactionsPoolCount(shardID uint32) (uint64, error) {
nodeStatusResponse, err := nsp.getNodeStatusMetrics(shardID)
if err != nil {
return 0, err
}

return getTransactionsPoolCount(nodeStatusResponse.Data)
}

func getMinNonce(noncesSlice []uint64) uint64 {
// initialize min with max uint64 value
min := uint64(math.MaxUint64)
Expand Down Expand Up @@ -432,6 +445,15 @@ func getTrieStatistics(nodeStatusData interface{}) (*data.TrieStatisticsAPIRespo
return trieStatistics, nil
}

func getTransactionsPoolCount(nodeStatusData interface{}) (uint64, error) {
txPoolLoadMetric, ok := getMetric(nodeStatusData, MetricTxPoolLoad)
if !ok {
return 0, ErrCannotParseNodeStatusMetrics
}

return getUint(txPoolLoadMetric), nil
}

func getMetric(nodeStatusData interface{}, metric string) (interface{}, bool) {
metricsMapI, ok := nodeStatusData.(map[string]interface{})
if !ok {
Expand Down
104 changes: 104 additions & 0 deletions process/nodeStatusProcessor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,110 @@ func TestNodeStatusProcessor_GetTriesStatistics(t *testing.T) {
})
}

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

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

localErr := errors.New("local error")
nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{
GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) {
return nil, localErr
},
},
&mock.GenericApiResponseCacherMock{},
time.Second,
)

count, err := nodeStatusProc.GetTransactionsPoolCount(0)
require.Equal(t, uint64(0), count)
require.Equal(t, localErr, err)
})
t.Run("error sending request", func(t *testing.T) {
t.Parallel()

nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{
GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) {
return []*data.NodeData{
{Address: "address1", ShardId: 0},
}, nil
},
CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) {
return 0, errors.New("endpoint error")
},
},
&mock.GenericApiResponseCacherMock{},
time.Second,
)

count, err := nodeStatusProc.GetTransactionsPoolCount(0)
require.Equal(t, uint64(0), count)
require.True(t, errors.Is(err, ErrSendingRequest))
})
t.Run("missing metric from response", func(t *testing.T) {
t.Parallel()

nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{
GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) {
return []*data.NodeData{
{Address: "address1", ShardId: 0},
}, nil
},
CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) {
localMap := map[string]interface{}{
"metrics": map[string]interface{}{},
}

genericResp := &data.GenericAPIResponse{Data: localMap}
genRespBytes, _ := json.Marshal(genericResp)

return 0, json.Unmarshal(genRespBytes, value)
},
},
&mock.GenericApiResponseCacherMock{},
time.Second,
)

count, err := nodeStatusProc.GetTransactionsPoolCount(0)
require.Equal(t, uint64(0), count)
require.Equal(t, ErrCannotParseNodeStatusMetrics, err)
})
t.Run("should work", func(t *testing.T) {
t.Parallel()

providedCount := uint64(42)
nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{
GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) {
require.Equal(t, uint32(1), shardId)
return []*data.NodeData{
{Address: "address1", ShardId: 1},
}, nil
},
CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) {
require.Equal(t, "/node/status", path)
localMap := map[string]interface{}{
"metrics": map[string]interface{}{
"erd_tx_pool_load": float64(providedCount),
},
}

genericResp := &data.GenericAPIResponse{Data: localMap}
genRespBytes, _ := json.Marshal(genericResp)

return 0, json.Unmarshal(genRespBytes, value)
},
},
&mock.GenericApiResponseCacherMock{},
time.Nanosecond,
)

count, err := nodeStatusProc.GetTransactionsPoolCount(1)
require.NoError(t, err)
require.Equal(t, providedCount, count)
})
}

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

Expand Down
Loading