Repository: farcaster-project/farcaster-node
Pull request #362 (2021-12-14 13:42:48)

Swap test funding

codecov-commenter: # [Codecov](https://codecov.io/gh/farcaster-project/farcaster-node/pull/362?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#362](https://codecov.io/gh/farcaster-project/farcaster-node/pull/362?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (a9011f0) into [main](https://codecov.io/gh/farcaster-project/farcaster-node/commit/78a72508207240b46df3c5ff7d2e4b26dedf12dd?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (78a7250) will **decrease** coverage by `0.1%`.
> The diff coverage is `0.9%`.

[![Impacted file tree graph](https://codecov.io/gh/farcaster-project/farcaster-node/pull/362/graphs/tree.svg?width=650&height=150&src=pr&token=ZZ9RnFhgFC&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)](https://codecov.io/gh/farcaster-project/farcaster-node/pull/362?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)

```diff
@@           Coverage Diff           @@
##            main    #362     +/-   ##
=======================================
- Coverage   13.8%   13.7%   -0.1%     
=======================================
  Files         27      27             
  Lines       7392    7457     +65     
=======================================
+ Hits        1020    1021      +1     
- Misses      6372    6436     +64     
```


| [Impacted Files](https://codecov.io/gh/farcaster-project/farcaster-node/pull/362?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [src/error.rs](https://codecov.io/gh/farcaster-project/farcaster-node/pull/362/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-c3JjL2Vycm9yLnJz) | `0.0% <0.0%> (ø)` | |
| [src/farcasterd/runtime.rs](https://codecov.io/gh/farcaster-project/farcaster-node/pull/362/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-c3JjL2ZhcmNhc3RlcmQvcnVudGltZS5ycw==) | `0.0% <0.0%> (ø)` | |
| [src/swapd/runtime.rs](https://codecov.io/gh/farcaster-project/farcaster-node/pull/362/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-c3JjL3N3YXBkL3J1bnRpbWUucnM=) | `0.0% <0.0%> (ø)` | |
| [src/rpc/request.rs](https://codecov.io/gh/farcaster-project/farcaster-node/pull/362/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-c3JjL3JwYy9yZXF1ZXN0LnJz) | `14.9% <3.8%> (-0.6%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/farcaster-project/farcaster-node/pull/362?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/farcaster-project/farcaster-node/pull/362?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None). Last update [78a7250...a9011f0](https://codecov.io/gh/farcaster-project/farcaster-node/pull/362?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).

TheCharlatan: No, definitely not related to that, only the parallel swaps are failing. I think the block generation is still not isolated enough between single swaps, so the swaps may enter states that are not expected yet. I moved the mutex now to act on the entire swap. Lets see if that improves it. I also clarified the `parallel` swap name a bit, since the swaps don't execute in parallel yet, it just makes and takes in parallel. I'm going to work on truly parallel swap tests soon.

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index f2967103..58c33edf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -973,6 +973,7 @@ dependencies = [
  "paste",
  "rand 0.8.4",
  "regex",
+ "rustc-hex",
  "serde 1.0.127",
  "serde_json",
  "serde_with",
diff --git a/Cargo.toml b/Cargo.toml
index 8ce0b318..186626f8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -88,6 +88,7 @@ slip132 = "0.3"
 strict_encoding = "1.7.4"
 strict_encoding_derive = "1.7.4"
 sysinfo = { version = "0.18.2" }
+rustc-hex = "2.1.0"
 # Async
 tokio = { version = "1.12.0", features = ["full"] }
 toml = { version = "0.5", optional = true }
diff --git a/src/error.rs b/src/error.rs
index 9360bcd9..bf904069 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -174,12 +174,36 @@ impl From<monero::util::address::Error> for Error {
     }
 }
 
+impl From<bitcoin::util::address::Error> for Error {
+    fn from(err: bitcoin::util::address::Error) -> Self {
+        Error::Farcaster(err.to_string())
+    }
+}
+
+impl From<monero::util::amount::ParsingError> for Error {
+    fn from(err: monero::util::amount::ParsingError) -> Self {
+        Error::Farcaster(err.to_string())
+    }
+}
+
+impl From<bitcoin::util::amount::ParseAmountError> for Error {
+    fn from(err: bitcoin::util::amount::ParseAmountError) -> Self {
+        Error::Farcaster(err.to_string())
+    }
+}
+
 impl From<electrum_client::Error> for Error {
     fn from(err: electrum_client::Error) -> Self {
         Error::Syncer(SyncerError::Electrum(err))
     }
 }
 
+impl From<rustc_hex::FromHexError> for Error {
+    fn from(err: rustc_hex::FromHexError) -> Self {
+        Error::Farcaster(err.to_string())
+    }
+}
+
 impl From<bitcoin::hashes::Error> for Error {
     fn from(err: bitcoin::hashes::Error) -> Self {
         Error::BitcoinHashes(err)
diff --git a/src/farcasterd/runtime.rs b/src/farcasterd/runtime.rs
index 9f1a85fa..a76ba370 100644
--- a/src/farcasterd/runtime.rs
+++ b/src/farcasterd/runtime.rs
@@ -1223,8 +1223,15 @@ impl Runtime {
                     .iter()
                     .filter(|(_, (_, _, autofund))| !*autofund)
                     .enumerate()
-                    .map(|(i, (swapid, (addr, amount, _)))| {
-                        let mut res = format!("{:#?} needs {} to {}", swapid, amount, addr);
+                    .map(|(i, (swap_id, (address, amount, _)))| {
+                        let mut res = format!(
+                            "{}",
+                            MoneroFundingInfo {
+                                swap_id: *swap_id,
+                                amount: *amount,
+                                address: *address,
+                            }
+                        );
                         if i < len - 1 {
                             res.push('\n');
                         }
@@ -1245,8 +1252,15 @@ impl Runtime {
                     .iter()
                     .filter(|(_, (_, _, autofund))| !*autofund)
                     .enumerate()
-                    .map(|(i, (swapid, (addr, amount, _)))| {
-                        let mut res = format!("{:#?} needs {} to {}", swapid, amount, addr);
+                    .map(|(i, (swap_id, (address, amount, _)))| {
+                        let mut res = format!(
+                            "{}",
+                            BitcoinFundingInfo {
+                                swap_id: *swap_id,
+                                amount: *amount,
+                                address: address.clone(),
+                            }
+                        );
                         if i < len - 1 {
                             res.push('\n');
                         }
diff --git a/src/rpc/request.rs b/src/rpc/request.rs
index ca98c468..670cdde6 100644
--- a/src/rpc/request.rs
+++ b/src/rpc/request.rs
@@ -598,8 +598,30 @@ pub enum FundingInfo {
     Monero(MoneroFundingInfo),
 }
 
-#[derive(Clone, Debug, Display, StrictDecode, StrictEncode)]
-#[display("bitcoin_funding_info")]
+impl FromStr for BitcoinFundingInfo {
+    type Err = Error;
+    fn from_str(s: &str) -> Result<Self, Error> {
+        let content: Vec<&str> = s.split(" ").collect();
+
+        Ok(BitcoinFundingInfo {
+            swap_id: SwapId::from_str(content[0])?,
+            amount: bitcoin::Amount::from_str(&format!("{} {}", content[2], content[3]))?,
+            address: bitcoin::Address::from_str(content[5])?,
+        })
+    }
+}
+
+impl fmt::Display for BitcoinFundingInfo {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(
+            f,
+            "{:#?} needs {} to {}",
+            self.swap_id, self.amount, self.address
+        )
+    }
+}
+
+#[derive(Clone, Debug, StrictDecode, StrictEncode)]
 pub struct BitcoinFundingInfo {
     pub swap_id: SwapId,
     pub address: bitcoin::Address,
@@ -642,8 +664,33 @@ impl StrictDecode for MoneroFundingInfo {
     }
 }
 
-#[derive(Clone, Debug, Display)]
-#[display("monero_funding_info")]
+impl FromStr for MoneroFundingInfo {
+    type Err = Error;
+    fn from_str(s: &str) -> Result<Self, Error> {
+        let content: Vec<&str> = s.split(" ").collect();
+        Ok(MoneroFundingInfo {
+            swap_id: SwapId::from_str(content[0])?,
+            amount: monero::Amount::from_str_with_denomination(&format!(
+                "{} {}",
+                content[2], content[3]
+            ))?,
+
+            address: monero::Address::from_str(content[5])?,
+        })
+    }
+}
+
+impl fmt::Display for MoneroFundingInfo {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(
+            f,
+            "{:#?} needs {} to {}",
+            self.swap_id, self.amount, self.address
+        )
+    }
+}
+
+#[derive(Clone, Debug)]
 pub struct MoneroFundingInfo {
     pub swap_id: SwapId,
     pub amount: monero::Amount,


Test patch:
diff --git a/tests/functional-swap.rs b/tests/functional-swap.rs
index b082cf3c..0b841f1a 100644
--- a/tests/functional-swap.rs
+++ b/tests/functional-swap.rs
@@ -2,6 +2,9 @@
 extern crate log;
 
 use bitcoincore_rpc::{Auth, Client, RpcApi};
+use farcaster_core::swap::SwapId;
+use farcaster_node::rpc::request::BitcoinFundingInfo;
+use farcaster_node::rpc::request::MoneroFundingInfo;
 use farcaster_node::rpc::request::NodeInfo;
 use futures::future::join_all;
 use std::collections::HashSet;
@@ -40,8 +43,8 @@ async fn swap_bob_maker_normal() {
         "Bob".to_string(),
         Arc::clone(&bitcoin_rpc),
         Arc::clone(&monero_wallet),
-        "1 BTC".to_string(),
-        "1 XMR".to_string(),
+        bitcoin::Amount::from_str("1 BTC").unwrap(),
+        monero::Amount::from_str_with_denomination("1 XMR").unwrap(),
     )
     .await;
 
@@ -78,8 +81,8 @@ async fn swap_bob_maker_refund_race_cancel() {
         "Bob".to_string(),
         Arc::clone(&bitcoin_rpc),
         Arc::clone(&monero_wallet),
-        "1 BTC".to_string(),
-        "1 XMR".to_string(),
+        bitcoin::Amount::from_str("1 BTC").unwrap(),
+        monero::Amount::from_str_with_denomination("1 XMR").unwrap(),
     )
     .await;
 
@@ -116,8 +119,8 @@ async fn swap_bob_maker_refund_kill_alice_after_funding() {
         "Bob".to_string(),
         Arc::clone(&bitcoin_rpc),
         Arc::clone(&monero_wallet),
-        "1 BTC".to_string(),
-        "1 XMR".to_string(),
+        bitcoin::Amount::from_str("1 BTC").unwrap(),
+        monero::Amount::from_str_with_denomination("1 XMR").unwrap(),
     )
     .await;
 
@@ -153,8 +156,8 @@ async fn swap_bob_maker_refund_alice_does_not_fund() {
         "Bob".to_string(),
         Arc::clone(&bitcoin_rpc),
         Arc::clone(&monero_wallet),
-        "1 BTC".to_string(),
-        "1 XMR".to_string(),
+        bitcoin::Amount::from_str("1 BTC").unwrap(),
+        monero::Amount::from_str_with_denomination("1 XMR").unwrap(),
     )
     .await;
 
@@ -188,8 +191,8 @@ async fn swap_bob_maker_punish_kill_bob() {
         "Bob".to_string(),
         Arc::clone(&bitcoin_rpc),
         Arc::clone(&monero_wallet),
-        "1 BTC".to_string(),
-        "1 XMR".to_string(),
+        bitcoin::Amount::from_str("1 BTC").unwrap(),
+        monero::Amount::from_str_with_denomination("1 XMR").unwrap(),
     )
     .await;
 
@@ -226,8 +229,8 @@ async fn swap_alice_maker() {
         "Alice".to_string(),
         Arc::clone(&bitcoin_rpc),
         Arc::clone(&monero_wallet),
-        "1 BTC".to_string(),
-        "1 XMR".to_string(),
+        bitcoin::Amount::from_str("1 BTC").unwrap(),
+        monero::Amount::from_str_with_denomination("1 XMR").unwrap(),
     )
     .await;
 
@@ -250,7 +253,7 @@ async fn swap_alice_maker() {
 #[tokio::test]
 #[timeout(600000)]
 #[ignore]
-async fn swap_parallel() {
+async fn swap_parallel_offer_creation_serial_execution() {
     let execution_mutex = Arc::new(Mutex::new(0));
     let bitcoin_rpc = Arc::new(bitcoin_setup());
     let (monero_regtest, monero_wallet) = monero_setup().await;
@@ -264,8 +267,8 @@ async fn swap_parallel() {
         "Alice".to_string(),
         Arc::clone(&bitcoin_rpc),
         Arc::clone(&monero_wallet),
-        "1 BTC".to_string(),
-        "1 XMR".to_string(),
+        bitcoin::Amount::from_str("1 BTC").unwrap(),
+        monero::Amount::from_str_with_denomination("1 XMR").unwrap(),
     )
     .await;
 
@@ -287,8 +290,8 @@ async fn swap_parallel() {
         "Alice".to_string(),
         Arc::clone(&bitcoin_rpc),
         Arc::clone(&monero_wallet),
-        "1 BTC".to_string(),
-        "0.999999999999 XMR".to_string(),
+        bitcoin::Amount::from_str("1 BTC").unwrap(),
+        monero::Amount::from_str_with_denomination("0.999999999999 XMR").unwrap(),
     )
     .await;
 
@@ -310,8 +313,8 @@ async fn swap_parallel() {
         "Bob".to_string(),
         Arc::clone(&bitcoin_rpc),
         Arc::clone(&monero_wallet),
-        "1 BTC".to_string(),
-        "1 XMR".to_string(),
+        bitcoin::Amount::from_str("1 BTC").unwrap(),
+        monero::Amount::from_str_with_denomination("1 XMR").unwrap(),
     )
     .await;
 
@@ -333,8 +336,8 @@ async fn swap_parallel() {
         "Bob".to_string(),
         Arc::clone(&bitcoin_rpc),
         Arc::clone(&monero_wallet),
-        "1 BTC".to_string(),
-        "0.999999999999 XMR".to_string(),
+        bitcoin::Amount::from_str("1 BTC").unwrap(),
+        monero::Amount::from_str_with_denomination("0.999999999999 XMR").unwrap(),
     )
     .await;
 
@@ -387,7 +390,7 @@ async fn setup_farcaster_clients() -> (process::Child, Vec<String>, process::Chi
 
 #[allow(clippy::too_many_arguments)]
 async fn run_refund_swap_race_cancel(
-    swap_id: String,
+    swap_id: SwapId,
     data_dir_alice: Vec<String>,
     data_dir_bob: Vec<String>,
     bitcoin_rpc: Arc<bitcoincore_rpc::Client>,
@@ -410,13 +413,12 @@ async fn run_refund_swap_race_cancel(
         .unwrap();
 
     // run until bob has the btc funding address
-    let address =
+    let (address, amount) =
         retry_until_bitcoin_funding_address(swap_id.clone(), cli_bob_needs_funding_args.clone())
             .await;
 
     // fund the bitcoin address
     let lock = execution_mutex.lock().await;
-    let amount = bitcoin::Amount::ONE_SAT * 100000150;
     bitcoin_rpc
         .send_to_address(&address, amount, None, None, None, None, None, None)
         .unwrap();
@@ -436,9 +438,9 @@ async fn run_refund_swap_race_cancel(
         .unwrap();
 
     // run until the alice has the monero funding address and fund it
-    let monero_address =
+    let (monero_address, monero_amount) =
         retry_until_monero_funding_address(swap_id, cli_alice_needs_funding_args.clone()).await;
-    send_monero(Arc::clone(&monero_wallet), monero_address, 1000000000000).await;
+    send_monero(Arc::clone(&monero_wallet), monero_address, monero_amount).await;
 
     tokio::time::sleep(time::Duration::from_secs(20)).await;
 
@@ -533,7 +535,7 @@ async fn run_refund_swap_race_cancel(
 
 #[allow(clippy::too_many_arguments)]
 async fn run_refund_swap_kill_alice_after_funding(
-    swap_id: String,
+    swap_id: SwapId,
     data_dir_alice: Vec<String>,
     data_dir_bob: Vec<String>,
     bitcoin_rpc: Arc<bitcoincore_rpc::Client>,
@@ -553,13 +555,12 @@ async fn run_refund_swap_kill_alice_after_funding(
         .unwrap();
 
     // run until bob has the btc funding address
-    let address =
+    let (address, amount) =
         retry_until_bitcoin_funding_address(swap_id.clone(), cli_bob_needs_funding_args.clone())
             .await;
 
     // fund the bitcoin address
     let lock = execution_mutex.lock().await;
-    let amount = bitcoin::Amount::ONE_SAT * 100000150;
     bitcoin_rpc
         .send_to_address(&address, amount, None, None, None, None, None, None)
         .unwrap();
@@ -579,9 +580,9 @@ async fn run_refund_swap_kill_alice_after_funding(
         .unwrap();
 
     // run until the alice has the monero funding address and fund it
-    let monero_address =
+    let (monero_address, monero_amount) =
         retry_until_monero_funding_address(swap_id, cli_alice_needs_funding_args.clone()).await;
-    send_monero(Arc::clone(&monero_wallet), monero_address, 1000000000000).await;
+    send_monero(Arc::clone(&monero_wallet), monero_address, monero_amount).await;
 
     // kill alice
     cleanup_processes(vec![alice_farcasterd]);
@@ -635,7 +636,7 @@ async fn run_refund_swap_kill_alice_after_funding(
 
 #[allow(clippy::too_many_arguments)]
 async fn run_refund_swap_alice_does_not_fund(
-    swap_id: String,
+    swap_id: SwapId,
     data_dir_alice: Vec<String>,
     data_dir_bob: Vec<String>,
     bitcoin_rpc: Arc<bitcoincore_rpc::Client>,
@@ -655,13 +656,12 @@ async fn run_refund_swap_alice_does_not_fund(
         .unwrap();
 
     // run until bob has the btc funding address
-    let address =
+    let (address, amount) =
         retry_until_bitcoin_funding_address(swap_id.clone(), cli_bob_needs_funding_args.clone())
             .await;
 
     // fund the bitcoin address
     let lock = execution_mutex.lock().await;
-    let amount = bitcoin::Amount::ONE_SAT * 100000150;
     bitcoin_rpc
         .send_to_address(&address, amount, None, None, None, None, None, None)
         .unwrap();
@@ -735,7 +735,7 @@ async fn run_refund_swap_alice_does_not_fund(
 
 #[allow(clippy::too_many_arguments)]
 async fn run_punish_swap_kill_bob_before_monero_funding(
-    swap_id: String,
+    swap_id: SwapId,
     data_dir_alice: Vec<String>,
     data_dir_bob: Vec<String>,
     bitcoin_rpc: Arc<bitcoincore_rpc::Client>,
@@ -758,13 +758,12 @@ async fn run_punish_swap_kill_bob_before_monero_funding(
         .unwrap();
 
     // run until bob has the btc funding address
-    let address =
+    let (address, amount) =
         retry_until_bitcoin_funding_address(swap_id.clone(), cli_bob_needs_funding_args.clone())
             .await;
 
     // fund the bitcoin address
     let lock = execution_mutex.lock().await;
-    let amount = bitcoin::Amount::ONE_SAT * 100000150;
     bitcoin_rpc
         .send_to_address(&address, amount, None, None, None, None, None, None)
         .unwrap();
@@ -793,9 +792,9 @@ async fn run_punish_swap_kill_bob_before_monero_funding(
     cleanup_processes(vec![bob_farcasterd]);
 
     // run until alice has the monero funding address
-    let monero_address =
+    let (monero_address, monero_amount) =
         retry_until_monero_funding_address(swap_id, cli_alice_needs_funding_args.clone()).await;
-    send_monero(Arc::clone(&monero_wallet), monero_address, 1000000000000).await;
+    send_monero(Arc::clone(&monero_wallet), monero_address, monero_amount).await;
 
     tokio::time::sleep(time::Duration::from_secs(20)).await;
 
@@ -871,9 +870,9 @@ async fn make_and_take_offer(
     role: String,
     bitcoin_rpc: Arc<bitcoincore_rpc::Client>,
     monero_wallet: Arc<Mutex<monero_rpc::WalletClient>>,
-    btc_amount: String,
-    xmr_amount: String,
-) -> (String, bitcoin::Address, String) {
+    btc_amount: bitcoin::Amount,
+    xmr_amount: monero::Amount,
+) -> (String, bitcoin::Address, SwapId) {
     let maker_info_args = info_args(data_dir_maker.clone());
     let taker_info_args = info_args(data_dir_maker.clone());
 
@@ -887,11 +886,8 @@ async fn make_and_take_offer(
     let xmr_addr = xmr_address.to_string();
 
     let (stdout, _stderr) = run("../swap-cli", taker_info_args.clone()).unwrap();
-    let previous_swap_ids: HashSet<String> = cli_output_to_node_info(stdout)
-        .swaps
-        .iter()
-        .map(|swap_id| format!("{:#x}", swap_id))
-        .collect();
+    let previous_swap_ids: HashSet<SwapId> =
+        cli_output_to_node_info(stdout).swaps.drain(..).collect();
 
     let cli_make_args = make_offer_args(
         data_dir_maker.clone(),
@@ -921,7 +917,7 @@ async fn make_and_take_offer(
 
 #[allow(clippy::too_many_arguments)]
 async fn run_swap(
-    swap_id: String,
+    swap_id: SwapId,
     data_dir_alice: Vec<String>,
     data_dir_bob: Vec<String>,
     bitcoin_rpc: Arc<bitcoincore_rpc::Client>,
@@ -946,12 +942,11 @@ async fn run_swap(
     let lock = execution_mutex.lock().await;
 
     // run until bob has the btc funding address
-    let address =
+    let (address, amount) =
         retry_until_bitcoin_funding_address(swap_id.clone(), cli_bob_needs_funding_args.clone())
             .await;
 
     // fund the bitcoin address
-    let amount = bitcoin::Amount::ONE_SAT * 100000150;
     bitcoin_rpc
         .send_to_address(&address, amount, None, None, None, None, None, None)
         .unwrap();
@@ -971,9 +966,9 @@ async fn run_swap(
         .unwrap();
 
     // run until the alice has the monero funding address
-    let monero_address =
+    let (monero_address, monero_amount) =
         retry_until_monero_funding_address(swap_id, cli_alice_needs_funding_args.clone()).await;
-    send_monero(Arc::clone(&monero_wallet), monero_address, 1000000000000).await;
+    send_monero(Arc::clone(&monero_wallet), monero_address, monero_amount).await;
 
     tokio::time::sleep(time::Duration::from_secs(10)).await;
 
@@ -1009,7 +1004,6 @@ async fn run_swap(
         .get_received_by_address(&funding_btc_address, None)
         .unwrap();
     assert!(balance.as_sat() > 90000000);
-    drop(lock);
 
     // cache the monero balance before sweeping
     let monero_wallet_lock = monero_wallet.lock().await;
@@ -1053,6 +1047,7 @@ async fn run_swap(
     monero_wallet_lock.refresh(Some(1)).await.unwrap();
     let after_balance = monero_wallet_lock.get_balance(0, None).await.unwrap();
     drop(monero_wallet_lock);
+    drop(lock);
     let delta_balance = after_balance.balance - before_balance.balance;
     assert!(delta_balance > 999660000000);
 }
@@ -1166,9 +1161,9 @@ fn make_offer_args(
     data_dir: Vec<String>,
     role: String,
     btc_addr: String,
-    btc_amount: String,
+    btc_amount: bitcoin::Amount,
     xmr_addr: String,
-    xmr_amount: String,
+    xmr_amount: monero::Amount,
 ) -> Vec<String> {
     data_dir
         .into_iter()
@@ -1185,9 +1180,9 @@ fn make_offer_args(
             "--acc-blockchain".to_string(),
             "Monero".to_string(),
             "--btc-amount".to_string(),
-            btc_amount,
+            format!("{}", btc_amount),
             "--xmr-amount".to_string(),
-            xmr_amount,
+            format!("{}", xmr_amount),
             "--maker-role".to_string(),
             role,
             "--cancel-timelock".to_string(),
@@ -1227,10 +1222,10 @@ fn take_offer_args(
         .collect()
 }
 
-fn progress_args(data_dir: Vec<String>, swap_id: String) -> Vec<String> {
+fn progress_args(data_dir: Vec<String>, swap_id: SwapId) -> Vec<String> {
     data_dir
         .into_iter()
-        .chain(vec!["progress".to_string(), swap_id])
+        .chain(vec!["progress".to_string(), format!("{:#?}", swap_id)])
         .collect()
 }
 
@@ -1267,16 +1262,13 @@ async fn retry_until_offer(args: Vec<String>) -> Vec<String> {
     panic!("timeout before any offer could be retrieved");
 }
 
-async fn retry_until_swap_id(args: Vec<String>, previous_swap_ids: HashSet<String>) -> String {
+async fn retry_until_swap_id(args: Vec<String>, previous_swap_ids: HashSet<SwapId>) -> SwapId {
     for _ in 0..ALLOWED_RETRIES {
         let (stdout, _stderr) = run("../swap-cli", args.clone()).unwrap();
-        let new_swap_ids: HashSet<String> = cli_output_to_node_info(stdout)
-            .swaps
-            .iter()
-            .map(|swap_id| format!("{:#x}", swap_id))
-            .collect();
+        let new_swap_ids: HashSet<SwapId> =
+            cli_output_to_node_info(stdout).swaps.drain(..).collect();
         if let Some(swap_id) = new_swap_ids.difference(&previous_swap_ids).next() {
-            return swap_id.to_string();
+            return swap_id.clone();
         }
         tokio::time::sleep(time::Duration::from_secs(1)).await;
     }
@@ -1284,58 +1276,61 @@ async fn retry_until_swap_id(args: Vec<String>, previous_swap_ids: HashSet<Strin
 }
 
 async fn retry_until_bitcoin_funding_address(
-    swap_id: String,
+    swap_id: SwapId,
     args: Vec<String>,
-) -> bitcoin::Address {
+) -> (bitcoin::Address, bitcoin::Amount) {
     for _ in 0..ALLOWED_RETRIES {
         let (stdout, _stderr) = run("../swap-cli", args.clone()).unwrap();
 
         // get the btc funding address
-        let bitcoin_address: Vec<String> = stdout
+        let funding_infos: Vec<BitcoinFundingInfo> = stdout
             .iter()
             .filter_map(|element| {
-                let content: Vec<&str> = element.split(" ").collect();
-                if content.len() < 5 {
-                    return None;
-                }
-                if content[5].contains("bcr") && content[0] == &swap_id {
-                    Some(content[5].to_string())
+                if let Ok(funding_info) = BitcoinFundingInfo::from_str(element) {
+                    if funding_info.swap_id == swap_id {
+                        Some(funding_info)
+                    } else {
+                        None
+                    }
                 } else {
                     None
                 }
             })
             .collect();
 
-        if !bitcoin_address.is_empty() {
-            return bitcoin::Address::from_str(&bitcoin_address[0]).unwrap();
+        if !funding_infos.is_empty() {
+            return (funding_infos[0].address.clone(), funding_infos[0].amount);
         }
         tokio::time::sleep(time::Duration::from_secs(1)).await;
     }
     panic!("timeout before any bitcoin funding address could be retrieved");
 }
 
-async fn retry_until_monero_funding_address(swap_id: String, args: Vec<String>) -> monero::Address {
+async fn retry_until_monero_funding_address(
+    swap_id: SwapId,
+    args: Vec<String>,
+) -> (monero::Address, monero::Amount) {
     for _ in 0..ALLOWED_RETRIES {
         let (stdout, _stderr) = run("../swap-cli", args.clone()).unwrap();
 
         // get the monero funding address
-        let monero_addresses: Vec<String> = stdout
+        let funding_infos: Vec<MoneroFundingInfo> = stdout
             .iter()
             .filter_map(|element| {
-                let content: Vec<&str> = element.split(" ").collect();
-                if content.len() < 5 {
-                    return None;
-                }
-                if content[5].contains("4") && content[0] == &swap_id {
-                    Some(content[5].to_string())
+                if let Ok(funding_info) = MoneroFundingInfo::from_str(element) {
+                    if funding_info.swap_id == swap_id {
+                        Some(funding_info)
+                    } else {
+                        None
+                    }
                 } else {
                     None
                 }
             })
             .collect();
 
-        if !monero_addresses.is_empty() {
-            return monero::Address::from_str(&monero_addresses[0]).unwrap();
+        if !funding_infos.is_empty() {
+            return (funding_infos[0].address.clone(), funding_infos[0].amount);
         }
         tokio::time::sleep(time::Duration::from_secs(1)).await;
     }
@@ -1566,10 +1561,10 @@ pub fn launch(name: &str, args: impl IntoIterator<Item = String>) -> io::Result<
 async fn send_monero(
     wallet: Arc<Mutex<monero_rpc::WalletClient>>,
     address: monero::Address,
-    amount: u64,
+    amount: monero::Amount,
 ) -> Vec<u8> {
     let mut destination: HashMap<monero::Address, u64> = HashMap::new();
-    destination.insert(address, amount);
+    destination.insert(address, amount.as_pico());
     let options = monero_rpc::TransferOptions {
         account_index: None,
         subaddr_indices: None,