Add Tor to main

This commit is contained in:
Philipp Hoenisch 2020-10-26 16:55:53 +11:00
parent cf8accf30d
commit 8811a0a205
No known key found for this signature in database
GPG Key ID: E5F8E74C672BC666
5 changed files with 170 additions and 19 deletions

View File

@ -28,20 +28,38 @@ 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.
#[cfg(not(feature = "tor"))]
pub async fn swap(
listen: Multiaddr,
redeem_address: ::bitcoin::Address,
punish_address: ::bitcoin::Address,
) -> Result<()> {
let swarm = new_swarm(listen)?;
_swap(redeem_address, punish_address, swarm).await
}
#[cfg(feature = "tor")]
pub async fn swap(
listen: Multiaddr,
local_port: u16,
redeem_address: ::bitcoin::Address,
punish_address: ::bitcoin::Address,
) -> Result<()> {
let swarm = new_swarm(listen, local_port)?;
_swap(redeem_address, punish_address, swarm).await
}
// 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.
async fn _swap(
redeem_address: ::bitcoin::Address,
punish_address: ::bitcoin::Address,
mut swarm: Swarm,
) -> Result<()> {
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,6 +129,32 @@ pub async fn swap(
Ok(())
}
#[cfg(feature = "tor")]
fn new_swarm(listen: Multiaddr, port: u16) -> Result<Swarm> {
use anyhow::Context as _;
let behaviour = Alice::default();
let local_key_pair = behaviour.identity();
let local_peer_id = behaviour.peer_id();
let transport = transport::build(local_key_pair, Some((listen.clone(), port)))?;
let mut swarm = libp2p::swarm::SwarmBuilder::new(transport, behaviour, local_peer_id.clone())
.executor(Box::new(TokioExecutor {
handle: tokio::runtime::Handle::current(),
}))
.build();
Swarm::listen_on(&mut swarm, listen.clone())
.with_context(|| format!("Address is not supported: {:#}", listen))?;
tracing::info!("Initialized swarm: {}", local_peer_id);
Ok(swarm)
}
#[cfg(not(feature = "tor"))]
fn new_swarm(listen: Multiaddr) -> Result<Swarm> {
use anyhow::Context as _;

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,14 @@ 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")]
use swap::tor::{AuthenticatedConnection, UnauthenticatedConnection};
#[cfg(feature = "tor")]
use torut::onion::TorSecretKeyV3;
#[cfg(feature = "tor")]
pub const TOR_PORT: u16 = PORT + 1;
#[tokio::main]
async fn main() -> Result<()> {
@ -43,7 +49,21 @@ async fn main() -> Result<()> {
trace::init_tracing(LevelFilter::Debug)?;
#[cfg(feature = "tor")]
let (addr, _ac) = {
let tor_secret_key = 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 +91,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 +112,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 +120,35 @@ async fn main() -> Result<()> {
Ok(())
}
#[cfg(feature = "tor")]
async fn create_tor_service(tor_secret_key: TorSecretKeyV3) -> Result<AuthenticatedConnection> {
// todo use configurable ports for tor connection
let mut authenticated_connection = 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<()> {
#[cfg(not(feature = "tor"))]
{
alice::swap(addr, redeem, punish).await
}
#[cfg(feature = "tor")]
{
alice::swap(addr, PORT, redeem, punish).await
}
}
async fn swap_as_bob<W>(
@ -164,3 +213,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)>;