mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-07-30 01:58:55 -04:00
Download and run monero wallet rpc on swap cli startup
If the monero wallet rpc has not already been downloaded we download the monero cli package and extract the wallet rpc. The unneeded files are cleaned up. The monero wallet rpc is started on a random port which is provided to the swap cli. We added a fork of tokio-tar via a git subtree because we needed a tokio-tar version that was compatible with tokio 1.0. Remove this subtree in favor of a regular cargo dependency when this PR merges: https://github.com/vorot93/tokio-tar/pull/3.
This commit is contained in:
parent
18ba8f49c4
commit
51c16f23d8
12 changed files with 354 additions and 52 deletions
|
@ -13,6 +13,7 @@ name = "swap"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
async-compression = { version = "0.3", features = ["bzip2", "tokio"] }
|
||||
async-recursion = "0.3.1"
|
||||
async-trait = "0.1"
|
||||
atty = "0.2"
|
||||
|
@ -36,7 +37,7 @@ pem = "0.8"
|
|||
prettytable-rs = "0.8"
|
||||
rand = "0.7"
|
||||
rand_chacha = "0.2.0"
|
||||
reqwest = { version = "0.11", default-features = false }
|
||||
reqwest = { version = "0.11", features = ["rustls-tls", "stream"], default-features = false }
|
||||
rust_decimal = "1.10"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_cbor = "0.11"
|
||||
|
@ -48,8 +49,10 @@ structopt = "0.3"
|
|||
strum = { version = "0.20", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
time = "0.2"
|
||||
tokio = { version = "1.0", features = ["rt-multi-thread", "time", "macros", "sync"] }
|
||||
tokio = { version = "1.0", features = ["rt-multi-thread", "time", "macros", "sync", "process", "fs"] }
|
||||
tokio-tar = { path = "../tokio-tar" }
|
||||
tokio-tungstenite = { version = "0.13", features = [ "tls" ] }
|
||||
tokio-util = { version = "0.6.3", features = ["io"] }
|
||||
toml = "0.5"
|
||||
tracing = { version = "0.1", features = ["attributes"] }
|
||||
tracing-futures = { version = "0.2", features = ["std-future", "futures-03"] }
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
use anyhow::{Context, Result};
|
||||
use prettytable::{row, Table};
|
||||
use reqwest::Url;
|
||||
use std::{path::Path, sync::Arc};
|
||||
use structopt::StructOpt;
|
||||
use swap::{
|
||||
|
@ -84,6 +85,12 @@ async fn main() -> Result<()> {
|
|||
let monero_network = monero::Network::Stagenet;
|
||||
let execution_params = execution_params::Testnet::get_execution_params();
|
||||
|
||||
let monero_wallet_rpc = monero::WalletRpc::new(config.data.dir.join("monero")).await?;
|
||||
|
||||
let monero_wallet_rpc_process = monero_wallet_rpc
|
||||
.run(monero_network, "stagenet.community.xmr.to")
|
||||
.await?;
|
||||
|
||||
match opt.cmd {
|
||||
Command::BuyXmr {
|
||||
alice_peer_id,
|
||||
|
@ -96,6 +103,7 @@ async fn main() -> Result<()> {
|
|||
&wallet_data_dir,
|
||||
monero_network,
|
||||
seed,
|
||||
monero_wallet_rpc_process.endpoint(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
@ -149,6 +157,7 @@ async fn main() -> Result<()> {
|
|||
&wallet_data_dir,
|
||||
monero_network,
|
||||
seed,
|
||||
monero_wallet_rpc_process.endpoint(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
@ -180,6 +189,7 @@ async fn main() -> Result<()> {
|
|||
&wallet_data_dir,
|
||||
monero_network,
|
||||
seed,
|
||||
monero_wallet_rpc_process.endpoint(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
@ -230,6 +240,7 @@ async fn main() -> Result<()> {
|
|||
&wallet_data_dir,
|
||||
monero_network,
|
||||
seed,
|
||||
monero_wallet_rpc_process.endpoint(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
@ -268,6 +279,7 @@ async fn init_wallets(
|
|||
bitcoin_wallet_data_dir: &Path,
|
||||
monero_network: monero::Network,
|
||||
seed: Seed,
|
||||
monero_wallet_rpc_url: Url,
|
||||
) -> Result<(bitcoin::Wallet, monero::Wallet)> {
|
||||
let bitcoin_wallet = bitcoin::Wallet::new(
|
||||
config.bitcoin.electrum_rpc_url,
|
||||
|
@ -290,7 +302,7 @@ async fn init_wallets(
|
|||
);
|
||||
|
||||
let monero_wallet = monero::Wallet::new(
|
||||
config.monero.wallet_rpc_url.clone(),
|
||||
monero_wallet_rpc_url.clone(),
|
||||
monero_network,
|
||||
MONERO_BLOCKCHAIN_MONITORING_WALLET_NAME.to_string(),
|
||||
);
|
||||
|
@ -306,7 +318,7 @@ async fn init_wallets(
|
|||
.context(format!(
|
||||
"Unable to create Monero wallet for blockchain monitoring.\
|
||||
Please ensure that the monero-wallet-rpc is available at {}",
|
||||
config.monero.wallet_rpc_url
|
||||
monero_wallet_rpc_url
|
||||
))?;
|
||||
|
||||
info!(
|
||||
|
|
|
@ -13,13 +13,11 @@ use url::Url;
|
|||
|
||||
pub const DEFAULT_ELECTRUM_HTTP_URL: &str = "https://blockstream.info/testnet/api/";
|
||||
const DEFAULT_ELECTRUM_RPC_URL: &str = "ssl://electrum.blockstream.info:60002";
|
||||
const DEFAULT_MONERO_WALLET_RPC_TESTNET_URL: &str = "http://127.0.0.1:38083/json_rpc";
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)]
|
||||
pub struct Config {
|
||||
pub data: Data,
|
||||
pub bitcoin: Bitcoin,
|
||||
pub monero: Monero,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
@ -48,12 +46,6 @@ pub struct Bitcoin {
|
|||
pub electrum_rpc_url: Url,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Monero {
|
||||
pub wallet_rpc_url: Url,
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, Copy)]
|
||||
#[error("config not initialized")]
|
||||
pub struct ConfigNotInitialized {}
|
||||
|
@ -118,11 +110,6 @@ pub fn query_user_for_initial_testnet_config() -> Result<Config> {
|
|||
.interact_text()?;
|
||||
let electrum_rpc_url = Url::parse(electrum_rpc_url.as_str())?;
|
||||
|
||||
let monero_wallet_rpc_url = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Enter Monero Wallet RPC URL or hit enter to use default")
|
||||
.default(DEFAULT_MONERO_WALLET_RPC_TESTNET_URL.to_owned())
|
||||
.interact_text()?;
|
||||
let monero_wallet_rpc_url = monero_wallet_rpc_url.as_str().parse()?;
|
||||
println!();
|
||||
|
||||
Ok(Config {
|
||||
|
@ -131,9 +118,6 @@ pub fn query_user_for_initial_testnet_config() -> Result<Config> {
|
|||
electrum_http_url,
|
||||
electrum_rpc_url,
|
||||
},
|
||||
monero: Monero {
|
||||
wallet_rpc_url: monero_wallet_rpc_url,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -156,9 +140,6 @@ mod tests {
|
|||
electrum_http_url: Url::from_str(DEFAULT_ELECTRUM_HTTP_URL).unwrap(),
|
||||
electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL).unwrap(),
|
||||
},
|
||||
monero: Monero {
|
||||
wallet_rpc_url: Url::from_str("http://127.0.0.1:38083/json_rpc").unwrap(),
|
||||
},
|
||||
};
|
||||
|
||||
initial_setup(config_path.clone(), || Ok(expected.clone())).unwrap();
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
pub mod wallet;
|
||||
mod wallet_rpc;
|
||||
|
||||
pub use ::monero::{Network, PrivateKey, PublicKey};
|
||||
pub use curve25519_dalek::scalar::Scalar;
|
||||
pub use wallet::Wallet;
|
||||
pub use wallet_rpc::{WalletRpc, WalletRpcProcess};
|
||||
|
||||
use crate::bitcoin;
|
||||
use ::bitcoin::hashes::core::fmt::Formatter;
|
||||
|
|
164
swap/src/monero/wallet_rpc.rs
Normal file
164
swap/src/monero/wallet_rpc.rs
Normal file
|
@ -0,0 +1,164 @@
|
|||
use ::monero::Network;
|
||||
use anyhow::{Context, Result};
|
||||
use async_compression::tokio::bufread::BzDecoder;
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
use reqwest::Url;
|
||||
use std::{
|
||||
io::ErrorKind,
|
||||
path::{Path, PathBuf},
|
||||
process::Stdio,
|
||||
};
|
||||
use tokio::{
|
||||
fs::{remove_file, OpenOptions},
|
||||
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
|
||||
process::{Child, Command},
|
||||
};
|
||||
use tokio_tar::Archive;
|
||||
use tokio_util::{
|
||||
codec::{BytesCodec, FramedRead},
|
||||
io::StreamReader,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
const DOWNLOAD_URL: &str = "http://downloads.getmonero.org/cli/monero-mac-x64-v0.17.1.9.tar.bz2";
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-linux-x64-v0.17.1.9.tar.bz2";
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
|
||||
compile_error!("unsupported operating system");
|
||||
|
||||
const PACKED_FILE: &str = "monero-wallet-rpc";
|
||||
|
||||
pub struct WalletRpcProcess {
|
||||
_child: Child,
|
||||
port: u16,
|
||||
}
|
||||
|
||||
impl WalletRpcProcess {
|
||||
pub fn endpoint(&self) -> Url {
|
||||
Url::parse(&format!("http://127.0.0.1:{}/json_rpc", self.port))
|
||||
.expect("Static url template is always valid")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WalletRpc {
|
||||
working_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl WalletRpc {
|
||||
pub async fn new(working_dir: impl AsRef<Path>) -> Result<WalletRpc> {
|
||||
let working_dir = working_dir.as_ref();
|
||||
|
||||
if !working_dir.exists() {
|
||||
tokio::fs::create_dir(working_dir).await?;
|
||||
}
|
||||
|
||||
let monero_wallet_rpc = WalletRpc {
|
||||
working_dir: working_dir.to_path_buf(),
|
||||
};
|
||||
|
||||
if monero_wallet_rpc.tar_path().exists() {
|
||||
remove_file(monero_wallet_rpc.tar_path()).await?;
|
||||
}
|
||||
|
||||
if !monero_wallet_rpc.exec_path().exists() {
|
||||
let mut options = OpenOptions::new();
|
||||
let mut file = options
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(monero_wallet_rpc.tar_path())
|
||||
.await?;
|
||||
|
||||
let byte_stream = reqwest::get(DOWNLOAD_URL)
|
||||
.await?
|
||||
.bytes_stream()
|
||||
.map_err(|err| std::io::Error::new(ErrorKind::Other, err));
|
||||
|
||||
let mut stream = FramedRead::new(
|
||||
BzDecoder::new(StreamReader::new(byte_stream)),
|
||||
BytesCodec::new(),
|
||||
)
|
||||
.map_ok(|bytes| bytes.freeze());
|
||||
|
||||
while let Some(chunk) = stream.next().await {
|
||||
file.write(&chunk?).await?;
|
||||
}
|
||||
|
||||
file.flush().await?;
|
||||
|
||||
let mut options = OpenOptions::new();
|
||||
let file = options
|
||||
.read(true)
|
||||
.open(monero_wallet_rpc.tar_path())
|
||||
.await?;
|
||||
|
||||
let mut ar = Archive::new(file);
|
||||
let mut entries = ar.entries()?;
|
||||
|
||||
while let Some(file) = entries.next().await {
|
||||
let mut f = file?;
|
||||
if f.path()?
|
||||
.to_str()
|
||||
.context("Could not find convert path to str in tar ball")?
|
||||
.contains(PACKED_FILE)
|
||||
{
|
||||
f.unpack(monero_wallet_rpc.exec_path()).await?;
|
||||
}
|
||||
}
|
||||
|
||||
remove_file(monero_wallet_rpc.tar_path()).await?;
|
||||
}
|
||||
|
||||
Ok(monero_wallet_rpc)
|
||||
}
|
||||
pub async fn run(&self, network: Network, daemon_host: &str) -> Result<WalletRpcProcess> {
|
||||
let port = tokio::net::TcpListener::bind("127.0.0.1:0")
|
||||
.await?
|
||||
.local_addr()?
|
||||
.port();
|
||||
|
||||
let mut child = Command::new(self.exec_path())
|
||||
.stdout(Stdio::piped())
|
||||
.kill_on_drop(true)
|
||||
.arg(match network {
|
||||
Network::Mainnet => "--mainnet",
|
||||
Network::Stagenet => "--stagenet",
|
||||
Network::Testnet => "--testnet",
|
||||
})
|
||||
.arg("--daemon-host")
|
||||
.arg(daemon_host)
|
||||
.arg("--rpc-bind-port")
|
||||
.arg(format!("{}", port))
|
||||
.arg("--disable-rpc-login")
|
||||
.arg("--wallet-dir")
|
||||
.arg(self.working_dir.join("monero-data"))
|
||||
.spawn()?;
|
||||
|
||||
let stdout = child
|
||||
.stdout
|
||||
.take()
|
||||
.expect("monero wallet rpc stdout was not piped parent process");
|
||||
|
||||
let mut reader = BufReader::new(stdout).lines();
|
||||
|
||||
while let Some(line) = reader.next_line().await? {
|
||||
if line.contains("Starting wallet RPC server") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(WalletRpcProcess {
|
||||
_child: child,
|
||||
port,
|
||||
})
|
||||
}
|
||||
|
||||
fn tar_path(&self) -> PathBuf {
|
||||
self.working_dir.join("monero-cli-wallet.tar")
|
||||
}
|
||||
|
||||
fn exec_path(&self) -> PathBuf {
|
||||
self.working_dir.join(PACKED_FILE)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue