mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-10-01 01:45:40 -04:00
Cleanup
Move state machine executors into seperate files Remove check for ack message from Alice. Seems like a bad idea to rely on an acknowledgement message instead of looking at the blockchain. Fix warnings
This commit is contained in:
parent
ff7daf16f3
commit
ae94b170fd
@ -1,6 +1,7 @@
|
|||||||
//! Run an XMR/BTC swap in the role of Alice.
|
//! Run an XMR/BTC swap in the role of Alice.
|
||||||
//! Alice holds XMR and wishes receive BTC.
|
//! Alice holds XMR and wishes receive BTC.
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use async_recursion::async_recursion;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use backoff::{backoff::Constant as ConstantBackoff, future::FutureOperation as _};
|
use backoff::{backoff::Constant as ConstantBackoff, future::FutureOperation as _};
|
||||||
use genawaiter::GeneratorState;
|
use genawaiter::GeneratorState;
|
||||||
@ -25,6 +26,7 @@ use self::{amounts::*, message0::*, message1::*, message2::*, message3::*};
|
|||||||
use crate::{
|
use crate::{
|
||||||
bitcoin,
|
bitcoin,
|
||||||
bitcoin::TX_LOCK_MINE_TIMEOUT,
|
bitcoin::TX_LOCK_MINE_TIMEOUT,
|
||||||
|
io::Io,
|
||||||
monero,
|
monero,
|
||||||
network::{
|
network::{
|
||||||
peer_tracker::{self, PeerTracker},
|
peer_tracker::{self, PeerTracker},
|
||||||
@ -36,6 +38,7 @@ use crate::{
|
|||||||
storage::Database,
|
storage::Database,
|
||||||
SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK,
|
SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK,
|
||||||
};
|
};
|
||||||
|
|
||||||
use xmr_btc::{
|
use xmr_btc::{
|
||||||
alice::{self, action_generator, Action, ReceiveBitcoinRedeemEncsig, State0},
|
alice::{self, action_generator, Action, ReceiveBitcoinRedeemEncsig, State0},
|
||||||
bitcoin::BroadcastSignedTransaction,
|
bitcoin::BroadcastSignedTransaction,
|
||||||
@ -43,6 +46,120 @@ use xmr_btc::{
|
|||||||
monero::{CreateWalletForOutput, Transfer},
|
monero::{CreateWalletForOutput, Transfer},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The same data structure is used for swap execution and recovery.
|
||||||
|
// This allows for a seamless transition from a failed swap to recovery.
|
||||||
|
pub enum AliceState {
|
||||||
|
Started,
|
||||||
|
Negotiated,
|
||||||
|
BtcLocked,
|
||||||
|
XmrLocked,
|
||||||
|
BtcRedeemed,
|
||||||
|
XmrRefunded,
|
||||||
|
Cancelled,
|
||||||
|
Punished,
|
||||||
|
SafelyAborted,
|
||||||
|
}
|
||||||
|
|
||||||
|
// State machine driver for swap execution
|
||||||
|
#[async_recursion]
|
||||||
|
pub async fn simple_swap(state: AliceState, io: Io) -> Result<AliceState> {
|
||||||
|
match state {
|
||||||
|
AliceState::Started => {
|
||||||
|
// Alice and Bob exchange swap info
|
||||||
|
// Todo: Poll the swarm here until Alice and Bob have exchanged info
|
||||||
|
simple_swap(AliceState::Negotiated, io).await
|
||||||
|
}
|
||||||
|
AliceState::Negotiated => {
|
||||||
|
// Alice and Bob have exchanged info
|
||||||
|
// Todo: Alice watches for BTC to be locked on chain
|
||||||
|
// Todo: Timeout at t1?
|
||||||
|
simple_swap(AliceState::BtcLocked, io).await
|
||||||
|
}
|
||||||
|
AliceState::BtcLocked => {
|
||||||
|
// Alice has seen that Bob has locked BTC
|
||||||
|
// Todo: Alice locks XMR
|
||||||
|
simple_swap(AliceState::XmrLocked, io).await
|
||||||
|
}
|
||||||
|
AliceState::XmrLocked => {
|
||||||
|
// Alice has locked Xmr
|
||||||
|
// Alice waits until Bob sends her key to redeem BTC
|
||||||
|
// Todo: Poll the swarm here until msg from Bob arrives or t1
|
||||||
|
if unimplemented!("key_received") {
|
||||||
|
// Alice redeems BTC
|
||||||
|
simple_swap(AliceState::BtcRedeemed, io).await
|
||||||
|
} else {
|
||||||
|
// submit TxCancel
|
||||||
|
simple_swap(AliceState::Cancelled, io).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AliceState::Cancelled => {
|
||||||
|
// Wait until t2 or if TxRefund is seen
|
||||||
|
// If Bob has refunded the Alice should extract Bob's monero secret key and move
|
||||||
|
// the TxLockXmr output to her wallet.
|
||||||
|
if unimplemented!("refunded") {
|
||||||
|
simple_swap(AliceState::XmrRefunded, io).await
|
||||||
|
} else {
|
||||||
|
simple_swap(AliceState::Punished, io).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AliceState::XmrRefunded => Ok(AliceState::XmrRefunded),
|
||||||
|
AliceState::BtcRedeemed => Ok(AliceState::BtcRedeemed),
|
||||||
|
AliceState::Punished => Ok(AliceState::Punished),
|
||||||
|
AliceState::SafelyAborted => Ok(AliceState::SafelyAborted),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// State machine driver for recovery execution
|
||||||
|
#[async_recursion]
|
||||||
|
pub async fn abort(state: AliceState, io: Io) -> Result<AliceState> {
|
||||||
|
match state {
|
||||||
|
AliceState::Started => {
|
||||||
|
// Nothing has been commited by either party, abort swap.
|
||||||
|
abort(AliceState::SafelyAborted, io).await
|
||||||
|
}
|
||||||
|
AliceState::Negotiated => {
|
||||||
|
// Nothing has been commited by either party, abort swap.
|
||||||
|
abort(AliceState::SafelyAborted, io).await
|
||||||
|
}
|
||||||
|
AliceState::BtcLocked => {
|
||||||
|
// Alice has seen that Bob has locked BTC
|
||||||
|
// Alice does not need to do anything to recover
|
||||||
|
abort(AliceState::SafelyAborted, io).await
|
||||||
|
}
|
||||||
|
AliceState::XmrLocked => {
|
||||||
|
// Alice has locked XMR
|
||||||
|
// Alice watches for TxRedeem until t1
|
||||||
|
if unimplemented!("TxRedeemSeen") {
|
||||||
|
// Alice has successfully redeemed, protocol was a success
|
||||||
|
abort(AliceState::BtcRedeemed, io).await
|
||||||
|
} else if unimplemented!("T1Elapsed") {
|
||||||
|
// publish TxCancel or see if it has been published
|
||||||
|
abort(AliceState::Cancelled, io).await
|
||||||
|
} else {
|
||||||
|
Err(unimplemented!())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AliceState::Cancelled => {
|
||||||
|
// Alice has cancelled the swap
|
||||||
|
// Alice waits watches for t2 or TxRefund
|
||||||
|
if unimplemented!("TxRefundSeen") {
|
||||||
|
// Bob has refunded and leaked s_b
|
||||||
|
abort(AliceState::XmrRefunded, io).await
|
||||||
|
} else if unimplemented!("T1Elapsed") {
|
||||||
|
// publish TxCancel or see if it has been published
|
||||||
|
// Wait until t2 and publish TxPunish
|
||||||
|
abort(AliceState::Punished, io).await
|
||||||
|
} else {
|
||||||
|
Err(unimplemented!())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AliceState::BtcRedeemed => Ok(AliceState::BtcRedeemed),
|
||||||
|
AliceState::XmrRefunded => Ok(AliceState::XmrRefunded),
|
||||||
|
AliceState::Punished => Ok(AliceState::Punished),
|
||||||
|
AliceState::SafelyAborted => Ok(AliceState::SafelyAborted),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn swap(
|
pub async fn swap(
|
||||||
bitcoin_wallet: Arc<bitcoin::Wallet>,
|
bitcoin_wallet: Arc<bitcoin::Wallet>,
|
||||||
monero_wallet: Arc<monero::Wallet>,
|
monero_wallet: Arc<monero::Wallet>,
|
||||||
|
169
swap/src/bob.rs
169
swap/src/bob.rs
@ -1,19 +1,7 @@
|
|||||||
//! Run an XMR/BTC swap in the role of Bob.
|
//! Run an XMR/BTC swap in the role of Bob.
|
||||||
//! Bob holds BTC and wishes receive XMR.
|
//! Bob holds BTC and wishes receive XMR.
|
||||||
use self::{amounts::*, message0::*, message1::*, message2::*, message3::*};
|
|
||||||
use crate::{
|
|
||||||
bitcoin::{self, TX_LOCK_MINE_TIMEOUT},
|
|
||||||
monero,
|
|
||||||
network::{
|
|
||||||
peer_tracker::{self, PeerTracker},
|
|
||||||
transport::SwapTransport,
|
|
||||||
TokioExecutor,
|
|
||||||
},
|
|
||||||
state,
|
|
||||||
storage::Database,
|
|
||||||
Cmd, Rsp, SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK,
|
|
||||||
};
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use async_recursion::async_recursion;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use backoff::{backoff::Constant as ConstantBackoff, future::FutureOperation as _};
|
use backoff::{backoff::Constant as ConstantBackoff, future::FutureOperation as _};
|
||||||
use futures::{
|
use futures::{
|
||||||
@ -27,6 +15,28 @@ use std::{process, sync::Arc, time::Duration};
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
mod amounts;
|
||||||
|
mod message0;
|
||||||
|
mod message1;
|
||||||
|
mod message2;
|
||||||
|
mod message3;
|
||||||
|
|
||||||
|
use self::{amounts::*, message0::*, message1::*, message2::*, message3::*};
|
||||||
|
use crate::{
|
||||||
|
bitcoin::{self, TX_LOCK_MINE_TIMEOUT},
|
||||||
|
io::Io,
|
||||||
|
monero,
|
||||||
|
network::{
|
||||||
|
peer_tracker::{self, PeerTracker},
|
||||||
|
transport::SwapTransport,
|
||||||
|
TokioExecutor,
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
storage::Database,
|
||||||
|
Cmd, Rsp, SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK,
|
||||||
|
};
|
||||||
|
|
||||||
use xmr_btc::{
|
use xmr_btc::{
|
||||||
alice,
|
alice,
|
||||||
bitcoin::{BroadcastSignedTransaction, EncryptedSignature, SignTxLock},
|
bitcoin::{BroadcastSignedTransaction, EncryptedSignature, SignTxLock},
|
||||||
@ -34,11 +44,128 @@ use xmr_btc::{
|
|||||||
monero::CreateWalletForOutput,
|
monero::CreateWalletForOutput,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod amounts;
|
// The same data structure is used for swap execution and recovery.
|
||||||
mod message0;
|
// This allows for a seamless transition from a failed swap to recovery.
|
||||||
mod message1;
|
pub enum BobState {
|
||||||
mod message2;
|
Started,
|
||||||
mod message3;
|
Negotiated,
|
||||||
|
BtcLocked,
|
||||||
|
XmrLocked,
|
||||||
|
BtcRedeemed,
|
||||||
|
BtcRefunded,
|
||||||
|
XmrRedeemed,
|
||||||
|
Cancelled,
|
||||||
|
Punished,
|
||||||
|
SafelyAborted,
|
||||||
|
}
|
||||||
|
|
||||||
|
// State machine driver for swap execution
|
||||||
|
#[async_recursion]
|
||||||
|
pub async fn simple_swap(state: BobState, io: Io) -> Result<BobState> {
|
||||||
|
match state {
|
||||||
|
BobState::Started => {
|
||||||
|
// Alice and Bob exchange swap info
|
||||||
|
// Todo: Poll the swarm here until Alice and Bob have exchanged info
|
||||||
|
simple_swap(BobState::Negotiated, io).await
|
||||||
|
}
|
||||||
|
BobState::Negotiated => {
|
||||||
|
// Alice and Bob have exchanged info
|
||||||
|
// Bob Locks Btc
|
||||||
|
simple_swap(BobState::BtcLocked, io).await
|
||||||
|
}
|
||||||
|
BobState::BtcLocked => {
|
||||||
|
// Bob has locked Btc
|
||||||
|
// Watch for Alice to Lock Xmr
|
||||||
|
simple_swap(BobState::XmrLocked, io).await
|
||||||
|
}
|
||||||
|
BobState::XmrLocked => {
|
||||||
|
// Alice has locked Xmr
|
||||||
|
// Bob sends Alice his key
|
||||||
|
// Todo: This should be a oneshot
|
||||||
|
if unimplemented!("Redeemed before t1") {
|
||||||
|
simple_swap(BobState::BtcRedeemed, io).await
|
||||||
|
} else {
|
||||||
|
// submit TxCancel
|
||||||
|
simple_swap(BobState::Cancelled, io).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BobState::Cancelled => {
|
||||||
|
if unimplemented!("<t2") {
|
||||||
|
// submit TxRefund
|
||||||
|
simple_swap(BobState::BtcRefunded, io).await
|
||||||
|
} else {
|
||||||
|
simple_swap(BobState::Punished, io).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BobState::BtcRefunded => Ok(BobState::BtcRefunded),
|
||||||
|
BobState::BtcRedeemed => {
|
||||||
|
// Bob redeems XMR using revealed s_a
|
||||||
|
simple_swap(BobState::XmrRedeemed, io).await
|
||||||
|
}
|
||||||
|
BobState::Punished => Ok(BobState::Punished),
|
||||||
|
BobState::SafelyAborted => Ok(BobState::SafelyAborted),
|
||||||
|
BobState::XmrRedeemed => Ok(BobState::XmrRedeemed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// State machine driver for recovery execution
|
||||||
|
#[async_recursion]
|
||||||
|
pub async fn abort(state: BobState, io: Io) -> Result<BobState> {
|
||||||
|
match state {
|
||||||
|
BobState::Started => {
|
||||||
|
// Nothing has been commited by either party, abort swap.
|
||||||
|
abort(BobState::SafelyAborted, io).await
|
||||||
|
}
|
||||||
|
BobState::Negotiated => {
|
||||||
|
// Nothing has been commited by either party, abort swap.
|
||||||
|
abort(BobState::SafelyAborted, io).await
|
||||||
|
}
|
||||||
|
BobState::BtcLocked => {
|
||||||
|
// Bob has locked BTC and must refund it
|
||||||
|
// Bob waits for alice to publish TxRedeem or t1
|
||||||
|
if unimplemented!("TxRedeemSeen") {
|
||||||
|
// Alice has redeemed revealing s_a
|
||||||
|
abort(BobState::BtcRedeemed, io).await
|
||||||
|
} else if unimplemented!("T1Elapsed") {
|
||||||
|
// publish TxCancel or see if it has been published
|
||||||
|
abort(BobState::Cancelled, io).await
|
||||||
|
} else {
|
||||||
|
Err(unimplemented!())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BobState::XmrLocked => {
|
||||||
|
// Alice has locked Xmr
|
||||||
|
// Wait until t1
|
||||||
|
if unimplemented!(">t1 and <t2") {
|
||||||
|
// Bob publishes TxCancel
|
||||||
|
abort(BobState::Cancelled, io).await
|
||||||
|
} else {
|
||||||
|
// >t2
|
||||||
|
// submit TxCancel
|
||||||
|
abort(BobState::Punished, io).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BobState::Cancelled => {
|
||||||
|
// Bob has cancelled the swap
|
||||||
|
// If <t2 Bob refunds
|
||||||
|
if unimplemented!("<t2") {
|
||||||
|
// Submit TxRefund
|
||||||
|
abort(BobState::BtcRefunded, io).await
|
||||||
|
} else {
|
||||||
|
// Bob failed to refund in time and has been punished
|
||||||
|
abort(BobState::Punished, io).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BobState::BtcRedeemed => {
|
||||||
|
// Bob uses revealed s_a to redeem XMR
|
||||||
|
abort(BobState::XmrRedeemed, io).await
|
||||||
|
}
|
||||||
|
BobState::BtcRefunded => Ok(BobState::BtcRefunded),
|
||||||
|
BobState::Punished => Ok(BobState::Punished),
|
||||||
|
BobState::SafelyAborted => Ok(BobState::SafelyAborted),
|
||||||
|
BobState::XmrRedeemed => Ok(BobState::XmrRedeemed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn swap(
|
pub async fn swap(
|
||||||
@ -97,9 +224,6 @@ pub async fn swap(
|
|||||||
|
|
||||||
swarm.request_amounts(alice.clone(), btc);
|
swarm.request_amounts(alice.clone(), btc);
|
||||||
|
|
||||||
// What is going on here, shouldn't this be a simple req/resp??
|
|
||||||
// Why do we need mspc channels?
|
|
||||||
// Todo: simplify this code
|
|
||||||
let (btc, xmr) = match swarm.next().await {
|
let (btc, xmr) = match swarm.next().await {
|
||||||
OutEvent::Amounts(amounts) => {
|
OutEvent::Amounts(amounts) => {
|
||||||
info!("Got amounts from Alice: {:?}", amounts);
|
info!("Got amounts from Alice: {:?}", amounts);
|
||||||
@ -110,6 +234,7 @@ pub async fn swap(
|
|||||||
info!("User rejected amounts proposed by Alice, aborting...");
|
info!("User rejected amounts proposed by Alice, aborting...");
|
||||||
process::exit(0);
|
process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("User accepted amounts proposed by Alice");
|
info!("User accepted amounts proposed by Alice");
|
||||||
(amounts.btc, amounts.xmr)
|
(amounts.btc, amounts.xmr)
|
||||||
}
|
}
|
||||||
@ -237,7 +362,7 @@ pub async fn swap(
|
|||||||
|
|
||||||
pub type Swarm = libp2p::Swarm<Bob>;
|
pub type Swarm = libp2p::Swarm<Bob>;
|
||||||
|
|
||||||
pub fn new_swarm(transport: SwapTransport, behaviour: Bob) -> Result<Swarm> {
|
fn new_swarm(transport: SwapTransport, behaviour: Bob) -> Result<Swarm> {
|
||||||
let local_peer_id = behaviour.peer_id();
|
let local_peer_id = behaviour.peer_id();
|
||||||
|
|
||||||
let swarm = libp2p::swarm::SwarmBuilder::new(transport, behaviour, local_peer_id.clone())
|
let swarm = libp2p::swarm::SwarmBuilder::new(transport, behaviour, local_peer_id.clone())
|
||||||
|
8
swap/src/io.rs
Normal file
8
swap/src/io.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// This struct contains all the I/O required to execute a swap
|
||||||
|
pub struct Io {
|
||||||
|
// swarm: libp2p::Swarm<>,
|
||||||
|
// bitcoind_rpc: _,
|
||||||
|
// monerod_rpc: _,
|
||||||
|
// monero_wallet_rpc: _,
|
||||||
|
// db: _,
|
||||||
|
}
|
@ -6,6 +6,7 @@ pub mod bitcoin;
|
|||||||
pub mod bob;
|
pub mod bob;
|
||||||
pub mod bob_simple;
|
pub mod bob_simple;
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
|
pub mod io;
|
||||||
pub mod monero;
|
pub mod monero;
|
||||||
pub mod network;
|
pub mod network;
|
||||||
pub mod recover;
|
pub mod recover;
|
||||||
|
Loading…
Reference in New Issue
Block a user