From bdc065b0fd762cb39633ac61ac2acb0b437f2c5c Mon Sep 17 00:00:00 2001 From: Drew Nutter Date: Mon, 30 Dec 2024 12:09:04 -0300 Subject: [PATCH] feat(rpc-client): support getVoteAccounts (#455) --- src/core/pubkey.zig | 10 +++++++ src/rpc/client.zig | 66 +++++++++++++++++++++++++++++++++++++++++++++ src/rpc/types.zig | 16 +++++++++++ 3 files changed, 92 insertions(+) diff --git a/src/core/pubkey.zig b/src/core/pubkey.zig index acace56db..820a57ee6 100644 --- a/src/core/pubkey.zig +++ b/src/core/pubkey.zig @@ -1,6 +1,9 @@ const std = @import("std"); const sig = @import("../sig.zig"); +const Allocator = std.mem.Allocator; +const ParseOptions = std.json.ParseOptions; + pub const Pubkey = extern struct { data: [size]u8, const Self = @This(); @@ -54,6 +57,13 @@ pub const Pubkey = extern struct { ) !void { return base58.format(self.data, writer); } + + pub fn jsonParse(_: Allocator, source: anytype, _: ParseOptions) !Pubkey { + return switch (try source.next()) { + .string => |s| .{ .data = base58.decode(s) catch return error.UnexpectedToken }, + else => error.UnexpectedToken, + }; + } }; const Error = error{ InvalidBytesLength, InvalidEncodedLength, InvalidEncodedValue }; diff --git a/src/rpc/client.zig b/src/rpc/client.zig index 751ea35f5..fa03efaf3 100644 --- a/src/rpc/client.zig +++ b/src/rpc/client.zig @@ -344,6 +344,25 @@ pub const Client = struct { return self.sendFetchRequest(allocator, types.RpcVersionInfo, request, .{}); } + const GetVoteAccountsConfig = struct { + commitment: ?types.Commitment = null, + votePubkey: ?Pubkey = null, + keepUnstakedDelinquents: ?bool = null, + delinquintSlotDistance: ?u64 = null, + }; + + pub fn getVoteAccounts( + self: *Client, + allocator: std.mem.Allocator, + config: GetVoteAccountsConfig, + ) !Response(types.GetVoteAccountsResponse) { + var request = try Request.init(allocator, "getVoteAccounts"); + defer request.deinit(); + try request.addConfig(config); + + return self.sendFetchRequest(allocator, types.GetVoteAccountsResponse, request, .{}); + } + /// Sends a JSON-RPC request to the HTTP endpoint and parses the response. /// If the request fails, it will be retried up to `max_retries` times, restarting the HTTP client /// if necessary. If the response fails to parse, an error will be returned. @@ -590,3 +609,50 @@ test "getVersion" { defer response.deinit(); _ = try response.result(); } + +test "getVoteAccounts response parses correctly" { + const response_json = + \\{ + \\ "jsonrpc": "2.0", + \\ "result": { + \\ "current": [ + \\ { + \\ "commission": 0, + \\ "epochVoteAccount": true, + \\ "epochCredits": [ + \\ [1, 64, 0], + \\ [2, 192, 64] + \\ ], + \\ "nodePubkey": "B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD", + \\ "lastVote": 147, + \\ "activatedStake": 42, + \\ "votePubkey": "3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw", + \\ "rootSlot": 100 + \\ } + \\ ], + \\ "delinquent": [] + \\ }, + \\ "id": 1 + \\} + ; + + var response = try Response(types.GetVoteAccountsResponse).init(std.testing.allocator, .{}); + try response.bytes.appendSlice(response_json); + defer response.deinit(); + try response.parse(); + const actual = try response.result(); + const expected = types.GetVoteAccountsResponse{ + .current = &.{.{ + .commission = 0, + .epochVoteAccount = true, + .epochCredits = &.{ .{ 1, 64, 0 }, .{ 2, 192, 64 } }, + .nodePubkey = try Pubkey.fromString("B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD"), + .lastVote = 147, + .activatedStake = 42, + .votePubkey = try Pubkey.fromString("3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"), + .rootSlot = 100, + }}, + .delinquent = &.{}, + }; + try std.testing.expect(sig.utils.types.eql(expected, actual)); +} diff --git a/src/rpc/types.zig b/src/rpc/types.zig index 18b68b034..9c940e9a9 100644 --- a/src/rpc/types.zig +++ b/src/rpc/types.zig @@ -151,3 +151,19 @@ pub const RpcVersionInfo = struct { }; pub const Signature = []const u8; + +pub const GetVoteAccountsResponse = struct { + current: []const VoteAccount, + delinquent: []const VoteAccount, +}; + +pub const VoteAccount = struct { + votePubkey: sig.core.Pubkey, + nodePubkey: sig.core.Pubkey, + activatedStake: u64, + epochVoteAccount: bool, + commission: u8, + lastVote: u64, + epochCredits: []const [3]u64, + rootSlot: u64, +};