Merge pull request #27 from comit-network/tor-swap

This commit is contained in:
Philipp Hoenisch 2020-10-28 13:28:56 +11:00 committed by GitHub
commit dbd7f2b0c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 141 additions and 23 deletions

View File

@ -40,7 +40,10 @@ jobs:
- name: Check code formatting
run: cargo fmt --all -- --check
- name: Run clippy
- name: Run clippy with default features
run: cargo clippy --workspace --all-targets -- -D warnings
- name: Run clippy with all features enabled (e.g. tor)
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
build_test:

View File

@ -28,20 +28,16 @@ use xmr_btc::{alice::State0, bob, monero};
pub type Swarm = libp2p::Swarm<Alice>;
// TODO: After we have done some testing replace all the 'panic's with log
// statements or error returns.
// FIXME: This whole function is horrible, needs total re-write.
pub async fn swap(
listen: Multiaddr,
local_port: Option<u16>,
redeem_address: ::bitcoin::Address,
punish_address: ::bitcoin::Address,
) -> Result<()> {
let mut swarm = new_swarm(listen, local_port)?;
let message0: bob::Message0;
let mut last_amounts: Option<SwapAmounts> = None;
let mut swarm = new_swarm(listen)?;
loop {
match swarm.next().await {
OutEvent::ConnectionEstablished(id) => {
@ -111,7 +107,7 @@ pub async fn swap(
Ok(())
}
fn new_swarm(listen: Multiaddr) -> Result<Swarm> {
fn new_swarm(listen: Multiaddr, port: Option<u16>) -> Result<Swarm> {
use anyhow::Context as _;
let behaviour = Alice::default();
@ -119,7 +115,21 @@ fn new_swarm(listen: Multiaddr) -> Result<Swarm> {
let local_key_pair = behaviour.identity();
let local_peer_id = behaviour.peer_id();
let transport = transport::build(local_key_pair)?;
let transport;
#[cfg(feature = "tor")]
{
transport = match port {
Some(port) => transport::build(local_key_pair, Some((listen.clone(), port)))?,
None => anyhow::bail!("Must supply local port"),
};
}
#[cfg(not(feature = "tor"))]
{
transport = match port {
None => transport::build(local_key_pair)?,
Some(_) => anyhow::bail!("local port should not be provided for non-tor usage"),
};
}
let mut swarm = libp2p::swarm::SwarmBuilder::new(transport, behaviour, local_peer_id.clone())
.executor(Box::new(TokioExecutor {
@ -130,7 +140,7 @@ fn new_swarm(listen: Multiaddr) -> Result<Swarm> {
Swarm::listen_on(&mut swarm, listen.clone())
.with_context(|| format!("Address is not supported: {:#}", listen))?;
info!("Initialized swarm: {}", local_peer_id);
tracing::info!("Initialized swarm: {}", local_peer_id);
Ok(swarm)
}

View File

@ -114,7 +114,16 @@ fn new_swarm() -> Result<Swarm> {
let local_key_pair = behaviour.identity();
let local_peer_id = behaviour.peer_id();
let transport = transport::build(local_key_pair)?;
let transport = {
#[cfg(feature = "tor")]
{
transport::build(local_key_pair, None)?
}
#[cfg(not(feature = "tor"))]
{
transport::build(local_key_pair)?
}
};
let swarm = libp2p::swarm::SwarmBuilder::new(transport, behaviour, local_peer_id.clone())
.executor(Box::new(TokioExecutor {

View File

@ -11,4 +11,9 @@ pub struct Options {
/// Run the swap as Bob and try to swap this many BTC (in satoshi).
#[structopt(long = "sats")]
pub satoshis: Option<u64>,
/// Alice's onion multitaddr (only required for Bob, Alice will autogenerate
/// one)
#[structopt(long)]
pub alice_address: Option<String>,
}

View File

@ -12,22 +12,21 @@
)]
#![forbid(unsafe_code)]
use anyhow::{bail, Result};
use anyhow::{bail, Context, Result};
use cli::Options;
use futures::{channel::mpsc, StreamExt};
use libp2p::Multiaddr;
use log::LevelFilter;
use std::{io, io::Write, process};
use structopt::StructOpt;
use swap::{alice, bitcoin::Wallet, bob, Cmd, Rsp, SwapAmounts};
use tracing::info;
use url::Url;
use xmr_btc::bitcoin::{BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock};
mod cli;
mod trace;
use cli::Options;
use swap::{alice, bitcoin::Wallet, bob, Cmd, Rsp, SwapAmounts};
use xmr_btc::bitcoin::{BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock};
// TODO: Add root seed file instead of generating new seed each run.
// TODO: Remove all instances of the todo! macro
@ -35,7 +34,10 @@ use xmr_btc::bitcoin::{BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock};
// Alice's address and port until we have a config file.
pub const PORT: u16 = 9876; // Arbitrarily chosen.
pub const ADDR: &str = "127.0.0.1";
pub const BITCOIND_JSON_RPC_URL: &str = "127.0.0.1:8332";
pub const BITCOIND_JSON_RPC_URL: &str = "http://127.0.0.1:8332";
#[cfg(feature = "tor")]
pub const TOR_PORT: u16 = PORT + 1;
#[tokio::main]
async fn main() -> Result<()> {
@ -43,7 +45,21 @@ async fn main() -> Result<()> {
trace::init_tracing(LevelFilter::Debug)?;
#[cfg(feature = "tor")]
let (addr, _ac) = {
let tor_secret_key = torut::onion::TorSecretKeyV3::generate();
let onion_address = tor_secret_key
.public()
.get_onion_address()
.get_address_without_dot_onion();
(
format!("/onion3/{}:{}", onion_address, TOR_PORT),
create_tor_service(tor_secret_key).await?,
)
};
#[cfg(not(feature = "tor"))]
let addr = format!("/ip4/{}/tcp/{}", ADDR, PORT);
let alice: Multiaddr = addr.parse().expect("failed to parse Alice's address");
if opt.as_alice {
@ -71,6 +87,12 @@ async fn main() -> Result<()> {
} else {
info!("running swap node as Bob ...");
let alice_address = match opt.alice_address {
Some(addr) => addr,
None => bail!("Address required to dial"),
};
let alice_address = multiaddr(&alice_address)?;
let url = Url::parse(BITCOIND_JSON_RPC_URL).expect("failed to parse url");
let bitcoin_wallet = Wallet::new("bob", &url)
.await
@ -86,7 +108,7 @@ async fn main() -> Result<()> {
(None, None) => bail!("Please supply an amount to swap"),
(Some(_picos), _) => todo!("support starting with picos"),
(None, Some(sats)) => {
swap_as_bob(sats, alice, refund, bitcoin_wallet).await?;
swap_as_bob(sats, alice_address, refund, bitcoin_wallet).await?;
}
};
}
@ -94,12 +116,37 @@ async fn main() -> Result<()> {
Ok(())
}
#[cfg(feature = "tor")]
async fn create_tor_service(
tor_secret_key: torut::onion::TorSecretKeyV3,
) -> Result<swap::tor::AuthenticatedConnection> {
// TODO use configurable ports for tor connection
let mut authenticated_connection = swap::tor::UnauthenticatedConnection::default()
.init_authenticated_connection()
.await?;
tracing::info!("Tor authenticated.");
authenticated_connection
.add_service(TOR_PORT, &tor_secret_key)
.await?;
tracing::info!("Tor service added.");
Ok(authenticated_connection)
}
async fn swap_as_alice(
addr: Multiaddr,
redeem: bitcoin::Address,
punish: bitcoin::Address,
) -> Result<()> {
alice::swap(addr, redeem, punish).await
#[cfg(not(feature = "tor"))]
{
alice::swap(addr, None, redeem, punish).await
}
#[cfg(feature = "tor")]
{
alice::swap(addr, Some(PORT), redeem, punish).await
}
}
async fn swap_as_bob<W>(
@ -164,3 +211,10 @@ fn verify(amounts: SwapAmounts) -> Rsp {
fn is_yes(s: &str) -> bool {
matches!(s, "y" | "Y" | "yes" | "YES" | "Yes")
}
fn multiaddr(s: &str) -> Result<Multiaddr> {
let addr = s
.parse()
.with_context(|| format!("failed to parse multiaddr: {}", s))?;
Ok(addr)
}

View File

@ -10,18 +10,18 @@ use libp2p::{
dns::DnsConfig,
mplex::MplexConfig,
noise::{self, NoiseConfig, X25519Spec},
tcp::TokioTcpConfig,
yamux, PeerId,
};
// TOOD: Add the tor transport builder.
/// Builds a libp2p transport with the following features:
/// Builds a libp2p transport without Tor with the following features:
/// - TcpConnection
/// - DNS name resolution
/// - authentication via noise
/// - multiplexing via yamux or mplex
#[cfg(not(feature = "tor"))]
pub fn build(id_keys: identity::Keypair) -> Result<SwapTransport> {
use libp2p::tcp::TokioTcpConfig;
let dh_keys = noise::Keypair::<X25519Spec>::new().into_authentic(&id_keys)?;
let noise = NoiseConfig::xx(dh_keys).into_authenticated();
@ -41,4 +41,41 @@ pub fn build(id_keys: identity::Keypair) -> Result<SwapTransport> {
Ok(transport)
}
/// Builds a libp2p transport with Tor and with the following features:
/// - TCP connection over the Tor network
/// - DNS name resolution
/// - authentication via noise
/// - multiplexing via yamux or mplex
#[cfg(feature = "tor")]
pub fn build(
id_keys: identity::Keypair,
address_port_pair: Option<(libp2p::core::Multiaddr, u16)>,
) -> Result<SwapTransport> {
use libp2p_tokio_socks5::Socks5TokioTcpConfig;
use std::collections::HashMap;
let mut map = HashMap::new();
if let Some((addr, port)) = address_port_pair {
map.insert(addr, port);
}
let dh_keys = noise::Keypair::<X25519Spec>::new().into_authentic(&id_keys)?;
let noise = NoiseConfig::xx(dh_keys).into_authenticated();
let socks = Socks5TokioTcpConfig::default().nodelay(true).onion_map(map);
let dns = DnsConfig::new(socks)?;
let transport = dns
.upgrade(Version::V1)
.authenticate(noise)
.multiplex(SelectUpgrade::new(
yamux::Config::default(),
MplexConfig::new(),
))
.map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer)))
.boxed();
Ok(transport)
}
pub type SwapTransport = Boxed<(PeerId, StreamMuxerBox)>;