Bob saves lock proof after received so he can resume swap

This commit is contained in:
Daniel Karzel 2021-01-21 16:39:30 +11:00
parent c5cfc2ce20
commit 3593f5323a
7 changed files with 136 additions and 22 deletions

View File

@ -382,7 +382,7 @@ pub struct Transfer {
pub unsigned_txset: String,
}
#[derive(Clone, Copy, Debug, Deserialize)]
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
pub struct BlockHeight {
pub height: u32,
}

View File

@ -1,8 +1,10 @@
use crate::{
monero::TransferProof,
protocol::{bob, bob::BobState},
SwapAmounts,
};
use ::bitcoin::hashes::core::fmt::Display;
use monero_harness::rpc::wallet::BlockHeight;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
@ -17,6 +19,11 @@ pub enum Bob {
BtcLocked {
state3: bob::State3,
},
XmrLockProofReceived {
state: bob::State3,
lock_transfer_proof: TransferProof,
monero_wallet_restore_blockheight: BlockHeight,
},
XmrLocked {
state4: bob::State4,
},
@ -43,6 +50,15 @@ impl From<BobState> for Bob {
BobState::Started { state0, amounts } => Bob::Started { state0, amounts },
BobState::Negotiated(state2) => Bob::Negotiated { state2 },
BobState::BtcLocked(state3) => Bob::BtcLocked { state3 },
BobState::XmrLockProofReceived {
state,
lock_transfer_proof,
monero_wallet_restore_blockheight,
} => Bob::XmrLockProofReceived {
state,
lock_transfer_proof,
monero_wallet_restore_blockheight,
},
BobState::XmrLocked(state4) => Bob::XmrLocked { state4 },
BobState::EncSigSent(state4) => Bob::EncSigSent { state4 },
BobState::BtcRedeemed(state5) => Bob::BtcRedeemed(state5),
@ -66,6 +82,15 @@ impl From<Bob> for BobState {
Bob::Started { state0, amounts } => BobState::Started { state0, amounts },
Bob::Negotiated { state2 } => BobState::Negotiated(state2),
Bob::BtcLocked { state3 } => BobState::BtcLocked(state3),
Bob::XmrLockProofReceived {
state,
lock_transfer_proof,
monero_wallet_restore_blockheight,
} => BobState::XmrLockProofReceived {
state,
lock_transfer_proof,
monero_wallet_restore_blockheight,
},
Bob::XmrLocked { state4 } => BobState::XmrLocked(state4),
Bob::EncSigSent { state4 } => BobState::EncSigSent(state4),
Bob::BtcRedeemed(state5) => BobState::BtcRedeemed(state5),
@ -87,6 +112,9 @@ impl Display for Bob {
Bob::Started { .. } => write!(f, "Started"),
Bob::Negotiated { .. } => f.write_str("Negotiated"),
Bob::BtcLocked { .. } => f.write_str("Bitcoin locked"),
Bob::XmrLockProofReceived { .. } => {
f.write_str("XMR lock transaction transfer proof received")
}
Bob::XmrLocked { .. } => f.write_str("Monero locked"),
Bob::CancelTimelockExpired(_) => f.write_str("Cancel timelock is expired"),
Bob::BtcCancelled(_) => f.write_str("Bitcoin refundable"),

View File

@ -139,7 +139,7 @@ impl Display for Amount {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct TransferProof {
tx_hash: TxHash,
#[serde(with = "monero_private_key")]
@ -159,7 +159,7 @@ impl TransferProof {
}
// TODO: add constructor/ change String to fixed length byte array
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct TxHash(pub String);
impl From<TxHash> for String {

View File

@ -150,6 +150,9 @@ pub async fn wait_for_bitcoin_encrypted_signature(
.recv_message3()
.await
.context("Failed to receive Bitcoin encrypted signature from Bob")?;
tracing::debug!("Message 3 received, returning it");
Ok(msg3.tx_redeem_encsig)
}

View File

@ -6,7 +6,7 @@ use crate::{
},
config::Config,
monero,
monero::monero_private_key,
monero::{monero_private_key, TransferProof},
protocol::{alice, bob},
ExpiredTimelocks, SwapAmounts,
};
@ -16,6 +16,7 @@ use ecdsa_fun::{
nonce::Deterministic,
Signature,
};
use monero_harness::rpc::wallet::BlockHeight;
use rand::{CryptoRng, RngCore};
use serde::{Deserialize, Serialize};
use sha2::Sha256;
@ -29,6 +30,11 @@ pub enum BobState {
},
Negotiated(State2),
BtcLocked(State3),
XmrLockProofReceived {
state: State3,
lock_transfer_proof: TransferProof,
monero_wallet_restore_blockheight: BlockHeight,
},
XmrLocked(State4),
EncSigSent(State4),
BtcRedeemed(State5),
@ -50,6 +56,9 @@ impl fmt::Display for BobState {
BobState::Started { .. } => write!(f, "started"),
BobState::Negotiated(..) => write!(f, "negotiated"),
BobState::BtcLocked(..) => write!(f, "btc is locked"),
BobState::XmrLockProofReceived { .. } => {
write!(f, "XMR lock transaction transfer proof received")
}
BobState::XmrLocked(..) => write!(f, "xmr is locked"),
BobState::EncSigSent(..) => write!(f, "encrypted signature is sent"),
BobState::BtcRedeemed(..) => write!(f, "btc is redeemed"),
@ -311,7 +320,7 @@ impl State3 {
pub async fn watch_for_lock_xmr<W>(
self,
xmr_wallet: &W,
msg: alice::Message2,
transfer_proof: TransferProof,
monero_wallet_restore_blockheight: u32,
) -> Result<State4>
where
@ -326,7 +335,7 @@ impl State3 {
.watch_for_transfer(
S,
self.v.public(),
msg.tx_lock_proof,
transfer_proof,
self.xmr,
self.min_monero_confirmations,
)

View File

@ -11,7 +11,7 @@ use async_recursion::async_recursion;
use rand::{rngs::OsRng, CryptoRng, RngCore};
use std::sync::Arc;
use tokio::select;
use tracing::{debug, info};
use tracing::info;
use uuid::Uuid;
pub fn is_complete(state: &BobState) -> bool {
@ -28,6 +28,10 @@ pub fn is_btc_locked(state: &BobState) -> bool {
matches!(state, BobState::BtcLocked(..))
}
pub fn is_lock_proof_received(state: &BobState) -> bool {
matches!(state, BobState::XmrLockProofReceived { .. })
}
pub fn is_xmr_locked(state: &BobState) -> bool {
matches!(state, BobState::XmrLocked(..))
}
@ -155,23 +159,12 @@ where
msg2 = msg2_watcher => {
let msg2 = msg2?;
info!("Received XMR lock transaction transfer proof from Alice, watching for transfer confirmations");
debug!("Transfer proof: {:?}", msg2.tx_lock_proof);
let xmr_lock_watcher = state3.clone()
.watch_for_lock_xmr(monero_wallet.as_ref(), msg2, monero_wallet_restore_blockheight.height);
let cancel_timelock_expires = state3.wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref());
select! {
state4 = xmr_lock_watcher => {
BobState::XmrLocked(state4?)
},
_ = cancel_timelock_expires => {
let state4 = state3.state4();
BobState::CancelTimelockExpired(state4)
}
BobState::XmrLockProofReceived {
state: state3,
lock_transfer_proof: msg2.tx_lock_proof,
monero_wallet_restore_blockheight
}
},
_ = cancel_timelock_expires => {
let state4 = state3.state4();
@ -197,6 +190,53 @@ where
)
.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?
{
event_loop_handle.dial().await?;
let xmr_lock_watcher = state.clone().watch_for_lock_xmr(
monero_wallet.as_ref(),
lock_transfer_proof,
monero_wallet_restore_blockheight.height,
);
let cancel_timelock_expires =
state.wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref());
select! {
state4 = xmr_lock_watcher => {
BobState::XmrLocked(state4?)
},
_ = cancel_timelock_expires => {
let state4 = state.state4();
BobState::CancelTimelockExpired(state4)
}
}
} else {
let state4 = state.state4();
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,
rng,
swap_id,
config,
)
.await
}
BobState::XmrLocked(state) => {
let state = if let ExpiredTimelocks::None =
state.expired_timelock(bitcoin_wallet.as_ref()).await?

View File

@ -0,0 +1,34 @@
use swap::protocol::{
alice, bob,
bob::{swap::is_lock_proof_received, BobState},
};
pub mod testutils;
#[tokio::test]
async fn given_bob_restarts_after_lock_proof_received_resume_swap() {
testutils::setup_test(|mut ctx| async move {
let alice_swap = ctx.new_swap_as_alice().await;
let bob_swap = ctx.new_swap_as_bob().await;
let alice_handle = alice::run(alice_swap);
let alice_swap_handle = tokio::spawn(alice_handle);
let bob_state = bob::run_until(bob_swap, is_lock_proof_received)
.await
.unwrap();
assert!(matches!(bob_state, BobState::XmrLockProofReceived {..}));
let bob_swap = ctx.recover_bob_from_db().await;
assert!(matches!(bob_swap.state, BobState::XmrLockProofReceived {..}));
let bob_state = bob::run(bob_swap).await.unwrap();
ctx.assert_bob_redeemed(bob_state).await;
let alice_state = alice_swap_handle.await.unwrap().unwrap();
ctx.assert_alice_redeemed(alice_state).await;
})
.await;
}