Merge pull request #25 from asymmetric-research/provider-interface

Static tests
This commit is contained in:
Matt Johnstone 2024-06-15 09:44:42 +02:00 committed by GitHub
commit f1e948fb3b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 348 additions and 22 deletions

View File

@ -72,15 +72,19 @@ func NewSolanaCollector(rpcAddr string) *solanaCollector {
func (c *solanaCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.totalValidatorsDesc
ch <- c.solanaVersion
ch <- c.validatorActivatedStake
ch <- c.validatorLastVote
ch <- c.validatorRootSlot
ch <- c.validatorDelinquent
}
func (c *solanaCollector) mustEmitMetrics(ch chan<- prometheus.Metric, response *rpc.GetVoteAccountsResponse) {
func (c *solanaCollector) mustEmitMetrics(ch chan<- prometheus.Metric, response *rpc.VoteAccounts) {
ch <- prometheus.MustNewConstMetric(c.totalValidatorsDesc, prometheus.GaugeValue,
float64(len(response.Result.Delinquent)), "delinquent")
float64(len(response.Delinquent)), "delinquent")
ch <- prometheus.MustNewConstMetric(c.totalValidatorsDesc, prometheus.GaugeValue,
float64(len(response.Result.Current)), "current")
float64(len(response.Current)), "current")
for _, account := range append(response.Result.Current, response.Result.Delinquent...) {
for _, account := range append(response.Current, response.Delinquent...) {
ch <- prometheus.MustNewConstMetric(c.validatorActivatedStake, prometheus.GaugeValue,
float64(account.ActivatedStake), account.VotePubkey, account.NodePubkey)
ch <- prometheus.MustNewConstMetric(c.validatorLastVote, prometheus.GaugeValue,
@ -88,11 +92,11 @@ func (c *solanaCollector) mustEmitMetrics(ch chan<- prometheus.Metric, response
ch <- prometheus.MustNewConstMetric(c.validatorRootSlot, prometheus.GaugeValue,
float64(account.RootSlot), account.VotePubkey, account.NodePubkey)
}
for _, account := range response.Result.Current {
for _, account := range response.Current {
ch <- prometheus.MustNewConstMetric(c.validatorDelinquent, prometheus.GaugeValue,
0, account.VotePubkey, account.NodePubkey)
}
for _, account := range response.Result.Delinquent {
for _, account := range response.Delinquent {
ch <- prometheus.MustNewConstMetric(c.validatorDelinquent, prometheus.GaugeValue,
1, account.VotePubkey, account.NodePubkey)
}

View File

@ -0,0 +1,163 @@
package main
import (
"bytes"
"fmt"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
"testing"
"time"
)
var staticCollector = createSolanaCollector(&staticRPCClient{})
func TestSolanaCollector_Collect(t *testing.T) {
prometheus.NewPedanticRegistry().MustRegister(staticCollector)
testCases := map[string]string{
"solana_active_validators": `
# HELP solana_active_validators Total number of active validators by state
# TYPE solana_active_validators gauge
solana_active_validators{state="current"} 2
solana_active_validators{state="delinquent"} 1
`,
"solana_validator_activated_stake": `
# HELP solana_validator_activated_stake Activated stake per validator
# TYPE solana_validator_activated_stake gauge
solana_validator_activated_stake{nodekey="4MUdt8D2CadJKeJ8Fv2sz4jXU9xv4t2aBPpTf6TN8bae",pubkey="xKUz6fZ79SXnjGYaYhhYTYQBoRUBoCyuDMkBa1tL3zU"} 49
solana_validator_activated_stake{nodekey="B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD",pubkey="3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"} 42
solana_validator_activated_stake{nodekey="C97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD",pubkey="4ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"} 43
`,
"solana_validator_last_vote": `
# HELP solana_validator_last_vote Last voted slot per validator
# TYPE solana_validator_last_vote gauge
solana_validator_last_vote{nodekey="4MUdt8D2CadJKeJ8Fv2sz4jXU9xv4t2aBPpTf6TN8bae",pubkey="xKUz6fZ79SXnjGYaYhhYTYQBoRUBoCyuDMkBa1tL3zU"} 92
solana_validator_last_vote{nodekey="B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD",pubkey="3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"} 147
solana_validator_last_vote{nodekey="C97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD",pubkey="4ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"} 148
`,
"solana_validator_root_slot": `
# HELP solana_validator_root_slot Root slot per validator
# TYPE solana_validator_root_slot gauge
solana_validator_root_slot{nodekey="4MUdt8D2CadJKeJ8Fv2sz4jXU9xv4t2aBPpTf6TN8bae",pubkey="xKUz6fZ79SXnjGYaYhhYTYQBoRUBoCyuDMkBa1tL3zU"} 3
solana_validator_root_slot{nodekey="B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD",pubkey="3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"} 18
solana_validator_root_slot{nodekey="C97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD",pubkey="4ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"} 19
`,
"solana_validator_delinquent": `
# HELP solana_validator_delinquent Whether a validator is delinquent
# TYPE solana_validator_delinquent gauge
solana_validator_delinquent{nodekey="4MUdt8D2CadJKeJ8Fv2sz4jXU9xv4t2aBPpTf6TN8bae",pubkey="xKUz6fZ79SXnjGYaYhhYTYQBoRUBoCyuDMkBa1tL3zU"} 1
solana_validator_delinquent{nodekey="B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD",pubkey="3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"} 0
solana_validator_delinquent{nodekey="C97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD",pubkey="4ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"} 0
`,
"solana_node_version": `
# HELP solana_node_version Node version of solana
# TYPE solana_node_version gauge
solana_node_version{version="1.16.7"} 1
`,
}
for testName, expectedValue := range testCases {
t.Run(
testName,
func(t *testing.T) {
if err := testutil.CollectAndCompare(
staticCollector,
bytes.NewBufferString(expectedValue),
testName,
); err != nil {
t.Errorf("unexpected collecting result for %s: \n%s", testName, err)
}
},
)
}
}
func TestSolanaCollector_WatchSlots(t *testing.T) {
go staticCollector.WatchSlots()
time.Sleep(1 * time.Second)
tests := []struct {
expectedValue float64
metric prometheus.Gauge
}{
{
expectedValue: float64(staticEpochInfo.AbsoluteSlot),
metric: confirmedSlotHeight,
},
{
expectedValue: float64(staticEpochInfo.TransactionCount),
metric: totalTransactionsTotal,
},
{
expectedValue: float64(staticEpochInfo.Epoch),
metric: currentEpochNumber,
},
{
expectedValue: float64(staticEpochInfo.AbsoluteSlot - staticEpochInfo.SlotIndex),
metric: epochFirstSlot,
},
{
expectedValue: float64(staticEpochInfo.AbsoluteSlot - staticEpochInfo.SlotIndex + staticEpochInfo.SlotsInEpoch),
metric: epochLastSlot,
},
}
for _, testCase := range tests {
name := extractName(testCase.metric.Desc())
t.Run(
name,
func(t *testing.T) {
assert.Equal(t, testCase.expectedValue, testutil.ToFloat64(testCase.metric))
},
)
}
hosts := []string{
"B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD",
"C97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD",
"4MUdt8D2CadJKeJ8Fv2sz4jXU9xv4t2aBPpTf6TN8bae",
}
metrics := map[string]*prometheus.CounterVec{
"solana_leader_slots_total": leaderSlotsTotal,
"solana_leader_slots_by_epoch": leaderSlotsByEpoch,
}
statuses := []string{"valid", "skipped"}
for name, metric := range metrics {
// subtest for each metric:
t.Run(name, func(t *testing.T) {
for _, status := range statuses {
// sub subtest for each status (as each one requires a different calc)
t.Run(status, func(t *testing.T) {
for _, host := range hosts {
testBlockProductionMetric(t, metric, host, status)
}
})
}
})
}
}
func testBlockProductionMetric(
t *testing.T,
metric *prometheus.CounterVec,
host string,
status string,
) {
hostInfo := staticBlockProduction.Hosts[host]
// get expected value depending on status:
var expectedValue float64
switch status {
case "valid":
expectedValue = float64(hostInfo.BlocksProduced)
case "skipped":
expectedValue = float64(hostInfo.LeaderSlots - hostInfo.BlocksProduced)
}
// get labels (leaderSlotsByEpoch requires an extra one)
labels := []string{status, host}
if metric == leaderSlotsByEpoch {
labels = append(labels, fmt.Sprintf("%d", staticEpochInfo.Epoch))
}
// now we can do the assertion:
assert.Equal(t, expectedValue, testutil.ToFloat64(metric.WithLabelValues(labels...)))
}

View File

@ -74,11 +74,21 @@ func (c *solanaCollector) WatchSlots() {
}
cancel()
totalTransactionsTotal.Set(float64(info.TransactionCount))
confirmedSlotHeight.Set(float64(info.AbsoluteSlot))
// watermark is the last slot number we generated ticks for. Set it to the current offset on startup (we do not backfill slots we missed at startup)
watermark := info.AbsoluteSlot
currentEpoch, firstSlot, lastSlot := getEpochBounds(info)
currentEpochNumber.Set(float64(currentEpoch))
epochFirstSlot.Set(float64(firstSlot))
epochLastSlot.Set(float64(lastSlot))
klog.Infof("Starting at slot %d in epoch %d (%d-%d)", firstSlot, currentEpoch, firstSlot, lastSlot)
_, err = updateCounters(c.rpcClient, currentEpoch, watermark, &lastSlot)
if err != nil {
klog.Error(err)
}
ticker := time.NewTicker(slotPacerSchedule)
for {

View File

@ -0,0 +1,138 @@
package main
import (
"context"
"github.com/certusone/solana_exporter/pkg/rpc"
"github.com/prometheus/client_golang/prometheus"
"regexp"
)
type (
staticRPCClient struct{}
// TODO: create dynamicRPCClient + according tests!
)
var (
staticEpochInfo = rpc.EpochInfo{
AbsoluteSlot: 166598,
BlockHeight: 166500,
Epoch: 27,
SlotIndex: 2790,
SlotsInEpoch: 8192,
TransactionCount: 22661093,
}
staticBlockProduction = rpc.BlockProduction{
FirstSlot: 1000,
LastSlot: 2000,
Hosts: map[string]rpc.BlockProductionPerHost{
"B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD": {
LeaderSlots: 400,
BlocksProduced: 360,
},
"C97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD": {
LeaderSlots: 300,
BlocksProduced: 296,
},
"4MUdt8D2CadJKeJ8Fv2sz4jXU9xv4t2aBPpTf6TN8bae": {
LeaderSlots: 300,
BlocksProduced: 0,
},
},
}
)
//goland:noinspection GoUnusedParameter
func (c *staticRPCClient) GetEpochInfo(ctx context.Context, commitment rpc.Commitment) (*rpc.EpochInfo, error) {
return &staticEpochInfo, nil
}
//goland:noinspection GoUnusedParameter
func (c *staticRPCClient) GetSlot(ctx context.Context) (int64, error) {
return staticEpochInfo.AbsoluteSlot, nil
}
//goland:noinspection GoUnusedParameter
func (c *staticRPCClient) GetVersion(ctx context.Context) (*string, error) {
version := "1.16.7"
return &version, nil
}
//goland:noinspection GoUnusedParameter
func (c *staticRPCClient) GetVoteAccounts(
ctx context.Context,
params []interface{},
) (*rpc.VoteAccounts, error) {
voteAccounts := rpc.VoteAccounts{
Current: []rpc.VoteAccount{
{
ActivatedStake: 42,
Commission: 0,
EpochCredits: [][]int{
{1, 64, 0},
{2, 192, 64},
},
EpochVoteAccount: true,
LastVote: 147,
NodePubkey: "B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD",
RootSlot: 18,
VotePubkey: "3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw",
},
{
ActivatedStake: 43,
Commission: 1,
EpochCredits: [][]int{
{2, 65, 1},
{3, 193, 65},
},
EpochVoteAccount: true,
LastVote: 148,
NodePubkey: "C97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD",
RootSlot: 19,
VotePubkey: "4ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw",
},
},
Delinquent: []rpc.VoteAccount{
{
ActivatedStake: 49,
Commission: 2,
EpochCredits: [][]int{
{10, 594, 6},
{9, 98, 4},
},
EpochVoteAccount: true,
LastVote: 92,
NodePubkey: "4MUdt8D2CadJKeJ8Fv2sz4jXU9xv4t2aBPpTf6TN8bae",
RootSlot: 3,
VotePubkey: "xKUz6fZ79SXnjGYaYhhYTYQBoRUBoCyuDMkBa1tL3zU",
},
},
}
return &voteAccounts, nil
}
//goland:noinspection GoUnusedParameter
func (c *staticRPCClient) GetBlockProduction(
ctx context.Context,
firstSlot *int64,
lastSlot *int64,
) (rpc.BlockProduction, error) {
return staticBlockProduction, nil
}
// extractName takes a Prometheus descriptor and returns its name
func extractName(desc *prometheus.Desc) string {
// Get the string representation of the descriptor
descString := desc.String()
// Use regex to extract the metric name and help message from the descriptor string
reName := regexp.MustCompile(`fqName: "([^"]+)"`)
nameMatch := reName.FindStringSubmatch(descString)
var name string
if len(nameMatch) > 1 {
name = nameMatch[1]
}
return name
}

2
go.mod
View File

@ -4,5 +4,7 @@ go 1.13
require (
github.com/prometheus/client_golang v1.4.0
github.com/prometheus/common v0.9.1
github.com/stretchr/testify v1.4.0
k8s.io/klog/v2 v2.4.0
)

7
go.sum
View File

@ -9,6 +9,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -31,8 +32,10 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@ -43,6 +46,7 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
@ -65,6 +69,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -84,10 +89,12 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IV
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=

View File

@ -32,7 +32,7 @@ type (
Error rpcError2 `json:"error"`
}
blockProductionPerHost struct {
BlockProductionPerHost struct {
LeaderSlots int64
BlocksProduced int64
}
@ -40,7 +40,7 @@ type (
BlockProduction struct {
FirstSlot int64
LastSlot int64
Hosts map[string]blockProductionPerHost
Hosts map[string]BlockProductionPerHost
}
)
@ -81,10 +81,10 @@ func (c *Client) GetBlockProduction(ctx context.Context, firstSlot *int64, lastS
ret.FirstSlot = resp.Result.Value.Range.FirstSlot
ret.LastSlot = *resp.Result.Value.Range.LastSlot
ret.Hosts = make(map[string]blockProductionPerHost)
ret.Hosts = make(map[string]BlockProductionPerHost)
for id, arr := range resp.Result.Value.ByIdentity {
ret.Hosts[id] = blockProductionPerHost{
ret.Hosts[id] = BlockProductionPerHost{
LeaderSlots: arr[0],
BlocksProduced: arr[1],
}

View File

@ -15,12 +15,12 @@ type (
rpcAddr string
}
rpcError struct {
rpcError1 struct {
Message string `json:"message"`
Code int64 `json:"id"`
}
rpcError2 struct {
rpcError2 struct { // TODO: combine these error types into a single one
Message string `json:"message"`
Code int64 `json:"code"`
}
@ -58,7 +58,7 @@ type Provider interface {
// The method takes a context for cancellation and a slice of parameters to filter the vote accounts.
// It returns a pointer to a GetVoteAccountsResponse struct containing the vote accounts details,
// or an error if the operation fails.
GetVoteAccounts(ctx context.Context, params []interface{}) (*GetVoteAccountsResponse, error)
GetVoteAccounts(ctx context.Context, params []interface{}) (*VoteAccounts, error)
// GetVersion retrieves the version of the Solana node.
// The method takes a context for cancellation.

View File

@ -25,7 +25,7 @@ type (
GetEpochInfoResponse struct {
Result EpochInfo `json:"result"`
Error rpcError `json:"error"`
Error rpcError1 `json:"error"`
}
)

View File

@ -19,17 +19,19 @@ type (
VotePubkey string `json:"votePubkey"`
}
VoteAccounts struct {
Current []VoteAccount `json:"current"`
Delinquent []VoteAccount `json:"delinquent"`
}
GetVoteAccountsResponse struct {
Result struct {
Current []VoteAccount `json:"current"`
Delinquent []VoteAccount `json:"delinquent"`
} `json:"result"`
Error rpcError `json:"error"`
Result VoteAccounts `json:"result"`
Error rpcError1 `json:"error"`
}
)
// https://docs.solana.com/developing/clients/jsonrpc-api#getvoteaccounts
func (c *Client) GetVoteAccounts(ctx context.Context, params []interface{}) (*GetVoteAccountsResponse, error) {
func (c *Client) GetVoteAccounts(ctx context.Context, params []interface{}) (*VoteAccounts, error) {
body, err := c.rpcRequest(ctx, formatRPCRequest("getVoteAccounts", params))
if err != nil {
return nil, fmt.Errorf("RPC call failed: %w", err)
@ -46,5 +48,5 @@ func (c *Client) GetVoteAccounts(ctx context.Context, params []interface{}) (*Ge
return nil, fmt.Errorf("RPC error: %d %v", resp.Error.Code, resp.Error.Message)
}
return &resp, nil
return &resp.Result, nil
}

View File

@ -13,7 +13,7 @@ type (
Result struct {
Version string `json:"solana-core"`
} `json:"result"`
Error rpcError `json:"error"`
Error rpcError1 `json:"error"`
}
)