Merge branch 'master' into rpc-server

This commit is contained in:
binarybaron 2023-08-09 22:52:14 +02:00
commit 75bc64d248
34 changed files with 942 additions and 800 deletions

View File

@ -45,12 +45,12 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout tagged commit - name: Checkout tagged commit
uses: actions/checkout@v3.3.0 uses: actions/checkout@v3.5.3
with: with:
ref: ${{ github.event.release.target_commitish }} ref: ${{ github.event.release.target_commitish }}
token: ${{ secrets.BOTTY_GITHUB_TOKEN }} token: ${{ secrets.BOTTY_GITHUB_TOKEN }}
- uses: Swatinem/rust-cache@v2.2.0 - uses: Swatinem/rust-cache@v2.6.0
- uses: dtolnay/rust-toolchain@master - uses: dtolnay/rust-toolchain@master
with: with:

View File

@ -13,12 +13,19 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v3.3.0 uses: actions/checkout@v3.5.3
- uses: Swatinem/rust-cache@v2.2.0 - uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.67
components: clippy,rustfmt
- uses: Swatinem/rust-cache@v2.6.0
- name: Check formatting - name: Check formatting
uses: dprint/check@v2.1 uses: dprint/check@v2.2
with:
dprint-version: 0.39.1
- name: Run clippy with default features - name: Run clippy with default features
run: cargo clippy --workspace --all-targets -- -D warnings run: cargo clippy --workspace --all-targets -- -D warnings
@ -30,9 +37,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v3.3.0 uses: actions/checkout@v3.5.3
- uses: Swatinem/rust-cache@v2.0.2 - uses: Swatinem/rust-cache@v2.6.0
- name: Build swap - name: Build swap
run: cargo build --bin swap run: cargo build --bin swap
@ -44,9 +51,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v3.3.0 uses: actions/checkout@v3.5.3
- uses: Swatinem/rust-cache@v2.0.2 - uses: Swatinem/rust-cache@v2.6.0
- name: Install sqlx-cli - name: Install sqlx-cli
run: cargo install sqlx-cli run: cargo install sqlx-cli
@ -71,13 +78,13 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v3.3.0 uses: actions/checkout@v3.5.3
- uses: Swatinem/rust-cache@v2.2.0 - uses: Swatinem/rust-cache@v2.6.0
- uses: dtolnay/rust-toolchain@master - uses: dtolnay/rust-toolchain@master
with: with:
toolchain: 1.63 toolchain: 1.67
targets: armv7-unknown-linux-gnueabihf targets: armv7-unknown-linux-gnueabihf
- name: Build binary - name: Build binary
@ -111,9 +118,9 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v3.3.0 uses: actions/checkout@v3.5.3
- uses: Swatinem/rust-cache@v2.2.0 - uses: Swatinem/rust-cache@v2.6.0
- name: Build tests - name: Build tests
run: cargo build --tests --workspace --all-features run: cargo build --tests --workspace --all-features
@ -148,9 +155,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v3.3.0 uses: actions/checkout@v3.5.3
- uses: Swatinem/rust-cache@v2.2.0 - uses: Swatinem/rust-cache@v2.6.0
- name: Run test ${{ matrix.test_name }} - name: Run test ${{ matrix.test_name }}
run: cargo test --package swap --all-features --test ${{ matrix.test_name }} -- --nocapture run: cargo test --package swap --all-features --test ${{ matrix.test_name }} -- --nocapture

View File

@ -11,7 +11,7 @@ jobs:
if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/') if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3.3.0 - uses: actions/checkout@v3.5.3
- name: Extract version from branch name - name: Extract version from branch name
id: extract-version id: extract-version

View File

@ -12,7 +12,7 @@ jobs:
name: "Draft a new release" name: "Draft a new release"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3.3.0 - uses: actions/checkout@v3.5.3
with: with:
token: ${{ secrets.BOTTY_GITHUB_TOKEN }} token: ${{ secrets.BOTTY_GITHUB_TOKEN }}
@ -41,8 +41,12 @@ jobs:
- name: Commit changelog and manifest files - name: Commit changelog and manifest files
id: make-commit id: make-commit
env:
DPRINT_VERSION: 0.39.1
RUST_TOOLCHAIN: 1.67
run: | run: |
curl -fsSL https://dprint.dev/install.sh | sh rustup component add rustfmt --toolchain "$RUST_TOOLCHAIN-x86_64-unknown-linux-gnu"
curl -fsSL https://dprint.dev/install.sh | sh -s $DPRINT_VERSION
/home/runner/.dprint/bin/dprint fmt /home/runner/.dprint/bin/dprint fmt
git add CHANGELOG.md Cargo.lock swap/Cargo.toml git add CHANGELOG.md Cargo.lock swap/Cargo.toml
@ -54,7 +58,7 @@ jobs:
run: git push origin release/${{ github.event.inputs.version }} --force run: git push origin release/${{ github.event.inputs.version }} --force
- name: Create pull request - name: Create pull request
uses: thomaseizinger/create-pull-request@1.3.0 uses: thomaseizinger/create-pull-request@1.3.1
with: with:
GITHUB_TOKEN: ${{ secrets.BOTTY_GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.BOTTY_GITHUB_TOKEN }}
head: release/${{ github.event.inputs.version }} head: release/${{ github.event.inputs.version }}

View File

@ -10,7 +10,7 @@ jobs:
name: Create preview release name: Create preview release
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3.3.0 - uses: actions/checkout@v3.5.3
- name: Delete 'preview' release - name: Delete 'preview' release
uses: larryjoelane/delete-release-action@v1.0.24 uses: larryjoelane/delete-release-action@v1.0.24

View File

@ -7,9 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.12.2] - 2023-08-08
### Changed ### Changed
- Minimum Supported Rust Version (MSRV) bumped to 1.63 - Minimum Supported Rust Version (MSRV) bumped to 1.67
- ASB can now register with multiple rendezvous nodes. The `rendezvous_point` option in `config.toml` can be a string with comma separated addresses, or a toml array of address strings.
## [0.12.1] - 2023-01-09 ## [0.12.1] - 2023-01-09
@ -342,7 +345,8 @@ It is possible to migrate critical data from the old db to the sqlite but there
- Fixed an issue where Alice would not verify if Bob's Bitcoin lock transaction is semantically correct, i.e. pays the agreed upon amount to an output owned by both of them. - Fixed an issue where Alice would not verify if Bob's Bitcoin lock transaction is semantically correct, i.e. pays the agreed upon amount to an output owned by both of them.
Fixing this required a **breaking change** on the network layer and hence old versions are not compatible with this version. Fixing this required a **breaking change** on the network layer and hence old versions are not compatible with this version.
[unreleased]: https://github.com/comit-network/xmr-btc-swap/compare/0.12.1...HEAD [Unreleased]: https://github.com/comit-network/xmr-btc-swap/compare/0.12.2...HEAD
[0.12.2]: https://github.com/comit-network/xmr-btc-swap/compare/0.12.1...0.12.2
[0.12.1]: https://github.com/comit-network/xmr-btc-swap/compare/0.12.0...0.12.1 [0.12.1]: https://github.com/comit-network/xmr-btc-swap/compare/0.12.0...0.12.1
[0.12.0]: https://github.com/comit-network/xmr-btc-swap/compare/0.11.0...0.12.0 [0.12.0]: https://github.com/comit-network/xmr-btc-swap/compare/0.11.0...0.12.0
[0.11.0]: https://github.com/comit-network/xmr-btc-swap/compare/0.10.2...0.11.0 [0.11.0]: https://github.com/comit-network/xmr-btc-swap/compare/0.10.2...0.11.0

