replace TorProcess with rust-native arti client and upgrade to tokio1

This commit is contained in:
scilio
2024-04-16 11:16:53 -04:00
parent df2cb31258
commit 75f2bdf2ba
21 changed files with 5663 additions and 2432 deletions
+54 -37
View File
@@ -1,27 +1,31 @@
use crate::common::node::IntegrationGrinNode;
use crate::common::wallet::{GrinWalletManager, IntegrationGrinWallet};
use grin_core::core::Transaction;
use grin_onion::crypto::comsig::ComSignature;
use grin_onion::crypto::dalek::DalekPublicKey;
use grin_onion::onion::Onion;
use grin_wallet_impls::tor::process::TorProcess;
use mwixnet::client::MixClientImpl;
use mwixnet::{tor, SwapError, SwapServer, SwapStore};
use secp256k1zkp::SecretKey;
use std::iter;
use std::net::TcpListener;
use std::sync::Arc;
use grin_core::core::Transaction;
use tor_rtcompat::PreferredRuntime;
use x25519_dalek::{PublicKey as xPublicKey, StaticSecret};
pub struct IntegrationSwapServer {
use grin_onion::crypto::comsig::ComSignature;
use grin_onion::crypto::dalek::DalekPublicKey;
use grin_onion::onion::Onion;
use mwixnet::mix_client::MixClientImpl;
use mwixnet::tor::TorService;
use mwixnet::{tor, SwapError, SwapServer, SwapStore};
use secp256k1zkp::SecretKey;
use crate::common::node::IntegrationGrinNode;
use crate::common::wallet::{GrinWalletManager, IntegrationGrinWallet};
pub struct IntegrationSwapServer<R: tor_rtcompat::Runtime> {
server_key: SecretKey,
tor_process: TorProcess,
tor_instance: Arc<grin_util::Mutex<TorService<R>>>,
swap_server: Arc<tokio::sync::Mutex<dyn SwapServer>>,
rpc_server: jsonrpc_http_server::Server,
_wallet: Arc<grin_util::Mutex<IntegrationGrinWallet>>,
}
impl IntegrationSwapServer {
impl<R: tor_rtcompat::Runtime> IntegrationSwapServer<R> {
pub async fn async_swap(&self, onion: &Onion, comsig: &ComSignature) -> Result<(), SwapError> {
self.swap_server.lock().await.swap(&onion, &comsig).await
}
@@ -31,21 +35,25 @@ impl IntegrationSwapServer {
}
}
pub struct IntegrationMixServer {
pub struct IntegrationMixServer<R: tor_rtcompat::Runtime> {
server_key: SecretKey,
tor_process: TorProcess,
tor_instance: Arc<grin_util::Mutex<TorService<R>>>,
rpc_server: jsonrpc_http_server::Server,
_wallet: Arc<grin_util::Mutex<IntegrationGrinWallet>>,
}
async fn async_new_swap_server(
async fn async_new_swap_server<R>(
data_dir: &str,
rt_handle: &tokio::runtime::Handle,
tor_runtime: R,
wallets: &mut GrinWalletManager,
server_key: &SecretKey,
node: &Arc<grin_util::Mutex<IntegrationGrinNode>>,
next_server: Option<&IntegrationMixServer>,
) -> IntegrationSwapServer {
next_server: Option<&IntegrationMixServer<R>>,
) -> IntegrationSwapServer<R>
where
R: tor_rtcompat::Runtime,
{
let wallet = wallets.async_new_wallet(&node.lock().api_address()).await;
let server_config = mwixnet::ServerConfig {
@@ -55,10 +63,6 @@ async fn async_new_swap_server(
.unwrap()
.local_addr()
.unwrap(),
socks_proxy_addr: TcpListener::bind("127.0.0.1:0")
.unwrap()
.local_addr()
.unwrap(),
grin_node_url: node.lock().api_address(),
grin_node_secret_path: None,
wallet_owner_url: wallet.lock().owner_address(),
@@ -72,7 +76,10 @@ async fn async_new_swap_server(
// Open SwapStore
let store = SwapStore::new(format!("{}/db", data_dir).as_str()).unwrap();
let tor_process = tor::init_tor_listener(&data_dir, &server_config).unwrap();
let tor_instance = tor::async_init_tor(tor_runtime, &data_dir, &server_config)
.await
.unwrap();
let tor_instance = Arc::new(grin_util::Mutex::new(tor_instance));
let (swap_server, rpc_server) = mwixnet::swap_listen(
rt_handle,
@@ -80,6 +87,7 @@ async fn async_new_swap_server(
match next_server {
Some(s) => Some(Arc::new(MixClientImpl::new(
server_config.clone(),
tor_instance.clone(),
DalekPublicKey::from_secret(&s.server_key),
))),
None => None,
@@ -92,22 +100,26 @@ async fn async_new_swap_server(
IntegrationSwapServer {
server_key: server_key.clone(),
tor_process,
tor_instance,
swap_server,
rpc_server,
_wallet: wallet,
}
}
async fn async_new_mix_server(
async fn async_new_mix_server<R>(
data_dir: &str,
rt_handle: &tokio::runtime::Handle,
tor_runtime: R,
wallets: &mut GrinWalletManager,
server_key: &SecretKey,
node: &Arc<grin_util::Mutex<IntegrationGrinNode>>,
prev_server: DalekPublicKey,
next_server: Option<&IntegrationMixServer>,
) -> IntegrationMixServer {
next_server: Option<&IntegrationMixServer<R>>,
) -> IntegrationMixServer<R>
where
R: tor_rtcompat::Runtime,
{
let wallet = wallets.async_new_wallet(&node.lock().api_address()).await;
let server_config = mwixnet::ServerConfig {
key: server_key.clone(),
@@ -116,10 +128,6 @@ async fn async_new_mix_server(
.unwrap()
.local_addr()
.unwrap(),
socks_proxy_addr: TcpListener::bind("127.0.0.1:0")
.unwrap()
.local_addr()
.unwrap(),
grin_node_url: node.lock().api_address(),
grin_node_secret_path: None,
wallet_owner_url: wallet.lock().owner_address(),
@@ -131,7 +139,10 @@ async fn async_new_mix_server(
},
};
let tor_process = tor::init_tor_listener(&data_dir, &server_config).unwrap();
let tor_instance = tor::async_init_tor(tor_runtime, &data_dir, &server_config)
.await
.unwrap();
let tor_instance = Arc::new(grin_util::Mutex::new(tor_instance));
let (_, rpc_server) = mwixnet::mix_listen(
rt_handle,
@@ -139,6 +150,7 @@ async fn async_new_mix_server(
match next_server {
Some(s) => Some(Arc::new(MixClientImpl::new(
server_config.clone(),
tor_instance.clone(),
DalekPublicKey::from_secret(&s.server_key),
))),
None => None,
@@ -150,16 +162,16 @@ async fn async_new_mix_server(
IntegrationMixServer {
server_key: server_key.clone(),
tor_process,
tor_instance,
rpc_server,
_wallet: wallet,
}
}
pub struct Servers {
pub swapper: IntegrationSwapServer,
pub swapper: IntegrationSwapServer<PreferredRuntime>,
pub mixers: Vec<IntegrationMixServer>,
pub mixers: Vec<IntegrationMixServer<PreferredRuntime>>,
}
impl Servers {
@@ -176,12 +188,16 @@ impl Servers {
.take(num_mixers + 1)
.collect();
// Setup mock tor network
let tor_runtime = PreferredRuntime::current().unwrap();
// Build mixers in reverse order
let mut mixers = Vec::new();
for i in (0..num_mixers).rev() {
let mix_server = async_new_mix_server(
format!("{}/mixers/{}", test_dir, i).as_str(),
rt_handle,
tor_runtime.clone(),
wallets,
&server_keys[i + 1],
&node,
@@ -206,6 +222,7 @@ impl Servers {
let swapper = async_new_swap_server(
format!("{}/swapper", test_dir).as_str(),
rt_handle,
tor_runtime.clone(),
wallets,
&server_keys[0],
&node,
@@ -234,11 +251,11 @@ impl Servers {
pub fn stop_all(&mut self) {
self.swapper.rpc_server.close_handle().close();
self.swapper.tor_process.kill().unwrap();
self.swapper.tor_instance.lock().stop();
self.mixers.iter_mut().for_each(|mixer| {
mixer.rpc_server.close_handle().close();
mixer.tor_process.kill().unwrap();
mixer.tor_instance.lock().stop();
});
}
}
+324 -342
View File
@@ -1,13 +1,11 @@
use crate::common::types::BlockFees;
use grin_api::client;
use grin_api::json_rpc::Response;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc;
use std::thread;
use grin_core::core::{FeeFields, Output, OutputFeatures, Transaction, TxKernel};
use grin_core::global::ChainTypes;
use grin_core::libtx::tx_fee;
use grin_keychain::{BlindingFactor, ExtKeychain, Identifier, Keychain, SwitchCommitmentType};
use grin_onion::crypto::comsig::ComSignature;
use grin_onion::onion::Onion;
use grin_onion::Hop;
use grin_util::{Mutex, ZeroingString};
use grin_wallet_api::Owner;
use grin_wallet_config::WalletConfig;
@@ -15,417 +13,401 @@ use grin_wallet_controller::controller;
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient};
use grin_wallet_libwallet::{InitTxArgs, Slate, VersionedSlate, WalletInfo, WalletInst};
use log::error;
use serde_derive::{Deserialize, Serialize};
use serde_json::json;
use x25519_dalek::PublicKey as xPublicKey;
use grin_onion::crypto::comsig::ComSignature;
use grin_onion::onion::Onion;
use grin_onion::Hop;
use mwixnet::http;
use mwixnet::wallet::HttpWallet;
use secp256k1zkp::pedersen::Commitment;
use secp256k1zkp::{Secp256k1, SecretKey};
use serde_derive::{Deserialize, Serialize};
use serde_json::json;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc;
use std::thread;
use x25519_dalek::PublicKey as xPublicKey;
use crate::common::types::BlockFees;
/// 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<Identifier>,
/// Output
pub output: Output,
/// Kernel
pub kernel: TxKernel,
/// Key Id
pub key_id: Option<Identifier>,
}
pub struct IntegrationGrinWallet {
wallet: Arc<
Mutex<
Box<
dyn WalletInst<
'static,
DefaultLCProvider<'static, HTTPNodeClient, ExtKeychain>,
HTTPNodeClient,
ExtKeychain,
>,
>,
>,
>,
api_listen_port: u16,
owner_api: Arc<
Owner<DefaultLCProvider<'static, HTTPNodeClient, ExtKeychain>, HTTPNodeClient, ExtKeychain>,
>,
http_client: Arc<HttpWallet>,
wallet: Arc<
Mutex<
Box<
dyn WalletInst<
'static,
DefaultLCProvider<'static, HTTPNodeClient, ExtKeychain>,
HTTPNodeClient,
ExtKeychain,
>,
>,
>,
>,
api_listen_port: u16,
owner_api: Arc<
Owner<DefaultLCProvider<'static, HTTPNodeClient, ExtKeychain>, HTTPNodeClient, ExtKeychain>,
>,
http_client: Arc<HttpWallet>,
}
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>,
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>,
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<WalletInfo, mwixnet::WalletError> {
let params = json!({
pub async fn async_retrieve_summary_info(&self) -> Result<WalletInfo, mwixnet::WalletError> {
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", &params)
.await?;
Ok(wallet_info)
}
let (_, wallet_info): (bool, WalletInfo) = self
.http_client
.clone()
.async_perform_request("retrieve_summary_info", &params)
.await?;
Ok(wallet_info)
}
pub async fn async_send(
&self,
receiving_wallet: &IntegrationGrinWallet,
amount: u64,
) -> Result<Transaction, mwixnet::WalletError> {
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<Transaction, mwixnet::WalletError> {
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<VersionedSlate, mwixnet::WalletError> {
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<VersionedSlate, mwixnet::WalletError> {
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", &params)
.await?;
let slate: VersionedSlate = self
.http_client
.clone()
.async_perform_request("init_send_tx", &params)
.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", &params)
.await?;
self.http_client
.clone()
.async_perform_request("tx_lock_outputs", &params)
.await?;
Ok(slate)
}
Ok(slate)
}
pub async fn async_receive_tx(
&self,
slate: &VersionedSlate,
) -> Result<VersionedSlate, grin_servers::common::types::Error> {
let req_body = json!({
"jsonrpc": "2.0",
"method": "receive_tx",
"id": 1,
"params": [slate, null, null]
});
pub async fn async_receive_tx(
&self,
slate: &VersionedSlate,
) -> Result<VersionedSlate, grin_servers::common::types::Error> {
let params = json!([slate, null, null]);
let response =
http::async_send_json_request(&self.foreign_api(), &None, "receive_tx", &params)
.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)
})?;
Ok(response)
}
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)
}
async fn async_finalize_tx(
&self,
slate: &VersionedSlate,
) -> Result<VersionedSlate, mwixnet::WalletError> {
let params = json!({
async fn async_finalize_tx(
&self,
slate: &VersionedSlate,
) -> Result<VersionedSlate, mwixnet::WalletError> {
let params = json!({
"token": self.http_client.clone().get_token(),
"slate": slate
});
self.http_client
.clone()
.async_perform_request("finalize_tx", &params)
.await
}
self.http_client
.clone()
.async_perform_request("finalize_tx", &params)
.await
}
async fn async_post_tx(
&self,
finalized_slate: &VersionedSlate,
fluff: bool,
) -> Result<VersionedSlate, mwixnet::WalletError> {
let params = json!({
#[allow(dead_code)]
async fn async_post_tx(
&self,
finalized_slate: &VersionedSlate,
fluff: bool,
) -> Result<VersionedSlate, mwixnet::WalletError> {
let params = json!({
"token": self.http_client.clone().get_token(),
"slate": finalized_slate,
"fluff": fluff
});
self.http_client
.clone()
.async_perform_request("post_tx", &params)
.await
}
self.http_client
.clone()
.async_perform_request("post_tx", &params)
.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<CbData, grin_servers::common::types::Error> {
let req_body = json!({
"jsonrpc": "2.0",
"method": "build_coinbase",
"id": 1,
"params": {
"block_fees": block_fees
}
/// 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<CbData, grin_servers::common::types::Error> {
let params = json!({
"block_fees": block_fees
});
let response =
http::async_send_json_request(&self.foreign_api(), &None, "build_coinbase", &params)
.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 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(response)
}
Ok(parsed)
}
pub fn build_onion(
&self,
commitment: &Commitment,
server_pubkeys: &Vec<xPublicKey>,
) -> 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<xPublicKey>,
) -> 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<DefaultLCProvider<'static, HTTPNodeClient, ExtKeychain>, HTTPNodeClient, ExtKeychain>,
> {
self.owner_api.clone()
}
pub fn owner_api(
&self,
) -> Arc<
Owner<DefaultLCProvider<'static, HTTPNodeClient, ExtKeychain>, 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<SecretKey> {
self.http_client.as_ref().get_token().keychain_mask.clone()
}
pub fn keychain_mask(&self) -> Option<SecretKey> {
self.http_client.as_ref().get_token().keychain_mask.clone()
}
pub fn get_client(&self) -> Arc<HttpWallet> {
self.http_client.clone()
}
pub fn get_client(&self) -> Arc<HttpWallet> {
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<Arc<Mutex<IntegrationGrinWallet>>>,
wallets: Vec<Arc<Mutex<IntegrationGrinWallet>>>,
}
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<Mutex<IntegrationGrinWallet>> {
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<Mutex<IntegrationGrinWallet>> {
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
}
}