From 3a348003110c6eff33958cb7f821ed98c5b2f7a0 Mon Sep 17 00:00:00 2001 From: Philipp Hoenisch Date: Mon, 2 Nov 2020 16:00:35 +1100 Subject: [PATCH] Refactor into monero helper struct --- monero-harness/src/image.rs | 7 +- monero-harness/src/lib.rs | 179 ++++++++++++++++++++++++++------ monero-harness/tests/monerod.rs | 37 +------ monero-harness/tests/wallet.rs | 64 +++--------- 4 files changed, 170 insertions(+), 117 deletions(-) diff --git a/monero-harness/src/image.rs b/monero-harness/src/image.rs index 81b2333f..4d699fc9 100644 --- a/monero-harness/src/image.rs +++ b/monero-harness/src/image.rs @@ -104,8 +104,8 @@ impl Monero { self } - pub fn wallet(name: &str) -> Self { - let wallet = WalletArgs::new(name, WALLET_RPC_PORT); + pub fn wallet(name: &str, daemon_address: String) -> Self { + let wallet = WalletArgs::new(name, daemon_address, WALLET_RPC_PORT); let default = Monero::default(); Self { args: Args { @@ -252,8 +252,7 @@ impl MonerodArgs { } impl WalletArgs { - pub fn new(wallet_name: &str, rpc_port: u16) -> Self { - let daemon_address = format!("{}:{}", MONEROD_DAEMON_CONTAINER_NAME, MONEROD_RPC_PORT); + pub fn new(wallet_name: &str, daemon_address: String, rpc_port: u16) -> Self { WalletArgs { disable_rpc_login: true, confirm_external_bind: true, diff --git a/monero-harness/src/lib.rs b/monero-harness/src/lib.rs index a2f5d935..d9e5612b 100644 --- a/monero-harness/src/lib.rs +++ b/monero-harness/src/lib.rs @@ -36,7 +36,7 @@ use crate::{ }, rpc::{ monerod, - wallet::{self, Transfer}, + wallet::{self, GetAddress, Transfer}, }, }; @@ -48,13 +48,117 @@ const WAIT_WALLET_SYNC_MILLIS: u64 = 1000; #[derive(Clone, Debug)] pub struct Monero { - rpc_port: u16, - name: String, + monerod: Monerod, + wallets: Vec, + miner_address: String, +} +impl<'c> Monero { + /// Starts a new regtest monero container setup consisting out of 1 monerod + /// node and n wallets. The containers will be prefixed with + /// `container_prefix` if provided. There will be 1 miner wallet started + /// automatically. Default monerod container name will be: `monerod` + /// Default miner wallet container name will be: `miner` + /// Default network `monero` + pub async fn new( + cli: &'c Cli, + container_prefix: Option, + network_prefix: Option, + additional_wallets: Vec, + ) -> Result<(Self, Vec>)> { + let monerod_name = format!( + "{}{}", + container_prefix.unwrap_or_else(|| "".to_string()), + MONEROD_DAEMON_CONTAINER_NAME + ); + let network = format!( + "{}{}", + network_prefix.unwrap_or_else(|| "".to_string()), + MONEROD_DEFAULT_NETWORK + ); + + tracing::info!("Starting monerod..."); + let (monerod, monerod_container) = Monerod::new(cli, monerod_name, network)?; + let mut containers = vec![monerod_container]; + let mut wallets = vec![]; + + tracing::info!("Starting miner..."); + let (miner_wallet, miner_container) = MoneroWalletRpc::new(cli, "miner", &monerod).await?; + let miner_address = miner_wallet.address().await?.address; + + monerod.start_miner(&miner_address).await?; + + tracing::info!("Waiting for miner wallet to catch up..."); + let block_height = monerod.inner().get_block_count().await?; + miner_wallet + .wait_for_wallet_height(block_height) + .await + .unwrap(); + + wallets.push(miner_wallet); + containers.push(miner_container); + for wallet in additional_wallets.iter() { + tracing::info!("Starting wallet: {}...", wallet); + let (wallet, container) = MoneroWalletRpc::new(cli, wallet, &monerod).await?; + wallets.push(wallet); + containers.push(container); + } + + Ok(( + Self { + monerod, + wallets, + miner_address, + }, + containers, + )) + } + + pub fn monerod(&self) -> &Monerod { + &self.monerod + } + + pub fn wallet(&self, name: &str) -> Result<&MoneroWalletRpc> { + let wallet = self + .wallets + .iter() + .find(|wallet| wallet.name.eq(name)) + .ok_or_else(|| anyhow!("Could not find wallet container."))?; + + Ok(wallet) + } + + pub async fn fund(&self, address: &str, amount: u64) -> Result { + let transfer = self.wallet("miner")?.transfer(address, amount).await?; + + self.monerod + .inner() + .generate_blocks(10, &self.miner_address) + .await?; + Ok(transfer) + } } -impl<'c> Monero { +#[derive(Clone, Debug)] +pub struct Monerod { + rpc_port: u16, + name: String, + network: String, +} + +#[derive(Clone, Debug)] +pub struct MoneroWalletRpc { + rpc_port: u16, + name: String, + network: String, +} + +impl<'c> Monerod { /// Starts a new regtest monero container. - pub fn new_monerod(cli: &'c Cli) -> Result<(Self, Container<'c, Cli, image::Monero>)> { + fn new( + cli: &'c Cli, + name: String, + network: String, + ) -> Result<(Self, Container<'c, Cli, image::Monero>)> { let monerod_rpc_port: u16 = port_check::free_local_port().ok_or_else(|| anyhow!("Could not retrieve free port"))?; @@ -62,38 +166,58 @@ impl<'c> Monero { local: monerod_rpc_port, internal: MONEROD_RPC_PORT, }); - let run_args = RunArgs::default() - .with_name(MONEROD_DAEMON_CONTAINER_NAME) - .with_network(MONEROD_DEFAULT_NETWORK); + .with_name(name.clone()) + .with_network(network.clone()); let docker = cli.run_with_args(image, run_args); Ok(( Self { rpc_port: monerod_rpc_port, - name: "monerod".to_string(), + name, + network, }, docker, )) } + pub fn inner(&self) -> monerod::Client { + monerod::Client::localhost(self.rpc_port) + } + + /// Spawns a task to mine blocks in a regular interval to the provided + /// address + pub async fn start_miner(&self, miner_wallet_address: &str) -> Result<()> { + let monerod = self.inner(); + // generate the first 70 as bulk + let block = monerod.generate_blocks(70, &miner_wallet_address).await?; + println!("Generated {:?} blocks", block); + let _ = tokio::spawn(mine(monerod.clone(), miner_wallet_address.to_string())); + Ok(()) + } +} + +impl<'c> MoneroWalletRpc { /// Starts a new wallet container which is attached to /// MONEROD_DEFAULT_NETWORK and MONEROD_DAEMON_CONTAINER_NAME - pub async fn new_wallet( + async fn new( cli: &'c Cli, name: &str, + monerod: &Monerod, ) -> Result<(Self, Container<'c, Cli, image::Monero>)> { let wallet_rpc_port: u16 = port_check::free_local_port().ok_or_else(|| anyhow!("Could not retrieve free port"))?; - let image = image::Monero::wallet(&name).with_mapped_port(Port { + let daemon_address = format!("{}:{}", monerod.name, MONEROD_RPC_PORT); + let image = image::Monero::wallet(&name, daemon_address).with_mapped_port(Port { local: wallet_rpc_port, internal: WALLET_RPC_PORT, }); + let network = monerod.network.clone(); let run_args = RunArgs::default() .with_name(name) - .with_network(MONEROD_DEFAULT_NETWORK); + .with_network(network.clone()); let docker = cli.run_with_args(image, run_args); // create new wallet @@ -106,34 +230,20 @@ impl<'c> Monero { Self { rpc_port: wallet_rpc_port, name: name.to_string(), + network, }, docker, )) } - pub fn monerod_rpc_client(&self) -> monerod::Client { - monerod::Client::localhost(self.rpc_port) - } - - pub fn wallet_rpc_client(&self) -> wallet::Client { + pub fn inner(&self) -> wallet::Client { wallet::Client::localhost(self.rpc_port) } - /// Spawns a task to mine blocks in a regular interval to the provided - /// address - pub async fn start_miner(&self, miner_wallet_address: &str) -> Result<()> { - let monerod = self.monerod_rpc_client(); - // generate the first 70 as bulk - let block = monerod.generate_blocks(70, &miner_wallet_address).await?; - println!("Generated {:?} blocks", block); - let _ = tokio::spawn(mine(monerod.clone(), miner_wallet_address.to_string())); - Ok(()) - } - // It takes a little while for the wallet to sync with monerod. pub async fn wait_for_wallet_height(&self, height: u32) -> Result<()> { let mut retry: u8 = 0; - while self.wallet_rpc_client().block_height().await?.height < height { + while self.inner().block_height().await?.height < height { if retry >= 30 { // ~30 seconds bail!("Wallet could not catch up with monerod after 30 retries.") @@ -146,14 +256,21 @@ impl<'c> Monero { /// Sends amount to address pub async fn transfer(&self, address: &str, amount: u64) -> Result { - let miner_wallet = self.wallet_rpc_client(); + let miner_wallet = self.inner(); let transfer = miner_wallet.transfer(0, amount, address).await?; Ok(transfer) } -} + pub async fn address(&self) -> Result { + self.inner().get_address(0).await + } + + pub async fn balance(&self) -> Result { + self.inner().get_balance(0).await + } +} /// Mine a block ever BLOCK_TIME_SECS seconds. async fn mine(monerod: monerod::Client, reward_address: String) -> Result<()> { loop { diff --git a/monero-harness/tests/monerod.rs b/monero-harness/tests/monerod.rs index 5a0666a3..53a865e7 100644 --- a/monero-harness/tests/monerod.rs +++ b/monero-harness/tests/monerod.rs @@ -7,45 +7,18 @@ use tokio::time; #[tokio::test] async fn init_miner_and_mine_to_miner_address() { let tc = Cli::default(); - let (monerod, _monerod_container) = Monero::new_monerod(&tc).unwrap(); + let (monero, _monerod_container) = Monero::new(&tc, None, None, vec![]).await.unwrap(); - let (miner_wallet, _wallet_container) = Monero::new_wallet(&tc, "miner").await.unwrap(); + let monerod = monero.monerod(); + let miner_wallet = monero.wallet("miner").unwrap(); - let address = miner_wallet - .wallet_rpc_client() - .get_address(0) - .await - .unwrap() - .address; - - monerod.start_miner(&address).await.unwrap(); - - let block_height = monerod - .monerod_rpc_client() - .get_block_count() - .await - .unwrap(); - - miner_wallet - .wait_for_wallet_height(block_height) - .await - .unwrap(); - - let got_miner_balance = miner_wallet - .wallet_rpc_client() - .get_balance(0) - .await - .unwrap(); + let got_miner_balance = miner_wallet.balance().await.unwrap(); assert_that!(got_miner_balance).is_greater_than(0); time::delay_for(Duration::from_millis(1010)).await; // after a bit more than 1 sec another block should have been mined - let block_height = monerod - .monerod_rpc_client() - .get_block_count() - .await - .unwrap(); + let block_height = monerod.inner().get_block_count().await.unwrap(); assert_that(&block_height).is_greater_than(70); } diff --git a/monero-harness/tests/wallet.rs b/monero-harness/tests/wallet.rs index 64a69cad..a780f791 100644 --- a/monero-harness/tests/wallet.rs +++ b/monero-harness/tests/wallet.rs @@ -8,60 +8,24 @@ async fn fund_transfer_and_check_tx_key() { let fund_bob = 0; let tc = Cli::default(); - let (monerod, _monerod_container) = Monero::new_monerod(&tc).unwrap(); + let (monero, _containers) = Monero::new(&tc, Some("test".to_string()), None, vec![ + "alice".to_string(), + "bob".to_string(), + ]) + .await + .unwrap(); + let alice_wallet = monero.wallet("alice").unwrap(); + let bob_wallet = monero.wallet("bob").unwrap(); - let (miner_wallet, _wallet_container) = Monero::new_wallet(&tc, "miner").await.unwrap(); - let (alice_wallet, _alice_wallet_container) = Monero::new_wallet(&tc, "alice").await.unwrap(); - let (bob_wallet, _bob_wallet_container) = Monero::new_wallet(&tc, "bob").await.unwrap(); + let alice_address = alice_wallet.address().await.unwrap().address; - let address = miner_wallet - .wallet_rpc_client() - .get_address(0) - .await - .unwrap() - .address; + let transfer = monero.fund(&alice_address, fund_alice).await.unwrap(); - monerod.start_miner(&address).await.unwrap(); - - let block_height = monerod - .monerod_rpc_client() - .get_block_count() - .await - .unwrap(); - - miner_wallet - .wait_for_wallet_height(block_height) - .await - .unwrap(); - - let alice_address = alice_wallet - .wallet_rpc_client() - .get_address(0) - .await - .unwrap() - .address; - - let transfer = miner_wallet - .transfer(&alice_address, fund_alice) - .await - .unwrap(); - - monerod - .monerod_rpc_client() - .generate_blocks(10, &address) - .await - .unwrap(); - - let refreshed = alice_wallet.wallet_rpc_client().refresh().await.unwrap(); + let refreshed = alice_wallet.inner().refresh().await.unwrap(); assert_that(&refreshed.received_money).is_true(); - let got_alice_balance = alice_wallet - .wallet_rpc_client() - .get_balance(0) - .await - .unwrap(); - - let got_bob_balance = bob_wallet.wallet_rpc_client().get_balance(0).await.unwrap(); + let got_alice_balance = alice_wallet.balance().await.unwrap(); + let got_bob_balance = bob_wallet.balance().await.unwrap(); assert_that(&got_alice_balance).is_equal_to(fund_alice); assert_that(&got_bob_balance).is_equal_to(fund_bob); @@ -70,7 +34,7 @@ async fn fund_transfer_and_check_tx_key() { let tx_key = transfer.tx_key; let res = alice_wallet - .wallet_rpc_client() + .inner() .check_tx_key(&tx_id, &tx_key, &alice_address) .await .expect("failed to check tx by key");