This commit is contained in:
Sergio Chouhy 2026-04-19 23:13:51 -03:00
parent c43418721e
commit 6316f59777
17 changed files with 137 additions and 92 deletions

View File

@ -268,7 +268,7 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
to_label: None,
to_npk: None,
to_vpk: None,
to_identifier: 0,
to_identifier: 0,
amount: fund_amount,
}),
)
@ -501,7 +501,7 @@ async fn transfer_via_ata_private_owner() -> Result<()> {
to_label: None,
to_npk: None,
to_vpk: None,
to_identifier: 0,
to_identifier: 0,
amount: fund_amount,
}),
)
@ -616,7 +616,7 @@ async fn burn_via_ata_private_owner() -> Result<()> {
to_label: None,
to_npk: None,
to_vpk: None,
to_identifier: 0,
to_identifier: 0,
amount: fund_amount,
}),
)

View File

@ -226,8 +226,11 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
let recipient_vsk = [99; 32];
let recipient_vpk = ViewingPublicKey::from_scalar(recipient_vsk);
let recipient_npk = NullifierPublicKey::from(&recipient_nsk);
let recipient_pre =
AccountWithMetadata::new(Account::default(), false, AccountId::from((&recipient_npk, 0)));
let recipient_pre = AccountWithMetadata::new(
Account::default(),
false,
AccountId::from((&recipient_npk, 0)),
);
let eph_holder_from = EphemeralKeyHolder::new(&sender_npk);
let sender_ss = eph_holder_from.calculate_shared_secret_sender(&sender_vpk);

View File

