From df2cb312580f3573c23c4c8eb8f54fbaabc79005 Mon Sep 17 00:00:00 2001 From: scilio Date: Mon, 8 Apr 2024 16:50:46 -0400 Subject: [PATCH] randomized reorg check timer & swap_tx_not_found test --- doc/swap_api.md | 1 + src/bin/mwixnet.rs | 13 +- src/servers/swap.rs | 30 +- src/store.rs | 3 +- tests/common/wallet.rs | 634 ++++++++++++++++++++--------------------- 5 files changed, 355 insertions(+), 326 deletions(-) diff --git a/doc/swap_api.md b/doc/swap_api.md index a779d63..fa84f86 100644 --- a/doc/swap_api.md +++ b/doc/swap_api.md @@ -47,6 +47,7 @@ In case of errors, the API will return a `SwapError` type with one of the follow - `FeeTooLow`: The provided fee is too low. - `StoreError`: An error occurred when saving swap to the data store. - `ClientError`: An error occurred during client communication. +- `SwapTxNotFound`: The previous swap transaction was not found in data store. - `UnknownError`: An unknown error occurred. ### Example diff --git a/src/bin/mwixnet.rs b/src/bin/mwixnet.rs index 101af2f..24ab6c4 100644 --- a/src/bin/mwixnet.rs +++ b/src/bin/mwixnet.rs @@ -14,12 +14,12 @@ use grin_util::{StopState, ZeroingString}; use mwixnet::client::{MixClient, MixClientImpl}; use mwixnet::node::GrinNode; use mwixnet::store::StoreError; +use rand::{thread_rng, Rng}; use rpassword; use std::path::PathBuf; use std::sync::{Arc, Mutex}; use std::thread::{sleep, spawn}; use std::time::Duration; -use grin_core::core::Transaction; #[macro_use] extern crate clap; @@ -262,7 +262,10 @@ fn real_main() -> Result<(), Box> { let close_handle = http_server.close_handle(); let round_handle = spawn(move || { - let mut secs = 0; + let mut rng = thread_rng(); + let mut secs = 0u32; + let mut reorg_secs = 0u32; + let mut reorg_window = rng.gen_range(900u32, 3600u32); let prev_tx = Arc::new(Mutex::new(None)); let server = swap_server.clone(); @@ -274,6 +277,7 @@ fn real_main() -> Result<(), Box> { sleep(Duration::from_secs(1)); secs = (secs + 1) % server_config.interval_s; + reorg_secs = (reorg_secs + 1) % reorg_window; if secs == 0 { let prev_tx_clone = prev_tx.clone(); @@ -286,7 +290,9 @@ fn real_main() -> Result<(), Box> { _ => None, }; }); - } else if secs % 30 == 0 { + reorg_secs = 0; + reorg_window = rng.gen_range(900u32, 3600u32); + } else if reorg_secs == 0 { let prev_tx_clone = prev_tx.clone(); let server_clone = server.clone(); rt.spawn(async move { @@ -304,6 +310,7 @@ fn real_main() -> Result<(), Box> { }; } }); + reorg_window = rng.gen_range(900u32, 3600u32); } } }); diff --git a/src/servers/swap.rs b/src/servers/swap.rs index ce0602b..916ba3e 100644 --- a/src/servers/swap.rs +++ b/src/servers/swap.rs @@ -45,6 +45,8 @@ pub enum SwapError { NodeError(String), #[error("Client communication error: {0:?}")] ClientError(String), + #[error("Swap transaction not found: {0:?}")] + SwapTxNotFound(Commitment), #[error("{0}")] UnknownError(String), } @@ -327,7 +329,8 @@ impl SwapServer for SwapServerImpl { async fn check_reorg(&self, tx: Arc) -> Result>, SwapError> { let excess = tx.kernels().first().unwrap().excess; - if let Ok(swap_tx) = self.store.lock().await.get_swap_tx(&excess) { + let locked_store = self.store.lock().await; + if let Ok(swap_tx) = locked_store.get_swap_tx(&excess) { // If kernel is in active chain, return tx if self.node.async_get_kernel(&excess, Some(swap_tx.chain_tip.0), None).await?.is_some() { return Ok(Some(tx)); @@ -341,7 +344,6 @@ impl SwapServer for SwapServerImpl { // Collect all swaps based on tx's inputs, and execute_round with those swaps let next_block_height = self.node.async_get_chain_tip().await?.0 + 1; - let locked_store = self.store.lock().await; let mut swaps = Vec::new(); for input_commit in &tx.inputs_committed() { if let Ok(swap) = locked_store.get_swap(&input_commit) { @@ -353,7 +355,7 @@ impl SwapServer for SwapServerImpl { self.async_execute_round(&locked_store, swaps).await } else { - Err(SwapError::UnknownError("Swap transaction not found".to_string())) // TODO: Create SwapError enum value + Err(SwapError::SwapTxNotFound(excess)) } } } @@ -453,7 +455,7 @@ mod tests { use crate::tx::TxComponents; use ::function_name::named; - use grin_core::core::{Committed, Input, Output, OutputFeatures, Transaction, Weighting}; + use grin_core::core::{Committed, Input, Inputs, Output, OutputFeatures, Transaction, Weighting}; use grin_onion::crypto::comsig::ComSignature; use grin_onion::crypto::secp; use grin_onion::onion::Onion; @@ -833,6 +835,26 @@ mod tests { Ok(()) } + /// Returns SwapTxNotFound when trying to check_reorg with a transaction not found in the store. + #[tokio::test] + #[named] + async fn swap_tx_not_found() -> Result<(), Box> { + let test_dir = init_test!(); + + let server_key = secp::random_secret(); + let node: Arc = Arc::new(MockGrinNode::new()); + let (server, _) = super::test_util::new_swapper(&test_dir, &server_key, None, node.clone()); + let kern = tx::build_kernel(&secp::random_secret(), 1000u64)?; + let tx: Arc = Arc::new(Transaction::new(Inputs::default(), &[], &[kern.clone()])); + let result = server.check_reorg(tx).await; + assert_eq!( + Err(SwapError::SwapTxNotFound(kern.excess())), + result + ); + + Ok(()) + } + /// Returns PeelOnionFailure when a failure occurs trying to decrypt the onion payload. #[tokio::test] #[named] diff --git a/src/store.rs b/src/store.rs index 61d128e..e666701 100644 --- a/src/store.rs +++ b/src/store.rs @@ -236,7 +236,7 @@ impl SwapStore { /// Reads a single value by key fn read + Copy, V: Readable>(&self, prefix: u8, k: K) -> Result { - store::option_to_not_found(self.db.get_ser(&store::to_key(prefix, k)[..], None), || { + store::option_to_not_found(self.db.get_ser(&store::to_key(prefix, k), None), || { format!("{}:{}", prefix, k.to_hex()) }) .map_err(StoreError::ReadError) @@ -279,7 +279,6 @@ impl SwapStore { } /// Reads a swap from the database - #[allow(dead_code)] pub fn get_swap(&self, input_commit: &Commitment) -> Result { self.read(SWAP_PREFIX, input_commit) } diff --git a/tests/common/wallet.rs b/tests/common/wallet.rs index 21ef399..bf49de9 100644 --- a/tests/common/wallet.rs +++ b/tests/common/wallet.rs @@ -8,7 +8,7 @@ use grin_keychain::{BlindingFactor, ExtKeychain, Identifier, Keychain, SwitchCom use grin_onion::crypto::comsig::ComSignature; use grin_onion::onion::Onion; use grin_onion::Hop; -use grin_util::{Mutex, ToHex, ZeroingString}; +use grin_util::{Mutex, ZeroingString}; use grin_wallet_api::Owner; use grin_wallet_config::WalletConfig; use grin_wallet_controller::controller; @@ -28,243 +28,243 @@ use x25519_dalek::PublicKey as xPublicKey; /// Response to build a coinbase output. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct CbData { - /// Output - pub output: Output, - /// Kernel - pub kernel: TxKernel, - /// Key Id - pub key_id: Option, + /// Output + pub output: Output, + /// Kernel + pub kernel: TxKernel, + /// Key Id + pub key_id: Option, } pub struct IntegrationGrinWallet { - wallet: Arc< - Mutex< - Box< - dyn WalletInst< - 'static, - DefaultLCProvider<'static, HTTPNodeClient, ExtKeychain>, - HTTPNodeClient, - ExtKeychain, - >, - >, - >, - >, - api_listen_port: u16, - owner_api: Arc< - Owner, HTTPNodeClient, ExtKeychain>, - >, - http_client: Arc, + wallet: Arc< + Mutex< + Box< + dyn WalletInst< + 'static, + DefaultLCProvider<'static, HTTPNodeClient, ExtKeychain>, + HTTPNodeClient, + ExtKeychain, + >, + >, + >, + >, + api_listen_port: u16, + owner_api: Arc< + Owner, HTTPNodeClient, ExtKeychain>, + >, + http_client: Arc, } impl IntegrationGrinWallet { - pub async fn async_new_wallet( - wallet_dir: String, - api_listen_port: u16, - node_api: String, - ) -> IntegrationGrinWallet { - let node_client = HTTPNodeClient::new(&node_api, None).unwrap(); - let mut wallet = Box::new( - DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap(), - ) - as Box< - dyn WalletInst< - 'static, - DefaultLCProvider, - HTTPNodeClient, - ExtKeychain, - >, - >; + pub async fn async_new_wallet( + wallet_dir: String, + api_listen_port: u16, + node_api: String, + ) -> IntegrationGrinWallet { + let node_client = HTTPNodeClient::new(&node_api, None).unwrap(); + let mut wallet = Box::new( + DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap(), + ) + as Box< + dyn WalletInst< + 'static, + DefaultLCProvider, + HTTPNodeClient, + ExtKeychain, + >, + >; - // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc... - let lc = wallet.lc_provider().unwrap(); + // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc... + let lc = wallet.lc_provider().unwrap(); - let mut wallet_config = WalletConfig::default(); - wallet_config.check_node_api_http_addr = node_api.clone(); - wallet_config.owner_api_listen_port = Some(api_listen_port); - wallet_config.api_secret_path = None; - wallet_config.data_file_dir = wallet_dir.clone(); + let mut wallet_config = WalletConfig::default(); + wallet_config.check_node_api_http_addr = node_api.clone(); + wallet_config.owner_api_listen_port = Some(api_listen_port); + wallet_config.api_secret_path = None; + wallet_config.data_file_dir = wallet_dir.clone(); - // The top level wallet directory should be set manually (in the reference implementation, - // this is provided in the WalletConfig) - let _ = lc.set_top_level_directory(&wallet_config.data_file_dir); + // The top level wallet directory should be set manually (in the reference implementation, + // this is provided in the WalletConfig) + let _ = lc.set_top_level_directory(&wallet_config.data_file_dir); - lc.create_config( - &ChainTypes::AutomatedTesting, - "grin-wallet.toml", - Some(wallet_config.clone()), - None, - None, - ) - .unwrap(); + lc.create_config( + &ChainTypes::AutomatedTesting, + "grin-wallet.toml", + Some(wallet_config.clone()), + None, + None, + ) + .unwrap(); - lc.create_wallet(None, None, 12, ZeroingString::from("pass"), false) - .unwrap(); + lc.create_wallet(None, None, 12, ZeroingString::from("pass"), false) + .unwrap(); - // Start owner API - let km = Arc::new(Mutex::new(None)); - let wallet = Arc::new(Mutex::new(wallet)); - let owner_api = Arc::new(Owner::new(wallet.clone(), None)); + // Start owner API + let km = Arc::new(Mutex::new(None)); + let wallet = Arc::new(Mutex::new(wallet)); + let owner_api = Arc::new(Owner::new(wallet.clone(), None)); - let address_str = format!("127.0.0.1:{}", api_listen_port); - let owner_addr: SocketAddr = address_str.parse().unwrap(); - let thr_wallet = wallet.clone(); - let _thread_handle = thread::spawn(move || { - controller::owner_listener( - thr_wallet, - km, - address_str.as_str(), - None, - None, - Some(true), - None, - false, - ) - .unwrap() - }); + let address_str = format!("127.0.0.1:{}", api_listen_port); + let owner_addr: SocketAddr = address_str.parse().unwrap(); + let thr_wallet = wallet.clone(); + let _thread_handle = thread::spawn(move || { + controller::owner_listener( + thr_wallet, + km, + address_str.as_str(), + None, + None, + Some(true), + None, + false, + ) + .unwrap() + }); - let http_client = Arc::new( - HttpWallet::async_open_wallet(&owner_addr, &None, &ZeroingString::from("pass")) - .await - .unwrap(), - ); + let http_client = Arc::new( + HttpWallet::async_open_wallet(&owner_addr, &None, &ZeroingString::from("pass")) + .await + .unwrap(), + ); - IntegrationGrinWallet { - wallet, - api_listen_port, - owner_api, - http_client, - } - } + IntegrationGrinWallet { + wallet, + api_listen_port, + owner_api, + http_client, + } + } - pub async fn async_retrieve_summary_info(&self) -> Result { - let params = json!({ + pub async fn async_retrieve_summary_info(&self) -> Result { + let params = json!({ "token": self.http_client.clone().get_token(), "refresh_from_node": true, "minimum_confirmations": 1 }); - let (_, wallet_info): (bool, WalletInfo) = self - .http_client - .clone() - .async_perform_request("retrieve_summary_info", ¶ms) - .await?; - Ok(wallet_info) - } + let (_, wallet_info): (bool, WalletInfo) = self + .http_client + .clone() + .async_perform_request("retrieve_summary_info", ¶ms) + .await?; + Ok(wallet_info) + } - pub async fn async_send( - &self, - receiving_wallet: &IntegrationGrinWallet, - amount: u64, - ) -> Result { - let slate = self.async_init_send_tx(amount).await.unwrap(); - let slate = receiving_wallet.async_receive_tx(&slate).await.unwrap(); - let slate = self.async_finalize_tx(&slate).await.unwrap(); - let tx = Slate::from(slate).tx_or_err().unwrap().clone(); - Ok(tx) - } + pub async fn async_send( + &self, + receiving_wallet: &IntegrationGrinWallet, + amount: u64, + ) -> Result { + let slate = self.async_init_send_tx(amount).await.unwrap(); + let slate = receiving_wallet.async_receive_tx(&slate).await.unwrap(); + let slate = self.async_finalize_tx(&slate).await.unwrap(); + let tx = Slate::from(slate).tx_or_err().unwrap().clone(); + Ok(tx) + } - async fn async_init_send_tx( - &self, - amount: u64, - ) -> Result { - let args = InitTxArgs { - src_acct_name: None, - amount, - minimum_confirmations: 0, - max_outputs: 10, - num_change_outputs: 1, - selection_strategy_is_use_all: false, - ..Default::default() - }; - let params = json!({ + async fn async_init_send_tx( + &self, + amount: u64, + ) -> Result { + let args = InitTxArgs { + src_acct_name: None, + amount, + minimum_confirmations: 0, + max_outputs: 10, + num_change_outputs: 1, + selection_strategy_is_use_all: false, + ..Default::default() + }; + let params = json!({ "token": self.http_client.clone().get_token(), "args": args }); - let slate: VersionedSlate = self - .http_client - .clone() - .async_perform_request("init_send_tx", ¶ms) - .await?; + let slate: VersionedSlate = self + .http_client + .clone() + .async_perform_request("init_send_tx", ¶ms) + .await?; - let params = json!({ + let params = json!({ "token": self.http_client.clone().get_token(), "slate": &slate }); - self.http_client - .clone() - .async_perform_request("tx_lock_outputs", ¶ms) - .await?; + self.http_client + .clone() + .async_perform_request("tx_lock_outputs", ¶ms) + .await?; - Ok(slate) - } + Ok(slate) + } - pub async fn async_receive_tx( - &self, - slate: &VersionedSlate, - ) -> Result { - let req_body = json!({ + pub async fn async_receive_tx( + &self, + slate: &VersionedSlate, + ) -> Result { + let req_body = json!({ "jsonrpc": "2.0", "method": "receive_tx", "id": 1, "params": [slate, null, null] }); - let res: Response = client::post_async(self.foreign_api().as_str(), &req_body, None) - .await - .map_err(|e| { - let report = format!("Failed to receive tx. Is the wallet listening? {}", e); - error!("{}", report); - grin_servers::common::types::Error::WalletComm(report) - })?; + let res: Response = client::post_async(self.foreign_api().as_str(), &req_body, None) + .await + .map_err(|e| { + let report = format!("Failed to receive tx. Is the wallet listening? {}", e); + error!("{}", report); + grin_servers::common::types::Error::WalletComm(report) + })?; - let parsed: VersionedSlate = res.clone().into_result().map_err(|e| { - let report = format!("Error parsing result: {}", e); - error!("{}", report); - grin_servers::common::types::Error::WalletComm(report) - })?; + let parsed: VersionedSlate = res.clone().into_result().map_err(|e| { + let report = format!("Error parsing result: {}", e); + error!("{}", report); + grin_servers::common::types::Error::WalletComm(report) + })?; - Ok(parsed) - } + Ok(parsed) + } - async fn async_finalize_tx( - &self, - slate: &VersionedSlate, - ) -> Result { - let params = json!({ + async fn async_finalize_tx( + &self, + slate: &VersionedSlate, + ) -> Result { + let params = json!({ "token": self.http_client.clone().get_token(), "slate": slate }); - self.http_client - .clone() - .async_perform_request("finalize_tx", ¶ms) - .await - } + self.http_client + .clone() + .async_perform_request("finalize_tx", ¶ms) + .await + } - async fn async_post_tx( - &self, - finalized_slate: &VersionedSlate, - fluff: bool, - ) -> Result { - let params = json!({ + async fn async_post_tx( + &self, + finalized_slate: &VersionedSlate, + fluff: bool, + ) -> Result { + let params = json!({ "token": self.http_client.clone().get_token(), "slate": finalized_slate, "fluff": fluff }); - self.http_client - .clone() - .async_perform_request("post_tx", ¶ms) - .await - } + self.http_client + .clone() + .async_perform_request("post_tx", ¶ms) + .await + } - /// Call the wallet API to create a coinbase output for the given block_fees. - /// Will retry based on default "retry forever with backoff" behavior. - pub async fn async_create_coinbase( - &self, - block_fees: &BlockFees, - ) -> Result { - let req_body = json!({ + /// Call the wallet API to create a coinbase output for the given block_fees. + /// Will retry based on default "retry forever with backoff" behavior. + pub async fn async_create_coinbase( + &self, + block_fees: &BlockFees, + ) -> Result { + let req_body = json!({ "jsonrpc": "2.0", "method": "build_coinbase", "id": 1, @@ -273,159 +273,159 @@ impl IntegrationGrinWallet { } }); - let res: Response = client::post_async(self.foreign_api().as_str(), &req_body, None) - .await - .map_err(|e| { - let report = format!("Failed to get coinbase. Is the wallet listening? {}", e); - error!("{}", report); - grin_servers::common::types::Error::WalletComm(report) - })?; - let parsed: CbData = res.clone().into_result().map_err(|e| { - let report = format!("Error parsing result: {}", e); - error!("{}", report); - grin_servers::common::types::Error::WalletComm(report) - })?; + let res: Response = client::post_async(self.foreign_api().as_str(), &req_body, None) + .await + .map_err(|e| { + let report = format!("Failed to get coinbase. Is the wallet listening? {}", e); + error!("{}", report); + grin_servers::common::types::Error::WalletComm(report) + })?; + let parsed: CbData = res.clone().into_result().map_err(|e| { + let report = format!("Error parsing result: {}", e); + error!("{}", report); + grin_servers::common::types::Error::WalletComm(report) + })?; - Ok(parsed) - } + Ok(parsed) + } - pub fn build_onion( - &self, - commitment: &Commitment, - server_pubkeys: &Vec, - ) -> Result<(Onion, ComSignature), grin_wallet_libwallet::Error> { - let keychain = self - .wallet - .lock() - .lc_provider()? - .wallet_inst()? - .keychain(self.keychain_mask().as_ref())?; - let (_, outputs) = - self.owner_api - .retrieve_outputs(self.keychain_mask().as_ref(), false, false, None)?; + pub fn build_onion( + &self, + commitment: &Commitment, + server_pubkeys: &Vec, + ) -> Result<(Onion, ComSignature), grin_wallet_libwallet::Error> { + let keychain = self + .wallet + .lock() + .lc_provider()? + .wallet_inst()? + .keychain(self.keychain_mask().as_ref())?; + let (_, outputs) = + self.owner_api + .retrieve_outputs(self.keychain_mask().as_ref(), false, false, None)?; - let mut output = None; - for o in &outputs { - if o.commit == *commitment { - output = Some(o.output.clone()); - break; - } - } + let mut output = None; + for o in &outputs { + if o.commit == *commitment { + output = Some(o.output.clone()); + break; + } + } - if output.is_none() { - return Err(grin_wallet_libwallet::Error::GenericError(String::from( - "output not found", - ))); - } + if output.is_none() { + return Err(grin_wallet_libwallet::Error::GenericError(String::from( + "output not found", + ))); + } - let amount = output.clone().unwrap().value; - let input_blind = keychain.derive_key( - amount, - &output.clone().unwrap().key_id, - SwitchCommitmentType::Regular, - )?; + let amount = output.clone().unwrap().value; + let input_blind = keychain.derive_key( + amount, + &output.clone().unwrap().key_id, + SwitchCommitmentType::Regular, + )?; - let fee = tx_fee(1, 1, 1); - let new_amount = amount - (fee * server_pubkeys.len() as u64); - let new_output = self.owner_api.build_output( - self.keychain_mask().as_ref(), - OutputFeatures::Plain, - new_amount, - )?; + let fee = tx_fee(1, 1, 1); + let new_amount = amount - (fee * server_pubkeys.len() as u64); + let new_output = self.owner_api.build_output( + self.keychain_mask().as_ref(), + OutputFeatures::Plain, + new_amount, + )?; - let secp = Secp256k1::new(); - let mut blind_sum = new_output - .blind - .split(&BlindingFactor::from_secret_key(input_blind.clone()), &secp)?; + let secp = Secp256k1::new(); + let mut blind_sum = new_output + .blind + .split(&BlindingFactor::from_secret_key(input_blind.clone()), &secp)?; - let hops = server_pubkeys - .iter() - .enumerate() - .map(|(i, &p)| { - if (i + 1) == server_pubkeys.len() { - Hop { - server_pubkey: p.clone(), - excess: blind_sum.secret_key(&secp).unwrap(), - fee: FeeFields::from(fee as u32), - rangeproof: Some(new_output.output.proof.clone()), - } - } else { - let hop_excess = BlindingFactor::rand(&secp); - blind_sum = blind_sum.split(&hop_excess, &secp).unwrap(); - Hop { - server_pubkey: p.clone(), - excess: hop_excess.secret_key(&secp).unwrap(), - fee: FeeFields::from(fee as u32), - rangeproof: None, - } - } - }) - .collect(); + let hops = server_pubkeys + .iter() + .enumerate() + .map(|(i, &p)| { + if (i + 1) == server_pubkeys.len() { + Hop { + server_pubkey: p.clone(), + excess: blind_sum.secret_key(&secp).unwrap(), + fee: FeeFields::from(fee as u32), + rangeproof: Some(new_output.output.proof.clone()), + } + } else { + let hop_excess = BlindingFactor::rand(&secp); + blind_sum = blind_sum.split(&hop_excess, &secp).unwrap(); + Hop { + server_pubkey: p.clone(), + excess: hop_excess.secret_key(&secp).unwrap(), + fee: FeeFields::from(fee as u32), + rangeproof: None, + } + } + }) + .collect(); - let onion = grin_onion::create_onion(&commitment, &hops).unwrap(); - let comsig = ComSignature::sign(amount, &input_blind, &onion.serialize().unwrap()).unwrap(); + let onion = grin_onion::create_onion(&commitment, &hops).unwrap(); + let comsig = ComSignature::sign(amount, &input_blind, &onion.serialize().unwrap()).unwrap(); - Ok((onion, comsig)) - } + Ok((onion, comsig)) + } - pub fn owner_api( - &self, - ) -> Arc< - Owner, HTTPNodeClient, ExtKeychain>, - > { - self.owner_api.clone() - } + pub fn owner_api( + &self, + ) -> Arc< + Owner, HTTPNodeClient, ExtKeychain>, + > { + self.owner_api.clone() + } - pub fn foreign_api(&self) -> String { - format!("http://127.0.0.1:{}/v2/foreign", self.api_listen_port) - } + pub fn foreign_api(&self) -> String { + format!("http://127.0.0.1:{}/v2/foreign", self.api_listen_port) + } - pub fn owner_address(&self) -> SocketAddr { - SocketAddr::new( - IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), - self.api_listen_port, - ) - } + pub fn owner_address(&self) -> SocketAddr { + SocketAddr::new( + IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), + self.api_listen_port, + ) + } - pub fn keychain_mask(&self) -> Option { - self.http_client.as_ref().get_token().keychain_mask.clone() - } + pub fn keychain_mask(&self) -> Option { + self.http_client.as_ref().get_token().keychain_mask.clone() + } - pub fn get_client(&self) -> Arc { - self.http_client.clone() - } + pub fn get_client(&self) -> Arc { + self.http_client.clone() + } } #[allow(dead_code)] pub struct GrinWalletManager { - // base directory for the server instance - working_dir: String, + // base directory for the server instance + working_dir: String, - wallets: Vec>>, + wallets: Vec>>, } impl GrinWalletManager { - pub fn new(test_dir: &str) -> GrinWalletManager { - GrinWalletManager { - working_dir: String::from(test_dir), - wallets: vec![], - } - } + pub fn new(test_dir: &str) -> GrinWalletManager { + GrinWalletManager { + working_dir: String::from(test_dir), + wallets: vec![], + } + } - pub async fn async_new_wallet( - &mut self, - node_api_addr: &SocketAddr, - ) -> Arc> { - let wallet_dir = format!("{}/wallets/{}", self.working_dir, self.wallets.len()); - let wallet = Arc::new(Mutex::new( - IntegrationGrinWallet::async_new_wallet( - wallet_dir, - 21000 + self.wallets.len() as u16, - format!("http://{}", node_api_addr), - ) - .await, - )); - self.wallets.push(wallet.clone()); - wallet - } + pub async fn async_new_wallet( + &mut self, + node_api_addr: &SocketAddr, + ) -> Arc> { + let wallet_dir = format!("{}/wallets/{}", self.working_dir, self.wallets.len()); + let wallet = Arc::new(Mutex::new( + IntegrationGrinWallet::async_new_wallet( + wallet_dir, + 21000 + self.wallets.len() as u16, + format!("http://{}", node_api_addr), + ) + .await, + )); + self.wallets.push(wallet.clone()); + wallet + } }