counted block size by vote vs non-vote tx
This commit is contained in:
parent
e46d20d68e
commit
6fa9afb6aa
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue