From 8a30ef725c3084d010430305f3ee6f0b3f714bfa Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 9 Jun 2021 12:13:29 +1000 Subject: [PATCH] Refactor transports to construct them specific for each application Instead of splitting up the transports into capabilities, we compose them directly for each application. This allows us to remove the websocket transport for the CLI which is really only needed for the ASB to allow retrieval of quotes via the browser. --- CHANGELOG.md | 6 +++ swap/src/asb.rs | 1 + swap/src/asb/transport.rs | 19 +++++++++ swap/src/cli.rs | 1 + swap/src/cli/transport.rs | 32 ++++++++++++++ swap/src/network/swarm.rs | 77 +++++++++++++--------------------- swap/src/network/transport.rs | 79 ++++++++++------------------------- 7 files changed, 111 insertions(+), 104 deletions(-) create mode 100644 swap/src/asb/transport.rs create mode 100644 swap/src/cli/transport.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 03c055b9..39f1d5fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Printing the deposit address to the terminal as a QR code. To not break automated scripts or integrations with other software, this behaviour is disabled if `--json` is passed to the application. +### Removed + +- The websocket transport from the CLI. + Websockets were only ever intended to be used for the ASB side to allow websites to retrieve quotes. + The CLI can use regular TCP connections and having both - TCP and websockets - causes problems and unnecessary overhead. + ## [0.7.0] - 2021-05-28 ### Fixed diff --git a/swap/src/asb.rs b/swap/src/asb.rs index 222046c1..47ceb072 100644 --- a/swap/src/asb.rs +++ b/swap/src/asb.rs @@ -2,5 +2,6 @@ pub mod command; pub mod config; mod rate; pub mod tracing; +pub mod transport; pub use rate::Rate; diff --git a/swap/src/asb/transport.rs b/swap/src/asb/transport.rs new file mode 100644 index 00000000..a552a8f0 --- /dev/null +++ b/swap/src/asb/transport.rs @@ -0,0 +1,19 @@ +use crate::network::transport::authenticate_and_multiplex; +use anyhow::Result; +use libp2p::core::muxing::StreamMuxerBox; +use libp2p::core::transport::Boxed; +use libp2p::dns::TokioDnsConfig; +use libp2p::tcp::TokioTcpConfig; +use libp2p::websocket::WsConfig; +use libp2p::{identity, PeerId, Transport}; + +/// Creates the libp2p transport for the ASB. +pub fn new(identity: &identity::Keypair) -> Result> { + let tcp = TokioTcpConfig::new().nodelay(true); + let tcp_with_dns = TokioDnsConfig::system(tcp)?; + let websocket_with_dns = WsConfig::new(tcp_with_dns.clone()); + + let transport = tcp_with_dns.or_transport(websocket_with_dns).boxed(); + + authenticate_and_multiplex(transport, identity) +} diff --git a/swap/src/cli.rs b/swap/src/cli.rs index e28b6580..7962efd8 100644 --- a/swap/src/cli.rs +++ b/swap/src/cli.rs @@ -1,2 +1,3 @@ pub mod command; pub mod tracing; +pub mod transport; diff --git a/swap/src/cli/transport.rs b/swap/src/cli/transport.rs new file mode 100644 index 00000000..68a5cd1a --- /dev/null +++ b/swap/src/cli/transport.rs @@ -0,0 +1,32 @@ +use crate::network::tor_transport::TorDialOnlyTransport; +use crate::network::transport::authenticate_and_multiplex; +use anyhow::Result; +use libp2p::core::muxing::StreamMuxerBox; +use libp2p::core::transport::{Boxed, OptionalTransport}; +use libp2p::dns::TokioDnsConfig; +use libp2p::tcp::TokioTcpConfig; +use libp2p::{identity, PeerId, Transport}; + +/// Creates the libp2p transport for the swap CLI. +/// +/// The CLI's transport needs the following capabilities: +/// - Establish TCP connections +/// - Resolve DNS entries +/// - Dial onion-addresses through a running Tor daemon by connecting to the +/// socks5 port. If the port is not given, we will fall back to the regular +/// TCP transport. +pub fn new( + identity: &identity::Keypair, + maybe_tor_socks5_port: Option, +) -> Result> { + let tcp = TokioTcpConfig::new().nodelay(true); + let tcp_with_dns = TokioDnsConfig::system(tcp)?; + let maybe_tor_transport = match maybe_tor_socks5_port { + Some(port) => OptionalTransport::some(TorDialOnlyTransport::new(port)), + None => OptionalTransport::none(), + }; + + let transport = maybe_tor_transport.or_transport(tcp_with_dns).boxed(); + + authenticate_and_multiplex(transport, identity) +} diff --git a/swap/src/network/swarm.rs b/swap/src/network/swarm.rs index 2e2a8e1d..37e9f219 100644 --- a/swap/src/network/swarm.rs +++ b/swap/src/network/swarm.rs @@ -1,10 +1,9 @@ -use crate::network::transport; use crate::protocol::alice::event_loop::LatestRate; use crate::protocol::{alice, bob}; use crate::seed::Seed; -use crate::{env, monero, tor}; +use crate::{asb, cli, env, monero, tor}; use anyhow::Result; -use libp2p::swarm::{NetworkBehaviour, SwarmBuilder}; +use libp2p::swarm::SwarmBuilder; use libp2p::{PeerId, Swarm}; use std::fmt::Debug; @@ -22,18 +21,27 @@ pub fn asb( where LR: LatestRate + Send + 'static + Debug, { - with_clear_net( - seed, - alice::Behaviour::new( - balance, - lock_fee, - min_buy, - max_buy, - latest_rate, - resume_only, - env_config, - ), - ) + let behaviour = alice::Behaviour::new( + balance, + lock_fee, + min_buy, + max_buy, + latest_rate, + resume_only, + env_config, + ); + + let identity = seed.derive_libp2p_identity(); + let transport = asb::transport::new(&identity)?; + let peer_id = identity.public().into_peer_id(); + + let swarm = SwarmBuilder::new(transport, behaviour, peer_id) + .executor(Box::new(|f| { + tokio::spawn(f); + })) + .build(); + + Ok(swarm) } pub async fn cli( @@ -41,41 +49,16 @@ pub async fn cli( alice: PeerId, tor_socks5_port: u16, ) -> Result> { - let client = tor::Client::new(tor_socks5_port); - if client.assert_tor_running().await.is_ok() { - return with_tor(seed, bob::Behaviour::new(alice), tor_socks5_port).await; - } - with_clear_net(seed, bob::Behaviour::new(alice)) -} + let maybe_tor_socks5_port = match tor::Client::new(tor_socks5_port).assert_tor_running().await { + Ok(()) => Some(tor_socks5_port), + Err(_) => None, + }; + + let behaviour = bob::Behaviour::new(alice); -fn with_clear_net(seed: &Seed, behaviour: B) -> Result> -where - B: NetworkBehaviour, -{ - tracing::info!("All connections will go through clear net"); let identity = seed.derive_libp2p_identity(); - let transport = transport::build_clear_net(&identity)?; + let transport = cli::transport::new(&identity, maybe_tor_socks5_port)?; let peer_id = identity.public().into_peer_id(); - tracing::debug!(%peer_id, "Our peer-id"); - - let swarm = SwarmBuilder::new(transport, behaviour, peer_id) - .executor(Box::new(|f| { - tokio::spawn(f); - })) - .build(); - - Ok(swarm) -} - -async fn with_tor(seed: &Seed, behaviour: B, tor_socks5_port: u16) -> Result> -where - B: NetworkBehaviour, -{ - tracing::info!("All connections will go through Tor socks5 proxy"); - let identity = seed.derive_libp2p_identity(); - let transport = transport::build_tor(&identity, tor_socks5_port)?; - let peer_id = identity.public().into_peer_id(); - tracing::debug!(%peer_id, "Our peer-id"); let swarm = SwarmBuilder::new(transport, behaviour, peer_id) .executor(Box::new(|f| { diff --git a/swap/src/network/transport.rs b/swap/src/network/transport.rs index c4fa929d..35ac3e8c 100644 --- a/swap/src/network/transport.rs +++ b/swap/src/network/transport.rs @@ -1,74 +1,39 @@ -use crate::network::tor_transport::TorDialOnlyTransport; use anyhow::Result; +use futures::{AsyncRead, AsyncWrite}; use libp2p::core::muxing::StreamMuxerBox; use libp2p::core::transport::Boxed; use libp2p::core::upgrade::{SelectUpgrade, Version}; -use libp2p::dns::TokioDnsConfig; use libp2p::mplex::MplexConfig; use libp2p::noise::{self, NoiseConfig, X25519Spec}; -use libp2p::tcp::TokioTcpConfig; -use libp2p::websocket::WsConfig; use libp2p::{identity, yamux, PeerId, Transport}; 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 -pub fn build_clear_net(id_keys: &identity::Keypair) -> Result { - let dh_keys = noise::Keypair::::new().into_authentic(id_keys)?; - let noise = NoiseConfig::xx(dh_keys).into_authenticated(); +/// "Completes" a transport by applying the authentication and multiplexing +/// upgrades. +/// +/// Even though the actual transport technology in use might be different, for +/// two libp2p applications to be compatible, the authentication and +/// multiplexing upgrades need to be compatible. +pub fn authenticate_and_multiplex( + transport: Boxed, + identity: &identity::Keypair, +) -> Result> +where + T: AsyncRead + AsyncWrite + Unpin + Send + 'static, +{ + let auth_upgrade = { + let noise_identity = noise::Keypair::::new().into_authentic(identity)?; + NoiseConfig::xx(noise_identity).into_authenticated() + }; + let multiplex_upgrade = SelectUpgrade::new(yamux::YamuxConfig::default(), MplexConfig::new()); - let tcp = TokioTcpConfig::new().nodelay(true); - let dns = TokioDnsConfig::system(tcp)?; - let websocket = WsConfig::new(dns.clone()); - - let transport = websocket - .or_transport(dns) + let transport = transport .upgrade(Version::V1) - .authenticate(noise) - .multiplex(SelectUpgrade::new( - yamux::YamuxConfig::default(), - MplexConfig::new(), - )) + .authenticate(auth_upgrade) + .multiplex(multiplex_upgrade) .timeout(Duration::from_secs(20)) .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) .boxed(); Ok(transport) } - -/// Builds a libp2p transport with the following features: -/// - TorTcpConnection -/// - WebSocketConnection -/// - DNS name resolution -/// - authentication via noise -/// - multiplexing via yamux or mplex -pub fn build_tor(id_keys: &identity::Keypair, tor_socks5_port: u16) -> Result { - let dh_keys = noise::Keypair::::new().into_authentic(id_keys)?; - let noise = NoiseConfig::xx(dh_keys).into_authenticated(); - - let tcp = TokioTcpConfig::new().nodelay(true); - let tcp_with_dns = TokioDnsConfig::system(tcp)?; - let websocket_with_dns = WsConfig::new(tcp_with_dns.clone()); - let tor_dial_only = TorDialOnlyTransport::new(tor_socks5_port); - - let transport = tor_dial_only - .or_transport(tcp_with_dns) - .or_transport(websocket_with_dns) - .upgrade(Version::V1) - .authenticate(noise) - .multiplex(SelectUpgrade::new( - yamux::YamuxConfig::default(), - MplexConfig::new(), - )) - .timeout(Duration::from_secs(20)) - .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) - .boxed(); - - Ok(transport) -} - -pub type SwapTransport = Boxed<(PeerId, StreamMuxerBox)>;