940
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -44,13 +44,13 @@ It is not recommended to bump fees when swapping because it can have unpredictab
## Contributing ## Contributing
We are encourage community contributions whether it be a bug fix or an improvement to the documentation. We encourage community contributions whether it be a bug fix or an improvement to the documentation.
Please have a look at the [contribution guidelines](./CONTRIBUTING.md). Please have a look at the [contribution guidelines](./CONTRIBUTING.md).
## Rust Version Support ## Rust Version Support
Please note that only the latest stable Rust toolchain is supported. Please note that only the latest stable Rust toolchain is supported.
All stable toolchains since 1.63 _should_ work. All stable toolchains since 1.67 _should_ work.
## Contact ## Contact

View File

@ -42,13 +42,16 @@ Since the ASB is a long running task we specify the person running an ASB as ser
The ASB daemon supports the libp2p [rendezvous-protocol](https://github.com/libp2p/specs/tree/master/rendezvous). The ASB daemon supports the libp2p [rendezvous-protocol](https://github.com/libp2p/specs/tree/master/rendezvous).
Usage of the rendezvous functionality is entirely optional. Usage of the rendezvous functionality is entirely optional.
You can configure a rendezvous point in the `[network]` section of your config file. You can configure one or more rendezvous points in the `[network]` section of your config file.
For the registration to be successful, you also need to configure the externally reachable addresses within the `[network]` section. For the registration to be successful, you also need to configure the externally reachable addresses within the `[network]` section.
For example: For example:
```toml ```toml
[network] [network]
rendezvous_point = "/dns4/discover.unstoppableswap.net/tcp/8888/p2p/12D3KooWA6cnqJpVnreBVnoro8midDL9Lpzmg8oJPoAGi7YYaamE" rendezvous_point = [
"/dns4/discover.unstoppableswap.net/tcp/8888/p2p/12D3KooWA6cnqJpVnreBVnoro8midDL9Lpzmg8oJPoAGi7YYaamE",
"/dns4/eratosthen.es/tcp/7798/p2p/12D3KooWAh7EXXa2ZyegzLGdjvj1W4G3EXrTGrf6trraoT1MEobs",
]
external_addresses = ["/dns4/example.com/tcp/9939"] external_addresses = ["/dns4/example.com/tcp/9939"]
``` ```

View File

@ -3,22 +3,16 @@
"projectType": "openSource", "projectType": "openSource",
"incremental": true, "incremental": true,
"markdown": {}, "markdown": {},
"rustfmt": { "exec": {
"edition": 2021, "associations": "**/*.{rs}",
"condense_wildcard_suffixes": true, "rustfmt": "rustfmt --edition 2021",
"format_macro_matchers": true, "rustfmt.associations": "**/*.rs"
"imports_granularity": "Module",
"use_field_init_shorthand": true,
"format_code_in_doc_comments": true,
"normalize_comments": true,
"wrap_comments": true,
"overflow_delimited_expr": true
}, },
"includes": ["**/*.{md}", "**/*.{toml}", "**/*.{rs}"], "includes": ["**/*.{md}", "**/*.{toml}", "**/*.{rs}"],
"excludes": ["target/"], "excludes": ["target/"],
"plugins": [ "plugins": [
"https://plugins.dprint.dev/markdown-0.13.1.wasm", "https://plugins.dprint.dev/markdown-0.13.1.wasm",
"https://github.com/thomaseizinger/dprint-plugin-cargo-toml/releases/download/0.1.0/cargo-toml-0.1.0.wasm", "https://github.com/thomaseizinger/dprint-plugin-cargo-toml/releases/download/0.1.0/cargo-toml-0.1.0.wasm",
"https://plugins.dprint.dev/rustfmt-0.6.1.exe-plugin@99b89a0599fd3a63e597e03436862157901f3facae2f0c2fbd0b9f656cdbc2a5" "https://plugins.dprint.dev/exec-0.3.5.json@d687dda57be0fe9a0088ccdaefa5147649ff24127d8b3ea227536c68ee7abeab"
] ]
} }

View File

@ -249,7 +249,7 @@ impl<'c> Monerod {
/// address /// address
pub async fn start_miner(&self, miner_wallet_address: &str) -> Result<()> { pub async fn start_miner(&self, miner_wallet_address: &str) -> Result<()> {
let monerod = self.client().clone(); let monerod = self.client().clone();
let _ = tokio::spawn(mine(monerod, miner_wallet_address.to_string())); tokio::spawn(mine(monerod, miner_wallet_address.to_string()));
Ok(()) Ok(())
} }
} }

View File

@ -19,5 +19,5 @@ serde_json = "1.0"
tracing = "0.1" tracing = "0.1"
[dev-dependencies] [dev-dependencies]
hex-literal = "0.3" hex-literal = "0.4"
tokio = { version = "1", features = [ "full" ] } tokio = { version = "1", features = [ "full" ] }

View File

@ -47,9 +47,10 @@ impl Client {
} }
pub async fn get_o_indexes(&self, txid: Hash) -> Result<GetOIndexesResponse> { pub async fn get_o_indexes(&self, txid: Hash) -> Result<GetOIndexesResponse> {
self.binary_request(self.get_o_indexes_bin_url.clone(), GetOIndexesPayload { self.binary_request(
txid, self.get_o_indexes_bin_url.clone(),
}) GetOIndexesPayload { txid },
)
.await .await
} }

View File

