mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-01 22:03:15 +00:00
feat: deterministic keys
This commit is contained in:
parent
20c276e63e
commit
ec149d3227
@ -36,6 +36,9 @@ path = "../wallet"
|
|||||||
[dependencies.common]
|
[dependencies.common]
|
||||||
path = "../common"
|
path = "../common"
|
||||||
|
|
||||||
|
[dependencies.key_protocol]
|
||||||
|
path = "../key_protocol"
|
||||||
|
|
||||||
[dependencies.nssa]
|
[dependencies.nssa]
|
||||||
path = "../nssa"
|
path = "../nssa"
|
||||||
features = ["no_docker"]
|
features = ["no_docker"]
|
||||||
|
|||||||
@ -54,6 +54,8 @@ fn make_private_account_input_from_str(addr: &str) -> String {
|
|||||||
pub async fn pre_test(
|
pub async fn pre_test(
|
||||||
home_dir: PathBuf,
|
home_dir: PathBuf,
|
||||||
) -> Result<(ServerHandle, JoinHandle<Result<()>>, TempDir)> {
|
) -> Result<(ServerHandle, JoinHandle<Result<()>>, TempDir)> {
|
||||||
|
wallet::execute_setup("test_pass".to_string()).await?;
|
||||||
|
|
||||||
let home_dir_sequencer = home_dir.join("sequencer");
|
let home_dir_sequencer = home_dir.join("sequencer");
|
||||||
|
|
||||||
let mut sequencer_config =
|
let mut sequencer_config =
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use std::{collections::HashMap, path::PathBuf, pin::Pin, time::Duration};
|
use std::{collections::HashMap, path::PathBuf, pin::Pin, time::Duration};
|
||||||
|
|
||||||
use common::{PINATA_BASE58, sequencer_client::SequencerClient};
|
use common::{PINATA_BASE58, sequencer_client::SequencerClient};
|
||||||
|
use key_protocol::key_management::key_tree::chain_index::ChainIndex;
|
||||||
use log::info;
|
use log::info;
|
||||||
use nssa::{Address, ProgramDeploymentTransaction, program::Program};
|
use nssa::{Address, ProgramDeploymentTransaction, program::Program};
|
||||||
use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point};
|
use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point};
|
||||||
@ -13,7 +14,7 @@ use wallet::{
|
|||||||
pinata_program::PinataProgramAgnosticSubcommand,
|
pinata_program::PinataProgramAgnosticSubcommand,
|
||||||
token_program::TokenProgramAgnosticSubcommand,
|
token_program::TokenProgramAgnosticSubcommand,
|
||||||
},
|
},
|
||||||
config::{PersistentAccountData, PersistentStorage},
|
config::PersistentStorage,
|
||||||
helperfunctions::{fetch_config, fetch_persistent_storage},
|
helperfunctions::{fetch_config, fetch_persistent_storage},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -72,7 +73,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
#[nssa_integration_test]
|
#[nssa_integration_test]
|
||||||
pub async fn test_success_move_to_another_account() {
|
pub async fn test_success_move_to_another_account() {
|
||||||
info!("########## test_success_move_to_another_account ##########");
|
info!("########## test_success_move_to_another_account ##########");
|
||||||
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {}));
|
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
}));
|
||||||
|
|
||||||
let wallet_config = fetch_config().await.unwrap();
|
let wallet_config = fetch_config().await.unwrap();
|
||||||
|
|
||||||
@ -273,47 +276,43 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
let wallet_config = fetch_config().await.unwrap();
|
let wallet_config = fetch_config().await.unwrap();
|
||||||
|
|
||||||
// Create new account for the token definition
|
// Create new account for the token definition
|
||||||
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
let SubcommandReturnValue::RegisterAccount {
|
||||||
NewSubcommand::Public {},
|
addr: definition_addr,
|
||||||
|
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
|
NewSubcommand::Public {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("invalid subcommand return value");
|
||||||
|
};
|
||||||
// Create new account for the token supply holder
|
// Create new account for the token supply holder
|
||||||
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } =
|
||||||
NewSubcommand::Public {},
|
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
)))
|
NewSubcommand::Public {
|
||||||
.await
|
cci: ChainIndex::root(),
|
||||||
.unwrap();
|
},
|
||||||
|
)))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("invalid subcommand return value");
|
||||||
|
};
|
||||||
// Create new account for receiving a token transaction
|
// Create new account for receiving a token transaction
|
||||||
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
let SubcommandReturnValue::RegisterAccount {
|
||||||
NewSubcommand::Public {},
|
addr: recipient_addr,
|
||||||
|
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
|
NewSubcommand::Public {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
else {
|
||||||
let PersistentStorage {
|
panic!("invalid subcommand return value");
|
||||||
accounts: persistent_accounts,
|
};
|
||||||
last_synced_block: _,
|
|
||||||
} = fetch_persistent_storage().await.unwrap();
|
|
||||||
|
|
||||||
let mut new_persistent_accounts_addr = Vec::new();
|
|
||||||
|
|
||||||
for per_acc in persistent_accounts {
|
|
||||||
match per_acc {
|
|
||||||
PersistentAccountData::Public(per_acc) => {
|
|
||||||
if (per_acc.address.to_string() != ACC_RECEIVER)
|
|
||||||
&& (per_acc.address.to_string() != ACC_SENDER)
|
|
||||||
{
|
|
||||||
new_persistent_accounts_addr.push(per_acc.address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let [definition_addr, supply_addr, recipient_addr] = new_persistent_accounts_addr
|
|
||||||
.try_into()
|
|
||||||
.expect("Failed to produce new account, not present in persistent accounts");
|
|
||||||
|
|
||||||
// Create new token
|
// Create new token
|
||||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||||
@ -433,7 +432,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
let SubcommandReturnValue::RegisterAccount {
|
let SubcommandReturnValue::RegisterAccount {
|
||||||
addr: definition_addr,
|
addr: definition_addr,
|
||||||
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
NewSubcommand::Public {},
|
NewSubcommand::Public {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -443,7 +444,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
// Create new account for the token supply holder (private)
|
// Create new account for the token supply holder (private)
|
||||||
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } =
|
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } =
|
||||||
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
NewSubcommand::Private {},
|
NewSubcommand::Private {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -454,7 +457,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
let SubcommandReturnValue::RegisterAccount {
|
let SubcommandReturnValue::RegisterAccount {
|
||||||
addr: recipient_addr,
|
addr: recipient_addr,
|
||||||
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
NewSubcommand::Private {},
|
NewSubcommand::Private {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -584,7 +589,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
let SubcommandReturnValue::RegisterAccount {
|
let SubcommandReturnValue::RegisterAccount {
|
||||||
addr: definition_addr,
|
addr: definition_addr,
|
||||||
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
NewSubcommand::Public {},
|
NewSubcommand::Public {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -594,7 +601,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
// Create new account for the token supply holder (private)
|
// Create new account for the token supply holder (private)
|
||||||
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } =
|
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } =
|
||||||
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
NewSubcommand::Private {},
|
NewSubcommand::Private {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -605,7 +614,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
let SubcommandReturnValue::RegisterAccount {
|
let SubcommandReturnValue::RegisterAccount {
|
||||||
addr: recipient_addr,
|
addr: recipient_addr,
|
||||||
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
NewSubcommand::Private {},
|
NewSubcommand::Private {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -716,7 +727,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
let SubcommandReturnValue::RegisterAccount {
|
let SubcommandReturnValue::RegisterAccount {
|
||||||
addr: definition_addr,
|
addr: definition_addr,
|
||||||
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
NewSubcommand::Public {},
|
NewSubcommand::Public {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -726,7 +739,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
// Create new account for the token supply holder (public)
|
// Create new account for the token supply holder (public)
|
||||||
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } =
|
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } =
|
||||||
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
NewSubcommand::Public {},
|
NewSubcommand::Public {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -737,7 +752,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
let SubcommandReturnValue::RegisterAccount {
|
let SubcommandReturnValue::RegisterAccount {
|
||||||
addr: recipient_addr,
|
addr: recipient_addr,
|
||||||
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
NewSubcommand::Private {},
|
NewSubcommand::Private {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -847,7 +864,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
let SubcommandReturnValue::RegisterAccount {
|
let SubcommandReturnValue::RegisterAccount {
|
||||||
addr: definition_addr,
|
addr: definition_addr,
|
||||||
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
NewSubcommand::Public {},
|
NewSubcommand::Public {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -857,7 +876,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
// Create new account for the token supply holder (private)
|
// Create new account for the token supply holder (private)
|
||||||
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } =
|
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } =
|
||||||
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
NewSubcommand::Private {},
|
NewSubcommand::Private {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -868,7 +889,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
let SubcommandReturnValue::RegisterAccount {
|
let SubcommandReturnValue::RegisterAccount {
|
||||||
addr: recipient_addr,
|
addr: recipient_addr,
|
||||||
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
NewSubcommand::Public {},
|
NewSubcommand::Public {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -1066,7 +1089,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
);
|
);
|
||||||
let from: Address = ACC_SENDER_PRIVATE.parse().unwrap();
|
let from: Address = ACC_SENDER_PRIVATE.parse().unwrap();
|
||||||
|
|
||||||
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {}));
|
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
}));
|
||||||
|
|
||||||
let sub_ret = wallet::execute_subcommand(command).await.unwrap();
|
let sub_ret = wallet::execute_subcommand(command).await.unwrap();
|
||||||
let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else {
|
let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else {
|
||||||
@ -1082,8 +1107,7 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
let (to_keys, _) = wallet_storage
|
let (to_keys, _) = wallet_storage
|
||||||
.storage
|
.storage
|
||||||
.user_data
|
.user_data
|
||||||
.user_private_accounts
|
.get_private_account(&to_addr)
|
||||||
.get(&to_addr)
|
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -1134,7 +1158,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
|
|
||||||
let from: Address = ACC_SENDER_PRIVATE.parse().unwrap();
|
let from: Address = ACC_SENDER_PRIVATE.parse().unwrap();
|
||||||
|
|
||||||
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {}));
|
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
}));
|
||||||
|
|
||||||
let sub_ret = wallet::execute_subcommand(command).await.unwrap();
|
let sub_ret = wallet::execute_subcommand(command).await.unwrap();
|
||||||
let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else {
|
let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else {
|
||||||
@ -1150,8 +1176,7 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
let (to_keys, _) = wallet_storage
|
let (to_keys, _) = wallet_storage
|
||||||
.storage
|
.storage
|
||||||
.user_data
|
.user_data
|
||||||
.user_private_accounts
|
.get_private_account(&to_addr)
|
||||||
.get(&to_addr)
|
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -1428,7 +1453,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
#[nssa_integration_test]
|
#[nssa_integration_test]
|
||||||
pub async fn test_authenticated_transfer_initialize_function() {
|
pub async fn test_authenticated_transfer_initialize_function() {
|
||||||
info!("########## test initialize account for authenticated transfer ##########");
|
info!("########## test initialize account for authenticated transfer ##########");
|
||||||
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {}));
|
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
}));
|
||||||
let SubcommandReturnValue::RegisterAccount { addr } =
|
let SubcommandReturnValue::RegisterAccount { addr } =
|
||||||
wallet::execute_subcommand(command).await.unwrap()
|
wallet::execute_subcommand(command).await.unwrap()
|
||||||
else {
|
else {
|
||||||
@ -1528,7 +1555,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
// Create new account for the token supply holder (private)
|
// Create new account for the token supply holder (private)
|
||||||
let SubcommandReturnValue::RegisterAccount { addr: winner_addr } =
|
let SubcommandReturnValue::RegisterAccount { addr: winner_addr } =
|
||||||
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
wallet::execute_subcommand(Command::Account(AccountSubcommand::New(
|
||||||
NewSubcommand::Private {},
|
NewSubcommand::Private {
|
||||||
|
cci: ChainIndex::root(),
|
||||||
|
},
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|||||||
@ -14,6 +14,7 @@ hex = "0.4.3"
|
|||||||
aes-gcm.workspace = true
|
aes-gcm.workspace = true
|
||||||
bip39.workspace = true
|
bip39.workspace = true
|
||||||
hmac-sha512.workspace = true
|
hmac-sha512.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
nssa-core = { path = "../nssa/core", features = ["host"] }
|
nssa-core = { path = "../nssa/core", features = ["host"] }
|
||||||
|
|
||||||
[dependencies.common]
|
[dependencies.common]
|
||||||
|
|||||||
@ -1,59 +1,57 @@
|
|||||||
use std::str::FromStr;
|
use std::{fmt::Display, str::FromStr};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
|
||||||
pub struct ChainIndex(Vec<u32>);
|
pub struct ChainIndex(Vec<u32>);
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum ChainIndexError {
|
||||||
|
#[error("No root found")]
|
||||||
|
NoRootFound,
|
||||||
|
#[error("Failed to parse segment into a number")]
|
||||||
|
ParseIntError(#[from] std::num::ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
impl FromStr for ChainIndex {
|
impl FromStr for ChainIndex {
|
||||||
type Err = hex::FromHexError;
|
type Err = ChainIndexError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
if s.is_empty() {
|
if !s.starts_with("/") {
|
||||||
return Ok(Self(vec![]));
|
return Err(ChainIndexError::NoRootFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
let hex_decoded = hex::decode(s)?;
|
if s == "/" {
|
||||||
|
return Ok(ChainIndex(vec![]));
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let uprooted_substring = s.strip_prefix("/").unwrap();
|
||||||
|
|
||||||
|
let splitted_chain: Vec<&str> = uprooted_substring.split("/").collect();
|
||||||
|
let mut res = vec![];
|
||||||
|
|
||||||
|
for split_ch in splitted_chain {
|
||||||
|
let cci = split_ch.parse()?;
|
||||||
|
res.push(cci);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self(res))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::to_string_trait_impl)]
|
impl Display for ChainIndex {
|
||||||
impl ToString for ChainIndex {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
fn to_string(&self) -> String {
|
write!(f, "/")?;
|
||||||
if self.0.is_empty() {
|
for cci in &self.0[..(self.0.len() - 1)] {
|
||||||
return "".to_string();
|
write!(f, "{cci}/")?;
|
||||||
}
|
}
|
||||||
|
write!(f, "{}", self.0.last().unwrap())
|
||||||
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 {
|
impl ChainIndex {
|
||||||
pub fn root() -> Self {
|
pub fn root() -> Self {
|
||||||
ChainIndex::from_str("").unwrap()
|
ChainIndex::from_str("/").unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chain(&self) -> &[u32] {
|
pub fn chain(&self) -> &[u32] {
|
||||||
@ -85,31 +83,40 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_chain_id_root_correct() {
|
fn test_chain_id_root_correct() {
|
||||||
let chain_id = ChainIndex::root();
|
let chain_id = ChainIndex::root();
|
||||||
let chain_id_2 = ChainIndex::from_str("").unwrap();
|
let chain_id_2 = ChainIndex::from_str("/").unwrap();
|
||||||
|
|
||||||
assert_eq!(chain_id, chain_id_2);
|
assert_eq!(chain_id, chain_id_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_chain_id_deser_correct() {
|
fn test_chain_id_deser_correct() {
|
||||||
let chain_id = ChainIndex::from_str("01010000").unwrap();
|
let chain_id = ChainIndex::from_str("/257").unwrap();
|
||||||
|
|
||||||
assert_eq!(chain_id.chain(), &[257]);
|
assert_eq!(chain_id.chain(), &[257]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_chain_id_next_in_line_correct() {
|
fn test_chain_id_next_in_line_correct() {
|
||||||
let chain_id = ChainIndex::from_str("01010000").unwrap();
|
let chain_id = ChainIndex::from_str("/257").unwrap();
|
||||||
let next_in_line = chain_id.next_in_line();
|
let next_in_line = chain_id.next_in_line();
|
||||||
|
|
||||||
assert_eq!(next_in_line, ChainIndex::from_str("02010000").unwrap());
|
assert_eq!(next_in_line, ChainIndex::from_str("/258").unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_chain_id_child_correct() {
|
fn test_chain_id_child_correct() {
|
||||||
let chain_id = ChainIndex::from_str("01010000").unwrap();
|
let chain_id = ChainIndex::from_str("/257").unwrap();
|
||||||
let child = chain_id.n_th_child(3);
|
let child = chain_id.n_th_child(3);
|
||||||
|
|
||||||
assert_eq!(child, ChainIndex::from_str("0101000003000000").unwrap());
|
assert_eq!(child, ChainIndex::from_str("/257/3").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_correct_display() {
|
||||||
|
let chainid = ChainIndex(vec![5, 7, 8]);
|
||||||
|
|
||||||
|
let string_index = format!("{chainid}");
|
||||||
|
|
||||||
|
assert_eq!(string_index, "/5/7/8".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,18 @@ impl<Node: KeyNode> KeyTree<Node> {
|
|||||||
Self { key_map, addr_map }
|
Self { key_map, addr_map }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_from_root(root: Node) -> Self {
|
||||||
|
let mut key_map = BTreeMap::new();
|
||||||
|
let mut addr_map = HashMap::new();
|
||||||
|
|
||||||
|
addr_map.insert(root.address(), ChainIndex::root());
|
||||||
|
key_map.insert(ChainIndex::root(), root);
|
||||||
|
|
||||||
|
Self { key_map, addr_map }
|
||||||
|
}
|
||||||
|
|
||||||
|
//ToDo: Add function to create a tree from list of nodes with consistency check.
|
||||||
|
|
||||||
pub fn find_next_last_child_of_id(&self, parent_id: &ChainIndex) -> Option<u32> {
|
pub fn find_next_last_child_of_id(&self, parent_id: &ChainIndex) -> Option<u32> {
|
||||||
if !self.key_map.contains_key(parent_id) {
|
if !self.key_map.contains_key(parent_id) {
|
||||||
return None;
|
return None;
|
||||||
@ -160,7 +172,7 @@ mod tests {
|
|||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
tree.key_map
|
tree.key_map
|
||||||
.contains_key(&ChainIndex::from_str("00000000").unwrap())
|
.contains_key(&ChainIndex::from_str("/0").unwrap())
|
||||||
);
|
);
|
||||||
|
|
||||||
let next_last_child_for_parent_id = tree
|
let next_last_child_for_parent_id = tree
|
||||||
@ -199,7 +211,7 @@ mod tests {
|
|||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
tree.key_map
|
tree.key_map
|
||||||
.contains_key(&ChainIndex::from_str("00000000").unwrap())
|
.contains_key(&ChainIndex::from_str("/0").unwrap())
|
||||||
);
|
);
|
||||||
|
|
||||||
let next_last_child_for_parent_id = tree
|
let next_last_child_for_parent_id = tree
|
||||||
@ -208,7 +220,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(next_last_child_for_parent_id, 1);
|
assert_eq!(next_last_child_for_parent_id, 1);
|
||||||
|
|
||||||
let key_opt = tree.generate_new_node(ChainIndex::from_str("03000000").unwrap());
|
let key_opt = tree.generate_new_node(ChainIndex::from_str("/3").unwrap());
|
||||||
|
|
||||||
assert_eq!(key_opt, None);
|
assert_eq!(key_opt, None);
|
||||||
}
|
}
|
||||||
@ -229,7 +241,7 @@ mod tests {
|
|||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
tree.key_map
|
tree.key_map
|
||||||
.contains_key(&ChainIndex::from_str("00000000").unwrap())
|
.contains_key(&ChainIndex::from_str("/0").unwrap())
|
||||||
);
|
);
|
||||||
|
|
||||||
let next_last_child_for_parent_id = tree
|
let next_last_child_for_parent_id = tree
|
||||||
@ -242,7 +254,7 @@ mod tests {
|
|||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
tree.key_map
|
tree.key_map
|
||||||
.contains_key(&ChainIndex::from_str("01000000").unwrap())
|
.contains_key(&ChainIndex::from_str("/1").unwrap())
|
||||||
);
|
);
|
||||||
|
|
||||||
let next_last_child_for_parent_id = tree
|
let next_last_child_for_parent_id = tree
|
||||||
@ -251,58 +263,58 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(next_last_child_for_parent_id, 2);
|
assert_eq!(next_last_child_for_parent_id, 2);
|
||||||
|
|
||||||
tree.generate_new_node(ChainIndex::from_str("00000000").unwrap())
|
tree.generate_new_node(ChainIndex::from_str("/0").unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let next_last_child_for_parent_id = tree
|
let next_last_child_for_parent_id = tree
|
||||||
.find_next_last_child_of_id(&ChainIndex::from_str("00000000").unwrap())
|
.find_next_last_child_of_id(&ChainIndex::from_str("/0").unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(next_last_child_for_parent_id, 1);
|
assert_eq!(next_last_child_for_parent_id, 1);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
tree.key_map
|
tree.key_map
|
||||||
.contains_key(&ChainIndex::from_str("0000000000000000").unwrap())
|
.contains_key(&ChainIndex::from_str("/0/0").unwrap())
|
||||||
);
|
);
|
||||||
|
|
||||||
tree.generate_new_node(ChainIndex::from_str("00000000").unwrap())
|
tree.generate_new_node(ChainIndex::from_str("/0").unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let next_last_child_for_parent_id = tree
|
let next_last_child_for_parent_id = tree
|
||||||
.find_next_last_child_of_id(&ChainIndex::from_str("00000000").unwrap())
|
.find_next_last_child_of_id(&ChainIndex::from_str("/0").unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(next_last_child_for_parent_id, 2);
|
assert_eq!(next_last_child_for_parent_id, 2);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
tree.key_map
|
tree.key_map
|
||||||
.contains_key(&ChainIndex::from_str("0000000001000000").unwrap())
|
.contains_key(&ChainIndex::from_str("/0/1").unwrap())
|
||||||
);
|
);
|
||||||
|
|
||||||
tree.generate_new_node(ChainIndex::from_str("00000000").unwrap())
|
tree.generate_new_node(ChainIndex::from_str("/0").unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let next_last_child_for_parent_id = tree
|
let next_last_child_for_parent_id = tree
|
||||||
.find_next_last_child_of_id(&ChainIndex::from_str("00000000").unwrap())
|
.find_next_last_child_of_id(&ChainIndex::from_str("/0").unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(next_last_child_for_parent_id, 3);
|
assert_eq!(next_last_child_for_parent_id, 3);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
tree.key_map
|
tree.key_map
|
||||||
.contains_key(&ChainIndex::from_str("0000000002000000").unwrap())
|
.contains_key(&ChainIndex::from_str("/0/2").unwrap())
|
||||||
);
|
);
|
||||||
|
|
||||||
tree.generate_new_node(ChainIndex::from_str("0000000001000000").unwrap())
|
tree.generate_new_node(ChainIndex::from_str("/0/1").unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
tree.key_map
|
tree.key_map
|
||||||
.contains_key(&ChainIndex::from_str("000000000100000000000000").unwrap())
|
.contains_key(&ChainIndex::from_str("/0/1/0").unwrap())
|
||||||
);
|
);
|
||||||
|
|
||||||
let next_last_child_for_parent_id = tree
|
let next_last_child_for_parent_id = tree
|
||||||
.find_next_last_child_of_id(&ChainIndex::from_str("0000000001000000").unwrap())
|
.find_next_last_child_of_id(&ChainIndex::from_str("/0/1").unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(next_last_child_for_parent_id, 1);
|
assert_eq!(next_last_child_for_parent_id, 1);
|
||||||
|
|||||||
@ -166,4 +166,14 @@ mod tests {
|
|||||||
|
|
||||||
let _ = top_secret_key_holder.generate_outgoing_viewing_secret_key();
|
let _ = top_secret_key_holder.generate_outgoing_viewing_secret_key();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_seeds_generated_same_from_same_mnemonic() {
|
||||||
|
let mnemonic = "test_pass";
|
||||||
|
|
||||||
|
let seed_holder1 = SeedHolder::new_mnemonic(mnemonic.to_string());
|
||||||
|
let seed_holder2 = SeedHolder::new_mnemonic(mnemonic.to_string());
|
||||||
|
|
||||||
|
assert_eq!(seed_holder1.seed, seed_holder2.seed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,8 +19,6 @@ pub struct NSSAUserData {
|
|||||||
///Default private accounts
|
///Default private accounts
|
||||||
pub default_user_private_accounts:
|
pub default_user_private_accounts:
|
||||||
HashMap<nssa::Address, (KeyChain, nssa_core::account::Account)>,
|
HashMap<nssa::Address, (KeyChain, nssa_core::account::Account)>,
|
||||||
///Mnemonic passphrase
|
|
||||||
pub password: String,
|
|
||||||
/// Tree of public keys
|
/// Tree of public keys
|
||||||
pub public_key_tree: KeyTreePublic,
|
pub public_key_tree: KeyTreePublic,
|
||||||
/// Tree of private keys
|
/// Tree of private keys
|
||||||
@ -82,38 +80,6 @@ impl NSSAUserData {
|
|||||||
default_user_private_accounts: default_accounts_key_chains,
|
default_user_private_accounts: default_accounts_key_chains,
|
||||||
public_key_tree,
|
public_key_tree,
|
||||||
private_key_tree,
|
private_key_tree,
|
||||||
password: "mnemonic".to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_with_accounts_and_password(
|
|
||||||
default_accounts_keys: HashMap<nssa::Address, nssa::PrivateKey>,
|
|
||||||
default_accounts_key_chains: HashMap<
|
|
||||||
nssa::Address,
|
|
||||||
(KeyChain, nssa_core::account::Account),
|
|
||||||
>,
|
|
||||||
public_key_tree: KeyTreePublic,
|
|
||||||
private_key_tree: KeyTreePrivate,
|
|
||||||
password: String,
|
|
||||||
) -> Result<Self> {
|
|
||||||
if !Self::valid_public_key_transaction_pairing_check(&default_accounts_keys) {
|
|
||||||
anyhow::bail!(
|
|
||||||
"Key transaction pairing check not satisfied, there is addresses, which is not derived from keys"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Self::valid_private_key_transaction_pairing_check(&default_accounts_key_chains) {
|
|
||||||
anyhow::bail!(
|
|
||||||
"Key transaction pairing check not satisfied, there is addresses, which is not derived from keys"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
default_pub_account_signing_keys: default_accounts_keys,
|
|
||||||
default_user_private_accounts: default_accounts_key_chains,
|
|
||||||
public_key_tree,
|
|
||||||
private_key_tree,
|
|
||||||
password,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,9 +103,7 @@ impl NSSAUserData {
|
|||||||
Some(key)
|
Some(key)
|
||||||
//Then seek in tree
|
//Then seek in tree
|
||||||
} else {
|
} else {
|
||||||
self.public_key_tree
|
self.public_key_tree.get_node(*address).map(Into::into)
|
||||||
.get_node(*address)
|
|
||||||
.and_then(|chain_keys| Some(chain_keys.into()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,9 +127,7 @@ impl NSSAUserData {
|
|||||||
Some(key)
|
Some(key)
|
||||||
//Then seek in tree
|
//Then seek in tree
|
||||||
} else {
|
} else {
|
||||||
self.private_key_tree
|
self.private_key_tree.get_node(*address).map(Into::into)
|
||||||
.get_node(*address)
|
|
||||||
.and_then(|chain_keys| Some(chain_keys.into()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,9 +141,7 @@ impl NSSAUserData {
|
|||||||
Some(key)
|
Some(key)
|
||||||
//Then seek in tree
|
//Then seek in tree
|
||||||
} else {
|
} else {
|
||||||
self.private_key_tree
|
self.private_key_tree.get_node_mut(*address).map(Into::into)
|
||||||
.get_node_mut(*address)
|
|
||||||
.and_then(|chain_keys| Some(chain_keys.into()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use key_protocol::{
|
use key_protocol::{
|
||||||
key_management::{
|
key_management::{
|
||||||
key_tree::{KeyTreePrivate, KeyTreePublic},
|
key_tree::{KeyTreePrivate, KeyTreePublic, chain_index::ChainIndex},
|
||||||
secret_holders::SeedHolder,
|
secret_holders::SeedHolder,
|
||||||
},
|
},
|
||||||
key_protocol_core::NSSAUserData,
|
key_protocol_core::NSSAUserData,
|
||||||
@ -21,8 +21,73 @@ impl WalletChainStore {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
config: WalletConfig,
|
config: WalletConfig,
|
||||||
persistent_accounts: Vec<PersistentAccountData>,
|
persistent_accounts: Vec<PersistentAccountData>,
|
||||||
password: String,
|
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
|
if persistent_accounts.is_empty() {
|
||||||
|
anyhow::bail!("Roots not found; please run setup beforehand");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut public_init_acc_map = HashMap::new();
|
||||||
|
let mut private_init_acc_map = HashMap::new();
|
||||||
|
|
||||||
|
let public_root = persistent_accounts
|
||||||
|
.iter()
|
||||||
|
.find(|data| match data {
|
||||||
|
&PersistentAccountData::Public(data) => data.chain_index == ChainIndex::root(),
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let private_root = persistent_accounts
|
||||||
|
.iter()
|
||||||
|
.find(|data| match data {
|
||||||
|
&PersistentAccountData::Private(data) => data.chain_index == ChainIndex::root(),
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut public_tree = KeyTreePublic::new_from_root(match public_root {
|
||||||
|
PersistentAccountData::Public(data) => data.data,
|
||||||
|
_ => unreachable!(),
|
||||||
|
});
|
||||||
|
let mut private_tree = KeyTreePrivate::new_from_root(match private_root {
|
||||||
|
PersistentAccountData::Private(data) => data.data,
|
||||||
|
_ => unreachable!(),
|
||||||
|
});
|
||||||
|
|
||||||
|
for pers_acc_data in persistent_accounts {
|
||||||
|
match pers_acc_data {
|
||||||
|
PersistentAccountData::Public(data) => {
|
||||||
|
public_tree.insert(data.address, data.chain_index, data.data);
|
||||||
|
}
|
||||||
|
PersistentAccountData::Private(data) => {
|
||||||
|
private_tree.insert(data.address, data.chain_index, data.data);
|
||||||
|
}
|
||||||
|
PersistentAccountData::Preconfigured(acc_data) => match acc_data {
|
||||||
|
InitialAccountData::Public(data) => {
|
||||||
|
public_init_acc_map.insert(data.address.parse()?, data.pub_sign_key);
|
||||||
|
}
|
||||||
|
InitialAccountData::Private(data) => {
|
||||||
|
private_init_acc_map
|
||||||
|
.insert(data.address.parse()?, (data.key_chain, data.account));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
user_data: NSSAUserData::new_with_accounts(
|
||||||
|
public_init_acc_map,
|
||||||
|
private_init_acc_map,
|
||||||
|
public_tree,
|
||||||
|
private_tree,
|
||||||
|
)?,
|
||||||
|
wallet_config: config,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_storage(config: WalletConfig, password: String) -> Result<Self> {
|
||||||
let mut public_init_acc_map = HashMap::new();
|
let mut public_init_acc_map = HashMap::new();
|
||||||
let mut private_init_acc_map = HashMap::new();
|
let mut private_init_acc_map = HashMap::new();
|
||||||
|
|
||||||
@ -42,19 +107,8 @@ impl WalletChainStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut public_tree = KeyTreePublic::new(&SeedHolder::new_mnemonic(password.clone()));
|
let public_tree = KeyTreePublic::new(&SeedHolder::new_mnemonic(password.clone()));
|
||||||
let mut private_tree = KeyTreePrivate::new(&SeedHolder::new_mnemonic(password));
|
let private_tree = KeyTreePrivate::new(&SeedHolder::new_mnemonic(password));
|
||||||
|
|
||||||
for pers_acc_data in persistent_accounts {
|
|
||||||
match pers_acc_data {
|
|
||||||
PersistentAccountData::Public(data) => {
|
|
||||||
public_tree.insert(data.address, data.chain_index, data.data);
|
|
||||||
}
|
|
||||||
PersistentAccountData::Private(data) => {
|
|
||||||
private_tree.insert(data.address, data.chain_index, data.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
user_data: NSSAUserData::new_with_accounts(
|
user_data: NSSAUserData::new_with_accounts(
|
||||||
@ -73,25 +127,41 @@ impl WalletChainStore {
|
|||||||
account: nssa_core::account::Account,
|
account: nssa_core::account::Account,
|
||||||
) {
|
) {
|
||||||
println!("inserting at address {}, this account {:?}", addr, account);
|
println!("inserting at address {}, this account {:?}", addr, account);
|
||||||
self.user_data
|
|
||||||
.private_key_tree
|
if self
|
||||||
.addr_map
|
.user_data
|
||||||
.get(&addr)
|
.default_user_private_accounts
|
||||||
.and_then(|chain_index| {
|
.contains_key(&addr)
|
||||||
Some(
|
{
|
||||||
|
self.user_data
|
||||||
|
.default_user_private_accounts
|
||||||
|
.entry(addr)
|
||||||
|
.and_modify(|data| data.1 = account);
|
||||||
|
} else {
|
||||||
|
self.user_data
|
||||||
|
.private_key_tree
|
||||||
|
.addr_map
|
||||||
|
.get(&addr)
|
||||||
|
.map(|chain_index| {
|
||||||
self.user_data
|
self.user_data
|
||||||
.private_key_tree
|
.private_key_tree
|
||||||
.key_map
|
.key_map
|
||||||
.entry(chain_index.clone())
|
.entry(chain_index.clone())
|
||||||
.and_modify(|data| data.value.1 = account),
|
.and_modify(|data| data.value.1 = account)
|
||||||
)
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::config::InitialAccountData;
|
use key_protocol::key_management::key_tree::{
|
||||||
|
keys_private::ChildKeysPrivate, keys_public::ChildKeysPublic, traits::KeyNode,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::config::{
|
||||||
|
InitialAccountData, PersistentAccountDataPrivate, PersistentAccountDataPublic,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -199,10 +269,35 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_sample_persistent_accounts() -> Vec<PersistentAccountData> {
|
||||||
|
let mut accs = vec![];
|
||||||
|
|
||||||
|
let public_data = ChildKeysPublic::root([42; 64]);
|
||||||
|
|
||||||
|
accs.push(PersistentAccountData::Public(PersistentAccountDataPublic {
|
||||||
|
address: public_data.address(),
|
||||||
|
chain_index: ChainIndex::root(),
|
||||||
|
data: public_data,
|
||||||
|
}));
|
||||||
|
|
||||||
|
let private_data = ChildKeysPrivate::root([47; 64]);
|
||||||
|
|
||||||
|
accs.push(PersistentAccountData::Private(
|
||||||
|
PersistentAccountDataPrivate {
|
||||||
|
address: private_data.address(),
|
||||||
|
chain_index: ChainIndex::root(),
|
||||||
|
data: private_data,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
accs
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_new_initializes_correctly() {
|
fn test_new_initializes_correctly() {
|
||||||
let config = create_sample_wallet_config();
|
let config = create_sample_wallet_config();
|
||||||
|
let accs = create_sample_persistent_accounts();
|
||||||
|
|
||||||
let _ = WalletChainStore::new(config.clone(), vec![], "test_pass".to_string()).unwrap();
|
let _ = WalletChainStore::new(config.clone(), accs).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,14 +90,14 @@ pub enum AccountSubcommand {
|
|||||||
#[derive(Subcommand, Debug, Clone)]
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
pub enum NewSubcommand {
|
pub enum NewSubcommand {
|
||||||
///Register new public account
|
///Register new public account
|
||||||
Public {
|
Public {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
cci: ChainIndex
|
cci: ChainIndex,
|
||||||
},
|
},
|
||||||
///Register new private account
|
///Register new private account
|
||||||
Private {
|
Private {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
cci: ChainIndex
|
cci: ChainIndex,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -49,12 +49,12 @@ pub enum InitialAccountData {
|
|||||||
pub enum PersistentAccountData {
|
pub enum PersistentAccountData {
|
||||||
Public(PersistentAccountDataPublic),
|
Public(PersistentAccountDataPublic),
|
||||||
Private(PersistentAccountDataPrivate),
|
Private(PersistentAccountDataPrivate),
|
||||||
|
Preconfigured(InitialAccountData),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct PersistentStorage {
|
pub struct PersistentStorage {
|
||||||
pub accounts: Vec<PersistentAccountData>,
|
pub accounts: Vec<PersistentAccountData>,
|
||||||
pub password: String,
|
|
||||||
pub last_synced_block: u64,
|
pub last_synced_block: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,6 +72,7 @@ impl PersistentAccountData {
|
|||||||
match &self {
|
match &self {
|
||||||
Self::Public(acc) => acc.address,
|
Self::Public(acc) => acc.address,
|
||||||
Self::Private(acc) => acc.address,
|
Self::Private(acc) => acc.address,
|
||||||
|
Self::Preconfigured(acc) => acc.address(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,6 +101,12 @@ impl From<PersistentAccountDataPrivate> for PersistentAccountData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<InitialAccountData> for PersistentAccountData {
|
||||||
|
fn from(value: InitialAccountData) -> Self {
|
||||||
|
Self::Preconfigured(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct GasConfig {
|
pub struct GasConfig {
|
||||||
/// Gas spent per deploying one byte of data
|
/// Gas spent per deploying one byte of data
|
||||||
|
|||||||
@ -12,6 +12,7 @@ use serde::Serialize;
|
|||||||
use crate::{
|
use crate::{
|
||||||
HOME_DIR_ENV_VAR,
|
HOME_DIR_ENV_VAR,
|
||||||
config::{
|
config::{
|
||||||
|
InitialAccountData, InitialAccountDataPrivate, InitialAccountDataPublic,
|
||||||
PersistentAccountDataPrivate, PersistentAccountDataPublic, PersistentStorage, WalletConfig,
|
PersistentAccountDataPrivate, PersistentAccountDataPublic, PersistentStorage, WalletConfig,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -102,11 +103,9 @@ pub async fn fetch_persistent_storage() -> Result<PersistentStorage> {
|
|||||||
Ok(serde_json::from_slice(&storage_content)?)
|
Ok(serde_json::from_slice(&storage_content)?)
|
||||||
}
|
}
|
||||||
Err(err) => match err.kind() {
|
Err(err) => match err.kind() {
|
||||||
std::io::ErrorKind::NotFound => Ok(PersistentStorage {
|
std::io::ErrorKind::NotFound => {
|
||||||
accounts: vec![],
|
anyhow::bail!("Not found, please setup roots from config command beforehand");
|
||||||
password: "default".to_string(),
|
}
|
||||||
last_synced_block: 0,
|
|
||||||
}),
|
|
||||||
_ => {
|
_ => {
|
||||||
anyhow::bail!("IO error {err:#?}");
|
anyhow::bail!("IO error {err:#?}");
|
||||||
}
|
}
|
||||||
@ -123,27 +122,53 @@ pub fn produce_data_for_storage(
|
|||||||
|
|
||||||
for (addr, key) in &user_data.public_key_tree.addr_map {
|
for (addr, key) in &user_data.public_key_tree.addr_map {
|
||||||
if let Some(data) = user_data.public_key_tree.key_map.get(key) {
|
if let Some(data) = user_data.public_key_tree.key_map.get(key) {
|
||||||
vec_for_storage.push(PersistentAccountDataPublic {
|
vec_for_storage.push(
|
||||||
address: *addr,
|
PersistentAccountDataPublic {
|
||||||
chain_index: key.clone(),
|
address: *addr,
|
||||||
data: data.clone(),
|
chain_index: key.clone(),
|
||||||
}.into());
|
data: data.clone(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (addr, key) in &user_data.private_key_tree.addr_map {
|
for (addr, key) in &user_data.private_key_tree.addr_map {
|
||||||
if let Some(data) = user_data.private_key_tree.key_map.get(key) {
|
if let Some(data) = user_data.private_key_tree.key_map.get(key) {
|
||||||
vec_for_storage.push(PersistentAccountDataPrivate {
|
vec_for_storage.push(
|
||||||
address: *addr,
|
PersistentAccountDataPrivate {
|
||||||
chain_index: key.clone(),
|
address: *addr,
|
||||||
data: data.clone(),
|
chain_index: key.clone(),
|
||||||
}.into());
|
data: data.clone(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (addr, key) in &user_data.default_pub_account_signing_keys {
|
||||||
|
vec_for_storage.push(
|
||||||
|
InitialAccountData::Public(InitialAccountDataPublic {
|
||||||
|
address: addr.to_string(),
|
||||||
|
pub_sign_key: key.clone(),
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (addr, (key_chain, account)) in &user_data.default_user_private_accounts {
|
||||||
|
vec_for_storage.push(
|
||||||
|
InitialAccountData::Private(InitialAccountDataPrivate {
|
||||||
|
address: addr.to_string(),
|
||||||
|
account: account.clone(),
|
||||||
|
key_chain: key_chain.clone(),
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
PersistentStorage {
|
PersistentStorage {
|
||||||
accounts: vec_for_storage,
|
accounts: vec_for_storage,
|
||||||
password: user_data.password.clone(),
|
|
||||||
last_synced_block,
|
last_synced_block,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use common::{
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use chain_storage::WalletChainStore;
|
use chain_storage::WalletChainStore;
|
||||||
use config::WalletConfig;
|
use config::WalletConfig;
|
||||||
use key_protocol::key_management::key_tree::chain_index::ChainIndex;
|
use key_protocol::key_management::key_tree::{chain_index::ChainIndex, traits::KeyNode};
|
||||||
use log::info;
|
use log::info;
|
||||||
use nssa::{
|
use nssa::{
|
||||||
Account, Address, privacy_preserving_transaction::message::EncryptedAccountData,
|
Account, Address, privacy_preserving_transaction::message::EncryptedAccountData,
|
||||||
@ -62,11 +62,10 @@ impl WalletCore {
|
|||||||
|
|
||||||
let PersistentStorage {
|
let PersistentStorage {
|
||||||
accounts: persistent_accounts,
|
accounts: persistent_accounts,
|
||||||
password,
|
|
||||||
last_synced_block,
|
last_synced_block,
|
||||||
} = fetch_persistent_storage().await?;
|
} = fetch_persistent_storage().await?;
|
||||||
|
|
||||||
let storage = WalletChainStore::new(config, persistent_accounts, password)?;
|
let storage = WalletChainStore::new(config, persistent_accounts)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
storage,
|
storage,
|
||||||
@ -76,6 +75,23 @@ impl WalletCore {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn start_from_config_new_storage(
|
||||||
|
config: WalletConfig,
|
||||||
|
password: String,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?);
|
||||||
|
let tx_poller = TxPoller::new(config.clone(), client.clone());
|
||||||
|
|
||||||
|
let storage = WalletChainStore::new_storage(config, password)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
storage,
|
||||||
|
poller: tx_poller,
|
||||||
|
sequencer_client: client.clone(),
|
||||||
|
last_synced_block: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
///Store persistent data at home
|
///Store persistent data at home
|
||||||
pub async fn store_persistent_data(&self) -> Result<PathBuf> {
|
pub async fn store_persistent_data(&self) -> Result<PathBuf> {
|
||||||
let home = get_home()?;
|
let home = get_home()?;
|
||||||
@ -233,6 +249,18 @@ pub enum Command {
|
|||||||
Config(ConfigSubcommand),
|
Config(ConfigSubcommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Represents CLI command for a wallet with setup included
|
||||||
|
#[derive(Debug, Subcommand, Clone)]
|
||||||
|
#[clap(about)]
|
||||||
|
pub enum OverCommand {
|
||||||
|
#[command(subcommand)]
|
||||||
|
Command(Command),
|
||||||
|
Setup {
|
||||||
|
#[arg(short, long)]
|
||||||
|
password: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
///To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config
|
///To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config
|
||||||
///
|
///
|
||||||
/// All account adresses must be valid 32 byte base58 strings.
|
/// All account adresses must be valid 32 byte base58 strings.
|
||||||
@ -247,7 +275,7 @@ pub struct Args {
|
|||||||
pub continious_run: bool,
|
pub continious_run: bool,
|
||||||
/// Wallet command
|
/// Wallet command
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Option<Command>,
|
pub command: Option<OverCommand>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -341,8 +369,11 @@ pub async fn parse_block_range(
|
|||||||
if let NSSATransaction::PrivacyPreserving(tx) = nssa_tx {
|
if let NSSATransaction::PrivacyPreserving(tx) = nssa_tx {
|
||||||
let mut affected_accounts = vec![];
|
let mut affected_accounts = vec![];
|
||||||
|
|
||||||
for (acc_addr, (key_chain, _)) in
|
for (acc_addr, (key_chain, _)) in wallet_core
|
||||||
&wallet_core.storage.user_data.user_private_accounts
|
.storage
|
||||||
|
.user_data
|
||||||
|
.default_user_private_accounts
|
||||||
|
.iter()
|
||||||
{
|
{
|
||||||
let view_tag = EncryptedAccountData::compute_view_tag(
|
let view_tag = EncryptedAccountData::compute_view_tag(
|
||||||
key_chain.nullifer_public_key.clone(),
|
key_chain.nullifer_public_key.clone(),
|
||||||
@ -379,6 +410,51 @@ pub async fn parse_block_range(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (_, keys_node) in wallet_core
|
||||||
|
.storage
|
||||||
|
.user_data
|
||||||
|
.private_key_tree
|
||||||
|
.key_map
|
||||||
|
.iter()
|
||||||
|
{
|
||||||
|
let acc_addr = keys_node.address();
|
||||||
|
let key_chain = &keys_node.value.0;
|
||||||
|
|
||||||
|
let view_tag = EncryptedAccountData::compute_view_tag(
|
||||||
|
key_chain.nullifer_public_key.clone(),
|
||||||
|
key_chain.incoming_viewing_public_key.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (ciph_id, encrypted_data) in tx
|
||||||
|
.message()
|
||||||
|
.encrypted_private_post_states
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
if encrypted_data.view_tag == view_tag {
|
||||||
|
let ciphertext = &encrypted_data.ciphertext;
|
||||||
|
let commitment = &tx.message.new_commitments[ciph_id];
|
||||||
|
let shared_secret = key_chain
|
||||||
|
.calculate_shared_secret_receiver(encrypted_data.epk.clone());
|
||||||
|
|
||||||
|
let res_acc = nssa_core::EncryptionScheme::decrypt(
|
||||||
|
ciphertext,
|
||||||
|
&shared_secret,
|
||||||
|
commitment,
|
||||||
|
ciph_id as u32,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(res_acc) = res_acc {
|
||||||
|
println!(
|
||||||
|
"Received new account for addr {acc_addr:#?} with account object {res_acc:#?}"
|
||||||
|
);
|
||||||
|
|
||||||
|
affected_accounts.push((acc_addr, res_acc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (affected_addr, new_acc) in affected_accounts {
|
for (affected_addr, new_acc) in affected_accounts {
|
||||||
wallet_core
|
wallet_core
|
||||||
.storage
|
.storage
|
||||||
@ -426,3 +502,12 @@ pub async fn execute_continious_run() -> Result<()> {
|
|||||||
latest_block_num = seq_client.get_last_block().await?.last_block;
|
latest_block_num = seq_client.get_last_block().await?.last_block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn execute_setup(password: String) -> Result<()> {
|
||||||
|
let config = fetch_config().await?;
|
||||||
|
let wallet_core = WalletCore::start_from_config_new_storage(config.clone(), password).await?;
|
||||||
|
|
||||||
|
wallet_core.store_persistent_data().await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{CommandFactory, Parser};
|
use clap::{CommandFactory, Parser};
|
||||||
use tokio::runtime::Builder;
|
use tokio::runtime::Builder;
|
||||||
use wallet::{Args, execute_continious_run, execute_subcommand};
|
use wallet::{Args, OverCommand, execute_continious_run, execute_setup, execute_subcommand};
|
||||||
|
|
||||||
pub const NUM_THREADS: usize = 2;
|
pub const NUM_THREADS: usize = 2;
|
||||||
|
|
||||||
@ -17,8 +17,15 @@ fn main() -> Result<()> {
|
|||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
runtime.block_on(async move {
|
runtime.block_on(async move {
|
||||||
if let Some(command) = args.command {
|
if let Some(overcommand) = args.command {
|
||||||
execute_subcommand(command).await.unwrap();
|
match overcommand {
|
||||||
|
OverCommand::Command(command) => {
|
||||||
|
execute_subcommand(command).await.unwrap();
|
||||||
|
}
|
||||||
|
OverCommand::Setup { password } => {
|
||||||
|
execute_setup(password).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if args.continious_run {
|
} else if args.continious_run {
|
||||||
execute_continious_run().await.unwrap();
|
execute_continious_run().await.unwrap();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user