diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8648436..33bd91d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,7 +19,7 @@ jobs: name: Run tests against Zig implementations strategy: matrix: - program: [helloworld, transfer-lamports, cpi, token] + program: [helloworld, transfer-lamports, cpi, pubkey, token] fail-fast: false runs-on: ubuntu-latest steps: @@ -53,7 +53,7 @@ jobs: name: Run tests against Rust implementations strategy: matrix: - program: [helloworld, transfer-lamports, cpi, token] + program: [helloworld, transfer-lamports, cpi, pubkey, token] fail-fast: false runs-on: ubuntu-latest steps: diff --git a/Cargo.lock b/Cargo.lock index 4182126..604d513 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4472,6 +4472,17 @@ dependencies = [ "solana-sdk", ] +[[package]] +name = "solana-program-rosetta-pubkey" +version = "1.0.0" +dependencies = [ + "solana-instruction", + "solana-program-entrypoint", + "solana-program-test", + "solana-pubkey", + "solana-sdk", +] + [[package]] name = "solana-program-rosetta-transfer-lamports" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index 5c11012..ed2795d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "cpi", "cpi/pinocchio", "helloworld", + "pubkey", "token", "transfer-lamports", "transfer-lamports/pinocchio" diff --git a/README.md b/README.md index 6cdee2e..14b8254 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,16 @@ Note: `create_program_address` consumes 1500 CUs, and `invoke` consumes 1000, so we can subtract 2500 CUs from each program to see the actual cost of the program logic. +### Pubkey + +A program to compare two `Pubkey` instances. This operation is very common in +on-chain programs, but it can be expensive. + +| Language | CU Usage | +| --- | --- | +| Rust | 14 | +| Zig | 15 | + ### Token A reduced instruction set from SPL-Token. Includes an entrypoint, instruction diff --git a/pubkey/Cargo.toml b/pubkey/Cargo.toml new file mode 100644 index 0000000..d296889 --- /dev/null +++ b/pubkey/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "solana-program-rosetta-pubkey" +version = "1.0.0" +edition = "2021" + +[features] +test-sbf = [] + +[dependencies] +solana-program-entrypoint = "2.1.0" +solana-pubkey = "2.1.0" + +[dev-dependencies] +solana-instruction = "2.1.0" +solana-program-test = "2.1.0" +solana-sdk = "2.1.0" + +[lib] +crate-type = ["cdylib", "lib"] + +[lints] +workspace = true diff --git a/pubkey/src/lib.rs b/pubkey/src/lib.rs new file mode 100644 index 0000000..a535040 --- /dev/null +++ b/pubkey/src/lib.rs @@ -0,0 +1,24 @@ +//! A program demonstrating a comparison of pubkeys +#![deny(missing_docs)] +#![allow(clippy::arithmetic_side_effects)] + +use solana_pubkey::Pubkey; + +/// Entrypoint for the program +#[no_mangle] +pub extern "C" fn entrypoint(input: *mut u8) -> u64 { + unsafe { + let key: &Pubkey = &*(input.add(16) as *const Pubkey); + let owner: &Pubkey = &*(input.add(16 + 32) as *const Pubkey); + + if *key == *owner { + 0 + } else { + 1 + } + } +} +#[cfg(target_os = "solana")] +#[no_mangle] +fn custom_panic(_info: &core::panic::PanicInfo<'_>) {} +solana_program_entrypoint::custom_heap_default!(); diff --git a/pubkey/tests/functional.rs b/pubkey/tests/functional.rs new file mode 100644 index 0000000..3044d72 --- /dev/null +++ b/pubkey/tests/functional.rs @@ -0,0 +1,39 @@ +use { + solana_instruction::{AccountMeta, Instruction}, + solana_program_test::*, + solana_pubkey::Pubkey, + solana_sdk::{account::Account, signature::Signer, transaction::Transaction}, +}; + +const PROGRAM_ID: Pubkey = Pubkey::from_str_const("PubkeyComp111111111111111111111111111111111"); +const TEST_KEY: Pubkey = Pubkey::from_str_const("PubkeyComp111111111111111111111111111111112"); + +#[tokio::test] +async fn correct_key() { + let mut program_test = ProgramTest::new( + option_env!("PROGRAM_NAME").unwrap_or("solana_program_rosetta_pubkey"), + PROGRAM_ID, + None, + ); + program_test.add_account( + TEST_KEY, + Account { + lamports: 100_000, + data: vec![0], + owner: TEST_KEY, + ..Account::default() + }, + ); + let (banks_client, payer, recent_blockhash) = program_test.start().await; + + let mut transaction = Transaction::new_with_payer( + &[Instruction::new_with_bincode( + PROGRAM_ID, + &(), + vec![AccountMeta::new_readonly(TEST_KEY, false)], + )], + Some(&payer.pubkey()), + ); + transaction.sign(&[&payer], recent_blockhash); + banks_client.process_transaction(transaction).await.unwrap(); +} diff --git a/pubkey/zig/build.zig b/pubkey/zig/build.zig new file mode 100644 index 0000000..050adba --- /dev/null +++ b/pubkey/zig/build.zig @@ -0,0 +1,15 @@ +const std = @import("std"); +const solana = @import("solana-program-sdk"); + +pub fn build(b: *std.Build) !void { + const target = b.resolveTargetQuery(solana.sbf_target); + const optimize = .ReleaseFast; + const program = b.addSharedLibrary(.{ + .name = "solana_program_rosetta_pubkey", + .root_source_file = b.path("main.zig"), + .target = target, + .optimize = optimize, + }); + _ = solana.buildProgram(b, program, target, optimize); + b.installArtifact(program); +} diff --git a/pubkey/zig/build.zig.zon b/pubkey/zig/build.zig.zon new file mode 100644 index 0000000..2410a7f --- /dev/null +++ b/pubkey/zig/build.zig.zon @@ -0,0 +1,30 @@ +.{ + .name = "solana-program-rosetta-pubkey-zig", + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.13.0", + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + .minimum_zig_version = "0.13.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + .@"solana-program-sdk" = .{ + .url = "https://github.com/joncinque/solana-program-sdk-zig/archive/refs/tags/v0.14.0.tar.gz", + .hash = "1220bdfa4ea1ab6330959ce4bc40feb5b39a7f98923a266a94b69e27fd20c3526786", + }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "main.zig", + "../../LICENSE", + "../../README.md", + }, +} diff --git a/pubkey/zig/main.zig b/pubkey/zig/main.zig new file mode 100644 index 0000000..75cb39d --- /dev/null +++ b/pubkey/zig/main.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const PublicKey = @import("solana-program-sdk").PublicKey; + +export fn entrypoint(input: [*]u8) u64 { + const id: *align(1) PublicKey = @ptrCast(input + 16); + const owner_id: *align(1) PublicKey = @ptrCast(input + 16 + 32); + if (id.equals(owner_id.*)) { + return 0; + } else { + return 1; + } +} diff --git a/transfer-lamports/src/entrypoint.rs b/transfer-lamports/src/entrypoint.rs deleted file mode 100644 index 3d5a0da..0000000 --- a/transfer-lamports/src/entrypoint.rs +++ /dev/null @@ -1,2 +0,0 @@ -//! Program entrypoint - diff --git a/transfer-lamports/src/processor.rs b/transfer-lamports/src/processor.rs deleted file mode 100644 index 8c9e8f3..0000000 --- a/transfer-lamports/src/processor.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![allow(clippy::arithmetic_side_effects)] -//! Program instruction processor - -use solana_program::{ - account_info::{next_account_info, AccountInfo}, - entrypoint::ProgramResult, - pubkey::Pubkey, -}; - -/// Instruction processor -pub fn process_instruction( - _program_id: &Pubkey, - accounts: &[AccountInfo], - instruction_data: &[u8], -) -> ProgramResult { - // Create an iterator to safely reference accounts in the slice - let account_info_iter = &mut accounts.iter(); - let transfer_amount = u64::from_le_bytes(instruction_data.try_into().unwrap()); - - // As part of the program specification the first account is the source - // account and the second is the destination account - let source_info = next_account_info(account_info_iter)?; - let destination_info = next_account_info(account_info_iter)?; - - // Withdraw five lamports from the source - **source_info.try_borrow_mut_lamports()? -= transfer_amount; - // Deposit five lamports into the destination - **destination_info.try_borrow_mut_lamports()? += transfer_amount; - - Ok(()) -}