@ -1,6 +1,5 @@
use ::monero ::Network ;
use anyhow ::{ Context , Result } ;
use async_compression ::tokio ::bufread ::BzDecoder ;
use big_bytes ::BigByte ;
use futures ::{ StreamExt , TryStreamExt } ;
use reqwest ::{ header ::CONTENT_LENGTH , Url } ;
@ -14,23 +13,33 @@ 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 ;
pub struct WalletRpcProcess {
_child : Child ,
port : u16 ,
@ -59,8 +68,8 @@ impl WalletRpc {
working_dir : working_dir . to_path_buf ( ) ,
} ;
if monero_wallet_rpc . t ar_path( ) . exists ( ) {
remove_file ( monero_wallet_rpc . t ar_path( ) ) . await ? ;
if monero_wallet_rpc . archive _path( ) . exists ( ) {
remove_file ( monero_wallet_rpc . archive _path( ) ) . await ? ;
}
if ! monero_wallet_rpc . exec_path ( ) . exists ( ) {
@ -69,7 +78,7 @@ impl WalletRpc {
. read ( true )
. write ( true )
. create_new ( true )
. open ( monero_wallet_rpc . t ar_path( ) )
. open ( monero_wallet_rpc . archive _path( ) )
. await ? ;
let response = reqwest ::get ( DOWNLOAD_URL ) . await ? ;
@ -88,43 +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 ( ) ? ;
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 ? ;
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 ?
@ -169,11 +163,78 @@ impl WalletRpc {
} )
}
fn t ar_path( & self ) -> PathBuf {
self . working_dir . join ( "monero-cli-wallet. t ar")
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 ( ( ) )
}
}