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

Release v0.11.3 Metrics API #327

Merged
merged 8 commits into from
Nov 10, 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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
BUILDDIR ?= $(CURDIR)/build
LEDGER_ENABLED ?= false

APP_VER := v0.11.2
APP_VER := v0.11.3
COMMIT := $(GIT_COMMIT_HASH)
TEST_DOCKER_REPO=stratos-chain-e2e

Expand Down
77 changes: 76 additions & 1 deletion proto/stratos/pot/v1/pot.proto
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,79 @@ message TotalReward {
(gogoproto.moretags) = "yaml:\"traffic_reward\"",
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}
}

message Metrics {
string total_supply = 1 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.jsontag) = "total_supply",
(gogoproto.moretags) = "yaml:\"total_supply\""
];
string total_mining_supply = 2 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.jsontag) = "total_mining_supply",
(gogoproto.moretags) = "yaml:\"total_mining_supply\""
];
string total_mined_tokens = 3 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.jsontag) = "total_mined_tokens",
(gogoproto.moretags) = "yaml:\"total_mined_tokens\""
];
string total_resource_nodes_deposit = 4 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.jsontag) = "total_resource_nodes_deposit",
(gogoproto.moretags) = "yaml:\"total_resource_nodes_deposit\""
];
string total_bonded_delegation = 5 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.jsontag) = "total_bonded_delegation",
(gogoproto.moretags) = "yaml:\"total_bonded_delegation\""
];
string total_unbonded_delegation = 6 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.jsontag) = "total_unbonded_delegation",
(gogoproto.moretags) = "yaml:\"total_unbonded_delegation\""
];
string total_unbonding_delegation = 7 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.jsontag) = "total_unbonding_delegation",
(gogoproto.moretags) = "yaml:\"total_unbonding_delegation\""
];
string circulation_supply = 8 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.jsontag) = "circulation_supply",
(gogoproto.moretags) = "yaml:\"circulation_supply\""
];
string total_mining_reward = 9 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.jsontag) = "total_mining_reward",
(gogoproto.moretags) = "yaml:\"total_mining_reward\""
];
string chain_mining_reward = 10 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.jsontag) = "chain_mining_reward",
(gogoproto.moretags) = "yaml:\"chain_mining_reward\""
];
string resource_mining_reward = 11 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.jsontag) = "resource_mining_reward",
(gogoproto.moretags) = "yaml:\"resource_mining_reward\""
];
string meta_mining_reward = 12 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.jsontag) = "meta_mining_reward",
(gogoproto.moretags) = "yaml:\"meta_mining_reward\""
];
}
11 changes: 11 additions & 0 deletions proto/stratos/pot/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ service Query {
rpc TotalRewardByEpoch(QueryTotalRewardByEpochRequest) returns (QueryTotalRewardByEpochResponse) {
option (google.api.http).get = "/stratos/pot/v1/total-reward/{epoch}";
}

rpc Metrics(QueryMetricsRequest) returns (QueryMetricsResponse) {
option (google.api.http).get = "/stratos/pot/v1/metrics";
}
}

// QueryVolumeReportRequest is request type for the Query/VolumeReport RPC method
Expand Down Expand Up @@ -158,3 +162,10 @@ message QueryTotalRewardByEpochResponse {
];
}

message QueryMetricsRequest {}

message QueryMetricsResponse {
Metrics metrics = 1 [
(gogoproto.nullable) = false
];
}
139 changes: 96 additions & 43 deletions x/evm/pool/tx_pool.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package pool

