mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-12-25 15:39:25 -05:00
Implemented Alice transition from Started
to Negotiated
This commit is contained in:
parent
0fe5131a8a
commit
c4cd64d134
@ -4,7 +4,6 @@ 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},
|
||||||
@ -16,7 +15,7 @@ use crate::{
|
|||||||
storage::Database,
|
storage::Database,
|
||||||
SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK,
|
SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::{bail, Result};
|
||||||
use async_recursion::async_recursion;
|
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 _};
|
||||||
@ -60,23 +59,99 @@ pub enum AliceState {
|
|||||||
|
|
||||||
// State machine driver for swap execution
|
// State machine driver for swap execution
|
||||||
#[async_recursion]
|
#[async_recursion]
|
||||||
pub async fn simple_swap(state: AliceState, io: Io) -> Result<AliceState> {
|
pub async fn simple_swap<R: RngCore + CryptoRng>(
|
||||||
|
state: AliceState,
|
||||||
|
&mut rng: R,
|
||||||
|
mut swarm: Swarm,
|
||||||
|
bitcoin_wallet: Arc<crate::bitcoin::Wallet>,
|
||||||
|
) -> Result<AliceState> {
|
||||||
match state {
|
match state {
|
||||||
AliceState::Started => {
|
AliceState::Started => {
|
||||||
// Alice and Bob exchange swap info
|
// Bob dials us
|
||||||
// Todo: Poll the swarm here until Alice and Bob have exchanged info
|
let bob_peer_id = match swarm.next().await {
|
||||||
simple_swap(AliceState::Negotiated, io).await
|
OutEvent::ConnectionEstablished(bob_peer_id) => bob_peer_id,
|
||||||
|
other => bail!("Unexpected event received: {:?}", other),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bob sends us a request
|
||||||
|
let (btc, channel) = match swarm.next().await {
|
||||||
|
OutEvent::Request(amounts::OutEvent::Btc { btc, channel }) => (btc, channel),
|
||||||
|
other => bail!("Unexpected event received: {:?}", other),
|
||||||
|
};
|
||||||
|
|
||||||
|
let amounts = calculate_amounts(btc);
|
||||||
|
swarm.send_amounts(channel, amounts);
|
||||||
|
|
||||||
|
let SwapAmounts { btc, xmr } = amounts;
|
||||||
|
|
||||||
|
let redeem_address = bitcoin_wallet.as_ref().new_address().await?;
|
||||||
|
let punish_address = redeem_address.clone();
|
||||||
|
|
||||||
|
let state0 = State0::new(
|
||||||
|
rng,
|
||||||
|
btc,
|
||||||
|
xmr,
|
||||||
|
REFUND_TIMELOCK,
|
||||||
|
PUNISH_TIMELOCK,
|
||||||
|
redeem_address,
|
||||||
|
punish_address,
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO(Franck) This is not needed
|
||||||
|
// Review if the swarm really needs to store states
|
||||||
|
swarm.set_state0(state0.clone());
|
||||||
|
|
||||||
|
// Bob sends us message0
|
||||||
|
let message0 = match swarm.next().await {
|
||||||
|
OutEvent::Message0(msg) => msg,
|
||||||
|
other => bail!("Unexpected event received: {:?}", other),
|
||||||
|
};
|
||||||
|
|
||||||
|
let state1 = state0.receive(message0)?;
|
||||||
|
|
||||||
|
// TODO(Franck) We should use the same channel everytime,
|
||||||
|
// Can we remove this response channel?
|
||||||
|
let (state2, channel) = match swarm.next().await {
|
||||||
|
OutEvent::Message1 { msg, channel } => {
|
||||||
|
let state2 = state1.receive(msg);
|
||||||
|
(state2, channel)
|
||||||
|
}
|
||||||
|
other => bail!("Unexpected event: {:?}", other),
|
||||||
|
};
|
||||||
|
|
||||||
|
let message1 = state2.next_message();
|
||||||
|
swarm.send_message1(channel, message1);
|
||||||
|
|
||||||
|
let (state3, channel) = match swarm.next().await {
|
||||||
|
OutEvent::Message2 { msg, channel } => {
|
||||||
|
let state3 = state2.receive(msg)?;
|
||||||
|
(state3, channel)
|
||||||
|
}
|
||||||
|
other => bail!("Unexpected event: {:?}", other),
|
||||||
|
};
|
||||||
|
|
||||||
|
let swap_id = Uuid::new_v4();
|
||||||
|
// TODO(Franck): Use the same terminology (negotiated) to describe this state.
|
||||||
|
db.insert_latest_state(swap_id, state::Alice::Handshaken(state3.clone()).into())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"State transitioned from Started to Negotiated, Bob peer id is {}",
|
||||||
|
bob_peer_id
|
||||||
|
);
|
||||||
|
|
||||||
|
simple_swap(AliceState::Negotiated, swarm, rng, bitcoin_wallet).await
|
||||||
}
|
}
|
||||||
AliceState::Negotiated => {
|
AliceState::Negotiated => {
|
||||||
// Alice and Bob have exchanged info
|
// Alice and Bob have exchanged info
|
||||||
// Todo: Alice watches for BTC to be locked on chain
|
// Todo: Alice watches for BTC to be locked on chain
|
||||||
// Todo: Timeout at t1?
|
// Todo: Timeout at t1?
|
||||||
simple_swap(AliceState::BtcLocked, io).await
|
simple_swap(AliceState::BtcLocked, swarm, rng, bitcoin_wallet).await
|
||||||
}
|
}
|
||||||
AliceState::BtcLocked => {
|
AliceState::BtcLocked => {
|
||||||
// Alice has seen that Bob has locked BTC
|
// Alice has seen that Bob has locked BTC
|
||||||
// Todo: Alice locks XMR
|
// Todo: Alice locks XMR
|
||||||
simple_swap(AliceState::XmrLocked, io).await
|
simple_swap(AliceState::XmrLocked, swarm, bitcoin_wallet).await
|
||||||
}
|
}
|
||||||
AliceState::XmrLocked => {
|
AliceState::XmrLocked => {
|
||||||
// Alice has locked Xmr
|
// Alice has locked Xmr
|
||||||
@ -84,10 +159,10 @@ pub async fn simple_swap(state: AliceState, io: Io) -> Result<AliceState> {
|
|||||||
// Todo: Poll the swarm here until msg from Bob arrives or t1
|
// Todo: Poll the swarm here until msg from Bob arrives or t1
|
||||||
if unimplemented!("key_received") {
|
if unimplemented!("key_received") {
|
||||||
// Alice redeems BTC
|
// Alice redeems BTC
|
||||||
simple_swap(AliceState::BtcRedeemed, io).await
|
simple_swap(AliceState::BtcRedeemed, swarm, bitcoin_wallet).await
|
||||||
} else {
|
} else {
|
||||||
// submit TxCancel
|
// submit TxCancel
|
||||||
simple_swap(AliceState::Cancelled, io).await
|
simple_swap(AliceState::Cancelled, swarm, bitcoin_wallet).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AliceState::Cancelled => {
|
AliceState::Cancelled => {
|
||||||
@ -95,9 +170,9 @@ pub async fn simple_swap(state: AliceState, io: Io) -> Result<AliceState> {
|
|||||||
// If Bob has refunded the Alice should extract Bob's monero secret key and move
|
// If Bob has refunded the Alice should extract Bob's monero secret key and move
|
||||||
// the TxLockXmr output to her wallet.
|
// the TxLockXmr output to her wallet.
|
||||||
if unimplemented!("refunded") {
|
if unimplemented!("refunded") {
|
||||||
simple_swap(AliceState::XmrRefunded, io).await
|
simple_swap(AliceState::XmrRefunded, swarm, bitcoin_wallet).await
|
||||||
} else {
|
} else {
|
||||||
simple_swap(AliceState::Punished, io).await
|
simple_swap(AliceState::Punished, swarm, bitcoin_wallet).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AliceState::XmrRefunded => Ok(AliceState::XmrRefunded),
|
AliceState::XmrRefunded => Ok(AliceState::XmrRefunded),
|
||||||
@ -109,30 +184,30 @@ pub async fn simple_swap(state: AliceState, io: Io) -> Result<AliceState> {
|
|||||||
|
|
||||||
// State machine driver for recovery execution
|
// State machine driver for recovery execution
|
||||||
#[async_recursion]
|
#[async_recursion]
|
||||||
pub async fn abort(state: AliceState, io: Io) -> Result<AliceState> {
|
pub async fn abort(state: AliceState) -> Result<AliceState> {
|
||||||
match state {
|
match state {
|
||||||
AliceState::Started => {
|
AliceState::Started => {
|
||||||
// Nothing has been commited by either party, abort swap.
|
// Nothing has been commited by either party, abort swap.
|
||||||
abort(AliceState::SafelyAborted, io).await
|
abort(AliceState::SafelyAborted).await
|
||||||
}
|
}
|
||||||
AliceState::Negotiated => {
|
AliceState::Negotiated => {
|
||||||
// Nothing has been commited by either party, abort swap.
|
// Nothing has been commited by either party, abort swap.
|
||||||
abort(AliceState::SafelyAborted, io).await
|
abort(AliceState::SafelyAborted).await
|
||||||
}
|
}
|
||||||
AliceState::BtcLocked => {
|
AliceState::BtcLocked => {
|
||||||
// Alice has seen that Bob has locked BTC
|
// Alice has seen that Bob has locked BTC
|
||||||
// Alice does not need to do anything to recover
|
// Alice does not need to do anything to recover
|
||||||
abort(AliceState::SafelyAborted, io).await
|
abort(AliceState::SafelyAborted).await
|
||||||
}
|
}
|
||||||
AliceState::XmrLocked => {
|
AliceState::XmrLocked => {
|
||||||
// Alice has locked XMR
|
// Alice has locked XMR
|
||||||
// Alice watches for TxRedeem until t1
|
// Alice watches for TxRedeem until t1
|
||||||
if unimplemented!("TxRedeemSeen") {
|
if unimplemented!("TxRedeemSeen") {
|
||||||
// Alice has successfully redeemed, protocol was a success
|
// Alice has successfully redeemed, protocol was a success
|
||||||
abort(AliceState::BtcRedeemed, io).await
|
abort(AliceState::BtcRedeemed).await
|
||||||
} else if unimplemented!("T1Elapsed") {
|
} else if unimplemented!("T1Elapsed") {
|
||||||
// publish TxCancel or see if it has been published
|
// publish TxCancel or see if it has been published
|
||||||
abort(AliceState::Cancelled, io).await
|
abort(AliceState::Cancelled).await
|
||||||
} else {
|
} else {
|
||||||
Err(unimplemented!())
|
Err(unimplemented!())
|
||||||
}
|
}
|
||||||
@ -142,11 +217,11 @@ pub async fn abort(state: AliceState, io: Io) -> Result<AliceState> {
|
|||||||
// Alice waits watches for t2 or TxRefund
|
// Alice waits watches for t2 or TxRefund
|
||||||
if unimplemented!("TxRefundSeen") {
|
if unimplemented!("TxRefundSeen") {
|
||||||
// Bob has refunded and leaked s_b
|
// Bob has refunded and leaked s_b
|
||||||
abort(AliceState::XmrRefunded, io).await
|
abort(AliceState::XmrRefunded).await
|
||||||
} else if unimplemented!("T1Elapsed") {
|
} else if unimplemented!("T1Elapsed") {
|
||||||
// publish TxCancel or see if it has been published
|
// publish TxCancel or see if it has been published
|
||||||
// Wait until t2 and publish TxPunish
|
// Wait until t2 and publish TxPunish
|
||||||
abort(AliceState::Punished, io).await
|
abort(AliceState::Punished).await
|
||||||
} else {
|
} else {
|
||||||
Err(unimplemented!())
|
Err(unimplemented!())
|
||||||
}
|
}
|
||||||
@ -164,7 +239,7 @@ pub async fn swap(
|
|||||||
db: Database,
|
db: Database,
|
||||||
listen: Multiaddr,
|
listen: Multiaddr,
|
||||||
transport: SwapTransport,
|
transport: SwapTransport,
|
||||||
behaviour: Alice,
|
behaviour: Behaviour,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
struct Network(Arc<Mutex<Swarm>>);
|
struct Network(Arc<Mutex<Swarm>>);
|
||||||
|
|
||||||
@ -359,9 +434,9 @@ pub async fn swap(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Swarm = libp2p::Swarm<Alice>;
|
pub type Swarm = libp2p::Swarm<Behaviour>;
|
||||||
|
|
||||||
fn new_swarm(listen: Multiaddr, transport: SwapTransport, behaviour: Alice) -> Result<Swarm> {
|
fn new_swarm(listen: Multiaddr, transport: SwapTransport, behaviour: Behaviour) -> Result<Swarm> {
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
|
|
||||||
let local_peer_id = behaviour.peer_id();
|
let local_peer_id = behaviour.peer_id();
|
||||||
@ -384,6 +459,8 @@ fn new_swarm(listen: Multiaddr, transport: SwapTransport, behaviour: Alice) -> R
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum OutEvent {
|
pub enum OutEvent {
|
||||||
ConnectionEstablished(PeerId),
|
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(amounts::OutEvent), // Not-uniform with Bob on purpose, ready for adding Xmr event.
|
||||||
Message0(bob::Message0),
|
Message0(bob::Message0),
|
||||||
Message1 {
|
Message1 {
|
||||||
@ -446,7 +523,7 @@ impl From<message3::OutEvent> for OutEvent {
|
|||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
#[behaviour(out_event = "OutEvent", event_process = false)]
|
#[behaviour(out_event = "OutEvent", event_process = false)]
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
pub struct Alice {
|
pub struct Behaviour {
|
||||||
pt: PeerTracker,
|
pt: PeerTracker,
|
||||||
amounts: Amounts,
|
amounts: Amounts,
|
||||||
message0: Message0,
|
message0: Message0,
|
||||||
@ -457,7 +534,7 @@ pub struct Alice {
|
|||||||
identity: Keypair,
|
identity: Keypair,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Alice {
|
impl Behaviour {
|
||||||
pub fn identity(&self) -> Keypair {
|
pub fn identity(&self) -> Keypair {
|
||||||
self.identity.clone()
|
self.identity.clone()
|
||||||
}
|
}
|
||||||
@ -490,7 +567,7 @@ impl Alice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Alice {
|
impl Default for Behaviour {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let identity = Keypair::generate_ed25519();
|
let identity = Keypair::generate_ed25519();
|
||||||
|
|
||||||
@ -507,8 +584,8 @@ impl Default for Alice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_amounts(btc: ::bitcoin::Amount) -> SwapAmounts {
|
fn calculate_amounts(btc: ::bitcoin::Amount) -> SwapAmounts {
|
||||||
// TODO: Get this from an exchange.
|
// TODO (Franck): This should instead verify that the received amounts matches
|
||||||
// This value corresponds to 100 XMR per BTC
|
// the command line arguments This value corresponds to 100 XMR per BTC
|
||||||
const PICONERO_PER_SAT: u64 = 1_000_000;
|
const PICONERO_PER_SAT: u64 = 1_000_000;
|
||||||
|
|
||||||
let picos = btc.as_sat() * PICONERO_PER_SAT;
|
let picos = btc.as_sat() * PICONERO_PER_SAT;
|
||||||
|
@ -19,7 +19,7 @@ use prettytable::{row, Table};
|
|||||||
use std::{io, io::Write, process, sync::Arc};
|
use std::{io, io::Write, process, sync::Arc};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use swap::{
|
use swap::{
|
||||||
alice::{self, Alice},
|
alice::{self, Behaviour},
|
||||||
bitcoin,
|
bitcoin,
|
||||||
bob::{self, Bob},
|
bob::{self, Bob},
|
||||||
cli::Options,
|
cli::Options,
|
||||||
@ -52,7 +52,7 @@ async fn main() -> Result<()> {
|
|||||||
} => {
|
} => {
|
||||||
info!("running swap node as Alice ...");
|
info!("running swap node as Alice ...");
|
||||||
|
|
||||||
let behaviour = Alice::default();
|
let behaviour = Behaviour::default();
|
||||||
let local_key_pair = behaviour.identity();
|
let local_key_pair = behaviour.identity();
|
||||||
|
|
||||||
let (listen_addr, _ac, transport) = match tor_port {
|
let (listen_addr, _ac, transport) = match tor_port {
|
||||||
@ -180,7 +180,7 @@ async fn swap_as_alice(
|
|||||||
db: Database,
|
db: Database,
|
||||||
addr: Multiaddr,
|
addr: Multiaddr,
|
||||||
transport: SwapTransport,
|
transport: SwapTransport,
|
||||||
behaviour: Alice,
|
behaviour: Behaviour,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
alice::swap(
|
alice::swap(
|
||||||
bitcoin_wallet,
|
bitcoin_wallet,
|
||||||
|
@ -36,7 +36,6 @@ use crate::{
|
|||||||
storage::Database,
|
storage::Database,
|
||||||
Cmd, Rsp, SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK,
|
Cmd, Rsp, SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK,
|
||||||
};
|
};
|
||||||
|
|
||||||
use xmr_btc::{
|
use xmr_btc::{
|
||||||
alice,
|
alice,
|
||||||
bitcoin::{BroadcastSignedTransaction, EncryptedSignature, SignTxLock},
|
bitcoin::{BroadcastSignedTransaction, EncryptedSignature, SignTxLock},
|
||||||
|
@ -74,7 +74,7 @@ async fn swap() {
|
|||||||
));
|
));
|
||||||
let bob_xmr_wallet = Arc::new(swap::monero::Wallet(monero.wallet("bob").unwrap().client()));
|
let bob_xmr_wallet = Arc::new(swap::monero::Wallet(monero.wallet("bob").unwrap().client()));
|
||||||
|
|
||||||
let alice_behaviour = alice::Alice::default();
|
let alice_behaviour = alice::Behaviour::default();
|
||||||
let alice_transport = build(alice_behaviour.identity()).unwrap();
|
let alice_transport = build(alice_behaviour.identity()).unwrap();
|
||||||
|
|
||||||
let db = Database::open(std::path::Path::new("../.swap-db/")).unwrap();
|
let db = Database::open(std::path::Path::new("../.swap-db/")).unwrap();
|
||||||
|
Loading…
Reference in New Issue
Block a user