Add windows support to monero rpc installer

This commit is contained in:
rishflab 2021-03-01 17:34:25 +11:00
parent 27df9128be
commit bcdde021eb
3 changed files with 160 additions and 41 deletions

View file

@ -1,6 +1,5 @@
use ::monero::Network;
use anyhow::{bail, Context, Result};
use async_compression::tokio::bufread::BzDecoder;
use anyhow::{Context, Result};
use big_bytes::BigByte;
use futures::{StreamExt, TryStreamExt};
use reqwest::{header::CONTENT_LENGTH, Url};
@ -14,23 +13,29 @@ use tokio::{
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
process::{Child, Command},
};
use tokio_tar::Archive;
use tokio_util::{
codec::{BytesCodec, FramedRead},
io::StreamReader,
};
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
compile_error!("unsupported operating system");
#[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");
#[cfg(target_os = "windows")]
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-win-x64-v0.17.1.9.zip";
#[cfg(any(target_os = "macos", target_os = "linux"))]
const PACKED_FILE: &str = "monero-wallet-rpc";
#[cfg(target_os = "windows")]
const PACKED_FILE: &str = "monero-wallet-rpc.exe";
#[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("monero wallet rpc executable not found in downloaded archive")]
pub struct ExecutableNotFoundInArchive;
@ -63,8 +68,8 @@ impl 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.archive_path().exists() {
remove_file(monero_wallet_rpc.archive_path()).await?;
}
if !monero_wallet_rpc.exec_path().exists() {
@ -73,7 +78,7 @@ impl WalletRpc {
.read(true)
.write(true)
.create_new(true)
.open(monero_wallet_rpc.tar_path())
.open(monero_wallet_rpc.archive_path())
.await?;
let response = reqwest::get(DOWNLOAD_URL).await?;
@ -92,48 +97,28 @@ impl WalletRpc {
.bytes_stream()
.map_err(|err| std::io::Error::new(ErrorKind::Other, err));
#[cfg(not(target_os = "windows"))]
let mut stream = FramedRead::new(
BzDecoder::new(StreamReader::new(byte_stream)),
async_compression::tokio::bufread::BzDecoder::new(StreamReader::new(byte_stream)),
BytesCodec::new(),
)
.map_ok(|bytes| bytes.freeze());
#[cfg(target_os = "windows")]
let mut stream = FramedRead::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()?;
loop {
match entries.next().await {
Some(file) => {
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?;
break;
}
}
None => bail!(ExecutableNotFoundInArchive),
}
}
remove_file(monero_wallet_rpc.tar_path()).await?;
Self::extract_archive(&monero_wallet_rpc).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?
@ -178,11 +163,78 @@ impl WalletRpc {
})
}
fn tar_path(&self) -> PathBuf {
self.working_dir.join("monero-cli-wallet.tar")
fn archive_path(&self) -> PathBuf {
self.working_dir.join("monero-cli-wallet.archive")
}
fn exec_path(&self) -> PathBuf {
self.working_dir.join(PACKED_FILE)
}
#[cfg(not(target_os = "windows"))]
async fn extract_archive(monero_wallet_rpc: &Self) -> Result<()> {
use anyhow::bail;
use tokio_tar::Archive;
let mut options = OpenOptions::new();
let file = options
.read(true)
.open(monero_wallet_rpc.archive_path())
.await?;
let mut ar = Archive::new(file);
let mut entries = ar.entries()?;
loop {
match entries.next().await {
Some(file) => {
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?;
break;
}
}
None => bail!(ExecutableNotFoundInArchive),
}
}
remove_file(monero_wallet_rpc.archive_path()).await?;
Ok(())
}
#[cfg(target_os = "windows")]
async fn extract_archive(monero_wallet_rpc: &Self) -> Result<()> {
use std::fs::File;
use tokio::task::JoinHandle;
use zip::ZipArchive;
let archive_path = monero_wallet_rpc.archive_path();
let exec_path = monero_wallet_rpc.exec_path();
let extract: JoinHandle<Result<()>> = tokio::task::spawn_blocking(|| {
let file = File::open(archive_path)?;
let mut zip = ZipArchive::new(file)?;
let name = zip
.file_names()
.find(|name| name.contains(PACKED_FILE))
.context(ExecutableNotFoundInArchive)?
.to_string();
let mut rpc = zip.by_name(&name)?;
let mut file = File::create(exec_path)?;
std::io::copy(&mut rpc, &mut file)?;
Ok(())
});
extract.await??;
remove_file(monero_wallet_rpc.archive_path()).await?;
Ok(())
}
}