mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-12-19 12:54:24 -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"
|
curve25519-dalek = "2"
|
||||||
ecdsa_fun = { version = "0.3.1", features = ["libsecp_compat"] }
|
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
|
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"
|
genawaiter = "0.99.1"
|
||||||
miniscript = "1"
|
miniscript = "1"
|
||||||
monero = "0.9"
|
monero = "0.9"
|
||||||
|
@ -51,7 +51,9 @@ pub mod bob;
|
|||||||
pub mod monero;
|
pub mod monero;
|
||||||
pub mod transport;
|
pub mod transport;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use ecdsa_fun::{adaptor::Adaptor, nonce::Deterministic};
|
use ecdsa_fun::{adaptor::Adaptor, nonce::Deterministic};
|
||||||
|
use futures::future::Either;
|
||||||
use genawaiter::sync::{Gen, GenBoxed};
|
use genawaiter::sync::{Gen, GenBoxed};
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
|
|
||||||
@ -75,6 +77,11 @@ pub trait ReceiveTransferProof {
|
|||||||
fn receive_transfer_proof(&self) -> monero::TransferProof;
|
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.
|
/// 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
|
/// 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
|
where
|
||||||
N: ReceiveTransferProof + Send + Sync,
|
N: ReceiveTransferProof + Send + Sync,
|
||||||
M: monero::WatchForTransfer + 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),
|
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 {
|
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;
|
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
|
// the source of this could be the database, this layer doesn't care
|
||||||
let transfer_proof = network.receive_transfer_proof();
|
let transfer_proof = network.receive_transfer_proof();
|
||||||
|
|
||||||
@ -122,12 +144,22 @@ where
|
|||||||
));
|
));
|
||||||
let S = S_a_monero + S_b_monero;
|
let S = S_a_monero + S_b_monero;
|
||||||
|
|
||||||
// TODO: We should require a specific number of confirmations on the lock
|
match futures::future::select(
|
||||||
// transaction
|
monero_ledger.watch_for_transfer(
|
||||||
monero_ledger
|
S,
|
||||||
.watch_for_transfer(S, v.public(), transfer_proof, xmr, 10)
|
v.public(),
|
||||||
.await
|
transfer_proof,
|
||||||
.map_err(|e| SwapFailedRefund::InsufficientXMR(e))?;
|
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 = bitcoin::TxRedeem::new(&tx_lock, &redeem_address);
|
||||||
let tx_redeem_encsig = b.encsign(S_a_bitcoin.clone(), tx_redeem.digest());
|
let tx_redeem_encsig = b.encsign(S_a_bitcoin.clone(), tx_redeem.digest());
|
||||||
@ -166,8 +198,6 @@ where
|
|||||||
}
|
}
|
||||||
.await;
|
.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() {
|
if swap_result.is_err() {
|
||||||
let tx_cancel =
|
let tx_cancel =
|
||||||
bitcoin::TxCancel::new(&tx_lock, refund_timelock, A.clone(), b.public());
|
bitcoin::TxCancel::new(&tx_lock, refund_timelock, A.clone(), b.public());
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use backoff::{future::FutureOperation as _, ExponentialBackoff};
|
||||||
use bitcoin::{util::psbt::PartiallySignedTransaction, Address, Amount, Transaction, Txid};
|
use bitcoin::{util::psbt::PartiallySignedTransaction, Address, Amount, Transaction, Txid};
|
||||||
use bitcoin_harness::{bitcoind_rpc::PsbtBase64, Bitcoind};
|
use bitcoin_harness::{bitcoind_rpc::PsbtBase64, Bitcoind};
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::time;
|
use tokio::time;
|
||||||
use xmr_btc::bitcoin::{
|
use xmr_btc::{
|
||||||
BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock, TxLock, WatchForRawTransaction,
|
bitcoin::{
|
||||||
|
BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock, TxLock, WatchForRawTransaction,
|
||||||
|
},
|
||||||
|
MedianTime,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[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