diff --git a/.gitignore b/.gitignore index b335496..0419004 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ solana-zig/ zig-cache/ zig-out/ .zig-cache/ +.DS_Store diff --git a/program-test/pubkey/main.zig b/program-test/pubkey/main.zig index aef3de3..4169aea 100644 --- a/program-test/pubkey/main.zig +++ b/program-test/pubkey/main.zig @@ -1,7 +1,13 @@ const sol = @import("solana_program_sdk"); -export fn entrypoint(input: [*]u8) u64 { - const context = sol.Context.load(input) catch return 1; - sol.print("Hello zig program {s}", .{context.program_id}); - return 0; +fn processInstruction(program_id: *sol.PublicKey, accounts: []sol.Account, data: []const u8) sol.ProgramError!void { + _ = accounts; + _ = data; + sol.print("Hello zig program {s}", .{program_id}); + return; +} + +// Declare the program entrypoint +comptime { + sol.entrypoint(processInstruction); } diff --git a/program-test/tests/pubkey.rs b/program-test/tests/pubkey.rs index d8fda0f..9e86851 100644 --- a/program-test/tests/pubkey.rs +++ b/program-test/tests/pubkey.rs @@ -15,7 +15,7 @@ fn program_test() -> ProgramTest { #[tokio::test] async fn call() { let pt = program_test(); - let mut context = pt.start_with_context().await; + let context = pt.start_with_context().await; let blockhash = context.banks_client.get_latest_blockhash().await.unwrap(); let transaction = Transaction::new_signed_with_payer( &[Instruction { diff --git a/src/context.zig b/src/context.zig index deb0aaf..4385610 100644 --- a/src/context.zig +++ b/src/context.zig @@ -7,19 +7,30 @@ const PublicKey = @import("public_key.zig").PublicKey; pub const Context = struct { num_accounts: u64, - accounts: [64]Account, + // MAX support parse account number is 64 + accounts: [MAX_ACCOUNTS]Account, data: []const u8, program_id: *align(1) PublicKey, + // FUTURE: maybe future change this number + const MAX_ACCOUNTS = 64; + pub fn load(input: [*]u8) !Context { var ptr: [*]u8 = input; + // Get the number of accounts const num_accounts: *u64 = @ptrCast(@alignCast(ptr)); + // Check if the number of accounts is within the supported range + if (num_accounts.* > MAX_ACCOUNTS) { + return error.MaxAccountsExceeded; + } + // next ptr point to account data ptr += @sizeOf(u64); + // Account Parse var i: usize = 0; - var accounts: [64]Account = undefined; - while (i < num_accounts.*) { + var accounts: [MAX_ACCOUNTS]Account = undefined; + while (i < num_accounts.*) : (i += 1) { const data: *Account.Data = @ptrCast(@alignCast(ptr)); if (data.duplicate_index != std.math.maxInt(u8)) { ptr += @sizeOf(u64); @@ -29,7 +40,6 @@ pub const Context = struct { ptr += Account.DATA_HEADER + data.data_len + ACCOUNT_DATA_PADDING + @sizeOf(u64); ptr = @ptrFromInt(std.mem.alignForward(u64, @intFromPtr(ptr), @alignOf(u64))); } - i += 1; } const data_len: *u64 = @ptrCast(@alignCast(ptr)); diff --git a/src/entrypoint.zig b/src/entrypoint.zig new file mode 100644 index 0000000..b9033cb --- /dev/null +++ b/src/entrypoint.zig @@ -0,0 +1,22 @@ +const PublicKey = @import("public_key.zig").PublicKey; +const Account = @import("account.zig").Account; +const ProgramError = @import("error.zig").ProgramError; +const Context = @import("context.zig").Context; + +const processInstruction = fn (program_id: *PublicKey, accounts: []Account, data: []const u8) ProgramError!void; + +pub fn declareEntrypoint(comptime process_instruction: processInstruction) void { + const S = struct { + pub export fn entrypoint(input: [*]u8) callconv(.C) u64 { + var context = Context.load(input) catch return 1; + process_instruction(context.program_id, context.accounts[0..context.num_accounts], context.data) catch |err| return @intFromError(err); + return 0; + } + }; + _ = &S.entrypoint; +} + +/// Helper macro-like function for simple entrypoint declaration +pub inline fn entrypoint(comptime process_instruction: processInstruction) void { + declareEntrypoint(process_instruction); +} diff --git a/src/error.zig b/src/error.zig new file mode 100644 index 0000000..aed8cd0 --- /dev/null +++ b/src/error.zig @@ -0,0 +1,6 @@ +pub const ProgramError = error{ + AlreadyInUse, + InvalidAccountType, + Uninitialized, + IncorrectSize, +}; diff --git a/src/rent.zig b/src/rent.zig index 2fae570..90c47db 100644 --- a/src/rent.zig +++ b/src/rent.zig @@ -58,6 +58,9 @@ pub const Rent = struct { log.print("failed to get rent sysvar: error code {}", .{result}); return error.Unexpected; } + } else { + log.log("cannot get rent data in non-bpf context"); + return error.Unexpected; } return rent; } diff --git a/src/root.zig b/src/root.zig index d9452f2..bf7a088 100644 --- a/src/root.zig +++ b/src/root.zig @@ -11,6 +11,8 @@ pub usingnamespace @import("clock.zig"); pub usingnamespace @import("rent.zig"); pub usingnamespace @import("log.zig"); pub usingnamespace @import("hash.zig"); +pub usingnamespace @import("error.zig"); +pub usingnamespace @import("entrypoint.zig"); pub const blake3 = @import("blake3.zig"); //pub const system_program = @import("system_program.zig");