mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-10-01 01:45:40 -04:00
Merge branch 'master' into on-chain-protocol
This commit is contained in:
commit
ade0e08db6
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
@ -46,9 +46,20 @@ jobs:
|
|||||||
build_test:
|
build_test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install and stop tor in case it was running
|
||||||
|
run: |
|
||||||
|
sudo apt install software-properties-common
|
||||||
|
sudo curl https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | sudo gpg --import
|
||||||
|
sudo gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | sudo apt-key add -
|
||||||
|
sudo add-apt-repository 'deb https://deb.torproject.org/torproject.org bionic main'
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install tor deb.torproject.org-keyring
|
||||||
|
sudo /etc/init.d/tor stop
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
|
@ -4,6 +4,8 @@ version = "0.1.0"
|
|||||||
authors = ["CoBloX Team <team@coblox.tech>"]
|
authors = ["CoBloX Team <team@coblox.tech>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
# TODO: Check for stale dependencies, this looks like its a bit of a mess.
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
@ -17,10 +19,13 @@ genawaiter = "0.99.1"
|
|||||||
miniscript = { version = "1", features = ["serde"] }
|
miniscript = { version = "1", features = ["serde"] }
|
||||||
monero = { version = "0.9", features = ["serde_support"] }
|
monero = { version = "0.9", features = ["serde_support"] }
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
|
reqwest = { version = "0.10", default-features = false, features = ["socks"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
sha2 = "0.9"
|
sha2 = "0.9"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
tokio = { version = "0.2", features = ["time"] }
|
tokio = { version = "0.2", default-features = false, features = ["blocking", "macros", "rt-core", "time", "rt-threaded"] }
|
||||||
|
torut = { version = "0.1", optional = true }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
@ -28,11 +33,18 @@ backoff = { version = "0.2", features = ["tokio"] }
|
|||||||
base64 = "0.12"
|
base64 = "0.12"
|
||||||
bitcoin-harness = { git = "https://github.com/coblox/bitcoin-harness-rs", rev = "7ff30a559ab57cc3aa71189e71433ef6b2a6c3a2" }
|
bitcoin-harness = { git = "https://github.com/coblox/bitcoin-harness-rs", rev = "7ff30a559ab57cc3aa71189e71433ef6b2a6c3a2" }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
hyper = "0.13"
|
||||||
monero-harness = { path = "../monero-harness" }
|
monero-harness = { path = "../monero-harness" }
|
||||||
reqwest = { version = "0.10", default-features = false }
|
reqwest = { version = "0.10", default-features = false }
|
||||||
serde_cbor = "0.11"
|
serde_cbor = "0.11"
|
||||||
sled = "0.34"
|
sled = "0.34"
|
||||||
|
port_check = "0.1"
|
||||||
|
spectral = "0.6"
|
||||||
|
tempfile = "3"
|
||||||
testcontainers = "0.10"
|
testcontainers = "0.10"
|
||||||
tokio = { version = "0.2", default-features = false, features = ["blocking", "macros", "rt-core", "time", "rt-threaded"] }
|
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.2"
|
tracing-subscriber = "0.2"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
tor = ["torut"]
|
||||||
|
@ -50,6 +50,8 @@ pub mod bitcoin;
|
|||||||
pub mod bob;
|
pub mod bob;
|
||||||
pub mod monero;
|
pub mod monero;
|
||||||
pub mod serde;
|
pub mod serde;
|
||||||
|
#[cfg(feature = "tor")]
|
||||||
|
pub mod tor;
|
||||||
pub mod transport;
|
pub mod transport;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
117
xmr-btc/src/tor.rs
Normal file
117
xmr-btc/src/tor.rs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use std::{
|
||||||
|
future::Future,
|
||||||
|
net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4},
|
||||||
|
};
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
use torut::{
|
||||||
|
control::{AsyncEvent, AuthenticatedConn, ConnError, UnauthenticatedConn},
|
||||||
|
onion::TorSecretKeyV3,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct UnauthenticatedConnection {
|
||||||
|
tor_proxy_address: SocketAddrV4,
|
||||||
|
tor_control_port_address: SocketAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for UnauthenticatedConnection {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
tor_proxy_address: SocketAddrV4::new(Ipv4Addr::LOCALHOST, 9050),
|
||||||
|
tor_control_port_address: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 9051)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnauthenticatedConnection {
|
||||||
|
pub fn with_ports(proxy_port: u16, control_port: u16) -> Self {
|
||||||
|
Self {
|
||||||
|
tor_proxy_address: SocketAddrV4::new(Ipv4Addr::LOCALHOST, proxy_port),
|
||||||
|
tor_control_port_address: SocketAddr::V4(SocketAddrV4::new(
|
||||||
|
Ipv4Addr::LOCALHOST,
|
||||||
|
control_port,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// checks if tor is running
|
||||||
|
async fn assert_tor_running(&self) -> Result<()> {
|
||||||
|
// Make sure you are running tor and this is your socks port
|
||||||
|
let proxy = reqwest::Proxy::all(format!("socks5h://{}", self.tor_proxy_address).as_str())
|
||||||
|
.map_err(|_| anyhow!("tor proxy should be there"))?;
|
||||||
|
let client = reqwest::Client::builder().proxy(proxy).build()?;
|
||||||
|
|
||||||
|
let res = client.get("https://check.torproject.org").send().await?;
|
||||||
|
let text = res.text().await?;
|
||||||
|
|
||||||
|
if !text.contains("Congratulations. This browser is configured to use Tor.") {
|
||||||
|
bail!("Tor is currently not running")
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn init_unauthenticated_connection(&self) -> Result<UnauthenticatedConn<TcpStream>> {
|
||||||
|
// Connect to local tor service via control port
|
||||||
|
let sock = TcpStream::connect(self.tor_control_port_address).await?;
|
||||||
|
let uc = UnauthenticatedConn::new(sock);
|
||||||
|
Ok(uc)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new authenticated connection to your local Tor service
|
||||||
|
pub async fn init_authenticated_connection(self) -> Result<AuthenticatedConnection> {
|
||||||
|
self.assert_tor_running().await?;
|
||||||
|
|
||||||
|
let mut uc = self
|
||||||
|
.init_unauthenticated_connection()
|
||||||
|
.await
|
||||||
|
.map_err(|_| anyhow!("Tor instance not running."))?;
|
||||||
|
|
||||||
|
let tor_info = uc
|
||||||
|
.load_protocol_info()
|
||||||
|
.await
|
||||||
|
.map_err(|_| anyhow!("Failed to load protocol info from Tor."))?;
|
||||||
|
|
||||||
|
let tor_auth_data = tor_info
|
||||||
|
.make_auth_data()?
|
||||||
|
.ok_or_else(|| anyhow!("Failed to make auth data."))?;
|
||||||
|
|
||||||
|
// Get an authenticated connection to the Tor via the Tor Controller protocol.
|
||||||
|
uc.authenticate(&tor_auth_data)
|
||||||
|
.await
|
||||||
|
.map_err(|_| anyhow!("Failed to authenticate with Tor"))?;
|
||||||
|
|
||||||
|
Ok(AuthenticatedConnection {
|
||||||
|
authenticated_connection: uc.into_authenticated().await,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handler = fn(AsyncEvent<'_>) -> Box<dyn Future<Output = Result<(), ConnError>> + Unpin>;
|
||||||
|
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct AuthenticatedConnection {
|
||||||
|
authenticated_connection: AuthenticatedConn<TcpStream, Handler>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthenticatedConnection {
|
||||||
|
/// Add an ephemeral tor service on localhost with the provided key
|
||||||
|
pub async fn add_service(&mut self, port: u16, tor_key: &TorSecretKeyV3) -> Result<()> {
|
||||||
|
self.authenticated_connection
|
||||||
|
.add_onion_v3(
|
||||||
|
tor_key,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
&mut [(
|
||||||
|
port,
|
||||||
|
SocketAddr::new(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)), port),
|
||||||
|
)]
|
||||||
|
.iter(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!("Could not add onion service.: {:#?}", e))
|
||||||
|
}
|
||||||
|
}
|
130
xmr-btc/tests/tor.rs
Normal file
130
xmr-btc/tests/tor.rs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#[cfg(feature = "tor")]
|
||||||
|
mod tor_test {
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
use spectral::prelude::*;
|
||||||
|
use std::{convert::Infallible, fs};
|
||||||
|
use tempfile::{Builder, NamedTempFile};
|
||||||
|
use tokio::sync::oneshot::Receiver;
|
||||||
|
use torut::{
|
||||||
|
onion::TorSecretKeyV3,
|
||||||
|
utils::{run_tor, AutoKillChild},
|
||||||
|
};
|
||||||
|
use tracing_subscriber::util::SubscriberInitExt;
|
||||||
|
use xmr_btc::tor::UnauthenticatedConnection;
|
||||||
|
|
||||||
|
async fn hello_world(
|
||||||
|
_req: hyper::Request<hyper::Body>,
|
||||||
|
) -> Result<hyper::Response<hyper::Body>, Infallible> {
|
||||||
|
Ok(hyper::Response::new("Hello World".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_test_service(port: u16, rx: Receiver<()>) {
|
||||||
|
let make_svc =
|
||||||
|
make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(hello_world)) });
|
||||||
|
let addr = ([127, 0, 0, 1], port).into();
|
||||||
|
let server = hyper::Server::bind(&addr).serve(make_svc);
|
||||||
|
let graceful = server.with_graceful_shutdown(async {
|
||||||
|
rx.await.ok();
|
||||||
|
});
|
||||||
|
tokio::spawn(async {
|
||||||
|
if let Err(e) = graceful.await {
|
||||||
|
eprintln!("server error: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tracing::info!("Test server started at port: {}", port);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_tmp_tor() -> Result<(AutoKillChild, u16, u16, NamedTempFile)> {
|
||||||
|
// we create an empty torrc file to not use the system one
|
||||||
|
let temp_torrc = Builder::new().tempfile()?;
|
||||||
|
let torrc_file = format!("{}", fs::canonicalize(temp_torrc.path())?.display());
|
||||||
|
tracing::info!("Temp torrc file created at: {}", torrc_file);
|
||||||
|
|
||||||
|
let control_port = if port_check::is_local_port_free(9051) {
|
||||||
|
9051
|
||||||
|
} else {
|
||||||
|
port_check::free_local_port().unwrap()
|
||||||
|
};
|
||||||
|
let proxy_port = if port_check::is_local_port_free(9050) {
|
||||||
|
9050
|
||||||
|
} else {
|
||||||
|
port_check::free_local_port().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let child = run_tor(
|
||||||
|
"tor",
|
||||||
|
&mut [
|
||||||
|
"--CookieAuthentication",
|
||||||
|
"1",
|
||||||
|
"--ControlPort",
|
||||||
|
control_port.to_string().as_str(),
|
||||||
|
"--SocksPort",
|
||||||
|
proxy_port.to_string().as_str(),
|
||||||
|
"-f",
|
||||||
|
&torrc_file,
|
||||||
|
]
|
||||||
|
.iter(),
|
||||||
|
)?;
|
||||||
|
tracing::info!("Tor running with pid: {}", child.id());
|
||||||
|
let child = AutoKillChild::new(child);
|
||||||
|
Ok((child, control_port, proxy_port, temp_torrc))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_tor_control_port() -> Result<()> {
|
||||||
|
let _guard = tracing_subscriber::fmt()
|
||||||
|
.with_env_filter("info")
|
||||||
|
.set_default();
|
||||||
|
|
||||||
|
// start tmp tor
|
||||||
|
let (_child, control_port, proxy_port, _tmp_torrc) = run_tmp_tor()?;
|
||||||
|
|
||||||
|
// Setup test HTTP Server
|
||||||
|
let (tx, rx) = tokio::sync::oneshot::channel::<()>();
|
||||||
|
let port = 8080;
|
||||||
|
start_test_service(port, rx);
|
||||||
|
|
||||||
|
// Connect to local Tor service
|
||||||
|
let mut authenticated_connection =
|
||||||
|
UnauthenticatedConnection::with_ports(proxy_port, control_port)
|
||||||
|
.init_authenticated_connection()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
tracing::info!("Tor authenticated.");
|
||||||
|
|
||||||
|
// Expose an onion service that re-directs to the echo server.
|
||||||
|
let tor_secret_key_v3 = TorSecretKeyV3::generate();
|
||||||
|
authenticated_connection
|
||||||
|
.add_service(port, &tor_secret_key_v3)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Test if Tor service forwards to HTTP Server
|
||||||
|
|
||||||
|
let proxy = reqwest::Proxy::all(format!("socks5h://127.0.0.1:{}", proxy_port).as_str())
|
||||||
|
.expect("tor proxy should be there");
|
||||||
|
let client = reqwest::Client::builder().proxy(proxy).build()?;
|
||||||
|
let onion_address = tor_secret_key_v3.public().get_onion_address().to_string();
|
||||||
|
let onion_url = format!("http://{}:8080", onion_address);
|
||||||
|
|
||||||
|
tracing::info!("Tor service added: {}", onion_url);
|
||||||
|
|
||||||
|
let res = client.get(&onion_url).send().await?;
|
||||||
|
|
||||||
|
assert_that(&res.status()).is_equal_to(StatusCode::OK);
|
||||||
|
|
||||||
|
let text = res.text().await?;
|
||||||
|
assert_that!(text).contains("Hello World");
|
||||||
|
tracing::info!(
|
||||||
|
"Local server called via Tor proxy. Its response is: {}",
|
||||||
|
text
|
||||||
|
);
|
||||||
|
|
||||||
|
// gracefully shut down server
|
||||||
|
let _ = tx.send(());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user