counted block size by vote vs non-vote tx

This commit is contained in:
Matt Johnstone 2024-10-26 14:20:09 +02:00
parent e46d20d68e
commit 6fa9afb6aa
No known key found for this signature in database
GPG Key ID: BE985FBB9BE7D3BB
4 changed files with 61 additions and 22 deletions

View File

@ -13,20 +13,24 @@ import (
) )
const ( const (
SkipStatusLabel = "status" SkipStatusLabel = "status"
StateLabel = "state" StateLabel = "state"
NodekeyLabel = "nodekey" NodekeyLabel = "nodekey"
VotekeyLabel = "votekey" VotekeyLabel = "votekey"
VersionLabel = "version" VersionLabel = "version"
AddressLabel = "address" AddressLabel = "address"
EpochLabel = "epoch" EpochLabel = "epoch"
IdentityLabel = "identity" IdentityLabel = "identity"
TransactionTypeLabel = "transaction_type"
StatusSkipped = "skipped" StatusSkipped = "skipped"
StatusValid = "valid" StatusValid = "valid"
StateCurrent = "current" StateCurrent = "current"
StateDelinquent = "delinquent" StateDelinquent = "delinquent"
TransactionTypeVote = "vote"
TransactionTypeTotal = "total"
) )
type SolanaCollector struct { type SolanaCollector struct {

View File

@ -42,7 +42,7 @@ type SlotWatcher struct {
InflationRewardsMetric *prometheus.GaugeVec InflationRewardsMetric *prometheus.GaugeVec
FeeRewardsMetric *prometheus.CounterVec FeeRewardsMetric *prometheus.CounterVec
BlockSizeMetric *prometheus.GaugeVec BlockSizeMetric *prometheus.GaugeVec
BlockHeight *prometheus.GaugeVec BlockHeightMetric *prometheus.GaugeVec
} }
func NewSlotWatcher(client rpc.Provider, config *ExporterConfig) *SlotWatcher { func NewSlotWatcher(client rpc.Provider, config *ExporterConfig) *SlotWatcher {
@ -111,9 +111,9 @@ func NewSlotWatcher(client rpc.Provider, config *ExporterConfig) *SlotWatcher {
Name: "solana_block_size", Name: "solana_block_size",
Help: fmt.Sprintf("Number of transactions per block, grouped by %s", NodekeyLabel), Help: fmt.Sprintf("Number of transactions per block, grouped by %s", NodekeyLabel),
}, },
[]string{NodekeyLabel}, []string{NodekeyLabel, TransactionTypeLabel},
), ),
BlockHeight: prometheus.NewGaugeVec( BlockHeightMetric: prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "solana_block_height", Name: "solana_block_height",
Help: fmt.Sprintf("The current block height of the node, grouped by %s", IdentityLabel), Help: fmt.Sprintf("The current block height of the node, grouped by %s", IdentityLabel),
@ -134,7 +134,7 @@ func NewSlotWatcher(client rpc.Provider, config *ExporterConfig) *SlotWatcher {
watcher.InflationRewardsMetric, watcher.InflationRewardsMetric,
watcher.FeeRewardsMetric, watcher.FeeRewardsMetric,
watcher.BlockSizeMetric, watcher.BlockSizeMetric,
watcher.BlockHeight, watcher.BlockHeightMetric,
} { } {
if err := prometheus.Register(collector); err != nil { if err := prometheus.Register(collector); err != nil {
var ( var (
@ -179,7 +179,7 @@ func (c *SlotWatcher) WatchSlots(ctx context.Context) {
c.TotalTransactionsMetric.Set(float64(epochInfo.TransactionCount)) c.TotalTransactionsMetric.Set(float64(epochInfo.TransactionCount))
c.SlotHeightMetric.Set(float64(epochInfo.AbsoluteSlot)) c.SlotHeightMetric.Set(float64(epochInfo.AbsoluteSlot))
c.BlockHeight.WithLabelValues(c.config.Identity).Set(float64(epochInfo.BlockHeight)) c.BlockHeightMetric.WithLabelValues(c.config.Identity).Set(float64(epochInfo.BlockHeight))
// if we get here, then the tracking numbers are set, so this is a "normal" run. // if we get here, then the tracking numbers are set, so this is a "normal" run.
// start by checking if we have progressed since last run: // start by checking if we have progressed since last run:
@ -363,13 +363,11 @@ func (c *SlotWatcher) fetchAndEmitBlockInfos(ctx context.Context, endSlot int64)
// fetchAndEmitSingleBlockInfo fetches and emits the fee reward + block size for a single block. // fetchAndEmitSingleBlockInfo fetches and emits the fee reward + block size for a single block.
func (c *SlotWatcher) fetchAndEmitSingleBlockInfo( func (c *SlotWatcher) fetchAndEmitSingleBlockInfo(
ctx context.Context, identity string, epoch int64, slot int64, ctx context.Context, nodekey string, epoch int64, slot int64,
) error { ) error {
var transactionDetails string transactionDetails := "none"
if c.config.MonitorBlockSizes { if c.config.MonitorBlockSizes {
transactionDetails = "accounts" transactionDetails = "full"
} else {
transactionDetails = "none"
} }
block, err := c.client.GetBlock(ctx, rpc.CommitmentConfirmed, slot, transactionDetails) block, err := c.client.GetBlock(ctx, rpc.CommitmentConfirmed, slot, transactionDetails)
if err != nil { if err != nil {
@ -388,19 +386,25 @@ func (c *SlotWatcher) fetchAndEmitSingleBlockInfo(
if reward.RewardType == "fee" { if reward.RewardType == "fee" {
// make sure we haven't made a logic issue or something: // make sure we haven't made a logic issue or something:
assertf( assertf(
reward.Pubkey == identity, reward.Pubkey == nodekey,
"fetching fee reward for %v but got fee reward for %v", "fetching fee reward for %v but got fee reward for %v",
identity, nodekey,
reward.Pubkey, reward.Pubkey,
) )
amount := float64(reward.Lamports) / float64(rpc.LamportsInSol) amount := float64(reward.Lamports) / float64(rpc.LamportsInSol)
c.FeeRewardsMetric.WithLabelValues(identity, toString(epoch)).Add(amount) c.FeeRewardsMetric.WithLabelValues(nodekey, toString(epoch)).Add(amount)
} }
} }
// track block size: // track block size:
if c.config.MonitorBlockSizes { if c.config.MonitorBlockSizes {
c.BlockSizeMetric.WithLabelValues(identity).Set(float64(len(block.Transactions))) c.BlockSizeMetric.WithLabelValues(nodekey, TransactionTypeTotal).Set(float64(len(block.Transactions)))
// now count and emit votes:
voteCount, err := CountVoteTransactions(block)
if err != nil {
return err
}
c.BlockHeightMetric.WithLabelValues(nodekey, TransactionTypeVote).Set(float64(voteCount))
} }
return nil return nil

View File

@ -2,12 +2,15 @@ package main
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"github.com/asymmetric-research/solana_exporter/pkg/rpc" "github.com/asymmetric-research/solana_exporter/pkg/rpc"
"github.com/asymmetric-research/solana_exporter/pkg/slog" "github.com/asymmetric-research/solana_exporter/pkg/slog"
"slices" "slices"
) )
const VoteProgram = "Vote111111111111111111111111111111111111111"
func assertf(condition bool, format string, args ...any) { func assertf(condition bool, format string, args ...any) {
logger := slog.Get() logger := slog.Get()
if !condition { if !condition {
@ -121,3 +124,22 @@ func GetEpochBounds(info *rpc.EpochInfo) (int64, int64) {
firstSlot := info.AbsoluteSlot - info.SlotIndex firstSlot := info.AbsoluteSlot - info.SlotIndex
return firstSlot, firstSlot + info.SlotsInEpoch - 1 return firstSlot, firstSlot + info.SlotsInEpoch - 1
} }
func CountVoteTransactions(block *rpc.Block) (int, error) {
txData, err := json.Marshal(block.Transactions)
if err != nil {
return 0, fmt.Errorf("failed to marshal transactions: %w", err)
}
var transactions []rpc.FullTransaction
if err := json.Unmarshal(txData, &transactions); err != nil {
return 0, fmt.Errorf("failed to unmarshal transactions: %w", err)
}
voteCount := 0
for _, tx := range transactions {
if slices.Contains(tx.Transaction.Message.AccountKeys, VoteProgram) {
voteCount++
}
}
return voteCount, nil
}

View File

@ -98,9 +98,18 @@ type (
RewardType string `json:"rewardType"` RewardType string `json:"rewardType"`
Commission uint8 `json:"commission"` Commission uint8 `json:"commission"`
} }
Identity struct { Identity struct {
Identity string `json:"identity"` Identity string `json:"identity"`
} }
FullTransaction struct {
Transaction struct {
Message struct {
AccountKeys []string `json:"accountKeys"`
} `json:"message"`
} `json:"transaction"`
}
) )
func (e *RPCError) Error() string { func (e *RPCError) Error() string {