From bc442bcad31cf3acf9262bcbeed1c3563317ed15 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Tue, 30 Mar 2021 11:53:21 +1100 Subject: [PATCH] Await 10 confirmations of lock tx in refund Awaiting the confirmations in an earlier state can cause trouble with resuming swaps with short cancel expiries (test scenarios). Since it is the responsibility of the refund state to ensure that the XMR can be sweeped, we now ensure that the lock transaction has 10 confirmations before refunding the XMR using generate_from_keys. --- swap/src/database/alice.rs | 34 ++++++++++-- swap/src/protocol/alice/state.rs | 6 +++ swap/src/protocol/alice/swap.rs | 88 +++++++++++++++++++------------- 3 files changed, 90 insertions(+), 38 deletions(-) diff --git a/swap/src/database/alice.rs b/swap/src/database/alice.rs index 9af07122..b76e1f85 100644 --- a/swap/src/database/alice.rs +++ b/swap/src/database/alice.rs @@ -30,27 +30,33 @@ pub enum Alice { }, XmrLockTransferProofSent { monero_wallet_restore_blockheight: BlockHeight, + transfer_proof: TransferProof, state3: alice::State3, }, EncSigLearned { monero_wallet_restore_blockheight: BlockHeight, + transfer_proof: TransferProof, encrypted_signature: EncryptedSignature, state3: alice::State3, }, CancelTimelockExpired { monero_wallet_restore_blockheight: BlockHeight, + transfer_proof: TransferProof, state3: alice::State3, }, BtcCancelled { monero_wallet_restore_blockheight: BlockHeight, + transfer_proof: TransferProof, state3: alice::State3, }, BtcPunishable { monero_wallet_restore_blockheight: BlockHeight, + transfer_proof: TransferProof, state3: alice::State3, }, BtcRefunded { monero_wallet_restore_blockheight: BlockHeight, + transfer_proof: TransferProof, state3: alice::State3, #[serde(with = "monero_private_key")] spend_key: monero::PrivateKey, @@ -95,52 +101,62 @@ impl From<&AliceState> for Alice { }, AliceState::XmrLockTransferProofSent { monero_wallet_restore_blockheight, + transfer_proof, state3, } => Alice::XmrLockTransferProofSent { monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight, + transfer_proof: transfer_proof.clone(), state3: state3.as_ref().clone(), }, AliceState::EncSigLearned { monero_wallet_restore_blockheight, + transfer_proof, state3, encrypted_signature, } => Alice::EncSigLearned { monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight, + transfer_proof: transfer_proof.clone(), state3: state3.as_ref().clone(), encrypted_signature: *encrypted_signature.clone(), }, AliceState::BtcRedeemed => Alice::Done(AliceEndState::BtcRedeemed), AliceState::BtcCancelled { monero_wallet_restore_blockheight, + transfer_proof, state3, - .. } => Alice::BtcCancelled { monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight, + transfer_proof: transfer_proof.clone(), state3: state3.as_ref().clone(), }, AliceState::BtcRefunded { monero_wallet_restore_blockheight, + transfer_proof, spend_key, state3, } => Alice::BtcRefunded { monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight, + transfer_proof: transfer_proof.clone(), spend_key: *spend_key, state3: state3.as_ref().clone(), }, AliceState::BtcPunishable { monero_wallet_restore_blockheight, + transfer_proof, state3, - .. } => Alice::BtcPunishable { monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight, + transfer_proof: transfer_proof.clone(), state3: state3.as_ref().clone(), }, AliceState::XmrRefunded => Alice::Done(AliceEndState::XmrRefunded), AliceState::CancelTimelockExpired { monero_wallet_restore_blockheight, + transfer_proof, state3, } => Alice::CancelTimelockExpired { monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight, + transfer_proof: transfer_proof.clone(), state3: state3.as_ref().clone(), }, AliceState::BtcPunished => Alice::Done(AliceEndState::BtcPunished), @@ -178,48 +194,60 @@ impl From for AliceState { }, Alice::XmrLockTransferProofSent { monero_wallet_restore_blockheight, + transfer_proof, state3, } => AliceState::XmrLockTransferProofSent { monero_wallet_restore_blockheight, + transfer_proof, state3: Box::new(state3), }, Alice::EncSigLearned { monero_wallet_restore_blockheight, + transfer_proof, state3: state, encrypted_signature, } => AliceState::EncSigLearned { monero_wallet_restore_blockheight, + transfer_proof, state3: Box::new(state), encrypted_signature: Box::new(encrypted_signature), }, Alice::CancelTimelockExpired { monero_wallet_restore_blockheight, + transfer_proof, state3, } => AliceState::CancelTimelockExpired { monero_wallet_restore_blockheight, + transfer_proof, state3: Box::new(state3), }, Alice::BtcCancelled { monero_wallet_restore_blockheight, + transfer_proof, state3, } => AliceState::BtcCancelled { monero_wallet_restore_blockheight, + transfer_proof, state3: Box::new(state3), }, Alice::BtcPunishable { monero_wallet_restore_blockheight, + transfer_proof, state3, } => AliceState::BtcPunishable { monero_wallet_restore_blockheight, + transfer_proof, state3: Box::new(state3), }, Alice::BtcRefunded { monero_wallet_restore_blockheight, - state3, + transfer_proof, spend_key, + state3, } => AliceState::BtcRefunded { monero_wallet_restore_blockheight, + transfer_proof, spend_key, state3: Box::new(state3), }, diff --git a/swap/src/protocol/alice/state.rs b/swap/src/protocol/alice/state.rs index d771b156..4a6194fb 100644 --- a/swap/src/protocol/alice/state.rs +++ b/swap/src/protocol/alice/state.rs @@ -34,30 +34,36 @@ pub enum AliceState { }, XmrLockTransferProofSent { monero_wallet_restore_blockheight: BlockHeight, + transfer_proof: TransferProof, state3: Box, }, EncSigLearned { monero_wallet_restore_blockheight: BlockHeight, + transfer_proof: TransferProof, encrypted_signature: Box, state3: Box, }, BtcRedeemed, BtcCancelled { monero_wallet_restore_blockheight: BlockHeight, + transfer_proof: TransferProof, state3: Box, }, BtcRefunded { monero_wallet_restore_blockheight: BlockHeight, + transfer_proof: TransferProof, spend_key: monero::PrivateKey, state3: Box, }, BtcPunishable { monero_wallet_restore_blockheight: BlockHeight, + transfer_proof: TransferProof, state3: Box, }, XmrRefunded, CancelTimelockExpired { monero_wallet_restore_blockheight: BlockHeight, + transfer_proof: TransferProof, state3: Box, }, BtcPunished, diff --git a/swap/src/protocol/alice/swap.rs b/swap/src/protocol/alice/swap.rs index 98333390..6bfc8af1 100644 --- a/swap/src/protocol/alice/swap.rs +++ b/swap/src/protocol/alice/swap.rs @@ -101,9 +101,9 @@ async fn next_state( .await?; AliceState::XmrLockTransactionSent { - state3, - transfer_proof, monero_wallet_restore_blockheight, + transfer_proof, + state3, } } _ => AliceState::SafelyAborted, @@ -120,47 +120,44 @@ async fn next_state( .await?; AliceState::XmrLocked { - state3, monero_wallet_restore_blockheight, transfer_proof, + state3, } } _ => AliceState::CancelTimelockExpired { - state3, monero_wallet_restore_blockheight, + transfer_proof, + state3, }, }, AliceState::XmrLocked { - state3, - transfer_proof, monero_wallet_restore_blockheight, + transfer_proof, + state3, } => match state3.expired_timelocks(bitcoin_wallet).await? { ExpiredTimelocks::None => { event_loop_handle .send_transfer_proof(transfer_proof.clone()) .await?; - // TODO: Handle this upon refund instead. - // Make sure that the balance of the created wallet is unlocked instead of - // watching for transfer. - monero_wallet - .watch_for_transfer(state3.lock_xmr_watch_request(transfer_proof, 10)) - .await?; - XmrLockTransferProofSent { - state3, monero_wallet_restore_blockheight, + transfer_proof, + state3, } } _ => AliceState::CancelTimelockExpired { - state3, monero_wallet_restore_blockheight, + transfer_proof, + state3, }, }, AliceState::XmrLockTransferProofSent { - state3, monero_wallet_restore_blockheight, + transfer_proof, + state3, } => { let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await; @@ -168,32 +165,36 @@ async fn next_state( ExpiredTimelocks::None => { select! { _ = tx_lock_status.wait_until_confirmed_with(state3.cancel_timelock) => { - AliceState::CancelTimelockExpired { - state3, - monero_wallet_restore_blockheight, + AliceState::CancelTimelockExpired { + monero_wallet_restore_blockheight, + transfer_proof, + state3, } } enc_sig = event_loop_handle.recv_encrypted_signature() => { tracing::info!("Received encrypted signature"); AliceState::EncSigLearned { - state3, - encrypted_signature: Box::new(enc_sig?), monero_wallet_restore_blockheight, + transfer_proof, + encrypted_signature: Box::new(enc_sig?), + state3, } } } } _ => AliceState::CancelTimelockExpired { - state3, monero_wallet_restore_blockheight, + transfer_proof, + state3, }, } } AliceState::EncSigLearned { - state3, - encrypted_signature, monero_wallet_restore_blockheight, + transfer_proof, + encrypted_signature, + state3, } => match state3.expired_timelocks(bitcoin_wallet).await? { ExpiredTimelocks::None => { let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await; @@ -212,8 +213,9 @@ async fn next_state( .await?; AliceState::CancelTimelockExpired { - state3, monero_wallet_restore_blockheight, + transfer_proof, + state3, } } }, @@ -224,20 +226,23 @@ async fn next_state( .await?; AliceState::CancelTimelockExpired { - state3, monero_wallet_restore_blockheight, + transfer_proof, + state3, } } } } _ => AliceState::CancelTimelockExpired { - state3, monero_wallet_restore_blockheight, + transfer_proof, + state3, }, }, AliceState::CancelTimelockExpired { - state3, monero_wallet_restore_blockheight, + transfer_proof, + state3, } => { let transaction = state3.signed_cancel_transaction()?; @@ -259,13 +264,15 @@ async fn next_state( } AliceState::BtcCancelled { - state3, monero_wallet_restore_blockheight, + transfer_proof, + state3, } } AliceState::BtcCancelled { - state3, monero_wallet_restore_blockheight, + transfer_proof, + state3, } => { let tx_refund_status = bitcoin_wallet.subscribe_to(state3.tx_refund()).await; let tx_cancel_status = bitcoin_wallet.subscribe_to(state3.tx_cancel()).await; @@ -278,26 +285,35 @@ async fn next_state( let spend_key = state3.extract_monero_private_key(published_refund_tx)?; AliceState::BtcRefunded { + monero_wallet_restore_blockheight, + transfer_proof, spend_key, state3, - monero_wallet_restore_blockheight, } } _ = tx_cancel_status.wait_until_confirmed_with(state3.punish_timelock) => { AliceState::BtcPunishable { - state3, monero_wallet_restore_blockheight, + transfer_proof, + state3, } } } } AliceState::BtcRefunded { + monero_wallet_restore_blockheight, + transfer_proof, spend_key, state3, - monero_wallet_restore_blockheight, } => { let view_key = state3.v; + // Ensure that the XMR to be refunded are spendable by awaiting 10 confirmations + // on the lock transaction + monero_wallet + .watch_for_transfer(state3.lock_xmr_watch_request(transfer_proof, 10)) + .await?; + monero_wallet .create_from(spend_key, view_key, monero_wallet_restore_blockheight) .await?; @@ -305,8 +321,9 @@ async fn next_state( AliceState::XmrRefunded } AliceState::BtcPunishable { - state3, monero_wallet_restore_blockheight, + transfer_proof, + state3, } => { let signed_tx_punish = state3.signed_punish_transaction()?; @@ -341,9 +358,10 @@ async fn next_state( let spend_key = state3.extract_monero_private_key(published_refund_tx)?; AliceState::BtcRefunded { + monero_wallet_restore_blockheight, + transfer_proof, spend_key, state3, - monero_wallet_restore_blockheight, } } }