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:
rishflab 2020-11-16 10:21:17 +11:00
parent ff7daf16f3
commit ae94b170fd
4 changed files with 273 additions and 22 deletions

View File

@ -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>,

View File

@ -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
View 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: _,
}

View File

@ -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;