From 6fa9afb6aa80e553a24a7d80e38b3b9e87aa3f40 Mon Sep 17 00:00:00 2001 From: Matt Johnstone Date: Sat, 26 Oct 2024 14:20:09 +0200 Subject: [PATCH] counted block size by vote vs non-vote tx --- cmd/solana_exporter/exporter.go | 20 ++++++++++++-------- cmd/solana_exporter/slots.go | 32 ++++++++++++++++++-------------- cmd/solana_exporter/utils.go | 22 ++++++++++++++++++++++ pkg/rpc/responses.go | 9 +++++++++ 4 files changed, 61 insertions(+), 22 deletions(-) diff --git a/cmd/solana_exporter/exporter.go b/cmd/solana_exporter/exporter.go index d260be8..54d7636 100644 --- a/cmd/solana_exporter/exporter.go +++ b/cmd/solana_exporter/exporter.go @@ -13,20 +13,24 @@ import ( ) const ( - SkipStatusLabel = "status" - StateLabel = "state" - NodekeyLabel = "nodekey" - VotekeyLabel = "votekey" - VersionLabel = "version" - AddressLabel = "address" - EpochLabel = "epoch" - IdentityLabel = "identity" + SkipStatusLabel = "status" + StateLabel = "state" + NodekeyLabel = "nodekey" + VotekeyLabel = "votekey" + VersionLabel = "version" + AddressLabel = "address" + EpochLabel = "epoch" + IdentityLabel = "identity" + TransactionTypeLabel = "transaction_type" StatusSkipped = "skipped" StatusValid = "valid" StateCurrent = "current" StateDelinquent = "delinquent" + + TransactionTypeVote = "vote" + TransactionTypeTotal = "total" ) type SolanaCollector struct { diff --git a/cmd/solana_exporter/slots.go b/cmd/solana_exporter/slots.go index b3ec2d8..80fab67 100644 --- a/cmd/solana_exporter/slots.go +++ b/cmd/solana_exporter/slots.go @@ -42,7 +42,7 @@ type SlotWatcher struct { InflationRewardsMetric *prometheus.GaugeVec FeeRewardsMetric *prometheus.CounterVec BlockSizeMetric *prometheus.GaugeVec - BlockHeight *prometheus.GaugeVec + BlockHeightMetric *prometheus.GaugeVec } func NewSlotWatcher(client rpc.Provider, config *ExporterConfig) *SlotWatcher { @@ -111,9 +111,9 @@ func NewSlotWatcher(client rpc.Provider, config *ExporterConfig) *SlotWatcher { Name: "solana_block_size", 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{ Name: "solana_block_height", 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.FeeRewardsMetric, watcher.BlockSizeMetric, - watcher.BlockHeight, + watcher.BlockHeightMetric, } { if err := prometheus.Register(collector); err != nil { var ( @@ -179,7 +179,7 @@ func (c *SlotWatcher) WatchSlots(ctx context.Context) { c.TotalTransactionsMetric.Set(float64(epochInfo.TransactionCount)) 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. // 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. func (c *SlotWatcher) fetchAndEmitSingleBlockInfo( - ctx context.Context, identity string, epoch int64, slot int64, + ctx context.Context, nodekey string, epoch int64, slot int64, ) error { - var transactionDetails string + transactionDetails := "none" if c.config.MonitorBlockSizes { - transactionDetails = "accounts" - } else { - transactionDetails = "none" + transactionDetails = "full" } block, err := c.client.GetBlock(ctx, rpc.CommitmentConfirmed, slot, transactionDetails) if err != nil { @@ -388,19 +386,25 @@ func (c *SlotWatcher) fetchAndEmitSingleBlockInfo( if reward.RewardType == "fee" { // make sure we haven't made a logic issue or something: assertf( - reward.Pubkey == identity, + reward.Pubkey == nodekey, "fetching fee reward for %v but got fee reward for %v", - identity, + nodekey, reward.Pubkey, ) 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: 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 diff --git a/cmd/solana_exporter/utils.go b/cmd/solana_exporter/utils.go index d830ab5..335bcda 100644 --- a/cmd/solana_exporter/utils.go +++ b/cmd/solana_exporter/utils.go @@ -2,12 +2,15 @@ package main import ( "context" + "encoding/json" "fmt" "github.com/asymmetric-research/solana_exporter/pkg/rpc" "github.com/asymmetric-research/solana_exporter/pkg/slog" "slices" ) +const VoteProgram = "Vote111111111111111111111111111111111111111" + func assertf(condition bool, format string, args ...any) { logger := slog.Get() if !condition { @@ -121,3 +124,22 @@ func GetEpochBounds(info *rpc.EpochInfo) (int64, int64) { firstSlot := info.AbsoluteSlot - info.SlotIndex 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 +} diff --git a/pkg/rpc/responses.go b/pkg/rpc/responses.go index 6a23180..30d800f 100644 --- a/pkg/rpc/responses.go +++ b/pkg/rpc/responses.go @@ -98,9 +98,18 @@ type ( RewardType string `json:"rewardType"` Commission uint8 `json:"commission"` } + Identity struct { Identity string `json:"identity"` } + + FullTransaction struct { + Transaction struct { + Message struct { + AccountKeys []string `json:"accountKeys"` + } `json:"message"` + } `json:"transaction"` + } ) func (e *RPCError) Error() string {