Skip to content

Commit

Permalink
EVM-850 Validator rootchain balance metrics (#1941)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Stefan Negovanović <stefan@ethernal.tech>
  • Loading branch information
igorcrevar and Stefan-Ethernal committed Sep 29, 2023
1 parent 1bcf30b commit 6079322
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 23 deletions.
7 changes: 7 additions & 0 deletions command/server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ type Config struct {

ConcurrentRequestsDebug uint64 `json:"concurrent_requests_debug" yaml:"concurrent_requests_debug"`
WebSocketReadLimit uint64 `json:"web_socket_read_limit" yaml:"web_socket_read_limit"`

MetricsInterval time.Duration `json:"metrics_interval" yaml:"metrics_interval"`
}

// Telemetry holds the config details for metric services.
Expand Down Expand Up @@ -96,6 +98,10 @@ const (
// DefaultRelayerTrackerPollInterval specifies time interval after which relayer node's event tracker
// polls child chain to get the latest block
DefaultRelayerTrackerPollInterval time.Duration = time.Second

// DefaultMetricsInterval specifies the time interval after which Prometheus metrics will be generated.
// A value of 0 means the metrics are disabled.
DefaultMetricsInterval time.Duration = time.Second * 8
)

// DefaultConfig returns the default server configuration
Expand Down Expand Up @@ -136,6 +142,7 @@ func DefaultConfig() *Config {
ConcurrentRequestsDebug: DefaultConcurrentRequestsDebug,
WebSocketReadLimit: DefaultWebSocketReadLimit,
RelayerTrackerPollInterval: DefaultRelayerTrackerPollInterval,
MetricsInterval: DefaultMetricsInterval,
}
}

Expand Down
3 changes: 3 additions & 0 deletions command/server/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const (
webSocketReadLimitFlag = "websocket-read-limit"

relayerTrackerPollIntervalFlag = "relayer-poll-interval"

metricsIntervalFlag = "metrics-interval"
)

// Flags that are deprecated, but need to be preserved for
Expand Down Expand Up @@ -190,5 +192,6 @@ func (p *serverParams) generateConfig() *server.Config {
Relayer: p.relayer,
NumBlockConfirmations: p.rawConfig.NumBlockConfirmations,
RelayerTrackerPollInterval: p.rawConfig.RelayerTrackerPollInterval,
MetricsInterval: p.rawConfig.MetricsInterval,
}
}
7 changes: 7 additions & 0 deletions command/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,13 @@ func setFlags(cmd *cobra.Command) {
"interval (number of seconds) at which relayer's tracker polls for latest block at childchain",
)

cmd.Flags().DurationVar(
&params.rawConfig.MetricsInterval,
metricsIntervalFlag,
defaultConfig.MetricsInterval,
"the interval (in seconds) at which special metrics are generated. a value of zero means the metrics are disabled",
)

setLegacyFlags(cmd)

setDevFlags(cmd)
Expand Down
2 changes: 2 additions & 0 deletions consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package consensus
import (
"context"
"log"
"time"

"github.com/0xPolygon/polygon-edge/blockchain"
"github.com/0xPolygon/polygon-edge/chain"
Expand Down Expand Up @@ -78,6 +79,7 @@ type Params struct {
BlockTime uint64

NumBlockConfirmations uint64
MetricsInterval time.Duration
}

// Factory is the factory function to create a discovery consensus
Expand Down
29 changes: 12 additions & 17 deletions consensus/polybft/checkpoint_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/0xPolygon/polygon-edge/merkle-tree"
"github.com/0xPolygon/polygon-edge/txrelayer"
"github.com/0xPolygon/polygon-edge/types"
metrics "github.com/armon/go-metrics"
hclog "github.com/hashicorp/go-hclog"
"github.com/umbracle/ethgo"
)
Expand Down Expand Up @@ -98,33 +97,31 @@ func newCheckpointManager(key ethgo.Key, checkpointOffset uint64,
}
}

