diff --git a/src/account.zig b/src/account.zig index eedbad9..8ac3ac8 100644 --- a/src/account.zig +++ b/src/account.zig @@ -4,29 +4,30 @@ const PublicKey = @import("public_key.zig").PublicKey; pub const ACCOUNT_DATA_PADDING = 10 * 1024; pub const Account = struct { + pub const DATA_HEADER = 88; /// A Solana account sliced from what is provided as inputs to the BPF virtual machine. - pub const Data = extern struct { + pub const Data = packed struct { duplicate_index: u8, - is_signer: bool, - is_writable: bool, - is_executable: bool, - _: [4]u8, + is_signer: u8, + is_writable: u8, + is_executable: u8, + original_data_len: u32, id: PublicKey, owner_id: PublicKey, lamports: u64, - data_len: usize, + data_len: u64, comptime { std.debug.assert(@offsetOf(Account.Data, "duplicate_index") == 0); std.debug.assert(@offsetOf(Account.Data, "is_signer") == 0 + 1); std.debug.assert(@offsetOf(Account.Data, "is_writable") == 0 + 1 + 1); std.debug.assert(@offsetOf(Account.Data, "is_executable") == 0 + 1 + 1 + 1); - std.debug.assert(@offsetOf(Account.Data, "_") == 0 + 1 + 1 + 1 + 1); + std.debug.assert(@offsetOf(Account.Data, "original_data_len") == 0 + 1 + 1 + 1 + 1); std.debug.assert(@offsetOf(Account.Data, "id") == 0 + 1 + 1 + 1 + 1 + 4); std.debug.assert(@offsetOf(Account.Data, "owner_id") == 0 + 1 + 1 + 1 + 1 + 4 + 32); std.debug.assert(@offsetOf(Account.Data, "lamports") == 0 + 1 + 1 + 1 + 1 + 4 + 32 + 32); std.debug.assert(@offsetOf(Account.Data, "data_len") == 0 + 1 + 1 + 1 + 1 + 4 + 32 + 32 + 8); - std.debug.assert(@sizeOf(Account.Data) == 1 + 1 + 1 + 1 + 4 + 32 + 32 + 8 + 8); + std.debug.assert(@bitSizeOf(Account.Data) == DATA_HEADER * 8); } }; @@ -63,30 +64,46 @@ pub const Account = struct { return self.ptr.owner_id; } + pub fn assign(self: Account, new_owner_id: PublicKey) void { + self.ptr.owner_id = new_owner_id; + } + pub fn data(self: Account) []u8 { - const data_ptr = @as([*]u8, @ptrFromInt(@intFromPtr(self.ptr))) + @sizeOf(Account.Data); + const data_ptr = @as([*]u8, @ptrFromInt(@intFromPtr(self.ptr))) + DATA_HEADER; return data_ptr[0..self.ptr.data_len]; } pub fn isWritable(self: Account) bool { - return self.ptr.is_writable; + return self.ptr.is_writable == 1; } pub fn isExecutable(self: Account) bool { - return self.ptr.is_executable; + return self.ptr.is_executable == 1; } pub fn isSigner(self: Account) bool { - return self.ptr.is_signer; + return self.ptr.is_signer == 1; } - pub fn dataLen(self: Account) usize { + pub fn dataLen(self: Account) u64 { return self.ptr.data_len; } + pub fn realloc(self: Account, new_data_len: u64) error.InvalidRealloc!void { + const diff = @subWithOverflow(new_data_len, self.original_data_len); + if (diff[1] == 0 and diff[0] > ACCOUNT_DATA_PADDING) { + return error.InvalidRealloc; + } + self.reallocUnchecked(new_data_len); + } + + pub fn reallocUnchecked(self: Account, new_data_len: u64) void { + self.ptr.data_len = new_data_len; + } + pub fn info(self: Account) Account.Info { - const data_ptr = @as([*]u8, @ptrFromInt(@intFromPtr(self.ptr))) + @sizeOf(Account.Data); - const rent_epoch = @as(*u64, @ptrFromInt(std.mem.alignForward(usize, @intFromPtr(self.ptr) + self.ptr.data_len + ACCOUNT_DATA_PADDING, @alignOf(usize)))); + const data_ptr = @as([*]u8, @ptrFromInt(@intFromPtr(self.ptr))) + DATA_HEADER; + const rent_epoch = @as(*u64, @ptrFromInt(std.mem.alignForward(u64, @intFromPtr(self.ptr) + self.ptr.data_len + ACCOUNT_DATA_PADDING, @alignOf(u64)))); return .{ .id = &self.ptr.id, diff --git a/src/context.zig b/src/context.zig index bdf9bde..d4667c4 100644 --- a/src/context.zig +++ b/src/context.zig @@ -9,7 +9,7 @@ pub const Context = struct { num_accounts: usize, accounts: [64]Account, data: []const u8, - program_id: *PublicKey, + program_id: *align(1) PublicKey, pub fn load(input: [*]u8) !Context { var ptr: [*]u8 = input; @@ -25,7 +25,7 @@ pub const Context = struct { ptr += @sizeOf(usize); accounts[i] = accounts[data.duplicate_index]; } else { - ptr += @sizeOf(Account.Data); + ptr += Account.DATA_HEADER; ptr = @as([*]u8, @ptrFromInt(std.mem.alignForward(usize, @intFromPtr(ptr) + data.data_len + ACCOUNT_DATA_PADDING, @alignOf(usize)))); ptr += @sizeOf(u64); accounts[i] = .{ .ptr = @as(*Account.Data, @ptrCast(@alignCast(data))) }; @@ -39,7 +39,7 @@ pub const Context = struct { const data = ptr[0..data_len]; ptr += data_len; - const program_id = @as(*PublicKey, @ptrCast(ptr)); + const program_id = @as(*align(1) PublicKey, @ptrCast(ptr)); ptr += @sizeOf(PublicKey); return Context{ diff --git a/src/public_key.zig b/src/public_key.zig index c61163d..8be20fd 100644 --- a/src/public_key.zig +++ b/src/public_key.zig @@ -13,17 +13,17 @@ pub const ProgramDerivedAddress = struct { bump_seed: [1]u8, }; -pub const PublicKey = extern struct { +pub const PublicKey = packed struct { pub const length: usize = 32; pub const base58_length: usize = 44; pub const max_num_seeds: usize = 16; pub const max_seed_length: usize = 32; - bytes: [PublicKey.length]u8, + bytes: u256, pub fn from(bytes: [PublicKey.length]u8) PublicKey { - return .{ .bytes = bytes }; + return .{ .bytes = mem.bytesToValue(u256, &bytes) }; } pub fn comptimeFromBase58(comptime encoded: []const u8) PublicKey { @@ -47,11 +47,11 @@ pub const PublicKey = extern struct { } pub fn equals(self: PublicKey, other: PublicKey) bool { - return mem.eql(u8, &self.bytes, &other.bytes); + return self.bytes == other.bytes; } pub fn isPointOnCurve(self: PublicKey) bool { - const Y = std.crypto.ecc.Curve25519.Fe.fromBytes(self.bytes); + const Y = std.crypto.ecc.Curve25519.Fe.fromBytes(mem.toBytes(self.bytes)); const Z = std.crypto.ecc.Curve25519.Fe.one; const YY = Y.sq(); const u = YY.sub(Z); @@ -125,9 +125,9 @@ pub const PublicKey = extern struct { inline while (i < seeds.len) : (i += 1) { hasher.update(seeds[i]); } - hasher.update(&program_id.bytes); + hasher.update(mem.asBytes(&program_id.bytes)); hasher.update("ProgramDerivedAddress"); - hasher.final(&address.bytes); + hasher.final(mem.asBytes(&address.bytes)); if (address.isPointOnCurve()) { return error.InvalidSeeds; @@ -222,7 +222,7 @@ pub const PublicKey = extern struct { _ = fmt; _ = options; var buffer: [base58.bitcoin.getEncodedLengthUpperBound(PublicKey.length)]u8 = undefined; - try writer.print("{s}", .{base58.bitcoin.encode(&buffer, &self.bytes)}); + try writer.print("{s}", .{base58.bitcoin.encode(&buffer, mem.asBytes(&self.bytes))}); } }; diff --git a/src/rent.zig b/src/rent.zig index 0014387..2fae570 100644 --- a/src/rent.zig +++ b/src/rent.zig @@ -21,7 +21,7 @@ pub const Rent = struct { /// Account storage overhead for calculation of base rent. pub const account_storage_overhead: u64 = 128; - pub const Data = extern struct { + pub const Data = packed struct { lamports_per_byte_year: u64 = Rent.default_lamports_per_byte_year, exemption_threshold: f64 = Rent.default_exemption_threshold, burn_percent: u8 = Rent.default_burn_percent, @@ -43,7 +43,7 @@ pub const Rent = struct { pub fn getMinimumBalance(self: Rent.Data, data_len: usize) u64 { const total_data_len: u64 = Rent.account_storage_overhead + data_len; - return @intFromFloat(@as(f64, @floatFromInt(total_data_len * self.lamports_per_byte_year)) * self.exemption_threshold); + return total_data_len * self.lamports_per_byte_year * @as(u64, @intFromFloat(self.exemption_threshold)); } };