|
|
|
@ -5,11 +5,11 @@ use crate::{
|
|
|
|
|
SignTxLock, TxCancel, WatchForRawTransaction,
|
|
|
|
|
},
|
|
|
|
|
monero,
|
|
|
|
|
monero::WatchForTransfer,
|
|
|
|
|
serde::monero_private_key,
|
|
|
|
|
transport::{ReceiveMessage, SendMessage},
|
|
|
|
|
};
|
|
|
|
|
use anyhow::{anyhow, Result};
|
|
|
|
|
use async_trait::async_trait;
|
|
|
|
|
use ecdsa_fun::{
|
|
|
|
|
adaptor::{Adaptor, EncryptedSignature},
|
|
|
|
|
nonce::Deterministic,
|
|
|
|
@ -28,11 +28,11 @@ use std::{
|
|
|
|
|
sync::Arc,
|
|
|
|
|
time::Duration,
|
|
|
|
|
};
|
|
|
|
|
use tokio::{sync::Mutex, time::timeout};
|
|
|
|
|
use tokio::time::timeout;
|
|
|
|
|
use tracing::error;
|
|
|
|
|
|
|
|
|
|
pub mod message;
|
|
|
|
|
use crate::monero::{CreateWalletForOutput, WatchForTransfer};
|
|
|
|
|
use crate::monero::CreateWalletForOutput;
|
|
|
|
|
pub use message::{Message, Message0, Message1, Message2, Message3};
|
|
|
|
|
|
|
|
|
|
#[allow(clippy::large_enum_variant)]
|
|
|
|
@ -48,12 +48,6 @@ pub enum Action {
|
|
|
|
|
RefundBtc(bitcoin::Transaction),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: This could be moved to the monero module
|
|
|
|
|
#[async_trait]
|
|
|
|
|
pub trait ReceiveTransferProof {
|
|
|
|
|
async fn receive_transfer_proof(&mut self) -> monero::TransferProof;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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
|
|
|
|
@ -61,8 +55,7 @@ pub trait ReceiveTransferProof {
|
|
|
|
|
///
|
|
|
|
|
/// The argument `bitcoin_tx_lock_timeout` is used to determine how long we will
|
|
|
|
|
/// wait for Bob, the caller of this function, to lock up the bitcoin.
|
|
|
|
|
pub fn action_generator<N, M, B>(
|
|
|
|
|
network: Arc<Mutex<N>>,
|
|
|
|
|
pub fn action_generator<M, B>(
|
|
|
|
|
monero_client: Arc<M>,
|
|
|
|
|
bitcoin_client: Arc<B>,
|
|
|
|
|
// TODO: Replace this with a new, slimmer struct?
|
|
|
|
@ -85,7 +78,6 @@ pub fn action_generator<N, M, B>(
|
|
|
|
|
bitcoin_tx_lock_timeout: u64,
|
|
|
|
|
) -> GenBoxed<Action, (), ()>
|
|
|
|
|
where
|
|
|
|
|
N: ReceiveTransferProof + Send + 'static,
|
|
|
|
|
M: monero::WatchForTransfer + Send + Sync + 'static,
|
|
|
|
|
B: bitcoin::BlockHeight
|
|
|
|
|
+ bitcoin::TransactionBlockHeight
|
|
|
|
@ -108,8 +100,6 @@ where
|
|
|
|
|
InactiveBob,
|
|
|
|
|
/// The refund timelock has been reached.
|
|
|
|
|
BtcExpired,
|
|
|
|
|
/// Alice did not lock up enough monero in the shared output.
|
|
|
|
|
InsufficientXmr(monero::InsufficientFunds),
|
|
|
|
|
/// Could not find Bob's signature on the redeem transaction witness
|
|
|
|
|
/// stack.
|
|
|
|
|
BtcRedeemSignature,
|
|
|
|
@ -140,39 +130,21 @@ where
|
|
|
|
|
.shared();
|
|
|
|
|
pin_mut!(poll_until_btc_has_expired);
|
|
|
|
|
|
|
|
|
|
let transfer_proof = {
|
|
|
|
|
let mut guard = network.as_ref().lock().await;
|
|
|
|
|
let transfer_proof = match select(
|
|
|
|
|
guard.receive_transfer_proof(),
|
|
|
|
|
poll_until_btc_has_expired.clone(),
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
{
|
|
|
|
|
Either::Left((proof, _)) => proof,
|
|
|
|
|
Either::Right(_) => return Err(SwapFailed::AfterBtcLock(Reason::BtcExpired)),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
tracing::debug!("select returned transfer proof from message");
|
|
|
|
|
|
|
|
|
|
transfer_proof
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let S_b_monero = monero::PublicKey::from_private_key(&monero::PrivateKey::from_scalar(
|
|
|
|
|
s_b.into_ed25519(),
|
|
|
|
|
));
|
|
|
|
|
let S = S_a_monero + S_b_monero;
|
|
|
|
|
|
|
|
|
|
match select(
|
|
|
|
|
monero_client.watch_for_transfer(S, v.public(), transfer_proof, xmr, 0),
|
|
|
|
|
let monero_joint_address =
|
|
|
|
|
monero::Address::standard(monero::Network::Mainnet, S, v.public().into());
|
|
|
|
|
|
|
|
|
|
if let Either::Right(_) = select(
|
|
|
|
|
monero_client.watch_for_transfer(monero_joint_address, xmr, v),
|
|
|
|
|
poll_until_btc_has_expired.clone(),
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
{
|
|
|
|
|
Either::Left((Err(e), _)) => {
|
|
|
|
|
return Err(SwapFailed::AfterBtcLock(Reason::InsufficientXmr(e)))
|
|
|
|
|
}
|
|
|
|
|
Either::Right(_) => return Err(SwapFailed::AfterBtcLock(Reason::BtcExpired)),
|
|
|
|
|
_ => {}
|
|
|
|
|
return Err(SwapFailed::AfterBtcLock(Reason::BtcExpired));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let tx_redeem = bitcoin::TxRedeem::new(&tx_lock, &redeem_address);
|
|
|
|
@ -300,8 +272,7 @@ pub async fn next_state<
|
|
|
|
|
Ok(state3.into())
|
|
|
|
|
}
|
|
|
|
|
State::State3(state3) => {
|
|
|
|
|
let message2 = transport.receive_message().await?.try_into()?;
|
|
|
|
|
let state4 = state3.watch_for_lock_xmr(monero_wallet, message2).await?;
|
|
|
|
|
let state4 = state3.watch_for_lock_xmr(monero_wallet).await?;
|
|
|
|
|
tracing::info!("bob has seen that alice has locked xmr");
|
|
|
|
|
Ok(state4.into())
|
|
|
|
|
}
|
|
|
|
@ -589,7 +560,7 @@ pub struct State3 {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl State3 {
|
|
|
|
|
pub async fn watch_for_lock_xmr<W>(self, xmr_wallet: &W, msg: alice::Message2) -> Result<State4>
|
|
|
|
|
pub async fn watch_for_lock_xmr<W>(self, xmr_wallet: &W) -> Result<State4>
|
|
|
|
|
where
|
|
|
|
|
W: monero::WatchForTransfer,
|
|
|
|
|
{
|
|
|
|
@ -598,15 +569,12 @@ impl State3 {
|
|
|
|
|
));
|
|
|
|
|
let S = self.S_a_monero + S_b_monero;
|
|
|
|
|
|
|
|
|
|
let monero_joint_address =
|
|
|
|
|
monero::Address::standard(monero::Network::Mainnet, S, self.v.public().into());
|
|
|
|
|
|
|
|
|
|
xmr_wallet
|
|
|
|
|
.watch_for_transfer(
|
|
|
|
|
S,
|
|
|
|
|
self.v.public(),
|
|
|
|
|
msg.tx_lock_proof,
|
|
|
|
|
self.xmr,
|
|
|
|
|
monero::MIN_CONFIRMATIONS,
|
|
|
|
|
)
|
|
|
|
|
.await?;
|
|
|
|
|
.watch_for_transfer(monero_joint_address, self.xmr, self.v)
|
|
|
|
|
.await;
|
|
|
|
|
|
|
|
|
|
Ok(State4 {
|
|
|
|
|
A: self.A,
|
|
|
|
|