added inflation rewards metric
This commit is contained in:
parent
68a3a29280
commit
172d15a309
|
@ -14,17 +14,28 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
httpTimeout = 60 * time.Second
|
||||
rpcAddr = flag.String("rpcURI", "", "Solana RPC URI (including protocol and path)")
|
||||
addr = flag.String("addr", ":8080", "Listen address")
|
||||
votePubkey = flag.String("votepubkey", "", "Validator vote address (will only return results of this address)")
|
||||
httpTimeoutSecs = flag.Int("http_timeout", 60, "HTTP timeout in seconds")
|
||||
balanceAddresses = flag.String("balance-addresses", "", "Comma-separated list of addresses to monitor balances")
|
||||
httpTimeout = 60 * time.Second
|
||||
rpcAddr = flag.String("rpcURI", "", "Solana RPC URI (including protocol and path)")
|
||||
addr = flag.String("addr", ":8080", "Listen address")
|
||||
votePubkey = flag.String("votepubkey", "", "Validator vote address (will only return results of this address)")
|
||||
httpTimeoutSecs = flag.Int("http_timeout", 60, "HTTP timeout in seconds")
|
||||
|
||||
// addresses:
|
||||
balanceAddresses = flag.String(
|
||||
"balance-addresses",
|
||||
"",
|
||||
"Comma-separated list of addresses to monitor SOL balances.",
|
||||
)
|
||||
leaderSlotAddresses = flag.String(
|
||||
"leader-slot-addresses",
|
||||
"",
|
||||
"Comma-separated list of addresses to monitor leader slots by epoch for, leave nil to track by epoch for all validators (this creates a lot of Prometheus metrics with every new epoch).",
|
||||
)
|
||||
inflationRewardAddresses = flag.String(
|
||||
"inflation-reward-addresses",
|
||||
"",
|
||||
"Comma-separated list of validator vote accounts to track inflationary rewards for",
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -35,9 +46,10 @@ type solanaCollector struct {
|
|||
rpcClient rpc.Provider
|
||||
|
||||
// config:
|
||||
slotPace time.Duration
|
||||
balanceAddresses []string
|
||||
leaderSlotAddresses []string
|
||||
slotPace time.Duration
|
||||
balanceAddresses []string
|
||||
leaderSlotAddresses []string
|
||||
inflationRewardAddresses []string
|
||||
|
||||
/// descriptors:
|
||||
totalValidatorsDesc *prometheus.Desc
|
||||
|
@ -50,13 +62,18 @@ type solanaCollector struct {
|
|||
}
|
||||
|
||||
func createSolanaCollector(
|
||||
provider rpc.Provider, slotPace time.Duration, balanceAddresses []string, leaderSlotAddresses []string,
|
||||
provider rpc.Provider,
|
||||
slotPace time.Duration,
|
||||
balanceAddresses []string,
|
||||
leaderSlotAddresses []string,
|
||||
inflationRewardAddresses []string,
|
||||
) *solanaCollector {
|
||||
return &solanaCollector{
|
||||
rpcClient: provider,
|
||||
slotPace: slotPace,
|
||||
balanceAddresses: balanceAddresses,
|
||||
leaderSlotAddresses: leaderSlotAddresses,
|
||||
rpcClient: provider,
|
||||
slotPace: slotPace,
|
||||
balanceAddresses: balanceAddresses,
|
||||
leaderSlotAddresses: leaderSlotAddresses,
|
||||
inflationRewardAddresses: inflationRewardAddresses,
|
||||
totalValidatorsDesc: prometheus.NewDesc(
|
||||
"solana_active_validators",
|
||||
"Total number of active validators by state",
|
||||
|
@ -102,8 +119,12 @@ func createSolanaCollector(
|
|||
}
|
||||
}
|
||||
|
||||
func NewSolanaCollector(rpcAddr string, balanceAddresses []string, leaderSlotAddresses []string) *solanaCollector {
|
||||
return createSolanaCollector(rpc.NewRPCClient(rpcAddr), slotPacerSchedule, balanceAddresses, leaderSlotAddresses)
|
||||
func NewSolanaCollector(
|
||||
rpcAddr string, balanceAddresses []string, leaderSlotAddresses []string, inflationRewardAddresses []string,
|
||||
) *solanaCollector {
|
||||
return createSolanaCollector(
|
||||
rpc.NewRPCClient(rpcAddr), slotPacerSchedule, balanceAddresses, leaderSlotAddresses, inflationRewardAddresses,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *solanaCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
|
@ -233,6 +254,7 @@ func main() {
|
|||
var (
|
||||
balAddresses []string
|
||||
lsAddresses []string
|
||||
irAddresses []string
|
||||
)
|
||||
if *balanceAddresses != "" {
|
||||
balAddresses = strings.Split(*balanceAddresses, ",")
|
||||
|
@ -240,8 +262,11 @@ func main() {
|
|||
if *leaderSlotAddresses != "" {
|
||||
lsAddresses = strings.Split(*leaderSlotAddresses, ",")
|
||||
}
|
||||
if *inflationRewardAddresses != "" {
|
||||
irAddresses = strings.Split(*inflationRewardAddresses, ",")
|
||||
}
|
||||
|
||||
collector := NewSolanaCollector(*rpcAddr, balAddresses, lsAddresses)
|
||||
collector := NewSolanaCollector(*rpcAddr, balAddresses, lsAddresses, irAddresses)
|
||||
|
||||
slotWatcher := NewCollectorSlotWatcher(collector)
|
||||
go slotWatcher.WatchSlots(context.Background(), collector.slotPace)
|
||||
|
|
|
@ -42,6 +42,7 @@ type (
|
|||
|
||||
var (
|
||||
identities = []string{"aaa", "bbb", "ccc"}
|
||||
votekeys = []string{"AAA", "BBB", "CCC"}
|
||||
balances = map[string]float64{"aaa": 1, "bbb": 2, "ccc": 3}
|
||||
identityVotes = map[string]string{"aaa": "AAA", "bbb": "BBB", "ccc": "CCC"}
|
||||
nv = len(identities)
|
||||
|
@ -375,7 +376,7 @@ func runCollectionTests(t *testing.T, collector prometheus.Collector, testCases
|
|||
}
|
||||
|
||||
func TestSolanaCollector_Collect_Static(t *testing.T) {
|
||||
collector := createSolanaCollector(&staticRPCClient{}, slotPacerSchedule, identities, []string{})
|
||||
collector := createSolanaCollector(&staticRPCClient{}, slotPacerSchedule, identities, []string{}, votekeys)
|
||||
prometheus.NewPedanticRegistry().MustRegister(collector)
|
||||
|
||||
testCases := []collectionTest{
|
||||
|
@ -453,7 +454,7 @@ solana_account_balance{address="ccc"} 3
|
|||
|
||||
func TestSolanaCollector_Collect_Dynamic(t *testing.T) {
|
||||
client := newDynamicRPCClient()
|
||||
collector := createSolanaCollector(client, slotPacerSchedule, identities, []string{})
|
||||
collector := createSolanaCollector(client, slotPacerSchedule, identities, []string{}, votekeys)
|
||||
prometheus.NewPedanticRegistry().MustRegister(collector)
|
||||
|
||||
// start off by testing initial state:
|
||||
|
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/asymmetric-research/solana_exporter/pkg/rpc"
|
||||
|
@ -17,7 +18,9 @@ const (
|
|||
type SlotWatcher struct {
|
||||
client rpc.Provider
|
||||
|
||||
leaderSlotAddresses []string
|
||||
// config:
|
||||
leaderSlotAddresses []string
|
||||
inflationRewardAddresses []string
|
||||
|
||||
// currentEpoch is the current epoch we are watching
|
||||
currentEpoch int64
|
||||
|
@ -70,10 +73,22 @@ var (
|
|||
},
|
||||
[]string{"status", "nodekey", "epoch"},
|
||||
)
|
||||
|
||||
inflationRewards = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "solana_inflation_rewards",
|
||||
Help: "Inflation reward earned per leader, per epoch",
|
||||
},
|
||||
[]string{"votekey", "epoch"},
|
||||
)
|
||||
)
|
||||
|
||||
func NewCollectorSlotWatcher(collector *solanaCollector) *SlotWatcher {
|
||||
return &SlotWatcher{client: collector.rpcClient, leaderSlotAddresses: collector.leaderSlotAddresses}
|
||||
return &SlotWatcher{
|
||||
client: collector.rpcClient,
|
||||
leaderSlotAddresses: collector.leaderSlotAddresses,
|
||||
inflationRewardAddresses: collector.inflationRewardAddresses,
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -84,6 +99,7 @@ func init() {
|
|||
prometheus.MustRegister(epochLastSlot)
|
||||
prometheus.MustRegister(leaderSlotsTotal)
|
||||
prometheus.MustRegister(leaderSlotsByEpoch)
|
||||
prometheus.MustRegister(inflationRewards)
|
||||
}
|
||||
|
||||
func (c *SlotWatcher) WatchSlots(ctx context.Context, pace time.Duration) {
|
||||
|
@ -123,6 +139,13 @@ func (c *SlotWatcher) WatchSlots(ctx context.Context, pace time.Duration) {
|
|||
}
|
||||
|
||||
if epochInfo.Epoch > c.currentEpoch {
|
||||
// if we have configured inflation reward addresses, fetch em
|
||||
if len(c.inflationRewardAddresses) > 0 {
|
||||
err = c.fetchAndEmitInflationRewards(ctx, c.currentEpoch)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to emit inflation rewards, bailing out: %v", err)
|
||||
}
|
||||
}
|
||||
c.closeCurrentEpoch(ctx, epochInfo)
|
||||
}
|
||||
|
||||
|
@ -244,3 +267,28 @@ func getEpochBounds(info *rpc.EpochInfo) (int64, int64) {
|
|||
firstSlot := info.AbsoluteSlot - info.SlotIndex
|
||||
return firstSlot, firstSlot + info.SlotsInEpoch - 1
|
||||
}
|
||||
|
||||
// fetchAndEmitInflationRewards fetches and emits the inflation rewards for the configured inflationRewardAddresses
|
||||
// at the provided epoch
|
||||
func (c *SlotWatcher) fetchAndEmitInflationRewards(ctx context.Context, epoch int64) error {
|
||||
epochStr := fmt.Sprintf("%d", epoch)
|
||||
klog.Infof("Fetching inflation reward for epoch %v ...", epochStr)
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, httpTimeout)
|
||||
defer cancel()
|
||||
|
||||
rewardInfos, err := c.client.GetInflationReward(
|
||||
ctx, c.inflationRewardAddresses, rpc.CommitmentFinalized, &epoch, nil,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, rewardInfo := range rewardInfos {
|
||||
address := c.inflationRewardAddresses[i]
|
||||
reward := float64(rewardInfo.Amount) / float64(rpc.LamportsInSol)
|
||||
inflationRewards.WithLabelValues(address, epochStr).Set(reward)
|
||||
}
|
||||
klog.Infof("Fetched inflation reward for epoch %v.", epochStr)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ func TestSolanaCollector_WatchSlots_Static(t *testing.T) {
|
|||
leaderSlotsTotal.Reset()
|
||||
leaderSlotsByEpoch.Reset()
|
||||
|
||||
collector := createSolanaCollector(&staticRPCClient{}, 100*time.Millisecond, identities, []string{})
|
||||
collector := createSolanaCollector(&staticRPCClient{}, 100*time.Millisecond, identities, []string{}, votekeys)
|
||||
watcher := NewCollectorSlotWatcher(collector)
|
||||
prometheus.NewPedanticRegistry().MustRegister(collector)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
@ -145,8 +145,8 @@ func TestSolanaCollector_WatchSlots_Dynamic(t *testing.T) {
|
|||
|
||||
// create clients:
|
||||
client := newDynamicRPCClient()
|
||||
collector := createSolanaCollector(client, 300*time.Millisecond, identities, []string{})
|
||||
watcher := SlotWatcher{client: client}
|
||||
collector := createSolanaCollector(client, 300*time.Millisecond, identities, []string{}, votekeys)
|
||||
watcher := NewCollectorSlotWatcher(collector)
|
||||
prometheus.NewPedanticRegistry().MustRegister(collector)
|
||||
|
||||
// start client/collector and wait a bit:
|
||||
|
|
Loading…
Reference in New Issue