@ -1,4 +1,4 @@
[toolchain] [toolchain]
channel = "1.63" # also update this in the readme, changelog, and github actions channel = "1.67" # also update this in the readme, changelog, and github actions
components = ["clippy"] components = ["clippy"]
targets = ["armv7-unknown-linux-gnueabihf"] targets = ["armv7-unknown-linux-gnueabihf"]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "swap" name = "swap"
version = "0.12.1" version = "0.12.2"
authors = [ "The COMIT guys <hello@comit.network>" ] authors = [ "The COMIT guys <hello@comit.network>" ]
edition = "2021" edition = "2021"
description = "XMR/BTC trustless atomic swaps." description = "XMR/BTC trustless atomic swaps."
@ -14,16 +14,16 @@ async-compression = { version = "0.3", features = [ "bzip2", "tokio" ] }
async-trait = "0.1" async-trait = "0.1"
atty = "0.2" atty = "0.2"
backoff = { version = "0.4", features = [ "tokio" ] } backoff = { version = "0.4", features = [ "tokio" ] }
base64 = "0.20" base64 = "0.21"
bdk = "0.25" bdk = "0.28"
big-bytes = "1" big-bytes = "1"
bitcoin = { version = "0.29", features = [ "rand", "serde" ] } bitcoin = { version = "0.29", features = [ "rand", "serde" ] }
bmrng = "0.5" bmrng = "0.5"
comfy-table = "6.1" comfy-table = "6.1"
config = { version = "0.13", default-features = false, features = [ "toml" ] } config = { version = "0.13", default-features = false, features = [ "toml" ] }
conquer-once = "0.3" conquer-once = "0.4"
curve25519-dalek = { package = "curve25519-dalek-ng", version = "4" } curve25519-dalek = { package = "curve25519-dalek-ng", version = "4" }
data-encoding = "2.3" data-encoding = "2.4"
dialoguer = "0.10" dialoguer = "0.10"
directories-next = "2" directories-next = "2"
ecdsa_fun = { git = "https://github.com/LLFourn/secp256kfun", default-features = false, features = [ "libsecp_compat", "serde", "adaptor" ] } ecdsa_fun = { git = "https://github.com/LLFourn/secp256kfun", default-features = false, features = [ "libsecp_compat", "serde", "adaptor" ] }
@ -66,7 +66,7 @@ tracing-appender = "0.2"
tracing-futures = { version = "0.2", features = [ "std-future", "futures-03" ] } tracing-futures = { version = "0.2", features = [ "std-future", "futures-03" ] }
tracing-subscriber = { version = "0.3", default-features = false, features = [ "fmt", "ansi", "env-filter", "time", "tracing-log", "json" ] } tracing-subscriber = { version = "0.3", default-features = false, features = [ "fmt", "ansi", "env-filter", "time", "tracing-log", "json" ] }
url = { version = "2", features = [ "serde" ] } url = { version = "2", features = [ "serde" ] }
uuid = { version = "1.2", features = [ "serde", "v4" ] } uuid = { version = "1.4", features = [ "serde", "v4" ] }
void = "1" void = "1"
[target.'cfg(not(windows))'.dependencies] [target.'cfg(not(windows))'.dependencies]
@ -86,11 +86,11 @@ port_check = "0.1"
proptest = "1" proptest = "1"
sequential-test = "0.2.4" sequential-test = "0.2.4"
serde_cbor = "0.11" serde_cbor = "0.11"
serial_test = "0.10" serial_test = "2.0"
spectral = "0.6" spectral = "0.6"
tempfile = "3" tempfile = "3"
testcontainers = "0.12" testcontainers = "0.12"
[build-dependencies] [build-dependencies]
anyhow = "1" anyhow = "1"
vergen = { version = "7", default-features = false, features = [ "git", "build" ] } vergen = { version = "7.5", default-features = false, features = [ "git", "build" ] }

View File

@ -8,6 +8,7 @@ pub mod tracing;
pub use event_loop::{EventLoop, EventLoopHandle, FixedRate, KrakenRate, LatestRate}; pub use event_loop::{EventLoop, EventLoopHandle, FixedRate, KrakenRate, LatestRate};
pub use network::behaviour::{Behaviour, OutEvent}; pub use network::behaviour::{Behaviour, OutEvent};
pub use network::rendezvous::RendezvousNode;
pub use network::transport; pub use network::transport;
pub use rate::Rate; pub use rate::Rate;
pub use recovery::cancel::cancel; pub use recovery::cancel::cancel;
@ -18,4 +19,4 @@ pub use recovery::safely_abort::safely_abort;
pub use recovery::{cancel, refund}; pub use recovery::{cancel, refund};
#[cfg(test)] #[cfg(test)]
pub use network::rendezous; pub use network::rendezvous;

View File

