Wait for lock tx and send transfer proof in separate state

Sending the transfer transaction in a distinct state helps ensuring
that we do not send the Monero lock transaction twice in a restart
scenario.
Waiting for the first transaction confirmation in a separate state
helps ensuring that we send the transfer proof in a restart scenario.
This commit is contained in:
Daniel Karzel 2021-03-25 19:55:54 +11:00 committed by Thomas Eizinger
parent dfd69c9c80
commit 183e8f02de
No known key found for this signature in database
GPG Key ID: 651AC83A6C6C8B96
4 changed files with 126 additions and 28 deletions

View File

@ -1,6 +1,6 @@
use crate::bitcoin::EncryptedSignature; use crate::bitcoin::EncryptedSignature;
use crate::monero; use crate::monero;
use crate::monero::monero_private_key; use crate::monero::{monero_private_key, TransferProof};
use crate::protocol::alice; use crate::protocol::alice;
use crate::protocol::alice::AliceState; use crate::protocol::alice::AliceState;
use ::bitcoin::hashes::core::fmt::Display; use ::bitcoin::hashes::core::fmt::Display;
@ -18,7 +18,17 @@ pub enum Alice {
BtcLocked { BtcLocked {
state3: alice::State3, state3: alice::State3,
}, },
XmrLockTransactionSent {
monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: alice::State3,
},
XmrLocked { XmrLocked {
monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: alice::State3,
},
XmrLockTransferProofSent {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
state3: alice::State3, state3: alice::State3,
}, },
@ -65,10 +75,28 @@ impl From<&AliceState> for Alice {
AliceState::BtcLocked { state3 } => Alice::BtcLocked { AliceState::BtcLocked { state3 } => Alice::BtcLocked {
state3: state3.as_ref().clone(), state3: state3.as_ref().clone(),
}, },
AliceState::XmrLockTransactionSent {
monero_wallet_restore_blockheight,
transfer_proof,
state3,
} => Alice::XmrLockTransactionSent {
monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight,
transfer_proof: transfer_proof.clone(),
state3: state3.as_ref().clone(),
},
AliceState::XmrLocked { AliceState::XmrLocked {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3, state3,
} => Alice::XmrLocked { } => Alice::XmrLocked {
monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight,
transfer_proof: transfer_proof.clone(),
state3: state3.as_ref().clone(),
},
AliceState::XmrLockTransferProofSent {
monero_wallet_restore_blockheight,
state3,
} => Alice::XmrLockTransferProofSent {
monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight, monero_wallet_restore_blockheight: *monero_wallet_restore_blockheight,
state3: state3.as_ref().clone(), state3: state3.as_ref().clone(),
}, },
@ -130,10 +158,28 @@ impl From<Alice> for AliceState {
Alice::BtcLocked { state3 } => AliceState::BtcLocked { Alice::BtcLocked { state3 } => AliceState::BtcLocked {
state3: Box::new(state3), state3: Box::new(state3),
}, },
Alice::XmrLockTransactionSent {
monero_wallet_restore_blockheight,
transfer_proof,
state3,
} => AliceState::XmrLockTransactionSent {
monero_wallet_restore_blockheight,
transfer_proof,
state3: Box::new(state3),
},
Alice::XmrLocked { Alice::XmrLocked {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
state3, state3,
} => AliceState::XmrLocked { } => AliceState::XmrLocked {
monero_wallet_restore_blockheight,
transfer_proof,
state3: Box::new(state3),
},
Alice::XmrLockTransferProofSent {
monero_wallet_restore_blockheight,
state3,
} => AliceState::XmrLockTransferProofSent {
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
state3: Box::new(state3), state3: Box::new(state3),
}, },
@ -192,7 +238,11 @@ impl Display for Alice {
match self { match self {
Alice::Started { .. } => write!(f, "Started"), Alice::Started { .. } => write!(f, "Started"),
Alice::BtcLocked { .. } => f.write_str("Bitcoin locked"), Alice::BtcLocked { .. } => f.write_str("Bitcoin locked"),
Alice::XmrLockTransactionSent { .. } => f.write_str("Monero lock transaction sent"),
Alice::XmrLocked { .. } => f.write_str("Monero locked"), Alice::XmrLocked { .. } => f.write_str("Monero locked"),
Alice::XmrLockTransferProofSent { .. } => {
f.write_str("Monero lock transfer proof sent")
}
Alice::CancelTimelockExpired { .. } => f.write_str("Cancel timelock is expired"), Alice::CancelTimelockExpired { .. } => f.write_str("Cancel timelock is expired"),
Alice::BtcCancelled { .. } => f.write_str("Bitcoin cancel transaction published"), Alice::BtcCancelled { .. } => f.write_str("Bitcoin cancel transaction published"),
Alice::BtcPunishable { .. } => f.write_str("Bitcoin punishable"), Alice::BtcPunishable { .. } => f.write_str("Bitcoin punishable"),

View File

@ -22,7 +22,17 @@ pub enum AliceState {
BtcLocked { BtcLocked {
state3: Box<State3>, state3: Box<State3>,
}, },
XmrLockTransactionSent {
monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: Box<State3>,
},
XmrLocked { XmrLocked {
monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: Box<State3>,
},
XmrLockTransferProofSent {
monero_wallet_restore_blockheight: BlockHeight, monero_wallet_restore_blockheight: BlockHeight,
state3: Box<State3>, state3: Box<State3>,
}, },
@ -59,7 +69,11 @@ impl fmt::Display for AliceState {
match self { match self {
AliceState::Started { .. } => write!(f, "started"), AliceState::Started { .. } => write!(f, "started"),
AliceState::BtcLocked { .. } => write!(f, "btc is locked"), AliceState::BtcLocked { .. } => write!(f, "btc is locked"),
AliceState::XmrLockTransactionSent { .. } => write!(f, "xmr lock transaction sent"),
AliceState::XmrLocked { .. } => write!(f, "xmr is locked"), AliceState::XmrLocked { .. } => write!(f, "xmr is locked"),
AliceState::XmrLockTransferProofSent { .. } => {
write!(f, "xmr lock transfer proof sent")
}
AliceState::EncSigLearned { .. } => write!(f, "encrypted signature is learned"), AliceState::EncSigLearned { .. } => write!(f, "encrypted signature is learned"),
AliceState::BtcRedeemed => write!(f, "btc is redeemed"), AliceState::BtcRedeemed => write!(f, "btc is redeemed"),
AliceState::BtcCancelled { .. } => write!(f, "btc is cancelled"), AliceState::BtcCancelled { .. } => write!(f, "btc is cancelled"),

View File

@ -5,6 +5,7 @@ use crate::env::Config;
use crate::protocol::alice; use crate::protocol::alice;
use crate::protocol::alice::event_loop::EventLoopHandle; use crate::protocol::alice::event_loop::EventLoopHandle;
use crate::protocol::alice::AliceState; use crate::protocol::alice::AliceState;
use crate::protocol::alice::AliceState::XmrLockTransferProofSent;
use crate::{bitcoin, database, monero}; use crate::{bitcoin, database, monero};
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
@ -88,10 +89,8 @@ async fn next_state(
} }
} }
} }
AliceState::BtcLocked { state3 } => match state3 AliceState::BtcLocked { state3 } => {
.expired_timelocks(bitcoin_wallet) match state3.expired_timelocks(bitcoin_wallet).await? {
.await?
{
ExpiredTimelocks::None => { ExpiredTimelocks::None => {
// Record the current monero wallet block height so we don't have to scan from // Record the current monero wallet block height so we don't have to scan from
// block 0 for scenarios where we create a refund wallet. // block 0 for scenarios where we create a refund wallet.
@ -101,30 +100,65 @@ async fn next_state(
.transfer(state3.lock_xmr_transfer_request()) .transfer(state3.lock_xmr_transfer_request())
.await?; .await?;
AliceState::XmrLockTransactionSent {
state3,
transfer_proof,
monero_wallet_restore_blockheight,
}
}
_ => AliceState::SafelyAborted,
}
}
AliceState::XmrLockTransactionSent {
monero_wallet_restore_blockheight,
transfer_proof,
state3,
} => match state3.expired_timelocks(bitcoin_wallet).await? {
ExpiredTimelocks::None => {
monero_wallet monero_wallet
.watch_for_transfer(state3.lock_xmr_watch_request(transfer_proof.clone(), 1)) .watch_for_transfer(state3.lock_xmr_watch_request(transfer_proof.clone(), 1))
.await?; .await?;
// TODO: Waiting for XMR confirmations should be done in a separate
// state! We have to record that Alice has already sent the transaction.
// Otherwise Alice might publish the lock tx twice!
event_loop_handle
.send_transfer_proof(transfer_proof.clone())
.await?;
monero_wallet
.watch_for_transfer(state3.lock_xmr_watch_request(transfer_proof, 10))
.await?;
AliceState::XmrLocked { AliceState::XmrLocked {
state3, state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
transfer_proof,
} }
} }
_ => AliceState::SafelyAborted, _ => AliceState::CancelTimelockExpired {
state3,
monero_wallet_restore_blockheight,
}, },
},
AliceState::XmrLocked { AliceState::XmrLocked {
state3,
transfer_proof,
monero_wallet_restore_blockheight,
} => 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,
}
}
_ => AliceState::CancelTimelockExpired {
state3,
monero_wallet_restore_blockheight,
},
},
AliceState::XmrLockTransferProofSent {
state3, state3,
monero_wallet_restore_blockheight, monero_wallet_restore_blockheight,
} => { } => {

View File

@ -790,8 +790,8 @@ struct Containers<'a> {
pub mod alice_run_until { pub mod alice_run_until {
use swap::protocol::alice::AliceState; use swap::protocol::alice::AliceState;
pub fn is_xmr_locked(state: &AliceState) -> bool { pub fn is_xmr_lock_transaction_sent(state: &AliceState) -> bool {
matches!(state, AliceState::XmrLocked { .. }) matches!(state, AliceState::XmrLockTransactionSent { .. })
} }
pub fn is_encsig_learned(state: &AliceState) -> bool { pub fn is_encsig_learned(state: &AliceState) -> bool {