Refactoring

This commit is contained in:
Sosthene00 2024-04-05 19:12:44 +02:00
parent 741b303348
commit 750bf69aa5
7 changed files with 439 additions and 110 deletions

View File

@ -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
}
}

View File

@ -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)))
}
}

View 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"))
}
}
}

View 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())
}

View File

@ -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
)
}

View File

@ -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();

View File

@ -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;