mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-04-16 08:03:14 +00:00
refactor private key tree to store a vec<(Identifier, AccountId)>
This commit is contained in:
parent
0ecec7016e
commit
4c050f35dd
@ -178,7 +178,6 @@ async fn private_transfer_to_owned_account_using_claiming_path() -> Result<()> {
|
||||
.storage()
|
||||
.user_data
|
||||
.get_private_account(to_account_id)
|
||||
.cloned()
|
||||
.context("Failed to get private account")?;
|
||||
|
||||
// Send to this account using claiming path (using npk and vpk instead of account ID)
|
||||
@ -347,7 +346,6 @@ async fn private_transfer_to_owned_account_continuous_run_path() -> Result<()> {
|
||||
.storage()
|
||||
.user_data
|
||||
.get_private_account(to_account_id)
|
||||
.cloned()
|
||||
.context("Failed to get private account")?;
|
||||
|
||||
// Send transfer using nullifier and viewing public keys
|
||||
|
||||
@ -64,7 +64,6 @@ async fn sync_private_account_with_non_zero_chain_index() -> Result<()> {
|
||||
.storage()
|
||||
.user_data
|
||||
.get_private_account(to_account_id)
|
||||
.cloned()
|
||||
.context("Failed to get private account")?;
|
||||
|
||||
// Send to this account using claiming path (using npk and vpk instead of account ID)
|
||||
@ -264,16 +263,16 @@ async fn restore_keys_from_seed() -> Result<()> {
|
||||
.expect("Acc 4 should be restored");
|
||||
|
||||
assert_eq!(
|
||||
acc1.value.1.program_owner,
|
||||
acc1.value.1[0].1.program_owner,
|
||||
Program::authenticated_transfer_program().id()
|
||||
);
|
||||
assert_eq!(
|
||||
acc2.value.1.program_owner,
|
||||
acc2.value.1[0].1.program_owner,
|
||||
Program::authenticated_transfer_program().id()
|
||||
);
|
||||
|
||||
assert_eq!(acc1.value.1.balance, 100);
|
||||
assert_eq!(acc2.value.1.balance, 101);
|
||||
assert_eq!(acc1.value.1[0].1.balance, 100);
|
||||
assert_eq!(acc2.value.1[0].1.balance, 101);
|
||||
|
||||
info!("Tree checks passed, testing restored accounts can transact");
|
||||
|
||||
|
||||
@ -1144,7 +1144,6 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
|
||||
.storage()
|
||||
.user_data
|
||||
.get_private_account(recipient_account_id)
|
||||
.cloned()
|
||||
.context("Failed to get private account keys")?;
|
||||
|
||||
// Mint using claiming path (foreign account)
|
||||
|
||||
@ -10,7 +10,7 @@ use crate::key_management::{
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct ChildKeysPrivate {
|
||||
pub value: (KeyChain, nssa::Account, Identifier),
|
||||
pub value: (KeyChain, Vec<(Identifier, nssa::Account)>),
|
||||
pub ccc: [u8; 32],
|
||||
/// Can be [`None`] if root.
|
||||
pub cci: Option<u32>,
|
||||
@ -46,15 +46,14 @@ impl ChildKeysPrivate {
|
||||
viewing_secret_key: vsk,
|
||||
},
|
||||
},
|
||||
nssa::Account::default(),
|
||||
0,
|
||||
vec![],
|
||||
),
|
||||
ccc,
|
||||
cci: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nth_child(&self, cci: u32, identifier: Identifier) -> Self {
|
||||
pub fn nth_child(&self, cci: u32) -> Self {
|
||||
#[expect(clippy::arithmetic_side_effects, reason = "TODO: fix later")]
|
||||
let parent_pt =
|
||||
Scalar::from_repr(self.value.0.private_key_holder.nullifier_secret_key.into())
|
||||
@ -96,8 +95,7 @@ impl ChildKeysPrivate {
|
||||
viewing_secret_key: vsk,
|
||||
},
|
||||
},
|
||||
nssa::Account::default(),
|
||||
identifier,
|
||||
vec![],
|
||||
),
|
||||
ccc,
|
||||
cci: Some(cci),
|
||||
@ -111,17 +109,13 @@ impl ChildKeysPrivate {
|
||||
pub fn child_index(&self) -> Option<u32> {
|
||||
self.cci
|
||||
}
|
||||
|
||||
pub fn account_id(&self) -> nssa::AccountId {
|
||||
nssa::AccountId::from((&self.value.0.nullifier_public_key, self.value.2))
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::single_char_lifetime_names,
|
||||
reason = "TODO add meaningful name"
|
||||
)]
|
||||
impl<'a> From<&'a ChildKeysPrivate> for &'a (KeyChain, nssa::Account, Identifier) {
|
||||
impl<'a> From<&'a ChildKeysPrivate> for &'a (KeyChain, Vec<(Identifier, nssa::Account)>) {
|
||||
fn from(value: &'a ChildKeysPrivate) -> Self {
|
||||
&value.value
|
||||
}
|
||||
@ -131,7 +125,7 @@ impl<'a> From<&'a ChildKeysPrivate> for &'a (KeyChain, nssa::Account, Identifier
|
||||
clippy::single_char_lifetime_names,
|
||||
reason = "TODO add meaningful name"
|
||||
)]
|
||||
impl<'a> From<&'a mut ChildKeysPrivate> for &'a mut (KeyChain, nssa::Account, Identifier) {
|
||||
impl<'a> From<&'a mut ChildKeysPrivate> for &'a mut (KeyChain, Vec<(Identifier, nssa::Account)>) {
|
||||
fn from(value: &'a mut ChildKeysPrivate) -> Self {
|
||||
&mut value.value
|
||||
}
|
||||
@ -143,11 +137,17 @@ impl KeyTreeNode for ChildKeysPrivate {
|
||||
}
|
||||
|
||||
fn derive_child(&self, cci: u32) -> Self {
|
||||
self.nth_child(cci, 0)
|
||||
self.nth_child(cci)
|
||||
}
|
||||
|
||||
fn account_ids(&self) -> Vec<nssa::AccountId> {
|
||||
vec![self.account_id()]
|
||||
self.value
|
||||
.1
|
||||
.iter()
|
||||
.map(|(identifier, _)| {
|
||||
nssa::AccountId::from((&self.value.0.nullifier_public_key, *identifier))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,7 +217,7 @@ mod tests {
|
||||
];
|
||||
|
||||
let root_node = ChildKeysPrivate::root(seed);
|
||||
let child_node = ChildKeysPrivate::nth_child(&root_node, 42_u32, 0);
|
||||
let child_node = ChildKeysPrivate::nth_child(&root_node, 42_u32);
|
||||
|
||||
let expected_ccc: [u8; 32] = [
|
||||
27, 73, 133, 213, 214, 63, 217, 184, 164, 17, 172, 140, 223, 95, 255, 157, 11, 0, 58,
|
||||
|
||||
@ -63,10 +63,7 @@ impl<N: KeyTreeNode> KeyTree<N> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_new_node(
|
||||
&mut self,
|
||||
parent_cci: &ChainIndex,
|
||||
) -> Option<(nssa::AccountId, ChainIndex)> {
|
||||
pub fn generate_new_node(&mut self, parent_cci: &ChainIndex) -> Option<ChainIndex> {
|
||||
let parent_keys = self.key_map.get(parent_cci)?;
|
||||
let next_child_id = self
|
||||
.find_next_last_child_of_id(parent_cci)
|
||||
@ -75,30 +72,28 @@ impl<N: KeyTreeNode> KeyTree<N> {
|
||||
|
||||
let child_keys = parent_keys.derive_child(next_child_id);
|
||||
let account_ids = child_keys.account_ids();
|
||||
let primary_account_id = *account_ids.first().expect("account_ids() must be non-empty");
|
||||
|
||||
for account_id in account_ids {
|
||||
self.account_id_map.insert(account_id, next_cci.clone());
|
||||
}
|
||||
self.key_map.insert(next_cci.clone(), child_keys);
|
||||
|
||||
Some((primary_account_id, next_cci))
|
||||
Some(next_cci)
|
||||
}
|
||||
|
||||
pub fn fill_node(&mut self, chain_index: &ChainIndex) -> Option<(nssa::AccountId, ChainIndex)> {
|
||||
pub fn fill_node(&mut self, chain_index: &ChainIndex) -> Option<ChainIndex> {
|
||||
let parent_keys = self.key_map.get(&chain_index.parent()?)?;
|
||||
let child_id = *chain_index.chain().last()?;
|
||||
|
||||
let child_keys = parent_keys.derive_child(child_id);
|
||||
let account_ids = child_keys.account_ids();
|
||||
let primary_account_id = *account_ids.first().expect("account_ids() must be non-empty");
|
||||
|
||||
for account_id in account_ids {
|
||||
self.account_id_map.insert(account_id, chain_index.clone());
|
||||
}
|
||||
self.key_map.insert(chain_index.clone(), child_keys);
|
||||
|
||||
Some((primary_account_id, chain_index.clone()))
|
||||
Some(chain_index.clone())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -155,7 +150,7 @@ impl<N: KeyTreeNode> KeyTree<N> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_new_node_layered(&mut self) -> Option<(nssa::AccountId, ChainIndex)> {
|
||||
pub fn generate_new_node_layered(&mut self) -> Option<ChainIndex> {
|
||||
self.fill_node(&self.find_next_slot_layered())
|
||||
}
|
||||
|
||||
@ -204,35 +199,27 @@ impl<N: KeyTreeNode> KeyTree<N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyTree<ChildKeysPrivate> {
|
||||
/// Cleanup of non-initialized accounts in a private tree.
|
||||
///
|
||||
/// If account is default, removes them, stops at first non-default account.
|
||||
///
|
||||
/// Walks through tree in lairs of same depth using `ChainIndex::chain_ids_at_depth()`.
|
||||
///
|
||||
/// Chain must be parsed for accounts beforehand.
|
||||
///
|
||||
/// Slow, maintains tree consistency.
|
||||
pub fn cleanup_tree_remove_uninit_layered(&mut self, depth: u32) {
|
||||
let depth = usize::try_from(depth).expect("Depth is expected to fit in usize");
|
||||
'outer: for i in (1..depth).rev() {
|
||||
println!("Cleanup of tree at depth {i}");
|
||||
for id in ChainIndex::chain_ids_at_depth(i) {
|
||||
if let Some(node) = self.key_map.get(&id) {
|
||||
if node.value.1 == nssa::Account::default() {
|
||||
let addr = node.account_id();
|
||||
self.remove(addr);
|
||||
} else {
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyTree<ChildKeysPublic> {
|
||||
/// Generate a new public key node, returning the account ID and chain index.
|
||||
pub fn generate_new_public_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 = *node.account_ids().first()?;
|
||||
Some((account_id, cci))
|
||||
}
|
||||
|
||||
/// Generate a new public key node using layered placement, returning the account ID and chain
|
||||
/// index.
|
||||
pub fn generate_new_public_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 = *node.account_ids().first()?;
|
||||
Some((account_id, cci))
|
||||
}
|
||||
|
||||
/// Cleanup of non-initialized accounts in a public tree.
|
||||
///
|
||||
/// If account is default, removes them, stops at first non-default account.
|
||||
@ -267,6 +254,37 @@ impl KeyTree<ChildKeysPublic> {
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyTree<ChildKeysPrivate> {
|
||||
/// Cleanup of non-initialized accounts in a private tree.
|
||||
///
|
||||
/// If account has no synced entries, removes it, stops at first initialized account.
|
||||
///
|
||||
/// Walks through tree in layers of same depth using `ChainIndex::chain_ids_at_depth()`.
|
||||
///
|
||||
/// Chain must be parsed for accounts beforehand.
|
||||
///
|
||||
/// Slow, maintains tree consistency.
|
||||
pub fn cleanup_tree_remove_uninit_layered(&mut self, depth: u32) {
|
||||
let depth = usize::try_from(depth).expect("Depth is expected to fit in usize");
|
||||
'outer: for i in (1..depth).rev() {
|
||||
println!("Cleanup of tree at depth {i}");
|
||||
for id in ChainIndex::chain_ids_at_depth(i) {
|
||||
if let Some(node) = self.key_map.get(&id) {
|
||||
if node.value.1.is_empty() {
|
||||
let account_ids: Vec<_> = node.account_ids();
|
||||
self.key_map.remove(&id);
|
||||
for addr in account_ids {
|
||||
self.account_id_map.remove(&addr);
|
||||
}
|
||||
} else {
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![expect(clippy::shadow_unrelated, reason = "We don't care about this in tests")]
|
||||
@ -486,25 +504,51 @@ mod tests {
|
||||
.key_map
|
||||
.get_mut(&ChainIndex::from_str("/1").unwrap())
|
||||
.unwrap();
|
||||
acc.value.1.balance = 2;
|
||||
acc.value.1.push((0, {
|
||||
let mut a = nssa::Account::default();
|
||||
a.balance = 2;
|
||||
a
|
||||
}));
|
||||
|
||||
let acc = tree
|
||||
.key_map
|
||||
.get_mut(&ChainIndex::from_str("/2").unwrap())
|
||||
.unwrap();
|
||||
acc.value.1.balance = 3;
|
||||
acc.value.1.push((0, {
|
||||
let mut a = nssa::Account::default();
|
||||
a.balance = 3;
|
||||
a
|
||||
}));
|
||||
|
||||
let acc = tree
|
||||
.key_map
|
||||
.get_mut(&ChainIndex::from_str("/0/1").unwrap())
|
||||
.unwrap();
|
||||
acc.value.1.balance = 5;
|
||||
acc.value.1.push((0, {
|
||||
let mut a = nssa::Account::default();
|
||||
a.balance = 5;
|
||||
a
|
||||
}));
|
||||
|
||||
let acc = tree
|
||||
.key_map
|
||||
.get_mut(&ChainIndex::from_str("/1/0").unwrap())
|
||||
.unwrap();
|
||||
acc.value.1.balance = 6;
|
||||
acc.value.1.push((0, {
|
||||
let mut a = nssa::Account::default();
|
||||
a.balance = 6;
|
||||
a
|
||||
}));
|
||||
|
||||
// Update account_id_map for nodes that now have entries
|
||||
for chain_index_str in ["/1", "/2", "/0/1", "/1/0"] {
|
||||
let id = ChainIndex::from_str(chain_index_str).unwrap();
|
||||
if let Some(node) = tree.key_map.get(&id) {
|
||||
for account_id in node.account_ids() {
|
||||
tree.account_id_map.insert(account_id, id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tree.cleanup_tree_remove_uninit_layered(10);
|
||||
|
||||
@ -526,15 +570,15 @@ mod tests {
|
||||
assert_eq!(key_set, key_set_res);
|
||||
|
||||
let acc = &tree.key_map[&ChainIndex::from_str("/1").unwrap()];
|
||||
assert_eq!(acc.value.1.balance, 2);
|
||||
assert_eq!(acc.value.1[0].1.balance, 2);
|
||||
|
||||
let acc = &tree.key_map[&ChainIndex::from_str("/2").unwrap()];
|
||||
assert_eq!(acc.value.1.balance, 3);
|
||||
assert_eq!(acc.value.1[0].1.balance, 3);
|
||||
|
||||
let acc = &tree.key_map[&ChainIndex::from_str("/0/1").unwrap()];
|
||||
assert_eq!(acc.value.1.balance, 5);
|
||||
assert_eq!(acc.value.1[0].1.balance, 5);
|
||||
|
||||
let acc = &tree.key_map[&ChainIndex::from_str("/1/0").unwrap()];
|
||||
assert_eq!(acc.value.1.balance, 6);
|
||||
assert_eq!(acc.value.1[0].1.balance, 6);
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,10 +172,11 @@ mod tests {
|
||||
// /0/0
|
||||
key_tree_private.generate_new_node_layered().unwrap();
|
||||
// /2
|
||||
let (second_child_id, _) = key_tree_private.generate_new_node_layered().unwrap();
|
||||
let second_chain_index = key_tree_private.generate_new_node_layered().unwrap();
|
||||
|
||||
key_tree_private
|
||||
.get_node(second_child_id)
|
||||
.key_map
|
||||
.get(&second_chain_index)
|
||||
.unwrap()
|
||||
.value
|
||||
.0
|
||||
|
||||
@ -20,7 +20,7 @@ pub struct NSSAUserData {
|
||||
pub default_pub_account_signing_keys: BTreeMap<nssa::AccountId, nssa::PrivateKey>,
|
||||
/// Default private accounts.
|
||||
pub default_user_private_accounts:
|
||||
BTreeMap<nssa::AccountId, (KeyChain, nssa_core::account::Account, Identifier)>,
|
||||
BTreeMap<nssa::AccountId, (KeyChain, Vec<(Identifier, nssa_core::account::Account)>)>,
|
||||
/// Tree of public keys.
|
||||
pub public_key_tree: KeyTreePublic,
|
||||
/// Tree of private keys.
|
||||
@ -46,15 +46,16 @@ impl NSSAUserData {
|
||||
fn valid_private_key_transaction_pairing_check(
|
||||
accounts_keys_map: &BTreeMap<
|
||||
nssa::AccountId,
|
||||
(KeyChain, nssa_core::account::Account, Identifier),
|
||||
(KeyChain, Vec<(Identifier, nssa_core::account::Account)>),
|
||||
>,
|
||||
) -> bool {
|
||||
let mut check_res = true;
|
||||
for (account_id, (key, _, identifier)) in accounts_keys_map {
|
||||
let expected_account_id =
|
||||
nssa::AccountId::from((&key.nullifier_public_key, *identifier));
|
||||
if expected_account_id != *account_id {
|
||||
println!("{expected_account_id}, {account_id}");
|
||||
for (account_id, (key, entries)) in accounts_keys_map {
|
||||
let any_match = entries.iter().any(|(identifier, _)| {
|
||||
nssa::AccountId::from((&key.nullifier_public_key, *identifier)) == *account_id
|
||||
});
|
||||
if !any_match {
|
||||
println!("No matching entry found for account_id {account_id}");
|
||||
check_res = false;
|
||||
}
|
||||
}
|
||||
@ -65,7 +66,7 @@ impl NSSAUserData {
|
||||
default_accounts_keys: BTreeMap<nssa::AccountId, nssa::PrivateKey>,
|
||||
default_accounts_key_chains: BTreeMap<
|
||||
nssa::AccountId,
|
||||
(KeyChain, nssa_core::account::Account, Identifier),
|
||||
(KeyChain, Vec<(Identifier, nssa_core::account::Account)>),
|
||||
>,
|
||||
public_key_tree: KeyTreePublic,
|
||||
private_key_tree: KeyTreePrivate,
|
||||
@ -100,11 +101,11 @@ impl NSSAUserData {
|
||||
match parent_cci {
|
||||
Some(parent_cci) => self
|
||||
.public_key_tree
|
||||
.generate_new_node(&parent_cci)
|
||||
.generate_new_public_node(&parent_cci)
|
||||
.expect("Parent must be present in a tree"),
|
||||
None => self
|
||||
.public_key_tree
|
||||
.generate_new_node_layered()
|
||||
.generate_new_public_node_layered()
|
||||
.expect("Search for new node slot failed"),
|
||||
}
|
||||
}
|
||||
@ -122,11 +123,11 @@ impl NSSAUserData {
|
||||
|
||||
/// Generated new private key for privacy preserving transactions.
|
||||
///
|
||||
/// Returns the `account_id` of new account.
|
||||
/// Returns the `ChainIndex` of the new node.
|
||||
pub fn generate_new_privacy_preserving_transaction_key_chain(
|
||||
&mut self,
|
||||
parent_cci: Option<ChainIndex>,
|
||||
) -> (nssa::AccountId, ChainIndex) {
|
||||
) -> ChainIndex {
|
||||
match parent_cci {
|
||||
Some(parent_cci) => self
|
||||
.private_key_tree
|
||||
@ -139,31 +140,35 @@ impl NSSAUserData {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the signing key for public transaction signatures.
|
||||
/// Returns the key chain and account data for the given private account ID.
|
||||
#[must_use]
|
||||
pub fn get_private_account(
|
||||
&self,
|
||||
account_id: nssa::AccountId,
|
||||
) -> Option<&(KeyChain, nssa_core::account::Account, Identifier)> {
|
||||
self.default_user_private_accounts
|
||||
.get(&account_id)
|
||||
.or_else(|| self.private_key_tree.get_node(account_id).map(Into::into))
|
||||
}
|
||||
|
||||
/// Returns the signing key for public transaction signatures.
|
||||
pub fn get_private_account_mut(
|
||||
&mut self,
|
||||
account_id: &nssa::AccountId,
|
||||
) -> Option<&mut (KeyChain, nssa_core::account::Account, Identifier)> {
|
||||
// First seek in defaults
|
||||
if let Some(key) = self.default_user_private_accounts.get_mut(account_id) {
|
||||
Some(key)
|
||||
// Then seek in tree
|
||||
} else {
|
||||
self.private_key_tree
|
||||
.get_node_mut(*account_id)
|
||||
.map(Into::into)
|
||||
) -> Option<(KeyChain, nssa_core::account::Account, Identifier)> {
|
||||
// Check default accounts
|
||||
if let Some((key_chain, entries)) = self.default_user_private_accounts.get(&account_id) {
|
||||
for &(identifier, ref account) in entries {
|
||||
let expected_id =
|
||||
nssa::AccountId::from((&key_chain.nullifier_public_key, identifier));
|
||||
if expected_id == account_id {
|
||||
return Some((key_chain.clone(), account.clone(), identifier));
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
// Check tree
|
||||
if let Some(node) = self.private_key_tree.get_node(account_id) {
|
||||
let key_chain = &node.value.0;
|
||||
for &(identifier, ref account) in &node.value.1 {
|
||||
let expected_id =
|
||||
nssa::AccountId::from((&key_chain.nullifier_public_key, identifier));
|
||||
if expected_id == account_id {
|
||||
return Some((key_chain.clone(), account.clone(), identifier));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn account_ids(&self) -> impl Iterator<Item = nssa::AccountId> {
|
||||
@ -206,16 +211,14 @@ mod tests {
|
||||
fn new_account() {
|
||||
let mut user_data = NSSAUserData::default();
|
||||
|
||||
let (account_id_private, _) = user_data
|
||||
let chain_index = user_data
|
||||
.generate_new_privacy_preserving_transaction_key_chain(Some(ChainIndex::root()));
|
||||
|
||||
let is_key_chain_generated = user_data.get_private_account(account_id_private).is_some();
|
||||
let is_key_chain_generated = user_data.private_key_tree.key_map.contains_key(&chain_index);
|
||||
|
||||
assert!(is_key_chain_generated);
|
||||
|
||||
let account_id_private_str = account_id_private.to_string();
|
||||
println!("{account_id_private_str:#?}");
|
||||
let key_chain = &user_data.get_private_account(account_id_private).unwrap().0;
|
||||
let key_chain = &user_data.private_key_tree.key_map[&chain_index].value.0;
|
||||
println!("{key_chain:#?}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,7 +98,16 @@ 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");
|
||||
let account_id = AccountId::from((&node.value.0.nullifier_public_key, 0_u128));
|
||||
|
||||
unsafe {
|
||||
(*out_account_id).data = *account_id.value();
|
||||
|
||||
@ -19,7 +19,8 @@
|
||||
},
|
||||
{
|
||||
"Private": {
|
||||
"account_id": "9DGDXnrNo4QhUUb2F8WDuDrPESja3eYDkZG5HkzvAvMC",
|
||||
"account_id": "GoKB6RuE6pT2KxCqDXQqiCuuuYZaGdJNfctzyqRdGBCy",
|
||||
"identifier": 0,
|
||||
"account": {
|
||||
"program_owner": [
|
||||
0,
|
||||
@ -214,7 +215,8 @@
|
||||
},
|
||||
{
|
||||
"Private": {
|
||||
"account_id": "A6AT9UvsgitUi8w4BH43n6DyX1bK37DtSCfjEWXQQUrQ",
|
||||
"account_id": "BCdMnPkdH2DrVhe7cGdawkPU9iapsSboRvJpWX8pWnLq",
|
||||
"identifier": 0,
|
||||
"account": {
|
||||
"program_owner": [
|
||||
0,
|
||||
|
||||
@ -77,8 +77,10 @@ impl WalletChainStore {
|
||||
public_init_acc_map.insert(data.account_id, data.pub_sign_key);
|
||||
}
|
||||
InitialAccountData::Private(data) => {
|
||||
private_init_acc_map
|
||||
.insert(data.account_id, (data.key_chain, data.account, data.identifier));
|
||||
private_init_acc_map.insert(
|
||||
data.account_id,
|
||||
(data.key_chain, vec![(data.identifier, data.account)]),
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -117,8 +119,10 @@ impl WalletChainStore {
|
||||
// startup. Fix this when program id can be fetched
|
||||
// from the node and queried from the wallet.
|
||||
account.program_owner = Program::authenticated_transfer_program().id();
|
||||
private_init_acc_map
|
||||
.insert(data.account_id, (data.key_chain, account, data.identifier));
|
||||
private_init_acc_map.insert(
|
||||
data.account_id,
|
||||
(data.key_chain, vec![(data.identifier, account)]),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -175,24 +179,86 @@ impl WalletChainStore {
|
||||
) {
|
||||
debug!("inserting at address {account_id}, this account {account:?}");
|
||||
|
||||
let entry = self
|
||||
.user_data
|
||||
.default_user_private_accounts
|
||||
.entry(account_id)
|
||||
.and_modify(|data| data.1 = account.clone());
|
||||
// Update default accounts if present
|
||||
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
|
||||
.iter()
|
||||
.find_map(|(id, _)| {
|
||||
if nssa::AccountId::from((&key_chain.nullifier_public_key, *id)) == account_id {
|
||||
Some(*id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or(0);
|
||||
// Update existing entry or insert new one
|
||||
if let Some((_, acc)) = entries.iter_mut().find(|(id, _)| *id == identifier) {
|
||||
*acc = account;
|
||||
} else {
|
||||
entries.push((identifier, account));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if matches!(entry, Entry::Vacant(_)) {
|
||||
self.user_data
|
||||
// Otherwise update the private key tree
|
||||
// Identifier is hardcoded to 0 until ciphertexts carry the identifier
|
||||
let identifier: nssa_core::Identifier = 0;
|
||||
|
||||
// Find the node by iterating all tree nodes for this account_id
|
||||
let chain_index = self
|
||||
.user_data
|
||||
.private_key_tree
|
||||
.account_id_map
|
||||
.get(&account_id)
|
||||
.cloned();
|
||||
|
||||
if let Some(chain_index) = chain_index {
|
||||
// Node already in account_id_map — update its entry
|
||||
if let Some(node) = self
|
||||
.user_data
|
||||
.private_key_tree
|
||||
.account_id_map
|
||||
.get(&account_id)
|
||||
.map(|chain_index| {
|
||||
.key_map
|
||||
.get_mut(&chain_index)
|
||||
{
|
||||
if let Some((_, acc)) =
|
||||
node.value.1.iter_mut().find(|(id, _)| *id == identifier)
|
||||
{
|
||||
*acc = account;
|
||||
} else {
|
||||
node.value.1.push((identifier, account));
|
||||
}
|
||||
}
|
||||
} 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,
|
||||
));
|
||||
if expected_id == account_id {
|
||||
if let Some((_, acc)) =
|
||||
node.value.1.iter_mut().find(|(id, _)| *id == identifier)
|
||||
{
|
||||
*acc = account;
|
||||
} else {
|
||||
node.value.1.push((identifier, account));
|
||||
}
|
||||
// Register in account_id_map
|
||||
self.user_data
|
||||
.private_key_tree
|
||||
.key_map
|
||||
.entry(chain_index.clone())
|
||||
.and_modify(|data| data.value.1 = account)
|
||||
});
|
||||
.account_id_map
|
||||
.insert(account_id, ci.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -229,7 +295,10 @@ mod tests {
|
||||
data: public_data,
|
||||
}),
|
||||
PersistentAccountData::Private(Box::new(PersistentAccountDataPrivate {
|
||||
account_id: private_data.account_id(),
|
||||
account_id: nssa::AccountId::from((
|
||||
&private_data.value.0.nullifier_public_key,
|
||||
0_u128,
|
||||
)),
|
||||
chain_index: ChainIndex::root(),
|
||||
data: private_data,
|
||||
})),
|
||||
|
||||
@ -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, PublicKey, program::Program};
|
||||
use nssa::{Account, AccountId, PublicKey, program::Program};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use token_core::{TokenDefinition, TokenHolding};
|
||||
|
||||
@ -147,7 +147,17 @@ impl WalletSubcommand for NewSubcommand {
|
||||
anyhow::bail!("Label '{label}' is already in use by another account");
|
||||
}
|
||||
|
||||
let (account_id, chain_index) = wallet_core.create_new_account_private(cci);
|
||||
let chain_index = wallet_core.create_new_account_private(cci);
|
||||
|
||||
let node = wallet_core
|
||||
.storage
|
||||
.user_data
|
||||
.private_key_tree
|
||||
.key_map
|
||||
.get(&chain_index)
|
||||
.expect("Node was just inserted");
|
||||
let key = &node.value.0;
|
||||
let account_id = AccountId::from((&key.nullifier_public_key, 0_u128));
|
||||
|
||||
if let Some(label) = label {
|
||||
wallet_core
|
||||
@ -156,12 +166,6 @@ impl WalletSubcommand for NewSubcommand {
|
||||
.insert(account_id.to_string(), Label::new(label));
|
||||
}
|
||||
|
||||
let (key, _, _) = wallet_core
|
||||
.storage
|
||||
.user_data
|
||||
.get_private_account(account_id)
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
"Generated new account with account_id Private/{account_id} at path {chain_index}",
|
||||
);
|
||||
|
||||
@ -188,16 +188,18 @@ pub fn produce_data_for_storage(
|
||||
);
|
||||
}
|
||||
|
||||
for (account_id, (key_chain, account, identifier)) in &user_data.default_user_private_accounts {
|
||||
vec_for_storage.push(
|
||||
InitialAccountData::Private(Box::new(PrivateAccountPrivateInitialData {
|
||||
account_id: *account_id,
|
||||
account: account.clone(),
|
||||
key_chain: key_chain.clone(),
|
||||
identifier: *identifier,
|
||||
}))
|
||||
.into(),
|
||||
);
|
||||
for (account_id, (key_chain, entries)) in &user_data.default_user_private_accounts {
|
||||
for (identifier, account) in entries {
|
||||
vec_for_storage.push(
|
||||
InitialAccountData::Private(Box::new(PrivateAccountPrivateInitialData {
|
||||
account_id: *account_id,
|
||||
account: account.clone(),
|
||||
key_chain: key_chain.clone(),
|
||||
identifier: *identifier,
|
||||
}))
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PersistentStorage {
|
||||
|
||||
@ -259,7 +259,7 @@ impl WalletCore {
|
||||
pub fn create_new_account_private(
|
||||
&mut self,
|
||||
chain_index: Option<ChainIndex>,
|
||||
) -> (AccountId, ChainIndex) {
|
||||
) -> ChainIndex {
|
||||
self.storage
|
||||
.user_data
|
||||
.generate_new_privacy_preserving_transaction_key_chain(chain_index)
|
||||
@ -295,14 +295,14 @@ impl WalletCore {
|
||||
self.storage
|
||||
.user_data
|
||||
.get_private_account(account_id)
|
||||
.map(|value| value.1.clone())
|
||||
.map(|(_keys, account, _identifier)| account)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_private_account_commitment(&self, account_id: AccountId) -> Option<Commitment> {
|
||||
let (_keys, account, _identifier) =
|
||||
self.storage.user_data.get_private_account(account_id)?;
|
||||
Some(Commitment::new(&account_id, account))
|
||||
Some(Commitment::new(&account_id, &account))
|
||||
}
|
||||
|
||||
/// Poll transactions.
|
||||
@ -485,14 +485,16 @@ impl WalletCore {
|
||||
.user_data
|
||||
.default_user_private_accounts
|
||||
.iter()
|
||||
.map(|(acc_account_id, (key_chain, _, _))| (*acc_account_id, key_chain, None))
|
||||
.map(|(acc_account_id, (key_chain, _))| (*acc_account_id, key_chain, None))
|
||||
.chain(self.storage.user_data.private_key_tree.key_map.iter().map(
|
||||
|(chain_index, keys_node)| {
|
||||
(
|
||||
keys_node.account_id(),
|
||||
&keys_node.value.0,
|
||||
chain_index.index(),
|
||||
)
|
||||
// Use identifier=0 as the expected first account for this node.
|
||||
// The actual identifier will be confirmed once the account is synced.
|
||||
let account_id = nssa::AccountId::from((
|
||||
&keys_node.value.0.nullifier_public_key,
|
||||
0_u128,
|
||||
));
|
||||
(account_id, &keys_node.value.0, chain_index.index())
|
||||
},
|
||||
));
|
||||
|
||||
|
||||
@ -211,7 +211,6 @@ async fn private_acc_preparation(
|
||||
.storage
|
||||
.user_data
|
||||
.get_private_account(account_id)
|
||||
.cloned()
|
||||
else {
|
||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user