@ -134,8 +134,8 @@ pub struct Data {
pub struct Network { pub struct Network {
#[serde(deserialize_with = "addr_list::deserialize")] #[serde(deserialize_with = "addr_list::deserialize")]
pub listen: Vec<Multiaddr>, pub listen: Vec<Multiaddr>,
#[serde(default)] #[serde(default, deserialize_with = "addr_list::deserialize")]
pub rendezvous_point: Option<Multiaddr>, pub rendezvous_point: Vec<Multiaddr>,
#[serde(default, deserialize_with = "addr_list::deserialize")] #[serde(default, deserialize_with = "addr_list::deserialize")]
pub external_addresses: Vec<Multiaddr>, pub external_addresses: Vec<Multiaddr>,
} }
@ -156,7 +156,7 @@ mod addr_list {
let list: Result<Vec<_>, _> = s let list: Result<Vec<_>, _> = s
.split(',') .split(',')
.filter(|s| !s.is_empty()) .filter(|s| !s.is_empty())
.map(|s| s.parse().map_err(de::Error::custom)) .map(|s| s.trim().parse().map_err(de::Error::custom))
.collect(); .collect();
Ok(list?) Ok(list?)
} }
@ -165,7 +165,7 @@ mod addr_list {
.iter() .iter()
.map(|v| { .map(|v| {
if let Value::String(s) = v { if let Value::String(s) = v {
s.parse().map_err(de::Error::custom) s.trim().parse().map_err(de::Error::custom)
} else { } else {
Err(de::Error::custom("expected a string")) Err(de::Error::custom("expected a string"))
} }
@ -347,10 +347,27 @@ pub fn query_user_for_initial_config(testnet: bool) -> Result<Config> {
} }
let ask_spread = Decimal::from_f64(ask_spread).context("Unable to parse spread")?; let ask_spread = Decimal::from_f64(ask_spread).context("Unable to parse spread")?;
let rendezvous_point = Input::<Multiaddr>::with_theme(&ColorfulTheme::default()) let mut number = 1;
.with_prompt("Do you want to advertise your ASB instance with a rendezvous node? Enter an empty string if not.") let mut done = false;
let mut rendezvous_points = Vec::new();
println!("ASB can register with multiple rendezvous nodes for discoverability. This can also be edited in the config file later.");
while !done {
let prompt = format!(
"Enter the address for rendezvous node ({number}). Or just hit Enter to continue."
);
let rendezvous_addr = Input::<Multiaddr>::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.allow_empty(true) .allow_empty(true)
.interact_text()?; .interact_text()?;
if rendezvous_addr.is_empty() {
done = true;
} else if rendezvous_points.contains(&rendezvous_addr) {
println!("That rendezvous address is already in the list.");
} else {
rendezvous_points.push(rendezvous_addr);
number += 1;
}
}
println!(); println!();
@ -358,11 +375,7 @@ pub fn query_user_for_initial_config(testnet: bool) -> Result<Config> {
data: Data { dir: data_dir }, data: Data { dir: data_dir },
network: Network { network: Network {
listen: listen_addresses, listen: listen_addresses,
rendezvous_point: if rendezvous_point.is_empty() { rendezvous_point: rendezvous_points, // keeping the singular key name for backcompat
None
} else {
Some(rendezvous_point)
},
external_addresses: vec![], external_addresses: vec![],
}, },
bitcoin: Bitcoin { bitcoin: Bitcoin {
@ -417,7 +430,7 @@ mod tests {
}, },
network: Network { network: Network {
listen: vec![defaults.listen_address_tcp, defaults.listen_address_ws], listen: vec![defaults.listen_address_tcp, defaults.listen_address_ws],
rendezvous_point: None, rendezvous_point: vec![],
external_addresses: vec![], external_addresses: vec![],
}, },
monero: Monero { monero: Monero {
@ -461,7 +474,7 @@ mod tests {
}, },
network: Network { network: Network {
listen: vec![defaults.listen_address_tcp, defaults.listen_address_ws], listen: vec![defaults.listen_address_tcp, defaults.listen_address_ws],
rendezvous_point: None, rendezvous_point: vec![],
external_addresses: vec![], external_addresses: vec![],
}, },
monero: Monero { monero: Monero {
@ -515,7 +528,7 @@ mod tests {
}, },
network: Network { network: Network {
listen, listen,
rendezvous_point: None, rendezvous_point: vec![],
external_addresses, external_addresses,
}, },
monero: Monero { monero: Monero {

View File

@ -253,8 +253,8 @@ where
channel channel
}.boxed()); }.boxed());
} }
SwarmEvent::Behaviour(OutEvent::Rendezvous(libp2p::rendezvous::client::Event::Registered { .. })) => { SwarmEvent::Behaviour(OutEvent::Rendezvous(libp2p::rendezvous::client::Event::Registered { rendezvous_node, ttl, namespace })) => {
tracing::info!("Successfully registered with rendezvous node"); tracing::info!("Successfully registered with rendezvous node: {} with namespace: {} and TTL: {:?}", rendezvous_node, namespace, ttl);
} }
SwarmEvent::Behaviour(OutEvent::Rendezvous(libp2p::rendezvous::client::Event::RegisterFailed(error))) => { SwarmEvent::Behaviour(OutEvent::Rendezvous(libp2p::rendezvous::client::Event::RegisterFailed(error))) => {
tracing::error!("Registration with rendezvous node failed: {:?}", error); tracing::error!("Registration with rendezvous node failed: {:?}", error);

View File

@ -44,7 +44,9 @@ pub mod transport {
} }
pub mod behaviour { pub mod behaviour {
use super::*; use libp2p::swarm::behaviour::toggle::Toggle;
use super::{rendezvous::RendezvousNode, *};
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
#[derive(Debug)] #[derive(Debug)]
@ -108,7 +110,7 @@ pub mod behaviour {
where where
LR: LatestRate + Send + 'static, LR: LatestRate + Send + 'static,
{ {
pub rendezvous: libp2p::swarm::behaviour::toggle::Toggle<rendezous::Behaviour>, pub rendezvous: Toggle<rendezvous::Behaviour>,
pub quote: quote::Behaviour, pub quote: quote::Behaviour,
pub swap_setup: alice::Behaviour<LR>, pub swap_setup: alice::Behaviour<LR>,
pub transfer_proof: transfer_proof::Behaviour, pub transfer_proof: transfer_proof::Behaviour,
@ -132,25 +134,22 @@ pub mod behaviour {
resume_only: bool, resume_only: bool,
env_config: env::Config, env_config: env::Config,
identify_params: (identity::Keypair, XmrBtcNamespace), identify_params: (identity::Keypair, XmrBtcNamespace),
rendezvous_params: Option<(identity::Keypair, PeerId, Multiaddr, XmrBtcNamespace)>, rendezvous_nodes: Vec<RendezvousNode>,
) -> Self { ) -> Self {
let agentVersion = format!("asb/{} ({})", env!("CARGO_PKG_VERSION"), identify_params.1); let (identity, namespace) = identify_params;
let protocolVersion = "/comit/xmr/btc/1.0.0".to_string(); let agent_version = format!("asb/{} ({})", env!("CARGO_PKG_VERSION"), namespace);
let identifyConfig = IdentifyConfig::new(protocolVersion, identify_params.0.public()) let protocol_version = "/comit/xmr/btc/1.0.0".to_string();
.with_agent_version(agentVersion); let identifyConfig = IdentifyConfig::new(protocol_version, identity.public())
.with_agent_version(agent_version);
let behaviour = if rendezvous_nodes.is_empty() {
None
} else {
Some(rendezvous::Behaviour::new(identity, rendezvous_nodes))
};
Self { Self {
rendezvous: libp2p::swarm::behaviour::toggle::Toggle::from(rendezvous_params.map( rendezvous: Toggle::from(behaviour),
|(identity, rendezvous_peer_id, rendezvous_address, namespace)| {
rendezous::Behaviour::new(
identity,
rendezvous_peer_id,
rendezvous_address,
namespace,
None, // use default ttl on rendezvous point
)
},
)),
quote: quote::asb(), quote: quote::asb(),
swap_setup: alice::Behaviour::new( swap_setup: alice::Behaviour::new(
min_buy, min_buy,
@ -186,13 +185,14 @@ pub mod behaviour {
} }
} }
pub mod rendezous { pub mod rendezvous {
use super::*; use super::*;
use libp2p::swarm::dial_opts::DialOpts; use libp2p::swarm::dial_opts::DialOpts;
use libp2p::swarm::DialError; use libp2p::swarm::DialError;
use std::collections::VecDeque;
use std::pin::Pin; use std::pin::Pin;
#[derive(PartialEq)] #[derive(Clone, PartialEq)]
enum ConnectionStatus { enum ConnectionStatus {
Disconnected, Disconnected,
Dialling, Dialling,
@ -209,39 +209,59 @@ pub mod rendezous {
pub struct Behaviour { pub struct Behaviour {
inner: libp2p::rendezvous::client::Behaviour, inner: libp2p::rendezvous::client::Behaviour,
rendezvous_point: Multiaddr, rendezvous_nodes: Vec<RendezvousNode>,
rendezvous_peer_id: PeerId, to_dial: VecDeque<PeerId>,
namespace: XmrBtcNamespace,
registration_status: RegistrationStatus,
connection_status: ConnectionStatus,
registration_ttl: Option<u64>,
} }
impl Behaviour { pub struct RendezvousNode {
pub address: Multiaddr,
connection_status: ConnectionStatus,
pub peer_id: PeerId,
registration_status: RegistrationStatus,
pub registration_ttl: Option<u64>,
pub namespace: XmrBtcNamespace,
}
impl RendezvousNode {
pub fn new( pub fn new(
identity: identity::Keypair, address: &Multiaddr,
rendezvous_peer_id: PeerId, peer_id: PeerId,
rendezvous_address: Multiaddr,
namespace: XmrBtcNamespace, namespace: XmrBtcNamespace,
registration_ttl: Option<u64>, registration_ttl: Option<u64>,
) -> Self { ) -> Self {
Self { Self {
inner: libp2p::rendezvous::client::Behaviour::new(identity), address: address.to_owned(),
rendezvous_point: rendezvous_address,
rendezvous_peer_id,
namespace,
registration_status: RegistrationStatus::RegisterOnNextConnection,
connection_status: ConnectionStatus::Disconnected, connection_status: ConnectionStatus::Disconnected,
namespace,
peer_id,
registration_status: RegistrationStatus::RegisterOnNextConnection,
registration_ttl, registration_ttl,
} }
} }
fn register(&mut self) { fn set_connection(&mut self, status: ConnectionStatus) {
self.inner.register( self.connection_status = status;
self.namespace.into(), }
self.rendezvous_peer_id,
self.registration_ttl, fn set_registration(&mut self, status: RegistrationStatus) {
); self.registration_status = status;
}
}
impl Behaviour {
pub fn new(identity: identity::Keypair, rendezvous_nodes: Vec<RendezvousNode>) -> Self {
Self {
inner: libp2p::rendezvous::client::Behaviour::new(identity),
rendezvous_nodes,
to_dial: VecDeque::new(),
}
}
/// Calls the rendezvous register method of the node at node_index in the Vec of rendezvous nodes
fn register(&mut self, node_index: usize) {
let node = &self.rendezvous_nodes[node_index];
self.inner
.register(node.namespace.into(), node.peer_id, node.registration_ttl);
} }
} }
@ -255,31 +275,37 @@ pub mod rendezous {
} }
fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec<Multiaddr> { fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec<Multiaddr> {
if peer_id == &self.rendezvous_peer_id { for node in self.rendezvous_nodes.iter() {
return vec![self.rendezvous_point.clone()]; if peer_id == &node.peer_id {
return vec![node.address.clone()];
}
} }
vec![] vec![]
} }
fn inject_connected(&mut self, peer_id: &PeerId) { fn inject_connected(&mut self, peer_id: &PeerId) {
if peer_id == &self.rendezvous_peer_id { for i in 0..self.rendezvous_nodes.len() {
self.connection_status = ConnectionStatus::Connected; if peer_id == &self.rendezvous_nodes[i].peer_id {
self.rendezvous_nodes[i].set_connection(ConnectionStatus::Connected);
match &self.registration_status { match &self.rendezvous_nodes[i].registration_status {
RegistrationStatus::RegisterOnNextConnection => { RegistrationStatus::RegisterOnNextConnection => {
self.register(); self.register(i);
self.registration_status = RegistrationStatus::Pending; self.rendezvous_nodes[i].set_registration(RegistrationStatus::Pending);
} }
RegistrationStatus::Registered { .. } => {} RegistrationStatus::Registered { .. } => {}
RegistrationStatus::Pending => {} RegistrationStatus::Pending => {}
} }
} }
} }
}
fn inject_disconnected(&mut self, peer_id: &PeerId) { fn inject_disconnected(&mut self, peer_id: &PeerId) {
if peer_id == &self.rendezvous_peer_id { for i in 0..self.rendezvous_nodes.len() {
self.connection_status = ConnectionStatus::Disconnected; let mut node = &mut self.rendezvous_nodes[i];
if peer_id == &node.peer_id {
node.connection_status = ConnectionStatus::Disconnected;
}
} }
} }
@ -298,9 +324,12 @@ pub mod rendezous {
_handler: Self::ProtocolsHandler, _handler: Self::ProtocolsHandler,
_error: &DialError, _error: &DialError,
) { ) {
for i in 0..self.rendezvous_nodes.len() {
let mut node = &mut self.rendezvous_nodes[i];
if let Some(id) = peer_id { if let Some(id) = peer_id {
if id == self.rendezvous_peer_id { if id == node.peer_id {
self.connection_status = ConnectionStatus::Disconnected; node.connection_status = ConnectionStatus::Disconnected;
}
} }
} }
} }
@ -311,42 +340,43 @@ pub mod rendezous {
cx: &mut std::task::Context<'_>, cx: &mut std::task::Context<'_>,
params: &mut impl PollParameters, params: &mut impl PollParameters,
) -> Poll<NetworkBehaviourAction<Self::OutEvent, Self::ProtocolsHandler>> { ) -> Poll<NetworkBehaviourAction<Self::OutEvent, Self::ProtocolsHandler>> {
match &mut self.registration_status { if let Some(peer_id) = self.to_dial.pop_front() {
RegistrationStatus::RegisterOnNextConnection => match self.connection_status {
ConnectionStatus::Disconnected => {
self.connection_status = ConnectionStatus::Dialling;
return Poll::Ready(NetworkBehaviourAction::Dial { return Poll::Ready(NetworkBehaviourAction::Dial {
opts: DialOpts::peer_id(self.rendezvous_peer_id) opts: DialOpts::peer_id(peer_id)
.condition(PeerCondition::Disconnected) .condition(PeerCondition::Disconnected)
.build(), .build(),
handler: Self::ProtocolsHandler::new(Duration::from_secs(30)), handler: Self::ProtocolsHandler::new(Duration::from_secs(30)),
}); });
} }
// check the status of each rendezvous node
for i in 0..self.rendezvous_nodes.len() {
let connection_status = self.rendezvous_nodes[i].connection_status.clone();
match &mut self.rendezvous_nodes[i].registration_status {
RegistrationStatus::RegisterOnNextConnection => match connection_status {
ConnectionStatus::Disconnected => {
self.rendezvous_nodes[i].set_connection(ConnectionStatus::Dialling);
self.to_dial.push_back(self.rendezvous_nodes[i].peer_id);
}
ConnectionStatus::Dialling => {} ConnectionStatus::Dialling => {}
ConnectionStatus::Connected => { ConnectionStatus::Connected => {
self.registration_status = RegistrationStatus::Pending; self.rendezvous_nodes[i].set_registration(RegistrationStatus::Pending);
self.register(); self.register(i);
} }
}, },
RegistrationStatus::Registered { re_register_in } => { RegistrationStatus::Registered { re_register_in } => {
if let Poll::Ready(()) = re_register_in.poll_unpin(cx) { if let Poll::Ready(()) = re_register_in.poll_unpin(cx) {
match self.connection_status { match connection_status {
ConnectionStatus::Connected => { ConnectionStatus::Connected => {
self.registration_status = RegistrationStatus::Pending; self.rendezvous_nodes[i]
self.register(); .set_registration(RegistrationStatus::Pending);
self.register(i);
} }
ConnectionStatus::Disconnected => { ConnectionStatus::Disconnected => {
self.registration_status = self.rendezvous_nodes[i].set_registration(
RegistrationStatus::RegisterOnNextConnection; RegistrationStatus::RegisterOnNextConnection,
);
return Poll::Ready(NetworkBehaviourAction::Dial { self.to_dial.push_back(self.rendezvous_nodes[i].peer_id);
opts: DialOpts::peer_id(self.rendezvous_peer_id)
.condition(PeerCondition::Disconnected)
.build(),
handler: Self::ProtocolsHandler::new(Duration::from_secs(30)),
});
} }
ConnectionStatus::Dialling => {} ConnectionStatus::Dialling => {}
} }
@ -354,19 +384,29 @@ pub mod rendezous {
} }
RegistrationStatus::Pending => {} RegistrationStatus::Pending => {}
} }
}
let inner_poll = self.inner.poll(cx, params); let inner_poll = self.inner.poll(cx, params);
// reset the timer if we successfully registered // reset the timer for the specific rendezvous node if we successfully registered
if let Poll::Ready(NetworkBehaviourAction::GenerateEvent( if let Poll::Ready(NetworkBehaviourAction::GenerateEvent(
libp2p::rendezvous::client::Event::Registered { ttl, .. }, libp2p::rendezvous::client::Event::Registered {
ttl,
rendezvous_node,
..
},
)) = &inner_poll )) = &inner_poll
{
if let Some(i) = self
.rendezvous_nodes
.iter()
.position(|n| &n.peer_id == rendezvous_node)
{ {
let half_of_ttl = Duration::from_secs(*ttl) / 2; let half_of_ttl = Duration::from_secs(*ttl) / 2;
let re_register_in = Box::pin(tokio::time::sleep(half_of_ttl));
self.registration_status = RegistrationStatus::Registered { let status = RegistrationStatus::Registered { re_register_in };
re_register_in: Box::pin(tokio::time::sleep(half_of_ttl)), self.rendezvous_nodes[i].set_registration(status);
}; }
} }
inner_poll inner_poll
@ -380,6 +420,7 @@ pub mod rendezous {
use futures::StreamExt; use futures::StreamExt;
use libp2p::rendezvous; use libp2p::rendezvous;
use libp2p::swarm::SwarmEvent; use libp2p::swarm::SwarmEvent;
use std::collections::HashMap;
#[tokio::test] #[tokio::test]
async fn given_no_initial_connection_when_constructed_asb_connects_and_registers_with_rendezvous_node( async fn given_no_initial_connection_when_constructed_asb_connects_and_registers_with_rendezvous_node(
@ -387,16 +428,16 @@ pub mod rendezous {
let mut rendezvous_node = new_swarm(|_, _| { let mut rendezvous_node = new_swarm(|_, _| {
rendezvous::server::Behaviour::new(rendezvous::server::Config::default()) rendezvous::server::Behaviour::new(rendezvous::server::Config::default())
}); });
let rendezvous_address = rendezvous_node.listen_on_random_memory_address().await; let address = rendezvous_node.listen_on_random_memory_address().await;
let rendezvous_point = RendezvousNode::new(
let mut asb = new_swarm(|_, identity| { &address,
rendezous::Behaviour::new( rendezvous_node.local_peer_id().to_owned(),
identity,
*rendezvous_node.local_peer_id(),
rendezvous_address,
XmrBtcNamespace::Testnet, XmrBtcNamespace::Testnet,
None, None,
) );
let mut asb = new_swarm(|_, identity| {
super::rendezvous::Behaviour::new(identity, vec![rendezvous_point])
}); });
asb.listen_on_random_memory_address().await; // this adds an external address asb.listen_on_random_memory_address().await; // this adds an external address
@ -428,16 +469,16 @@ pub mod rendezous {
rendezvous::server::Config::default().with_min_ttl(2), rendezvous::server::Config::default().with_min_ttl(2),
) )
}); });
let rendezvous_address = rendezvous_node.listen_on_random_memory_address().await; let address = rendezvous_node.listen_on_random_memory_address().await;
let rendezvous_point = RendezvousNode::new(
let mut asb = new_swarm(|_, identity| { &address,
rendezous::Behaviour::new( rendezvous_node.local_peer_id().to_owned(),
identity,
*rendezvous_node.local_peer_id(),
rendezvous_address,
XmrBtcNamespace::Testnet, XmrBtcNamespace::Testnet,
Some(5), Some(5),
) );
let mut asb = new_swarm(|_, identity| {
super::rendezvous::Behaviour::new(identity, vec![rendezvous_point])
}); });
asb.listen_on_random_memory_address().await; // this adds an external address asb.listen_on_random_memory_address().await; // this adds an external address
@ -467,5 +508,62 @@ pub mod rendezous {
.unwrap() .unwrap()
.unwrap(); .unwrap();
} }
#[tokio::test]
async fn asb_registers_multiple() {
let registration_ttl = Some(10);
let mut rendezvous_nodes = Vec::new();
let mut registrations = HashMap::new();
// register with 5 rendezvous nodes
for _ in 0..5 {
let mut rendezvous = new_swarm(|_, _| {
rendezvous::server::Behaviour::new(
rendezvous::server::Config::default().with_min_ttl(2),
)
});
let address = rendezvous.listen_on_random_memory_address().await;
let id = *rendezvous.local_peer_id();
registrations.insert(id, 0);
rendezvous_nodes.push(RendezvousNode::new(
&address,
*rendezvous.local_peer_id(),
XmrBtcNamespace::Testnet,
registration_ttl,
));
tokio::spawn(async move {
loop {
rendezvous.next().await;
}
});
}
let mut asb = new_swarm(|_, identity| {
super::rendezvous::Behaviour::new(identity, rendezvous_nodes)
});
asb.listen_on_random_memory_address().await; // this adds an external address
let handle = tokio::spawn(async move {
loop {
if let SwarmEvent::Behaviour(rendezvous::client::Event::Registered {
rendezvous_node,
..
}) = asb.select_next_some().await
{
registrations
.entry(rendezvous_node)
.and_modify(|counter| *counter += 1);
}
if registrations.iter().all(|(_, &count)| count >= 4) {
break;
}
}
});
tokio::time::timeout(Duration::from_secs(30), handle)
.await
.unwrap()
.unwrap();
}
} }
} }

View File

@ -102,6 +102,19 @@ async fn main() -> Result<()> {
match cmd { match cmd {
Command::Start { resume_only } => { Command::Start { resume_only } => {
// check and warn for duplicate rendezvous points
let mut rendezvous_addrs = config.network.rendezvous_point.clone();
let prev_len = rendezvous_addrs.len();
rendezvous_addrs.sort();
rendezvous_addrs.dedup();
let new_len = rendezvous_addrs.len();
if new_len < prev_len {
tracing::warn!(
"`rendezvous_point` config has {} duplicate entries, they are being ignored.",
prev_len - new_len
);
}
let monero_wallet = init_monero_wallet(&config, env_config).await?; let monero_wallet = init_monero_wallet(&config, env_config).await?;
let monero_address = monero_wallet.get_main_address(); let monero_address = monero_wallet.get_main_address();
tracing::info!(%monero_address, "Monero wallet address"); tracing::info!(%monero_address, "Monero wallet address");
@ -161,7 +174,7 @@ async fn main() -> Result<()> {
resume_only, resume_only,
env_config, env_config,
namespace, namespace,
config.network.rendezvous_point, &rendezvous_addrs,
)?; )?;
for listen in config.network.listen.clone() { for listen in config.network.listen.clone() {

View File

@ -210,14 +210,20 @@ impl TxCancel {
}; };
// The order in which these are inserted doesn't matter // The order in which these are inserted doesn't matter
satisfier.insert(A, ::bitcoin::EcdsaSig { satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(), sig: sig_a.into(),
hash_ty: EcdsaSighashType::All, hash_ty: EcdsaSighashType::All,
}); },
satisfier.insert(B, ::bitcoin::EcdsaSig { );
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(), sig: sig_b.into(),
hash_ty: EcdsaSighashType::All, hash_ty: EcdsaSighashType::All,
}); },
);
satisfier satisfier
}; };

View File

@ -65,14 +65,20 @@ impl TxPunish {
let B = B.try_into()?; let B = B.try_into()?;
// The order in which these are inserted doesn't matter // The order in which these are inserted doesn't matter
satisfier.insert(A, ::bitcoin::EcdsaSig { satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(), sig: sig_a.into(),
hash_ty: EcdsaSighashType::All, hash_ty: EcdsaSighashType::All,
}); },
satisfier.insert(B, ::bitcoin::EcdsaSig { );
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(), sig: sig_b.into(),
hash_ty: EcdsaSighashType::All, hash_ty: EcdsaSighashType::All,
}); },
);
satisfier satisfier
}; };

View File

@ -87,14 +87,20 @@ impl TxRedeem {
}; };
// The order in which these are inserted doesn't matter // The order in which these are inserted doesn't matter
satisfier.insert(A, ::bitcoin::EcdsaSig { satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(), sig: sig_a.into(),
hash_ty: EcdsaSighashType::All, hash_ty: EcdsaSighashType::All,
}); },
satisfier.insert(B, ::bitcoin::EcdsaSig { );
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(), sig: sig_b.into(),
hash_ty: EcdsaSighashType::All, hash_ty: EcdsaSighashType::All,
}); },
);
satisfier satisfier
}; };

