Skip to content

Commit

Permalink
feat(rpc-client): support getVoteAccounts (#455)
Browse files Browse the repository at this point in the history
  • Loading branch information
dnut authored Dec 30, 2024
1 parent 1e0a16d commit bdc065b
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/core/pubkey.zig
Original file line number Diff line number Diff line change
@@ -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();
Expand Down Expand Up @@ -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 };
66 changes: 66 additions & 0 deletions src/rpc/client.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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));
}
16 changes: 16 additions & 0 deletions src/rpc/types.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

0 comments on commit bdc065b

Please sign in to comment.