Skip to content

Commit a80c326

Browse files
committed
pubkey: Add a simple program comparing two pubkeys
#### Problem It can be expensive to compare two pubkeys in an on-chain program, but it hasn't been quantified. #### Summary of changes Add a Rust and Zig program to do that
1 parent 1f32e5d commit a80c326

File tree

12 files changed

+166
-35
lines changed

12 files changed

+166
-35
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
name: Run tests against Zig implementations
2020
strategy:
2121
matrix:
22-
program: [helloworld, transfer-lamports, cpi, token]
22+
program: [helloworld, transfer-lamports, cpi, pubkey, token]
2323
fail-fast: false
2424
runs-on: ubuntu-latest
2525
steps:
@@ -53,7 +53,7 @@ jobs:
5353
name: Run tests against Rust implementations
5454
strategy:
5555
matrix:
56-
program: [helloworld, transfer-lamports, cpi, token]
56+
program: [helloworld, transfer-lamports, cpi, pubkey, token]
5757
fail-fast: false
5858
runs-on: ubuntu-latest
5959
steps:

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ members = [
33
"cpi",
44
"cpi/pinocchio",
55
"helloworld",
6+
"pubkey",
67
"token",
78
"transfer-lamports",
89
"transfer-lamports/pinocchio"

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,16 @@ Note: `create_program_address` consumes 1500 CUs, and `invoke` consumes 1000, so
194194
we can subtract 2500 CUs from each program to see the actual cost of the program
195195
logic.
196196

197+
### Pubkey
198+
199+
A program to compare two `Pubkey` instances. This operation is very common in
200+
on-chain programs, but it can be expensive.
201+
202+
| Language | CU Usage |
203+
| --- | --- |
204+
| Rust | 14 |
205+
| Zig | 15 |
206+
197207
### Token
198208

199209
A reduced instruction set from SPL-Token. Includes an entrypoint, instruction

pubkey/Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "solana-program-rosetta-pubkey"
3+
version = "1.0.0"
4+
edition = "2021"
5+
6+
[features]
7+
test-sbf = []
8+
9+
[dependencies]
10+
solana-program-entrypoint = "2.1.0"
11+
solana-pubkey = "2.1.0"
12+
13+
[dev-dependencies]
14+
solana-instruction = "2.1.0"
15+
solana-program-test = "2.1.0"
16+
solana-sdk = "2.1.0"
17+
18+
[lib]
19+
crate-type = ["cdylib", "lib"]
20+
21+
[lints]
22+
workspace = true

pubkey/src/lib.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//! A program demonstrating a comparison of pubkeys
2+
#![deny(missing_docs)]
3+
#![allow(clippy::arithmetic_side_effects)]
4+
5+
use solana_pubkey::Pubkey;
6+
7+
/// Entrypoint for the program
8+
#[no_mangle]
9+
pub extern "C" fn entrypoint(input: *mut u8) -> u64 {
10+
unsafe {
11+
let key: &Pubkey = &*(input.add(16) as *const Pubkey);
12+
let owner: &Pubkey = &*(input.add(16 + 32) as *const Pubkey);
13+
14+
if *key == *owner {
15+
0
16+
} else {
17+
1
18+
}
19+
}
20+
}
21+
#[cfg(target_os = "solana")]
22+
#[no_mangle]
23+
fn custom_panic(_info: &core::panic::PanicInfo<'_>) {}
24+
solana_program_entrypoint::custom_heap_default!();

pubkey/tests/functional.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use {
2+
solana_instruction::{AccountMeta, Instruction},
3+
solana_program_test::*,
4+
solana_pubkey::Pubkey,
5+
solana_sdk::{account::Account, signature::Signer, transaction::Transaction},
6+
};
7+
8+
const PROGRAM_ID: Pubkey = Pubkey::from_str_const("PubkeyComp111111111111111111111111111111111");
9+
const TEST_KEY: Pubkey = Pubkey::from_str_const("PubkeyComp111111111111111111111111111111112");
10+
11+
#[tokio::test]
12+
async fn correct_key() {
13+
let mut program_test = ProgramTest::new(
14+
option_env!("PROGRAM_NAME").unwrap_or("solana_program_rosetta_pubkey"),
15+
PROGRAM_ID,
16+
None,
17+
);
18+
program_test.add_account(
19+
TEST_KEY,
20+
Account {
21+
lamports: 100_000,
22+
data: vec![0],
23+
owner: TEST_KEY,
24+
..Account::default()
25+
},
26+
);
27+
let (banks_client, payer, recent_blockhash) = program_test.start().await;
28+
29+
let mut transaction = Transaction::new_with_payer(
30+
&[Instruction::new_with_bincode(
31+
PROGRAM_ID,
32+
&(),
33+
vec![AccountMeta::new_readonly(TEST_KEY, false)],
34+
)],
35+
Some(&payer.pubkey()),
36+
);
37+
transaction.sign(&[&payer], recent_blockhash);
38+
banks_client.process_transaction(transaction).await.unwrap();
39+
}

pubkey/zig/build.zig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const std = @import("std");
2+
const solana = @import("solana-program-sdk");
3+
4+
pub fn build(b: *std.Build) !void {
5+
const target = b.resolveTargetQuery(solana.sbf_target);
6+
const optimize = .ReleaseFast;
7+
const program = b.addSharedLibrary(.{
8+
.name = "solana_program_rosetta_pubkey",
9+
.root_source_file = b.path("main.zig"),
10+
.target = target,
11+
.optimize = optimize,
12+
});
13+
_ = solana.buildProgram(b, program, target, optimize);
14+
b.installArtifact(program);
15+
}

pubkey/zig/build.zig.zon

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
.{
2+
.name = "solana-program-rosetta-pubkey-zig",
3+
// This is a [Semantic Version](https://semver.org/).
4+
// In a future version of Zig it will be used for package deduplication.
5+
.version = "0.13.0",
6+
7+
// This field is optional.
8+
// This is currently advisory only; Zig does not yet do anything
9+
// with this value.
10+
.minimum_zig_version = "0.13.0",
11+
12+
// This field is optional.
13+
// Each dependency must either provide a `url` and `hash`, or a `path`.
14+
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
15+
// Once all dependencies are fetched, `zig build` no longer requires
16+
// internet connectivity.
17+
.dependencies = .{
18+
.@"solana-program-sdk" = .{
19+
.url = "https://github.com/joncinque/solana-program-sdk-zig/archive/refs/tags/v0.14.0.tar.gz",
20+
.hash = "1220bdfa4ea1ab6330959ce4bc40feb5b39a7f98923a266a94b69e27fd20c3526786",
21+
},
22+
},
23+
.paths = .{
24+
"build.zig",
25+
"build.zig.zon",
26+
"main.zig",
27+
"../../LICENSE",
28+
"../../README.md",
29+
},
30+
}

pubkey/zig/main.zig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const std = @import("std");
2+
const PublicKey = @import("solana-program-sdk").PublicKey;
3+
4+
export fn entrypoint(input: [*]u8) u64 {
5+
const id: *align(1) PublicKey = @ptrCast(input + 16);
6+
const owner_id: *align(1) PublicKey = @ptrCast(input + 16 + 32);
7+
if (id.equals(owner_id.*)) {
8+
return 0;
9+
} else {
10+
return 1;
11+
}
12+
}

0 commit comments

Comments
 (0)