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.
|
||||
//! Alice holds XMR and wishes receive BTC.
|
||||
use anyhow::Result;
|
||||
use async_recursion::async_recursion;
|
||||
use async_trait::async_trait;
|
||||
use backoff::{backoff::Constant as ConstantBackoff, future::FutureOperation as _};
|
||||
use genawaiter::GeneratorState;
|
||||
@ -25,6 +26,7 @@ use self::{amounts::*, message0::*, message1::*, message2::*, message3::*};
|
||||
use crate::{
|
||||
bitcoin,
|
||||
bitcoin::TX_LOCK_MINE_TIMEOUT,
|
||||
io::Io,
|
||||
monero,
|
||||
network::{
|
||||
peer_tracker::{self, PeerTracker},
|
||||
@ -36,6 +38,7 @@ use crate::{
|
||||
storage::Database,
|
||||
SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK,
|
||||
};
|
||||
|
||||
use xmr_btc::{
|
||||
alice::{self, action_generator, Action, ReceiveBitcoinRedeemEncsig, State0},
|
||||
bitcoin::BroadcastSignedTransaction,
|
||||
@ -43,6 +46,120 @@ use xmr_btc::{
|
||||
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(
|
||||
bitcoin_wallet: Arc<bitcoin::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.
|
||||
//! 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 async_recursion::async_recursion;
|
||||
use async_trait::async_trait;
|
||||
use backoff::{backoff::Constant as ConstantBackoff, future::FutureOperation as _};
|
||||
use futures::{
|
||||
@ -27,6 +15,28 @@ use std::{process, sync::Arc, time::Duration};
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::{debug, info, warn};
|
||||
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::{
|
||||
alice,
|
||||
bitcoin::{BroadcastSignedTransaction, EncryptedSignature, SignTxLock},
|
||||
@ -34,11 +44,128 @@ use xmr_btc::{
|
||||
monero::CreateWalletForOutput,
|
||||
};
|
||||
|
||||
mod amounts;
|
||||
mod message0;
|
||||
mod message1;
|
||||
mod message2;
|
||||
mod message3;
|
||||
// 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 BobState {
|
||||
Started,
|
||||
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)]
|
||||
pub async fn swap(
|
||||
@ -97,9 +224,6 @@ pub async fn swap(
|
||||
|
||||
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 {
|
||||
OutEvent::Amounts(amounts) => {
|
||||
info!("Got amounts from Alice: {:?}", amounts);
|
||||
@ -110,6 +234,7 @@ pub async fn swap(
|
||||
info!("User rejected amounts proposed by Alice, aborting...");
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
info!("User accepted amounts proposed by Alice");
|
||||
(amounts.btc, amounts.xmr)
|
||||
}
|
||||
@ -237,7 +362,7 @@ pub async fn swap(
|
||||
|
||||
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 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_simple;
|
||||
pub mod cli;
|
||||
pub mod io;
|
||||
pub mod monero;
|
||||
pub mod network;
|
||||
pub mod recover;
|
||||
|
Loading…
Reference in New Issue
Block a user