mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-02-26 01:21:17 -05:00
Add recovery command
This commit is contained in:
parent
09773dd15b
commit
f0d90130ad
@ -1,5 +1,6 @@
|
|||||||
use libp2p::core::Multiaddr;
|
use libp2p::core::Multiaddr;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(structopt::StructOpt, Debug)]
|
#[derive(structopt::StructOpt, Debug)]
|
||||||
#[structopt(name = "xmr-btc-swap", about = "Trustless XMR BTC swaps")]
|
#[structopt(name = "xmr-btc-swap", about = "Trustless XMR BTC swaps")]
|
||||||
@ -34,4 +35,14 @@ pub enum Options {
|
|||||||
tor: bool,
|
tor: bool,
|
||||||
},
|
},
|
||||||
History,
|
History,
|
||||||
|
Recover {
|
||||||
|
#[structopt(required = true)]
|
||||||
|
swap_id: Uuid,
|
||||||
|
|
||||||
|
#[structopt(default_value = "http://127.0.0.1:8332", long = "bitcoind")]
|
||||||
|
bitcoind_url: Url,
|
||||||
|
|
||||||
|
#[structopt(default_value = "http://127.0.0.1:18083", long = "monerod")]
|
||||||
|
monerod_url: Url,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,12 @@ 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,
|
alice::{self, Alice},
|
||||||
alice::Alice,
|
bitcoin,
|
||||||
bitcoin, bob,
|
bob::{self, Bob},
|
||||||
bob::Bob,
|
|
||||||
monero,
|
monero,
|
||||||
network::transport::{build, build_tor, SwapTransport},
|
network::transport::{build, build_tor, SwapTransport},
|
||||||
|
recover::recover,
|
||||||
Cmd, Rsp, SwapAmounts,
|
Cmd, Rsp, SwapAmounts,
|
||||||
};
|
};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
@ -145,6 +145,19 @@ async fn main() -> Result<()> {
|
|||||||
// Print the table to stdout
|
// Print the table to stdout
|
||||||
table.printstd();
|
table.printstd();
|
||||||
}
|
}
|
||||||
|
Options::Recover {
|
||||||
|
swap_id,
|
||||||
|
bitcoind_url,
|
||||||
|
monerod_url,
|
||||||
|
} => {
|
||||||
|
let state = db.get_state(swap_id)?;
|
||||||
|
let bitcoin_wallet = bitcoin::Wallet::new("bob", bitcoind_url)
|
||||||
|
.await
|
||||||
|
.expect("failed to create bitcoin wallet");
|
||||||
|
let monero_wallet = monero::Wallet::new(monerod_url);
|
||||||
|
|
||||||
|
recover(bitcoin_wallet, monero_wallet, state).await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -9,6 +9,7 @@ use futures::{
|
|||||||
pin_mut,
|
pin_mut,
|
||||||
};
|
};
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
|
use tracing::info;
|
||||||
use xmr_btc::bitcoin::{
|
use xmr_btc::bitcoin::{
|
||||||
poll_until_block_height_is_gte, BroadcastSignedTransaction, TransactionBlockHeight, TxCancel,
|
poll_until_block_height_is_gte, BroadcastSignedTransaction, TransactionBlockHeight, TxCancel,
|
||||||
TxPunish, TxRedeem, TxRefund, WatchForRawTransaction,
|
TxPunish, TxRedeem, TxRefund, WatchForRawTransaction,
|
||||||
@ -31,8 +32,12 @@ pub async fn alice_recover(
|
|||||||
state: Alice,
|
state: Alice,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match state {
|
match state {
|
||||||
Alice::Handshaken(_) | Alice::BtcLocked(_) | Alice::SwapComplete => {}
|
Alice::Handshaken(_) | Alice::BtcLocked(_) | Alice::SwapComplete => {
|
||||||
|
info!("Nothing to do");
|
||||||
|
}
|
||||||
Alice::XmrLocked(state) => {
|
Alice::XmrLocked(state) => {
|
||||||
|
info!("Monero still locked up");
|
||||||
|
|
||||||
let tx_cancel = TxCancel::new(
|
let tx_cancel = TxCancel::new(
|
||||||
&state.tx_lock,
|
&state.tx_lock,
|
||||||
state.refund_timelock,
|
state.refund_timelock,
|
||||||
@ -40,13 +45,15 @@ pub async fn alice_recover(
|
|||||||
state.B.clone(),
|
state.B.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ensure that TxCancel is on the blockchain
|
info!("Checking if the Bitcoin cancel transaction has been published");
|
||||||
if bitcoin_wallet
|
if bitcoin_wallet
|
||||||
.0
|
.0
|
||||||
.get_raw_transaction(tx_cancel.txid())
|
.get_raw_transaction(tx_cancel.txid())
|
||||||
.await
|
.await
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
|
info!("Bitcoin cancel transaction not yet published");
|
||||||
|
|
||||||
let tx_lock_height = bitcoin_wallet
|
let tx_lock_height = bitcoin_wallet
|
||||||
.transaction_block_height(state.tx_lock.txid())
|
.transaction_block_height(state.tx_lock.txid())
|
||||||
.await;
|
.await;
|
||||||
@ -72,6 +79,7 @@ pub async fn alice_recover(
|
|||||||
bitcoin_wallet
|
bitcoin_wallet
|
||||||
.broadcast_signed_transaction(tx_cancel)
|
.broadcast_signed_transaction(tx_cancel)
|
||||||
.await?;
|
.await?;
|
||||||
|
info!("Successfully published Bitcoin cancel transaction");
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx_cancel_height = bitcoin_wallet
|
let tx_cancel_height = bitcoin_wallet
|
||||||
@ -85,6 +93,7 @@ pub async fn alice_recover(
|
|||||||
|
|
||||||
let tx_refund = TxRefund::new(&tx_cancel, &state.refund_address);
|
let tx_refund = TxRefund::new(&tx_cancel, &state.refund_address);
|
||||||
|
|
||||||
|
info!("Waiting for either Bitcoin refund or punish timelock");
|
||||||
match select(
|
match select(
|
||||||
bitcoin_wallet.watch_for_raw_transaction(tx_refund.txid()),
|
bitcoin_wallet.watch_for_raw_transaction(tx_refund.txid()),
|
||||||
poll_until_bob_can_be_punished,
|
poll_until_bob_can_be_punished,
|
||||||
@ -92,6 +101,8 @@ pub async fn alice_recover(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Either::Left((tx_refund_published, ..)) => {
|
Either::Left((tx_refund_published, ..)) => {
|
||||||
|
info!("Found Bitcoin refund transaction");
|
||||||
|
|
||||||
let tx_refund_sig = tx_refund
|
let tx_refund_sig = tx_refund
|
||||||
.extract_signature_by_key(tx_refund_published, state.a.public())?;
|
.extract_signature_by_key(tx_refund_published, state.a.public())?;
|
||||||
let tx_refund_encsig = state
|
let tx_refund_encsig = state
|
||||||
@ -114,13 +125,16 @@ pub async fn alice_recover(
|
|||||||
monero_wallet
|
monero_wallet
|
||||||
.create_and_load_wallet_for_output(s_a + s_b, state.v)
|
.create_and_load_wallet_for_output(s_a + s_b, state.v)
|
||||||
.await?;
|
.await?;
|
||||||
|
info!("Successfully refunded monero");
|
||||||
}
|
}
|
||||||
Either::Right(_) => {
|
Either::Right(_) => {
|
||||||
|
info!("Punish timelock reached, attempting to punish Bob");
|
||||||
|
|
||||||
let tx_punish =
|
let tx_punish =
|
||||||
TxPunish::new(&tx_cancel, &state.punish_address, state.punish_timelock);
|
TxPunish::new(&tx_cancel, &state.punish_address, state.punish_timelock);
|
||||||
|
|
||||||
let sig_a = state.a.sign(tx_punish.digest());
|
let sig_a = state.a.sign(tx_punish.digest());
|
||||||
let sig_b = state.tx_cancel_sig_bob.clone();
|
let sig_b = state.tx_punish_sig_bob.clone();
|
||||||
|
|
||||||
let sig_tx_punish = tx_punish.add_signatures(
|
let sig_tx_punish = tx_punish.add_signatures(
|
||||||
&tx_cancel,
|
&tx_cancel,
|
||||||
@ -131,6 +145,7 @@ pub async fn alice_recover(
|
|||||||
bitcoin_wallet
|
bitcoin_wallet
|
||||||
.broadcast_signed_transaction(sig_tx_punish)
|
.broadcast_signed_transaction(sig_tx_punish)
|
||||||
.await?;
|
.await?;
|
||||||
|
info!("Successfully punished Bob's inactivity by taking bitcoin");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -212,11 +227,14 @@ pub async fn alice_recover(
|
|||||||
bitcoin_wallet
|
bitcoin_wallet
|
||||||
.broadcast_signed_transaction(sig_tx_punish)
|
.broadcast_signed_transaction(sig_tx_punish)
|
||||||
.await?;
|
.await?;
|
||||||
|
info!("Successfully redeemed bitcoin");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Alice::BtcPunishable(state) => {
|
Alice::BtcPunishable(state) => {
|
||||||
|
info!("Punish timelock reached, attempting to punish Bob");
|
||||||
|
|
||||||
let tx_cancel = TxCancel::new(
|
let tx_cancel = TxCancel::new(
|
||||||
&state.tx_lock,
|
&state.tx_lock,
|
||||||
state.refund_timelock,
|
state.refund_timelock,
|
||||||
@ -238,15 +256,19 @@ pub async fn alice_recover(
|
|||||||
bitcoin_wallet
|
bitcoin_wallet
|
||||||
.broadcast_signed_transaction(sig_tx_punish)
|
.broadcast_signed_transaction(sig_tx_punish)
|
||||||
.await?;
|
.await?;
|
||||||
|
info!("Successfully punished Bob's inactivity by taking bitcoin");
|
||||||
}
|
}
|
||||||
Alice::BtcRefunded {
|
Alice::BtcRefunded {
|
||||||
view_key,
|
view_key,
|
||||||
spend_key,
|
spend_key,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
info!("Bitcoin was refunded, attempting to refund monero");
|
||||||
|
|
||||||
monero_wallet
|
monero_wallet
|
||||||
.create_and_load_wallet_for_output(spend_key, view_key)
|
.create_and_load_wallet_for_output(spend_key, view_key)
|
||||||
.await?;
|
.await?;
|
||||||
|
info!("Successfully refunded monero");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -259,8 +281,12 @@ pub async fn bob_recover(
|
|||||||
state: Bob,
|
state: Bob,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match state {
|
match state {
|
||||||
Bob::Handshaken(_) | Bob::SwapComplete => {}
|
Bob::Handshaken(_) | Bob::SwapComplete => {
|
||||||
|
info!("Nothing to do");
|
||||||
|
}
|
||||||
Bob::BtcLocked(state) | Bob::XmrLocked(state) | Bob::BtcRefundable(state) => {
|
Bob::BtcLocked(state) | Bob::XmrLocked(state) | Bob::BtcRefundable(state) => {
|
||||||
|
info!("Bitcoin may still be locked up, attempting to refund");
|
||||||
|
|
||||||
let tx_cancel = TxCancel::new(
|
let tx_cancel = TxCancel::new(
|
||||||
&state.tx_lock,
|
&state.tx_lock,
|
||||||
state.refund_timelock,
|
state.refund_timelock,
|
||||||
@ -268,13 +294,15 @@ pub async fn bob_recover(
|
|||||||
state.b.public(),
|
state.b.public(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ensure that TxCancel is on the blockchain
|
info!("Checking if the Bitcoin cancel transaction has been published");
|
||||||
if bitcoin_wallet
|
if bitcoin_wallet
|
||||||
.0
|
.0
|
||||||
.get_raw_transaction(tx_cancel.txid())
|
.get_raw_transaction(tx_cancel.txid())
|
||||||
.await
|
.await
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
|
info!("Bitcoin cancel transaction not yet published");
|
||||||
|
|
||||||
let tx_lock_height = bitcoin_wallet
|
let tx_lock_height = bitcoin_wallet
|
||||||
.transaction_block_height(state.tx_lock.txid())
|
.transaction_block_height(state.tx_lock.txid())
|
||||||
.await;
|
.await;
|
||||||
@ -300,6 +328,7 @@ pub async fn bob_recover(
|
|||||||
bitcoin_wallet
|
bitcoin_wallet
|
||||||
.broadcast_signed_transaction(tx_cancel)
|
.broadcast_signed_transaction(tx_cancel)
|
||||||
.await?;
|
.await?;
|
||||||
|
info!("Successfully published Bitcoin cancel transaction");
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx_refund = TxRefund::new(&tx_cancel, &state.refund_address);
|
let tx_refund = TxRefund::new(&tx_cancel, &state.refund_address);
|
||||||
@ -318,11 +347,16 @@ pub async fn bob_recover(
|
|||||||
.expect("sig_{a,b} to be valid signatures for tx_refund")
|
.expect("sig_{a,b} to be valid signatures for tx_refund")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Check if Bitcoin has already been punished and provide a useful error
|
||||||
|
// message/log to the user if so
|
||||||
bitcoin_wallet
|
bitcoin_wallet
|
||||||
.broadcast_signed_transaction(signed_tx_refund)
|
.broadcast_signed_transaction(signed_tx_refund)
|
||||||
.await?;
|
.await?;
|
||||||
|
info!("Successfully refunded bitcoin");
|
||||||
}
|
}
|
||||||
Bob::BtcRedeemed(state) => {
|
Bob::BtcRedeemed(state) => {
|
||||||
|
info!("Bitcoin was redeemed, attempting to redeem monero");
|
||||||
|
|
||||||
let tx_redeem = TxRedeem::new(&state.tx_lock, &state.redeem_address);
|
let tx_redeem = TxRedeem::new(&state.tx_lock, &state.redeem_address);
|
||||||
let tx_redeem_published = bitcoin_wallet
|
let tx_redeem_published = bitcoin_wallet
|
||||||
.0
|
.0
|
||||||
@ -348,6 +382,7 @@ pub async fn bob_recover(
|
|||||||
monero_wallet
|
monero_wallet
|
||||||
.create_and_load_wallet_for_output(s_a + s_b, state.v)
|
.create_and_load_wallet_for_output(s_a + s_b, state.v)
|
||||||
.await?;
|
.await?;
|
||||||
|
info!("Successfully redeemed monero")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user