diff --git a/swap/src/database/alice.rs b/swap/src/database/alice.rs index 3c62c006..3358238f 100644 --- a/swap/src/database/alice.rs +++ b/swap/src/database/alice.rs @@ -15,6 +15,9 @@ pub enum Alice { Started { state3: alice::State3, }, + BtcLockTransactionSeen { + state3: alice::State3, + }, BtcLocked { state3: alice::State3, }, @@ -81,6 +84,9 @@ impl From<&AliceState> for Alice { AliceState::Started { state3 } => Alice::Started { state3: state3.as_ref().clone(), }, + AliceState::BtcLockTransactionSeen { state3 } => Alice::BtcLockTransactionSeen { + state3: state3.as_ref().clone(), + }, AliceState::BtcLocked { state3 } => Alice::BtcLocked { state3: state3.as_ref().clone(), }, @@ -179,6 +185,9 @@ impl From for AliceState { Alice::Started { state3 } => AliceState::Started { state3: Box::new(state3), }, + Alice::BtcLockTransactionSeen { state3 } => AliceState::BtcLockTransactionSeen { + state3: Box::new(state3), + }, Alice::BtcLocked { state3 } => AliceState::BtcLocked { state3: Box::new(state3), }, @@ -278,6 +287,9 @@ impl fmt::Display for Alice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Alice::Started { .. } => write!(f, "Started"), + Alice::BtcLockTransactionSeen { .. } => { + write!(f, "Bitcoin lock transaction in mempool") + } Alice::BtcLocked { .. } => f.write_str("Bitcoin locked"), Alice::XmrLockTransactionSent { .. } => f.write_str("Monero lock transaction sent"), Alice::XmrLocked { .. } => f.write_str("Monero locked"), diff --git a/swap/src/env.rs b/swap/src/env.rs index 95d4488a..e12390a8 100644 --- a/swap/src/env.rs +++ b/swap/src/env.rs @@ -6,6 +6,7 @@ use time::NumericalStdDurationShort; #[derive(Debug, Copy, Clone, PartialEq)] pub struct Config { + pub bitcoin_lock_mempool_timeout: Duration, pub bitcoin_lock_confirmed_timeout: Duration, pub bitcoin_finality_confirmations: u32, pub bitcoin_avg_block_time: Duration, @@ -43,7 +44,8 @@ pub struct Regtest; impl GetConfig for Mainnet { fn get_config() -> Config { Config { - bitcoin_lock_confirmed_timeout: 24.hours(), + bitcoin_lock_mempool_timeout: 3.minutes(), + bitcoin_lock_confirmed_timeout: 2.hours(), bitcoin_finality_confirmations: 2, bitcoin_avg_block_time: 10.minutes(), bitcoin_cancel_timelock: CancelTimelock::new(72), @@ -59,7 +61,8 @@ impl GetConfig for Mainnet { impl GetConfig for Testnet { fn get_config() -> Config { Config { - bitcoin_lock_confirmed_timeout: 12.hours(), + bitcoin_lock_mempool_timeout: 3.minutes(), + bitcoin_lock_confirmed_timeout: 1.hours(), bitcoin_finality_confirmations: 2, bitcoin_avg_block_time: 10.minutes(), bitcoin_cancel_timelock: CancelTimelock::new(12), @@ -75,6 +78,7 @@ impl GetConfig for Testnet { impl GetConfig for Regtest { fn get_config() -> Config { Config { + bitcoin_lock_mempool_timeout: 30.seconds(), bitcoin_lock_confirmed_timeout: 1.minutes(), bitcoin_finality_confirmations: 1, bitcoin_avg_block_time: 5.seconds(), diff --git a/swap/src/protocol/alice/recovery/cancel.rs b/swap/src/protocol/alice/recovery/cancel.rs index fedab340..c3168026 100644 --- a/swap/src/protocol/alice/recovery/cancel.rs +++ b/swap/src/protocol/alice/recovery/cancel.rs @@ -23,6 +23,7 @@ pub async fn cancel( // In case no XMR has been locked, move to Safely Aborted AliceState::Started { .. } + | AliceState::BtcLockTransactionSeen { .. } | AliceState::BtcLocked { .. } => bail!("Cannot cancel swap {} because it is in state {} where no XMR was locked.", swap_id, state), AliceState::XmrLockTransactionSent { monero_wallet_restore_blockheight, transfer_proof, state3, } diff --git a/swap/src/protocol/alice/recovery/punish.rs b/swap/src/protocol/alice/recovery/punish.rs index 6ffacdf5..965fdba3 100644 --- a/swap/src/protocol/alice/recovery/punish.rs +++ b/swap/src/protocol/alice/recovery/punish.rs @@ -36,7 +36,8 @@ pub async fn punish( AliceState::Started { .. } => bail!(Error::NoBtcLocked(state)), // Punish potentially possible (no knowledge of cancel transaction) - AliceState::BtcLocked { state3, .. } + AliceState::BtcLockTransactionSeen { state3 } + | AliceState::BtcLocked { state3, .. } | AliceState::XmrLockTransactionSent {state3, ..} | AliceState::XmrLocked {state3, ..} | AliceState::XmrLockTransferProofSent {state3, ..} diff --git a/swap/src/protocol/alice/recovery/redeem.rs b/swap/src/protocol/alice/recovery/redeem.rs index 55450c87..8344db7b 100644 --- a/swap/src/protocol/alice/recovery/redeem.rs +++ b/swap/src/protocol/alice/recovery/redeem.rs @@ -84,6 +84,7 @@ pub async fn redeem( Ok((txid, state)) } AliceState::Started { .. } + | AliceState::BtcLockTransactionSeen { .. } | AliceState::BtcLocked { .. } | AliceState::XmrLockTransactionSent { .. } | AliceState::XmrLocked { .. } diff --git a/swap/src/protocol/alice/recovery/refund.rs b/swap/src/protocol/alice/recovery/refund.rs index bb26ee67..3297a454 100644 --- a/swap/src/protocol/alice/recovery/refund.rs +++ b/swap/src/protocol/alice/recovery/refund.rs @@ -39,6 +39,7 @@ pub async fn refund( // In case no XMR has been locked, move to Safely Aborted AliceState::Started { .. } + | AliceState::BtcLockTransactionSeen { .. } | AliceState::BtcLocked { .. } => bail!(Error::NoXmrLocked(state)), // Refund potentially possible (no knowledge of cancel transaction) diff --git a/swap/src/protocol/alice/recovery/safely_abort.rs b/swap/src/protocol/alice/recovery/safely_abort.rs index 29df2814..8105f068 100644 --- a/swap/src/protocol/alice/recovery/safely_abort.rs +++ b/swap/src/protocol/alice/recovery/safely_abort.rs @@ -8,7 +8,9 @@ pub async fn safely_abort(swap_id: Uuid, db: Arc) -> Result { + AliceState::Started { .. } + | AliceState::BtcLockTransactionSeen { .. } + | AliceState::BtcLocked { .. } => { let state = AliceState::SafelyAborted; let db_state = (&state).into(); diff --git a/swap/src/protocol/alice/state.rs b/swap/src/protocol/alice/state.rs index 17522a76..a2b1a6a1 100644 --- a/swap/src/protocol/alice/state.rs +++ b/swap/src/protocol/alice/state.rs @@ -21,6 +21,9 @@ pub enum AliceState { Started { state3: Box, }, + BtcLockTransactionSeen { + state3: Box, + }, BtcLocked { state3: Box, }, @@ -79,6 +82,9 @@ impl fmt::Display for AliceState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { AliceState::Started { .. } => write!(f, "started"), + AliceState::BtcLockTransactionSeen { .. } => { + write!(f, "bitcoin lock transaction in mempool") + } AliceState::BtcLocked { .. } => write!(f, "btc is locked"), AliceState::XmrLockTransactionSent { .. } => write!(f, "xmr lock transaction sent"), AliceState::XmrLocked { .. } => write!(f, "xmr is locked"), diff --git a/swap/src/protocol/alice/swap.rs b/swap/src/protocol/alice/swap.rs index bf71895d..4070a04b 100644 --- a/swap/src/protocol/alice/swap.rs +++ b/swap/src/protocol/alice/swap.rs @@ -70,6 +70,27 @@ where Ok(match state { AliceState::Started { state3 } => { + let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await; + match timeout( + env_config.bitcoin_lock_mempool_timeout, + tx_lock_status.wait_until_seen(), + ) + .await + { + Err(_) => { + info!( + minutes = %env_config.bitcoin_lock_mempool_timeout.as_secs_f64() / 60.0, + "TxLock lock was not seen in mempool in time", + ); + AliceState::SafelyAborted + } + Ok(res) => { + res?; + AliceState::BtcLockTransactionSeen { state3 } + } + } + } + AliceState::BtcLockTransactionSeen { state3 } => { let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await; match timeout( env_config.bitcoin_lock_confirmed_timeout,