Skip to content

Commit b9f7b3e

Browse files
committed
feat(rpc): implement getBalance RPC method
- Add getBalance handler with commitment level support in AccountsDB - Add getSlotForCommitment to SlotTracker for commitment-based slot resolution - Update registerRPCHooks signature to accept slot_tracker and snapshot_slot - Use snapshot slot as fallback when no live slot tracker is available
1 parent ccfde8d commit b9f7b3e

File tree

4 files changed

+74
-5
lines changed

4 files changed

+74
-5
lines changed

src/accountsdb/db.zig

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const tracy = @import("tracy");
88

99
const sysvar = sig.runtime.sysvar;
1010
const snapgen = sig.accounts_db.snapshot.data.generate;
11+
const methods = sig.rpc.methods;
1112

1213
const Resolution = @import("../benchmarks.zig").Resolution;
1314

@@ -47,6 +48,9 @@ const Pubkey = sig.core.Pubkey;
4748
const Slot = sig.core.Slot;
4849
const AccountSharedData = sig.runtime.AccountSharedData;
4950

51+
const SlotTracker = sig.replay.trackers.SlotTracker;
52+
const Commitment = sig.rpc.methods.common.Commitment;
53+
5054
const GeyserWriter = sig.geyser.GeyserWriter;
5155

5256
const Counter = sig.prometheus.counter.Counter;
@@ -2873,9 +2877,64 @@ pub const AccountsDB = struct {
28732877
return slotSatisfiesMax(slot, max_slot) and slotSatisfiesMin(slot, min_slot);
28742878
}
28752879

2876-
pub fn registerRPCHooks(self: *AccountsDB, rpc_hooks: *sig.rpc.Hooks) !void {
2880+
pub fn registerRPCHooks(
2881+
self: *AccountsDB,
2882+
rpc_hooks: *sig.rpc.Hooks,
2883+
slot_tracker: ?*const sig.replay.trackers.SlotTracker,
2884+
snapshot_slot: Slot,
2885+
) !void {
28772886
try rpc_hooks.set(self.allocator, struct {
28782887
accountsdb: *AccountsDB,
2888+
slot_tracker: ?*const sig.replay.trackers.SlotTracker,
2889+
snapshot_slot: Slot,
2890+
2891+
pub fn getBalance(
2892+
this: @This(),
2893+
_: std.mem.Allocator,
2894+
params: methods.GetBalance,
2895+
) !methods.GetBalance.Response {
2896+
const config = params.config orelse methods.common.CommitmentSlotConfig{};
2897+
2898+
const commitment = config.commitment orelse .finalized;
2899+
const commitment_slot: Slot = if (this.slot_tracker) |tracker|
2900+
tracker.getSlotForCommitment(commitment)
2901+
else
2902+
this.snapshot_slot;
2903+
const max_slot: ?Slot = if (commitment == .processed) null else commitment_slot;
2904+
2905+
// Check minContextSlot constraint.
2906+
if (config.minContextSlot) |min_slot| {
2907+
if (commitment_slot < min_slot) {
2908+
return error.RpcMinContextSlotNotMet;
2909+
}
2910+
}
2911+
2912+
// Look up account
2913+
const result = this.accountsdb.getSlotAndAccountInSlotRangeWithReadLock(
2914+
&params.pubkey,
2915+
config.minContextSlot,
2916+
max_slot,
2917+
) catch return error.AccountsDbError;
2918+
2919+
const lamports: u64 = if (result) |r| blk: {
2920+
const account, _, var account_lock = r;
2921+
defer account_lock.unlock();
2922+
2923+
break :blk switch (account) {
2924+
.file => |aif| aif.account_info.lamports,
2925+
.unrooted_map => |um| um.lamports,
2926+
};
2927+
// TODO: is defaulting to 0 the correct behavior here?
2928+
} else 0;
2929+
2930+
return .{
2931+
.context = .{
2932+
.slot = commitment_slot,
2933+
.apiVersion = "2.0.15",
2934+
},
2935+
.value = lamports,
2936+
};
2937+
}
28792938

28802939
pub fn getAccountInfo(
28812940
this: @This(),
@@ -3098,7 +3157,7 @@ pub const AccountsDB = struct {
30983157

30993158
return null;
31003159
}
3101-
}{ .accountsdb = self });
3160+
}{ .accountsdb = self, .slot_tracker = slot_tracker, .snapshot_slot = snapshot_slot });
31023161
}
31033162
};
31043163

src/cmd.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1997,9 +1997,10 @@ fn mockRpcServer(allocator: std.mem.Allocator, cfg: config.Cmd) !void {
19971997
defer manifest.deinit(allocator);
19981998
}
19991999

2000+
const snapshot_slot = if (snap_files.incremental_info) |inc| inc.slot else snap_files.full.slot;
20002001
var rpc_hooks = sig.rpc.Hooks{};
20012002
defer rpc_hooks.deinit(allocator);
2002-
try accountsdb.registerRPCHooks(&rpc_hooks);
2003+
try accountsdb.registerRPCHooks(&rpc_hooks, null, snapshot_slot);
20032004

20042005
var server_ctx = try sig.rpc.server.Context.init(.{
20052006
.allocator = allocator,

src/replay/trackers.zig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const EpochSchedule = sig.core.EpochSchedule;
1010
const Slot = sig.core.Slot;
1111
const SlotConstants = sig.core.SlotConstants;
1212
const SlotState = sig.core.SlotState;
13+
const Commitment = sig.rpc.methods.common.Commitment;
1314

1415
pub const ForkChoiceProcessedSlot = struct {
1516
slot: std.atomic.Value(Slot) = .init(0),
@@ -122,6 +123,14 @@ pub const SlotTracker = struct {
122123
return elem.toRef();
123124
}
124125

126+
pub fn getSlotForCommitment(self: *const SlotTracker, commitment: Commitment) Slot {
127+
return switch (commitment) {
128+
.processed => self.latest_processed_slot.get(),
129+
.confirmed => self.latest_confirmed_slot.get(),
130+
.finalized => self.root.load(.monotonic),
131+
};
132+
}
133+
125134
pub const GetOrPutResult = struct {
126135
found_existing: bool,
127136
reference: Reference,

src/rpc/server/server.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ test "serveSpawn getSnapshot" {
285285
var rpc_hooks = sig.rpc.Hooks{};
286286
defer rpc_hooks.deinit(accountsdb.allocator);
287287

288-
try accountsdb.registerRPCHooks(&rpc_hooks);
288+
try accountsdb.registerRPCHooks(&rpc_hooks, null, 0);
289289

290290
const sock_addr = std.net.Address.initIp4(.{ 0, 0, 0, 0 }, 0);
291291
var server_ctx = try Context.init(.{
@@ -440,7 +440,7 @@ test "serveSpawn getAccountInfo" {
440440

441441
var rpc_hooks = sig.rpc.Hooks{};
442442
defer rpc_hooks.deinit(allocator);
443-
try accountsdb.registerRPCHooks(&rpc_hooks);
443+
try accountsdb.registerRPCHooks(&rpc_hooks, null, expected_slot);
444444

445445
const test_sock_addr = std.net.Address.initIp4(.{ 0, 0, 0, 0 }, 0);
446446
var server_ctx = try Context.init(.{

0 commit comments

Comments
 (0)