feat: private tree

This commit is contained in:
Pravdyvy 2025-11-07 16:21:14 +02:00
parent 4a430622f4
commit d9a130cc55
8 changed files with 498 additions and 234 deletions

View File

@ -0,0 +1,115 @@
use std::str::FromStr;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
pub struct ChainIndex(Vec<u32>);
impl FromStr for ChainIndex {
type Err = hex::FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Ok(Self(vec![]));
}
let hex_decoded = hex::decode(s)?;
if !hex_decoded.len().is_multiple_of(4) {
Err(hex::FromHexError::InvalidStringLength)
} else {
let mut res_vec = vec![];
for i in 0..(hex_decoded.len() / 4) {
res_vec.push(u32::from_le_bytes([
hex_decoded[4 * i],
hex_decoded[4 * i + 1],
hex_decoded[4 * i + 2],
hex_decoded[4 * i + 3],
]));
}
Ok(Self(res_vec))
}
}
}
#[allow(clippy::to_string_trait_impl)]
impl ToString for ChainIndex {
fn to_string(&self) -> String {
if self.0.is_empty() {
return "".to_string();
}
let mut res_vec = vec![];
for index in &self.0 {
res_vec.extend_from_slice(&index.to_le_bytes());
}
hex::encode(res_vec)
}
}
impl ChainIndex {
pub fn root() -> Self {
ChainIndex::from_str("").unwrap()
}
pub fn chain(&self) -> &[u32] {
&self.0
}
pub fn next_in_line(&self) -> ChainIndex {
let mut chain = self.0.clone();
//ToDo: Add overflow check
if let Some(last_p) = chain.last_mut() {
*last_p += 1
}
ChainIndex(chain)
}
pub fn n_th_child(&self, child_id: u32) -> ChainIndex {
let mut chain = self.0.clone();
chain.push(child_id);
ChainIndex(chain)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_chain_id_root_correct() {
let chain_id = ChainIndex::root();
let chain_id_2 = ChainIndex::from_str("").unwrap();
assert_eq!(chain_id, chain_id_2);
}
#[test]
fn test_chain_id_deser_correct() {
let chain_id = ChainIndex::from_str("01010000").unwrap();
assert_eq!(chain_id.chain(), &[257]);
}
#[test]
fn test_chain_id_next_in_line_correct() {
let chain_id = ChainIndex::from_str("01010000").unwrap();
let next_in_line = chain_id.next_in_line();
assert_eq!(next_in_line, ChainIndex::from_str("02010000").unwrap());
}
#[test]
fn test_chain_id_child_correct() {
let chain_id = ChainIndex::from_str("01010000").unwrap();
let child = chain_id.n_th_child(3);
assert_eq!(child, ChainIndex::from_str("0101000003000000").unwrap());
}
}

View File

@ -0,0 +1,201 @@
use k256::{Scalar, elliptic_curve::PrimeField};
use nssa_core::{NullifierPublicKey, NullifierSecretKey, encryption::IncomingViewingPublicKey};
use serde::{Deserialize, Serialize};
use crate::key_management::{
key_tree::traits::KeyNode,
secret_holders::{IncomingViewingSecretKey, OutgoingViewingSecretKey, SecretSpendingKey},
};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ChildKeysPrivate {
pub ssk: SecretSpendingKey,
pub nsk: NullifierSecretKey,
pub isk: IncomingViewingSecretKey,
pub ovk: OutgoingViewingSecretKey,
pub npk: NullifierPublicKey,
pub ipk: IncomingViewingPublicKey,
pub ccc: [u8; 32],
///Can be None if root
pub cci: Option<u32>,
pub account: nssa::Account,
}
impl KeyNode for ChildKeysPrivate {
fn root(seed: [u8; 64]) -> Self {
let hash_value = hmac_sha512::HMAC::mac(seed, "NSSA_master_priv");
let ssk = SecretSpendingKey(*hash_value.first_chunk::<32>().unwrap());
let ccc = *hash_value.last_chunk::<32>().unwrap();
let nsk = ssk.generate_nullifier_secret_key();
let isk = ssk.generate_incoming_viewing_secret_key();
let ovk = ssk.generate_outgoing_viewing_secret_key();
let npk = (&nsk).into();
let ipk = IncomingViewingPublicKey::from_scalar(isk);
Self {
ssk,
nsk,
isk,
ovk,
npk,
ipk,
ccc,
cci: None,
account: nssa::Account::default(),
}
}
fn n_th_child(&self, cci: u32) -> Self {
let parent_pt = Scalar::from_repr(self.ovk.into()).unwrap()
+ Scalar::from_repr(self.nsk.into()).unwrap()
* Scalar::from_repr(self.isk.into()).unwrap();
let mut input = vec![];
input.extend_from_slice(b"NSSA_seed_priv");
input.extend_from_slice(&parent_pt.to_bytes());
input.extend_from_slice(&cci.to_le_bytes());
let hash_value = hmac_sha512::HMAC::mac(input, self.ccc);
let ssk = SecretSpendingKey(*hash_value.first_chunk::<32>().unwrap());
let ccc = *hash_value.last_chunk::<32>().unwrap();
let nsk = ssk.generate_nullifier_secret_key();
let isk = ssk.generate_incoming_viewing_secret_key();
let ovk = ssk.generate_outgoing_viewing_secret_key();
let npk = (&nsk).into();
let ipk = IncomingViewingPublicKey::from_scalar(isk);
Self {
ssk,
nsk,
isk,
ovk,
npk,
ipk,
ccc,
cci: Some(cci),
account: nssa::Account::default(),
}
}
fn chain_code(&self) -> &[u8; 32] {
&self.ccc
}
fn child_index(&self) -> &Option<u32> {
&self.cci
}
fn address(&self) -> nssa::Address {
nssa::Address::from(&self.npk)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keys_deterministic_generation() {
let root_keys = ChildKeysPrivate::root([42; 64]);
let child_keys = root_keys.n_th_child(5);
assert_eq!(root_keys.cci, None);
assert_eq!(child_keys.cci, Some(5));
assert_eq!(
root_keys.ssk.0,
[
249, 83, 253, 32, 174, 204, 185, 44, 253, 167, 61, 92, 128, 5, 152, 4, 220, 21, 88,
84, 167, 180, 154, 249, 44, 77, 33, 136, 59, 131, 203, 152
]
);
assert_eq!(
child_keys.ssk.0,
[
16, 242, 229, 242, 252, 158, 153, 210, 234, 120, 70, 85, 83, 196, 5, 53, 28, 26,
187, 230, 22, 193, 146, 232, 237, 3, 166, 184, 122, 1, 233, 93
]
);
assert_eq!(
root_keys.nsk,
[
38, 195, 52, 182, 16, 66, 167, 156, 9, 14, 65, 100, 17, 93, 166, 71, 27, 148, 93,
85, 116, 109, 130, 8, 195, 222, 159, 214, 141, 41, 124, 57
]
);
assert_eq!(
child_keys.nsk,
[
215, 46, 2, 151, 174, 60, 86, 154, 5, 3, 175, 245, 12, 176, 220, 58, 250, 118, 236,
49, 254, 221, 229, 58, 40, 1, 170, 145, 175, 108, 23, 170
]
);
assert_eq!(
root_keys.isk,
[
153, 161, 15, 34, 96, 184, 165, 165, 27, 244, 155, 40, 70, 5, 241, 133, 78, 40, 61,
118, 48, 148, 226, 5, 97, 18, 201, 128, 82, 248, 163, 72
]
);
assert_eq!(
child_keys.isk,
[
192, 155, 55, 43, 164, 115, 71, 145, 227, 225, 21, 57, 55, 12, 226, 44, 10, 103,
39, 73, 230, 173, 60, 69, 69, 122, 110, 241, 164, 3, 192, 57
]
);
assert_eq!(
root_keys.ovk,
[
205, 87, 71, 129, 90, 242, 217, 200, 140, 252, 124, 46, 207, 7, 33, 156, 83, 166,
150, 81, 98, 131, 182, 156, 110, 92, 78, 140, 125, 218, 152, 154
]
);
assert_eq!(
child_keys.ovk,
[
131, 202, 219, 172, 219, 29, 48, 120, 226, 209, 209, 10, 216, 173, 48, 167, 233,
17, 35, 155, 30, 217, 176, 120, 72, 146, 250, 226, 165, 178, 255, 90
]
);
assert_eq!(
root_keys.npk.0,
[
65, 176, 149, 243, 192, 45, 216, 177, 169, 56, 229, 7, 28, 66, 204, 87, 109, 83,
152, 64, 14, 188, 179, 210, 147, 60, 22, 251, 203, 70, 89, 215
]
);
assert_eq!(
child_keys.npk.0,
[
69, 104, 130, 115, 48, 134, 19, 188, 67, 148, 163, 54, 155, 237, 57, 27, 136, 228,
111, 233, 205, 158, 149, 31, 84, 11, 241, 176, 243, 12, 138, 249
]
);
assert_eq!(
root_keys.ipk.0,
&[
3, 174, 56, 136, 244, 179, 18, 122, 38, 220, 36, 50, 200, 41, 104, 167, 70, 18, 60,
202, 93, 193, 29, 16, 125, 252, 96, 51, 199, 152, 47, 233, 178
]
);
assert_eq!(
child_keys.ipk.0,
&[
3, 18, 202, 246, 79, 141, 169, 51, 55, 202, 120, 169, 244, 201, 156, 162, 216, 115,
126, 53, 46, 94, 235, 125, 114, 178, 215, 81, 171, 93, 93, 88, 117
]
);
}
}

View File

@ -0,0 +1,119 @@
use serde::{Deserialize, Serialize};
use crate::key_management::key_tree::traits::KeyNode;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ChildKeysPublic {
pub csk: nssa::PrivateKey,
pub cpk: nssa::PublicKey,
pub ccc: [u8; 32],
///Can be None if root
pub cci: Option<u32>,
}
impl KeyNode for ChildKeysPublic {
fn root(seed: [u8; 64]) -> Self {
let hash_value = hmac_sha512::HMAC::mac(seed, "NSSA_master_pub");
let csk = nssa::PrivateKey::try_new(*hash_value.first_chunk::<32>().unwrap()).unwrap();
let ccc = *hash_value.last_chunk::<32>().unwrap();
let cpk = nssa::PublicKey::new_from_private_key(&csk);
Self {
csk,
cpk,
ccc,
cci: None,
}
}
fn n_th_child(&self, cci: u32) -> Self {
let mut hash_input = vec![];
hash_input.extend_from_slice(self.csk.value());
hash_input.extend_from_slice(&cci.to_le_bytes());
let hash_value = hmac_sha512::HMAC::mac(&hash_input, self.ccc);
let csk = nssa::PrivateKey::try_new(*hash_value.first_chunk::<32>().unwrap()).unwrap();
let ccc = *hash_value.last_chunk::<32>().unwrap();
let cpk = nssa::PublicKey::new_from_private_key(&csk);
Self {
csk,
cpk,
ccc,
cci: Some(cci),
}
}
fn chain_code(&self) -> &[u8; 32] {
&self.ccc
}
fn child_index(&self) -> &Option<u32> {
&self.cci
}
fn address(&self) -> nssa::Address {
nssa::Address::from(&self.cpk)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keys_deterministic_generation() {
let root_keys = ChildKeysPublic::root([42; 64]);
let child_keys = root_keys.n_th_child(5);
assert_eq!(root_keys.cci, None);
assert_eq!(child_keys.cci, Some(5));
assert_eq!(
root_keys.ccc,
[
61, 30, 91, 26, 133, 91, 236, 192, 231, 53, 186, 139, 11, 221, 202, 11, 178, 215,
254, 103, 191, 60, 117, 112, 1, 226, 31, 156, 83, 104, 150, 224
]
);
assert_eq!(
child_keys.ccc,
[
67, 26, 102, 68, 189, 155, 102, 80, 199, 188, 112, 142, 207, 157, 36, 210, 48, 224,
35, 6, 112, 180, 11, 190, 135, 218, 9, 14, 84, 231, 58, 98
]
);
assert_eq!(
root_keys.csk.value(),
&[
241, 82, 246, 237, 62, 130, 116, 47, 189, 112, 99, 67, 178, 40, 115, 245, 141, 193,
77, 164, 243, 76, 222, 64, 50, 146, 23, 145, 91, 164, 92, 116
]
);
assert_eq!(
child_keys.csk.value(),
&[
11, 151, 27, 212, 167, 26, 77, 234, 103, 145, 53, 191, 184, 25, 240, 191, 156, 25,
60, 144, 65, 22, 193, 163, 246, 227, 212, 81, 49, 170, 33, 158
]
);
assert_eq!(
root_keys.cpk.value(),
&[
220, 170, 95, 177, 121, 37, 86, 166, 56, 238, 232, 72, 21, 106, 107, 217, 158, 74,
133, 91, 143, 244, 155, 15, 2, 230, 223, 169, 13, 20, 163, 138
]
);
assert_eq!(
child_keys.cpk.value(),
&[
152, 249, 236, 111, 132, 96, 184, 122, 21, 179, 240, 15, 234, 155, 164, 144, 108,
110, 120, 74, 176, 147, 196, 168, 243, 186, 203, 79, 97, 17, 194, 52
]
);
}
}

View File

@ -1,143 +1,35 @@
use std::{
collections::{BTreeMap, HashMap},
str::FromStr,
use std::collections::{BTreeMap, HashMap};
use serde::{Deserialize, Serialize};
use crate::key_management::{
key_tree::{
chain_index::ChainIndex, keys_private::ChildKeysPrivate, keys_public::ChildKeysPublic,
traits::KeyNode,
},
secret_holders::SeedHolder,
};
use crate::key_management::secret_holders::SeedHolder;
pub mod chain_index;
pub mod keys_private;
pub mod keys_public;
pub mod traits;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct ChainIndex(Vec<u32>);
impl FromStr for ChainIndex {
type Err = hex::FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Ok(Self(vec![]));
}
let hex_decoded = hex::decode(s)?;
if !hex_decoded.len().is_multiple_of(4) {
Err(hex::FromHexError::InvalidStringLength)
} else {
let mut res_vec = vec![];
for i in 0..(hex_decoded.len() / 4) {
res_vec.push(u32::from_le_bytes([
hex_decoded[4 * i],
hex_decoded[4 * i + 1],
hex_decoded[4 * i + 2],
hex_decoded[4 * i + 3],
]));
}
Ok(Self(res_vec))
}
}
}
#[allow(clippy::to_string_trait_impl)]
impl ToString for ChainIndex {
fn to_string(&self) -> String {
if self.0.is_empty() {
return "".to_string();
}
let mut res_vec = vec![];
for index in &self.0 {
res_vec.extend_from_slice(&index.to_le_bytes());
}
hex::encode(res_vec)
}
}
impl ChainIndex {
pub fn root() -> Self {
ChainIndex::from_str("").unwrap()
}
pub fn chain(&self) -> &[u32] {
&self.0
}
pub fn next_in_line(&self) -> ChainIndex {
let mut chain = self.0.clone();
//ToDo: Add overflow check
if let Some(last_p) = chain.last_mut() {
*last_p += 1
}
ChainIndex(chain)
}
pub fn n_th_child(&self, child_id: u32) -> ChainIndex {
let mut chain = self.0.clone();
chain.push(child_id);
ChainIndex(chain)
}
}
#[derive(Debug)]
pub struct ChildKeysPublic {
pub csk: nssa::PrivateKey,
pub cpk: nssa::PublicKey,
pub ccc: [u8; 32],
///Can be None if root
pub cci: Option<u32>,
}
impl ChildKeysPublic {
pub fn root(seed: [u8; 64]) -> Self {
let hash_value = hmac_sha512::HMAC::mac(seed, "NSSA_master_pub");
let csk = nssa::PrivateKey::try_new(*hash_value.first_chunk::<32>().unwrap()).unwrap();
let ccc = *hash_value.last_chunk::<32>().unwrap();
let cpk = nssa::PublicKey::new_from_private_key(&csk);
Self {
csk,
cpk,
ccc,
cci: None,
}
}
pub fn n_th_child(&self, cci: u32) -> Self {
let mut hash_input = vec![];
hash_input.extend_from_slice(self.csk.value());
hash_input.extend_from_slice(&cci.to_le_bytes());
let hash_value = hmac_sha512::HMAC::mac(&hash_input, self.ccc);
let csk = nssa::PrivateKey::try_new(*hash_value.first_chunk::<32>().unwrap()).unwrap();
let ccc = *hash_value.last_chunk::<32>().unwrap();
let cpk = nssa::PublicKey::new_from_private_key(&csk);
Self {
csk,
cpk,
ccc,
cci: Some(cci),
}
}
}
#[derive(Debug)]
pub struct KeyTreePublic {
pub key_map: BTreeMap<ChainIndex, ChildKeysPublic>,
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct KeyTree<Node: KeyNode> {
pub key_map: BTreeMap<ChainIndex, Node>,
pub addr_map: HashMap<nssa::Address, ChainIndex>,
}
impl KeyTreePublic {
pub type KeyTreePublic = KeyTree<ChildKeysPublic>;
pub type KeyTreePrivate = KeyTree<ChildKeysPrivate>;
impl<Node: KeyNode> KeyTree<Node> {
pub fn new(seed: &SeedHolder) -> Self {
let seed_fit: [u8; 64] = seed.seed.clone().try_into().unwrap();
let root_keys = ChildKeysPublic::root(seed_fit);
let address = nssa::Address::from(&root_keys.cpk);
let root_keys = Node::root(seed_fit);
let address = root_keys.address();
let mut key_map = BTreeMap::new();
let mut addr_map = HashMap::new();
@ -199,7 +91,7 @@ impl KeyTreePublic {
let child_keys = father_keys.n_th_child(next_child_id);
let address = nssa::Address::from(&child_keys.cpk);
let address = child_keys.address();
self.key_map.insert(next_cci.clone(), child_keys);
self.addr_map.insert(address, next_cci);
@ -207,7 +99,7 @@ impl KeyTreePublic {
Some(address)
}
pub fn get_pub_keys(&self, addr: nssa::Address) -> Option<&ChildKeysPublic> {
pub fn get_pub_keys(&self, addr: nssa::Address) -> Option<&Node> {
self.addr_map
.get(&addr)
.and_then(|chain_id| self.key_map.get(chain_id))
@ -216,95 +108,12 @@ impl KeyTreePublic {
#[cfg(test)]
mod tests {
use std::str::FromStr;
use nssa::Address;
use super::*;
#[test]
fn test_chain_id_root_correct() {
let chain_id = ChainIndex::root();
let chain_id_2 = ChainIndex::from_str("").unwrap();
assert_eq!(chain_id, chain_id_2);
}
#[test]
fn test_chain_id_deser_correct() {
let chain_id = ChainIndex::from_str("01010000").unwrap();
assert_eq!(chain_id.chain(), &[257]);
}
#[test]
fn test_chain_id_next_in_line_correct() {
let chain_id = ChainIndex::from_str("01010000").unwrap();
let next_in_line = chain_id.next_in_line();
assert_eq!(next_in_line, ChainIndex::from_str("02010000").unwrap());
}
#[test]
fn test_chain_id_child_correct() {
let chain_id = ChainIndex::from_str("01010000").unwrap();
let child = chain_id.n_th_child(3);
assert_eq!(child, ChainIndex::from_str("0101000003000000").unwrap());
}
#[test]
fn test_keys_deterministic_generation() {
let root_keys = ChildKeysPublic::root([42; 64]);
let child_keys = root_keys.n_th_child(5);
assert_eq!(root_keys.cci, None);
assert_eq!(child_keys.cci, Some(5));
assert_eq!(
root_keys.ccc,
[
61, 30, 91, 26, 133, 91, 236, 192, 231, 53, 186, 139, 11, 221, 202, 11, 178, 215,
254, 103, 191, 60, 117, 112, 1, 226, 31, 156, 83, 104, 150, 224
]
);
assert_eq!(
child_keys.ccc,
[
67, 26, 102, 68, 189, 155, 102, 80, 199, 188, 112, 142, 207, 157, 36, 210, 48, 224,
35, 6, 112, 180, 11, 190, 135, 218, 9, 14, 84, 231, 58, 98
]
);
assert_eq!(
root_keys.csk.value(),
&[
241, 82, 246, 237, 62, 130, 116, 47, 189, 112, 99, 67, 178, 40, 115, 245, 141, 193,
77, 164, 243, 76, 222, 64, 50, 146, 23, 145, 91, 164, 92, 116
]
);
assert_eq!(
child_keys.csk.value(),
&[
11, 151, 27, 212, 167, 26, 77, 234, 103, 145, 53, 191, 184, 25, 240, 191, 156, 25,
60, 144, 65, 22, 193, 163, 246, 227, 212, 81, 49, 170, 33, 158
]
);
assert_eq!(
root_keys.cpk.value(),
&[
220, 170, 95, 177, 121, 37, 86, 166, 56, 238, 232, 72, 21, 106, 107, 217, 158, 74,
133, 91, 143, 244, 155, 15, 2, 230, 223, 169, 13, 20, 163, 138
]
);
assert_eq!(
child_keys.cpk.value(),
&[
152, 249, 236, 111, 132, 96, 184, 122, 21, 179, 240, 15, 234, 155, 164, 144, 108,
110, 120, 74, 176, 147, 196, 168, 243, 186, 203, 79, 97, 17, 194, 52
]
);
}
fn seed_holder_for_tests() -> SeedHolder {
SeedHolder {
seed: [42; 64].to_vec(),

View File

@ -0,0 +1,11 @@
pub trait KeyNode {
fn root(seed: [u8; 64]) -> Self;
fn n_th_child(&self, cci: u32) -> Self;
fn chain_code(&self) -> &[u8; 32];
fn child_index(&self) -> &Option<u32>;
fn address(&self) -> nssa::Address;
}

View File

@ -45,8 +45,8 @@ impl SeedHolder {
}
pub fn new_mnemonic(passphrase: String) -> Self {
let mut enthopy_bytes: [u8; 32] = [0; 32];
OsRng.fill_bytes(&mut enthopy_bytes);
//Enthropy bytes must be deterministic as well
let enthopy_bytes: [u8; 32] = [0; 32];
let mnemonic = Mnemonic::from_entropy(&enthopy_bytes).unwrap();
let seed_wide = mnemonic.to_seed(passphrase);

View File

@ -4,18 +4,26 @@ use anyhow::Result;
use k256::AffinePoint;
use serde::{Deserialize, Serialize};
use crate::key_management::KeyChain;
use crate::key_management::{
KeyChain,
key_tree::{KeyTreePrivate, KeyTreePublic, chain_index::ChainIndex},
};
pub type PublicKey = AffinePoint;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NSSAUserData {
///Map for all user public accounts
pub pub_account_signing_keys: HashMap<nssa::Address, nssa::PrivateKey>,
///Map for all user private accounts
pub user_private_accounts: HashMap<nssa::Address, (KeyChain, nssa_core::account::Account)>,
///Default public accounts
pub default_pub_account_signing_keys: HashMap<nssa::Address, nssa::PrivateKey>,
///Default private accounts
pub default_user_private_accounts:
HashMap<nssa::Address, (KeyChain, nssa_core::account::Account)>,
///Mnemonic passphrase
pub password: String,
/// Tree of public keys
pub public_key_tree: KeyTreePublic,
/// Tree of private keys
pub private_key_tree: KeyTreePrivate,
}
impl NSSAUserData {
@ -97,13 +105,13 @@ impl NSSAUserData {
/// Generated new private key for public transaction signatures
///
/// Returns the address of new account
pub fn generate_new_public_transaction_private_key(&mut self) -> nssa::Address {
let private_key = nssa::PrivateKey::new_os_random();
let address = nssa::Address::from(&nssa::PublicKey::new_from_private_key(&private_key));
self.pub_account_signing_keys.insert(address, private_key);
address
pub fn generate_new_public_transaction_private_key(
&mut self,
parent_cci: ChainIndex,
) -> nssa::Address {
self.public_key_tree
.generate_new_pub_keys(parent_cci)
.unwrap()
}
/// Returns the signing key for public transaction signatures
@ -111,7 +119,7 @@ impl NSSAUserData {
&self,
address: &nssa::Address,
) -> Option<&nssa::PrivateKey> {
self.pub_account_signing_keys.get(address)
self.public_key_tree.get_pub_keys(address)
}
/// Generated new private key for privacy preserving transactions

View File

@ -1,10 +1,11 @@
use nssa_core::address::Address;
use serde::{Deserialize, Serialize};
use crate::{PrivateKey, error::NssaError};
use sha2::{Digest, Sha256};
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PublicKey([u8; 32]);
impl PublicKey {