diff --git a/artifacts/program_methods/amm.bin b/artifacts/program_methods/amm.bin index e512196f..1d440d26 100644 Binary files a/artifacts/program_methods/amm.bin and b/artifacts/program_methods/amm.bin differ diff --git a/artifacts/program_methods/associated_token_account.bin b/artifacts/program_methods/associated_token_account.bin index e6a154cd..51b5033b 100644 Binary files a/artifacts/program_methods/associated_token_account.bin and b/artifacts/program_methods/associated_token_account.bin differ diff --git a/artifacts/program_methods/authenticated_transfer.bin b/artifacts/program_methods/authenticated_transfer.bin index 0c7a91d3..d70c11c7 100644 Binary files a/artifacts/program_methods/authenticated_transfer.bin and b/artifacts/program_methods/authenticated_transfer.bin differ diff --git a/artifacts/program_methods/clock.bin b/artifacts/program_methods/clock.bin index 99b35010..9d237c3b 100644 Binary files a/artifacts/program_methods/clock.bin and b/artifacts/program_methods/clock.bin differ diff --git a/artifacts/program_methods/pinata.bin b/artifacts/program_methods/pinata.bin index 0a0d44dc..1c5dcd79 100644 Binary files a/artifacts/program_methods/pinata.bin and b/artifacts/program_methods/pinata.bin differ diff --git a/artifacts/program_methods/pinata_token.bin b/artifacts/program_methods/pinata_token.bin index b2a2aa03..f3f385fd 100644 Binary files a/artifacts/program_methods/pinata_token.bin and b/artifacts/program_methods/pinata_token.bin differ diff --git a/artifacts/program_methods/privacy_preserving_circuit.bin b/artifacts/program_methods/privacy_preserving_circuit.bin index 8f9088e1..874eae64 100644 Binary files a/artifacts/program_methods/privacy_preserving_circuit.bin and b/artifacts/program_methods/privacy_preserving_circuit.bin differ diff --git a/artifacts/program_methods/token.bin b/artifacts/program_methods/token.bin index fdad244e..eab13cb2 100644 Binary files a/artifacts/program_methods/token.bin and b/artifacts/program_methods/token.bin differ diff --git a/artifacts/test_program_methods/auth_asserting_noop.bin b/artifacts/test_program_methods/auth_asserting_noop.bin index 8c82790e..dd667593 100644 Binary files a/artifacts/test_program_methods/auth_asserting_noop.bin and b/artifacts/test_program_methods/auth_asserting_noop.bin differ diff --git a/artifacts/test_program_methods/burner.bin b/artifacts/test_program_methods/burner.bin index 7b2ce5b1..92415e08 100644 Binary files a/artifacts/test_program_methods/burner.bin and b/artifacts/test_program_methods/burner.bin differ diff --git a/artifacts/test_program_methods/chain_caller.bin b/artifacts/test_program_methods/chain_caller.bin index 26336b78..573356a4 100644 Binary files a/artifacts/test_program_methods/chain_caller.bin and b/artifacts/test_program_methods/chain_caller.bin differ diff --git a/artifacts/test_program_methods/changer_claimer.bin b/artifacts/test_program_methods/changer_claimer.bin index d307650d..ad70a3a8 100644 Binary files a/artifacts/test_program_methods/changer_claimer.bin and b/artifacts/test_program_methods/changer_claimer.bin differ diff --git a/artifacts/test_program_methods/claimer.bin b/artifacts/test_program_methods/claimer.bin index 98d525dd..c32bfaf0 100644 Binary files a/artifacts/test_program_methods/claimer.bin and b/artifacts/test_program_methods/claimer.bin differ diff --git a/artifacts/test_program_methods/clock_chain_caller.bin b/artifacts/test_program_methods/clock_chain_caller.bin index e7ec85ee..6e4ce527 100644 Binary files a/artifacts/test_program_methods/clock_chain_caller.bin and b/artifacts/test_program_methods/clock_chain_caller.bin differ diff --git a/artifacts/test_program_methods/data_changer.bin b/artifacts/test_program_methods/data_changer.bin index 02d7511a..5c912e62 100644 Binary files a/artifacts/test_program_methods/data_changer.bin and b/artifacts/test_program_methods/data_changer.bin differ diff --git a/artifacts/test_program_methods/extra_output.bin b/artifacts/test_program_methods/extra_output.bin index 074ecc8d..4d1d519e 100644 Binary files a/artifacts/test_program_methods/extra_output.bin and b/artifacts/test_program_methods/extra_output.bin differ diff --git a/artifacts/test_program_methods/flash_swap_callback.bin b/artifacts/test_program_methods/flash_swap_callback.bin index 36d10f3d..e83d0848 100644 Binary files a/artifacts/test_program_methods/flash_swap_callback.bin and b/artifacts/test_program_methods/flash_swap_callback.bin differ diff --git a/artifacts/test_program_methods/flash_swap_initiator.bin b/artifacts/test_program_methods/flash_swap_initiator.bin index 2c4f85f8..3884f114 100644 Binary files a/artifacts/test_program_methods/flash_swap_initiator.bin and b/artifacts/test_program_methods/flash_swap_initiator.bin differ diff --git a/artifacts/test_program_methods/malicious_authorization_changer.bin b/artifacts/test_program_methods/malicious_authorization_changer.bin index 387f3994..8eb33bb0 100644 Binary files a/artifacts/test_program_methods/malicious_authorization_changer.bin and b/artifacts/test_program_methods/malicious_authorization_changer.bin differ diff --git a/artifacts/test_program_methods/malicious_caller_program_id.bin b/artifacts/test_program_methods/malicious_caller_program_id.bin index 57408dc0..083e7a9f 100644 Binary files a/artifacts/test_program_methods/malicious_caller_program_id.bin and b/artifacts/test_program_methods/malicious_caller_program_id.bin differ diff --git a/artifacts/test_program_methods/malicious_self_program_id.bin b/artifacts/test_program_methods/malicious_self_program_id.bin index 5fd89718..a3a8694f 100644 Binary files a/artifacts/test_program_methods/malicious_self_program_id.bin and b/artifacts/test_program_methods/malicious_self_program_id.bin differ diff --git a/artifacts/test_program_methods/minter.bin b/artifacts/test_program_methods/minter.bin index 2d828a6d..21f27ad7 100644 Binary files a/artifacts/test_program_methods/minter.bin and b/artifacts/test_program_methods/minter.bin differ diff --git a/artifacts/test_program_methods/missing_output.bin b/artifacts/test_program_methods/missing_output.bin index 136e1755..89690cce 100644 Binary files a/artifacts/test_program_methods/missing_output.bin and b/artifacts/test_program_methods/missing_output.bin differ diff --git a/artifacts/test_program_methods/modified_transfer.bin b/artifacts/test_program_methods/modified_transfer.bin index 0ebf3385..d63c1114 100644 Binary files a/artifacts/test_program_methods/modified_transfer.bin and b/artifacts/test_program_methods/modified_transfer.bin differ diff --git a/artifacts/test_program_methods/nonce_changer.bin b/artifacts/test_program_methods/nonce_changer.bin index 3e83434c..061fe244 100644 Binary files a/artifacts/test_program_methods/nonce_changer.bin and b/artifacts/test_program_methods/nonce_changer.bin differ diff --git a/artifacts/test_program_methods/noop.bin b/artifacts/test_program_methods/noop.bin index 4778dd92..a089fcf1 100644 Binary files a/artifacts/test_program_methods/noop.bin and b/artifacts/test_program_methods/noop.bin differ diff --git a/artifacts/test_program_methods/pda_claimer.bin b/artifacts/test_program_methods/pda_claimer.bin index 6027eebc..528c590f 100644 Binary files a/artifacts/test_program_methods/pda_claimer.bin and b/artifacts/test_program_methods/pda_claimer.bin differ diff --git a/artifacts/test_program_methods/pinata_cooldown.bin b/artifacts/test_program_methods/pinata_cooldown.bin index 884cfad6..22beb033 100644 Binary files a/artifacts/test_program_methods/pinata_cooldown.bin and b/artifacts/test_program_methods/pinata_cooldown.bin differ diff --git a/artifacts/test_program_methods/private_pda_delegator.bin b/artifacts/test_program_methods/private_pda_delegator.bin index 1fa13515..f9140423 100644 Binary files a/artifacts/test_program_methods/private_pda_delegator.bin and b/artifacts/test_program_methods/private_pda_delegator.bin differ diff --git a/artifacts/test_program_methods/program_owner_changer.bin b/artifacts/test_program_methods/program_owner_changer.bin index 1a4d101b..53e4d68d 100644 Binary files a/artifacts/test_program_methods/program_owner_changer.bin and b/artifacts/test_program_methods/program_owner_changer.bin differ diff --git a/artifacts/test_program_methods/simple_balance_transfer.bin b/artifacts/test_program_methods/simple_balance_transfer.bin index 465427fe..af2d91fd 100644 Binary files a/artifacts/test_program_methods/simple_balance_transfer.bin and b/artifacts/test_program_methods/simple_balance_transfer.bin differ diff --git a/artifacts/test_program_methods/time_locked_transfer.bin b/artifacts/test_program_methods/time_locked_transfer.bin index 2a18f0d3..b990c758 100644 Binary files a/artifacts/test_program_methods/time_locked_transfer.bin and b/artifacts/test_program_methods/time_locked_transfer.bin differ diff --git a/artifacts/test_program_methods/two_pda_claimer.bin b/artifacts/test_program_methods/two_pda_claimer.bin index 1739c26a..223c596d 100644 Binary files a/artifacts/test_program_methods/two_pda_claimer.bin and b/artifacts/test_program_methods/two_pda_claimer.bin differ diff --git a/artifacts/test_program_methods/validity_window.bin b/artifacts/test_program_methods/validity_window.bin index 491cc858..d7fccfec 100644 Binary files a/artifacts/test_program_methods/validity_window.bin and b/artifacts/test_program_methods/validity_window.bin differ diff --git a/artifacts/test_program_methods/validity_window_chain_caller.bin b/artifacts/test_program_methods/validity_window_chain_caller.bin index c190dd88..2ea73fe0 100644 Binary files a/artifacts/test_program_methods/validity_window_chain_caller.bin and b/artifacts/test_program_methods/validity_window_chain_caller.bin differ diff --git a/docs/LEZ testnet v0.1 tutorials/associated-token-accounts.md b/docs/LEZ testnet v0.1 tutorials/associated-token-accounts.md index 330ae909..7ed95e01 100644 --- a/docs/LEZ testnet v0.1 tutorials/associated-token-accounts.md +++ b/docs/LEZ testnet v0.1 tutorials/associated-token-accounts.md @@ -52,7 +52,7 @@ The derivation works as follows: ``` seed = SHA256(owner_id || definition_id) -ata_address = AccountId::from((ata_program_id, seed)) +ata_address = AccountId::for_public_pda(ata_program_id, seed) ``` Because the computation is pure, anyone who knows the owner and definition can reproduce the exact same ATA address — no network call required. diff --git a/examples/program_deployment/src/bin/run_hello_world_with_authorization_through_tail_call_with_pda.rs b/examples/program_deployment/src/bin/run_hello_world_with_authorization_through_tail_call_with_pda.rs index e6a8ca99..86c95ebf 100644 --- a/examples/program_deployment/src/bin/run_hello_world_with_authorization_through_tail_call_with_pda.rs +++ b/examples/program_deployment/src/bin/run_hello_world_with_authorization_through_tail_call_with_pda.rs @@ -46,7 +46,7 @@ async fn main() { let program = Program::new(bytecode).unwrap(); // Compute the PDA to pass it as input account to the public execution - let pda = AccountId::from((&program.id(), &PDA_SEED)); + let pda = AccountId::for_public_pda(&program.id(), &PDA_SEED); let account_ids = vec![pda]; let instruction_data = (); let nonces = vec![]; diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index 5d78ce79..80cffcb2 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -37,8 +37,10 @@ impl PdaSeed { } } -impl From<(&ProgramId, &PdaSeed)> for AccountId { - fn from(value: (&ProgramId, &PdaSeed)) -> Self { +impl AccountId { + /// Derives an [`AccountId`] for a public PDA from the program ID and seed. + #[must_use] + pub fn for_public_pda(program_id: &ProgramId, seed: &PdaSeed) -> Self { use risc0_zkvm::sha::{Impl, Sha256 as _}; const PROGRAM_DERIVED_ACCOUNT_ID_PREFIX: &[u8; 32] = b"/NSSA/v0.2/AccountId/PDA/\x00\x00\x00\x00\x00\x00\x00"; @@ -46,9 +48,38 @@ impl From<(&ProgramId, &PdaSeed)> for AccountId { let mut bytes = [0; 96]; bytes[0..32].copy_from_slice(PROGRAM_DERIVED_ACCOUNT_ID_PREFIX); let program_id_bytes: &[u8] = - bytemuck::try_cast_slice(value.0).expect("ProgramId should be castable to &[u8]"); + bytemuck::try_cast_slice(program_id).expect("ProgramId should be castable to &[u8]"); bytes[32..64].copy_from_slice(program_id_bytes); - bytes[64..].copy_from_slice(&value.1.0); + bytes[64..].copy_from_slice(&seed.0); + Self::new( + Impl::hash_bytes(&bytes) + .as_bytes() + .try_into() + .expect("Hash output must be exactly 32 bytes long"), + ) + } + + /// Derives an [`AccountId`] for a private PDA from the program ID, seed, and nullifier + /// public key. + /// + /// Unlike public PDAs ([`AccountId::for_public_pda`]), this includes the `npk` in the + /// derivation, making the address unique per group of controllers sharing viewing keys. + #[must_use] + pub fn for_private_pda( + program_id: &ProgramId, + seed: &PdaSeed, + npk: &NullifierPublicKey, + ) -> Self { + use risc0_zkvm::sha::{Impl, Sha256 as _}; + const PRIVATE_PDA_PREFIX: &[u8; 32] = b"/LEE/v0.3/AccountId/PrivatePDA/\x00"; + + let mut bytes = [0_u8; 128]; + bytes[0..32].copy_from_slice(PRIVATE_PDA_PREFIX); + let program_id_bytes: &[u8] = + bytemuck::try_cast_slice(program_id).expect("ProgramId should be castable to &[u8]"); + bytes[32..64].copy_from_slice(program_id_bytes); + bytes[64..96].copy_from_slice(&seed.0); + bytes[96..128].copy_from_slice(&npk.to_byte_array()); Self::new( Impl::hash_bytes(&bytes) .as_bytes() @@ -67,9 +98,9 @@ pub struct ChainedCall { pub instruction_data: InstructionData, /// PDA seeds authorized for the callee. For each callee `pre_state`, the outer circuit /// checks whether its `AccountId` matches a public PDA derivation - /// `AccountId::from((&caller, seed))` (mask 0) or a private PDA derivation - /// `private_pda_account_id(&caller, seed, npk)` (mask 3, where `npk` is the supplied npk - /// for that `pre_state`). Programs stay privacy-agnostic: they emit seeds, the circuit + /// `AccountId::for_public_pda(&caller, seed)` (mask 0) or a private PDA derivation + /// `AccountId::for_private_pda(&caller, seed, npk)` (mask 3, where `npk` is the supplied + /// npk for that `pre_state`). Programs stay privacy-agnostic: they emit seeds, the circuit /// resolves public vs private based on the `pre_state`'s mask. pub pda_seeds: Vec, } @@ -122,8 +153,8 @@ pub enum Claim { Authorized, /// The program requests ownership of the account through a PDA. The `pre_state`'s /// visibility mask selects the derivation formula: mask 0 uses - /// `AccountId::from((&program_id, &seed))`, mask 3 uses - /// `private_pda_account_id(&program_id, &seed, &npk)` with the supplied npk for that + /// `AccountId::for_public_pda(&program_id, &seed)`, mask 3 uses + /// `AccountId::for_private_pda(&program_id, &seed, &npk)` with the supplied npk for that /// `pre_state`. Programs stay privacy-agnostic: they emit a seed, the circuit resolves the /// rest from the mask. Pda(PdaSeed), @@ -488,40 +519,12 @@ pub enum ExecutionValidationError { }, } -/// Derives an [`AccountId`] for a private PDA from the program ID, seed, and nullifier public key. -/// -/// Unlike public PDAs (`AccountId::from((&ProgramId, &PdaSeed))`), this includes the `npk` in the -/// derivation, making the address unique per group of controllers sharing viewing keys. -#[must_use] -pub fn private_pda_account_id( - program_id: &ProgramId, - seed: &PdaSeed, - npk: &NullifierPublicKey, -) -> AccountId { - use risc0_zkvm::sha::{Impl, Sha256 as _}; - const PRIVATE_PDA_PREFIX: &[u8; 32] = b"/LEE/v0.3/AccountId/PrivatePDA/\x00"; - - let mut bytes = [0_u8; 128]; - bytes[0..32].copy_from_slice(PRIVATE_PDA_PREFIX); - let program_id_bytes: &[u8] = - bytemuck::try_cast_slice(program_id).expect("ProgramId should be castable to &[u8]"); - bytes[32..64].copy_from_slice(program_id_bytes); - bytes[64..96].copy_from_slice(&seed.0); - bytes[96..128].copy_from_slice(&npk.to_byte_array()); - AccountId::new( - Impl::hash_bytes(&bytes) - .as_bytes() - .try_into() - .expect("Hash output must be exactly 32 bytes long"), - ) -} - /// Computes the set of public-PDA `AccountId`s the callee is authorized to mutate. /// /// Returns only public-form derivations, suitable for contexts where all accounts are public /// (e.g. the public-execution path). The privacy circuit must additionally check each mask-3 -/// `pre_state` against `private_pda_account_id(caller, seed, npk)` with the supplied npk for -/// that `pre_state`. +/// `pre_state` against [`AccountId::for_private_pda`] with the supplied npk for that +/// `pre_state`. #[must_use] pub fn compute_authorized_pdas( caller_program_id: Option, @@ -532,7 +535,7 @@ pub fn compute_authorized_pdas( }; pda_seeds .iter() - .map(|seed| AccountId::from((&caller, seed))) + .map(|seed| AccountId::for_public_pda(&caller, seed)) .collect() } @@ -845,13 +848,13 @@ mod tests { assert_eq!(account_post_state.account_mut(), &mut account); } - // ---- private_pda_account_id tests ---- + // ---- AccountId::for_private_pda tests ---- - /// Pins `private_pda_account_id` against a hardcoded expected output for a specific + /// Pins `AccountId::for_private_pda` against a hardcoded expected output for a specific /// `(program_id, seed, npk)` triple. Any change to `PRIVATE_PDA_PREFIX`, byte ordering, /// or the underlying hash breaks this test. #[test] - fn private_pda_account_id_matches_pinned_value() { + fn for_private_pda_matches_pinned_value() { let program_id: ProgramId = [1; 8]; let seed = PdaSeed::new([2; 32]); let npk = NullifierPublicKey([3; 32]); @@ -859,68 +862,71 @@ mod tests { 132, 198, 103, 173, 244, 211, 188, 217, 249, 99, 126, 205, 152, 120, 192, 47, 13, 53, 133, 3, 17, 69, 92, 243, 140, 94, 182, 211, 218, 75, 215, 45, ]); - assert_eq!(private_pda_account_id(&program_id, &seed, &npk), expected); + assert_eq!( + AccountId::for_private_pda(&program_id, &seed, &npk), + expected + ); } /// Two groups with different viewing keys at the same (program, seed) get different addresses. #[test] - fn private_pda_account_id_differs_for_different_npk() { + fn for_private_pda_differs_for_different_npk() { let program_id: ProgramId = [1; 8]; let seed = PdaSeed::new([2; 32]); let npk_a = NullifierPublicKey([3; 32]); let npk_b = NullifierPublicKey([4; 32]); assert_ne!( - private_pda_account_id(&program_id, &seed, &npk_a), - private_pda_account_id(&program_id, &seed, &npk_b), + AccountId::for_private_pda(&program_id, &seed, &npk_a), + AccountId::for_private_pda(&program_id, &seed, &npk_b), ); } /// Different seeds produce different addresses, even with the same program and npk. #[test] - fn private_pda_account_id_differs_for_different_seed() { + fn for_private_pda_differs_for_different_seed() { let program_id: ProgramId = [1; 8]; let seed_a = PdaSeed::new([2; 32]); let seed_b = PdaSeed::new([5; 32]); let npk = NullifierPublicKey([3; 32]); assert_ne!( - private_pda_account_id(&program_id, &seed_a, &npk), - private_pda_account_id(&program_id, &seed_b, &npk), + AccountId::for_private_pda(&program_id, &seed_a, &npk), + AccountId::for_private_pda(&program_id, &seed_b, &npk), ); } /// Different programs produce different addresses, even with the same seed and npk. #[test] - fn private_pda_account_id_differs_for_different_program_id() { + fn for_private_pda_differs_for_different_program_id() { let program_id_a: ProgramId = [1; 8]; let program_id_b: ProgramId = [9; 8]; let seed = PdaSeed::new([2; 32]); let npk = NullifierPublicKey([3; 32]); assert_ne!( - private_pda_account_id(&program_id_a, &seed, &npk), - private_pda_account_id(&program_id_b, &seed, &npk), + AccountId::for_private_pda(&program_id_a, &seed, &npk), + AccountId::for_private_pda(&program_id_b, &seed, &npk), ); } /// A private PDA at the same (program, seed) has a different address than a public PDA, /// because the private formula uses a different prefix and includes npk. #[test] - fn private_pda_account_id_differs_from_public_pda() { + fn for_private_pda_differs_from_public_pda() { let program_id: ProgramId = [1; 8]; let seed = PdaSeed::new([2; 32]); let npk = NullifierPublicKey([3; 32]); - let private_id = private_pda_account_id(&program_id, &seed, &npk); - let public_id = AccountId::from((&program_id, &seed)); + let private_id = AccountId::for_private_pda(&program_id, &seed, &npk); + let public_id = AccountId::for_public_pda(&program_id, &seed); assert_ne!(private_id, public_id); } /// A private PDA address differs from a standard private account address at the same `npk`, /// because the private PDA formula includes `program_id` and `seed`. #[test] - fn private_pda_account_id_differs_from_standard_private() { + fn for_private_pda_differs_from_standard_private() { let program_id: ProgramId = [1; 8]; let seed = PdaSeed::new([2; 32]); let npk = NullifierPublicKey([3; 32]); - let private_pda_id = private_pda_account_id(&program_id, &seed, &npk); + let private_pda_id = AccountId::for_private_pda(&program_id, &seed, &npk); let standard_private_id = AccountId::from(&npk); assert_ne!(private_pda_id, standard_private_id); } @@ -933,7 +939,7 @@ mod tests { let caller: ProgramId = [1; 8]; let seed = PdaSeed::new([2; 32]); let result = compute_authorized_pdas(Some(caller), &[seed]); - let expected = AccountId::from((&caller, &seed)); + let expected = AccountId::for_public_pda(&caller, &seed); assert!(result.contains(&expected)); assert_eq!(result.len(), 1); } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 2687d170..f86f429f 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -368,7 +368,7 @@ pub mod tests { encryption::{EphemeralPublicKey, Scalar, ViewingPublicKey}, program::{ BlockValidityWindow, ExecutionValidationError, PdaSeed, ProgramId, - TimestampValidityWindow, WrappedBalanceSum, private_pda_account_id, + TimestampValidityWindow, WrappedBalanceSum, }, }; @@ -2352,7 +2352,7 @@ pub mod tests { /// Happy path: a program claims a new mask-3 account via `Claim::Pda(seed)`. The circuit /// reads the npk for that `pre_state` from `private_account_keys` at the `pre_state`'s - /// position, derives `AccountId` via `private_pda_account_id(program_id, seed, npk)`, and + /// position, derives `AccountId` via `AccountId::for_private_pda(program_id, seed, npk)`, and /// asserts it equals the `pre_state`'s `account_id`. The equality both validates the claim /// and binds the supplied npk to the `account_id`. #[test] @@ -2363,7 +2363,7 @@ pub mod tests { let seed = PdaSeed::new([42; 32]); let shared_secret = SharedSecretKey::new(&[55; 32], &keys.vpk()); - let account_id = private_pda_account_id(&program.id(), &seed, &npk); + let account_id = AccountId::for_private_pda(&program.id(), &seed, &npk); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let result = execute_and_prove( @@ -2385,7 +2385,7 @@ pub mod tests { } /// An npk is supplied that does not match the `pre_state`'s `account_id` under - /// `private_pda_account_id(program, claim_seed, npk)`. The claim equality check rejects. + /// `AccountId::for_private_pda(program, claim_seed, npk)`. The claim equality check rejects. #[test] fn private_pda_npk_mismatch_fails() { // `keys_a` produces the `pre_state`'s `account_id` (the registered pair), `keys_b` is @@ -2399,9 +2399,9 @@ pub mod tests { let shared_secret = SharedSecretKey::new(&[55; 32], &keys_b.vpk()); // `account_id` is derived from `npk_a`, but `npk_b` is supplied for this pre_state. - // `private_pda_account_id(program, seed, npk_b) != account_id`, so the claim check in + // `AccountId::for_private_pda(program, seed, npk_b) != account_id`, so the claim check in // the circuit must reject. - let account_id = private_pda_account_id(&program.id(), &seed, &npk_a); + let account_id = AccountId::for_private_pda(&program.id(), &seed, &npk_a); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let result = execute_and_prove( @@ -2421,7 +2421,7 @@ pub mod tests { /// private PDA via `Claim::Pda(seed)`, then chains to a callee (`noop`) delegating the same /// seed via `ChainedCall.pda_seeds`. In the callee's step, the `pre_state`'s authorization /// is established via the private derivation - /// `private_pda_account_id(delegator, seed, npk) == pre.account_id`. + /// `AccountId::for_private_pda(delegator, seed, npk) == pre.account_id`. #[test] fn caller_pda_seeds_authorize_private_pda_for_callee() { let delegator = Program::private_pda_delegator(); @@ -2431,7 +2431,7 @@ pub mod tests { let seed = PdaSeed::new([77; 32]); let shared_secret = SharedSecretKey::new(&[55; 32], &keys.vpk()); - let account_id = private_pda_account_id(&delegator.id(), &seed, &npk); + let account_id = AccountId::for_private_pda(&delegator.id(), &seed, &npk); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let callee_id = callee.id(); @@ -2468,7 +2468,7 @@ pub mod tests { let wrong_delegated_seed = PdaSeed::new([88; 32]); let shared_secret = SharedSecretKey::new(&[55; 32], &keys.vpk()); - let account_id = private_pda_account_id(&delegator.id(), &claim_seed, &npk); + let account_id = AccountId::for_private_pda(&delegator.id(), &claim_seed, &npk); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let callee_id = callee.id(); @@ -2505,8 +2505,8 @@ pub mod tests { let shared_a = SharedSecretKey::new(&[66; 32], &keys_a.vpk()); let shared_b = SharedSecretKey::new(&[77; 32], &keys_b.vpk()); - let account_a = private_pda_account_id(&program.id(), &seed, &keys_a.npk()); - let account_b = private_pda_account_id(&program.id(), &seed, &keys_b.npk()); + let account_a = AccountId::for_private_pda(&program.id(), &seed, &keys_a.npk()); + let account_b = AccountId::for_private_pda(&program.id(), &seed, &keys_b.npk()); let pre_a = AccountWithMetadata::new(Account::default(), false, account_a); let pre_b = AccountWithMetadata::new(Account::default(), false, account_b); @@ -2533,7 +2533,7 @@ pub mod tests { /// `ChainedCall.pda_seeds`, so position 0 is never bound and the assertion fires. // TODO: a follow-up PR in the Private PDAs series needs to let the wallet supply a // `(seed, original_owner_program_id)` side input per mask-3 `pre_state` so the circuit - // can re-verify `private_pda_account_id(owner, seed, npk) == pre.account_id` without a + // can re-verify `AccountId::for_private_pda(owner, seed, npk) == pre.account_id` without a // claim. #[test] fn private_pda_top_level_reuse_rejected_by_binding_check() { @@ -2545,7 +2545,7 @@ pub mod tests { // Simulate a previously-claimed private PDA: program_owner != DEFAULT, is_authorized = // true, account_id derived via the private formula. - let account_id = private_pda_account_id(&program.id(), &seed, &npk); + let account_id = AccountId::for_private_pda(&program.id(), &seed, &npk); let owned_pre_state = AccountWithMetadata::new( Account { program_owner: program.id(), @@ -2967,7 +2967,7 @@ pub mod tests { fn execution_that_requires_authentication_of_a_program_derived_account_id_succeeds() { let chain_caller = Program::chain_caller(); let pda_seed = PdaSeed::new([37; 32]); - let from = AccountId::from((&chain_caller.id(), &pda_seed)); + let from = AccountId::for_public_pda(&chain_caller.id(), &pda_seed); let to = AccountId::new([2; 32]); let initial_balance = 1000; let initial_data = [(from, initial_balance), (to, 0)]; @@ -3279,7 +3279,8 @@ pub mod tests { let pinata_definition_id = AccountId::new([1; 32]); let pinata_token_definition_id = AccountId::new([2; 32]); // Total supply of pinata token will be in an account under a PDA. - let pinata_token_holding_id = AccountId::from((&pinata_token.id(), &PdaSeed::new([0; 32]))); + let pinata_token_holding_id = + AccountId::for_public_pda(&pinata_token.id(), &PdaSeed::new([0; 32])); let winner_token_holding_id = AccountId::new([3; 32]); let expected_winner_account_holding = token_core::TokenHolding::Fungible { @@ -4296,8 +4297,8 @@ pub mod tests { let callback = Program::flash_swap_callback(); let token = Program::authenticated_transfer_program(); - let vault_id = AccountId::from((&initiator.id(), &PdaSeed::new([0_u8; 32]))); - let receiver_id = AccountId::from((&callback.id(), &PdaSeed::new([1_u8; 32]))); + let vault_id = AccountId::for_public_pda(&initiator.id(), &PdaSeed::new([0_u8; 32])); + let receiver_id = AccountId::for_public_pda(&callback.id(), &PdaSeed::new([1_u8; 32])); let initial_balance: u128 = 1000; let amount_out: u128 = 100; @@ -4347,8 +4348,8 @@ pub mod tests { let callback = Program::flash_swap_callback(); let token = Program::authenticated_transfer_program(); - let vault_id = AccountId::from((&initiator.id(), &PdaSeed::new([0_u8; 32]))); - let receiver_id = AccountId::from((&callback.id(), &PdaSeed::new([1_u8; 32]))); + let vault_id = AccountId::for_public_pda(&initiator.id(), &PdaSeed::new([0_u8; 32])); + let receiver_id = AccountId::for_public_pda(&callback.id(), &PdaSeed::new([1_u8; 32])); let initial_balance: u128 = 1000; let amount_out: u128 = 100; @@ -4405,8 +4406,8 @@ pub mod tests { let callback = Program::flash_swap_callback(); let token = Program::authenticated_transfer_program(); - let vault_id = AccountId::from((&initiator.id(), &PdaSeed::new([0_u8; 32]))); - let receiver_id = AccountId::from((&callback.id(), &PdaSeed::new([1_u8; 32]))); + let vault_id = AccountId::for_public_pda(&initiator.id(), &PdaSeed::new([0_u8; 32])); + let receiver_id = AccountId::for_public_pda(&callback.id(), &PdaSeed::new([1_u8; 32])); let initial_balance: u128 = 1000; @@ -4454,7 +4455,7 @@ pub mod tests { let initiator = Program::flash_swap_initiator(); let token = Program::authenticated_transfer_program(); - let vault_id = AccountId::from((&initiator.id(), &PdaSeed::new([0_u8; 32]))); + let vault_id = AccountId::for_public_pda(&initiator.id(), &PdaSeed::new([0_u8; 32])); let vault_account = Account { program_owner: token.id(), diff --git a/nssa/src/validated_state_diff.rs b/nssa/src/validated_state_diff.rs index 4528b0ae..30dd7867 100644 --- a/nssa/src/validated_state_diff.rs +++ b/nssa/src/validated_state_diff.rs @@ -225,7 +225,7 @@ impl ValidatedStateDiff { // The program can only claim accounts that correspond to the PDAs it is // authorized to claim. The public-execution path only sees public // accounts, so the public-PDA derivation is the correct formula here. - let pda = AccountId::from((&chained_call.program_id, &seed)); + let pda = AccountId::for_public_pda(&chained_call.program_id, &seed); ensure!( account_id == pda, InvalidProgramBehaviorError::MismatchedPdaClaim { diff --git a/program_methods/guest/src/bin/privacy_preserving_circuit.rs b/program_methods/guest/src/bin/privacy_preserving_circuit.rs index 46a5afa9..fe40db94 100644 --- a/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -12,7 +12,7 @@ use nssa_core::{ program::{ AccountPostState, BlockValidityWindow, ChainedCall, Claim, DEFAULT_PROGRAM_ID, MAX_NUMBER_CHAINED_CALLS, PdaSeed, ProgramId, ProgramOutput, TimestampValidityWindow, - private_pda_account_id, validate_execution, + validate_execution, }, }; use risc0_zkvm::{guest::env, serde::to_vec}; @@ -24,7 +24,8 @@ struct ExecutionState { block_validity_window: BlockValidityWindow, timestamp_validity_window: TimestampValidityWindow, /// Positions (in `pre_states`) of mask-3 accounts whose supplied npk has been bound to - /// their `AccountId` via a proven `private_pda_account_id(program_id, seed, npk)` check. + /// their `AccountId` via a proven `AccountId::for_private_pda(program_id, seed, npk)` + /// check. /// Two proof paths populate this set: a `Claim::Pda(seed)` in a program's `post_state` on /// that `pre_state`, or a caller's `ChainedCall.pda_seeds` entry matching that `pre_state` /// under the private derivation. Binding is an idempotent property, not an event: the same @@ -279,12 +280,13 @@ impl ExecutionState { let matched_caller_seed: Option<(PdaSeed, bool, ProgramId)> = caller_program_id .and_then(|caller| { caller_pda_seeds.iter().find_map(|seed| { - if AccountId::from((&caller, seed)) == pre_account_id { + if AccountId::for_public_pda(&caller, seed) == pre_account_id { return Some((*seed, false, caller)); } if let Some(npk) = private_pda_npk_by_position.get(&pre_state_position) - && private_pda_account_id(&caller, seed, npk) == pre_account_id + && AccountId::for_private_pda(&caller, seed, npk) + == pre_account_id { return Some((*seed, true, caller)); } @@ -343,7 +345,7 @@ impl ExecutionState { ); } Claim::Pda(seed) => { - let pda = AccountId::from((&program_id, &seed)); + let pda = AccountId::for_public_pda(&program_id, &seed); assert_eq!( pre_account_id, pda, "Invalid PDA claim for account {pre_account_id} which does not match derived PDA {pda}" @@ -367,7 +369,7 @@ impl ExecutionState { let npk = private_pda_npk_by_position.get(&pre_state_position).expect( "private PDA pre_state must have an npk in the position map", ); - let pda = private_pda_account_id(&program_id, &seed, npk); + let pda = AccountId::for_private_pda(&program_id, &seed, npk); assert_eq!( pre_account_id, pda, "Invalid private PDA claim for account {pre_account_id}" @@ -576,10 +578,11 @@ fn compute_circuit_output( // Private PDA account. The supplied npk has already been bound to // `pre_state.account_id` upstream in `validate_and_sync_states`, either via a // `Claim::Pda(seed)` match or via a caller `pda_seeds` match, both of which - // assert `private_pda_account_id(owner, seed, npk) == account_id`. The post-loop - // assertion in `derive_from_outputs` (see the `private_pda_bound_positions` check) - // guarantees that every mask-3 position has been through at least one such - // binding, so this branch can safely use the wallet npk without re-verifying. + // assert `AccountId::for_private_pda(owner, seed, npk) == account_id`. The + // post-loop assertion in `derive_from_outputs` (see the + // `private_pda_bound_positions` check) guarantees that every mask-3 + // position has been through at least one such binding, so this + // branch can safely use the wallet npk without re-verifying. let Some((npk, shared_secret)) = private_keys_iter.next() else { panic!("Missing private account key"); }; diff --git a/programs/amm/core/src/lib.rs b/programs/amm/core/src/lib.rs index 017f14ff..6e005c9e 100644 --- a/programs/amm/core/src/lib.rs +++ b/programs/amm/core/src/lib.rs @@ -135,10 +135,10 @@ pub fn compute_pool_pda( definition_token_a_id: AccountId, definition_token_b_id: AccountId, ) -> AccountId { - AccountId::from(( + AccountId::for_public_pda( &amm_program_id, &compute_pool_pda_seed(definition_token_a_id, definition_token_b_id), - )) + ) } #[must_use] @@ -175,10 +175,10 @@ pub fn compute_vault_pda( pool_id: AccountId, definition_token_id: AccountId, ) -> AccountId { - AccountId::from(( + AccountId::for_public_pda( &amm_program_id, &compute_vault_pda_seed(pool_id, definition_token_id), - )) + ) } #[must_use] @@ -199,7 +199,7 @@ pub fn compute_vault_pda_seed(pool_id: AccountId, definition_token_id: AccountId #[must_use] pub fn compute_liquidity_token_pda(amm_program_id: ProgramId, pool_id: AccountId) -> AccountId { - AccountId::from((&amm_program_id, &compute_liquidity_token_pda_seed(pool_id))) + AccountId::for_public_pda(&amm_program_id, &compute_liquidity_token_pda_seed(pool_id)) } #[must_use] diff --git a/programs/associated_token_account/core/src/lib.rs b/programs/associated_token_account/core/src/lib.rs index 994c632b..8fe6e267 100644 --- a/programs/associated_token_account/core/src/lib.rs +++ b/programs/associated_token_account/core/src/lib.rs @@ -61,7 +61,7 @@ pub fn compute_ata_seed(owner_id: AccountId, definition_id: AccountId) -> PdaSee } pub fn get_associated_token_account_id(ata_program_id: &ProgramId, seed: &PdaSeed) -> AccountId { - AccountId::from((ata_program_id, seed)) + AccountId::for_public_pda(ata_program_id, seed) } /// Verify the ATA's address matches `(ata_program_id, owner, definition)` and return