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.
This commit is contained in:
Daniel Karzel 2021-03-30 11:53:21 +11:00 committed by Thomas Eizinger
parent 1c129d58c4
commit bc442bcad3
No known key found for this signature in database
GPG key ID: 651AC83A6C6C8B96
3 changed files with 90 additions and 38 deletions

View file

@ -30,27 +30,33 @@ pub enum Alice {
}, },
XmrLockTransferProofSent { XmrLockTransferProofSent {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: alice::State3, state3: alice::State3,
}, },
EncSigLearned { EncSigLearned {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
encrypted_signature: EncryptedSignature, encrypted_signature: EncryptedSignature,
state3: alice::State3, state3: alice::State3,
}, },
CancelTimelockExpired { CancelTimelockExpired {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: alice::State3, state3: alice::State3,
}, },
BtcCancelled { BtcCancelled {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: alice::State3, state3: alice::State3,
}, },
BtcPunishable { BtcPunishable {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: alice::State3, state3: alice::State3,
}, },
BtcRefunded { BtcRefunded {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: alice::State3, state3: alice::State3,
#[serde(with = "monero_private_key")] #[serde(with = "monero_private_key")]
spend_key: monero::PrivateKey, spend_key: monero::PrivateKey,
@ -95,52 +101,62 @@ impl From<&AliceState> for Alice {
}, },
AliceState::XmrLockTransferProofSent { AliceState::XmrLockTransferProofSent {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3, state3,
} => Alice::XmrLockTransferProofSent { } => Alice::XmrLockTransferProofSent {
monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight, monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight,
transfer_proof: transfer_proof.clone(),
state3: state3.as_ref().clone(), state3: state3.as_ref().clone(),
}, },
AliceState::EncSigLearned { AliceState::EncSigLearned {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3, state3,
encrypted_signature, encrypted_signature,
} => Alice::EncSigLearned { } => Alice::EncSigLearned {
monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight, monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight,
transfer_proof: transfer_proof.clone(),
state3: state3.as_ref().clone(), state3: state3.as_ref().clone(),
encrypted_signature: *encrypted_signature.clone(), encrypted_signature: *encrypted_signature.clone(),
}, },
AliceState::BtcRedeemed => Alice::Done(AliceEndState::BtcRedeemed), AliceState::BtcRedeemed => Alice::Done(AliceEndState::BtcRedeemed),
AliceState::BtcCancelled { AliceState::BtcCancelled {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3, state3,
..
} => Alice::BtcCancelled { } => Alice::BtcCancelled {
monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight, monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight,
transfer_proof: transfer_proof.clone(),
state3: state3.as_ref().clone(), state3: state3.as_ref().clone(),
}, },
AliceState::BtcRefunded { AliceState::BtcRefunded {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
spend_key, spend_key,
state3, state3,
} => Alice::BtcRefunded { } => Alice::BtcRefunded {
monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight, monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight,
transfer_proof: transfer_proof.clone(),
spend_key: *spend_key, spend_key: *spend_key,
state3: state3.as_ref().clone(), state3: state3.as_ref().clone(),
}, },
AliceState::BtcPunishable { AliceState::BtcPunishable {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3, state3,
..
} => Alice::BtcPunishable { } => Alice::BtcPunishable {
monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight, monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight,
transfer_proof: transfer_proof.clone(),
state3: state3.as_ref().clone(), state3: state3.as_ref().clone(),
}, },
AliceState::XmrRefunded => Alice::Done(AliceEndState::XmrRefunded), AliceState::XmrRefunded => Alice::Done(AliceEndState::XmrRefunded),
AliceState::CancelTimelockExpired { AliceState::CancelTimelockExpired {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3, state3,
} => Alice::CancelTimelockExpired { } => Alice::CancelTimelockExpired {
monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight, monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight,
transfer_proof: transfer_proof.clone(),
state3: state3.as_ref().clone(), state3: state3.as_ref().clone(),
}, },
AliceState::BtcPunished => Alice::Done(AliceEndState::BtcPunished), AliceState::BtcPunished => Alice::Done(AliceEndState::BtcPunished),
@ -178,48 +194,60 @@ impl From<Alice> for AliceState {
}, },
Alice::XmrLockTransferProofSent { Alice::XmrLockTransferProofSent {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3, state3,
} => AliceState::XmrLockTransferProofSent { } => AliceState::XmrLockTransferProofSent {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3: Box::new(state3), state3: Box::new(state3),
}, },
Alice::EncSigLearned { Alice::EncSigLearned {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3: state, state3: state,
encrypted_signature, encrypted_signature,
} => AliceState::EncSigLearned { } => AliceState::EncSigLearned {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3: Box::new(state), state3: Box::new(state),
encrypted_signature: Box::new(encrypted_signature), encrypted_signature: Box::new(encrypted_signature),
}, },
Alice::CancelTimelockExpired { Alice::CancelTimelockExpired {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3, state3,
} => AliceState::CancelTimelockExpired { } => AliceState::CancelTimelockExpired {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3: Box::new(state3), state3: Box::new(state3),
}, },
Alice::BtcCancelled { Alice::BtcCancelled {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3, state3,
} => AliceState::BtcCancelled { } => AliceState::BtcCancelled {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3: Box::new(state3), state3: Box::new(state3),
}, },
Alice::BtcPunishable { Alice::BtcPunishable {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3, state3,
} => AliceState::BtcPunishable { } => AliceState::BtcPunishable {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3: Box::new(state3), state3: Box::new(state3),
}, },
Alice::BtcRefunded { Alice::BtcRefunded {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
state3, transfer_proof,
spend_key, spend_key,
state3,
} => AliceState::BtcRefunded { } => AliceState::BtcRefunded {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
spend_key, spend_key,
state3: Box::new(state3), state3: Box::new(state3),
}, },

