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.
This commit is contained in:
Thomas Eizinger 2021-06-09 12:13:29 +10:00
parent 90deb6451c
commit 8a30ef725c
No known key found for this signature in database
GPG Key ID: 651AC83A6C6C8B96
7 changed files with 111 additions and 104 deletions

View File

@ -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. - 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. 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 ## [0.7.0] - 2021-05-28
### Fixed ### Fixed

View File

@ -2,5 +2,6 @@ pub mod command;
pub mod config; pub mod config;
mod rate; mod rate;
pub mod tracing; pub mod tracing;
pub mod transport;
pub use rate::Rate; pub use rate::Rate;

19
swap/src/asb/transport.rs Normal file
View File

@ -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<Boxed<(PeerId, StreamMuxerBox)>> {
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)
}

View File

@ -1,2 +1,3 @@
pub mod command; pub mod command;
pub mod tracing; pub mod tracing;
pub mod transport;

32
swap/src/cli/transport.rs Normal file
View File

@ -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<u16>,
) -> Result<Boxed<(PeerId, StreamMuxerBox)>> {
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)
}

View File

@ -1,10 +1,9 @@
use crate::network::transport;
use crate::protocol::alice::event_loop::LatestRate; use crate::protocol::alice::event_loop::LatestRate;
use crate::protocol::{alice, bob}; use crate::protocol::{alice, bob};
use crate::seed::Seed; use crate::seed::Seed;
use crate::{env, monero, tor}; use crate::{asb, cli, env, monero, tor};
use anyhow::Result; use anyhow::Result;
use libp2p::swarm::{NetworkBehaviour, SwarmBuilder}; use libp2p::swarm::SwarmBuilder;
use libp2p::{PeerId, Swarm}; use libp2p::{PeerId, Swarm};
use std::fmt::Debug; use std::fmt::Debug;
@ -22,18 +21,27 @@ pub fn asb<LR>(
where where
LR: LatestRate + Send + 'static + Debug, LR: LatestRate + Send + 'static + Debug,
{ {
with_clear_net( let behaviour = alice::Behaviour::new(
seed, balance,
alice::Behaviour::new( lock_fee,
balance, min_buy,
lock_fee, max_buy,
min_buy, latest_rate,
max_buy, resume_only,
latest_rate, env_config,
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( pub async fn cli(
@ -41,41 +49,16 @@ pub async fn cli(
alice: PeerId, alice: PeerId,
tor_socks5_port: u16, tor_socks5_port: u16,
) -> Result<Swarm<bob::Behaviour>> { ) -> Result<Swarm<bob::Behaviour>> {
let client = tor::Client::new(tor_socks5_port); let maybe_tor_socks5_port = match tor::Client::new(tor_socks5_port).assert_tor_running().await {
if client.assert_tor_running().await.is_ok() { Ok(()) => Some(tor_socks5_port),
return with_tor(seed, bob::Behaviour::new(alice), tor_socks5_port).await; Err(_) => None,
} };
with_clear_net(seed, bob::Behaviour::new(alice))
} let behaviour = bob::Behaviour::new(alice);
fn with_clear_net<B>(seed: &Seed, behaviour: B) -> Result<Swarm<B>>
where
B: NetworkBehaviour,
{
tracing::info!("All connections will go through clear net");
let identity = seed.derive_libp2p_identity(); 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(); 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<B>(seed: &Seed, behaviour: B, tor_socks5_port: u16) -> Result<Swarm<B>>
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) let swarm = SwarmBuilder::new(transport, behaviour, peer_id)
.executor(Box::new(|f| { .executor(Box::new(|f| {

View File

@ -1,74 +1,39 @@
use crate::network::tor_transport::TorDialOnlyTransport;
use anyhow::Result; use anyhow::Result;
use futures::{AsyncRead, AsyncWrite};
use libp2p::core::muxing::StreamMuxerBox; use libp2p::core::muxing::StreamMuxerBox;
use libp2p::core::transport::Boxed; use libp2p::core::transport::Boxed;
use libp2p::core::upgrade::{SelectUpgrade, Version}; use libp2p::core::upgrade::{SelectUpgrade, Version};
use libp2p::dns::TokioDnsConfig;
use libp2p::mplex::MplexConfig; use libp2p::mplex::MplexConfig;
use libp2p::noise::{self, NoiseConfig, X25519Spec}; use libp2p::noise::{self, NoiseConfig, X25519Spec};
use libp2p::tcp::TokioTcpConfig;
use libp2p::websocket::WsConfig;
use libp2p::{identity, yamux, PeerId, Transport}; use libp2p::{identity, yamux, PeerId, Transport};
use std::time::Duration; use std::time::Duration;
/// Builds a libp2p transport with the following features: /// "Completes" a transport by applying the authentication and multiplexing
/// - TcpConnection /// upgrades.
/// - WebSocketConnection ///
/// - DNS name resolution /// Even though the actual transport technology in use might be different, for
/// - authentication via noise /// two libp2p applications to be compatible, the authentication and
/// - multiplexing via yamux or mplex /// multiplexing upgrades need to be compatible.
pub fn build_clear_net(id_keys: &identity::Keypair) -> Result<SwapTransport> { pub fn authenticate_and_multiplex<T>(
let dh_keys = noise::Keypair::<X25519Spec>::new().into_authentic(id_keys)?; transport: Boxed<T>,
let noise = NoiseConfig::xx(dh_keys).into_authenticated(); identity: &identity::Keypair,
) -> Result<Boxed<(PeerId, StreamMuxerBox)>>
where
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
let auth_upgrade = {
let noise_identity = noise::Keypair::<X25519Spec>::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 transport = transport
let dns = TokioDnsConfig::system(tcp)?;
let websocket = WsConfig::new(dns.clone());
let transport = websocket
.or_transport(dns)
.upgrade(Version::V1) .upgrade(Version::V1)
.authenticate(noise) .authenticate(auth_upgrade)
.multiplex(SelectUpgrade::new( .multiplex(multiplex_upgrade)
yamux::YamuxConfig::default(),
MplexConfig::new(),
))
.timeout(Duration::from_secs(20)) .timeout(Duration::from_secs(20))
.map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer)))
.boxed(); .boxed();
Ok(transport) 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<SwapTransport> {
let dh_keys = noise::Keypair::<X25519Spec>::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)>;