diff --git a/CHANGELOG.md b/CHANGELOG.md index fe44ac8e..4185b8fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 This is a breaking change because the swap ID is now agreed upon between CLI and ASB during swap setup. Resuming swaps started prior to this change can result in unexpected behaviour. +### Added + +- Websocket support for the ASB. + The ASB is now capable to listen on both TCP and Websocket connections. + Default websocket listening port is 9940. + ## [0.4.0] - 2021-04-06 ### Changed diff --git a/Cargo.lock b/Cargo.lock index fec0d11f..f4e41743 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1059,6 +1059,7 @@ dependencies = [ "cfg-if 0.1.10", "crc32fast", "libc", + "libz-sys", "miniz_oxide", ] @@ -1155,6 +1156,17 @@ dependencies = [ "syn", ] +[[package]] +name = "futures-rustls" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a1387e07917c711fb4ee4f48ea0adb04a3c9739e53ef85bf43ae1edc2937a8b" +dependencies = [ + "futures-io", + "rustls 0.19.0", + "webpki", +] + [[package]] name = "futures-sink" version = "0.3.14" @@ -1705,6 +1717,7 @@ dependencies = [ "libp2p-swarm", "libp2p-swarm-derive", "libp2p-tcp", + "libp2p-websocket", "libp2p-yamux", "parity-multiaddr", "parking_lot 0.11.1", @@ -1872,6 +1885,24 @@ dependencies = [ "tokio 1.5.0", ] +[[package]] +name = "libp2p-websocket" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cace60995ef6f637e4752cccbb2590f6bc358e8741a0d066307636c69a4b3a74" +dependencies = [ + "either", + "futures", + "futures-rustls", + "libp2p-core", + "log 0.4.14", + "quicksink", + "rw-stream-sink", + "soketto", + "url 2.2.1", + "webpki-roots 0.21.0", +] + [[package]] name = "libp2p-yamux" version = "0.31.0" @@ -1901,6 +1932,18 @@ dependencies = [ "typenum", ] +[[package]] +name = "libz-sys" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linked-hash-map" version = "0.5.4" @@ -2643,6 +2686,17 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quicksink" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77de3c815e5a160b1539c6592796801df2043ae35e123b46d73380cfa57af858" +dependencies = [ + "futures-core", + "futures-sink", + "pin-project-lite 0.1.12", +] + [[package]] name = "quote" version = "1.0.9" @@ -3415,6 +3469,22 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "soketto" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5c71ed3d54db0a699f4948e1bb3e45b450fa31fe602621dee6680361d569c88" +dependencies = [ + "base64 0.12.3", + "bytes 0.5.6", + "flate2", + "futures", + "httparse", + "log 0.4.14", + "rand 0.7.3", + "sha-1", +] + [[package]] name = "spectral" version = "0.6.0" @@ -4265,6 +4335,12 @@ dependencies = [ "serde", ] +[[package]] +name = "vcpkg" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" + [[package]] name = "vec_map" version = "0.8.2" diff --git a/swap/Cargo.toml b/swap/Cargo.toml index d7aa117d..e1472217 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -27,7 +27,7 @@ directories-next = "2" ecdsa_fun = { git = "https://github.com/LLFourn/secp256kfun", features = ["libsecp_compat", "serde"] } futures = { version = "0.3", default-features = false } itertools = "0.10" -libp2p = { version = "0.36", default-features = false, features = ["tcp-tokio", "yamux", "mplex", "dns-tokio", "noise", "request-response"] } +libp2p = { version = "0.36", default-features = false, features = ["tcp-tokio", "yamux", "mplex", "dns-tokio", "noise", "request-response", "websocket"] } libp2p-async-await = { git = "https://github.com/comit-network/rust-libp2p-async-await" } miniscript = { version = "5", features = ["serde"] } monero = { version = "0.11", features = ["serde_support"] } diff --git a/swap/src/asb/config.rs b/swap/src/asb/config.rs index 03a94f7e..25aec58a 100644 --- a/swap/src/asb/config.rs +++ b/swap/src/asb/config.rs @@ -11,7 +11,8 @@ use std::path::{Path, PathBuf}; use tracing::info; use url::Url; -const DEFAULT_LISTEN_ADDRESS: &str = "/ip4/0.0.0.0/tcp/9939"; +const DEFAULT_LISTEN_ADDRESS_TCP: &str = "/ip4/0.0.0.0/tcp/9939"; +const DEFAULT_LISTEN_ADDRESS_WS: &str = "/ip4/0.0.0.0/tcp/9940/ws"; const DEFAULT_ELECTRUM_RPC_URL: &str = "ssl://electrum.blockstream.info:60002"; const DEFAULT_MONERO_WALLET_RPC_TESTNET_URL: &str = "http://127.0.0.1:38083/json_rpc"; @@ -45,7 +46,7 @@ pub struct Data { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub struct Network { - pub listen: Multiaddr, + pub listen: Vec, } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] @@ -112,11 +113,14 @@ pub fn query_user_for_initial_testnet_config() -> Result { .interact_text()?; let data_dir = data_dir.as_str().parse()?; - let listen_address = Input::with_theme(&ColorfulTheme::default()) - .with_prompt("Enter multiaddress on which asb should list for peer-to-peer communications or hit return to use default") - .default(DEFAULT_LISTEN_ADDRESS.to_owned()) + let listen_addresses = Input::with_theme(&ColorfulTheme::default()) + .with_prompt("Enter multiaddresses (comma separated) on which asb should list for peer-to-peer communications or hit return to use default") + .default( format!("{},{}", DEFAULT_LISTEN_ADDRESS_TCP, DEFAULT_LISTEN_ADDRESS_WS)) .interact_text()?; - let listen_address = listen_address.as_str().parse()?; + let listen_addresses = listen_addresses + .split(',') + .map(|str| str.parse()) + .collect::, _>>()?; let electrum_rpc_url: String = Input::with_theme(&ColorfulTheme::default()) .with_prompt("Enter Electrum RPC URL or hit return to use default") @@ -134,7 +138,7 @@ pub fn query_user_for_initial_testnet_config() -> Result { Ok(Config { data: Data { dir: data_dir }, network: Network { - listen: listen_address, + listen: listen_addresses, }, bitcoin: Bitcoin { electrum_rpc_url }, monero: Monero { @@ -162,7 +166,10 @@ mod tests { electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL).unwrap(), }, network: Network { - listen: DEFAULT_LISTEN_ADDRESS.parse().unwrap(), + listen: vec![ + DEFAULT_LISTEN_ADDRESS_TCP.parse().unwrap(), + DEFAULT_LISTEN_ADDRESS_WS.parse().unwrap(), + ], }, monero: Monero { diff --git a/swap/src/bin/asb.rs b/swap/src/bin/asb.rs index eee8951f..45e25ff1 100644 --- a/swap/src/bin/asb.rs +++ b/swap/src/bin/asb.rs @@ -98,8 +98,11 @@ async fn main() -> Result<()> { let kraken_price_updates = kraken::connect()?; let mut swarm = swarm::alice(&seed)?; - Swarm::listen_on(&mut swarm, config.network.listen) - .context("Failed to listen network interface")?; + + for listen in config.network.listen { + Swarm::listen_on(&mut swarm, listen.clone()) + .with_context(|| format!("Failed to listen on network interface {}", listen))?; + } let (event_loop, mut swap_receiver) = EventLoop::new( swarm, diff --git a/swap/src/network/transport.rs b/swap/src/network/transport.rs index f07e612b..eec3dadb 100644 --- a/swap/src/network/transport.rs +++ b/swap/src/network/transport.rs @@ -6,11 +6,13 @@ use libp2p::core::{identity, Transport}; use libp2p::dns::TokioDnsConfig; use libp2p::mplex::MplexConfig; use libp2p::noise::{self, NoiseConfig, X25519Spec}; +use libp2p::websocket::WsConfig; use libp2p::{yamux, PeerId}; use std::time::Duration; /// Builds a libp2p transport with the following features: /// - TcpConnection +/// - WebSocketConnection /// - DNS name resolution /// - authentication via noise /// - multiplexing via yamux or mplex @@ -22,8 +24,10 @@ pub fn build(id_keys: &identity::Keypair) -> Result { let tcp = TokioTcpConfig::new().nodelay(true); let dns = TokioDnsConfig::system(tcp)?; + let websocket = WsConfig::new(dns.clone()); - let transport = dns + let transport = websocket + .or_transport(dns) .upgrade(Version::V1) .authenticate(noise) .multiplex(SelectUpgrade::new(