// getLatestCheckpointBlock queries CheckpointManager smart contract and retrieves latest checkpoint block number
func (c *checkpointManager) getLatestCheckpointBlock() (uint64, error) {
checkpointBlockNumMethodEncoded, err := currentCheckpointBlockNumMethod.Encode([]interface{}{})
// getCurrentCheckpointBlock queries CheckpointManager smart contract and retrieves the current checkpoint block number
func getCurrentCheckpointBlock(relayer txrelayer.TxRelayer, checkpointManagerAddr types.Address) (uint64, error) {
checkpointBlockNumInput, err := currentCheckpointBlockNumMethod.Encode([]interface{}{})
if err != nil {
return 0, fmt.Errorf("failed to encode currentCheckpointId function parameters: %w", err)
return 0, fmt.Errorf("failed to encode currentCheckpointBlockNumber function parameters: %w", err)
}

latestCheckpointBlockRaw, err := c.rootChainRelayer.Call(
c.key.Address(),
ethgo.Address(c.checkpointManagerAddr),
checkpointBlockNumMethodEncoded)
currentCheckpointBlockRaw, err := relayer.Call(ethgo.ZeroAddress, ethgo.Address(checkpointManagerAddr),
checkpointBlockNumInput)
if err != nil {
return 0, fmt.Errorf("failed to invoke currentCheckpointId function on the rootchain: %w", err)
return 0, fmt.Errorf("failed to invoke currentCheckpointBlockNumber function on the rootchain: %w", err)
}

latestCheckpointBlockNum, err := strconv.ParseUint(latestCheckpointBlockRaw, 0, 64)
currentCheckpointBlock, err := strconv.ParseUint(currentCheckpointBlockRaw, 0, 64)
if err != nil {
return 0, fmt.Errorf("failed to convert current checkpoint id '%s' to number: %w",
latestCheckpointBlockRaw, err)
return 0, fmt.Errorf("failed to convert current checkpoint block number '%s' to number: %w",
currentCheckpointBlockRaw, err)
}

return latestCheckpointBlockNum, nil
return currentCheckpointBlock, nil
}

// submitCheckpoint sends a transaction with checkpoint data to the rootchain
func (c *checkpointManager) submitCheckpoint(latestHeader *types.Header, isEndOfEpoch bool) error {
lastCheckpointBlockNumber, err := c.getLatestCheckpointBlock()
lastCheckpointBlockNumber, err := getCurrentCheckpointBlock(c.rootChainRelayer, c.checkpointManagerAddr)
if err != nil {
return err
}
Expand Down Expand Up @@ -234,8 +231,6 @@ func (c *checkpointManager) encodeAndSendCheckpoint(header *types.Header, extra
return fmt.Errorf("checkpoint submission transaction failed for block %d", header.Number)
}

// update checkpoint block number metrics
metrics.SetGauge([]string{"bridge", "checkpoint_block_number"}, float32(header.Number))
c.logger.Debug("send checkpoint txn success", "block number", header.Number, "gasUsed", receipt.GasUsed)

return nil
Expand Down
7 changes: 4 additions & 3 deletions consensus/polybft/checkpoint_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,13 @@ func TestCheckpointManager_getCurrentCheckpointID(t *testing.T) {
name: "Rootchain call returns an error",
checkpointID: "",
returnError: errors.New("internal error"),
errSubstring: "failed to invoke currentCheckpointId function on the rootchain",
errSubstring: "failed to invoke currentCheckpointBlockNumber function on the rootchain",
},
{
name: "Failed to parse return value from rootchain",
checkpointID: "Hello World!",
returnError: error(nil),
errSubstring: "failed to convert current checkpoint id",
errSubstring: "failed to convert current checkpoint block number",
},
}

Expand All @@ -222,7 +222,8 @@ func TestCheckpointManager_getCurrentCheckpointID(t *testing.T) {
key: acc.Ecdsa,
logger: hclog.NewNullLogger(),
}
actualCheckpointID, err := checkpointMgr.getLatestCheckpointBlock()
actualCheckpointID, err := getCurrentCheckpointBlock(checkpointMgr.rootChainRelayer,
checkpointMgr.checkpointManagerAddr)
if c.errSubstring == "" {
expectedCheckpointID, err := strconv.ParseUint(c.checkpointID, 0, 64)
require.NoError(t, err)
Expand Down
11 changes: 8 additions & 3 deletions consensus/polybft/polybft.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"path/filepath"
"time"

"github.com/hashicorp/go-hclog"

"github.com/0xPolygon/polygon-edge/chain"
"github.com/0xPolygon/polygon-edge/consensus"
"github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi"
Expand All @@ -24,7 +26,6 @@ import (
"github.com/0xPolygon/polygon-edge/state"
"github.com/0xPolygon/polygon-edge/syncer"
"github.com/0xPolygon/polygon-edge/types"
"github.com/hashicorp/go-hclog"
)

const (
Expand Down Expand Up @@ -156,7 +157,7 @@ func GenesisPostHookFactory(config *chain.Chain, engineName string) func(txn *st
proxyAddrMapping[contracts.RewardTokenContract] = contracts.RewardTokenContractV1
}

if err = setUpProxies(transition, polyBFTConfig.ProxyContractsAdmin, proxyAddrMapping); err != nil {
if err = initProxies(transition, polyBFTConfig.ProxyContractsAdmin, proxyAddrMapping); err != nil {
return err
}

Expand Down Expand Up @@ -527,6 +528,9 @@ func (p *Polybft) Start() error {
// start state DB process
go p.state.startStatsReleasing()

// polybft rootchain metrics
go p.publishRootchainMetrics(p.logger.Named("rootchain_metrics"))

return nil
}

Expand Down Expand Up @@ -781,7 +785,8 @@ func (p *Polybft) FilterExtra(extra []byte) ([]byte, error) {
return GetIbftExtraClean(extra)
}

func setUpProxies(transition *state.Transition, admin types.Address,
// initProxies initializes proxy contracts, that allow upgradeability of contracts implementation
func initProxies(transition *state.Transition, admin types.Address,
proxyToImplMap map[types.Address]types.Address) error {
for proxyAddress, implAddress := range proxyToImplMap {
protectSetupProxyFn := &contractsapi.ProtectSetUpProxyGenesisProxyFn{Initiator: contracts.SystemCaller}
Expand Down
54 changes: 54 additions & 0 deletions consensus/polybft/state_stats.go → consensus/polybft/stats.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package polybft

import (
"math/big"
"time"

"github.com/0xPolygon/polygon-edge/txrelayer"
"github.com/armon/go-metrics"
"github.com/hashicorp/go-hclog"
"github.com/prometheus/client_golang/prometheus"
"github.com/umbracle/ethgo"
)

// startStatsReleasing starts the process that releases BoltDB stats into prometheus periodically.
Expand Down Expand Up @@ -154,3 +158,53 @@ func (s *State) startStatsReleasing() {
prev = stats
}
}

// publishRootchainMetrics publishes rootchain related metrics
func (p *Polybft) publishRootchainMetrics(logger hclog.Logger) {
interval := p.config.MetricsInterval
validatorAddr := p.key.Address()
bridgeCfg := p.consensusConfig.Bridge

// zero means metrics are disabled
if interval <= 0 {
return
}

relayer, err := txrelayer.NewTxRelayer(txrelayer.WithIPAddress(bridgeCfg.JSONRPCEndpoint))
if err != nil {
logger.Error("failed to connect to the rootchain node", "err", err, "JSON RPC", bridgeCfg.JSONRPCEndpoint)

return
}

gweiPerWei := new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(9), nil)) // 10^9

ticker := time.NewTicker(interval)
defer ticker.Stop()

for {
select {
case <-p.closeCh:
return
case <-ticker.C:
// rootchain validator balance
balance, err := relayer.Client().Eth().GetBalance(p.key.Address(), ethgo.Latest)
if err != nil {
logger.Error("failed to query eth_getBalance", "err", err)
} else {
balanceInGwei := new(big.Float).Quo(new(big.Float).SetInt(balance), gweiPerWei)
balanceInGweiFloat, _ := balanceInGwei.Float32()

metrics.SetGauge([]string{"bridge", "validator_root_balance_gwei", validatorAddr.String()}, balanceInGweiFloat)
}

// rootchain current checkpoint block
checkpointBlock, err := getCurrentCheckpointBlock(relayer, bridgeCfg.CheckpointManagerAddr)
if err != nil {
logger.Error("failed to query latest checkpoint block", "err", err)
} else {
metrics.SetGauge([]string{"bridge", "checkpoint_block_number"}, float32(checkpointBlock))
}
}
}
}
1 change: 1 addition & 0 deletions server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type Config struct {

NumBlockConfirmations uint64
RelayerTrackerPollInterval time.Duration
MetricsInterval time.Duration
}

// Telemetry holds the config details for metric services
Expand Down
1 change: 1 addition & 0 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@ func (s *Server) setupConsensus() error {
SecretsManager: s.secretsManager,
BlockTime: uint64(blockTime.Seconds()),
NumBlockConfirmations: s.config.NumBlockConfirmations,
MetricsInterval: s.config.MetricsInterval,
},
)

Expand Down

0 comments on commit 6079322

Please sign in to comment.