From e13044820059b948bef2c80d3cebb1ceb2142b7b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 24 Mar 2021 16:48:05 +1100 Subject: [PATCH] Make as many fields of Alice's states private as possible To achieve this, we need to add some pure helpers to the state structs. This has the added benefit that we can reduce the amount of code within the swap function. --- swap/src/protocol/alice/state.rs | 92 ++++++++++++++++++++++---------- swap/src/protocol/alice/swap.rs | 46 ++++------------ 2 files changed, 74 insertions(+), 64 deletions(-) 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,