View file

@ -34,30 +34,36 @@ pub enum AliceState {
}, },
XmrLockTransferProofSent { XmrLockTransferProofSent {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: Box<State3>, state3: Box<State3>,
}, },
EncSigLearned { EncSigLearned {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
encrypted_signature: Box<bitcoin::EncryptedSignature>, encrypted_signature: Box<bitcoin::EncryptedSignature>,
state3: Box<State3>, state3: Box<State3>,
}, },
BtcRedeemed, BtcRedeemed,
BtcCancelled { BtcCancelled {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: Box<State3>, state3: Box<State3>,
}, },
BtcRefunded { BtcRefunded {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
spend_key: monero::PrivateKey, spend_key: monero::PrivateKey,
state3: Box<State3>, state3: Box<State3>,
}, },
BtcPunishable { BtcPunishable {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: Box<State3>, state3: Box<State3>,
}, },
XmrRefunded, XmrRefunded,
CancelTimelockExpired { CancelTimelockExpired {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: Box<State3>, state3: Box<State3>,
}, },
BtcPunished, BtcPunished,

View file

@ -101,9 +101,9 @@ async fn next_state(
.await?; .await?;
AliceState::XmrLockTransactionSent { AliceState::XmrLockTransactionSent {
state3,
transfer_proof,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
} }
} }
_ => AliceState::SafelyAborted, _ => AliceState::SafelyAborted,
@ -120,47 +120,44 @@ async fn next_state(
.await?; .await?;
AliceState::XmrLocked { AliceState::XmrLocked {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof, transfer_proof,
state3,
} }
} }
_ => AliceState::CancelTimelockExpired { _ => AliceState::CancelTimelockExpired {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
}, },
}, },
AliceState::XmrLocked { AliceState::XmrLocked {
state3,
transfer_proof,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
} => match state3.expired_timelocks(bitcoin_wallet).await? { } => match state3.expired_timelocks(bitcoin_wallet).await? {
ExpiredTimelocks::None => { ExpiredTimelocks::None => {
event_loop_handle event_loop_handle
.send_transfer_proof(transfer_proof.clone()) .send_transfer_proof(transfer_proof.clone())
.await?; .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 { XmrLockTransferProofSent {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
} }
} }
_ => AliceState::CancelTimelockExpired { _ => AliceState::CancelTimelockExpired {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
}, },
}, },
AliceState::XmrLockTransferProofSent { AliceState::XmrLockTransferProofSent {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
} => { } => {
let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await; let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await;
@ -169,31 +166,35 @@ async fn next_state(
select! { select! {
_ = tx_lock_status.wait_until_confirmed_with(state3.cancel_timelock) => { _ = tx_lock_status.wait_until_confirmed_with(state3.cancel_timelock) => {
AliceState::CancelTimelockExpired { AliceState::CancelTimelockExpired {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
} }
} }
enc_sig = event_loop_handle.recv_encrypted_signature() => { enc_sig = event_loop_handle.recv_encrypted_signature() => {
tracing::info!("Received encrypted signature"); tracing::info!("Received encrypted signature");
AliceState::EncSigLearned { AliceState::EncSigLearned {
state3,
encrypted_signature: Box::new(enc_sig?),
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
encrypted_signature: Box::new(enc_sig?),
state3,
} }
} }
} }
} }
_ => AliceState::CancelTimelockExpired { _ => AliceState::CancelTimelockExpired {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
}, },
} }
} }
AliceState::EncSigLearned { AliceState::EncSigLearned {
state3,
encrypted_signature,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
encrypted_signature,
state3,
} => match state3.expired_timelocks(bitcoin_wallet).await? { } => match state3.expired_timelocks(bitcoin_wallet).await? {
ExpiredTimelocks::None => { ExpiredTimelocks::None => {
let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await; let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await;
@ -212,8 +213,9 @@ async fn next_state(
.await?; .await?;
AliceState::CancelTimelockExpired { AliceState::CancelTimelockExpired {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
} }
} }
}, },
@ -224,20 +226,23 @@ async fn next_state(
.await?; .await?;
AliceState::CancelTimelockExpired { AliceState::CancelTimelockExpired {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
} }
} }
} }
} }
_ => AliceState::CancelTimelockExpired { _ => AliceState::CancelTimelockExpired {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
}, },
}, },
AliceState::CancelTimelockExpired { AliceState::CancelTimelockExpired {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
} => { } => {
let transaction = state3.signed_cancel_transaction()?; let transaction = state3.signed_cancel_transaction()?;
@ -259,13 +264,15 @@ async fn next_state(
} }
AliceState::BtcCancelled { AliceState::BtcCancelled {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
} }
} }
AliceState::BtcCancelled { AliceState::BtcCancelled {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
} => { } => {
let tx_refund_status = bitcoin_wallet.subscribe_to(state3.tx_refund()).await; let tx_refund_status = bitcoin_wallet.subscribe_to(state3.tx_refund()).await;
let tx_cancel_status = bitcoin_wallet.subscribe_to(state3.tx_cancel()).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)?; let spend_key = state3.extract_monero_private_key(published_refund_tx)?;
AliceState::BtcRefunded { AliceState::BtcRefunded {
monero_wallet_restore_blockheight,
transfer_proof,
spend_key, spend_key,
state3, state3,
monero_wallet_restore_blockheight,
} }
} }
_ = tx_cancel_status.wait_until_confirmed_with(state3.punish_timelock) => { _ = tx_cancel_status.wait_until_confirmed_with(state3.punish_timelock) => {
AliceState::BtcPunishable { AliceState::BtcPunishable {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
} }
} }
} }
} }
AliceState::BtcRefunded { AliceState::BtcRefunded {
monero_wallet_restore_blockheight,
transfer_proof,
spend_key, spend_key,
state3, state3,
monero_wallet_restore_blockheight,
} => { } => {
let view_key = state3.v; 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 monero_wallet
.create_from(spend_key, view_key, monero_wallet_restore_blockheight) .create_from(spend_key, view_key, monero_wallet_restore_blockheight)
.await?; .await?;
@ -305,8 +321,9 @@ async fn next_state(
AliceState::XmrRefunded AliceState::XmrRefunded
} }
AliceState::BtcPunishable { AliceState::BtcPunishable {
state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3,
} => { } => {
let signed_tx_punish = state3.signed_punish_transaction()?; 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)?; let spend_key = state3.extract_monero_private_key(published_refund_tx)?;
AliceState::BtcRefunded { AliceState::BtcRefunded {
monero_wallet_restore_blockheight,
transfer_proof,
spend_key, spend_key,
state3, state3,
monero_wallet_restore_blockheight,
} }
} }
} }