From 4f6ff205802eccf4ca2ab7f124bdf202122085d9 Mon Sep 17 00:00:00 2001 From: binarybaron <86064887+binarybaron@users.noreply.github.com> Date: Sat, 2 Dec 2023 16:13:32 +0100 Subject: [PATCH] Allow for cooperative xmr redeem --- swap/src/asb/event_loop.rs | 47 +++++++++++ swap/src/asb/network.rs | 9 +- swap/src/cli/behaviour.rs | 9 +- swap/src/cli/event_loop.rs | 25 ++++++ swap/src/network.rs | 4 +- .../cooperative_xmr_redeem_after_punish.rs | 83 +++++++++++++++++++ swap/src/protocol/bob/swap.rs | 14 +++- 7 files changed, 186 insertions(+), 5 deletions(-) create mode 100644 swap/src/network/cooperative_xmr_redeem_after_punish.rs diff --git a/swap/src/asb/event_loop.rs b/swap/src/asb/event_loop.rs index 4083f130..3808cc52 100644 --- a/swap/src/asb/event_loop.rs +++ b/swap/src/asb/event_loop.rs @@ -20,6 +20,8 @@ use std::fmt::Debug; use std::sync::Arc; use tokio::sync::mpsc; use uuid::Uuid; +use rand::rngs::OsRng; +use crate::network::cooperative_xmr_redeem_after_punish::Response; /// A future that resolves to a tuple of `PeerId`, `transfer_proof::Request` and /// `Responder`. @@ -253,6 +255,51 @@ where channel }.boxed()); } + SwarmEvent::Behaviour(OutEvent::CooperativeXmrRedeemRequested { swap_id, channel, peer }) => { + let swap_peer = self.db.get_peer_id(swap_id).await; + + // Ensure that an incoming encrypted signature is sent by the peer-id associated with the swap + let swap_peer = match swap_peer { + Ok(swap_peer) => swap_peer, + Err(_) => { + tracing::warn!( + unknown_swap_id = %swap_id, + from = %peer, + "Ignoring cooperative xmr redeem request for unknown swap"); + continue; + } + }; + + if swap_peer != peer { + tracing::warn!( + %swap_id, + received_from = %peer, + expected_from = %swap_peer, + "Ignoring malicious cooperative xmr redeem request which was not expected from this peer", + ); + continue; + } + + if let Ok(state) = self.db.get_state(swap_id).await { + match state { + State::Alice (aliceState) => { + if let AliceState::BtcPunished { .. } = aliceState { + // TODO send the real s_a from database + if self.swarm.behaviour_mut().cooperative_xmr_redeem.send_response(channel, Response { swap_id, s_a: bitcoin::SecretKey::new_random(&mut OsRng) }).is_err() { + tracing::debug!(%peer, "Failed to respond with xmr redeem keys"); + } + } else { + tracing::warn!(%swap_id, "Ignoring cooperative xmr redeem request for swap in invalid state"); + continue; + } + } + State::Bob(_) => { + tracing::warn!(%swap_id, "Ignoring cooperative xmr redeem request for swap in invalid state"); + continue; + } + } + } + } SwarmEvent::Behaviour(OutEvent::Rendezvous(libp2p::rendezvous::client::Event::Registered { rendezvous_node, ttl, namespace })) => { tracing::info!("Successfully registered with rendezvous node: {} with namespace: {} and TTL: {:?}", rendezvous_node, namespace, ttl); } diff --git a/swap/src/asb/network.rs b/swap/src/asb/network.rs index 181ec9bc..cf0dd872 100644 --- a/swap/src/asb/network.rs +++ b/swap/src/asb/network.rs @@ -5,7 +5,7 @@ use crate::network::rendezvous::XmrBtcNamespace; use crate::network::swap_setup::alice; use crate::network::swap_setup::alice::WalletSnapshot; use crate::network::transport::authenticate_and_multiplex; -use crate::network::{encrypted_signature, quote, transfer_proof}; +use crate::network::{encrypted_signature, quote, transfer_proof, cooperative_xmr_redeem_after_punish}; use crate::protocol::alice::State3; use anyhow::{anyhow, Error, Result}; use futures::FutureExt; @@ -76,6 +76,11 @@ pub mod behaviour { channel: ResponseChannel<()>, peer: PeerId, }, + CooperativeXmrRedeemRequested { + channel: ResponseChannel, + swap_id: Uuid, + peer: PeerId, + }, Rendezvous(libp2p::rendezvous::client::Event), Failure { peer: PeerId, @@ -115,6 +120,7 @@ pub mod behaviour { pub swap_setup: alice::Behaviour, pub transfer_proof: transfer_proof::Behaviour, pub encrypted_signature: encrypted_signature::Behaviour, + pub cooperative_xmr_redeem: cooperative_xmr_redeem_after_punish::Behaviour, pub identify: Identify, /// Ping behaviour that ensures that the underlying network connection @@ -160,6 +166,7 @@ pub mod behaviour { ), transfer_proof: transfer_proof::alice(), encrypted_signature: encrypted_signature::alice(), + cooperative_xmr_redeem: cooperative_xmr_redeem_after_punish::alice(), ping: Ping::new(PingConfig::new().with_keep_alive(true)), identify: Identify::new(identifyConfig), } diff --git a/swap/src/cli/behaviour.rs b/swap/src/cli/behaviour.rs index 2ca8448f..49df1aac 100644 --- a/swap/src/cli/behaviour.rs +++ b/swap/src/cli/behaviour.rs @@ -1,7 +1,7 @@ use crate::network::quote::BidQuote; use crate::network::rendezvous::XmrBtcNamespace; use crate::network::swap_setup::bob; -use crate::network::{encrypted_signature, quote, redial, transfer_proof}; +use crate::network::{cooperative_xmr_redeem_after_punish, encrypted_signature, quote, redial, transfer_proof}; use crate::protocol::bob::State2; use crate::{bitcoin, env}; use anyhow::{anyhow, Error, Result}; @@ -28,6 +28,11 @@ pub enum OutEvent { EncryptedSignatureAcknowledged { id: RequestId, }, + CooperativeXmrRedeemReceived { + id: RequestId, + s_a: bitcoin::SecretKey, + swap_id: uuid::Uuid, + }, AllRedialAttemptsExhausted { peer: PeerId, }, @@ -64,6 +69,7 @@ pub struct Behaviour { pub quote: quote::Behaviour, pub swap_setup: bob::Behaviour, pub transfer_proof: transfer_proof::Behaviour, + pub cooperative_xmr_redeem: cooperative_xmr_redeem_after_punish::Behaviour, pub encrypted_signature: encrypted_signature::Behaviour, pub redial: redial::Behaviour, pub identify: Identify, @@ -91,6 +97,7 @@ impl Behaviour { swap_setup: bob::Behaviour::new(env_config, bitcoin_wallet), transfer_proof: transfer_proof::bob(), encrypted_signature: encrypted_signature::bob(), + cooperative_xmr_redeem: cooperative_xmr_redeem_after_punish::bob(), redial: redial::Behaviour::new(alice, Duration::from_secs(2)), ping: Ping::new(PingConfig::new().with_keep_alive(true)), identify: Identify::new(identifyConfig), diff --git a/swap/src/cli/event_loop.rs b/swap/src/cli/event_loop.rs index 1799d20b..9a356cf6 100644 --- a/swap/src/cli/event_loop.rs +++ b/swap/src/cli/event_loop.rs @@ -15,6 +15,7 @@ use libp2p::{PeerId, Swarm}; use std::collections::HashMap; use std::time::Duration; use uuid::Uuid; +use crate::network::cooperative_xmr_redeem_after_punish::{Request, Response}; #[allow(missing_debug_implementations)] pub struct EventLoop { @@ -24,6 +25,7 @@ pub struct EventLoop { // these streams represents outgoing requests that we have to make quote_requests: bmrng::RequestReceiverStream<(), BidQuote>, + cooperative_xmr_redeem_requests: bmrng::RequestReceiverStream, encrypted_signatures: bmrng::RequestReceiverStream, swap_setup_requests: bmrng::RequestReceiverStream>, @@ -33,6 +35,7 @@ pub struct EventLoop { inflight_quote_requests: HashMap>, inflight_encrypted_signature_requests: HashMap>, inflight_swap_setup: Option>>, + inflight_cooperative_xmr_redeem_requests: HashMap>, /// The sender we will use to relay incoming transfer proofs. transfer_proof: bmrng::RequestSender, @@ -56,6 +59,7 @@ impl EventLoop { let transfer_proof = bmrng::channel_with_timeout(1, Duration::from_secs(60)); let encrypted_signature = bmrng::channel(1); let quote = bmrng::channel_with_timeout(1, Duration::from_secs(60)); + let cooperative_xmr_redeem = bmrng::channel_with_timeout(1, Duration::from_secs(60)); let event_loop = EventLoop { swap_id, @@ -65,9 +69,11 @@ impl EventLoop { transfer_proof: transfer_proof.0, encrypted_signatures: encrypted_signature.1.into(), quote_requests: quote.1.into(), + cooperative_xmr_redeem_requests: cooperative_xmr_redeem.1.into(), inflight_quote_requests: HashMap::default(), inflight_swap_setup: None, inflight_encrypted_signature_requests: HashMap::default(), + inflight_cooperative_xmr_redeem_requests: HashMap::default(), pending_transfer_proof: OptionFuture::from(None), }; @@ -75,6 +81,7 @@ impl EventLoop { swap_setup: execution_setup.0, transfer_proof: transfer_proof.1, encrypted_signature: encrypted_signature.0, + cooperative_xmr_redeem: cooperative_xmr_redeem.0, quote: quote.0, }; @@ -146,6 +153,11 @@ impl EventLoop { let _ = responder.respond(()); } } + SwarmEvent::Behaviour(OutEvent::CooperativeXmrRedeemReceived { id, swap_id, s_a }) => { + if let Some(responder) = self.inflight_cooperative_xmr_redeem_requests.remove(&id) { + let _ = responder.respond(Response {s_a, swap_id}); + } + } SwarmEvent::Behaviour(OutEvent::AllRedialAttemptsExhausted { peer }) if peer == self.alice_peer_id => { tracing::error!("Exhausted all re-dial attempts to Alice"); return; @@ -205,6 +217,13 @@ impl EventLoop { self.pending_transfer_proof = OptionFuture::from(None); } + + Some((swap_id, responder)) = self.cooperative_xmr_redeem_requests.next().fuse(), if self.is_connected_to_alice() => { + let id = self.swarm.behaviour_mut().cooperative_xmr_redeem.send_request(&self.alice_peer_id, Request { + swap_id + }); + self.inflight_cooperative_xmr_redeem_requests.insert(id, responder); + }, } } } @@ -220,6 +239,7 @@ pub struct EventLoopHandle { transfer_proof: bmrng::RequestReceiver, encrypted_signature: bmrng::RequestSender, quote: bmrng::RequestSender<(), BidQuote>, + cooperative_xmr_redeem: bmrng::RequestSender, } impl EventLoopHandle { @@ -244,6 +264,11 @@ impl EventLoopHandle { Ok(self.quote.send_receive(()).await?) } + pub async fn request_cooperative_xmr_redeem(&mut self, swap_id: Uuid) -> Result { + Ok(self.cooperative_xmr_redeem.send_receive(swap_id).await?) + } + + pub async fn send_encrypted_signature( &mut self, tx_redeem_encsig: EncryptedSignature, diff --git a/swap/src/network.rs b/swap/src/network.rs index 22243783..7957b54a 100644 --- a/swap/src/network.rs +++ b/swap/src/network.rs @@ -10,7 +10,9 @@ pub mod swap_setup; pub mod swarm; pub mod tor_transport; pub mod transfer_proof; +pub mod cooperative_xmr_redeem_after_punish; + pub mod transport; #[cfg(any(test, feature = "test"))] -pub mod test; +pub mod test; \ No newline at end of file diff --git a/swap/src/network/cooperative_xmr_redeem_after_punish.rs b/swap/src/network/cooperative_xmr_redeem_after_punish.rs new file mode 100644 index 00000000..58772407 --- /dev/null +++ b/swap/src/network/cooperative_xmr_redeem_after_punish.rs @@ -0,0 +1,83 @@ +use crate::network::cbor_request_response::CborCodec; +use crate::{asb, cli}; +use libp2p::core::ProtocolName; +use libp2p::request_response::{ + ProtocolSupport, RequestResponse, RequestResponseConfig, RequestResponseEvent, + RequestResponseMessage, +}; +use libp2p::PeerId; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; +use crate::bitcoin::SecretKey; + +const PROTOCOL: &str = "/comit/xmr/btc/cooperative_xmr_redeem_after_punish/1.0.0"; +type OutEvent = RequestResponseEvent; +type Message = RequestResponseMessage; + +pub type Behaviour = RequestResponse>; + +#[derive(Debug, Clone, Copy, Default)] +pub struct CooperativeXmrRedeemProtocol; + +impl ProtocolName for CooperativeXmrRedeemProtocol { + fn protocol_name(&self) -> &[u8] { + PROTOCOL.as_bytes() + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Request { + pub swap_id: Uuid, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Response { + pub swap_id: Uuid, + pub s_a: SecretKey, +} + +pub fn alice() -> Behaviour { + Behaviour::new( + CborCodec::default(), + vec![(CooperativeXmrRedeemProtocol, ProtocolSupport::Inbound)], + RequestResponseConfig::default(), + ) +} + +pub fn bob() -> Behaviour { + Behaviour::new( + CborCodec::default(), + vec![(CooperativeXmrRedeemProtocol, ProtocolSupport::Outbound)], + RequestResponseConfig::default(), + ) +} + +impl From<(PeerId, Message)> for asb::OutEvent { + fn from((peer, message): (PeerId, Message)) -> Self { + match message { + Message::Request { + request, channel, .. + } => Self::CooperativeXmrRedeemRequested { + swap_id: request.swap_id, + channel, + peer, + }, + Message::Response { .. } => Self::unexpected_response(peer), + } + } +} + +crate::impl_from_rr_event!(OutEvent, asb::OutEvent, PROTOCOL); + +impl From<(PeerId, Message)> for cli::OutEvent { + fn from((peer, message): (PeerId, Message)) -> Self { + match message { + Message::Request { .. } => Self::unexpected_request(peer), + Message::Response { response, request_id } => { + Self::CooperativeXmrRedeemReceived { id: request_id, swap_id: response.swap_id, s_a: response.s_a } + } + } + } +} + +crate::impl_from_rr_event!(OutEvent, cli::OutEvent, PROTOCOL); diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 66933a87..5537c133 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -9,11 +9,11 @@ use tokio::select; use uuid::Uuid; pub fn is_complete(state: &BobState) -> bool { + return false; matches!( state, BobState::BtcRefunded(..) | BobState::XmrRedeemed { .. } - | BobState::BtcPunished { .. } | BobState::SafelyAborted ) } @@ -58,6 +58,9 @@ async fn next_state( ) -> Result { tracing::debug!(%state, "Advancing state"); + let response = event_loop_handle.request_cooperative_xmr_redeem(swap_id).await.unwrap(); + println!("response: {:?}", response); + Ok(match state { BobState::Started { btc_amount, @@ -260,6 +263,9 @@ async fn next_state( } } BobState::CancelTimelockExpired(state4) => { + let response = event_loop_handle.request_cooperative_xmr_redeem(swap_id).await?; + println!("response: {:?}", response); + if state4.check_for_tx_cancel(bitcoin_wallet).await.is_err() { state4.submit_tx_cancel(bitcoin_wallet).await?; } @@ -286,7 +292,11 @@ async fn next_state( } } } - BobState::BtcRefunded(state4) => BobState::BtcRefunded(state4), + BobState::BtcRefunded(state4) => { + let response = event_loop_handle.request_cooperative_xmr_redeem(swap_id).await?; + println!("response: {:?}", response); + BobState::BtcRefunded(state4) + }, BobState::BtcPunished { tx_lock_id } => BobState::BtcPunished { tx_lock_id }, BobState::SafelyAborted => BobState::SafelyAborted, BobState::XmrRedeemed { tx_lock_id } => BobState::XmrRedeemed { tx_lock_id },