mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-12-18 12:24:31 -05:00
Trigger refund if the publication of Monero TxLock takes too long
This commit is contained in:
parent
08be87747f
commit
ba3011a9c9
@ -12,6 +12,7 @@ cross-curve-dleq = { git = "https://github.com/comit-network/cross-curve-dleq",
|
||||
curve25519-dalek = "2"
|
||||
ecdsa_fun = { version = "0.3.1", features = ["libsecp_compat"] }
|
||||
ed25519-dalek = "1.0.0-pre.4" # Cannot be 1 because they depend on curve25519-dalek version 3
|
||||
futures = "0.3"
|
||||
genawaiter = "0.99.1"
|
||||
miniscript = "1"
|
||||
monero = "0.9"
|
||||
|
@ -51,7 +51,9 @@ pub mod bob;
|
||||
pub mod monero;
|
||||
pub mod transport;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use ecdsa_fun::{adaptor::Adaptor, nonce::Deterministic};
|
||||
use futures::future::Either;
|
||||
use genawaiter::sync::{Gen, GenBoxed};
|
||||
use sha2::Sha256;
|
||||
|
||||
@ -75,6 +77,11 @@ pub trait ReceiveTransferProof {
|
||||
fn receive_transfer_proof(&self) -> monero::TransferProof;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait MedianTime {
|
||||
async fn median_time(&self) -> u32;
|
||||
}
|
||||
|
||||
/// Perform the on-chain protocol to swap monero and bitcoin as Bob.
|
||||
///
|
||||
/// This is called post handshake, after all the keys, addresses and most of the
|
||||
@ -104,16 +111,31 @@ pub fn action_generator_bob<N, M, B>(
|
||||
where
|
||||
N: ReceiveTransferProof + Send + Sync,
|
||||
M: monero::WatchForTransfer + Send + Sync,
|
||||
B: bitcoin::WatchForRawTransaction + Send + Sync,
|
||||
B: MedianTime + bitcoin::WatchForRawTransaction + Send + Sync,
|
||||
{
|
||||
enum SwapFailedRefund {
|
||||
enum SwapFailed {
|
||||
TimelockReached,
|
||||
InsufficientXMR(monero::InsufficientFunds),
|
||||
}
|
||||
|
||||
async fn poll_until_bitcoin_time<B>(bitcoin_client: &B, timestamp: u32)
|
||||
where
|
||||
B: MedianTime,
|
||||
{
|
||||
loop {
|
||||
if bitcoin_client.median_time().await >= timestamp {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Gen::new_boxed(|co| async move {
|
||||
let swap_result: Result<(), SwapFailedRefund> = async {
|
||||
let swap_result: Result<(), SwapFailed> = async {
|
||||
co.yield_(Action::LockBitcoin(tx_lock.clone())).await;
|
||||
|
||||
let poll_until_expiry = poll_until_bitcoin_time(bitcoin_ledger, refund_timelock);
|
||||
futures::pin_mut!(poll_until_expiry);
|
||||
|
||||
// the source of this could be the database, this layer doesn't care
|
||||
let transfer_proof = network.receive_transfer_proof();
|
||||
|
||||
@ -122,12 +144,22 @@ where
|
||||
));
|
||||
let S = S_a_monero + S_b_monero;
|
||||
|
||||
// TODO: We should require a specific number of confirmations on the lock
|
||||
// transaction
|
||||
monero_ledger
|
||||
.watch_for_transfer(S, v.public(), transfer_proof, xmr, 10)
|
||||
.await
|
||||
.map_err(|e| SwapFailedRefund::InsufficientXMR(e))?;
|
||||
match futures::future::select(
|
||||
monero_ledger.watch_for_transfer(
|
||||
S,
|
||||
v.public(),
|
||||
transfer_proof,
|
||||
xmr,
|
||||
monero::MIN_CONFIRMATIONS,
|
||||
),
|
||||
poll_until_expiry,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Either::Left((Err(e), _)) => return Err(SwapFailed::InsufficientXMR(e)),
|
||||
Either::Right(_) => return Err(SwapFailed::TimelockReached),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let tx_redeem = bitcoin::TxRedeem::new(&tx_lock, &redeem_address);
|
||||
let tx_redeem_encsig = b.encsign(S_a_bitcoin.clone(), tx_redeem.digest());
|
||||
@ -166,8 +198,6 @@ where
|
||||
}
|
||||
.await;
|
||||
|
||||
// NOTE: swap result should only be `Err` if we have reached the
|
||||
// `refund_timelock`. Therefore, we should always yield the refund action
|
||||
if swap_result.is_err() {
|
||||
let tx_cancel =
|
||||
bitcoin::TxCancel::new(&tx_lock, refund_timelock, A.clone(), b.public());
|
||||
|
@ -1,12 +1,16 @@
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use backoff::{future::FutureOperation as _, ExponentialBackoff};
|
||||
use bitcoin::{util::psbt::PartiallySignedTransaction, Address, Amount, Transaction, Txid};
|
||||
use bitcoin_harness::{bitcoind_rpc::PsbtBase64, Bitcoind};
|
||||
use reqwest::Url;
|
||||
use std::time::Duration;
|
||||
use tokio::time;
|
||||
use xmr_btc::bitcoin::{
|
||||
BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock, TxLock, WatchForRawTransaction,
|
||||
use xmr_btc::{
|
||||
bitcoin::{
|
||||
BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock, TxLock, WatchForRawTransaction,
|
||||
},
|
||||
MedianTime,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -117,3 +121,16 @@ impl WatchForRawTransaction for Wallet {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl MedianTime for Wallet {
|
||||
async fn median_time(&self) -> u32 {
|
||||
(|| async { Ok(self.0.median_time().await?) })
|
||||
.retry(ExponentialBackoff {
|
||||
max_elapsed_time: None,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.expect("transient errors to be retried")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user