Skip to content

fix(runtime): vote program conformance #714

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 7, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
first vote bugs
  • Loading branch information
yewman committed May 6, 2025
commit 54861e40d00e3596be4e04b6f57c6fa2b2635faf
3 changes: 2 additions & 1 deletion src/runtime/borrowed_account.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const Pubkey = sig.core.Pubkey;

const AccountSharedData = sig.runtime.AccountSharedData;
const TransactionContext = sig.runtime.TransactionContext;
const Rent = sig.runtime.sysvar.Rent;
const WLockGuard = sig.runtime.TransactionContextAccount.WLockGuard;

const MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION =
Expand Down Expand Up @@ -228,7 +229,7 @@ pub const BorrowedAccount = struct {
pub fn setExecutable(
self: *BorrowedAccount,
executable: bool,
rent: sysvar.Rent,
rent: Rent,
) InstructionError!void {
if (!rent.isExempt(self.account.lamports, self.account.data.len) or
!self.account.owner.equals(&self.context.program_id) or
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/program/bpf_loader/execute.zig
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ pub fn executeV3DeployWithMaxDataLen(
} });
try program_account.setExecutable(
true,
try ic.sc.sysvar_cache.get(sysvar.Rent),
ic.tc.rent,
);
}

Expand Down
47 changes: 42 additions & 5 deletions src/runtime/program/vote/execute.zig
Original file line number Diff line number Diff line change
Expand Up @@ -292,22 +292,20 @@ fn authorize(

switch (vote_authorize) {
.voter => {
const current_epoch = clock.epoch;

const authorized_withdrawer_signer = !std.meta.isError(validateIsSigner(
vote_state.withdrawer,
signers,
));

// [agave] https://github.com/anza-xyz/agave/blob/01e50dc39bde9a37a9f15d64069459fe7502ec3e/programs/vote/src/vote_state/mod.rs#L697-L701
const target_epoch = std.math.add(u64, current_epoch, 1) catch {
const target_epoch = std.math.add(u64, clock.leader_schedule_epoch, 1) catch {
return InstructionError.InvalidAccountData;
};

// [agave] https://github.com/anza-xyz/solana-sdk/blob/4e30766b8d327f0191df6490e48d9ef521956495/vote-interface/src/state/mod.rs#L872
const epoch_authorized_voter = try vote_state.getAndUpdateAuthorizedVoter(
allocator,
current_epoch,
clock.epoch,
);

// [agave] https://github.com/anza-xyz/agave/blob/01e50dc39bde9a37a9f15d64069459fe7502ec3e/programs/vote/src/vote_state/mod.rs#L701-L709
Expand Down Expand Up @@ -342,7 +340,14 @@ fn authorize(
vote_state.withdrawer = authorized;
},
}
try vote_account.serializeIntoAccountData(VoteStateVersions{ .current = vote_state });

try setVoteState(
allocator,
vote_account,
&vote_state,
ic.tc.rent,
&ic.tc.accounts_resize_delta,
);
}

/// [agave] https://github.com/anza-xyz/agave/blob/0603d1cbc3ac6737df8c9e587c1b7a5c870e90f4/programs/vote/src/vote_processor.rs#L82-L92
Expand Down Expand Up @@ -980,6 +985,38 @@ fn validateIsSigner(
return InstructionError.MissingRequiredSignature;
}

fn setVoteState(
allocator: std.mem.Allocator,
account: *BorrowedAccount,
state: *VoteState,
rent: Rent,
resize_delta: *i64,
) (error{OutOfMemory} || InstructionError)!void {
if (account.constAccountData().len < VoteState.MAX_VOTE_STATE_SIZE and
(!rent.isExempt(account.account.lamports, VoteState.MAX_VOTE_STATE_SIZE) or
std.meta.isError(account.setDataLength(
allocator,
resize_delta,
VoteState.MAX_VOTE_STATE_SIZE,
)))) {
var votes = try std.ArrayList(Lockout).initCapacity(allocator, state.votes.items.len);
defer votes.deinit();
for (state.votes.items) |vote| votes.appendAssumeCapacity(vote.lockout);
return account.serializeIntoAccountData(VoteStateVersions{ .v1_14_11 = .{
.node_pubkey = state.node_pubkey,
.withdrawer = state.withdrawer,
.commission = state.commission,
.votes = votes,
.root_slot = state.root_slot,
.voters = state.voters,
.prior_voters = state.prior_voters,
.epoch_credits = state.epoch_credits,
.last_timestamp = state.last_timestamp,
} });
}
return account.serializeIntoAccountData(VoteStateVersions{ .current = state.* });
}

// [agave] https://github.com/anza-xyz/agave/blob/bdba5c5f93eeb6b981d41ea3c14173eb36879d3c/programs/vote/src/vote_state/mod.rs#L3659
test "isCommissionUpdateAllowed epoch half check" {
const DEFAULT_SLOTS_PER_EPOCH = sig.core.time.DEFAULT_SLOTS_PER_EPOCH;
Expand Down
69 changes: 68 additions & 1 deletion src/runtime/program/vote/state.zig
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ pub const TowerSync = struct {
pub const AuthorizedVoters = struct {
voters: SortedMap(Epoch, Pubkey),

pub const @"!bincode-config": sig.bincode.FieldConfig(AuthorizedVoters) = .{
.deserializer = deserialize,
.serializer = serialize,
};

pub fn init(allocator: std.mem.Allocator, epoch: Epoch, pubkey: Pubkey) !AuthorizedVoters {
var authorized_voters = SortedMap(Epoch, Pubkey).init(allocator);
try authorized_voters.put(epoch, pubkey);
Expand Down Expand Up @@ -294,6 +299,33 @@ pub const AuthorizedVoters = struct {
return .{ last_voter, false };
}
}

fn deserialize(allocator: std.mem.Allocator, reader: anytype, _: sig.bincode.Params) !AuthorizedVoters {
var authorized_voters = AuthorizedVoters{
.voters = SortedMap(Epoch, Pubkey).init(allocator),
};
errdefer authorized_voters.deinit();

for (try reader.readInt(usize, std.builtin.Endian.little)) |_| {
const epoch = try reader.readInt(u64, std.builtin.Endian.little);
var pubkey = Pubkey.ZEROES;
const bytes_read = try reader.readAll(&pubkey.data);
if (bytes_read != Pubkey.SIZE) return error.NoBytesLeft;
try authorized_voters.voters.put(epoch, pubkey);
}

return authorized_voters;
}

pub fn serialize(writer: anytype, data: anytype, _: sig.bincode.Params) !void {
var authorized_voters: AuthorizedVoters = data;
try writer.writeInt(usize, authorized_voters.len(), std.builtin.Endian.little);
const items = authorized_voters.voters.items();
for (items[0], items[1]) |k, v| {
try writer.writeInt(u64, k, std.builtin.Endian.little);
try writer.writeAll(&v.data);
}
}
};

/// [agave] https://github.com/anza-xyz/solana-sdk/blob/4e30766b8d327f0191df6490e48d9ef521956495/vote-interface/src/state/vote_state_versions.rs#L20
Expand Down Expand Up @@ -1585,8 +1617,8 @@ pub const VoteState = struct {
};

pub const VoteAuthorize = enum {
withdrawer,
voter,
withdrawer,
};

pub fn createTestVoteState(
Expand Down Expand Up @@ -1642,6 +1674,41 @@ pub fn verifyAndGetVoteState(
return vote_state;
}

test "AuthorizeVoters.deserialize" {
const data_0 = &[_]u8{
3, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,

1, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,

2, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 4,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
const allocator = std.testing.allocator;
const authorized_voters = try sig.bincode.readFromSlice(allocator, AuthorizedVoters, data_0, .{});
defer authorized_voters.deinit();
try std.testing.expectEqual(3, authorized_voters.count());

const data_1 = try sig.bincode.writeAlloc(allocator, authorized_voters, .{});
defer allocator.free(data_1);
try std.testing.expectEqualSlices(u8, data_0, data_1);
}

test "Lockout.lockout" {
{
const lockout = Lockout{
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/testing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const SlotContext = sig.runtime.transaction_context.SlotContext;
const TransactionContext = sig.runtime.TransactionContext;
const TransactionContextAccount = sig.runtime.TransactionContextAccount;
const TransactionReturnData = sig.runtime.transaction_context.TransactionReturnData;
const Rent = sig.runtime.sysvar.Rent;
const ComputeBudget = sig.runtime.ComputeBudget;

pub const ExecuteContextsParams = struct {
Expand Down Expand Up @@ -133,6 +134,7 @@ pub fn createExecutionContexts(
.compute_meter = params.compute_meter,
.compute_budget = params.compute_budget,
.custom_error = params.custom_error,
.rent = Rent.DEFAULT,
.log_collector = params.log_collector,
.prev_blockhash = params.prev_blockhash,
.prev_lamports_per_signature = params.prev_lamports_per_signature,
Expand Down
4 changes: 4 additions & 0 deletions src/runtime/transaction_context.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const SysvarCache = sig.runtime.SysvarCache;
const InstructionContext = sig.runtime.InstructionContext;
const InstructionInfo = sig.runtime.InstructionInfo;
const ComputeBudget = sig.runtime.ComputeBudget;
const Rent = sig.runtime.sysvar.Rent;
const SerializedAccountMetadata = sig.runtime.program.bpf.serialize.SerializedAccountMeta;

// https://github.com/anza-xyz/agave/blob/0d34a1a160129c4293dac248e14231e9e773b4ce/program-runtime/src/compute_budget.rs#L139
Expand Down Expand Up @@ -96,6 +97,9 @@ pub const TransactionContext = struct {
/// Optional log collector
log_collector: ?LogCollector,

/// Rent
rent: Rent,

/// Previous blockhash and lamports per signature from the blockhash queue
prev_blockhash: Hash,
prev_lamports_per_signature: u64,
Expand Down
2 changes: 2 additions & 0 deletions src/vm/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const SlotContext = sig.runtime.transaction_context.SlotContext;
const TransactionContext = sig.runtime.TransactionContext;
const FeatureSet = sig.runtime.FeatureSet;
const Hash = sig.core.Hash;
const Rent = sig.runtime.sysvar.Rent;
const ComputeBudget = sig.runtime.ComputeBudget;

pub fn main() !void {
Expand Down Expand Up @@ -66,6 +67,7 @@ pub fn main() !void {
.accounts_resize_delta = 0,
.return_data = .{},
.custom_error = null,
.rent = Rent.DEFAULT,
.log_collector = null,
.compute_meter = cmd.limit,
.prev_blockhash = Hash.ZEROES,
Expand Down