@ -282,11 +282,7 @@ fn wallet_ffi_create_private_accounts() -> Result<()> {
// All returned NPKs must be unique and non-zero
assert_eq!(new_npks_ffi.len(), n_accounts);
let unique: HashSet<_> = new_npks_ffi.iter().collect();
assert_eq!(
unique.len(),
n_accounts,
"Duplicate NPKs returned"
);
assert_eq!(unique.len(), n_accounts, "Duplicate NPKs returned");
assert!(
new_npks_ffi.iter().all(|id| *id != [0_u8; 32]),
"Zero NPK returned"
@ -324,7 +320,10 @@ fn wallet_ffi_save_and_load_persistent_storage() -> Result<()> {
assert_ne!(first_npk, [0u8; 32], "First NPK should be non-zero");
assert_ne!(second_npk, [0u8; 32], "Second NPK should be non-zero");
assert_ne!(first_npk, second_npk, "Keys should differ after state was persisted");
assert_ne!(
first_npk, second_npk,
"Keys should differ after state was persisted"
);
Ok(())
}
@ -819,7 +818,9 @@ fn test_wallet_ffi_transfer_shielded() -> Result<()> {
let mut transfer_result = FfiTransferResult::default();
unsafe {
let to_identifier = FfiU128 { data: 0_u128.to_le_bytes() };
let to_identifier = FfiU128 {
data: 0_u128.to_le_bytes(),
};
wallet_ffi_transfer_shielded(
wallet_ffi_handle,
&raw const from,
@ -952,7 +953,9 @@ fn test_wallet_ffi_transfer_private() -> Result<()> {
let mut transfer_result = FfiTransferResult::default();
unsafe {
let to_identifier = FfiU128 { data: 0_u128.to_le_bytes() };
let to_identifier = FfiU128 {
data: 0_u128.to_le_bytes(),
};
wallet_ffi_transfer_private(
wallet_ffi_handle,
&raw const from,

View File

@ -256,7 +256,10 @@ impl KeyTree<ChildKeysPublic> {
}
impl KeyTree<ChildKeysPrivate> {
pub fn create_private_accounts_key_node(&mut self, parent_cci: &ChainIndex) -> Option<ChainIndex> {
pub fn create_private_accounts_key_node(
&mut self,
parent_cci: &ChainIndex,
) -> Option<ChainIndex> {
self.generate_new_node(parent_cci)
}
@ -273,8 +276,7 @@ impl KeyTree<ChildKeysPrivate> {
identifier: Identifier,
) -> Option<nssa::AccountId> {
let node = self.key_map.get(cci)?;
let account_id =
nssa::AccountId::from((&node.value.0.nullifier_public_key, identifier));
let account_id = nssa::AccountId::from((&node.value.0.nullifier_public_key, identifier));
if self.account_id_map.contains_key(&account_id) {
return None;
}

View File

@ -2,9 +2,8 @@ use std::collections::BTreeMap;
use anyhow::Result;
use k256::AffinePoint;
use serde::{Deserialize, Serialize};
use nssa_core::Identifier;
use serde::{Deserialize, Serialize};
use crate::key_management::{
KeyChain,
@ -122,10 +121,7 @@ impl NSSAUserData {
}
/// Creates a new receiving key node and returns its `ChainIndex`.
pub fn create_private_accounts_key(
&mut self,
parent_cci: Option<ChainIndex>,
) -> ChainIndex {
pub fn create_private_accounts_key(&mut self, parent_cci: Option<ChainIndex>) -> ChainIndex {
match parent_cci {
Some(parent_cci) => self
.private_key_tree
@ -221,10 +217,12 @@ mod tests {
fn new_account() {
let mut user_data = NSSAUserData::default();
let chain_index = user_data
.create_private_accounts_key(Some(ChainIndex::root()));
let chain_index = user_data.create_private_accounts_key(Some(ChainIndex::root()));
let is_key_chain_generated = user_data.private_key_tree.key_map.contains_key(&chain_index);
let is_key_chain_generated = user_data
.private_key_tree
.key_map
.contains_key(&chain_index);
assert!(is_key_chain_generated);
let key_chain = &user_data.private_key_tree.key_map[&chain_index].value.0;

View File

@ -115,7 +115,8 @@ mod tests {
use risc0_zkvm::sha::{Impl, Sha256 as _};
use crate::{
Commitment, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, account::{Account, AccountId},
Commitment, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH,
account::{Account, AccountId},
};
#[test]

View File

@ -212,11 +212,7 @@ mod tests {
);
let recipient_account_id = AccountId::from((&recipient_keys.npk(), 0));
let recipient = AccountWithMetadata::new(
Account::default(),
false,
recipient_account_id,
);
let recipient = AccountWithMetadata::new(Account::default(), false, recipient_account_id);
let balance_to_move: u128 = 37;
@ -291,11 +287,7 @@ mod tests {
let commitment_sender = Commitment::new(&sender_account_id, &sender_pre.account);
let recipient_account_id = AccountId::from((&recipient_keys.npk(), 0));
let recipient = AccountWithMetadata::new(
Account::default(),
false,
recipient_account_id,
);
let recipient = AccountWithMetadata::new(Account::default(), false, recipient_account_id);
let balance_to_move: u128 = 37;
let mut commitment_set = CommitmentSet::with_capacity(2);

View File

@ -1213,7 +1213,8 @@ pub mod tests {
let sender_nonce = sender.account.nonce;
let recipient = AccountWithMetadata::new(Account::default(), false, (&recipient_keys.npk(), 0));
let recipient =
AccountWithMetadata::new(Account::default(), false, (&recipient_keys.npk(), 0));
let esk = [3; 32];
let shared_secret = SharedSecretKey::new(&esk, &recipient_keys.vpk());
@ -1252,8 +1253,11 @@ pub mod tests {
let program = Program::authenticated_transfer_program();
let sender_account_id = AccountId::from((&sender_keys.npk(), 0));
let sender_commitment = Commitment::new(&sender_account_id, sender_private_account);
let sender_pre =
AccountWithMetadata::new(sender_private_account.clone(), true, (&sender_keys.npk(), 0));
let sender_pre = AccountWithMetadata::new(
sender_private_account.clone(),
true,
(&sender_keys.npk(), 0),
);
let recipient_pre =
AccountWithMetadata::new(Account::default(), false, (&recipient_keys.npk(), 0));
@ -1305,8 +1309,11 @@ pub mod tests {
let program = Program::authenticated_transfer_program();
let sender_account_id = AccountId::from((&sender_keys.npk(), 0));
let sender_commitment = Commitment::new(&sender_account_id, sender_private_account);
let sender_pre =
AccountWithMetadata::new(sender_private_account.clone(), true, (&sender_keys.npk(), 0));
let sender_pre = AccountWithMetadata::new(
sender_private_account.clone(),
true,
(&sender_keys.npk(), 0),
);
let recipient_pre = AccountWithMetadata::new(
state.get_account_by_id(*recipient_account_id),
false,
@ -2835,7 +2842,8 @@ pub mod tests {
vec![(sender_commitment.clone(), sender_init_nullifier)],
0,
);
let sender_pre = AccountWithMetadata::new(sender_private_account, true, (&sender_keys.npk(), 0));
let sender_pre =
AccountWithMetadata::new(sender_private_account, true, (&sender_keys.npk(), 0));
let recipient_private_key = PrivateKey::try_new([2; 32]).unwrap();
let recipient_account_id =
AccountId::from(&PublicKey::new_from_private_key(&recipient_private_key));

View File

@ -344,11 +344,7 @@ fn compute_circuit_output(
let account_id = AccountId::from((npk, *identifier));
assert_eq!(
account_id,
pre_state.account_id,
"AccountId mismatch"
);
assert_eq!(account_id, pre_state.account_id, "AccountId mismatch");
let (new_nullifier, new_nonce) = if account_visibility_mask == 1 {
// Private account with authentication

View File

@ -1108,7 +1108,11 @@ mod tests {
let epk = EphemeralPublicKey::from_scalar(esk);
let (output, proof) = execute_and_prove(
vec![AccountWithMetadata::new(Account::default(), true, (&npk, 0))],
vec![AccountWithMetadata::new(
Account::default(),
true,
(&npk, 0),
)],
Program::serialize_instruction(0_u128).unwrap(),
vec![1],
vec![(npk.clone(), 0, shared_secret)],

View File

@ -7,7 +7,10 @@ use nssa::AccountId;
use crate::{
block_on,
error::{print_error, WalletFfiError},
types::{FfiAccount, FfiAccountList, FfiAccountListEntry, FfiBytes32, FfiPrivateAccountKeys, WalletHandle},
types::{
FfiAccount, FfiAccountList, FfiAccountListEntry, FfiBytes32, FfiPrivateAccountKeys,
WalletHandle,
},
wallet::get_wallet,
};

View File

@ -135,7 +135,12 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded(
Err(e) => return e,
};
if from.is_null() || to_keys.is_null() || to_identifier.is_null() || amount.is_null() || out_result.is_null() {
if from.is_null()
|| to_keys.is_null()
|| to_identifier.is_null()
|| amount.is_null()
|| out_result.is_null()
{
print_error("Null pointer argument");
return WalletFfiError::NullPointer;
}
@ -162,9 +167,13 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded(
let transfer = NativeTokenTransfer(&wallet);
match block_on(
transfer.send_shielded_transfer_to_outer_account(from_id, to_npk, to_vpk, to_identifier, amount),
) {
match block_on(transfer.send_shielded_transfer_to_outer_account(
from_id,
to_npk,
to_vpk,
to_identifier,
amount,
)) {
Ok((tx_hash, _shared_key)) => {
let tx_hash = CString::new(tx_hash.to_string())
.map_or(ptr::null_mut(), std::ffi::CString::into_raw);
@ -307,7 +316,12 @@ pub unsafe extern "C" fn wallet_ffi_transfer_private(
Err(e) => return e,
};
if from.is_null() || to_keys.is_null() || to_identifier.is_null() || amount.is_null() || out_result.is_null() {
if from.is_null()
|| to_keys.is_null()
|| to_identifier.is_null()
|| amount.is_null()
|| out_result.is_null()
{
print_error("Null pointer argument");
return WalletFfiError::NullPointer;
}
@ -334,8 +348,13 @@ pub unsafe extern "C" fn wallet_ffi_transfer_private(
let transfer = NativeTokenTransfer(&wallet);
match block_on(transfer.send_private_transfer_to_outer_account(from_id, to_npk, to_vpk, to_identifier, amount))
{
match block_on(transfer.send_private_transfer_to_outer_account(
from_id,
to_npk,
to_vpk,
to_identifier,
amount,
)) {
Ok((tx_hash, _shared_key)) => {
let tx_hash = CString::new(tx_hash.to_string())
.map_or(ptr::null_mut(), std::ffi::CString::into_raw);

View File

@ -181,8 +181,10 @@ impl WalletChainStore {
debug!("inserting at address {account_id}, this account {account:?}");
// Update default accounts if present
if let Entry::Occupied(mut entry) =
self.user_data.default_user_private_accounts.entry(account_id)
if let Entry::Occupied(mut entry) = self
.user_data
.default_user_private_accounts
.entry(account_id)
{
let (key_chain, entries) = entry.get_mut();
let identifier = entries
@ -222,9 +224,7 @@ impl WalletChainStore {
.key_map
.get_mut(&chain_index)
{
if let Some((_, acc)) =
node.value.1.iter_mut().find(|(id, _)| *id == identifier)
{
if let Some((_, acc)) = node.value.1.iter_mut().find(|(id, _)| *id == identifier) {
*acc = account;
} else {
node.value.1.push((identifier, account));
@ -232,16 +232,9 @@ impl WalletChainStore {
}
} else {
// Node not yet in account_id_map — find it by checking all nodes
for (ci, node) in self
.user_data
.private_key_tree
.key_map
.iter_mut()
{
let expected_id = nssa::AccountId::from((
&node.value.0.nullifier_public_key,
identifier,
));
for (ci, node) in self.user_data.private_key_tree.key_map.iter_mut() {
let expected_id =
nssa::AccountId::from((&node.value.0.nullifier_public_key, identifier));
if expected_id == account_id {
if let Some((_, acc)) =
node.value.1.iter_mut().find(|(id, _)| *id == identifier)

View File

@ -411,7 +411,13 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_vpk.to_vec());
let (tx_hash, [secret_from, _]) = NativeTokenTransfer(wallet_core)
.send_private_transfer_to_outer_account(from, to_npk, to_vpk, to_identifier, amount)
.send_private_transfer_to_outer_account(
from,
to_npk,
to_vpk,
to_identifier,
amount,
)
.await?;
println!("Transaction hash is {tx_hash}");
@ -487,7 +493,13 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_vpk.to_vec());
let (tx_hash, _) = NativeTokenTransfer(wallet_core)
.send_shielded_transfer_to_outer_account(from, to_npk, to_vpk, to_identifier, amount)
.send_shielded_transfer_to_outer_account(
from,
to_npk,
to_vpk,
to_identifier,
amount,
)
.await?;
println!("Transaction hash is {tx_hash}");

View File

@ -165,8 +165,12 @@ pub fn produce_data_for_storage(
}
}
let covered_private_chain_indices: std::collections::BTreeSet<_> =
user_data.private_key_tree.account_id_map.values().cloned().collect();
let covered_private_chain_indices: std::collections::BTreeSet<_> = user_data
.private_key_tree
.account_id_map
.values()
.cloned()
.collect();
for (account_id, key) in &user_data.private_key_tree.account_id_map {
if let Some(data) = user_data.private_key_tree.key_map.get(key) {
@ -186,8 +190,7 @@ pub fn produce_data_for_storage(
// entirely, making it impossible to detect incoming transfers.
for (chain_index, node) in &user_data.private_key_tree.key_map {
if !covered_private_chain_indices.contains(chain_index) {
let account_id =
nssa::AccountId::from((&node.value.0.nullifier_public_key, 0_u128));
let account_id = nssa::AccountId::from((&node.value.0.nullifier_public_key, 0_u128));
vec_for_storage.push(
PersistentAccountDataPrivate {
account_id,

View File

@ -256,10 +256,7 @@ impl WalletCore {
.generate_new_public_transaction_private_key(chain_index)
}
pub fn create_private_accounts_key(
&mut self,
chain_index: Option<ChainIndex>,
) -> ChainIndex {
pub fn create_private_accounts_key(&mut self, chain_index: Option<ChainIndex>) -> ChainIndex {
self.storage
.user_data
.create_private_accounts_key(chain_index)
@ -486,9 +483,14 @@ impl WalletCore {
.default_user_private_accounts
.values()
.map(|(key_chain, _)| (key_chain, None))
.chain(self.storage.user_data.private_key_tree.key_map.iter().map(
|(chain_index, keys_node)| (&keys_node.value.0, chain_index.index()),
));
.chain(
self.storage
.user_data
.private_key_tree
.key_map
.iter()
.map(|(chain_index, keys_node)| (&keys_node.value.0, chain_index.index())),
);
let affected_accounts = private_account_key_chains
.flat_map(|(key_chain, index)| {

View File

@ -30,7 +30,12 @@ impl PrivacyPreservingAccount {
pub const fn is_private(&self) -> bool {
matches!(
&self,
Self::PrivateOwned(_) | Self::PrivateForeign { npk: _, vpk: _, identifier: _ }
Self::PrivateOwned(_)
| Self::PrivateForeign {
npk: _,
vpk: _,
identifier: _
}
)
}
}
@ -83,7 +88,11 @@ impl AccountManager {
(State::Private(pre), mask)
}
PrivacyPreservingAccount::PrivateForeign { npk, vpk, identifier } => {
PrivacyPreservingAccount::PrivateForeign {
npk,
vpk,
identifier,
} => {
let acc = nssa_core::account::Account::default();
let auth_acc = AccountWithMetadata::new(acc, false, (&npk, identifier));
let pre = AccountPreparedData {
@ -207,10 +216,8 @@ async fn private_acc_preparation(
wallet: &WalletCore,
account_id: AccountId,
) -> Result<AccountPreparedData, ExecutionFailureKind> {
let Some((from_keys, from_acc, from_identifier)) = wallet
.storage
.user_data
.get_private_account(account_id)
let Some((from_keys, from_acc, from_identifier)) =
wallet.storage.user_data.get_private_account(account_id)
else {
return Err(ExecutionFailureKind::KeyNotFoundError);
};
@ -228,8 +235,7 @@ async fn private_acc_preparation(
// TODO: Technically we could allow unauthorized owned accounts, but currently we don't have
// support from that in the wallet.
let sender_pre =
AccountWithMetadata::new(from_acc.clone(), true, (&from_npk, from_identifier));
let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, (&from_npk, from_identifier));
Ok(AccountPreparedData {
nsk: Some(nsk),