Record the monero-wallet-restore blockcheight before locking BTC

This solves issues where the CLI went offline after sending the BTC transaction, and the monero wallet restore blockheight being recorded after Alice locked the Monero, resulting in the generated XMR redeem wallet not detecting the transaction and reporting `No unlocked balance in the specified account`.
This commit is contained in:
Daniel Karzel 2021-12-20 14:58:11 +11:00
parent 637574af43
commit a9b10717ba
No known key found for this signature in database
GPG key ID: 30C3FC2E438ADB6E
10 changed files with 54 additions and 17 deletions

View file

@ -446,7 +446,7 @@ async fn main() -> Result<()> {
match swap_state {
BobState::Started { .. }
| BobState::SwapSetupCompleted(_)
| BobState::BtcLocked(_)
| BobState::BtcLocked { .. }
| BobState::XmrLockProofReceived { .. }
| BobState::XmrLocked(_)
| BobState::EncSigSent(_)

View file

@ -14,7 +14,7 @@ pub async fn cancel(
let state = db.get_state(swap_id).await?.try_into()?;
let state6 = match state {
BobState::BtcLocked(state3) => state3.cancel(),
BobState::BtcLocked { state3, .. } => state3.cancel(),
BobState::XmrLockProofReceived { state, .. } => state.cancel(),
BobState::XmrLocked(state4) => state4.cancel(),
BobState::EncSigSent(state4) => state4.cancel(),

View file

@ -14,7 +14,7 @@ pub async fn refund(
let state = db.get_state(swap_id).await?.try_into()?;
let state6 = match state {
BobState::BtcLocked(state3) => state3.cancel(),
BobState::BtcLocked { state3, .. } => state3.cancel(),
BobState::XmrLockProofReceived { state, .. } => state.cancel(),
BobState::XmrLocked(state4) => state4.cancel(),
BobState::EncSigSent(state4) => state4.cancel(),

View file

@ -20,6 +20,7 @@ pub enum Bob {
},
BtcLocked {
state3: bob::State3,
monero_wallet_restore_blockheight: BlockHeight,
},
XmrLockProofReceived {
state: bob::State3,
@ -57,7 +58,13 @@ impl From<BobState> for Bob {
change_address,
},
BobState::SwapSetupCompleted(state2) => Bob::ExecutionSetupDone { state2 },
BobState::BtcLocked(state3) => Bob::BtcLocked { state3 },
BobState::BtcLocked {
state3,
monero_wallet_restore_blockheight,
} => Bob::BtcLocked {
state3,
monero_wallet_restore_blockheight,
},
BobState::XmrLockProofReceived {
state,
lock_transfer_proof,
@ -95,7 +102,13 @@ impl From<Bob> for BobState {
change_address,
},
Bob::ExecutionSetupDone { state2 } => BobState::SwapSetupCompleted(state2),
Bob::BtcLocked { state3 } => BobState::BtcLocked(state3),
Bob::BtcLocked {
state3,
monero_wallet_restore_blockheight,
} => BobState::BtcLocked {
state3,
monero_wallet_restore_blockheight,
},
Bob::XmrLockProofReceived {
state,
lock_transfer_proof,

View file

@ -28,7 +28,10 @@ pub enum BobState {
change_address: bitcoin::Address,
},
SwapSetupCompleted(State2),
BtcLocked(State3),
BtcLocked {
state3: State3,
monero_wallet_restore_blockheight: BlockHeight,
},
XmrLockProofReceived {
state: State3,
lock_transfer_proof: TransferProof,
@ -54,7 +57,7 @@ impl fmt::Display for BobState {
match self {
BobState::Started { .. } => write!(f, "quote has been requested"),
BobState::SwapSetupCompleted(..) => write!(f, "execution setup done"),
BobState::BtcLocked(..) => write!(f, "btc is locked"),
BobState::BtcLocked { .. } => write!(f, "btc is locked"),
BobState::XmrLockProofReceived { .. } => {
write!(f, "XMR lock transaction transfer proof received")
}

View file

@ -85,6 +85,17 @@ async fn next_state(
BobState::SwapSetupCompleted(state2)
}
BobState::SwapSetupCompleted(state2) => {
// Record the current monero wallet block height so we don't have to scan from
// block 0 once we create the redeem wallet.
// This has to be done **before** the Bitcoin is locked in order to ensure that
// if Bob goes offline the recorded wallet-height is correct.
// If we only record this later, it can happen that Bob publishes the Bitcoin
// transaction, goes offline, while offline Alice publishes Monero.
// If the Monero transaction gets confirmed before Bob comes online again then
// Bob would record a wallet-height that is past the lock transaction height,
// which can lead to the wallet not detect the transaction.
let monero_wallet_restore_blockheight = monero_wallet.block_height().await?;
// Alice and Bob have exchanged info
let (state3, tx_lock) = state2.lock_btc().await?;
let signed_tx = bitcoin_wallet
@ -93,11 +104,17 @@ async fn next_state(
.context("Failed to sign Bitcoin lock transaction")?;
let (..) = bitcoin_wallet.broadcast(signed_tx, "lock").await?;
BobState::BtcLocked(state3)
BobState::BtcLocked {
state3,
monero_wallet_restore_blockheight,
}
}
// Bob has locked Btc
// Watch for Alice to Lock Xmr or for cancel timelock to elapse
BobState::BtcLocked(state3) => {
BobState::BtcLocked {
state3,
monero_wallet_restore_blockheight,
} => {
let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await;
if let ExpiredTimelocks::None = state3.current_epoch(bitcoin_wallet).await? {
@ -105,10 +122,6 @@ async fn next_state(
let cancel_timelock_expires =
tx_lock_status.wait_until_confirmed_with(state3.cancel_timelock);
// Record the current monero wallet block height so we don't have to scan from
// block 0 once we create the redeem wallet.
let monero_wallet_restore_blockheight = monero_wallet.block_height().await?;
tracing::info!("Waiting for Alice to lock Monero");
select! {