From b1affe3ecf1677f986341eeb206e152f5cd6ac30 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 18 Mar 2021 13:18:48 +1100 Subject: [PATCH] Insert latest state and call run_until only once Instead of calling this function in all the branches, we can simply make the whole match statement evaluate to the new state and perform this functionality at the very end. --- swap/src/protocol/alice/swap.rs | 277 ++++++++++---------------------- swap/src/protocol/bob/swap.rs | 195 +++++----------------- 2 files changed, 118 insertions(+), 354 deletions(-) diff --git a/swap/src/protocol/alice/swap.rs b/swap/src/protocol/alice/swap.rs index adedb9b0..5f520241 100644 --- a/swap/src/protocol/alice/swap.rs +++ b/swap/src/protocol/alice/swap.rs @@ -73,7 +73,7 @@ async fn run_until_internal( return Ok(state); } - match state { + let new_state = match state { AliceState::Started { state3 } => { timeout( env_config.bob_time_to_act, @@ -88,22 +88,7 @@ async fn run_until_internal( }) .await?; - let state = AliceState::BtcLocked { state3 }; - - let db_state = (&state).into(); - db.insert_latest_state(swap_id, database::Swap::Alice(db_state)) - .await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - bitcoin_wallet, - monero_wallet, - env_config, - swap_id, - db, - ) - .await + AliceState::BtcLocked { state3 } } AliceState::BtcLocked { state3 } => { // Record the current monero wallet block height so we don't have to scan from @@ -123,105 +108,60 @@ async fn run_until_internal( .send_transfer_proof(transfer_proof) .await?; - let state = AliceState::XmrLocked { + AliceState::XmrLocked { state3, monero_wallet_restore_blockheight, - }; - - let db_state = (&state).into(); - db.insert_latest_state(swap_id, database::Swap::Alice(db_state)) - .await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - bitcoin_wallet, - monero_wallet, - env_config, - swap_id, - db, - ) - .await + } } AliceState::XmrLocked { state3, monero_wallet_restore_blockheight, - } => { - let state = match state3.expired_timelocks(bitcoin_wallet.as_ref()).await? { - ExpiredTimelocks::None => { - select! { - _ = state3.wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref()) => { - AliceState::CancelTimelockExpired { - state3, - monero_wallet_restore_blockheight, - } + } => match state3.expired_timelocks(bitcoin_wallet.as_ref()).await? { + ExpiredTimelocks::None => { + select! { + _ = state3.wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref()) => { + AliceState::CancelTimelockExpired { + state3, + monero_wallet_restore_blockheight, } - enc_sig = event_loop_handle.recv_encrypted_signature() => { - tracing::info!("Received encrypted signature"); + } + 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, - } + AliceState::EncSigLearned { + state3, + encrypted_signature: Box::new(enc_sig?), + monero_wallet_restore_blockheight, } } } - _ => AliceState::CancelTimelockExpired { - state3, - monero_wallet_restore_blockheight, - }, - }; - - let db_state = (&state).into(); - db.insert_latest_state(swap_id, database::Swap::Alice(db_state)) - .await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - bitcoin_wallet.clone(), - monero_wallet, - env_config, - swap_id, - db, - ) - .await - } + } + _ => AliceState::CancelTimelockExpired { + state3, + monero_wallet_restore_blockheight, + }, + }, AliceState::EncSigLearned { state3, encrypted_signature, monero_wallet_restore_blockheight, - } => { - let state = match state3.expired_timelocks(bitcoin_wallet.as_ref()).await? { - ExpiredTimelocks::None => { - match TxRedeem::new(&state3.tx_lock, &state3.redeem_address).complete( - *encrypted_signature, - state3.a.clone(), - state3.s_a.to_secpfun_scalar(), - state3.B, - ) { - Ok(tx) => match bitcoin_wallet.broadcast(tx, "redeem").await { - Ok((_, finality)) => match finality.await { - Ok(_) => AliceState::BtcRedeemed, - Err(e) => { - bail!("Waiting for Bitcoin transaction finality failed with {}! The redeem transaction was published, but it is not ensured that the transaction was included! You're screwed.", e) - } - }, + } => match state3.expired_timelocks(bitcoin_wallet.as_ref()).await? { + ExpiredTimelocks::None => { + match TxRedeem::new(&state3.tx_lock, &state3.redeem_address).complete( + *encrypted_signature, + state3.a.clone(), + state3.s_a.to_secpfun_scalar(), + state3.B, + ) { + Ok(tx) => match bitcoin_wallet.broadcast(tx, "redeem").await { + Ok((_, finality)) => match finality.await { + Ok(_) => AliceState::BtcRedeemed, Err(e) => { - error!("Publishing the redeem transaction failed with {}, attempting to wait for cancellation now. If you restart the application before the timelock is expired publishing the redeem transaction will be retried.", e); - state3 - .wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref()) - .await?; - - AliceState::CancelTimelockExpired { - state3, - monero_wallet_restore_blockheight, - } + bail!("Waiting for Bitcoin transaction finality failed with {}! The redeem transaction was published, but it is not ensured that the transaction was included! You're screwed.", e) } }, Err(e) => { - error!("Constructing the redeem transaction failed with {}, attempting to wait for cancellation now.", e); + error!("Publishing the redeem transaction failed with {}, attempting to wait for cancellation now. If you restart the application before the timelock is expired publishing the redeem transaction will be retried.", e); state3 .wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref()) .await?; @@ -231,29 +171,25 @@ async fn run_until_internal( monero_wallet_restore_blockheight, } } + }, + Err(e) => { + error!("Constructing the redeem transaction failed with {}, attempting to wait for cancellation now.", e); + state3 + .wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref()) + .await?; + + AliceState::CancelTimelockExpired { + state3, + monero_wallet_restore_blockheight, + } } } - _ => AliceState::CancelTimelockExpired { - state3, - monero_wallet_restore_blockheight, - }, - }; - - let db_state = (&state).into(); - db.insert_latest_state(swap_id, database::Swap::Alice(db_state)) - .await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - bitcoin_wallet, - monero_wallet, - env_config, - swap_id, - db, - ) - .await - } + } + _ => AliceState::CancelTimelockExpired { + state3, + monero_wallet_restore_blockheight, + }, + }, AliceState::CancelTimelockExpired { state3, monero_wallet_restore_blockheight, @@ -281,24 +217,10 @@ async fn run_until_internal( // returned mined block height } - let state = AliceState::BtcCancelled { + AliceState::BtcCancelled { state3, monero_wallet_restore_blockheight, - }; - let db_state = (&state).into(); - db.insert_latest_state(swap_id, database::Swap::Alice(db_state)) - .await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - bitcoin_wallet, - monero_wallet, - env_config, - swap_id, - db, - ) - .await + } } AliceState::BtcCancelled { state3, @@ -314,7 +236,7 @@ async fn run_until_internal( status.is_confirmed_with(state3.punish_timelock) }); - let state = tokio::select! { + select! { seen_refund = seen_refund_tx => { seen_refund.context("Failed to monitor refund transaction")?; let published_refund_tx = bitcoin_wallet.get_raw_transaction(tx_refund.txid()).await?; @@ -338,23 +260,7 @@ async fn run_until_internal( monero_wallet_restore_blockheight, } } - }; - - let db_state = (&state).into(); - db.insert_latest_state(swap_id, database::Swap::Alice(db_state)) - .await?; - - run_until_internal( - state, - is_target_state, - event_loop_handle, - bitcoin_wallet.clone(), - monero_wallet, - env_config, - swap_id, - db, - ) - .await + } } AliceState::BtcRefunded { spend_key, @@ -367,11 +273,7 @@ async fn run_until_internal( .create_from(spend_key, view_key, monero_wallet_restore_blockheight) .await?; - let state = AliceState::XmrRefunded; - let db_state = (&state).into(); - db.insert_latest_state(swap_id, database::Swap::Alice(db_state)) - .await?; - Ok(state) + AliceState::XmrRefunded } AliceState::BtcPunishable { state3, @@ -409,51 +311,36 @@ async fn run_until_internal( state3.a.clone(), state3.S_b_bitcoin, )?; - let state = AliceState::BtcRefunded { + AliceState::BtcRefunded { spend_key, state3, monero_wallet_restore_blockheight, - }; - let db_state = (&state).into(); - db.insert_latest_state(swap_id, database::Swap::Alice(db_state)) - .await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - bitcoin_wallet.clone(), - monero_wallet, - env_config, - swap_id, - db, - ) - .await + } } Either::Left((Err(e), _)) => { bail!(e.context("Failed to monitor refund transaction")) } - Either::Right(_) => { - let state = AliceState::BtcPunished; - let db_state = (&state).into(); - db.insert_latest_state(swap_id, database::Swap::Alice(db_state)) - .await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - bitcoin_wallet.clone(), - monero_wallet, - env_config, - swap_id, - db, - ) - .await - } + Either::Right(_) => AliceState::BtcPunished, } } - AliceState::XmrRefunded => Ok(AliceState::XmrRefunded), - AliceState::BtcRedeemed => Ok(AliceState::BtcRedeemed), - AliceState::BtcPunished => Ok(AliceState::BtcPunished), - AliceState::SafelyAborted => Ok(AliceState::SafelyAborted), - } + AliceState::XmrRefunded => AliceState::XmrRefunded, + AliceState::BtcRedeemed => AliceState::BtcRedeemed, + AliceState::BtcPunished => AliceState::BtcPunished, + AliceState::SafelyAborted => AliceState::SafelyAborted, + }; + + let db_state = (&new_state).into(); + db.insert_latest_state(swap_id, database::Swap::Alice(db_state)) + .await?; + run_until_internal( + new_state, + is_target_state, + event_loop_handle, + bitcoin_wallet, + monero_wallet, + env_config, + swap_id, + db, + ) + .await } diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index f6b7ea78..0a7dabe6 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -66,7 +66,7 @@ async fn run_until_internal( return Ok(state); } - match state { + let new_state = match state { BobState::Started { btc_amount } => { let bitcoin_refund_address = bitcoin_wallet.new_address().await?; @@ -80,21 +80,7 @@ async fn run_until_internal( ) .await?; - let state = BobState::ExecutionSetupDone(state2); - let db_state = state.clone().into(); - db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - db, - bitcoin_wallet, - monero_wallet, - swap_id, - env_config, - receive_monero_address, - ) - .await + BobState::ExecutionSetupDone(state2) } BobState::ExecutionSetupDone(state2) => { // Do not lock Bitcoin if not connected to Alice. @@ -107,28 +93,12 @@ async fn run_until_internal( .context("Failed to sign Bitcoin lock transaction")?; let (..) = bitcoin_wallet.broadcast(signed_tx, "lock").await?; - let state = BobState::BtcLocked(state3); - let db_state = state.clone().into(); - db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - db, - bitcoin_wallet, - monero_wallet, - swap_id, - env_config, - receive_monero_address, - ) - .await + BobState::BtcLocked(state3) } // Bob has locked Btc // Watch for Alice to Lock Xmr or for cancel timelock to elapse BobState::BtcLocked(state3) => { - let state = if let ExpiredTimelocks::None = - state3.current_epoch(bitcoin_wallet.as_ref()).await? - { + if let ExpiredTimelocks::None = state3.current_epoch(bitcoin_wallet.as_ref()).await? { event_loop_handle.dial().await?; let transfer_proof_watcher = event_loop_handle.recv_transfer_proof(); @@ -163,30 +133,14 @@ async fn run_until_internal( } else { let state4 = state3.cancel(); BobState::CancelTimelockExpired(state4) - }; - let db_state = state.clone().into(); - db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - db, - bitcoin_wallet, - monero_wallet, - swap_id, - env_config, - receive_monero_address, - ) - .await + } } BobState::XmrLockProofReceived { state, lock_transfer_proof, monero_wallet_restore_blockheight, } => { - let state = if let ExpiredTimelocks::None = - state.current_epoch(bitcoin_wallet.as_ref()).await? - { + if let ExpiredTimelocks::None = state.current_epoch(bitcoin_wallet.as_ref()).await? { event_loop_handle.dial().await?; let xmr_lock_watcher = state.clone().watch_for_lock_xmr( @@ -217,27 +171,10 @@ async fn run_until_internal( } else { let state4 = state.cancel(); BobState::CancelTimelockExpired(state4) - }; - - let db_state = state.clone().into(); - db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - db, - bitcoin_wallet, - monero_wallet, - swap_id, - env_config, - receive_monero_address, - ) - .await + } } BobState::XmrLocked(state) => { - let state = if let ExpiredTimelocks::None = - state.expired_timelock(bitcoin_wallet.as_ref()).await? - { + if let ExpiredTimelocks::None = state.expired_timelock(bitcoin_wallet.as_ref()).await? { event_loop_handle.dial().await?; // Alice has locked Xmr // Bob sends Alice his key @@ -261,26 +198,10 @@ async fn run_until_internal( } } else { BobState::CancelTimelockExpired(state) - }; - let db_state = state.clone().into(); - db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - db, - bitcoin_wallet, - monero_wallet, - swap_id, - env_config, - receive_monero_address, - ) - .await + } } BobState::EncSigSent(state) => { - let state = if let ExpiredTimelocks::None = - state.expired_timelock(bitcoin_wallet.as_ref()).await? - { + if let ExpiredTimelocks::None = state.expired_timelock(bitcoin_wallet.as_ref()).await? { let state_clone = state.clone(); let redeem_watcher = state_clone.watch_for_redeem_btc(bitcoin_wallet.as_ref()); let cancel_timelock_expires = @@ -296,22 +217,7 @@ async fn run_until_internal( } } else { BobState::CancelTimelockExpired(state) - }; - - let db_state = state.clone().into(); - db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - db, - bitcoin_wallet.clone(), - monero_wallet, - swap_id, - env_config, - receive_monero_address, - ) - .await + } } BobState::BtcRedeemed(state) => { // Bob redeems XMR using revealed s_a @@ -326,23 +232,9 @@ async fn run_until_internal( tracing::info!("Sent XMR to {} in tx {}", receive_monero_address, tx_hash.0); } - let state = BobState::XmrRedeemed { + BobState::XmrRedeemed { tx_lock_id: state.tx_lock_id(), - }; - let db_state = state.clone().into(); - db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - db, - bitcoin_wallet, - monero_wallet, - swap_id, - env_config, - receive_monero_address, - ) - .await + } } BobState::CancelTimelockExpired(state4) => { if state4 @@ -353,26 +245,11 @@ async fn run_until_internal( state4.submit_tx_cancel(bitcoin_wallet.as_ref()).await?; } - let state = BobState::BtcCancelled(state4); - db.insert_latest_state(swap_id, Swap::Bob(state.clone().into())) - .await?; - - run_until_internal( - state, - is_target_state, - event_loop_handle, - db, - bitcoin_wallet, - monero_wallet, - swap_id, - env_config, - receive_monero_address, - ) - .await + BobState::BtcCancelled(state4) } BobState::BtcCancelled(state) => { // Bob has cancelled the swap - let state = match state.expired_timelock(bitcoin_wallet.as_ref()).await? { + match state.expired_timelock(bitcoin_wallet.as_ref()).await? { ExpiredTimelocks::None => { bail!( "Internal error: canceled state reached before cancel timelock was expired" @@ -385,28 +262,28 @@ async fn run_until_internal( ExpiredTimelocks::Punish => BobState::BtcPunished { tx_lock_id: state.tx_lock_id(), }, - }; - - let db_state = state.clone().into(); - db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?; - run_until_internal( - state, - is_target_state, - event_loop_handle, - db, - bitcoin_wallet, - monero_wallet, - swap_id, - env_config, - receive_monero_address, - ) - .await + } } - BobState::BtcRefunded(state4) => Ok(BobState::BtcRefunded(state4)), - BobState::BtcPunished { tx_lock_id } => Ok(BobState::BtcPunished { tx_lock_id }), - BobState::SafelyAborted => Ok(BobState::SafelyAborted), - BobState::XmrRedeemed { tx_lock_id } => Ok(BobState::XmrRedeemed { tx_lock_id }), - } + BobState::BtcRefunded(state4) => BobState::BtcRefunded(state4), + BobState::BtcPunished { tx_lock_id } => BobState::BtcPunished { tx_lock_id }, + BobState::SafelyAborted => BobState::SafelyAborted, + BobState::XmrRedeemed { tx_lock_id } => BobState::XmrRedeemed { tx_lock_id }, + }; + + let db_state = new_state.clone().into(); + db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?; + run_until_internal( + new_state, + is_target_state, + event_loop_handle, + db, + bitcoin_wallet, + monero_wallet, + swap_id, + env_config, + receive_monero_address, + ) + .await } pub async fn request_price_and_setup(