From ea08778b2f12cd109fd8dded5f523388eed43eee Mon Sep 17 00:00:00 2001 From: rishflab Date: Mon, 2 Nov 2020 16:00:10 +1100 Subject: [PATCH] Save state for Bob at specific points in the swap --- swap/Cargo.toml | 2 +- swap/src/alice.rs | 2 ++ swap/src/bob.rs | 41 +++++++++++++++++++++++++++++++++++++---- swap/src/main.rs | 10 ++++++++++ swap/src/storage.rs | 32 +++++++++++++++++++++++++++++++- xmr-btc/src/bob.rs | 2 +- 6 files changed, 82 insertions(+), 7 deletions(-) diff --git a/swap/Cargo.toml b/swap/Cargo.toml index af99bcba..8cb330b7 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -19,7 +19,7 @@ genawaiter = "0.99.1" libp2p = { version = "0.29", default-features = false, features = ["tcp-tokio", "yamux", "mplex", "dns", "noise", "request-response"] } libp2p-tokio-socks5 = "0.4" log = { version = "0.4", features = ["serde"] } -monero = "0.9" +monero = { version = "0.9", features = ["serde_support"] } monero-harness = { path = "../monero-harness" } rand = "0.7" reqwest = { version = "0.10", default-features = false, features = ["socks"] } diff --git a/swap/src/alice.rs b/swap/src/alice.rs index 036e4dc8..136f045f 100644 --- a/swap/src/alice.rs +++ b/swap/src/alice.rs @@ -31,6 +31,7 @@ use crate::{ transport::SwapTransport, TokioExecutor, }, + storage::Database, SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK, }; use xmr_btc::{ @@ -43,6 +44,7 @@ use xmr_btc::{ pub async fn swap( bitcoin_wallet: Arc, monero_wallet: Arc, + _db: Database, listen: Multiaddr, transport: SwapTransport, behaviour: Alice, diff --git a/swap/src/bob.rs b/swap/src/bob.rs index 2f973b99..b1296c7c 100644 --- a/swap/src/bob.rs +++ b/swap/src/bob.rs @@ -22,14 +22,14 @@ mod message3; use self::{amounts::*, message0::*, message1::*, message2::*, message3::*}; use crate::{ - bitcoin, - bitcoin::TX_LOCK_MINE_TIMEOUT, + bitcoin::{self, TX_LOCK_MINE_TIMEOUT}, monero, network::{ peer_tracker::{self, PeerTracker}, transport::SwapTransport, TokioExecutor, }, + storage::{self, Database}, Cmd, Rsp, SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK, }; use xmr_btc::{ @@ -43,6 +43,7 @@ use xmr_btc::{ pub async fn swap( bitcoin_wallet: Arc, monero_wallet: Arc, + db: Database, btc: u64, addr: Multiaddr, mut cmd_tx: Sender, @@ -141,6 +142,9 @@ pub async fn swap( other => panic!("unexpected event: {:?}", other), }; + db.insert_latest_state(&storage::Bob::Handshaken(state2.clone())) + .await?; + swarm.send_message2(alice.clone(), state2.next_message()); info!("Handshake complete"); @@ -151,7 +155,7 @@ pub async fn swap( network.clone(), monero_wallet.clone(), bitcoin_wallet.clone(), - state2, + state2.clone(), TX_LOCK_MINE_TIMEOUT, ); @@ -166,8 +170,17 @@ pub async fn swap( let _ = bitcoin_wallet .broadcast_signed_transaction(signed_tx_lock) .await?; + db.insert_latest_state(&storage::Bob::BtcLocked(state2.clone())) + .await?; } GeneratorState::Yielded(bob::Action::SendBtcRedeemEncsig(tx_redeem_encsig)) => { + // FIXME: We _know_ that this action is only yielded if the monero has been + // locked. This only works because we know that this is the case, but it may be + // cleaner to save the state inside an implementation of `watch_for_transfer` or + // modify the library code to make this easier + db.insert_latest_state(&storage::Bob::XmrLocked(state2.clone())) + .await?; + let mut guard = network.as_ref().lock().await; guard.0.send_message3(alice.clone(), tx_redeem_encsig); info!("Sent Bitcoin redeem encsig"); @@ -185,21 +198,41 @@ pub async fn swap( spend_key, view_key, }) => { + // FIXME: We _know_ that this action is only yielded if the bitcoin has been + // redeemed. This only works because we know that this is the case, but it may + // be cleaner to save the state inside an implementation of `watch_for_transfer` + // or modify the library code to make this easier + db.insert_latest_state(&storage::Bob::BtcRedeemed(state2.clone())) + .await?; + monero_wallet .create_and_load_wallet_for_output(spend_key, view_key) .await?; } GeneratorState::Yielded(bob::Action::CancelBtc(tx_cancel)) => { + db.insert_latest_state(&storage::Bob::BtcRefundable(state2.clone())) + .await?; + let _ = bitcoin_wallet .broadcast_signed_transaction(tx_cancel) .await?; + + db.insert_latest_state(&storage::Bob::BtcRefundable(state2.clone())) + .await?; } GeneratorState::Yielded(bob::Action::RefundBtc(tx_refund)) => { + db.insert_latest_state(&storage::Bob::BtcRefundable(state2.clone())) + .await?; + let _ = bitcoin_wallet .broadcast_signed_transaction(tx_refund) .await?; } - GeneratorState::Complete(()) => return Ok(()), + GeneratorState::Complete(()) => { + db.insert_latest_state(&storage::Bob::SwapComplete).await?; + + return Ok(()); + } } } } diff --git a/swap/src/main.rs b/swap/src/main.rs index 2f8fc74c..3f7a701e 100644 --- a/swap/src/main.rs +++ b/swap/src/main.rs @@ -27,6 +27,7 @@ use swap::{ network::transport::{build, build_tor, SwapTransport}, Cmd, Rsp, SwapAmounts, }; +use tempfile::tempdir; use tracing::info; mod cli; @@ -80,9 +81,12 @@ async fn main() -> Result<()> { let monero_wallet = Arc::new(monero::Wallet::new(monerod_url)); + let db = Database::open(db_dir.path()).unwrap(); + swap_as_alice( bitcoin_wallet, monero_wallet, + db listen_addr, transport, behaviour, @@ -113,9 +117,12 @@ async fn main() -> Result<()> { let monero_wallet = Arc::new(monero::Wallet::new(monerod_url)); + let db = Database::open(db_dir.path()).unwrap(); + swap_as_bob( bitcoin_wallet, monero_wallet, + db satoshis, alice_addr, transport, @@ -149,6 +156,7 @@ async fn create_tor_service( async fn swap_as_alice( bitcoin_wallet: Arc, monero_wallet: Arc, + db: Database, addr: Multiaddr, transport: SwapTransport, behaviour: Alice, @@ -159,6 +167,7 @@ async fn swap_as_alice( async fn swap_as_bob( bitcoin_wallet: Arc, monero_wallet: Arc, + db: Database, sats: u64, alice: Multiaddr, transport: SwapTransport, @@ -169,6 +178,7 @@ async fn swap_as_bob( tokio::spawn(bob::swap( bitcoin_wallet, monero_wallet, + db, sats, alice, cmd_tx, diff --git a/swap/src/storage.rs b/swap/src/storage.rs index 206dbfa1..7c9942e4 100644 --- a/swap/src/storage.rs +++ b/swap/src/storage.rs @@ -1,6 +1,36 @@ use anyhow::{anyhow, Context, Result}; -use serde::{de::DeserializeOwned, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::path::Path; +use xmr_btc::{alice, bitcoin::EncryptedSignature, bob, serde::monero_private_key}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum Bob { + Handshaken(bob::State2), + BtcLocked(bob::State2), + XmrLocked(bob::State2), + BtcRedeemed(bob::State2), + BtcRefundable(bob::State2), + SwapComplete, +} + +#[allow(clippy::large_enum_variant)] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum Alice { + Handshaken(alice::State3), + BtcLocked(alice::State3), + XmrLocked(alice::State3), + ReceivedEncSig { + state: alice::State3, + enc_sig: EncryptedSignature, + }, + BtcCancelled(alice::State3), + BtcRefunded { + state: alice::State3, + #[serde(with = "monero_private_key")] + s_b: monero::PrivateKey, + }, + SwapComplete, +} pub struct Database where diff --git a/xmr-btc/src/bob.rs b/xmr-btc/src/bob.rs index 89cb6492..d92303b2 100644 --- a/xmr-btc/src/bob.rs +++ b/xmr-btc/src/bob.rs @@ -495,7 +495,7 @@ impl State1 { } } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Clone, Deserialize, Serialize)] pub struct State2 { pub A: bitcoin::PublicKey, pub b: bitcoin::SecretKey,