From 739219799c2bf38592d5283f4dc741ac201094bf Mon Sep 17 00:00:00 2001 From: r4bbit <445106+0x-r4bbit@users.noreply.github.com> Date: Fri, 6 Feb 2026 11:34:54 +0100 Subject: [PATCH] Add PDAs for attestation account ID derivation --- Cargo.lock | 1 + examples/program_deployment/Cargo.toml | 1 + .../src/bin/run_attestation.rs | 49 +++++++++++++++---- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea0dca84..b2a39ff1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4604,6 +4604,7 @@ dependencies = [ "hex", "nssa", "nssa_core", + "sha2", "tokio", "wallet", ] diff --git a/examples/program_deployment/Cargo.toml b/examples/program_deployment/Cargo.toml index 81497226..5a8d4c6f 100644 --- a/examples/program_deployment/Cargo.toml +++ b/examples/program_deployment/Cargo.toml @@ -12,3 +12,4 @@ wallet.workspace = true tokio = { workspace = true, features = ["macros"] } clap.workspace = true hex.workspace = true +sha2.workspace = true diff --git a/examples/program_deployment/src/bin/run_attestation.rs b/examples/program_deployment/src/bin/run_attestation.rs index bd2578a9..0a7b14b7 100644 --- a/examples/program_deployment/src/bin/run_attestation.rs +++ b/examples/program_deployment/src/bin/run_attestation.rs @@ -4,6 +4,8 @@ use nssa::{ program::Program, public_transaction::{Message, WitnessSet}, }; +use nssa_core::program::PdaSeed; +use sha2::{Digest, Sha256}; use wallet::WalletCore; // Before running this example, compile the `attestation.rs` guest program with: @@ -20,11 +22,23 @@ use wallet::WalletCore; // Examples: // cargo run --bin run_attestation -- \ // methods/guest/target/riscv32im-risc0-zkvm-elf/docker/attestation.bin \ -// attest +// attest // // cargo run --bin run_attestation -- \ // methods/guest/target/riscv32im-risc0-zkvm-elf/docker/attestation.bin \ -// revoke +// revoke +// +// The attestation account ID is derived deterministically from (creator, subject, key). + +/// Derive the PDA seed from (creator, subject, key) by hashing the concatenation. +fn derive_attestation_seed(creator: &AccountId, subject: &AccountId, key: &[u8; 32]) -> PdaSeed { + let mut hasher = Sha256::new(); + hasher.update(creator.value()); + hasher.update(subject.value()); + hasher.update(key); + let hash: [u8; 32] = hasher.finalize().into(); + PdaSeed::new(hash) +} #[derive(Parser, Debug)] struct Cli { @@ -41,8 +55,6 @@ enum Command { Attest { /// Account ID of the creator (must be a self-owned public account) creator_id: String, - /// Account ID for the attestation record - attestation_id: String, /// Account ID of the subject being attested about subject_id: String, /// 32-byte attestation key as a hex string (64 hex chars) @@ -54,8 +66,10 @@ enum Command { Revoke { /// Account ID of the creator (must be a self-owned public account) creator_id: String, - /// Account ID of the attestation record to revoke - attestation_id: String, + /// Account ID of the subject + subject_id: String, + /// 32-byte attestation key as a hex string (64 hex chars) + key_hex: String, }, } @@ -73,19 +87,21 @@ async fn main() { match cli.command { Command::Attest { creator_id, - attestation_id, subject_id, key_hex, value_string, } => { let creator_id: AccountId = creator_id.parse().unwrap(); - let attestation_id: AccountId = attestation_id.parse().unwrap(); let subject_id: AccountId = subject_id.parse().unwrap(); let key_bytes: [u8; 32] = hex::decode(&key_hex) .expect("key_hex must be valid hex") .try_into() .expect("key_hex must decode to exactly 32 bytes"); + // Derive attestation account ID from (creator, subject, key) + let pda_seed = derive_attestation_seed(&creator_id, &subject_id, &key_bytes); + let attestation_id = AccountId::from((&program.id(), &pda_seed)); + let signing_key = wallet_core .storage() .user_data @@ -119,13 +135,24 @@ async fn main() { .send_tx_public(tx) .await .unwrap(); + + println!("Attestation account ID: {attestation_id}"); } Command::Revoke { creator_id, - attestation_id, + subject_id, + key_hex, } => { let creator_id: AccountId = creator_id.parse().unwrap(); - let attestation_id: AccountId = attestation_id.parse().unwrap(); + let subject_id: AccountId = subject_id.parse().unwrap(); + let key_bytes: [u8; 32] = hex::decode(&key_hex) + .expect("key_hex must be valid hex") + .try_into() + .expect("key_hex must decode to exactly 32 bytes"); + + // Derive attestation account ID from (creator, subject, key) + let pda_seed = derive_attestation_seed(&creator_id, &subject_id, &key_bytes); + let attestation_id = AccountId::from((&program.id(), &pda_seed)); let signing_key = wallet_core .storage() @@ -156,6 +183,8 @@ async fn main() { .send_tx_public(tx) .await .unwrap(); + + println!("Revoked attestation account ID: {attestation_id}"); } } }