Skip to content

Commit

Permalink
Merge pull request #62 from asymmetric-research/vote-count
Browse files Browse the repository at this point in the history
Measure block size by vote tx and normal
  • Loading branch information
johnstonematt authored Oct 28, 2024
2 parents cd3068f + 6fa9afb commit a0d34e7
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 22 deletions.
20 changes: 12 additions & 8 deletions cmd/solana_exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
32 changes: 18 additions & 14 deletions cmd/solana_exporter/slots.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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),
Expand All @@ -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 (
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down
22 changes: 22 additions & 0 deletions cmd/solana_exporter/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
9 changes: 9 additions & 0 deletions pkg/rpc/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit a0d34e7

Please sign in to comment.