From 0ee39e04ee159b39c1c9a90180019b56d996fb6a Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Fri, 26 Jun 2026 18:54:15 +0300 Subject: [PATCH 1/6] feat(wallet-ffi): pda helpers --- lez/wallet-ffi/src/lib.rs | 1 + lez/wallet-ffi/src/pda.rs | 45 +++++++++++++++++++++++++++++++++++++ lez/wallet-ffi/src/types.rs | 18 ++++++++++++++- lez/wallet-ffi/wallet_ffi.h | 34 ++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 lez/wallet-ffi/src/pda.rs 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..82269e20 --- /dev/null +++ b/lez/wallet-ffi/src/pda.rs @@ -0,0 +1,45 @@ +use lee::AccountId; + +use crate::{FfiBytes32, FfiNullifierPublicKey, FfiPdaSeed, FfiProgramId, FfiU128}; + +/// Produce account id for public PDA. +/// +/// # Parameters +/// - `program_id`: Id of a owner program +/// - `pda_seed`: 32 byte seed +/// +/// # Returns +/// - `FfiBytes32` representing account id bytes +#[no_mangle] +pub unsafe 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 public PDA. +/// +/// # Parameters +/// - `program_id`: Id of a owner program +/// - `pda_seed`: 32 byte seed +/// - `npk`: 32 byte nullifier public key(can be get from `wallet_ffi_get_private_account_keys`) +/// - `identifier`: little endian encoded `u128` +/// +/// # Returns +/// - `FfiBytes32` representing account id bytes +#[no_mangle] +pub unsafe extern "C" fn wallet_ffi_account_id_for_private_pda( + program_id: FfiProgramId, + pda_seed: FfiPdaSeed, + npk: FfiNullifierPublicKey, + identifier: FfiU128, +) -> FfiBytes32 { + AccountId::for_private_pda( + &program_id.data, + &pda_seed.into(), + &npk.into(), + identifier.into(), + ) + .into() +} diff --git a/lez/wallet-ffi/src/types.rs b/lez/wallet-ffi/src/types.rs index 98590619..848649b0 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,22 @@ pub struct FfiBytes32 { pub data: [u8; 32], } +pub type FfiPdaSeed = FfiBytes32; + +impl From for PdaSeed { + fn from(value: FfiPdaSeed) -> Self { + Self::new(value.data) + } +} + +pub type FfiNullifierPublicKey = FfiBytes32; + +impl From for NullifierPublicKey { + fn from(value: FfiNullifierPublicKey) -> Self { + Self(value.data) + } +} + /// 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..c5d24af7 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,36 @@ 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 a 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 public PDA. + * + * # Parameters + * - `program_id`: Id of a owner program + * - `pda_seed`: 32 byte seed + * - `npk`: 32 byte nullifier public key(can be get from `wallet_ffi_get_private_account_keys`) + * - `identifier`: little endian encoded `u128` + * + * # Returns + * - `FfiBytes32` representing account id bytes + */ +struct FfiBytes32 wallet_ffi_account_id_for_private_pda(struct FfiProgramId program_id, + FfiPdaSeed pda_seed, + FfiNullifierPublicKey npk, + struct FfiU128 identifier); + /** * Claim a pinata reward using a public transaction. * From 62437e48ee1998558c5f1c4f6ec03015e318b174 Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Fri, 26 Jun 2026 18:57:22 +0300 Subject: [PATCH 2/6] fix(wallet-ffi): functions is safe --- lez/wallet-ffi/src/pda.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lez/wallet-ffi/src/pda.rs b/lez/wallet-ffi/src/pda.rs index 82269e20..e0b8fbcf 100644 --- a/lez/wallet-ffi/src/pda.rs +++ b/lez/wallet-ffi/src/pda.rs @@ -11,7 +11,7 @@ use crate::{FfiBytes32, FfiNullifierPublicKey, FfiPdaSeed, FfiProgramId, FfiU128 /// # Returns /// - `FfiBytes32` representing account id bytes #[no_mangle] -pub unsafe extern "C" fn wallet_ffi_account_id_for_public_pda( +pub extern "C" fn wallet_ffi_account_id_for_public_pda( program_id: FfiProgramId, pda_seed: FfiPdaSeed, ) -> FfiBytes32 { @@ -29,7 +29,7 @@ pub unsafe extern "C" fn wallet_ffi_account_id_for_public_pda( /// # Returns /// - `FfiBytes32` representing account id bytes #[no_mangle] -pub unsafe extern "C" fn wallet_ffi_account_id_for_private_pda( +pub extern "C" fn wallet_ffi_account_id_for_private_pda( program_id: FfiProgramId, pda_seed: FfiPdaSeed, npk: FfiNullifierPublicKey, From e45da097cf5045301fc8ca1cee95c99c40221eb8 Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Fri, 26 Jun 2026 19:01:15 +0300 Subject: [PATCH 3/6] fix(doc): doctext fix --- lez/wallet-ffi/src/pda.rs | 2 +- lez/wallet-ffi/wallet_ffi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lez/wallet-ffi/src/pda.rs b/lez/wallet-ffi/src/pda.rs index e0b8fbcf..7144fe71 100644 --- a/lez/wallet-ffi/src/pda.rs +++ b/lez/wallet-ffi/src/pda.rs @@ -18,7 +18,7 @@ pub extern "C" fn wallet_ffi_account_id_for_public_pda( AccountId::for_public_pda(&program_id.data, &pda_seed.into()).into() } -/// Produce account id for public PDA. +/// Produce account id for private PDA. /// /// # Parameters /// - `program_id`: Id of a owner program diff --git a/lez/wallet-ffi/wallet_ffi.h b/lez/wallet-ffi/wallet_ffi.h index c5d24af7..8f3ad95f 100644 --- a/lez/wallet-ffi/wallet_ffi.h +++ b/lez/wallet-ffi/wallet_ffi.h @@ -932,7 +932,7 @@ struct FfiBytes32 wallet_ffi_account_id_for_public_pda(struct FfiProgramId progr FfiPdaSeed pda_seed); /** - * Produce account id for public PDA. + * Produce account id for private PDA. * * # Parameters * - `program_id`: Id of a owner program From 91e310b8a05f8c0fe0cf773ea85f577d502abb36 Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Mon, 6 Jul 2026 09:14:34 +0300 Subject: [PATCH 4/6] fix(wallet_ffi): suggestiion 1 --- lez/wallet-ffi/src/types.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lez/wallet-ffi/src/types.rs b/lez/wallet-ffi/src/types.rs index 848649b0..6ada99f7 100644 --- a/lez/wallet-ffi/src/types.rs +++ b/lez/wallet-ffi/src/types.rs @@ -45,6 +45,12 @@ impl From for NullifierPublicKey { } } +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)] From e61306e498d59c7420f2e4a3b292728726504c35 Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Wed, 8 Jul 2026 14:33:32 +0300 Subject: [PATCH 5/6] fix(wallet_ffi): suggestions 2 --- lez/wallet-ffi/src/pda.rs | 45 ++++++++++++++++++++++++++++++++++--- lez/wallet-ffi/src/types.rs | 8 +++++++ lez/wallet-ffi/wallet_ffi.h | 7 +++--- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/lez/wallet-ffi/src/pda.rs b/lez/wallet-ffi/src/pda.rs index 7144fe71..0c72c128 100644 --- a/lez/wallet-ffi/src/pda.rs +++ b/lez/wallet-ffi/src/pda.rs @@ -5,7 +5,7 @@ use crate::{FfiBytes32, FfiNullifierPublicKey, FfiPdaSeed, FfiProgramId, FfiU128 /// Produce account id for public PDA. /// /// # Parameters -/// - `program_id`: Id of a owner program +/// - `program_id`: Id of the owner program /// - `pda_seed`: 32 byte seed /// /// # Returns @@ -21,9 +21,10 @@ pub extern "C" fn wallet_ffi_account_id_for_public_pda( /// Produce account id for private PDA. /// /// # Parameters -/// - `program_id`: Id of a owner program +/// - `program_id`: Id of the owner program /// - `pda_seed`: 32 byte seed -/// - `npk`: 32 byte nullifier public key(can be get from `wallet_ffi_get_private_account_keys`) +/// - `npk`: 32 byte nullifier public key (can be obtained from +/// `wallet_ffi_get_private_account_keys`) /// - `identifier`: little endian encoded `u128` /// /// # Returns @@ -43,3 +44,41 @@ pub extern "C" fn wallet_ffi_account_id_for_private_pda( ) .into() } + +#[cfg(test)] +mod tests { + use lee::AccountId; + use lee_core::NullifierPublicKey; + use vault_core::PdaSeed; + + use crate::pda::{wallet_ffi_account_id_for_private_pda, wallet_ffi_account_id_for_public_pda}; + + #[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 npk = NullifierPublicKey([43; 32]); + let identifier = 100_000_u128; + + let pda_id = AccountId::for_private_pda(&program_id, &pda_seed, &npk, identifier); + let ffi_pda_id = wallet_ffi_account_id_for_private_pda( + program_id.into(), + pda_seed.into(), + npk.into(), + identifier.into(), + ); + + assert_eq!(pda_id.into_value(), ffi_pda_id.data); + } +} diff --git a/lez/wallet-ffi/src/types.rs b/lez/wallet-ffi/src/types.rs index 6ada99f7..39e1a5c9 100644 --- a/lez/wallet-ffi/src/types.rs +++ b/lez/wallet-ffi/src/types.rs @@ -37,6 +37,14 @@ impl From for PdaSeed { } } +impl From for FfiPdaSeed { + fn from(value: PdaSeed) -> Self { + Self { + data: *value.as_bytes(), + } + } +} + pub type FfiNullifierPublicKey = FfiBytes32; impl From for NullifierPublicKey { diff --git a/lez/wallet-ffi/wallet_ffi.h b/lez/wallet-ffi/wallet_ffi.h index 8f3ad95f..f84b4868 100644 --- a/lez/wallet-ffi/wallet_ffi.h +++ b/lez/wallet-ffi/wallet_ffi.h @@ -922,7 +922,7 @@ enum WalletFfiError wallet_ffi_free_label_list(struct LabelList *label_list); * Produce account id for public PDA. * * # Parameters - * - `program_id`: Id of a owner program + * - `program_id`: Id of the owner program * - `pda_seed`: 32 byte seed * * # Returns @@ -935,9 +935,10 @@ struct FfiBytes32 wallet_ffi_account_id_for_public_pda(struct FfiProgramId progr * Produce account id for private PDA. * * # Parameters - * - `program_id`: Id of a owner program + * - `program_id`: Id of the owner program * - `pda_seed`: 32 byte seed - * - `npk`: 32 byte nullifier public key(can be get from `wallet_ffi_get_private_account_keys`) + * - `npk`: 32 byte nullifier public key (can be obtained from + * `wallet_ffi_get_private_account_keys`) * - `identifier`: little endian encoded `u128` * * # Returns From 897562b0cfa689c033ba4f0809d2af62daf3cedd Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Wed, 8 Jul 2026 14:40:13 +0300 Subject: [PATCH 6/6] fix(deny): deny fix --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 84b5e1d6..7cef4c57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1854,9 +1854,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ]