Repository: farcaster-project/farcaster-node
Pull request #173 (2021-11-09 17:16:30)

Individual tests atomic
Based on #172.

- Split tests atomically, no big wrapper functions
- Remove no-capture, if test fail stdout is printed anyway, cleaner logs for passed tests
- Run all tests (bitcoin and monero ones) in sequence within 1 unique thread


Patch:
diff --git a/.gitignore b/.gitignore
index 8399fe74..db369b91 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,8 +11,3 @@
 
 # Shell utilities
 /shell
-
-# Functional tests: generated folders
-/tests/data_dir
-/tests/.farcaster_node_0
-/tests/.farcaster_node_1
diff --git a/Cargo.lock b/Cargo.lock
index 27a09903..1ca05ef7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -948,6 +948,7 @@ dependencies = [
  "monero",
  "monero-rpc",
  "nix",
+ "paste",
  "regex",
  "serde 1.0.127",
  "serde_json",
diff --git a/Cargo.toml b/Cargo.toml
index cc9a77e5..9349f538 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -90,6 +90,8 @@ tokio = { version = "1.12.0", features = ["full"] }
 toml = { version = "0.5", optional = true }
 # IPC
 zmq = "0.9"
+# Misc
+paste = "1.0"
 
 [build-dependencies]
 farcaster_core = { version = "0.3.0", features = ["serde"] }
diff --git a/README.md b/README.md
index 9fe1916f..07354ac2 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
 
 # Farcaster: cross-chain atomic swaps
 
-:warning: **THIS IS UNFINISHED, EXPERIMENTAL TECH AND YOU WILL LOSE YOUR MONEY IF YOU TRY IT ON MAINNET**
+:warning: **THIS IS UNFINISHED, EXPERIMENTAL TECH AND YOU WILL LOSE YOUR MONEY IF YOU TRY IT ON MAINNET** :warning:
 
 This project is called the **Farcaster Node**, it is _a collection of microservices for running cross-chain atomic swaps_. Currently the node is focused on Bitcoin-Monero atomic swaps, but is designed to be flexible and integrate new crypto-pairs in the future.
 
@@ -16,11 +16,9 @@ Farcaster Node is build on atomic swap primitives described in the [RFCs](https:
 
 ## Build and run
 
-The following section explain how to build and run Farcaster node locally or within containers through Docker and Docker compopse.
+### Local
 
-### Locally
-
-To compile the node, please install [cargo](https://doc.rust-lang.org/cargo/) (the Rust _package manager_), then run the following commands:
+To compile the node, please install [cargo](https://doc.rust-lang.org/cargo/), then run the following commands:
 
 ```bash
 sudo apt install -y libsqlite3-dev libssl-dev libzmq3-dev pkg-config build-essential cmake
@@ -43,7 +41,7 @@ monero-wallet-rpc --stagenet --rpc-bind-port 18083\
     --wallet-dir ~/.fc_monero_wallets
 ```
 
-Then start the node with:
+Then start the node with the following command:
 
 ```
 farcasterd -vv\
@@ -52,7 +50,11 @@ farcasterd -vv\
     --monero-rpc-wallet http://{ip:port}
 ```
 
-#### Connect a client
+### With docker
+
+You can follow the documentation on [how to use `farcasterd` with Docker](./doc/docker-stack.md).
+
+### Connect a client
 
 Once `farcasterd` is up & running you can issue commands to control its actions with a client. For the time being, only one client is provided within this repo: `swap-cli`.
 
@@ -64,9 +66,27 @@ swap-cli info
 
 Run `help` command for more details about available commands.
 
-### With docker
+### Remote client usage
+
+Example from using other URLs supported by crate `internet2` `node_addr.rs`, besides the default inter process communication (also used by the Docker image to expose the control bus).
+
+The daemon is controlled though ZMQ _ctl_ socket, an internal interface for control PRC protocol communications. Another ZMQ socket is used to forward all incoming protocol messages, the _msg_ socket. Both are node internal communication channels. Message from counterparty come through `peerd` services.
+
+**Farcasterd**
 
-You can follow the documentation on [how to use `farcasterd` with Docker](./doc/docker-stack.md) and [how to connect a remote client to `farcasterd`](./doc/docker-stack.md#connect-a-client).
+To launch `farcasterd` with network binded _control_ (`-x`) bus and _message_ bus (`-m`) instead of `ctl.rpc` and `msg.rpc` files:
+
+```
+farcasterd -vv -x "lnpz://127.0.0.1:9981/?api=esb" -m "lnpz://127.0.0.1:9982/?api=esb"
+```
+
+**Client**
+
+The following client can instruct the above `farcasterd` to return general informations as follows:
+
+```
+swap-cli -x "lnpz://127.0.0.1:9981/?api=esb" -m "lnpz://127.0.0.1:9982/?api=esb" info
+```
 
 ## Usage
 
diff --git a/doc/README.md b/doc/README.md
index a0e58075..2cd78c9c 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -1,5 +1,5 @@
 # Farcaster Node Documentation
 
-- [`test local swap`](./local-swap.md) describe how to run a swap with both participant locally.
-- [`docker stack`](./docker-stack.md) documents how to use the node with docker and docker compose.
-- [`sequence diagram`](./sequencediagram.txt) of message flow between services ([sequencediagram.org](https://sequencediagram.org/)).
+- [`test local swap`](./local-swap.md) describe how to run a swap with both participant locally
+- [`docker stack`](./docker-stack.md) documents how to use the node with docker and docker compose
+- [`sequence diagram`](./sequencediagram.txt) of message flow between services
diff --git a/doc/docker-stack.md b/doc/docker-stack.md
index 0decc8f3..60b5751e 100644
--- a/doc/docker-stack.md
+++ b/doc/docker-stack.md
@@ -13,7 +13,7 @@ docker-compose logs farcasterd -f --no-log-prefix
 
 These commands launch a local Monero Wallet RPC and the Farcaster node, then follow the logs produced by `farcasterd`. Daemons used by the services are public testnet servers.
 
-Install `swap-cli` on your machine (follow the documentation on [build and run locally](../README.md#locally)) and run:
+Install `swap-cli` on your machine and run:
 
 ```
 swap-cli -x "lnpz://127.0.0.1:9981/?api=esb" info
@@ -68,7 +68,7 @@ Stop the container with `docker stop farcaster_node` (ctrl+c does not work yet).
 
 :warning: this exposes the control bus on the host, only intended for debugging or on a trusted network.
 
-## Connect a client
+### Connect a client
 
 To connect a client and command `farcasterd` running inside a Docker container simply run:
 
@@ -76,28 +76,4 @@ To connect a client and command `farcasterd` running inside a Docker container s
 swap-cli -x "lnpz://127.0.0.1:9981/?api=esb" info
 ```
 
-This configure the cli to connects to the exposed port `9981` of the running container `farcasterd`.
-
-### Remote client usage
-
-Example from using other URLs supported by crate `internet2` `node_addr.rs`, besides the default inter process communication (also used by the Docker image to expose the control bus).
-
-The daemon is controlled though ZMQ _ctl_ socket, an internal interface for control PRC protocol communications. Another ZMQ socket is used to forward all incoming protocol messages, the _msg_ socket. Both are node internal communication channels. Message from counterparty come through `peerd` services.
-
-**Farcasterd**
-
-To launch `farcasterd` with network binded _control_ (`-x`) bus and _message_ bus (`-m`) instead of `ctl.rpc` and `msg.rpc` files:
-
-```
-farcasterd -vv -x "lnpz://127.0.0.1:9981/?api=esb" -m "lnpz://127.0.0.1:9982/?api=esb"
-```
-
-**Client**
-
-The following client can instruct the above `farcasterd` to return general informations as follows:
-
-```
-swap-cli -x "lnpz://127.0.0.1:9981/?api=esb" -m "lnpz://127.0.0.1:9982/?api=esb" info
-```
-
-:mega: It is worth noting that _ctl_ and _msg_ sockets can use different type of communication. E.g. the docker stack only binds the _ctl_ socket over the network and keeps the _msg_ to its default `msg.rpc` file inside the data directory.
+This configure the cli to connects to the exposed port `9981` of `farcasterd`.
diff --git a/doc/local-swap.md b/doc/local-swap.md
index 7cd7e01b..8ff3130e 100644
--- a/doc/local-swap.md
+++ b/doc/local-swap.md
@@ -2,7 +2,7 @@
 
 This document describe how to test a swap running both participant locally.
 
-You'll need to build and install the node locally as described [here](../README.md#locally).
+You'll need to build and install the node locally as described [here](../README.md#local).
 
 We'll create two node instances and will make them running a swap, for that make sure to have access to the three services: an `electrum server`, a `monero daemon`, and a `monero rpc wallet`.
 


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 292ed670..6649e59b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -16,13 +16,6 @@ jobs:
     steps:
       - uses: actions/checkout@v2
 
-      - name: Install latest nightly
-        uses: actions-rs/toolchain@v1
-        with:
-          toolchain: nightly
-          override: true
-          components: rustfmt
-
       - name: Run Rustfmt
         uses: actions-rs/cargo@v1
         with:
@@ -42,6 +35,7 @@ jobs:
         with:
           toolchain: nightly
           override: true
+          components: rustfmt
 
       - name: Build & test
         uses: actions-rs/cargo@v1
@@ -79,6 +73,7 @@ jobs:
         with:
           toolchain: nightly
           override: true
+          components: rustfmt
 
       - name: Correct permissions
         run: sudo chown -R $USER tests
@@ -87,7 +82,7 @@ jobs:
         uses: actions-rs/cargo@v1
         with:
           command: test
-          args: --workspace --all-targets --all-features --no-fail-fast --verbose -- --ignored --nocapture
+          args: --workspace --all-targets --all-features --no-fail-fast --verbose -- --ignored --test-threads=1
         env:
           CARGO_INCREMENTAL: '0'
           RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off'
diff --git a/tests/functional.rs b/tests/functional.rs
index 854a42e8..b9980437 100644
--- a/tests/functional.rs
+++ b/tests/functional.rs
@@ -21,6 +21,7 @@ use internet2::ZMQ_CONTEXT;
 use lnpbp::chain::Chain;
 use monero::Address;
 use monero_rpc::GetBlockHeaderSelector;
+use paste::paste;
 use std::collections::HashMap;
 use std::sync::mpsc::Receiver;
 use std::sync::mpsc::Sender;
@@ -40,34 +41,33 @@ const SOURCE2: ServiceId = ServiceId::Syncer(Coin::Monero, Network::Local);
 
 /*
 These tests need to run serialy, otherwise we cannot verify events based on the
-state of electrum and bitcoin
+state of electrum and bitcoin, for that we use `--test-threads=1` when running
+`cargo test`
 */
 
-#[test]
-#[ignore] // it's too expensive
-fn bitcoin_syncer_test() {
-    bitcoin_syncer_block_height_test(true);
-    bitcoin_syncer_address_test(true);
-    bitcoin_syncer_transaction_test(true);
-    bitcoin_syncer_broadcast_tx_test(true);
-    bitcoin_syncer_block_height_test(false);
-    bitcoin_syncer_address_test(false);
-    bitcoin_syncer_transaction_test(false);
-    bitcoin_syncer_broadcast_tx_test(false);
-    bitcoin_syncer_abort_test();
-}
+macro_rules! make_polling_test {
+    ($name:ident) => {
+        paste! {
+            #[test]
+            #[ignore]
+            fn [< $name _polling >] () {
+                $name(true);
+            }
 
-#[tokio::test]
-#[ignore] // it's too expensive
-async fn monero_syncer_test() {
-    monero_syncer_sweep_test().await;
-    monero_syncer_block_height_test().await;
-    monero_syncer_address_test().await;
-    monero_syncer_transaction_test().await;
-    monero_syncer_broadcast_tx_test().await;
-    monero_syncer_abort_test().await;
+            #[test]
+            #[ignore]
+            fn [< $name _no_polling >] () {
+                $name(false);
+            }
+        }
+    };
 }
 
+make_polling_test!(bitcoin_syncer_block_height_test);
+make_polling_test!(bitcoin_syncer_address_test);
+make_polling_test!(bitcoin_syncer_transaction_test);
+make_polling_test!(bitcoin_syncer_broadcast_tx_test);
+
 /*
 We test for the following scenarios in the block height tests:
 
@@ -84,7 +84,7 @@ fn bitcoin_syncer_block_height_test(polling: bool) {
     let address = bitcoin_rpc.get_new_address(None, None).unwrap();
 
     // start a bitcoin syncer
-    let (tx, rx_event) = create_bitcoin_syncer(polling);
+    let (tx, rx_event) = create_bitcoin_syncer(polling, "block_height");
 
     let blocks = bitcoin_rpc.get_block_count().unwrap();
 
@@ -181,7 +181,7 @@ fn bitcoin_syncer_address_test(polling: bool) {
     std::thread::sleep(duration);
 
     // start a bitcoin syncer
-    let (tx, rx_event) = create_bitcoin_syncer(polling);
+    let (tx, rx_event) = create_bitcoin_syncer(polling, "address");
 
     let blocks = bitcoin_rpc.get_block_count().unwrap();
 
@@ -383,7 +383,7 @@ fn bitcoin_syncer_transaction_test(polling: bool) {
     bitcoin_rpc.generate_to_address(110, &address).unwrap();
 
     // start a bitcoin syncer
-    let (tx, rx_event) = create_bitcoin_syncer(polling);
+    let (tx, rx_event) = create_bitcoin_syncer(polling, "transaction");
 
     // 294 Satoshi is the dust limit for a segwit transaction
     let amount = bitcoin::Amount::ONE_SAT * 294;
@@ -555,8 +555,10 @@ fn bitcoin_syncer_transaction_test(polling: bool) {
     assert_transaction_confirmations(request, Some(0), vec![0]);
 }
 
+#[test]
+#[ignore]
 fn bitcoin_syncer_abort_test() {
-    let (tx, rx_event) = create_bitcoin_syncer(true);
+    let (tx, rx_event) = create_bitcoin_syncer(true, "abort");
     let bitcoin_rpc = bitcoin_setup();
     let blocks = bitcoin_rpc.get_block_count().unwrap();
 
@@ -614,7 +616,7 @@ fn bitcoin_syncer_broadcast_tx_test(polling: bool) {
     let bitcoin_rpc = bitcoin_setup();
     let address = bitcoin_rpc.get_new_address(None, None).unwrap();
 
-    let (tx, rx_event) = create_bitcoin_syncer(polling);
+    let (tx, rx_event) = create_bitcoin_syncer(polling, "broadcast");
 
     // 294 Satoshi is the dust limit for a segwit transaction
     let amount = bitcoin::Amount::ONE_SAT * 294;
@@ -691,12 +693,17 @@ fn bitcoin_syncer_broadcast_tx_test(polling: bool) {
     assert_transaction_broadcasted(request, false, None);
 }
 
-fn create_bitcoin_syncer(polling: bool) -> (std::sync::mpsc::Sender<SyncerdTask>, zmq::Socket) {
+fn create_bitcoin_syncer(
+    polling: bool,
+    socket_name: &str,
+) -> (std::sync::mpsc::Sender<SyncerdTask>, zmq::Socket) {
+    let addr = format!("inproc://testmonerobridge-{}", socket_name);
+
     let (tx, rx): (Sender<SyncerdTask>, Receiver<SyncerdTask>) = std::sync::mpsc::channel();
     let tx_event = ZMQ_CONTEXT.socket(zmq::PAIR).unwrap();
     let rx_event = ZMQ_CONTEXT.socket(zmq::PAIR).unwrap();
-    tx_event.connect("inproc://testbitcoinbridge").unwrap();
-    rx_event.bind("inproc://testbitcoinbridge").unwrap();
+    tx_event.connect(&addr).unwrap();
+    rx_event.bind(&addr).unwrap();
     let mut syncer = BitcoinSyncer::new();
     let syncer_servers = SyncerServers {
         electrum_server: "tcp://localhost:50001".to_string(),
@@ -762,6 +769,8 @@ We test for the following scenarios in the block height tests:
 
 - Mine another block and receive two HeightChanged events
 */
+#[tokio::test]
+#[ignore]
 async fn monero_syncer_block_height_test() {
     let (regtest, wallet) = setup_monero().await;
     let address = wallet.get_address(0, None).await.unwrap();
@@ -772,7 +781,7 @@ async fn monero_syncer_block_height_test() {
     std::thread::sleep(duration);
 
     // create a monero syncer
-    let (tx, rx_event) = create_monero_syncer();
+    let (tx, rx_event) = create_monero_syncer("block_height");
 
     // Send a WatchHeight task
     let task = SyncerdTask {
@@ -828,6 +837,8 @@ async fn monero_syncer_block_height_test() {
     assert_received_height_changed(request, blocks);
 }
 
+#[tokio::test]
+#[ignore]
 async fn monero_syncer_sweep_test() {
     let (regtest, wallet) = setup_monero().await;
     let address = wallet.get_address(0, None).await.unwrap();
@@ -836,7 +847,7 @@ async fn monero_syncer_sweep_test() {
     let duration = std::time::Duration::from_secs(20);
     std::thread::sleep(duration);
 
-    let (tx, rx_event) = create_monero_syncer();
+    let (tx, rx_event) = create_monero_syncer("sweep");
 
     let spend_key = monero::PrivateKey::from_str(
         "77916d0cd56ed1920aef6ca56d8a41bac915b68e4c46a589e0956e27a7b77404",
@@ -897,6 +908,8 @@ transactions, ensure we receive only events for transactions after the from
 height
 
 */
+#[tokio::test]
+#[ignore]
 async fn monero_syncer_address_test() {
     let (regtest, wallet) = setup_monero().await;
     let address = wallet.get_address(0, None).await.unwrap();
@@ -907,7 +920,7 @@ async fn monero_syncer_address_test() {
     std::thread::sleep(duration);
 
     // create a monero syncer
-    let (tx, rx_event) = create_monero_syncer();
+    let (tx, rx_event) = create_monero_syncer("address");
 
     // Generate two addresses and watch them
     let view_key = wallet
@@ -1078,6 +1091,8 @@ the threshold confs are reached
 - Create a transaction, but don't relay it, then watch it and receive a tx not
 found confirmation event. Then relay and receive further events.
 */
+#[tokio::test]
+#[ignore]
 async fn monero_syncer_transaction_test() {
     let (regtest, wallet) = setup_monero().await;
     let address = wallet.get_address(0, None).await.unwrap().address;
@@ -1088,7 +1103,7 @@ async fn monero_syncer_transaction_test() {
     std::thread::sleep(duration);
 
     // create a monero syncer
-    let (tx, rx_event) = create_monero_syncer();
+    let (tx, rx_event) = create_monero_syncer("transaction");
 
     let txid_1 = send_monero(&wallet, address, 1).await;
 
@@ -1221,9 +1236,10 @@ async fn monero_syncer_transaction_test() {
     let request = get_request_from_message(message);
     assert_transaction_confirmations(request, None, vec![0]);
 
-    let id = wallet
+    wallet
         .relay_tx(hex::encode(transaction.tx_metadata.0))
-        .await;
+        .await
+        .unwrap();
     println!("awaiting confirmations");
     let message = rx_event.recv_multipart(0).unwrap();
     println!("received confirmation");
@@ -1238,8 +1254,10 @@ async fn monero_syncer_transaction_test() {
     assert_transaction_confirmations(request, Some(1), block_hash);
 }
 
+#[tokio::test]
+#[ignore]
 async fn monero_syncer_abort_test() {
-    let (tx, rx_event) = create_monero_syncer();
+    let (tx, rx_event) = create_monero_syncer("abort");
     let (regtest, wallet) = setup_monero().await;
     let address = wallet.get_address(0, None).await.unwrap();
     let blocks = regtest.generate_blocks(1, address.address).await.unwrap();
@@ -1288,12 +1306,14 @@ async fn monero_syncer_abort_test() {
 /*
 Check that a monero BroadcastTransaction task generates an error
 */
+#[tokio::test]
+#[ignore]
 async fn monero_syncer_broadcast_tx_test() {
     let (regtest, wallet) = setup_monero().await;
     let address = wallet.get_address(0, None).await.unwrap();
     regtest.generate_blocks(1, address.address).await.unwrap();
 
-    let (tx, rx_event) = create_monero_syncer();
+    let (tx, rx_event) = create_monero_syncer("broadcast");
 
     let task = SyncerdTask {
         task: Task::BroadcastTransaction(BroadcastTransaction { id: 0, tx: vec![0] }),
@@ -1329,12 +1349,13 @@ async fn setup_monero() -> (monero_rpc::RegtestDaemonClient, monero_rpc::WalletC
     (regtest, wallet)
 }
 
-fn create_monero_syncer() -> (std::sync::mpsc::Sender<SyncerdTask>, zmq::Socket) {
+fn create_monero_syncer(socket_name: &str) -> (std::sync::mpsc::Sender<SyncerdTask>, zmq::Socket) {
+    let addr = format!("inproc://testmonerobridge-{}", socket_name);
     let (tx, rx): (Sender<SyncerdTask>, Receiver<SyncerdTask>) = std::sync::mpsc::channel();
     let tx_event = ZMQ_CONTEXT.socket(zmq::PAIR).unwrap();
     let rx_event = ZMQ_CONTEXT.socket(zmq::PAIR).unwrap();
-    tx_event.connect("inproc://testmonerobridge").unwrap();
-    rx_event.bind("inproc://testmonerobridge").unwrap();
+    tx_event.connect(&addr).unwrap();
+    rx_event.bind(&addr).unwrap();
     let mut syncer = MoneroSyncer::new();
     let syncer_servers = SyncerServers {
         electrum_server: "".to_string(),