Skip to content
Open
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
20 changes: 20 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,22 @@ func getTxPoolForSender(c *gin.Context, ef TransactionFacadeHandler, sender, fie

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

// getTransactionsPoolCount will return the number of transactions currently in the pool.
// If a shard-id url param is provided, it returns the count only for that shard,
// otherwise it returns the counts for every shard.
func (group *transactionGroup) getTransactionsPoolCount(c *gin.Context) {
shardIDParam, err := parseUint32UrlParam(c, common.UrlParameterShardID)
if err != nil {
shared.RespondWith(c, http.StatusBadRequest, nil, errors.ErrBadUrlParams.Error(), data.ReturnCodeRequestError)
return
}

txPoolCounts, err := group.facade.GetTransactionsPoolCounts(shardIDParam)
if err != nil {
shared.RespondWith(c, http.StatusInternalServerError, nil, err.Error(), data.ReturnCodeInternalError)
return
}

shared.RespondWith(c, http.StatusOK, gin.H{"txPoolCounts": txPoolCounts}, "", data.ReturnCodeSuccess)
}
143 changes: 143 additions & 0 deletions api/groups/baseTransactionGroup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/http/httptest"
"testing"

"github.com/multiversx/mx-chain-core-go/core"
apiErrors "github.com/multiversx/mx-chain-proxy-go/api/errors"
"github.com/multiversx/mx-chain-proxy-go/api/groups"
"github.com/multiversx/mx-chain-proxy-go/api/mock"
Expand Down Expand Up @@ -71,6 +72,15 @@ type nonceGapsResp struct {
Data nonceGaps
}

type txPoolCounts struct {
TxPoolCounts map[string]uint64 `json:"txPoolCounts"`
}

type txPoolCountsResp struct {
GeneralResponse
Data txPoolCounts
}

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

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

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{
GetTransactionsPoolCountsHandler: func(shardIDParam core.OptionalUint32) (map[uint32]uint64, error) {
require.True(t, shardIDParam.HasValue)
assert.Equal(t, uint32(0), shardIDParam.Value)
return nil, 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 for single shard", func(t *testing.T) {
t.Parallel()

providedCounts := map[uint32]uint64{1: 42}
facade := &mock.FacadeStub{
GetTransactionsPoolCountsHandler: func(shardIDParam core.OptionalUint32) (map[uint32]uint64, error) {
require.True(t, shardIDParam.HasValue)
assert.Equal(t, uint32(1), shardIDParam.Value)
return providedCounts, 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 := txPoolCountsResp{}
loadResponse(resp.Body, &response)

assert.Equal(t, http.StatusOK, resp.Code)
assert.Equal(t, "", response.Error)
assert.Equal(t, uint64(42), response.Data.TxPoolCounts["1"])
})
t.Run("facade error for all shards should err", func(t *testing.T) {
t.Parallel()

expectedErr := errors.New("facade error")
facade := &mock.FacadeStub{
GetTransactionsPoolCountsHandler: func(shardIDParam core.OptionalUint32) (map[uint32]uint64, error) {
require.False(t, shardIDParam.HasValue)
return nil, expectedErr
},
}

transactionsGroup, err := groups.NewTransactionGroup(facade)
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.StatusInternalServerError, resp.Code)
assert.Equal(t, expectedErr.Error(), response.Error)
})
t.Run("should work for all shards", func(t *testing.T) {
t.Parallel()

providedCounts := map[uint32]uint64{0: 10, 1: 42, 2: 7}
facade := &mock.FacadeStub{
GetTransactionsPoolCountsHandler: func(shardIDParam core.OptionalUint32) (map[uint32]uint64, error) {
require.False(t, shardIDParam.HasValue)
return providedCounts, nil
},
}

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

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

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

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

assert.Equal(t, http.StatusOK, resp.Code)
assert.Equal(t, "", response.Error)
assert.Equal(t, uint64(10), response.Data.TxPoolCounts["0"])
assert.Equal(t, uint64(42), response.Data.TxPoolCounts["1"])
assert.Equal(t, uint64(7), response.Data.TxPoolCounts["2"])
})
}

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

Expand Down
2 changes: 2 additions & 0 deletions api/groups/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package groups
import (
"math/big"

"github.com/multiversx/mx-chain-core-go/core"
"github.com/multiversx/mx-chain-core-go/data/transaction"
"github.com/multiversx/mx-chain-core-go/data/vm"
"github.com/multiversx/mx-chain-proxy-go/common"
Expand Down Expand Up @@ -105,6 +106,7 @@ type TransactionFacadeHandler interface {
GetTransactionsPoolForSender(sender, fields string) (*data.TransactionsPoolForSender, error)
GetLastPoolNonceForSender(sender string) (uint64, error)
GetTransactionsPoolNonceGapsForSender(sender string) (*data.TransactionsPoolNonceGaps, error)
GetTransactionsPoolCounts(shardIDParam core.OptionalUint32) (map[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)
GetTransactionsPoolCountsHandler func(shardIDParam core.OptionalUint32) (map[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
}

// GetTransactionsPoolCounts -
func (f *FacadeStub) GetTransactionsPoolCounts(shardIDParam core.OptionalUint32) (map[uint32]uint64, error) {
if f.GetTransactionsPoolCountsHandler != nil {
return f.GetTransactionsPoolCountsHandler(shardIDParam)
}

return make(map[uint32]uint64), 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
7 changes: 7 additions & 0 deletions facade/baseFacade.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,13 @@ func (pf *ProxyFacade) GetTriesStatistics(shardID uint32) (*data.TrieStatisticsA
return pf.nodeStatusProc.GetTriesStatistics(shardID)
}

// GetTransactionsPoolCounts will return the number of transactions currently in the pool.
// If shardIDParam has a value, it returns the count only for the provided shard,
// otherwise it returns the counts for every shard.
func (pf *ProxyFacade) GetTransactionsPoolCounts(shardIDParam core.OptionalUint32) (map[uint32]uint64, error) {
return pf.nodeStatusProc.GetTransactionsPoolCounts(shardIDParam)
}

// 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
2 changes: 2 additions & 0 deletions facade/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package facade
import (
"math/big"

"github.com/multiversx/mx-chain-core-go/core"
"github.com/multiversx/mx-chain-core-go/data/transaction"
"github.com/multiversx/mx-chain-core-go/data/vm"
crypto "github.com/multiversx/mx-chain-crypto-go"
Expand Down Expand Up @@ -102,6 +103,7 @@ type NodeStatusProcessor interface {
GetGasConfigs() (*data.GenericAPIResponse, error)
GetTriesStatistics(shardID uint32) (*data.TrieStatisticsAPIResponse, error)
GetEpochStartData(epoch uint32, shardID uint32) (*data.GenericAPIResponse, error)
GetTransactionsPoolCounts(shardIDParam core.OptionalUint32) (map[uint32]uint64, error)
}

// BlocksProcessor defines what a blocks processor should do
Expand Down
14 changes: 13 additions & 1 deletion facade/mock/nodeStatusProcessorStub.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package mock

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

// NodeStatusProcessorStub --
type NodeStatusProcessorStub struct {
Expand All @@ -19,6 +22,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)
GetTransactionsPoolCountsCalled func(shardIDParam core.OptionalUint32) (map[uint32]uint64, error)
}

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

// GetTransactionsPoolCounts -
func (stub *NodeStatusProcessorStub) GetTransactionsPoolCounts(shardIDParam core.OptionalUint32) (map[uint32]uint64, error) {
if stub.GetTransactionsPoolCountsCalled != nil {
return stub.GetTransactionsPoolCountsCalled(shardIDParam)
}
return make(map[uint32]uint64), nil
}
53 changes: 53 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,47 @@ func (nsp *NodeStatusProcessor) GetTriesStatistics(shardID uint32) (*data.TrieSt
return getTrieStatistics(nodeStatusResponse.Data)
}

// GetTransactionsPoolCounts will return the number of transactions currently in the pool.
// If shardIDParam has a value, it returns the count only for the provided shard,
// otherwise it returns the counts for every shard.
func (nsp *NodeStatusProcessor) GetTransactionsPoolCounts(shardIDParam core.OptionalUint32) (map[uint32]uint64, error) {
shardsIDs := make(map[uint32]struct{})
if shardIDParam.HasValue {
shardsIDs[shardIDParam.Value] = struct{}{}
} else {
var err error
shardsIDs, err = nsp.getShardsIDs()
if err != nil {
return nil, err
}
}

counts := make(map[uint32]uint64, len(shardsIDs))
for shardID := range shardsIDs {
count, err := nsp.getTransactionsPoolCountForShard(shardID)
if err != nil {
return nil, err
}

counts[shardID] = count
}

return counts, nil
}

func (nsp *NodeStatusProcessor) getTransactionsPoolCountForShard(shardID uint32) (uint64, error) {
nodeStatusResponse, err := nsp.getNodeStatusMetrics(shardID)
if err != nil {
return 0, err
}

if nodeStatusResponse.Error != "" {
return 0, errors.New(nodeStatusResponse.Error)
}

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 +476,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
Loading
Loading