View File

@ -70,14 +70,20 @@ impl TxRefund {
}; };
// The order in which these are inserted doesn't matter // The order in which these are inserted doesn't matter
satisfier.insert(A, ::bitcoin::EcdsaSig { satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(), sig: sig_a.into(),
hash_ty: EcdsaSighashType::All, hash_ty: EcdsaSighashType::All,
}); },
satisfier.insert(B, ::bitcoin::EcdsaSig { );
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(), sig: sig_b.into(),
hash_ty: EcdsaSighashType::All, hash_ty: EcdsaSighashType::All,
}); },
);
satisfier satisfier
}; };

View File

@ -738,12 +738,15 @@ impl Client {
let client = bdk::electrum_client::Client::new(electrum_rpc_url.as_str()) let client = bdk::electrum_client::Client::new(electrum_rpc_url.as_str())
.context("Failed to initialize Electrum RPC client")?; .context("Failed to initialize Electrum RPC client")?;
let blockchain = ElectrumBlockchain::from(client); let blockchain = ElectrumBlockchain::from(client);
let last_sync = Instant::now()
.checked_sub(interval)
.expect("no underflow since block time is only 600 secs");
Ok(Self { Ok(Self {
electrum, electrum,
blockchain, blockchain,
latest_block_height: BlockHeight::try_from(latest_block)?, latest_block_height: BlockHeight::try_from(latest_block)?,
last_sync: Instant::now() - interval, last_sync,
sync_interval: interval, sync_interval: interval,
script_history: Default::default(), script_history: Default::default(),
subscriptions: Default::default(), subscriptions: Default::default(),

View File

@ -15,6 +15,7 @@ pub use list_sellers::{list_sellers, Seller, Status as SellerStatus};
mod tests { mod tests {
use super::*; use super::*;
use crate::asb; use crate::asb;
use crate::asb::rendezvous::RendezvousNode;
use crate::cli::list_sellers::{Seller, Status}; use crate::cli::list_sellers::{Seller, Status};
use crate::network::quote; use crate::network::quote;
use crate::network::quote::BidQuote; use crate::network::quote::BidQuote;
@ -33,10 +34,8 @@ mod tests {
async fn list_sellers_should_report_all_registered_asbs_with_a_quote() { async fn list_sellers_should_report_all_registered_asbs_with_a_quote() {
let namespace = XmrBtcNamespace::Mainnet; let namespace = XmrBtcNamespace::Mainnet;
let (rendezvous_address, rendezvous_peer_id) = setup_rendezvous_point().await; let (rendezvous_address, rendezvous_peer_id) = setup_rendezvous_point().await;
let expected_seller_1 = let expected_seller_1 = setup_asb(rendezvous_peer_id, &rendezvous_address, namespace).await;
setup_asb(rendezvous_peer_id, rendezvous_address.clone(), namespace).await; let expected_seller_2 = setup_asb(rendezvous_peer_id, &rendezvous_address, namespace).await;
let expected_seller_2 =
setup_asb(rendezvous_peer_id, rendezvous_address.clone(), namespace).await;
let list_sellers = list_sellers( let list_sellers = list_sellers(
rendezvous_peer_id, rendezvous_peer_id,
@ -72,7 +71,7 @@ mod tests {
async fn setup_asb( async fn setup_asb(
rendezvous_peer_id: PeerId, rendezvous_peer_id: PeerId,
rendezvous_address: Multiaddr, rendezvous_address: &Multiaddr,
namespace: XmrBtcNamespace, namespace: XmrBtcNamespace,
) -> Seller { ) -> Seller {
let static_quote = BidQuote { let static_quote = BidQuote {
@ -81,18 +80,18 @@ mod tests {
max_quantity: bitcoin::Amount::from_sat(9001), max_quantity: bitcoin::Amount::from_sat(9001),
}; };
let mut asb = new_swarm(|_, identity| StaticQuoteAsbBehaviour { let mut asb = new_swarm(|_, identity| {
rendezvous: asb::rendezous::Behaviour::new( let rendezvous_node =
identity, RendezvousNode::new(rendezvous_address, rendezvous_peer_id, namespace, None);
rendezvous_peer_id, let rendezvous = asb::rendezvous::Behaviour::new(identity, vec![rendezvous_node]);
rendezvous_address,
namespace, StaticQuoteAsbBehaviour {
None, rendezvous,
),
ping: Default::default(), ping: Default::default(),
quote: quote::asb(), quote: quote::asb(),
static_quote, static_quote,
registered: false, registered: false,
}
}); });
let asb_address = asb.listen_on_tcp_localhost().await; let asb_address = asb.listen_on_tcp_localhost().await;
@ -121,7 +120,7 @@ mod tests {
#[derive(libp2p::NetworkBehaviour)] #[derive(libp2p::NetworkBehaviour)]
#[behaviour(event_process = true)] #[behaviour(event_process = true)]
struct StaticQuoteAsbBehaviour { struct StaticQuoteAsbBehaviour {
rendezvous: asb::rendezous::Behaviour, rendezvous: asb::rendezvous::Behaviour,
// Support `Ping` as a workaround until https://github.com/libp2p/rust-libp2p/issues/2109 is fixed. // Support `Ping` as a workaround until https://github.com/libp2p/rust-libp2p/issues/2109 is fixed.
ping: libp2p::ping::Ping, ping: libp2p::ping::Ping,
quote: quote::Behaviour, quote: quote::Behaviour,

View File

@ -350,7 +350,9 @@ mod tests {
list.sort(); list.sort();
assert_eq!(list, vec![ assert_eq!(
list,
vec![
Seller { Seller {
multiaddr: "/ip4/127.0.0.1/tcp/5678".parse().unwrap(), multiaddr: "/ip4/127.0.0.1/tcp/5678".parse().unwrap(),
status: Status::Online(BidQuote { status: Status::Online(BidQuote {
@ -367,6 +369,7 @@ mod tests {
multiaddr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(), multiaddr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(),
status: Status::Unreachable status: Status::Unreachable
}, },
]) ]
)
} }
} }

View File

@ -155,13 +155,16 @@ impl ProtocolsHandler for Handler {
let env_config = self.env_config; let env_config = self.env_config;
let protocol = tokio::time::timeout(self.timeout, async move { let protocol = tokio::time::timeout(self.timeout, async move {
write_cbor_message(&mut substream, SpotPriceRequest { write_cbor_message(
&mut substream,
SpotPriceRequest {
btc: info.btc, btc: info.btc,
blockchain_network: BlockchainNetwork { blockchain_network: BlockchainNetwork {
bitcoin: env_config.bitcoin_network, bitcoin: env_config.bitcoin_network,
monero: env_config.monero_network, monero: env_config.monero_network,
}, },
}) },
)
.await?; .await?;
let xmr = Result::from(read_cbor_message::<SpotPriceResponse>(&mut substream).await?)?; let xmr = Result::from(read_cbor_message::<SpotPriceResponse>(&mut substream).await?)?;

View File

@ -1,9 +1,9 @@
use crate::asb::LatestRate; use crate::asb::{LatestRate, RendezvousNode};
use crate::libp2p_ext::MultiAddrExt; use crate::libp2p_ext::MultiAddrExt;
use crate::network::rendezvous::XmrBtcNamespace; use crate::network::rendezvous::XmrBtcNamespace;
use crate::seed::Seed; use crate::seed::Seed;
use crate::{asb, bitcoin, cli, env, tor}; use crate::{asb, bitcoin, cli, env, tor};
use anyhow::{Context, Result}; use anyhow::Result;
use libp2p::swarm::{NetworkBehaviour, SwarmBuilder}; use libp2p::swarm::{NetworkBehaviour, SwarmBuilder};
use libp2p::{identity, Multiaddr, Swarm}; use libp2p::{identity, Multiaddr, Swarm};
use std::fmt::Debug; use std::fmt::Debug;
@ -17,22 +17,23 @@ pub fn asb<LR>(
resume_only: bool, resume_only: bool,
env_config: env::Config, env_config: env::Config,
namespace: XmrBtcNamespace, namespace: XmrBtcNamespace,
rendezvous_point: Option<Multiaddr>, rendezvous_addrs: &[Multiaddr],
) -> Result<Swarm<asb::Behaviour<LR>>> ) -> Result<Swarm<asb::Behaviour<LR>>>
where where
LR: LatestRate + Send + 'static + Debug + Clone, LR: LatestRate + Send + 'static + Debug + Clone,
{ {
let identity = seed.derive_libp2p_identity(); let identity = seed.derive_libp2p_identity();
let rendezvous_params = if let Some(address) = rendezvous_point { let rendezvous_nodes = rendezvous_addrs
let peer_id = address .iter()
.map(|addr| {
let peer_id = addr
.extract_peer_id() .extract_peer_id()
.context("Rendezvous node address must contain peer ID")?; .expect("Rendezvous node address must contain peer ID");
Some((identity.clone(), peer_id, address, namespace)) RendezvousNode::new(addr, peer_id, namespace, None)
} else { })
None .collect();
};
let behaviour = asb::Behaviour::new( let behaviour = asb::Behaviour::new(
min_buy, min_buy,
@ -41,7 +42,7 @@ where
resume_only, resume_only,
env_config, env_config,
(identity.clone(), namespace), (identity.clone(), namespace),
rendezvous_params, rendezvous_nodes,
); );
let transport = asb::transport::new(&identity)?; let transport = asb::transport::new(&identity)?;

View File

@ -21,7 +21,7 @@ struct GlobalSpawnTokioExecutor;
impl Executor for GlobalSpawnTokioExecutor { impl Executor for GlobalSpawnTokioExecutor {
fn exec(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>) { fn exec(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>) {
let _ = tokio::spawn(future); tokio::spawn(future);
} }
} }

View File

@ -184,7 +184,9 @@ impl State0 {
let v = self.v_a + msg.v_b; let v = self.v_a + msg.v_b;
Ok((msg.swap_id, State1 { Ok((
msg.swap_id,
State1 {
a: self.a, a: self.a,
B: msg.B, B: msg.B,
s_a: self.s_a, s_a: self.s_a,
@ -206,7 +208,8 @@ impl State0 {
tx_punish_fee: self.tx_punish_fee, tx_punish_fee: self.tx_punish_fee,
tx_refund_fee: msg.tx_refund_fee, tx_refund_fee: msg.tx_refund_fee,
tx_cancel_fee: msg.tx_cancel_fee, tx_cancel_fee: msg.tx_cancel_fee,
})) },
))
} }
} }

View File

@ -187,6 +187,9 @@ mod tests {
#[test] #[test]
fn seed_from_pem_works() { fn seed_from_pem_works() {
use base64::engine::general_purpose;
use base64::Engine;
let payload: &str = "syl9wSYaruvgxg9P5Q1qkZaq5YkM6GvXkxe+VYrL/XM="; let payload: &str = "syl9wSYaruvgxg9P5Q1qkZaq5YkM6GvXkxe+VYrL/XM=";
// 32 bytes base64 encoded. // 32 bytes base64 encoded.
@ -195,7 +198,7 @@ syl9wSYaruvgxg9P5Q1qkZaq5YkM6GvXkxe+VYrL/XM=
-----END SEED----- -----END SEED-----
"; ";
let want = base64::decode(payload).unwrap(); let want = general_purpose::STANDARD.decode(payload).unwrap();
let pem = pem::parse(pem_string).unwrap(); let pem = pem::parse(pem_string).unwrap();
let got = Seed::from_pem(pem).unwrap(); let got = Seed::from_pem(pem).unwrap();

View File

@ -8,7 +8,7 @@ async fn ensure_same_swap_id_for_alice_and_bob() {
harness::setup_test(SlowCancelConfig, |mut ctx| async move { harness::setup_test(SlowCancelConfig, |mut ctx| async move {
let (bob_swap, _) = ctx.bob_swap().await; let (bob_swap, _) = ctx.bob_swap().await;
let bob_swap_id = bob_swap.id; let bob_swap_id = bob_swap.id;
let _ = tokio::spawn(bob::run(bob_swap)); tokio::spawn(bob::run(bob_swap));
// once Bob's swap is spawned we can retrieve Alice's swap and assert on the // once Bob's swap is spawned we can retrieve Alice's swap and assert on the
// swap ID // swap ID

View File

@ -158,13 +158,16 @@ async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) {
.await .await
.unwrap(); .unwrap();
(monero, Containers { (
monero,
Containers {
bitcoind_url, bitcoind_url,
bitcoind, bitcoind,
monerod_container, monerod_container,
monero_wallet_rpc_containers, monero_wallet_rpc_containers,
electrs, electrs,
}) },
)
} }
async fn init_bitcoind_container( async fn init_bitcoind_container(
@ -245,7 +248,7 @@ async fn start_alice(
resume_only, resume_only,
env_config, env_config,
XmrBtcNamespace::Testnet, XmrBtcNamespace::Testnet,
None, &[],
) )
.unwrap(); .unwrap();
swarm.listen_on(listen_address).unwrap(); swarm.listen_on(listen_address).unwrap();
@ -925,7 +928,7 @@ async fn init_bitcoind(node_url: Url, spendable_quantity: u32) -> Result<Client>
bitcoind_client bitcoind_client
.generatetoaddress(101 + spendable_quantity, reward_address.clone()) .generatetoaddress(101 + spendable_quantity, reward_address.clone())
.await?; .await?;
let _ = tokio::spawn(mine(bitcoind_client.clone(), reward_address)); tokio::spawn(mine(bitcoind_client.clone(), reward_address));
Ok(bitcoind_client) Ok(bitcoind_client)
} }