diff --git a/key_protocol/src/key_management/key_tree/mod.rs b/key_protocol/src/key_management/key_tree/mod.rs index 01a4922e..4ecdcf75 100644 --- a/key_protocol/src/key_management/key_tree/mod.rs +++ b/key_protocol/src/key_management/key_tree/mod.rs @@ -2,6 +2,7 @@ use std::collections::BTreeMap; use anyhow::Result; use nssa::{Account, AccountId}; +use nssa_core::Identifier; use serde::{Deserialize, Serialize}; use crate::key_management::{ @@ -255,26 +256,30 @@ impl KeyTree { } impl KeyTree { - /// Generate a new private key node, registering the identifier=0 account_id immediately. - pub fn generate_new_private_node( - &mut self, - parent_cci: &ChainIndex, - ) -> Option<(nssa::AccountId, ChainIndex)> { - let cci = self.generate_new_node(parent_cci)?; - let node = self.key_map.get(&cci)?; - let account_id = nssa::AccountId::from((&node.value.0.nullifier_public_key, 0_u128)); - self.account_id_map.insert(account_id, cci.clone()); - Some((account_id, cci)) + pub fn generate_new_private_node(&mut self, parent_cci: &ChainIndex) -> Option { + self.generate_new_node(parent_cci) } - /// Generate a new private key node using layered placement, registering the identifier=0 - /// account_id immediately. - pub fn generate_new_private_node_layered(&mut self) -> Option<(nssa::AccountId, ChainIndex)> { - let cci = self.generate_new_node_layered()?; - let node = self.key_map.get(&cci)?; - let account_id = nssa::AccountId::from((&node.value.0.nullifier_public_key, 0_u128)); + pub fn generate_new_private_node_layered(&mut self) -> Option { + self.generate_new_node_layered() + } + + /// Register an additional identifier on an existing private key node, inserting the derived + /// `AccountId` into `account_id_map`. Returns `None` if the node does not exist or the + /// `AccountId` is already registered. + pub fn register_identifier_on_node( + &mut self, + cci: &ChainIndex, + identifier: Identifier, + ) -> Option { + let node = self.key_map.get(cci)?; + let account_id = + nssa::AccountId::from((&node.value.0.nullifier_public_key, identifier)); + if self.account_id_map.contains_key(&account_id) { + return None; + } self.account_id_map.insert(account_id, cci.clone()); - Some((account_id, cci)) + Some(account_id) } /// Cleanup of non-initialized accounts in a private tree. diff --git a/key_protocol/src/key_protocol_core/mod.rs b/key_protocol/src/key_protocol_core/mod.rs index b756c8f9..fb0943d9 100644 --- a/key_protocol/src/key_protocol_core/mod.rs +++ b/key_protocol/src/key_protocol_core/mod.rs @@ -121,13 +121,11 @@ impl NSSAUserData { .or_else(|| self.public_key_tree.get_node(account_id).map(Into::into)) } - /// Generated new private key for privacy preserving transactions. - /// - /// Returns the `AccountId` (for identifier=0) and `ChainIndex` of the new node. + /// Generates a new private key node and returns its `ChainIndex`. pub fn generate_new_privacy_preserving_transaction_key_chain( &mut self, parent_cci: Option, - ) -> (nssa::AccountId, ChainIndex) { + ) -> ChainIndex { match parent_cci { Some(parent_cci) => self .private_key_tree @@ -140,6 +138,18 @@ impl NSSAUserData { } } + /// Registers an additional identifier on an existing private key node, deriving and recording + /// the corresponding `AccountId`. Returns `None` if the node does not exist or the identifier + /// is already registered. + pub fn register_identifier_on_private_key_chain( + &mut self, + cci: ChainIndex, + identifier: Identifier, + ) -> Option { + self.private_key_tree + .register_identifier_on_node(&cci, identifier) + } + /// Returns the key chain and account data for the given private account ID. #[must_use] pub fn get_private_account( @@ -211,16 +221,12 @@ mod tests { fn new_account() { let mut user_data = NSSAUserData::default(); - let (account_id, chain_index) = user_data + let chain_index = user_data .generate_new_privacy_preserving_transaction_key_chain(Some(ChainIndex::root())); let is_key_chain_generated = user_data.private_key_tree.key_map.contains_key(&chain_index); assert!(is_key_chain_generated); - let is_account_id_registered = - user_data.private_key_tree.account_id_map.contains_key(&account_id); - assert!(is_account_id_registered); - let key_chain = &user_data.private_key_tree.key_map[&chain_index].value.0; println!("{key_chain:#?}"); } diff --git a/wallet-ffi/src/account.rs b/wallet-ffi/src/account.rs index 49f6a8de..1762d73b 100644 --- a/wallet-ffi/src/account.rs +++ b/wallet-ffi/src/account.rs @@ -59,14 +59,14 @@ pub unsafe extern "C" fn wallet_ffi_create_account_public( WalletFfiError::Success } -/// Create a new private account. +/// Create a new private key node. /// -/// Private accounts use privacy-preserving transactions with nullifiers -/// and commitments. +/// Returns the nullifier public key (npk) to share with senders. Account IDs are +/// discovered later via sync when senders initialize accounts under this key. /// /// # Parameters /// - `handle`: Valid wallet handle -/// - `out_account_id`: Output pointer for the new account ID (32 bytes) +/// - `out_npk`: Output pointer for the nullifier public key (32 bytes) /// /// # Returns /// - `Success` on successful creation @@ -74,19 +74,19 @@ pub unsafe extern "C" fn wallet_ffi_create_account_public( /// /// # Safety /// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open` -/// - `out_account_id` must be a valid pointer to a `FfiBytes32` struct +/// - `out_npk` must be a valid pointer to a `FfiBytes32` struct #[no_mangle] pub unsafe extern "C" fn wallet_ffi_create_account_private( handle: *mut WalletHandle, - out_account_id: *mut FfiBytes32, + out_npk: *mut FfiBytes32, ) -> WalletFfiError { let wrapper = match get_wallet(handle) { Ok(w) => w, Err(e) => return e, }; - if out_account_id.is_null() { - print_error("Null output pointer for account_id"); + if out_npk.is_null() { + print_error("Null output pointer for npk"); return WalletFfiError::NullPointer; } @@ -98,10 +98,18 @@ pub unsafe extern "C" fn wallet_ffi_create_account_private( } }; - let (account_id, _chain_index) = wallet.create_new_account_private(None); + let chain_index = wallet.create_new_account_private(None); + + let node = wallet + .storage() + .user_data + .private_key_tree + .key_map + .get(&chain_index) + .expect("Node was just inserted"); unsafe { - (*out_account_id).data = *account_id.value(); + (*out_npk).data = node.value.0.nullifier_public_key.0; } WalletFfiError::Success diff --git a/wallet-ffi/wallet_ffi.h b/wallet-ffi/wallet_ffi.h index 395216b6..c469c9ee 100644 --- a/wallet-ffi/wallet_ffi.h +++ b/wallet-ffi/wallet_ffi.h @@ -243,14 +243,14 @@ enum WalletFfiError wallet_ffi_create_account_public(struct WalletHandle *handle struct FfiBytes32 *out_account_id); /** - * Create a new private account. + * Create a new private key node. * - * Private accounts use privacy-preserving transactions with nullifiers - * and commitments. + * Returns the nullifier public key (npk) to share with senders. Account IDs are + * discovered later via sync when senders initialize accounts under this key. * * # Parameters * - `handle`: Valid wallet handle - * - `out_account_id`: Output pointer for the new account ID (32 bytes) + * - `out_npk`: Output pointer for the nullifier public key (32 bytes) * * # Returns * - `Success` on successful creation @@ -258,10 +258,10 @@ enum WalletFfiError wallet_ffi_create_account_public(struct WalletHandle *handle * * # Safety * - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open` - * - `out_account_id` must be a valid pointer to a `FfiBytes32` struct + * - `out_npk` must be a valid pointer to a `FfiBytes32` struct */ enum WalletFfiError wallet_ffi_create_account_private(struct WalletHandle *handle, - struct FfiBytes32 *out_account_id); + struct FfiBytes32 *out_npk); /** * List all accounts in the wallet. diff --git a/wallet/src/cli/account.rs b/wallet/src/cli/account.rs index c3b9183f..21b8654a 100644 --- a/wallet/src/cli/account.rs +++ b/wallet/src/cli/account.rs @@ -2,7 +2,7 @@ use anyhow::{Context as _, Result}; use clap::Subcommand; use itertools::Itertools as _; use key_protocol::key_management::key_tree::chain_index::ChainIndex; -use nssa::{Account, AccountId, PublicKey, program::Program}; +use nssa::{Account, PublicKey, program::Program}; use sequencer_service_rpc::RpcClient as _; use token_core::{TokenDefinition, TokenHolding}; @@ -87,9 +87,6 @@ pub enum NewSubcommand { #[arg(long)] /// Chain index of a parent node. cci: Option, - #[arg(short, long)] - /// Label to assign to the new account. - label: Option, }, } @@ -136,18 +133,8 @@ impl WalletSubcommand for NewSubcommand { Ok(SubcommandReturnValue::RegisterAccount { account_id }) } - Self::Private { cci, label } => { - if let Some(label) = &label - && wallet_core - .storage - .labels - .values() - .any(|l| l.to_string() == *label) - { - anyhow::bail!("Label '{label}' is already in use by another account"); - } - - let (account_id, chain_index) = wallet_core.create_new_account_private(cci); + Self::Private { cci } => { + let chain_index = wallet_core.create_new_account_private(cci); let node = wallet_core .storage @@ -158,16 +145,7 @@ impl WalletSubcommand for NewSubcommand { .expect("Node was just inserted"); let key = &node.value.0; - if let Some(label) = label { - wallet_core - .storage - .labels - .insert(account_id.to_string(), Label::new(label)); - } - - println!( - "Generated new account with account_id Private/{account_id} at path {chain_index}", - ); + println!("Generated new private key node at path {chain_index}"); println!("With npk {}", hex::encode(key.nullifier_public_key.0)); println!( "With vpk {}", @@ -176,7 +154,7 @@ impl WalletSubcommand for NewSubcommand { wallet_core.store_persistent_data().await?; - Ok(SubcommandReturnValue::RegisterAccount { account_id }) + Ok(SubcommandReturnValue::Empty) } } } diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 3301b513..b2458390 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -259,7 +259,7 @@ impl WalletCore { pub fn create_new_account_private( &mut self, chain_index: Option, - ) -> (AccountId, ChainIndex) { + ) -> ChainIndex { self.storage .user_data .generate_new_privacy_preserving_transaction_key_chain(chain_index)