diff --git a/Cargo.lock b/Cargo.lock index d6afde36..16861330 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9519,9 +9519,9 @@ dependencies = [ [[package]] name = "spin" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" dependencies = [ "lock_api", ] diff --git a/artifacts/lee/privacy_preserving_circuit/privacy_preserving_circuit.bin b/artifacts/lee/privacy_preserving_circuit/privacy_preserving_circuit.bin index bb1b66fc..f54520a8 100644 Binary files a/artifacts/lee/privacy_preserving_circuit/privacy_preserving_circuit.bin and b/artifacts/lee/privacy_preserving_circuit/privacy_preserving_circuit.bin differ diff --git a/artifacts/lez/programs/amm.bin b/artifacts/lez/programs/amm.bin index 13d9aad6..ce19ce6f 100644 Binary files a/artifacts/lez/programs/amm.bin and b/artifacts/lez/programs/amm.bin differ diff --git a/artifacts/lez/programs/associated_token_account.bin b/artifacts/lez/programs/associated_token_account.bin index 81c9e602..f5b7b02a 100644 Binary files a/artifacts/lez/programs/associated_token_account.bin and b/artifacts/lez/programs/associated_token_account.bin differ diff --git a/artifacts/lez/programs/authenticated_transfer.bin b/artifacts/lez/programs/authenticated_transfer.bin index 85419195..2b90722d 100644 Binary files a/artifacts/lez/programs/authenticated_transfer.bin and b/artifacts/lez/programs/authenticated_transfer.bin differ diff --git a/artifacts/lez/programs/bridge.bin b/artifacts/lez/programs/bridge.bin index 1458cb82..3ac001d7 100644 Binary files a/artifacts/lez/programs/bridge.bin and b/artifacts/lez/programs/bridge.bin differ diff --git a/artifacts/lez/programs/bridge_lock.bin b/artifacts/lez/programs/bridge_lock.bin index 989b95ec..b0b68094 100644 Binary files a/artifacts/lez/programs/bridge_lock.bin and b/artifacts/lez/programs/bridge_lock.bin differ diff --git a/artifacts/lez/programs/clock.bin b/artifacts/lez/programs/clock.bin index c8794191..e39955c8 100644 Binary files a/artifacts/lez/programs/clock.bin and b/artifacts/lez/programs/clock.bin differ diff --git a/artifacts/lez/programs/cross_zone_inbox.bin b/artifacts/lez/programs/cross_zone_inbox.bin index c35173c1..02af9294 100644 Binary files a/artifacts/lez/programs/cross_zone_inbox.bin and b/artifacts/lez/programs/cross_zone_inbox.bin differ diff --git a/artifacts/lez/programs/cross_zone_outbox.bin b/artifacts/lez/programs/cross_zone_outbox.bin index bc7f52a1..a548a31e 100644 Binary files a/artifacts/lez/programs/cross_zone_outbox.bin and b/artifacts/lez/programs/cross_zone_outbox.bin differ diff --git a/artifacts/lez/programs/faucet.bin b/artifacts/lez/programs/faucet.bin index de80915b..be4e884c 100644 Binary files a/artifacts/lez/programs/faucet.bin and b/artifacts/lez/programs/faucet.bin differ diff --git a/artifacts/lez/programs/pinata.bin b/artifacts/lez/programs/pinata.bin index 02bc72af..d717d9d6 100644 Binary files a/artifacts/lez/programs/pinata.bin and b/artifacts/lez/programs/pinata.bin differ diff --git a/artifacts/lez/programs/pinata_token.bin b/artifacts/lez/programs/pinata_token.bin index ccd41eb3..cddd3eb1 100644 Binary files a/artifacts/lez/programs/pinata_token.bin and b/artifacts/lez/programs/pinata_token.bin differ diff --git a/artifacts/lez/programs/ping_receiver.bin b/artifacts/lez/programs/ping_receiver.bin index 24c8b572..9463fa1c 100644 Binary files a/artifacts/lez/programs/ping_receiver.bin and b/artifacts/lez/programs/ping_receiver.bin differ diff --git a/artifacts/lez/programs/ping_sender.bin b/artifacts/lez/programs/ping_sender.bin index e9570781..dfc27e67 100644 Binary files a/artifacts/lez/programs/ping_sender.bin and b/artifacts/lez/programs/ping_sender.bin differ diff --git a/artifacts/lez/programs/token.bin b/artifacts/lez/programs/token.bin index 43c43c96..b76eaadd 100644 Binary files a/artifacts/lez/programs/token.bin and b/artifacts/lez/programs/token.bin differ diff --git a/artifacts/lez/programs/vault.bin b/artifacts/lez/programs/vault.bin index 1f962a88..ae08f453 100644 Binary files a/artifacts/lez/programs/vault.bin and b/artifacts/lez/programs/vault.bin differ diff --git a/artifacts/lez/programs/wrapped_token.bin b/artifacts/lez/programs/wrapped_token.bin index d93b4c32..dc9e3454 100644 Binary files a/artifacts/lez/programs/wrapped_token.bin and b/artifacts/lez/programs/wrapped_token.bin differ diff --git a/lez/wallet-ffi/src/lib.rs b/lez/wallet-ffi/src/lib.rs index 361ae672..c91185e6 100644 --- a/lez/wallet-ffi/src/lib.rs +++ b/lez/wallet-ffi/src/lib.rs @@ -47,6 +47,7 @@ pub mod error; pub mod generic_transaction; pub mod keys; pub mod label; +pub mod pda; pub mod pinata; pub mod program_deployment; pub mod sync; diff --git a/lez/wallet-ffi/src/pda.rs b/lez/wallet-ffi/src/pda.rs new file mode 100644 index 00000000..35a9482b --- /dev/null +++ b/lez/wallet-ffi/src/pda.rs @@ -0,0 +1,142 @@ +use lee::AccountId; + +use crate::{ + error::WalletFfiError, FfiBytes32, FfiNullifierPublicKey, FfiPdaSeed, FfiPrivateAccountKeys, + FfiProgramId, FfiU128, +}; + +/// Produce account id for public PDA. +/// +/// # Parameters +/// - `program_id`: Id of the owner program +/// - `pda_seed`: 32 byte seed +/// +/// # Returns +/// - `FfiBytes32` representing account id bytes +#[no_mangle] +pub extern "C" fn wallet_ffi_account_id_for_public_pda( + program_id: FfiProgramId, + pda_seed: FfiPdaSeed, +) -> FfiBytes32 { + AccountId::for_public_pda(&program_id.data, &pda_seed.into()).into() +} + +/// Produce account id for private PDA. +/// +/// # Parameters +/// - `program_id`: Id of the owner program +/// - `pda_seed`: 32 byte seed +/// - `npk`: 32 byte nullifier public key (can be obtained from +/// `wallet_ffi_get_private_account_keys`) +/// - `viewing_public_key`: pointer to u8 (can be obtained from +/// `wallet_ffi_get_private_account_keys`) +/// - `viewing_public_key_len`: length of a `viewing_public_key` (can be obtained from +/// `wallet_ffi_get_private_account_keys`), must be `1184` +/// - `identifier`: little endian encoded `u128` +/// - `account_id`: valid pointer to `FfiBytes32` +/// +/// # Returns +/// - `Success` on successful parsing +/// - Error code on failure +/// +/// # Safety +/// - `viewing_public_key` must be a valid pointer to a `u8` +/// - `account_id` must be a valid pointer to a `FfiBytes32` struct +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_account_id_for_private_pda( + program_id: FfiProgramId, + pda_seed: FfiPdaSeed, + npk: FfiNullifierPublicKey, + viewing_public_key: *const u8, + viewing_public_key_len: usize, + identifier: FfiU128, + account_id: *mut FfiBytes32, +) -> WalletFfiError { + if viewing_public_key.is_null() { + return WalletFfiError::NullPointer; + } + + let ffi_private_keys = FfiPrivateAccountKeys { + nullifier_public_key: npk, + viewing_public_key, + viewing_public_key_len, + }; + + let vpk = ffi_private_keys.vpk(); + + if vpk.is_err() { + return vpk.err().unwrap(); + } + + unsafe { + *account_id = AccountId::for_private_pda( + &program_id.data, + &pda_seed.into(), + &ffi_private_keys.npk(), + &vpk.unwrap(), + identifier.into(), + ) + .into(); + } + + WalletFfiError::Success +} + +#[cfg(test)] +mod tests { + use lee::AccountId; + use lee_core::{encryption::ViewingPublicKey, NullifierPublicKey}; + use vault_core::PdaSeed; + + use crate::{ + error::WalletFfiError, + pda::{wallet_ffi_account_id_for_private_pda, wallet_ffi_account_id_for_public_pda}, + FfiBytes32, + }; + + #[test] + fn public_pda_consistent_derivation() { + let program_id = [100_u32, 101, 102, 103, 104, 105, 106, 107]; + let pda_seed = PdaSeed::new([42; 32]); + + let pda_id = AccountId::for_public_pda(&program_id, &pda_seed); + let ffi_pda_id = wallet_ffi_account_id_for_public_pda(program_id.into(), pda_seed.into()); + + assert_eq!(pda_id.into_value(), ffi_pda_id.data); + } + + #[test] + fn private_pda_consistent_derivation() { + let program_id = [100_u32, 101, 102, 103, 104, 105, 106, 107]; + let pda_seed = PdaSeed::new([42; 32]); + let vpk = ViewingPublicKey::from_bytes(vec![43; 1184]).unwrap(); + let npk = NullifierPublicKey([44; 32]); + let identifier = 100_000_u128; + + let pda_id = AccountId::for_private_pda(&program_id, &pda_seed, &npk, &vpk, identifier); + + let vpk_ptr = Box::into_raw(vpk.to_bytes().to_vec().into_boxed_slice()) as *const u8; + + let mut ffi_pda_id_base = FfiBytes32 { data: [0; 32] }; + let ffi_pda_id = &raw mut ffi_pda_id_base; + + let err = unsafe { + wallet_ffi_account_id_for_private_pda( + program_id.into(), + pda_seed.into(), + npk.into(), + vpk_ptr, + 1184, + identifier.into(), + ffi_pda_id, + ) + }; + + assert_eq!(err, WalletFfiError::Success); + + assert_eq!(pda_id.into_value(), unsafe { (*ffi_pda_id).data }); + + let vpk_slice = unsafe { std::slice::from_raw_parts_mut(vpk_ptr.cast_mut(), 1184) }; + drop(unsafe { Box::from_raw(std::ptr::from_mut(vpk_slice)) }); + } +} diff --git a/lez/wallet-ffi/src/types.rs b/lez/wallet-ffi/src/types.rs index 4f041c55..3779ba01 100644 --- a/lez/wallet-ffi/src/types.rs +++ b/lez/wallet-ffi/src/types.rs @@ -8,7 +8,7 @@ use std::{ }; use lee::{Data, ProgramId, SharedSecretKey}; -use lee_core::{encryption::MlKem768EncapsulationKey, NullifierPublicKey}; +use lee_core::{encryption::MlKem768EncapsulationKey, program::PdaSeed, NullifierPublicKey}; use wallet::{account::AccountIdWithPrivacy, AccountIdentity}; use crate::error::WalletFfiError; @@ -29,6 +29,36 @@ pub struct FfiBytes32 { pub data: [u8; 32], } +pub type FfiPdaSeed = FfiBytes32; + +impl From for PdaSeed { + fn from(value: FfiPdaSeed) -> Self { + Self::new(value.data) + } +} + +impl From for FfiPdaSeed { + fn from(value: PdaSeed) -> Self { + Self { + data: *value.as_bytes(), + } + } +} + +pub type FfiNullifierPublicKey = FfiBytes32; + +impl From for NullifierPublicKey { + fn from(value: FfiNullifierPublicKey) -> Self { + Self(value.data) + } +} + +impl From for FfiNullifierPublicKey { + fn from(value: NullifierPublicKey) -> Self { + Self { data: value.0 } + } +} + /// Program ID - 8 u32 values (32 bytes total). #[repr(C)] #[derive(Clone, Copy, Default)] diff --git a/lez/wallet-ffi/wallet_ffi.h b/lez/wallet-ffi/wallet_ffi.h index 8f15a888..4104fb61 100644 --- a/lez/wallet-ffi/wallet_ffi.h +++ b/lez/wallet-ffi/wallet_ffi.h @@ -320,6 +320,10 @@ typedef struct LabelList { enum WalletFfiError error; } LabelList; +typedef struct FfiBytes32 FfiPdaSeed; + +typedef struct FfiBytes32 FfiNullifierPublicKey; + typedef struct FfiCreateWalletOutput { struct WalletHandle *wallet; /** @@ -914,6 +918,50 @@ struct LabelList wallet_ffi_get_all_labels_for_account(struct WalletHandle *hand */ enum WalletFfiError wallet_ffi_free_label_list(struct LabelList *label_list); +/** + * Produce account id for public PDA. + * + * # Parameters + * - `program_id`: Id of the owner program + * - `pda_seed`: 32 byte seed + * + * # Returns + * - `FfiBytes32` representing account id bytes + */ +struct FfiBytes32 wallet_ffi_account_id_for_public_pda(struct FfiProgramId program_id, + FfiPdaSeed pda_seed); + +/** + * Produce account id for private PDA. + * + * # Parameters + * - `program_id`: Id of the owner program + * - `pda_seed`: 32 byte seed + * - `npk`: 32 byte nullifier public key (can be obtained from + * `wallet_ffi_get_private_account_keys`) + * - `viewing_public_key`: pointer to u8 (can be obtained from + * `wallet_ffi_get_private_account_keys`) + * - `viewing_public_key_len`: length of a `viewing_public_key` (can be obtained from + * `wallet_ffi_get_private_account_keys`), must be `1184` + * - `identifier`: little endian encoded `u128` + * - `account_id`: valid pointer to `FfiBytes32` + * + * # Returns + * - `Success` on successful parsing + * - Error code on failure + * + * # Safety + * - `viewing_public_key` must be a valid pointer to a `u8` + * - `account_id` must be a valid pointer to a `FfiBytes32` struct + */ +enum WalletFfiError wallet_ffi_account_id_for_private_pda(struct FfiProgramId program_id, + FfiPdaSeed pda_seed, + FfiNullifierPublicKey npk, + const uint8_t *viewing_public_key, + uintptr_t viewing_public_key_len, + struct FfiU128 identifier, + struct FfiBytes32 *account_id); + /** * Claim a pinata reward using a public transaction. * diff --git a/test_fixtures/fixtures/prebuilt_sequencer_db.dump b/test_fixtures/fixtures/prebuilt_sequencer_db.dump index e713b8f5..2aa0c51f 100644 Binary files a/test_fixtures/fixtures/prebuilt_sequencer_db.dump and b/test_fixtures/fixtures/prebuilt_sequencer_db.dump differ