diff --git a/cmd/solana_exporter/exporter.go b/cmd/solana_exporter/exporter.go index da6268a..69ec47d 100644 --- a/cmd/solana_exporter/exporter.go +++ b/cmd/solana_exporter/exporter.go @@ -50,10 +50,6 @@ type SolanaCollector struct { NodeFirstAvailableBlock *GaugeDesc } -func init() { - slog.Init() -} - func NewSolanaCollector(provider rpc.Provider, config *ExporterConfig) *SolanaCollector { collector := &SolanaCollector{ rpcClient: provider, diff --git a/pkg/rpc/client.go b/pkg/rpc/client.go index 1c2fb59..522ae94 100644 --- a/pkg/rpc/client.go +++ b/pkg/rpc/client.go @@ -35,49 +35,57 @@ type ( // It provides methods to retrieve block production information, epoch info, slot info, vote accounts, and node version. type Provider interface { - // GetBlockProduction retrieves the block production information for the specified slot range. - // The method takes a context for cancellation, and pointers to the first and last slots of the range. - // It returns a BlockProduction struct containing the block production details, or an error if the operation fails. + // GetBlockProduction returns recent block production information from the current or previous epoch. + // See API docs: https://solana.com/docs/rpc/http/getblockproduction GetBlockProduction( ctx context.Context, commitment Commitment, identity *string, firstSlot *int64, lastSlot *int64, ) (*BlockProduction, error) - // GetEpochInfo retrieves the information regarding the current epoch. - // The method takes a context for cancellation and a commitment level to specify the desired state. - // It returns a pointer to an EpochInfo struct containing the epoch details, or an error if the operation fails. + // GetEpochInfo returns information about the current epoch. + // See API docs: https://solana.com/docs/rpc/http/getepochinfo GetEpochInfo(ctx context.Context, commitment Commitment) (*EpochInfo, error) - // GetSlot retrieves the current slot number. - // The method takes a context for cancellation. - // It returns the current slot number as an int64, or an error if the operation fails. + // GetSlot returns the slot that has reached the given or default commitment level. + // See API docs: https://solana.com/docs/rpc/http/getslot GetSlot(ctx context.Context, commitment Commitment) (int64, error) - // GetVoteAccounts retrieves the vote accounts information. - // The method takes a context for cancellation and a slice of parameters to filter the vote accounts. - // It returns a pointer to a VoteAccounts struct containing the vote accounts details, - // or an error if the operation fails. + // GetVoteAccounts returns the account info and associated stake for all the voting accounts in the current bank. + // See API docs: https://solana.com/docs/rpc/http/getvoteaccounts GetVoteAccounts(ctx context.Context, commitment Commitment, votePubkey *string) (*VoteAccounts, error) - // GetVersion retrieves the version of the Solana node. - // The method takes a context for cancellation. - // It returns a string containing the version information, or an error if the operation fails. + // GetVersion returns the current Solana version running on the node. + // See API docs: https://solana.com/docs/rpc/http/getversion GetVersion(ctx context.Context) (string, error) - // GetBalance returns the SOL balance of the account at the provided address + // GetBalance returns the lamport balance of the account of provided pubkey. + // See API docs:https://solana.com/docs/rpc/http/getbalance GetBalance(ctx context.Context, commitment Commitment, address string) (float64, error) - // GetInflationReward returns the inflation rewards (in lamports) awarded to the given addresses (vote accounts) - // during the given epoch. + // GetInflationReward returns the inflation / staking reward for a list of addresses for an epoch. + // See API docs: https://solana.com/docs/rpc/http/getinflationreward GetInflationReward( ctx context.Context, commitment Commitment, addresses []string, epoch *int64, minContextSlot *int64, ) ([]InflationReward, error) + // GetLeaderSchedule returns the leader schedule for an epoch. + // See API docs: https://solana.com/docs/rpc/http/getleaderschedule GetLeaderSchedule(ctx context.Context, commitment Commitment, slot int64) (map[string][]int64, error) + // GetBlock returns identity and transaction information about a confirmed block in the ledger. + // See API docs: https://solana.com/docs/rpc/http/getblock GetBlock(ctx context.Context, commitment Commitment, slot int64, transactionDetails string) (*Block, error) + // GetHealth returns the current health of the node. A healthy node is one that is within a blockchain-configured slots + // of the latest cluster confirmed slot. + // See API docs: https://solana.com/docs/rpc/http/gethealth GetHealth(ctx context.Context) (*string, error) + + // GetMinimumLedgerSlot returns the lowest slot that the node has information about in its ledger. + // See API docs: https://solana.com/docs/rpc/http/minimumledgerslot GetMinimumLedgerSlot(ctx context.Context) (*int64, error) + + // GetFirstAvailableBlock returns the slot of the lowest confirmed block that has not been purged from the ledger + // See API docs: https://solana.com/docs/rpc/http/getfirstavailableblock GetFirstAvailableBlock(ctx context.Context) (*int64, error) } diff --git a/pkg/rpc/mock/server.go b/pkg/rpc/server.go similarity index 69% rename from pkg/rpc/mock/server.go rename to pkg/rpc/server.go index 250b638..5016d3d 100644 --- a/pkg/rpc/mock/server.go +++ b/pkg/rpc/server.go @@ -15,11 +15,11 @@ import ( type ( // MockServer represents a mock Solana RPC server for testing MockServer struct { - server *http.Server - listener net.Listener - // ResponseMap allows tests to set custom responses for specific methods - ResponseMap map[string]any - mu sync.RWMutex + // Responses allows tests to set custom responses for specific methods + Responses map[string]any + server *http.Server + listener net.Listener + mu sync.RWMutex } // RPCResponse represents a JSON-RPC response @@ -32,13 +32,13 @@ type ( ) // NewMockServer creates a new mock server instance -func NewMockServer() (*MockServer, error) { +func NewMockServer(responses map[string]any) (*MockServer, error) { listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { return nil, fmt.Errorf("failed to create listener: %v", err) } - ms := &MockServer{listener: listener, ResponseMap: make(map[string]any)} + ms := &MockServer{listener: listener, Responses: responses} mux := http.NewServeMux() mux.HandleFunc("/", ms.handleRPCRequest) @@ -64,11 +64,17 @@ func (ms *MockServer) Close() error { return ms.server.Shutdown(ctx) } +func (ms *MockServer) MustClose() { + if err := ms.Close(); err != nil { + panic(err) + } +} + // SetResponse sets a custom response for a specific method func (ms *MockServer) SetResponse(method string, response any) { ms.mu.Lock() defer ms.mu.Unlock() - ms.ResponseMap[method] = response + ms.Responses[method] = response } func (ms *MockServer) handleRPCRequest(w http.ResponseWriter, r *http.Request) { @@ -83,43 +89,38 @@ func (ms *MockServer) handleRPCRequest(w http.ResponseWriter, r *http.Request) { return } + response := RPCResponse{JsonRPC: "2.0", ID: req.Id} ms.mu.RLock() - result, exists := ms.ResponseMap[req.Method] + result, exists := ms.Responses[req.Method] ms.mu.RUnlock() if !exists { // Fall back to default responses if no custom response is set - result = getDefaultResponse(req.Method) + response.Error = rpc.RPCError{ + Code: -32601, + Message: "Method not found", + } + } else { + response.Result = result } - response := RPCResponse{JsonRPC: "2.0", Result: result, ID: req.Id} - w.Header().Set("Content-Type", "application/json") - _ = json.NewEncoder(w).Encode(response) -} - -// Helper function to get default responses -func getDefaultResponse(method string) any { - switch method { - case "getBalance": - return map[string]any{"context": map[string]any{"slot": 1234567}, "value": 100000000} - case "getSlot": - return 1234567 - // Add more default responses as needed - default: - return nil + err := json.NewEncoder(w).Encode(response) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) } } // TestHelper is a helper struct for tests type TestHelper struct { Server *MockServer + Client *rpc.Client T *testing.T } // NewTestHelper creates a new test helper with a running mock server -func NewTestHelper(t *testing.T) *TestHelper { - server, err := NewMockServer() +func NewTestHelper(t *testing.T, responses map[string]any) *TestHelper { + server, err := NewMockServer(responses) if err != nil { t.Fatalf("Failed to create mock server: %v", err) } @@ -130,5 +131,5 @@ func NewTestHelper(t *testing.T) *TestHelper { } }) - return &TestHelper{Server: server, T: t} + return &TestHelper{Server: server, Client: rpc.NewRPCClient(server.URL(), time.Second), T: t} } diff --git a/pkg/slog/main.go b/pkg/slog/main.go index cb90a22..13aed3f 100644 --- a/pkg/slog/main.go +++ b/pkg/slog/main.go @@ -10,8 +10,8 @@ import ( var log *zap.SugaredLogger -// Init initializes the logger -func Init() { +// init initializes the logger +func init() { config := zap.NewProductionConfig() // configure: