Refactoring
This commit is contained in:
parent
741b303348
commit
750bf69aa5
@ -1,12 +1,17 @@
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Mutex, OnceLock, PoisonError};
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
use anyhow::Error as AnyhowError;
|
||||
use serde_json::Error as SerdeJsonError;
|
||||
use shamir::SecretData;
|
||||
use sp_backend::bitcoin::secp256k1::SecretKey;
|
||||
use sp_backend::bitcoin::consensus::deserialize;
|
||||
use sp_backend::bitcoin::hex::{FromHex, HexToBytesError};
|
||||
use sp_backend::bitcoin::secp256k1::{PublicKey, SecretKey};
|
||||
use sp_backend::bitcoin::{Transaction, Txid};
|
||||
use sp_backend::silentpayments::Error as SpError;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -15,10 +20,12 @@ use tsify::Tsify;
|
||||
use wasm_bindgen::convert::FromWasmAbi;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use sp_backend::spclient::SpendKey;
|
||||
use sp_backend::spclient::{derive_keys_from_seed, OutputList, SpClient};
|
||||
use sp_backend::spclient::{SpWallet, SpendKey};
|
||||
|
||||
use crate::user::{User, UserKeys, CONNECTED_USERS};
|
||||
use crate::network::{BitcoinNetworkMsg, BitcoinTopic};
|
||||
use crate::silentpayments::check_transaction;
|
||||
use crate::user::{lock_connected_users, User, UserWallets, CONNECTED_USERS};
|
||||
|
||||
use crate::process::Process;
|
||||
|
||||
@ -55,6 +62,30 @@ impl From<SerdeJsonError> for ApiError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HexToBytesError> for ApiError {
|
||||
fn from(value: HexToBytesError) -> Self {
|
||||
ApiError {
|
||||
message: value.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sp_backend::bitcoin::secp256k1::Error> for ApiError {
|
||||
fn from(value: sp_backend::bitcoin::secp256k1::Error) -> Self {
|
||||
ApiError {
|
||||
message: value.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sp_backend::bitcoin::consensus::encode::Error> for ApiError {
|
||||
fn from(value: sp_backend::bitcoin::consensus::encode::Error) -> Self {
|
||||
ApiError {
|
||||
message: value.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<JsValue> for ApiError {
|
||||
fn into(self) -> JsValue {
|
||||
JsValue::from_str(&self.message)
|
||||
@ -62,7 +93,7 @@ impl Into<JsValue> for ApiError {
|
||||
}
|
||||
|
||||
#[derive(Tsify, Serialize, Deserialize)]
|
||||
#[tsify(into_wasm_abi)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct createUserReturn {
|
||||
pub user: User,
|
||||
@ -75,16 +106,11 @@ pub fn setup() {
|
||||
}
|
||||
|
||||
// Should be transfered to annother module
|
||||
pub struct GenerateSPWallet {
|
||||
pub sp_client: SpClient,
|
||||
pub sp_outputs: OutputList,
|
||||
}
|
||||
|
||||
pub fn generate_sp_wallet(
|
||||
label: Option<String>,
|
||||
birthday: u32,
|
||||
is_testnet: bool,
|
||||
) -> ApiResult<GenerateSPWallet> {
|
||||
) -> ApiResult<SpWallet> {
|
||||
let mut seed = [0u8; 64];
|
||||
rand::thread_rng().fill(&mut seed);
|
||||
let (scan_sk, spend_sk) = derive_keys_from_seed(&seed, is_testnet)?;
|
||||
@ -101,20 +127,7 @@ pub fn generate_sp_wallet(
|
||||
our_address.to_string()
|
||||
);
|
||||
|
||||
//let sp_client_json = serde_json::to_string(&sp_client)?;
|
||||
|
||||
// Generate an empty outputs
|
||||
let sp_outputs = OutputList::new(
|
||||
our_address.get_scan_key(),
|
||||
our_address.get_spend_key(),
|
||||
birthday,
|
||||
);
|
||||
//let sp_outputs_json = serde_json::to_string(&sp_outputs)?;
|
||||
|
||||
let res = GenerateSPWallet {
|
||||
sp_client,
|
||||
sp_outputs,
|
||||
};
|
||||
let res = SpWallet::new(sp_client, None)?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
@ -133,28 +146,28 @@ pub fn create_user(
|
||||
birthday_signet: u32,
|
||||
process: String,
|
||||
) -> ApiResult<createUserReturn> {
|
||||
let mut output_list: Vec<OutputList> = Vec::new();
|
||||
//recover
|
||||
let sp_wallet_recover = generate_sp_wallet(label.clone(), birthday_signet, true)?;
|
||||
output_list.push(sp_wallet_recover.sp_outputs);
|
||||
//revoke
|
||||
let sp_wallet_revoke = generate_sp_wallet(label.clone(), birthday_signet, true)?;
|
||||
output_list.push(sp_wallet_revoke.sp_outputs);
|
||||
//mainet
|
||||
let sp_wallet_main = generate_sp_wallet(label, birthday_main, false)?;
|
||||
output_list.push(sp_wallet_main.sp_outputs);
|
||||
|
||||
let user_keys = UserKeys::new(
|
||||
Some(sp_wallet_main.sp_client),
|
||||
sp_wallet_recover.sp_client,
|
||||
Some(sp_wallet_revoke.sp_client),
|
||||
let user_wallets = UserWallets::new(
|
||||
Some(sp_wallet_main),
|
||||
sp_wallet_recover,
|
||||
Some(sp_wallet_revoke),
|
||||
);
|
||||
|
||||
let user = User::new(user_keys, password, process)?;
|
||||
let user = User::new(user_wallets.clone(), password, process)?;
|
||||
|
||||
let outputs = user_wallets.get_all_outputs();
|
||||
|
||||
lock_connected_users()?.insert(user.pre_id.clone(), user_wallets);
|
||||
|
||||
let generate_user = createUserReturn {
|
||||
user,
|
||||
output_list_vec: output_list,
|
||||
output_list_vec: outputs,
|
||||
};
|
||||
|
||||
Ok(generate_user)
|
||||
@ -175,7 +188,7 @@ pub fn get_processes() -> ApiResult<get_process_return> {
|
||||
for _ in 0..number_managers {
|
||||
//add sp_client
|
||||
let sp_wallet = generate_sp_wallet(None, birthday_signet, true)?;
|
||||
let sp_address = sp_wallet.sp_client.get_receiving_address();
|
||||
let sp_address = sp_wallet.get_client().get_receiving_address();
|
||||
members.push(sp_address);
|
||||
}
|
||||
//instances of process
|
||||
@ -235,34 +248,6 @@ pub fn get_processes() -> ApiResult<get_process_return> {
|
||||
Ok(get_process_return(data_process))
|
||||
}
|
||||
|
||||
//for testing
|
||||
/*#[wasm_bindgen]
|
||||
pub fn get_process() -> ApiResult<get_process_return>{
|
||||
|
||||
let number_managers: u8 = 5;
|
||||
let number_users:u8 = 10;
|
||||
let birthday_signet = 50000;
|
||||
let mut members: Vec<ItemMember> = Vec::with_capacity((number_managers + number_users) as usize);
|
||||
|
||||
for _ in 0..number_managers{
|
||||
//add sp_client
|
||||
let sp_wallet = generate_sp_wallet(None, birthday_signet, true)?;
|
||||
let sp_address: SilentPaymentAddress = sp_wallet.sp_client.get_receiving_address().try_into()?;
|
||||
members.push(ItemMember::new(Role::Manager,sp_address, String::from("")));
|
||||
}
|
||||
for _ in 0..number_users{
|
||||
let sp_wallet = generate_sp_wallet(None, birthday_signet, true)?;
|
||||
let sp_address: SilentPaymentAddress = sp_wallet.sp_client.get_receiving_address().try_into()?;
|
||||
members.push(ItemMember::new(Role::User, sp_address, String::from("")));
|
||||
}
|
||||
|
||||
let process = Process::new(members);
|
||||
let mut data_process: Vec<Process> = Vec::new();
|
||||
data_process.push(process);
|
||||
Ok(get_process_return(data_process))
|
||||
|
||||
}*/
|
||||
|
||||
#[derive(Debug, Tsify, Serialize, Deserialize)]
|
||||
#[tsify(from_wasm_abi)]
|
||||
#[allow(non_camel_case_types)]
|
||||
@ -285,14 +270,84 @@ impl shamir_shares {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Tsify, Serialize, Deserialize)]
|
||||
#[tsify(from_wasm_abi)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct outputs_list(Vec<OutputList>);
|
||||
|
||||
impl outputs_list {
|
||||
fn as_inner(&self) -> &[OutputList] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn login_user(
|
||||
user_password: String,
|
||||
pre_id: String,
|
||||
recover: recover_data,
|
||||
shares: shamir_shares,
|
||||
outputs: outputs_list,
|
||||
) -> ApiResult<()> {
|
||||
let res = User::login(pre_id, user_password, recover.as_inner(), shares.as_inner())?;
|
||||
let res = User::login(
|
||||
pre_id,
|
||||
user_password,
|
||||
recover.as_inner(),
|
||||
shares.as_inner(),
|
||||
outputs.as_inner(),
|
||||
)?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn check_transaction_for_silent_payments(
|
||||
tx_hex: String,
|
||||
tweak_data_hex: String,
|
||||
) -> ApiResult<()> {
|
||||
let tx = deserialize::<Transaction>(&Vec::from_hex(&tx_hex)?)?;
|
||||
let tweak_data = PublicKey::from_str(&tweak_data_hex)?;
|
||||
|
||||
check_transaction(tx, tweak_data);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_bitcoin_network_msg(msg: Vec<u8>) -> ApiResult<()> {
|
||||
let parsed_msg = BitcoinNetworkMsg::new(&msg)?;
|
||||
|
||||
match parsed_msg.topic {
|
||||
BitcoinTopic::RawTx => {
|
||||
let tx = deserialize::<Transaction>(parsed_msg.data)?;
|
||||
let tweak_data = PublicKey::from_slice(parsed_msg.addon)?;
|
||||
check_transaction(tx, tweak_data);
|
||||
}
|
||||
BitcoinTopic::RawBlock => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_4nk_msg(raw: String) -> Option<String>{
|
||||
if let Ok(msg) = AnkNetworkMsg::new(&raw) {
|
||||
match msg.topic {
|
||||
AnkTopic::Faucet => {
|
||||
match Txid::from_str(msg.content) {
|
||||
Ok(txid) => {
|
||||
// return the txid for verification
|
||||
Some(txid.to_string())
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Invalid txid with a \"faucet\" message: {}", e.to_string());
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::debug!("Can't parse message as a valid 4nk message: {}", raw);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,24 @@
|
||||
#![allow(warnings)]
|
||||
use anyhow::Error;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
mod Prd_list;
|
||||
pub mod api;
|
||||
mod crypto;
|
||||
mod network;
|
||||
mod peers;
|
||||
mod process;
|
||||
mod silentpayments;
|
||||
mod user;
|
||||
|
||||
pub(crate) trait MutexExt<T> {
|
||||
fn lock_anyhow(&self) -> Result<MutexGuard<T>, Error>;
|
||||
}
|
||||
|
||||
impl<T: Debug> MutexExt<T> for Mutex<T> {
|
||||
fn lock_anyhow(&self) -> Result<MutexGuard<T>, Error> {
|
||||
self.lock()
|
||||
.map_err(|e| Error::msg(format!("Failed to lock: {}", e)))
|
||||
}
|
||||
}
|
||||
|
94
crates/sp_client/src/network.rs
Normal file
94
crates/sp_client/src/network.rs
Normal file
@ -0,0 +1,94 @@
|
||||
use anyhow::{Error, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tsify::Tsify;
|
||||
|
||||
const RAWTXTOPIC: &'static str = "rawtx";
|
||||
const RAWBLOCKTOPIC: &'static str = "rawblock";
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum BitcoinTopic {
|
||||
RawTx,
|
||||
RawBlock,
|
||||
}
|
||||
|
||||
impl BitcoinTopic {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::RawTx => RAWTXTOPIC,
|
||||
Self::RawBlock => RAWBLOCKTOPIC,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Tsify)]
|
||||
#[tsify(from_wasm_abi, into_wasm_abi)]
|
||||
pub struct BitcoinNetworkMsg<'a> {
|
||||
pub topic: BitcoinTopic,
|
||||
pub data: &'a [u8],
|
||||
pub sequence: &'a [u8],
|
||||
pub addon: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> BitcoinNetworkMsg<'a> {
|
||||
pub fn new(raw_msg: &'a [u8]) -> Result<Self> {
|
||||
let topic: BitcoinTopic;
|
||||
let data: &[u8];
|
||||
let sequence: &[u8];
|
||||
let addon: &[u8];
|
||||
let addon_len: usize;
|
||||
let raw_msg_len = raw_msg.len();
|
||||
|
||||
if raw_msg.starts_with(RAWTXTOPIC.as_bytes()) {
|
||||
topic = BitcoinTopic::RawTx;
|
||||
addon_len = 33;
|
||||
} else if raw_msg.starts_with(RAWBLOCKTOPIC.as_bytes()) {
|
||||
topic = BitcoinTopic::RawBlock;
|
||||
addon_len = 0;
|
||||
} else {
|
||||
return Err(Error::msg("Unknown prefix"));
|
||||
}
|
||||
|
||||
data = &raw_msg[topic.as_str().as_bytes().len()..raw_msg_len - 4 - addon_len];
|
||||
sequence = &raw_msg[raw_msg_len - 4 - addon_len..];
|
||||
addon = &raw_msg[raw_msg_len - addon_len..];
|
||||
|
||||
Ok(Self {
|
||||
topic,
|
||||
data,
|
||||
sequence,
|
||||
addon,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AnkTopic {
|
||||
Faucet,
|
||||
}
|
||||
|
||||
impl AnkTopic {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::Faucet => "faucet",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AnkNetworkMsg<'a> {
|
||||
pub topic: AnkTopic,
|
||||
pub content: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> AnkNetworkMsg<'a> {
|
||||
pub fn new(raw: &'a str) -> Result<Self> {
|
||||
if raw.starts_with(AnkTopic::Faucet.as_str()) {
|
||||
Ok(Self {
|
||||
topic: AnkTopic::Faucet,
|
||||
content: &raw[AnkTopic::Faucet.as_str().len()..],
|
||||
})
|
||||
} else {
|
||||
Err(Error::msg("Unknown 4nk message"))
|
||||
}
|
||||
}
|
||||
}
|
76
crates/sp_client/src/silentpayments.rs
Normal file
76
crates/sp_client/src/silentpayments.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use sp_backend::silentpayments::utils::receiving::calculate_shared_secret;
|
||||
use sp_backend::{
|
||||
bitcoin::{
|
||||
secp256k1::{PublicKey, Scalar, XOnlyPublicKey},
|
||||
Transaction,
|
||||
},
|
||||
silentpayments::receiving::Label,
|
||||
};
|
||||
|
||||
use crate::user::{lock_connected_users, CONNECTED_USERS};
|
||||
|
||||
type FoundOutputs = HashMap<Option<Label>, HashMap<XOnlyPublicKey, Scalar>>;
|
||||
|
||||
pub fn check_transaction(tx: Transaction, tweak_data: PublicKey) -> Result<FoundOutputs> {
|
||||
let connected_users = lock_connected_users()?;
|
||||
|
||||
let pubkeys_to_check: HashMap<XOnlyPublicKey, u32> = (0u32..)
|
||||
.zip(tx.output)
|
||||
.filter_map(|(i, o)| {
|
||||
if o.script_pubkey.is_p2tr() {
|
||||
let xonly = XOnlyPublicKey::from_slice(&o.script_pubkey.as_bytes()[2..])
|
||||
.expect("Transaction is invalid");
|
||||
Some((xonly, i))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Check the transaction for all connected users
|
||||
for (pre_id, keys) in connected_users.clone() {
|
||||
let recover = keys.recover;
|
||||
let shared_secret =
|
||||
calculate_shared_secret(tweak_data, recover.get_client().get_scan_key())?;
|
||||
let res = recover
|
||||
.get_client()
|
||||
.sp_receiver
|
||||
.scan_transaction(&shared_secret, pubkeys_to_check.keys().cloned().collect())?;
|
||||
|
||||
if res.len() > 0 {
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
if let Some(main) = keys.main {
|
||||
let shared_secret =
|
||||
calculate_shared_secret(tweak_data, main.get_client().get_scan_key())?;
|
||||
let res = main
|
||||
.get_client()
|
||||
.sp_receiver
|
||||
.scan_transaction(&shared_secret, pubkeys_to_check.keys().cloned().collect())?;
|
||||
|
||||
if res.len() > 0 {
|
||||
return Ok(res);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(revoke) = keys.revoke {
|
||||
let shared_secret =
|
||||
calculate_shared_secret(tweak_data, revoke.get_client().get_scan_key())?;
|
||||
let res = revoke
|
||||
.get_client()
|
||||
.sp_receiver
|
||||
.scan_transaction(&shared_secret, pubkeys_to_check.keys().cloned().collect())?;
|
||||
|
||||
if res.len() > 0 {
|
||||
return Ok(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(HashMap::new())
|
||||
}
|
@ -12,6 +12,7 @@ use sp_backend::bitcoin::hashes::HashEngine;
|
||||
use sp_backend::bitcoin::hex::{DisplayHex, FromHex};
|
||||
use sp_backend::bitcoin::secp256k1::SecretKey;
|
||||
use sp_backend::bitcoin::secp256k1::ThirtyTwoByteHash;
|
||||
use sp_backend::spclient::SpClient;
|
||||
use tsify::Tsify;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
@ -20,34 +21,42 @@ use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{Cursor, Read, Write};
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
use std::sync::{Mutex, MutexGuard, OnceLock};
|
||||
|
||||
use sp_backend::bitcoin::secp256k1::constants::SECRET_KEY_SIZE;
|
||||
use sp_backend::silentpayments::bitcoin_hashes::sha256;
|
||||
use sp_backend::silentpayments::sending::SilentPaymentAddress;
|
||||
use sp_backend::spclient::SpendKey;
|
||||
use sp_backend::spclient::{OutputList, SpClient};
|
||||
use sp_backend::spclient::{OutputList, SpWallet};
|
||||
|
||||
use crate::crypto::{Aes256Decryption, Aes256Encryption, HalfKey, Purpose};
|
||||
use crate::peers::Peer;
|
||||
use crate::user;
|
||||
use crate::MutexExt;
|
||||
|
||||
type PreId = String;
|
||||
|
||||
const MANAGERS_NUMBER: u8 = 10;
|
||||
const QUORUM_SHARD: f32 = 0.8;
|
||||
|
||||
pub static CONNECTED_USERS: OnceLock<Mutex<HashMap<PreId, UserKeys>>> = OnceLock::new();
|
||||
type UsersMap = HashMap<PreId, UserWallets>;
|
||||
pub static CONNECTED_USERS: OnceLock<Mutex<UsersMap>> = OnceLock::new();
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UserKeys {
|
||||
pub main: Option<SpClient>,
|
||||
pub recover: SpClient,
|
||||
pub revoke: Option<SpClient>,
|
||||
pub fn lock_connected_users() -> Result<MutexGuard<'static, UsersMap>> {
|
||||
CONNECTED_USERS
|
||||
.get_or_init(|| Mutex::new(HashMap::new()))
|
||||
.lock_anyhow()
|
||||
}
|
||||
|
||||
impl UserKeys {
|
||||
pub fn new(main: Option<SpClient>, recover: SpClient, revoke: Option<SpClient>) -> Self {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UserWallets {
|
||||
pub main: Option<SpWallet>,
|
||||
pub recover: SpWallet,
|
||||
pub revoke: Option<SpWallet>,
|
||||
}
|
||||
|
||||
impl UserWallets {
|
||||
pub fn new(main: Option<SpWallet>, recover: SpWallet, revoke: Option<SpWallet>) -> Self {
|
||||
Self {
|
||||
main,
|
||||
recover,
|
||||
@ -55,9 +64,22 @@ impl UserKeys {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_get_revoke(&self) -> Option<&SpClient> {
|
||||
pub fn try_get_revoke(&self) -> Option<&SpWallet> {
|
||||
self.revoke.as_ref()
|
||||
}
|
||||
|
||||
pub(crate) fn get_all_outputs(&self) -> Vec<OutputList> {
|
||||
let mut res = Vec::<OutputList>::new();
|
||||
if let Some(main) = &self.main {
|
||||
res.push(main.get_outputs().clone());
|
||||
}
|
||||
if let Some(revoke) = &self.revoke {
|
||||
res.push(revoke.get_outputs().clone());
|
||||
}
|
||||
res.push(self.recover.get_outputs().clone());
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Tsify)]
|
||||
@ -69,25 +91,30 @@ pub struct User {
|
||||
recover_data: Vec<u8>,
|
||||
revoke_data: Option<Vec<u8>>,
|
||||
shares: Vec<Vec<u8>>,
|
||||
outputs: Vec<OutputList>,
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn new(user_keys: UserKeys, user_password: String, process: String) -> Result<Self> {
|
||||
pub fn new(user_wallets: UserWallets, user_password: String, process: String) -> Result<Self> {
|
||||
let mut rng = thread_rng();
|
||||
|
||||
// image revoke
|
||||
// We just take the 2 revoke keys
|
||||
let mut revoke_data = Vec::with_capacity(64);
|
||||
if let Some(revoke) = user_keys.try_get_revoke() {
|
||||
revoke_data.extend_from_slice(revoke.get_scan_key().as_ref());
|
||||
revoke_data.extend_from_slice(revoke.try_get_secret_spend_key()?.as_ref());
|
||||
if let Some(revoke) = user_wallets.try_get_revoke() {
|
||||
revoke_data.extend_from_slice(revoke.get_client().get_scan_key().as_ref());
|
||||
revoke_data.extend_from_slice(revoke.get_client().try_get_secret_spend_key()?.as_ref());
|
||||
} else {
|
||||
return Err(Error::msg("No revoke wallet available"));
|
||||
}
|
||||
|
||||
// Take the 2 recover keys
|
||||
// split recover spend key
|
||||
let recover_spend_key = user_keys.recover.try_get_secret_spend_key()?.clone();
|
||||
let recover_spend_key = user_wallets
|
||||
.recover
|
||||
.get_client()
|
||||
.try_get_secret_spend_key()?
|
||||
.clone();
|
||||
let (part1_key, part2_key) = recover_spend_key.as_ref().split_at(SECRET_KEY_SIZE / 2);
|
||||
let mut recover_data = Vec::<u8>::with_capacity(180); // 32 * 3 + (12+16)*3
|
||||
|
||||
@ -161,7 +188,12 @@ impl User {
|
||||
|
||||
let scan_key_encryption = Aes256Encryption::import_key(
|
||||
Purpose::ThirtyTwoBytes,
|
||||
user_keys.recover.get_scan_key().secret_bytes().to_vec(),
|
||||
user_wallets
|
||||
.recover
|
||||
.get_client()
|
||||
.get_scan_key()
|
||||
.secret_bytes()
|
||||
.to_vec(),
|
||||
hash3.to_byte_array(),
|
||||
Aes256Gcm::generate_nonce(&mut rng).into(),
|
||||
)?;
|
||||
@ -171,12 +203,6 @@ impl User {
|
||||
|
||||
recover_data.extend_from_slice(&cipher_scan_key);
|
||||
|
||||
//Create PRDList
|
||||
//@todo
|
||||
//Send messages PRDList
|
||||
//@todo
|
||||
//Receive List Items (PCD)
|
||||
|
||||
Ok(User {
|
||||
pre_id: pre_id.to_string(),
|
||||
processes: vec![process],
|
||||
@ -184,6 +210,7 @@ impl User {
|
||||
recover_data,
|
||||
revoke_data: Some(revoke_data),
|
||||
shares,
|
||||
outputs: user_wallets.get_all_outputs(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -192,6 +219,7 @@ impl User {
|
||||
user_password: String,
|
||||
recover_data: &[u8],
|
||||
shares: &[Vec<u8>],
|
||||
outputs: &[OutputList],
|
||||
) -> Result<()> {
|
||||
let mut retrieved_spend_key = [0u8; 32];
|
||||
let mut retrieved_scan_key = [0u8; 32];
|
||||
@ -258,6 +286,13 @@ impl User {
|
||||
true,
|
||||
)?;
|
||||
|
||||
let recover_outputs = outputs
|
||||
.iter()
|
||||
.find(|o| o.check_fingerprint(&recover_client))
|
||||
.cloned();
|
||||
|
||||
let recover_wallet = SpWallet::new(recover_client, recover_outputs)?;
|
||||
|
||||
// Adding user to CONNECTED_USERS
|
||||
if let Some(current_users) = CONNECTED_USERS.get() {
|
||||
let mut lock = current_users.to_owned().lock().unwrap();
|
||||
@ -267,11 +302,11 @@ impl User {
|
||||
pre_id
|
||||
)));
|
||||
} else {
|
||||
lock.insert(pre_id.clone(), UserKeys::new(None, recover_client, None));
|
||||
lock.insert(pre_id.clone(), UserWallets::new(None, recover_wallet, None));
|
||||
}
|
||||
} else {
|
||||
let mut user_map = HashMap::new();
|
||||
user_map.insert(pre_id, UserKeys::new(None, recover_client, None));
|
||||
user_map.insert(pre_id, UserWallets::new(None, recover_wallet, None));
|
||||
let new_value = Mutex::new(user_map);
|
||||
if let Err(error) = CONNECTED_USERS.set(new_value) {
|
||||
return Err(Error::msg(
|
||||
@ -396,7 +431,7 @@ mod tests {
|
||||
const USER_PASSWORD: &str = "correct horse battery staple";
|
||||
const PROCESS: &str = "example";
|
||||
|
||||
fn helper_create_user_keys() -> UserKeys {
|
||||
fn helper_create_user_wallets() -> UserWallets {
|
||||
let label = "default".to_owned();
|
||||
let sp_main = SpClient::new(
|
||||
label.clone(),
|
||||
@ -422,16 +457,20 @@ mod tests {
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
let user_keys = UserKeys::new(Some(sp_main), sp_recover, Some(sp_revoke));
|
||||
let user_wallets = UserWallets::new(
|
||||
Some(SpWallet::new(sp_main, None).unwrap()),
|
||||
SpWallet::new(sp_recover, None).unwrap(),
|
||||
Some(SpWallet::new(sp_revoke, None).unwrap()),
|
||||
);
|
||||
|
||||
user_keys
|
||||
user_wallets
|
||||
}
|
||||
|
||||
// Test 1: Create User
|
||||
#[test]
|
||||
fn test_successful_creation() {
|
||||
let user_keys = helper_create_user_keys();
|
||||
let result = User::new(user_keys, USER_PASSWORD.to_owned(), PROCESS.to_owned());
|
||||
let user_wallets = helper_create_user_wallets();
|
||||
let result = User::new(user_wallets, USER_PASSWORD.to_owned(), PROCESS.to_owned());
|
||||
|
||||
assert!(result.is_ok());
|
||||
let user = result.unwrap();
|
||||
@ -439,14 +478,20 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_login() {
|
||||
let user_keys = helper_create_user_keys();
|
||||
let user = User::new(user_keys, USER_PASSWORD.to_owned(), PROCESS.to_owned()).unwrap();
|
||||
let user_wallets = helper_create_user_wallets();
|
||||
let user = User::new(
|
||||
user_wallets.clone(),
|
||||
USER_PASSWORD.to_owned(),
|
||||
PROCESS.to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let res = User::login(
|
||||
user.pre_id.clone(),
|
||||
USER_PASSWORD.to_owned(),
|
||||
&user.recover_data,
|
||||
&user.shares,
|
||||
&user_wallets.get_all_outputs(),
|
||||
);
|
||||
|
||||
assert!(res.is_ok());
|
||||
@ -458,7 +503,11 @@ mod tests {
|
||||
assert!(
|
||||
format!(
|
||||
"{}",
|
||||
recover.try_get_secret_spend_key().unwrap().display_secret()
|
||||
recover
|
||||
.get_client()
|
||||
.try_get_secret_spend_key()
|
||||
.unwrap()
|
||||
.display_secret()
|
||||
) == RECOVER_SPEND
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import Services from './services';
|
||||
import { WebSocketClient } from './websockets';
|
||||
|
||||
const wsurl = "ws://127.0.0.1:8090";
|
||||
const wsurl = "ws://192.168.1.44:8090";
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
try {
|
||||
const services = await Services.getInstance();
|
||||
|
@ -93,21 +93,42 @@ class Services {
|
||||
// To comment if test
|
||||
// if (!Services.instance.isPasswordValid(password)) return;
|
||||
|
||||
let label = null;
|
||||
let birthday_signet = 50000;
|
||||
let birthday_main = 500000;
|
||||
const user: createUserReturn = this.sdkClient.create_user(password, label, birthday_main, birthday_signet, this.current_process);
|
||||
const label = null;
|
||||
const birthday_signet = 50000;
|
||||
const birthday_main = 500000;
|
||||
|
||||
const services = await Services.getInstance();
|
||||
let createUserReturn: createUserReturn = services.sdkClient.create_user(password, label, birthday_main, birthday_signet, this.current_process);
|
||||
|
||||
let user = createUserReturn.user;
|
||||
const outputs_list = createUserReturn.output_list_vec;
|
||||
|
||||
const shares = user.shares;
|
||||
const revokeData = user.revoke_data;
|
||||
|
||||
user.shares = [];
|
||||
user.revoke_data = null;
|
||||
|
||||
try {
|
||||
const indexedDb = await IndexedDB.getInstance();
|
||||
const db = indexedDb.getDb();
|
||||
await indexedDb.writeObject(db, indexedDb.getStoreList().AnkUser, user.user, null);
|
||||
await indexedDb.writeObject(db, indexedDb.getStoreList().SpOutputs, user.output_list_vec, null);
|
||||
await indexedDb.writeObject(db, indexedDb.getStoreList().AnkUser, user, null);
|
||||
await indexedDb.writeObject(db, indexedDb.getStoreList().SpOutputs, outputs_list, null);
|
||||
} catch (error) {
|
||||
console.error("Failed to write user object :", error);
|
||||
}
|
||||
|
||||
await Services.instance.displayRevokeImage();
|
||||
let sp_address = "";
|
||||
try {
|
||||
sp_address = services.sdkClient.get_receiving_address(user.pre_id);
|
||||
console.info('Using sp_address:', sp_address);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
await services.obtainTokenWithFaucet(sp_address);
|
||||
|
||||
await services.displayRevokeImage(new Uint8Array(revokeData));
|
||||
}
|
||||
|
||||
public async displayRecover(): Promise<void> {
|
||||
@ -136,7 +157,7 @@ class Services {
|
||||
const process = processElement.value;
|
||||
console.log("JS password: " + password + " process: " + process);
|
||||
// To comment if test
|
||||
if (!Services.instance.isPasswordValid(password)) return;
|
||||
// if (!Services.instance.isPasswordValid(password)) return;
|
||||
|
||||
// TODO
|
||||
alert("Recover submit to do ...");
|
||||
@ -439,11 +460,28 @@ class Services {
|
||||
return success;
|
||||
}
|
||||
|
||||
public obtainTokenWithFaucet(wsclient: WebSocketClient, spaddress: string): string | null {
|
||||
private async pickWebsocketConnectionRandom(): Promise<WebSocketClient | null> {
|
||||
const services = await Services.getInstance();
|
||||
const websockets = services.websocketConnection;
|
||||
if (websockets.length === 0) {
|
||||
console.error("No websocket connection available at the moment");
|
||||
return null;
|
||||
} else {
|
||||
const random = Math.floor(Math.random() * websockets.length);
|
||||
return websockets[random];
|
||||
}
|
||||
}
|
||||
|
||||
public async obtainTokenWithFaucet(spaddress: string): Promise<string | null> {
|
||||
const services = await Services.getInstance();
|
||||
const connection = await services.pickWebsocketConnectionRandom();
|
||||
if (!connection) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
wsclient.sendMessage(spaddress);
|
||||
connection.sendMessage('faucet'+spaddress);
|
||||
} catch (error) {
|
||||
console.error("Failed to obtain tokens with relay ", wsclient.getUrl());
|
||||
console.error("Failed to obtain tokens with relay ", connection.getUrl());
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
|
Loading…
x
Reference in New Issue
Block a user