diff --git a/swap/src/alice.rs b/swap/src/alice.rs index 05d0ba81..903d22da 100644 --- a/swap/src/alice.rs +++ b/swap/src/alice.rs @@ -53,15 +53,15 @@ pub fn new_swarm( Ok(swarm) } -#[allow(clippy::large_enum_variant)] #[derive(Debug)] pub enum OutEvent { ConnectionEstablished(PeerId), // TODO (Franck): Change this to get both amounts so parties can verify the amounts are // expected early on. - Request(amounts::OutEvent), // Not-uniform with Bob on purpose, ready for adding Xmr event. + Request(Box), /* Not-uniform with Bob on purpose, ready for adding Xmr + * event. */ Message0 { - msg: bob::Message0, + msg: Box, channel: ResponseChannel, }, Message1 { @@ -87,14 +87,17 @@ impl From for OutEvent { impl From for OutEvent { fn from(event: amounts::OutEvent) -> Self { - OutEvent::Request(event) + OutEvent::Request(Box::new(event)) } } impl From for OutEvent { fn from(event: message0::OutEvent) -> Self { match event { - message0::OutEvent::Msg { channel, msg } => OutEvent::Message0 { msg, channel }, + message0::OutEvent::Msg { channel, msg } => OutEvent::Message0 { + msg: Box::new(msg), + channel, + }, } } } diff --git a/swap/src/alice/event_loop.rs b/swap/src/alice/event_loop.rs index b9cd08f4..f650932d 100644 --- a/swap/src/alice/event_loop.rs +++ b/swap/src/alice/event_loop.rs @@ -11,6 +11,7 @@ use libp2p::{ use tokio::sync::mpsc::{Receiver, Sender}; use xmr_btc::{alice, bob}; +#[allow(missing_debug_implementations)] pub struct Channels { sender: Sender, receiver: Receiver, @@ -29,6 +30,7 @@ impl Default for Channels { } } +#[derive(Debug)] pub struct EventLoopHandle { msg0: Receiver<(bob::Message0, ResponseChannel)>, msg1: Receiver<(bob::Message1, ResponseChannel)>, @@ -122,6 +124,7 @@ impl EventLoopHandle { } } +#[allow(missing_debug_implementations)] pub struct EventLoop { swarm: libp2p::Swarm, msg0: Sender<(bob::Message0, ResponseChannel)>, @@ -203,7 +206,7 @@ impl EventLoop { let _ = self.conn_established.send(alice).await; } OutEvent::Message0 { msg, channel } => { - let _ = self.msg0.send((msg, channel)).await; + let _ = self.msg0.send((*msg, channel)).await; } OutEvent::Message1 { msg, channel } => { let _ = self.msg1.send((msg, channel)).await; @@ -215,7 +218,7 @@ impl EventLoop { let _ = self.msg3.send(msg).await; } OutEvent::Request(event) => { - let _ = self.request.send(event).await; + let _ = self.request.send(*event).await; } } }, diff --git a/swap/src/alice/message0.rs b/swap/src/alice/message0.rs index 2fb8753b..92ca7a43 100644 --- a/swap/src/alice/message0.rs +++ b/swap/src/alice/message0.rs @@ -37,7 +37,7 @@ pub struct Message0 { impl Message0 { pub fn send(&mut self, channel: ResponseChannel, msg: xmr_btc::alice::Message0) { - let msg = AliceToBob::Message0(msg); + let msg = AliceToBob::Message0(Box::new(msg)); self.rr.send_response(channel, msg); } fn poll( @@ -82,7 +82,7 @@ impl NetworkBehaviourEventProcess> } => { if let BobToAlice::Message0(msg) = request { debug!("Received Message0"); - self.events.push_back(OutEvent::Msg { msg, channel }); + self.events.push_back(OutEvent::Msg { msg: *msg, channel }); } } RequestResponseEvent::Message { diff --git a/swap/src/alice/message1.rs b/swap/src/alice/message1.rs index 4d44b0a2..cf6d2bd2 100644 --- a/swap/src/alice/message1.rs +++ b/swap/src/alice/message1.rs @@ -38,7 +38,7 @@ pub struct Message1 { impl Message1 { pub fn send(&mut self, channel: ResponseChannel, msg: xmr_btc::alice::Message1) { - let msg = AliceToBob::Message1(msg); + let msg = AliceToBob::Message1(Box::new(msg)); self.rr.send_response(channel, msg); } diff --git a/swap/src/alice/swap.rs b/swap/src/alice/swap.rs index e6e6e8b4..d77f2187 100644 --- a/swap/src/alice/swap.rs +++ b/swap/src/alice/swap.rs @@ -10,12 +10,9 @@ use crate::{ wait_for_bitcoin_encrypted_signature, wait_for_bitcoin_refund, wait_for_locked_bitcoin, }, }, - bitcoin, bitcoin::EncryptedSignature, + database::{Database, Swap}, network::request_response::AliceToBob, - state, - state::{Alice, AliceEndState, Swap}, - storage::Database, SwapAmounts, }; use anyhow::Result; @@ -41,7 +38,7 @@ trait Rng: RngCore + CryptoRng + Send {} impl Rng for T where T: RngCore + CryptoRng + Send {} -#[allow(clippy::large_enum_variant)] +#[derive(Debug)] pub enum AliceState { Started { amounts: SwapAmounts, @@ -50,36 +47,36 @@ pub enum AliceState { Negotiated { channel: Option>, amounts: SwapAmounts, - state3: State3, + state3: Box, }, BtcLocked { channel: Option>, amounts: SwapAmounts, - state3: State3, + state3: Box, }, XmrLocked { - state3: State3, + state3: Box, }, EncSigLearned { - state3: State3, encrypted_signature: EncryptedSignature, + state3: Box, }, BtcRedeemed, BtcCancelled { - state3: State3, tx_cancel: TxCancel, + state3: Box, }, BtcRefunded { spend_key: monero::PrivateKey, - state3: State3, + state3: Box, }, BtcPunishable { tx_refund: TxRefund, - state3: State3, + state3: Box, }, XmrRefunded, CancelTimelockExpired { - state3: State3, + state3: Box, }, BtcPunished, SafelyAborted, @@ -105,114 +102,6 @@ impl fmt::Display for AliceState { } } -impl From<&AliceState> for state::Alice { - fn from(alice_state: &AliceState) -> Self { - match alice_state { - AliceState::Negotiated { state3, .. } => Alice::Negotiated(state3.clone()), - AliceState::BtcLocked { state3, .. } => Alice::BtcLocked(state3.clone()), - AliceState::XmrLocked { state3 } => Alice::XmrLocked(state3.clone()), - AliceState::EncSigLearned { - state3, - encrypted_signature, - } => Alice::EncSigLearned { - state: state3.clone(), - encrypted_signature: encrypted_signature.clone(), - }, - AliceState::BtcRedeemed => Alice::Done(AliceEndState::BtcRedeemed), - AliceState::BtcCancelled { state3, .. } => Alice::BtcCancelled(state3.clone()), - AliceState::BtcRefunded { spend_key, state3 } => Alice::BtcRefunded { - spend_key: *spend_key, - state3: state3.clone(), - }, - AliceState::BtcPunishable { state3, .. } => Alice::BtcPunishable(state3.clone()), - AliceState::XmrRefunded => Alice::Done(AliceEndState::XmrRefunded), - AliceState::CancelTimelockExpired { state3 } => { - Alice::CancelTimelockExpired(state3.clone()) - } - AliceState::BtcPunished => Alice::Done(AliceEndState::BtcPunished), - AliceState::SafelyAborted => Alice::Done(AliceEndState::SafelyAborted), - AliceState::Started { amounts, state0 } => Alice::Started { - amounts: *amounts, - state0: state0.clone(), - }, - } - } -} - -impl From for AliceState { - fn from(db_state: state::Alice) -> Self { - use AliceState::*; - match db_state { - Alice::Started { amounts, state0 } => Started { amounts, state0 }, - Alice::Negotiated(state3) => Negotiated { - channel: None, - amounts: SwapAmounts { - btc: state3.btc, - xmr: state3.xmr, - }, - state3, - }, - Alice::BtcLocked(state3) => BtcLocked { - channel: None, - amounts: SwapAmounts { - btc: state3.btc, - xmr: state3.xmr, - }, - state3, - }, - Alice::XmrLocked(state3) => XmrLocked { state3 }, - Alice::EncSigLearned { - state, - encrypted_signature, - } => EncSigLearned { - state3: state, - encrypted_signature, - }, - Alice::CancelTimelockExpired(state3) => AliceState::CancelTimelockExpired { state3 }, - Alice::BtcCancelled(state) => { - let tx_cancel = bitcoin::TxCancel::new( - &state.tx_lock, - state.cancel_timelock, - state.a.public(), - state.B, - ); - - BtcCancelled { - state3: state, - tx_cancel, - } - } - Alice::BtcPunishable(state) => { - let tx_cancel = bitcoin::TxCancel::new( - &state.tx_lock, - state.cancel_timelock, - state.a.public(), - state.B, - ); - let tx_refund = bitcoin::TxRefund::new(&tx_cancel, &state.refund_address); - BtcPunishable { - tx_refund, - state3: state, - } - } - Alice::BtcRefunded { - state3: state, - spend_key, - .. - } => BtcRefunded { - spend_key, - state3: state, - }, - Alice::Done(end_state) => match end_state { - AliceEndState::SafelyAborted => SafelyAborted, - AliceEndState::BtcRedeemed => BtcRedeemed, - AliceEndState::XmrRefunded => XmrRefunded, - AliceEndState::BtcPunished => BtcPunished, - }, - } - } -} - pub async fn swap( state: AliceState, event_loop_handle: EventLoopHandle, @@ -285,7 +174,7 @@ pub async fn run_until( let state = AliceState::Negotiated { channel: Some(channel), amounts, - state3, + state3: Box::new(state3), }; let db_state = (&state).into(); @@ -356,7 +245,7 @@ pub async fn run_until( lock_xmr( channel, amounts, - state3.clone(), + *state3.clone(), &mut event_loop_handle, monero_wallet.clone(), ) diff --git a/swap/src/bob.rs b/swap/src/bob.rs index 852e0f0f..33917b57 100644 --- a/swap/src/bob.rs +++ b/swap/src/bob.rs @@ -45,13 +45,12 @@ pub fn new_swarm(transport: SwapTransport, behaviour: Behaviour) -> Result), + Message1(Box), Message2(alice::Message2), Message3, } @@ -77,7 +76,7 @@ impl From for OutEvent { impl From for OutEvent { fn from(event: message0::OutEvent) -> Self { match event { - message0::OutEvent::Msg(msg) => OutEvent::Message0(msg), + message0::OutEvent::Msg(msg) => OutEvent::Message0(Box::new(msg)), } } } @@ -85,7 +84,7 @@ impl From for OutEvent { impl From for OutEvent { fn from(event: message1::OutEvent) -> Self { match event { - message1::OutEvent::Msg(msg) => OutEvent::Message1(msg), + message1::OutEvent::Msg(msg) => OutEvent::Message1(Box::new(msg)), } } } diff --git a/swap/src/bob/amounts.rs b/swap/src/bob/amounts.rs index 7428ad30..6350467c 100644 --- a/swap/src/bob/amounts.rs +++ b/swap/src/bob/amounts.rs @@ -19,7 +19,7 @@ use crate::{ SwapAmounts, }; -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] pub enum OutEvent { Amounts(SwapAmounts), } diff --git a/swap/src/bob/event_loop.rs b/swap/src/bob/event_loop.rs index fd88a294..80a3fac9 100644 --- a/swap/src/bob/event_loop.rs +++ b/swap/src/bob/event_loop.rs @@ -12,6 +12,7 @@ use tokio::{ use tracing::{debug, error, info}; use xmr_btc::{alice, bitcoin::EncryptedSignature, bob}; +#[derive(Debug)] pub struct Channels { sender: Sender, receiver: Receiver, @@ -30,6 +31,7 @@ impl Default for Channels { } } +#[derive(Debug)] pub struct EventLoopHandle { msg0: Receiver, msg1: Receiver, @@ -105,6 +107,7 @@ impl EventLoopHandle { } } +#[allow(missing_debug_implementations)] pub struct EventLoop { swarm: libp2p::Swarm, alice_peer_id: PeerId, @@ -189,10 +192,10 @@ impl EventLoop { } OutEvent::Amounts(_amounts) => info!("Amounts received from Alice"), OutEvent::Message0(msg) => { - let _ = self.msg0.send(msg).await; + let _ = self.msg0.send(*msg).await; } OutEvent::Message1(msg) => { - let _ = self.msg1.send(msg).await; + let _ = self.msg1.send(*msg).await; } OutEvent::Message2(msg) => { let _ = self.msg2.send(msg).await; diff --git a/swap/src/bob/message0.rs b/swap/src/bob/message0.rs index 89504fbb..16ae9588 100644 --- a/swap/src/bob/message0.rs +++ b/swap/src/bob/message0.rs @@ -33,7 +33,7 @@ pub struct Message0 { impl Message0 { pub fn send(&mut self, alice: PeerId, msg: bob::Message0) { - let msg = BobToAlice::Message0(msg); + let msg = BobToAlice::Message0(Box::new(msg)); let _id = self.rr.send_request(&alice, msg); } @@ -80,7 +80,7 @@ impl NetworkBehaviourEventProcess> } => { if let AliceToBob::Message0(msg) = response { debug!("Received Message0"); - self.events.push_back(OutEvent::Msg(msg)); + self.events.push_back(OutEvent::Msg(*msg)); } } RequestResponseEvent::InboundFailure { error, .. } => { diff --git a/swap/src/bob/message1.rs b/swap/src/bob/message1.rs index 32fd6c52..62696180 100644 --- a/swap/src/bob/message1.rs +++ b/swap/src/bob/message1.rs @@ -80,7 +80,7 @@ impl NetworkBehaviourEventProcess> } => { if let AliceToBob::Message1(msg) = response { debug!("Received Message1"); - self.events.push_back(OutEvent::Msg(msg)); + self.events.push_back(OutEvent::Msg(*msg)); } } RequestResponseEvent::InboundFailure { error, .. } => { diff --git a/swap/src/bob/message3.rs b/swap/src/bob/message3.rs index 20873bfa..e53c08e9 100644 --- a/swap/src/bob/message3.rs +++ b/swap/src/bob/message3.rs @@ -16,7 +16,7 @@ use tracing::error; use crate::network::request_response::{AliceToBob, BobToAlice, Codec, Message3Protocol, TIMEOUT}; use xmr_btc::bob; -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub enum OutEvent { Msg, } diff --git a/swap/src/bob/swap.rs b/swap/src/bob/swap.rs index d3b6ec05..223f29c1 100644 --- a/swap/src/bob/swap.rs +++ b/swap/src/bob/swap.rs @@ -1,10 +1,4 @@ -use crate::{ - bob::event_loop::EventLoopHandle, - state, - state::{Bob, BobEndState}, - storage::Database, - SwapAmounts, -}; +use crate::{bob::event_loop::EventLoopHandle, database, database::Database, SwapAmounts}; use anyhow::{bail, Result}; use async_recursion::async_recursion; use rand::{CryptoRng, RngCore}; @@ -55,46 +49,6 @@ impl fmt::Display for BobState { } } -impl From for state::Bob { - fn from(bob_state: BobState) -> Self { - match bob_state { - BobState::Started { state0, amounts } => Bob::Started { state0, amounts }, - BobState::Negotiated(state2) => Bob::Negotiated { state2 }, - BobState::BtcLocked(state3) => Bob::BtcLocked { state3 }, - BobState::XmrLocked(state4) => Bob::XmrLocked { state4 }, - BobState::EncSigSent(state4) => Bob::EncSigSent { state4 }, - BobState::BtcRedeemed(state5) => Bob::BtcRedeemed(state5), - BobState::CancelTimelockExpired(state4) => Bob::CancelTimelockExpired(state4), - BobState::BtcCancelled(state4) => Bob::BtcCancelled(state4), - BobState::BtcRefunded(state4) => Bob::Done(BobEndState::BtcRefunded(Box::new(state4))), - BobState::XmrRedeemed => Bob::Done(BobEndState::XmrRedeemed), - BobState::BtcPunished => Bob::Done(BobEndState::BtcPunished), - BobState::SafelyAborted => Bob::Done(BobEndState::SafelyAborted), - } - } -} - -impl From for BobState { - fn from(db_state: state::Bob) -> Self { - match db_state { - Bob::Started { state0, amounts } => BobState::Started { state0, amounts }, - Bob::Negotiated { state2 } => BobState::Negotiated(state2), - Bob::BtcLocked { state3 } => BobState::BtcLocked(state3), - Bob::XmrLocked { state4 } => BobState::XmrLocked(state4), - Bob::EncSigSent { state4 } => BobState::EncSigSent(state4), - Bob::BtcRedeemed(state5) => BobState::BtcRedeemed(state5), - Bob::CancelTimelockExpired(state4) => BobState::CancelTimelockExpired(state4), - Bob::BtcCancelled(state4) => BobState::BtcCancelled(state4), - Bob::Done(end_state) => match end_state { - BobEndState::SafelyAborted => BobState::SafelyAborted, - BobEndState::XmrRedeemed => BobState::XmrRedeemed, - BobEndState::BtcRefunded(state4) => BobState::BtcRefunded(*state4), - BobEndState::BtcPunished => BobState::BtcPunished, - }, - } - } -} - // TODO(Franck): Make this a method on a struct #[allow(clippy::too_many_arguments)] pub async fn swap( @@ -179,7 +133,7 @@ where let state = BobState::Negotiated(state2); let db_state = state.clone().into(); - db.insert_latest_state(swap_id, state::Swap::Bob(db_state)) + db.insert_latest_state(swap_id, database::Swap::Bob(db_state)) .await?; run_until( state, @@ -201,7 +155,7 @@ where let state = BobState::BtcLocked(state3); let db_state = state.clone().into(); - db.insert_latest_state(swap_id, state::Swap::Bob(db_state)) + db.insert_latest_state(swap_id, database::Swap::Bob(db_state)) .await?; run_until( state, @@ -255,7 +209,7 @@ where BobState::CancelTimelockExpired(state4) }; let db_state = state.clone().into(); - db.insert_latest_state(swap_id, state::Swap::Bob(db_state)) + db.insert_latest_state(swap_id, database::Swap::Bob(db_state)) .await?; run_until( state, @@ -297,7 +251,7 @@ where BobState::CancelTimelockExpired(state) }; let db_state = state.clone().into(); - db.insert_latest_state(swap_id, state::Swap::Bob(db_state)) + db.insert_latest_state(swap_id, database::Swap::Bob(db_state)) .await?; run_until( state, @@ -333,7 +287,7 @@ where }; let db_state = state.clone().into(); - db.insert_latest_state(swap_id, state::Swap::Bob(db_state)) + db.insert_latest_state(swap_id, database::Swap::Bob(db_state)) .await?; run_until( state, @@ -353,7 +307,7 @@ where let state = BobState::XmrRedeemed; let db_state = state.clone().into(); - db.insert_latest_state(swap_id, state::Swap::Bob(db_state)) + db.insert_latest_state(swap_id, database::Swap::Bob(db_state)) .await?; run_until( state, @@ -377,7 +331,7 @@ where } let state = BobState::BtcCancelled(state4); - db.insert_latest_state(swap_id, state::Swap::Bob(state.clone().into())) + db.insert_latest_state(swap_id, database::Swap::Bob(state.clone().into())) .await?; run_until( @@ -406,7 +360,7 @@ where }; let db_state = state.clone().into(); - db.insert_latest_state(swap_id, state::Swap::Bob(db_state)) + db.insert_latest_state(swap_id, database::Swap::Bob(db_state)) .await?; run_until( state, diff --git a/swap/src/storage.rs b/swap/src/database.rs similarity index 86% rename from swap/src/storage.rs rename to swap/src/database.rs index dc6c3e80..49d61930 100644 --- a/swap/src/storage.rs +++ b/swap/src/database.rs @@ -1,9 +1,15 @@ -use crate::state::Swap; use anyhow::{anyhow, bail, Context, Result}; -use serde::{de::DeserializeOwned, Serialize}; -use std::path::Path; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use std::{fmt::Display, path::Path}; use uuid::Uuid; +mod alice; +mod bob; + +pub use alice::*; +pub use bob::*; + +#[derive(Debug)] pub struct Database(sled::Db); impl Database { @@ -79,11 +85,37 @@ where Ok(serde_cbor::from_slice(&v)?) } +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub enum Swap { + Alice(Alice), + Bob(Bob), +} + +impl From for Swap { + fn from(from: Alice) -> Self { + Swap::Alice(from) + } +} + +impl From for Swap { + fn from(from: Bob) -> Self { + Swap::Bob(from) + } +} + +impl Display for Swap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Swap::Alice(alice) => Display::fmt(alice, f), + Swap::Bob(bob) => Display::fmt(bob, f), + } + } +} + #[cfg(test)] mod tests { - use crate::state::{Alice, AliceEndState, Bob, BobEndState}; - use super::*; + use crate::database::{Alice, AliceEndState, Bob, BobEndState}; #[tokio::test] async fn can_write_and_read_to_multiple_keys() { diff --git a/swap/src/database/alice.rs b/swap/src/database/alice.rs new file mode 100644 index 00000000..f2069d67 --- /dev/null +++ b/swap/src/database/alice.rs @@ -0,0 +1,172 @@ +use crate::{alice::swap::AliceState, SwapAmounts}; +use bitcoin::hashes::core::fmt::Display; +use serde::{Deserialize, Serialize}; +use xmr_btc::{ + alice, + bitcoin::{EncryptedSignature, TxCancel, TxRefund}, + monero, + serde::monero_private_key, +}; + +// Large enum variant is fine because this is only used for storage +// and is dropped once written in DB. +#[allow(clippy::large_enum_variant)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub enum Alice { + Started { + amounts: SwapAmounts, + state0: alice::State0, + }, + Negotiated(alice::State3), + BtcLocked(alice::State3), + XmrLocked(alice::State3), + EncSigLearned { + encrypted_signature: EncryptedSignature, + state3: alice::State3, + }, + CancelTimelockExpired(alice::State3), + BtcCancelled(alice::State3), + BtcPunishable(alice::State3), + BtcRefunded { + state3: alice::State3, + #[serde(with = "monero_private_key")] + spend_key: monero::PrivateKey, + }, + Done(AliceEndState), +} + +#[derive(Copy, Clone, strum::Display, Debug, Deserialize, Serialize, PartialEq)] +pub enum AliceEndState { + SafelyAborted, + BtcRedeemed, + XmrRefunded, + BtcPunished, +} + +impl From<&AliceState> for Alice { + fn from(alice_state: &AliceState) -> Self { + match alice_state { + AliceState::Negotiated { state3, .. } => Alice::Negotiated(state3.as_ref().clone()), + AliceState::BtcLocked { state3, .. } => Alice::BtcLocked(state3.as_ref().clone()), + AliceState::XmrLocked { state3 } => Alice::XmrLocked(state3.as_ref().clone()), + AliceState::EncSigLearned { + state3, + encrypted_signature, + } => Alice::EncSigLearned { + state3: state3.as_ref().clone(), + encrypted_signature: encrypted_signature.clone(), + }, + AliceState::BtcRedeemed => Alice::Done(AliceEndState::BtcRedeemed), + AliceState::BtcCancelled { state3, .. } => Alice::BtcCancelled(state3.as_ref().clone()), + AliceState::BtcRefunded { spend_key, state3 } => Alice::BtcRefunded { + spend_key: *spend_key, + state3: state3.as_ref().clone(), + }, + AliceState::BtcPunishable { state3, .. } => { + Alice::BtcPunishable(state3.as_ref().clone()) + } + AliceState::XmrRefunded => Alice::Done(AliceEndState::XmrRefunded), + AliceState::CancelTimelockExpired { state3 } => { + Alice::CancelTimelockExpired(state3.as_ref().clone()) + } + AliceState::BtcPunished => Alice::Done(AliceEndState::BtcPunished), + AliceState::SafelyAborted => Alice::Done(AliceEndState::SafelyAborted), + AliceState::Started { amounts, state0 } => Alice::Started { + amounts: *amounts, + state0: state0.clone(), + }, + } + } +} + +impl From for AliceState { + fn from(db_state: Alice) -> Self { + match db_state { + Alice::Started { amounts, state0 } => AliceState::Started { amounts, state0 }, + Alice::Negotiated(state3) => AliceState::Negotiated { + channel: None, + amounts: SwapAmounts { + btc: state3.btc, + xmr: state3.xmr, + }, + state3: Box::new(state3), + }, + Alice::BtcLocked(state3) => AliceState::BtcLocked { + channel: None, + amounts: SwapAmounts { + btc: state3.btc, + xmr: state3.xmr, + }, + state3: Box::new(state3), + }, + Alice::XmrLocked(state3) => AliceState::XmrLocked { + state3: Box::new(state3), + }, + Alice::EncSigLearned { + state3: state, + encrypted_signature, + } => AliceState::EncSigLearned { + state3: Box::new(state), + encrypted_signature, + }, + Alice::CancelTimelockExpired(state3) => AliceState::CancelTimelockExpired { + state3: Box::new(state3), + }, + Alice::BtcCancelled(state) => { + let tx_cancel = TxCancel::new( + &state.tx_lock, + state.cancel_timelock, + state.a.public(), + state.B, + ); + + AliceState::BtcCancelled { + state3: Box::new(state), + tx_cancel, + } + } + Alice::BtcPunishable(state3) => { + let tx_cancel = TxCancel::new( + &state3.tx_lock, + state3.cancel_timelock, + state3.a.public(), + state3.B, + ); + let tx_refund = TxRefund::new(&tx_cancel, &state3.refund_address); + AliceState::BtcPunishable { + tx_refund, + state3: Box::new(state3), + } + } + Alice::BtcRefunded { + state3, spend_key, .. + } => AliceState::BtcRefunded { + spend_key, + state3: Box::new(state3), + }, + Alice::Done(end_state) => match end_state { + AliceEndState::SafelyAborted => AliceState::SafelyAborted, + AliceEndState::BtcRedeemed => AliceState::BtcRedeemed, + AliceEndState::XmrRefunded => AliceState::XmrRefunded, + AliceEndState::BtcPunished => AliceState::BtcPunished, + }, + } + } +} + +impl Display for Alice { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Alice::Started { .. } => write!(f, "Started"), + Alice::Negotiated(_) => f.write_str("Negotiated"), + Alice::BtcLocked(_) => f.write_str("Bitcoin locked"), + Alice::XmrLocked(_) => f.write_str("Monero locked"), + Alice::CancelTimelockExpired(_) => f.write_str("Cancel timelock is expired"), + Alice::BtcCancelled(_) => f.write_str("Bitcoin cancel transaction published"), + Alice::BtcPunishable(_) => f.write_str("Bitcoin punishable"), + Alice::BtcRefunded { .. } => f.write_str("Monero refundable"), + Alice::Done(end_state) => write!(f, "Done: {}", end_state), + Alice::EncSigLearned { .. } => f.write_str("Encrypted signature learned"), + } + } +} diff --git a/swap/src/database/bob.rs b/swap/src/database/bob.rs new file mode 100644 index 00000000..0f462138 --- /dev/null +++ b/swap/src/database/bob.rs @@ -0,0 +1,92 @@ +use crate::{bob::swap::BobState, SwapAmounts}; +use bitcoin::hashes::core::fmt::Display; +use serde::{Deserialize, Serialize}; +use xmr_btc::bob; + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub enum Bob { + Started { + state0: bob::State0, + amounts: SwapAmounts, + }, + Negotiated { + state2: bob::State2, + }, + BtcLocked { + state3: bob::State3, + }, + XmrLocked { + state4: bob::State4, + }, + EncSigSent { + state4: bob::State4, + }, + BtcRedeemed(bob::State5), + CancelTimelockExpired(bob::State4), + BtcCancelled(bob::State4), + Done(BobEndState), +} + +#[derive(Clone, strum::Display, Debug, Deserialize, Serialize, PartialEq)] +pub enum BobEndState { + SafelyAborted, + XmrRedeemed, + BtcRefunded(Box), + BtcPunished, +} + +impl From for Bob { + fn from(bob_state: BobState) -> Self { + match bob_state { + BobState::Started { state0, amounts } => Bob::Started { state0, amounts }, + BobState::Negotiated(state2) => Bob::Negotiated { state2 }, + BobState::BtcLocked(state3) => Bob::BtcLocked { state3 }, + BobState::XmrLocked(state4) => Bob::XmrLocked { state4 }, + BobState::EncSigSent(state4) => Bob::EncSigSent { state4 }, + BobState::BtcRedeemed(state5) => Bob::BtcRedeemed(state5), + BobState::CancelTimelockExpired(state4) => Bob::CancelTimelockExpired(state4), + BobState::BtcCancelled(state4) => Bob::BtcCancelled(state4), + BobState::BtcRefunded(state4) => Bob::Done(BobEndState::BtcRefunded(Box::new(state4))), + BobState::XmrRedeemed => Bob::Done(BobEndState::XmrRedeemed), + BobState::BtcPunished => Bob::Done(BobEndState::BtcPunished), + BobState::SafelyAborted => Bob::Done(BobEndState::SafelyAborted), + } + } +} + +impl From for BobState { + fn from(db_state: Bob) -> Self { + match db_state { + Bob::Started { state0, amounts } => BobState::Started { state0, amounts }, + Bob::Negotiated { state2 } => BobState::Negotiated(state2), + Bob::BtcLocked { state3 } => BobState::BtcLocked(state3), + Bob::XmrLocked { state4 } => BobState::XmrLocked(state4), + Bob::EncSigSent { state4 } => BobState::EncSigSent(state4), + Bob::BtcRedeemed(state5) => BobState::BtcRedeemed(state5), + Bob::CancelTimelockExpired(state4) => BobState::CancelTimelockExpired(state4), + Bob::BtcCancelled(state4) => BobState::BtcCancelled(state4), + Bob::Done(end_state) => match end_state { + BobEndState::SafelyAborted => BobState::SafelyAborted, + BobEndState::XmrRedeemed => BobState::XmrRedeemed, + BobEndState::BtcRefunded(state4) => BobState::BtcRefunded(*state4), + BobEndState::BtcPunished => BobState::BtcPunished, + }, + } + } +} + +impl Display for Bob { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Bob::Started { .. } => write!(f, "Started"), + Bob::Negotiated { .. } => f.write_str("Negotiated"), + Bob::BtcLocked { .. } => f.write_str("Bitcoin locked"), + Bob::XmrLocked { .. } => f.write_str("Monero locked"), + Bob::CancelTimelockExpired(_) => f.write_str("Cancel timelock is expired"), + Bob::BtcCancelled(_) => f.write_str("Bitcoin refundable"), + Bob::BtcRedeemed(_) => f.write_str("Monero redeemable"), + Bob::Done(end_state) => write!(f, "Done: {}", end_state), + Bob::EncSigSent { .. } => f.write_str("Encrypted signature sent"), + } + } +} diff --git a/swap/src/lib.rs b/swap/src/lib.rs index b71d005f..04a66898 100644 --- a/swap/src/lib.rs +++ b/swap/src/lib.rs @@ -1,3 +1,16 @@ +#![warn( + unused_extern_crates, + missing_debug_implementations, + missing_copy_implementations, + rust_2018_idioms, + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::fallible_impl_from, + clippy::cast_precision_loss, + clippy::cast_possible_wrap, + clippy::dbg_macro +)] +#![forbid(unsafe_code)] #![allow(non_snake_case)] use ::serde::{Deserialize, Serialize}; @@ -7,10 +20,9 @@ pub mod alice; pub mod bitcoin; pub mod bob; pub mod cli; +pub mod database; pub mod monero; pub mod network; -pub mod state; -pub mod storage; pub mod trace; pub type Never = std::convert::Infallible; @@ -42,7 +54,7 @@ pub struct SwapAmounts { // TODO: Display in XMR and BTC (not picos and sats). impl Display for SwapAmounts { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{} sats for {} piconeros", diff --git a/swap/src/bin/swap.rs b/swap/src/main.rs similarity index 99% rename from swap/src/bin/swap.rs rename to swap/src/main.rs index deb18d18..419b3320 100644 --- a/swap/src/bin/swap.rs +++ b/swap/src/main.rs @@ -11,6 +11,7 @@ clippy::dbg_macro )] #![forbid(unsafe_code)] +#![allow(non_snake_case)] use anyhow::{bail, Context, Result}; use libp2p::{core::Multiaddr, PeerId}; @@ -24,10 +25,9 @@ use swap::{ bitcoin, bob, bob::swap::BobState, cli::{Command, Options, Resume}, + database::{Database, Swap}, monero, network::transport::build, - state::Swap, - storage::Database, trace::init_tracing, SwapAmounts, }; diff --git a/swap/src/monero.rs b/swap/src/monero.rs index e97e9d64..3c860c5c 100644 --- a/swap/src/monero.rs +++ b/swap/src/monero.rs @@ -7,6 +7,7 @@ use url::Url; pub use xmr_btc::monero::*; +#[derive(Debug)] pub struct Wallet(pub wallet::Client); impl Wallet { diff --git a/swap/src/network.rs b/swap/src/network.rs index 90bf44de..916ffa5a 100644 --- a/swap/src/network.rs +++ b/swap/src/network.rs @@ -7,6 +7,7 @@ pub mod peer_tracker; pub mod request_response; pub mod transport; +#[allow(missing_debug_implementations)] pub struct TokioExecutor { pub handle: Handle, } diff --git a/swap/src/network/request_response.rs b/swap/src/network/request_response.rs index a042debc..e58d81ad 100644 --- a/swap/src/network/request_response.rs +++ b/swap/src/network/request_response.rs @@ -22,12 +22,11 @@ const BUF_SIZE: usize = 1024 * 1024; /// Messages Bob sends to Alice. #[derive(Clone, Debug, Serialize, Deserialize)] -#[allow(clippy::large_enum_variant)] pub enum BobToAlice { #[serde(with = "::bitcoin::util::amount::serde::as_sat")] AmountsFromBtc(::bitcoin::Amount), AmountsFromXmr(monero::Amount), - Message0(bob::Message0), + Message0(Box), Message1(bob::Message1), Message2(bob::Message2), Message3(bob::Message3), @@ -35,11 +34,10 @@ pub enum BobToAlice { /// Messages Alice sends to Bob. #[derive(Clone, Debug, Serialize, Deserialize)] -#[allow(clippy::large_enum_variant)] pub enum AliceToBob { Amounts(SwapAmounts), - Message0(alice::Message0), - Message1(alice::Message1), + Message0(Box), + Message1(Box), Message2(alice::Message2), Message3, // empty response } diff --git a/swap/src/state.rs b/swap/src/state.rs deleted file mode 100644 index 7590c2c8..00000000 --- a/swap/src/state.rs +++ /dev/null @@ -1,130 +0,0 @@ -use crate::SwapAmounts; -use serde::{Deserialize, Serialize}; -use std::fmt::Display; -use xmr_btc::{alice, bitcoin::EncryptedSignature, bob, monero, serde::monero_private_key}; - -#[allow(clippy::large_enum_variant)] -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] -pub enum Swap { - Alice(Alice), - Bob(Bob), -} - -#[allow(clippy::large_enum_variant)] -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] -pub enum Alice { - Started { - amounts: SwapAmounts, - state0: alice::State0, - }, - Negotiated(alice::State3), - BtcLocked(alice::State3), - XmrLocked(alice::State3), - EncSigLearned { - state: alice::State3, - encrypted_signature: EncryptedSignature, - }, - CancelTimelockExpired(alice::State3), - BtcCancelled(alice::State3), - BtcPunishable(alice::State3), - BtcRefunded { - state3: alice::State3, - #[serde(with = "monero_private_key")] - spend_key: monero::PrivateKey, - }, - Done(AliceEndState), -} - -#[derive(Clone, strum::Display, Debug, Deserialize, Serialize, PartialEq)] -pub enum AliceEndState { - SafelyAborted, - BtcRedeemed, - XmrRefunded, - BtcPunished, -} - -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] -pub enum Bob { - Started { - state0: bob::State0, - amounts: SwapAmounts, - }, - Negotiated { - state2: bob::State2, - }, - BtcLocked { - state3: bob::State3, - }, - XmrLocked { - state4: bob::State4, - }, - EncSigSent { - state4: bob::State4, - }, - BtcRedeemed(bob::State5), - CancelTimelockExpired(bob::State4), - BtcCancelled(bob::State4), - Done(BobEndState), -} - -#[derive(Clone, strum::Display, Debug, Deserialize, Serialize, PartialEq)] -pub enum BobEndState { - SafelyAborted, - XmrRedeemed, - BtcRefunded(Box), - BtcPunished, -} - -impl From for Swap { - fn from(from: Alice) -> Self { - Swap::Alice(from) - } -} - -impl From for Swap { - fn from(from: Bob) -> Self { - Swap::Bob(from) - } -} - -impl Display for Swap { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Swap::Alice(alice) => Display::fmt(alice, f), - Swap::Bob(bob) => Display::fmt(bob, f), - } - } -} - -impl Display for Alice { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Alice::Started { .. } => write!(f, "Started"), - Alice::Negotiated(_) => f.write_str("Negotiated"), - Alice::BtcLocked(_) => f.write_str("Bitcoin locked"), - Alice::XmrLocked(_) => f.write_str("Monero locked"), - Alice::CancelTimelockExpired(_) => f.write_str("Cancel timelock is expired"), - Alice::BtcCancelled(_) => f.write_str("Bitcoin cancel transaction published"), - Alice::BtcPunishable(_) => f.write_str("Bitcoin punishable"), - Alice::BtcRefunded { .. } => f.write_str("Monero refundable"), - Alice::Done(end_state) => write!(f, "Done: {}", end_state), - Alice::EncSigLearned { .. } => f.write_str("Encrypted signature learned"), - } - } -} - -impl Display for Bob { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Bob::Started { .. } => write!(f, "Started"), - Bob::Negotiated { .. } => f.write_str("Negotiated"), - Bob::BtcLocked { .. } => f.write_str("Bitcoin locked"), - Bob::XmrLocked { .. } => f.write_str("Monero locked"), - Bob::CancelTimelockExpired(_) => f.write_str("Cancel timelock is expired"), - Bob::BtcCancelled(_) => f.write_str("Bitcoin refundable"), - Bob::BtcRedeemed(_) => f.write_str("Monero redeemable"), - Bob::Done(end_state) => write!(f, "Done: {}", end_state), - Bob::EncSigSent { .. } => f.write_str("Encrypted signature sent"), - } - } -} diff --git a/swap/tests/happy_path_restart_alice.rs b/swap/tests/happy_path_restart_alice.rs index d2dfd9d8..4ab738d3 100644 --- a/swap/tests/happy_path_restart_alice.rs +++ b/swap/tests/happy_path_restart_alice.rs @@ -2,7 +2,7 @@ use crate::testutils::{init_alice, init_bob}; use get_port::get_port; use libp2p::Multiaddr; use rand::rngs::OsRng; -use swap::{alice, alice::swap::AliceState, bitcoin, bob, storage::Database}; +use swap::{alice, alice::swap::AliceState, bitcoin, bob, database::Database}; use tempfile::tempdir; use testcontainers::clients::Cli; use testutils::init_tracing; @@ -112,8 +112,8 @@ async fn given_alice_restarts_after_encsig_is_learned_resume_swap() { let alice_db = Database::open(alice_db_datadir.path()).unwrap(); let resume_state = - if let swap::state::Swap::Alice(state) = alice_db.get_state(alice_swap_id).unwrap() { - assert!(matches!(state, swap::state::Alice::EncSigLearned {..})); + if let swap::database::Swap::Alice(state) = alice_db.get_state(alice_swap_id).unwrap() { + assert!(matches!(state, swap::database::Alice::EncSigLearned {..})); state.into() } else { unreachable!() diff --git a/swap/tests/happy_path_restart_bob_after_comm.rs b/swap/tests/happy_path_restart_bob_after_comm.rs index a3ec875d..fb7310c8 100644 --- a/swap/tests/happy_path_restart_bob_after_comm.rs +++ b/swap/tests/happy_path_restart_bob_after_comm.rs @@ -2,7 +2,7 @@ use crate::testutils::{init_alice, init_bob}; use get_port::get_port; use libp2p::Multiaddr; use rand::rngs::OsRng; -use swap::{alice, bitcoin, bob, bob::swap::BobState, storage::Database}; +use swap::{alice, bitcoin, bob, bob::swap::BobState, database::Database}; use tempfile::tempdir; use testcontainers::clients::Cli; use testutils::init_tracing; @@ -114,13 +114,13 @@ async fn given_bob_restarts_after_encsig_is_sent_resume_swap() { let bob_db = Database::open(bob_db_datadir.path()).unwrap(); - let resume_state = if let swap::state::Swap::Bob(state) = bob_db.get_state(bob_swap_id).unwrap() - { - assert!(matches!(state, swap::state::Bob::EncSigSent {..})); - state.into() - } else { - unreachable!() - }; + let resume_state = + if let swap::database::Swap::Bob(state) = bob_db.get_state(bob_swap_id).unwrap() { + assert!(matches!(state, swap::database::Bob::EncSigSent {..})); + state.into() + } else { + unreachable!() + }; let (event_loop_after_restart, event_loop_handle_after_restart) = testutils::init_bob_event_loop(alice_peer_id, alice_multiaddr); diff --git a/swap/tests/happy_path_restart_bob_before_comm.rs b/swap/tests/happy_path_restart_bob_before_comm.rs index 63197e8f..ede14655 100644 --- a/swap/tests/happy_path_restart_bob_before_comm.rs +++ b/swap/tests/happy_path_restart_bob_before_comm.rs @@ -2,7 +2,7 @@ use crate::testutils::{init_alice, init_bob}; use get_port::get_port; use libp2p::Multiaddr; use rand::rngs::OsRng; -use swap::{alice, alice::swap::AliceState, bitcoin, bob, bob::swap::BobState, storage::Database}; +use swap::{alice, alice::swap::AliceState, bitcoin, bob, bob::swap::BobState, database::Database}; use tempfile::tempdir; use testcontainers::clients::Cli; use testutils::init_tracing; diff --git a/swap/tests/refund_restart_alice.rs b/swap/tests/refund_restart_alice.rs index 4ebcd2fe..58fc7ee7 100644 --- a/swap/tests/refund_restart_alice.rs +++ b/swap/tests/refund_restart_alice.rs @@ -3,7 +3,7 @@ use futures::future::try_join; use get_port::get_port; use libp2p::Multiaddr; use rand::rngs::OsRng; -use swap::{alice, alice::swap::AliceState, bob, bob::swap::BobState, storage::Database}; +use swap::{alice, alice::swap::AliceState, bob, bob::swap::BobState, database::Database}; use tempfile::tempdir; use testcontainers::clients::Cli; use testutils::init_tracing; diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index 3ad2633a..6ec96aae 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -4,8 +4,8 @@ use monero_harness::{image, Monero}; use rand::rngs::OsRng; use std::sync::Arc; use swap::{ - alice, alice::swap::AliceState, bitcoin, bob, bob::swap::BobState, monero, - network::transport::build, storage::Database, SwapAmounts, + alice, alice::swap::AliceState, bitcoin, bob, bob::swap::BobState, database::Database, monero, + network::transport::build, SwapAmounts, }; use tempfile::tempdir; use testcontainers::{clients::Cli, Container}; diff --git a/xmr-btc/src/alice.rs b/xmr-btc/src/alice.rs index 29abc24a..ce52a320 100644 --- a/xmr-btc/src/alice.rs +++ b/xmr-btc/src/alice.rs @@ -376,11 +376,10 @@ pub async fn next_state< state6.redeem_btc(bitcoin_wallet).await?; Ok(state6.into()) } - State::State6(state6) => Ok(state6.into()), + State::State6(state6) => Ok((*state6).into()), } } -#[allow(clippy::large_enum_variant)] #[derive(Debug, Deserialize, Serialize)] pub enum State { State0(State0), @@ -389,7 +388,7 @@ pub enum State { State3(State3), State4(State4), State5(State5), - State6(State6), + State6(Box), } impl_try_from_parent_enum!(State0, State); @@ -398,7 +397,7 @@ impl_try_from_parent_enum!(State2, State); impl_try_from_parent_enum!(State3, State); impl_try_from_parent_enum!(State4, State); impl_try_from_parent_enum!(State5, State); -impl_try_from_parent_enum!(State6, State); +impl_try_from_parent_enum_for_boxed!(State6, State); impl_from_child_enum!(State0, State); impl_from_child_enum!(State1, State); @@ -406,7 +405,7 @@ impl_from_child_enum!(State2, State); impl_from_child_enum!(State3, State); impl_from_child_enum!(State4, State); impl_from_child_enum!(State5, State); -impl_from_child_enum!(State6, State); +impl_from_child_enum_for_boxed!(State6, State); impl State { pub fn new( diff --git a/xmr-btc/src/bitcoin/transactions.rs b/xmr-btc/src/bitcoin/transactions.rs index b15ba8c3..8966cd8b 100644 --- a/xmr-btc/src/bitcoin/transactions.rs +++ b/xmr-btc/src/bitcoin/transactions.rs @@ -50,6 +50,8 @@ impl TxLock { } pub fn as_outpoint(&self) -> OutPoint { + // This is fine because a transaction that has that many outputs is not + // realistic #[allow(clippy::cast_possible_truncation)] OutPoint::new(self.inner.txid(), self.lock_output_vout() as u32) } diff --git a/xmr-btc/src/bob.rs b/xmr-btc/src/bob.rs index 7fa9569e..696d3cbd 100644 --- a/xmr-btc/src/bob.rs +++ b/xmr-btc/src/bob.rs @@ -43,7 +43,6 @@ use crate::{ use ::bitcoin::{Transaction, Txid}; pub use message::{Message, Message0, Message1, Message2, Message3}; -#[allow(clippy::large_enum_variant)] #[derive(Debug)] pub enum Action { LockBtc(bitcoin::TxLock), diff --git a/xmr-btc/src/lib.rs b/xmr-btc/src/lib.rs index 3cfe0c50..22fc8807 100644 --- a/xmr-btc/src/lib.rs +++ b/xmr-btc/src/lib.rs @@ -41,6 +41,24 @@ mod utils { }; } + macro_rules! impl_try_from_parent_enum_for_boxed { + ($type:ident, $parent:ident) => { + impl TryFrom<$parent> for $type { + type Error = anyhow::Error; + + fn try_from(from: $parent) -> Result { + if let $parent::$type(inner) = from { + Ok(*inner) + } else { + Err(anyhow::anyhow!( + "Failed to convert parent state to child state" + )) + } + } + } + }; + } + macro_rules! impl_from_child_enum { ($type:ident, $parent:ident) => { impl From<$type> for $parent { @@ -50,6 +68,16 @@ mod utils { } }; } + + macro_rules! impl_from_child_enum_for_boxed { + ($type:ident, $parent:ident) => { + impl From<$type> for $parent { + fn from(from: $type) -> Self { + $parent::$type(Box::new(from)) + } + } + }; + } } pub mod alice;