import (
"errors"
"fmt"
"math/big"
"sync"
Expand Down Expand Up @@ -34,33 +35,29 @@ const (
)

var (
initInterval = 5 * time.Second // Time interval to prepare some after tendermint initializations
evictionInterval = time.Minute // Time interval to check for evictable transactions
statsReportInterval = 8 * time.Second // Time interval to report transaction pool stats
processQueueInterval = 5 * time.Second // Time interval to process queue transactions in case if their ready and move to pending stage
processPendingInterval = 5 * time.Second // Time interval of pending tx broadcating to a tm mempool
)

type txpoolResetRequest struct {
oldHead, newHead *types.Header
}

// TxPool contains all currently known transactions. Transactions
// enter the pool when they are received from the local network or submitted
// locally. They exit the pool when they are included in the blockchain.
type TxPool struct {
config core.TxPoolConfig
srvCfg config.Config
logger log.Logger
clientCtx client.Context
evmCtx *evm.Context
signer types.Signer
mempool mempl.Mempool
mu sync.RWMutex

evmkeeper *evmkeeper.Keeper // Active keeper to get current state
pendingNonces *txNoncer // Pending state tracking virtual nonces
currentMaxGas uint64 // Current gas limit for transaction caps
config core.TxPoolConfig
srvCfg config.Config
logger log.Logger
clientCtx client.Context
evmCtx *evm.Context
signerCache types.Signer // should be loaded in dynamic as potentially will not exist during chain initialization
mempool mempl.Mempool
mu sync.RWMutex
imu sync.Mutex // only for initializations

evmkeeper *evmkeeper.Keeper // Active keeper to get current state
pendingNonces *txNoncer // Pending state tracking virtual nonces
maxGasLimitCache uint64 // Current gas limit for transaction caps, only for cache

beats map[common.Address]time.Time // Last heartbeat from each known account

Expand All @@ -69,32 +66,34 @@ type TxPool struct {
all *txLookup // All transactions to allow lookups
}

// catchInitPanic used only in methods which are dependent from the store and potentially could be down
// due to not initialized chain
func catchInitPanic(err *error) {
if errRec := recover(); errRec != nil {
log.Warn("storage still not initialized, value could not be loaded")
*err = errors.New("chain not started yet")
}
}

// NewTxPool creates a new transaction pool to gather, sort and filter inbound
// transactions from the network.
func NewTxPool(config core.TxPoolConfig, srvCfg config.Config, clientCtx client.Context, mempool mempl.Mempool, evmkeeper *evmkeeper.Keeper, evmCtx *evm.Context) (*TxPool, error) {
sdkCtx := evmCtx.GetSdkContext()
params := evmkeeper.GetParams(sdkCtx)
pool := &TxPool{
config: config,
srvCfg: srvCfg,
clientCtx: clientCtx,
evmCtx: evmCtx,
signer: types.LatestSignerForChainID(params.ChainConfig.ChainID.BigInt()),
mempool: mempool,
evmkeeper: evmkeeper,
beats: make(map[common.Address]time.Time),
pending: make(map[common.Address]*txList),
queue: make(map[common.Address]*txList),
all: newTxLookup(),
config: config,
srvCfg: srvCfg,
clientCtx: clientCtx,
evmCtx: evmCtx,
mempool: mempool,
evmkeeper: evmkeeper,
signerCache: nil,
maxGasLimitCache: 0,
beats: make(map[common.Address]time.Time),
pending: make(map[common.Address]*txList),
queue: make(map[common.Address]*txList),
all: newTxLookup(),
}
pool.pendingNonces = newTxNoncer(pool.evmCtx, pool.evmkeeper)

gasLimit, err := evmtypes.BlockMaxGasFromConsensusParams(nil)
if err != nil {
return nil, fmt.Errorf("failed to get tx pool current max gas: %w (possible DB not started?)", err)
}
pool.currentMaxGas = uint64(gasLimit)

go pool.eventLoop()

return pool, nil
Expand Down Expand Up @@ -161,6 +160,42 @@ func (pool *TxPool) eventLoop() {
}
}

// getSigner return signer for existing chain id
func (pool *TxPool) getSigner() (_ types.Signer, err error) {
defer catchInitPanic(&err)

if pool.signerCache == nil {
pool.imu.Lock()
defer pool.imu.Unlock()

if pool.signerCache == nil {
sdkCtx := pool.evmCtx.GetSdkContext()
params := pool.evmkeeper.GetParams(sdkCtx)
pool.signerCache = types.LatestSignerForChainID(params.ChainConfig.ChainID.BigInt())
}
}
return pool.signerCache, nil
}

// getMaxGasLimit return current gas limit for transaction caps
func (pool *TxPool) getMaxGasLimit() (_ uint64, err error) {
defer catchInitPanic(&err)

if pool.maxGasLimitCache == 0 {
pool.imu.Lock()
defer pool.imu.Unlock()

if pool.maxGasLimitCache == 0 {
gasLimit, err := evmtypes.BlockMaxGasFromConsensusParams(nil)
if err != nil {
return 0, fmt.Errorf("failed to get tx pool current max gas: %w (possible DB not started?)", err)
}
pool.maxGasLimitCache = uint64(gasLimit)
}
}
return pool.maxGasLimitCache, nil
}

// Get returns a transaction if it is contained in the pool and nil otherwise.
func (pool *TxPool) Get(hash common.Hash) *types.Transaction {
return pool.all.Get(hash)
Expand Down Expand Up @@ -220,6 +255,16 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
log.Trace("Validating tx", "hash", tx.Hash())
sdkCtx := pool.evmCtx.GetSdkContext()

currentMaxGas, err := pool.getMaxGasLimit()
if err != nil {
return err
}

signer, err := pool.getSigner()
if err != nil {
return err
}

// Accept only legacy transactions until EIP-2718/2930 activates.
if !tx.Protected() {
return core.ErrTxTypeNotSupported
Expand All @@ -238,7 +283,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
return core.ErrNegativeValue
}
// Ensure the transaction doesn't exceed the current block limit gas.
if pool.currentMaxGas < tx.Gas() {
if currentMaxGas < tx.Gas() {
return core.ErrGasLimit
}
// Sanity check for extremely large numbers
Expand All @@ -253,7 +298,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
return core.ErrTipAboveFeeCap
}
// Make sure the transaction is signed properly.
from, err := types.Sender(pool.signer, tx)
from, err := types.Sender(signer, tx)
if err != nil {
return core.ErrInvalidSender
}
Expand Down Expand Up @@ -327,8 +372,10 @@ func (pool *TxPool) Add(tx *types.Transaction) (replaced bool, err error) {
if err != nil {
return false, err
}

signer, _ := pool.getSigner()
// Try to replace an existing transaction in the pending pool
from, _ := types.Sender(pool.signer, tx) // already validated
from, _ := types.Sender(signer, tx) // already validated
if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
log.Trace("Tx overlap", "hash", tx.Hash())
// Nonce already pending, check if required price bump is met
Expand All @@ -354,7 +401,9 @@ func (pool *TxPool) Add(tx *types.Transaction) (replaced bool, err error) {
// Note, this method assumes the pool lock is held!
func (pool *TxPool) enqueueTx(tx *types.Transaction, addAll bool) (bool, error) {
log.Trace("Tx enqeue", "hash", tx.Hash())
from, _ := types.Sender(pool.signer, tx) // already validated

signer, _ := pool.getSigner()
from, _ := types.Sender(signer, tx) // already validated
if pool.queue[from] == nil {
pool.queue[from] = newTxList(false)
}
Expand Down Expand Up @@ -389,7 +438,8 @@ func (pool *TxPool) removeTx(hash common.Hash) {
log.Error("Tx not found during removal", "hash", hash)
return
}
addr, _ := types.Sender(pool.signer, tx) // already validated during insertion
signer, _ := pool.getSigner()
addr, _ := types.Sender(signer, tx) // already validated during insertion

// Remove it from the list of known transactions
pool.all.Remove(hash)
Expand Down Expand Up @@ -477,6 +527,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
var promoted []*types.Transaction

sdkCtx := pool.evmCtx.GetSdkContext()
currentMaxGas, _ := pool.getMaxGasLimit()

// Iterate over all accounts and promote any executable transactions
for _, addr := range accounts {
Expand All @@ -492,7 +543,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
}
log.Trace("Removed old queued transactions", "count", len(forwards))
// Drop all transactions that are too costly (low balance or out of gas)
drops, _ := list.Filter(pool.evmkeeper.GetBalance(sdkCtx, addr), pool.currentMaxGas)
drops, _ := list.Filter(pool.evmkeeper.GetBalance(sdkCtx, addr), currentMaxGas)
for _, tx := range drops {
hash := tx.Hash()
pool.all.Remove(hash)
Expand Down Expand Up @@ -526,6 +577,8 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
// to trigger a re-heap is this function
func (pool *TxPool) demoteUnexecutables() {
sdkCtx := pool.evmCtx.GetSdkContext()
currentMaxGas, _ := pool.getMaxGasLimit()

// Iterate over all accounts and demote any non-executable transactions
for addr, list := range pool.pending {
nonce := pool.evmkeeper.GetNonce(sdkCtx, addr)
Expand All @@ -538,7 +591,7 @@ func (pool *TxPool) demoteUnexecutables() {
log.Trace("Removed old pending transaction", "hash", hash)
}
// Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later
drops, invalids := list.Filter(pool.evmkeeper.GetBalance(sdkCtx, addr), pool.currentMaxGas)
drops, invalids := list.Filter(pool.evmkeeper.GetBalance(sdkCtx, addr), currentMaxGas)
for _, tx := range drops {
hash := tx.Hash()
log.Trace("Removed unpayable pending transaction", "hash", hash)
Expand Down
19 changes: 19 additions & 0 deletions x/pot/client/rest/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func registerQueryRoutes(clientCtx client.Context, r *mux.Router) {
r.HandleFunc("/pot/total-mined-token", getTotalMinedTokenHandlerFn(clientCtx, types.QueryTotalMinedToken)).Methods("GET")
r.HandleFunc("/pot/circulation-supply", getCirculationSupplyHandlerFn(clientCtx, types.QueryCirculationSupply)).Methods("GET")
r.HandleFunc("/pot/total-reward/{epoch}", getTotalRewardByEpochHandlerFn(clientCtx, types.QueryTotalRewardByEpoch)).Methods("GET")
r.HandleFunc("/pot/metrics", getMetricsHandlerFn(clientCtx, types.QueryMetrics)).Methods("GET")
}

// GET request handler to query params of POT module
Expand Down Expand Up @@ -259,3 +260,21 @@ func getTotalRewardByEpochHandlerFn(clientCtx client.Context, queryPath string)
rest.PostProcessResponse(w, cliCtx, res)
}
}

func getMetricsHandlerFn(clientCtx client.Context, queryPath string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r)
if !ok {
return
}

route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, queryPath)
res, height, err := cliCtx.Query(route)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}
}
Loading