diff --git a/swap/src/protocol/alice/state.rs b/swap/src/protocol/alice/state.rs index e16fd8e1..6507e33d 100644 --- a/swap/src/protocol/alice/state.rs +++ b/swap/src/protocol/alice/state.rs @@ -4,6 +4,7 @@ use crate::bitcoin::{ use crate::env::Config; use crate::monero::wallet::{TransferRequest, WatchRequest}; use crate::monero::TransferProof; +use crate::monero_ext::ScalarExt; use crate::protocol::alice::{Message1, Message3}; use crate::protocol::bob::{Message0, Message2, Message4}; use crate::protocol::CROSS_CURVE_PROOF_SYSTEM; @@ -76,18 +77,18 @@ impl fmt::Display for AliceState { #[derive(Clone, Debug, PartialEq)] pub struct State0 { - pub a: bitcoin::SecretKey, - pub s_a: monero::Scalar, - pub v_a: monero::PrivateViewKey, - pub(crate) S_a_monero: monero::PublicKey, - pub(crate) S_a_bitcoin: bitcoin::PublicKey, - pub dleq_proof_s_a: CrossCurveDLEQProof, - pub btc: bitcoin::Amount, - pub xmr: monero::Amount, - pub cancel_timelock: CancelTimelock, - pub punish_timelock: PunishTimelock, - pub redeem_address: bitcoin::Address, - pub punish_address: bitcoin::Address, + a: bitcoin::SecretKey, + s_a: monero::Scalar, + v_a: monero::PrivateViewKey, + S_a_monero: monero::PublicKey, + S_a_bitcoin: bitcoin::PublicKey, + dleq_proof_s_a: CrossCurveDLEQProof, + btc: bitcoin::Amount, + xmr: monero::Amount, + cancel_timelock: CancelTimelock, + punish_timelock: PunishTimelock, + redeem_address: bitcoin::Address, + punish_address: bitcoin::Address, } impl State0 { @@ -292,23 +293,23 @@ impl State2 { #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct State3 { - pub a: bitcoin::SecretKey, - pub B: bitcoin::PublicKey, - pub s_a: monero::Scalar, - pub S_b_monero: monero::PublicKey, - pub S_b_bitcoin: bitcoin::PublicKey, + a: bitcoin::SecretKey, + B: bitcoin::PublicKey, + s_a: monero::Scalar, + S_b_monero: monero::PublicKey, + S_b_bitcoin: bitcoin::PublicKey, pub v: monero::PrivateViewKey, #[serde(with = "::bitcoin::util::amount::serde::as_sat")] - pub btc: bitcoin::Amount, - pub xmr: monero::Amount, + btc: bitcoin::Amount, + xmr: monero::Amount, pub cancel_timelock: CancelTimelock, pub punish_timelock: PunishTimelock, - pub refund_address: bitcoin::Address, - pub redeem_address: bitcoin::Address, - pub punish_address: bitcoin::Address, + refund_address: bitcoin::Address, + redeem_address: bitcoin::Address, + punish_address: bitcoin::Address, pub tx_lock: bitcoin::TxLock, - pub tx_punish_sig_bob: bitcoin::Signature, - pub tx_cancel_sig_bob: bitcoin::Signature, + tx_punish_sig_bob: bitcoin::Signature, + tx_cancel_sig_bob: bitcoin::Signature, } impl State3 { @@ -364,15 +365,48 @@ impl State3 { TxCancel::new(&self.tx_lock, self.cancel_timelock, self.a.public(), self.B) } - pub fn tx_punish(&self) -> TxPunish { + pub fn tx_refund(&self) -> TxRefund { + bitcoin::TxRefund::new(&self.tx_cancel(), &self.refund_address) + } + + pub fn extract_monero_private_key( + &self, + published_refund_tx: bitcoin::Transaction, + ) -> Result { + self.tx_refund().extract_monero_private_key( + published_refund_tx, + self.s_a, + self.a.clone(), + self.S_b_bitcoin, + ) + } + + pub fn signed_redeem_transaction( + &self, + sig: bitcoin::EncryptedSignature, + ) -> Result { + bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address) + .complete(sig, self.a.clone(), self.s_a.to_secpfun_scalar(), self.B) + .context("Failed to complete Bitcoin redeem transaction") + } + + pub fn signed_cancel_transaction(&self) -> Result { + self.tx_cancel() + .complete_as_alice(self.a.clone(), self.B, self.tx_cancel_sig_bob.clone()) + .context("Failed to complete Bitcoin cancel transaction") + } + + pub fn signed_punish_transaction(&self) -> Result { + self.tx_punish() + .complete(self.tx_punish_sig_bob.clone(), self.a.clone(), self.B) + .context("Failed to complete Bitcoin punish transaction") + } + + fn tx_punish(&self) -> TxPunish { bitcoin::TxPunish::new( &self.tx_cancel(), &self.punish_address, self.punish_timelock, ) } - - pub fn tx_refund(&self) -> TxRefund { - bitcoin::TxRefund::new(&self.tx_cancel(), &self.refund_address) - } } diff --git a/swap/src/protocol/alice/swap.rs b/swap/src/protocol/alice/swap.rs index 6806f668..dc732bf7 100644 --- a/swap/src/protocol/alice/swap.rs +++ b/swap/src/protocol/alice/swap.rs @@ -1,8 +1,7 @@ //! Run an XMR/BTC swap in the role of Alice. //! Alice holds XMR and wishes receive BTC. -use crate::bitcoin::{ExpiredTimelocks, TxRedeem}; +use crate::bitcoin::ExpiredTimelocks; use crate::env::Config; -use crate::monero_ext::ScalarExt; use crate::protocol::alice; use crate::protocol::alice::event_loop::EventLoopHandle; use crate::protocol::alice::AliceState; @@ -158,12 +157,7 @@ async fn next_state( } => match state3.expired_timelocks(bitcoin_wallet).await? { ExpiredTimelocks::None => { let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await; - match TxRedeem::new(&state3.tx_lock, &state3.redeem_address).complete( - *encrypted_signature, - state3.a.clone(), - state3.s_a.to_secpfun_scalar(), - state3.B, - ) { + match state3.signed_redeem_transaction(*encrypted_signature) { Ok(tx) => match bitcoin_wallet.broadcast(tx, "redeem").await { Ok((_, subscription)) => match subscription.wait_until_final().await { Ok(_) => AliceState::BtcRedeemed, @@ -205,18 +199,14 @@ async fn next_state( state3, monero_wallet_restore_blockheight, } => { - let tx_cancel = state3.tx_cancel(); + let transaction = state3.signed_cancel_transaction()?; // If Bob hasn't yet broadcasted the tx cancel, we do it if bitcoin_wallet - .get_raw_transaction(tx_cancel.txid()) + .get_raw_transaction(transaction.txid()) .await .is_err() { - let transaction = tx_cancel - .complete_as_alice(state3.a.clone(), state3.B, state3.tx_cancel_sig_bob.clone()) - .context("Failed to complete Bitcoin cancel transaction")?; - if let Err(e) = bitcoin_wallet.broadcast(transaction, "cancel").await { tracing::debug!( "Assuming transaction is already broadcasted because: {:#}", @@ -243,14 +233,9 @@ async fn next_state( select! { seen_refund = tx_refund_status.wait_until_seen() => { seen_refund.context("Failed to monitor refund transaction")?; - let published_refund_tx = bitcoin_wallet.get_raw_transaction(state3.tx_refund().txid()).await?; - let spend_key = state3.tx_refund().extract_monero_private_key( - published_refund_tx, - state3.s_a, - state3.a.clone(), - state3.S_b_bitcoin, - )?; + let published_refund_tx = bitcoin_wallet.get_raw_transaction(state3.tx_refund().txid()).await?; + let spend_key = state3.extract_monero_private_key(published_refund_tx)?; AliceState::BtcRefunded { spend_key, @@ -283,11 +268,7 @@ async fn next_state( state3, monero_wallet_restore_blockheight, } => { - let signed_tx_punish = state3.tx_punish().complete( - state3.tx_punish_sig_bob.clone(), - state3.a.clone(), - state3.B, - )?; + let signed_tx_punish = state3.signed_punish_transaction()?; let punish = async { let (txid, subscription) = @@ -313,16 +294,11 @@ async fn next_state( // because a punish tx failure is not recoverable (besides re-trying) if the // refund tx was not included. - let tx_refund = state3.tx_refund(); - let published_refund_tx = - bitcoin_wallet.get_raw_transaction(tx_refund.txid()).await?; + let published_refund_tx = bitcoin_wallet + .get_raw_transaction(state3.tx_refund().txid()) + .await?; - let spend_key = tx_refund.extract_monero_private_key( - published_refund_tx, - state3.s_a, - state3.a.clone(), - state3.S_b_bitcoin, - )?; + let spend_key = state3.extract_monero_private_key(published_refund_tx)?; AliceState::BtcRefunded { spend_key,