mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-10-01 01:45:40 -04:00
Bitcoin transaction published state
This improves the error handling on the ASB. Once the Bitcoin redeem transaction is seen in mempool, the state machine cannot transition to a cancel scenario anymore because at that point the CLI will have redeemed the Monero. The additional state then waits for transaction finality.
This commit is contained in:
parent
db319d0a90
commit
01af9a5676
@ -45,6 +45,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
3. ASB is running in resume-only mode and does not accept incoming swap requests
|
3. ASB is running in resume-only mode and does not accept incoming swap requests
|
||||||
- An issue where the monero daemon port used by the `monero-wallet-rpc` could not be specified.
|
- An issue where the monero daemon port used by the `monero-wallet-rpc` could not be specified.
|
||||||
The CLI parameter `--monero-daemon-host` was changed to `--monero-daemon-address` where host and port have to be specified.
|
The CLI parameter `--monero-daemon-host` was changed to `--monero-daemon-address` where host and port have to be specified.
|
||||||
|
- An issue where an ASB redeem scenario can transition to a cancel and publish scenario that will fail.
|
||||||
|
This is a breaking change for the ASB, because it introduces a new state into the database.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -39,6 +39,9 @@ pub enum Alice {
|
|||||||
encrypted_signature: EncryptedSignature,
|
encrypted_signature: EncryptedSignature,
|
||||||
state3: alice::State3,
|
state3: alice::State3,
|
||||||
},
|
},
|
||||||
|
BtcRedeemTransactionPublished {
|
||||||
|
state3: alice::State3,
|
||||||
|
},
|
||||||
CancelTimelockExpired {
|
CancelTimelockExpired {
|
||||||
monero_wallet_restore_blockheight: BlockHeight,
|
monero_wallet_restore_blockheight: BlockHeight,
|
||||||
transfer_proof: TransferProof,
|
transfer_proof: TransferProof,
|
||||||
@ -119,6 +122,11 @@ impl From<&AliceState> for Alice {
|
|||||||
state3: state3.as_ref().clone(),
|
state3: state3.as_ref().clone(),
|
||||||
encrypted_signature: *encrypted_signature.clone(),
|
encrypted_signature: *encrypted_signature.clone(),
|
||||||
},
|
},
|
||||||
|
AliceState::BtcRedeemTransactionPublished { state3 } => {
|
||||||
|
Alice::BtcRedeemTransactionPublished {
|
||||||
|
state3: state3.as_ref().clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
AliceState::BtcRedeemed => Alice::Done(AliceEndState::BtcRedeemed),
|
AliceState::BtcRedeemed => Alice::Done(AliceEndState::BtcRedeemed),
|
||||||
AliceState::BtcCancelled {
|
AliceState::BtcCancelled {
|
||||||
monero_wallet_restore_blockheight,
|
monero_wallet_restore_blockheight,
|
||||||
@ -212,6 +220,11 @@ impl From<Alice> for AliceState {
|
|||||||
state3: Box::new(state),
|
state3: Box::new(state),
|
||||||
encrypted_signature: Box::new(encrypted_signature),
|
encrypted_signature: Box::new(encrypted_signature),
|
||||||
},
|
},
|
||||||
|
Alice::BtcRedeemTransactionPublished { state3 } => {
|
||||||
|
AliceState::BtcRedeemTransactionPublished {
|
||||||
|
state3: Box::new(state3),
|
||||||
|
}
|
||||||
|
}
|
||||||
Alice::CancelTimelockExpired {
|
Alice::CancelTimelockExpired {
|
||||||
monero_wallet_restore_blockheight,
|
monero_wallet_restore_blockheight,
|
||||||
transfer_proof,
|
transfer_proof,
|
||||||
@ -271,12 +284,15 @@ impl Display for Alice {
|
|||||||
Alice::XmrLockTransferProofSent { .. } => {
|
Alice::XmrLockTransferProofSent { .. } => {
|
||||||
f.write_str("Monero lock transfer proof sent")
|
f.write_str("Monero lock transfer proof sent")
|
||||||
}
|
}
|
||||||
|
Alice::EncSigLearned { .. } => f.write_str("Encrypted signature learned"),
|
||||||
|
Alice::BtcRedeemTransactionPublished { .. } => {
|
||||||
|
f.write_str("Bitcoin redeem transaction published")
|
||||||
|
}
|
||||||
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"),
|
||||||
Alice::BtcRefunded { .. } => f.write_str("Monero refundable"),
|
Alice::BtcRefunded { .. } => f.write_str("Monero refundable"),
|
||||||
Alice::Done(end_state) => write!(f, "Done: {}", end_state),
|
Alice::Done(end_state) => write!(f, "Done: {}", end_state),
|
||||||
Alice::EncSigLearned { .. } => f.write_str("Encrypted signature learned"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,9 @@ pub async fn cancel(
|
|||||||
(monero_wallet_restore_blockheight, transfer_proof, state3)
|
(monero_wallet_restore_blockheight, transfer_proof, state3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The redeem transaction was already published, it is not safe to cancel anymore
|
||||||
|
AliceState::BtcRedeemTransactionPublished { .. } => bail!(" The redeem transaction was already published, it is not safe to cancel anymore"),
|
||||||
|
|
||||||
// The cancel tx was already published, but Alice not yet in final state
|
// The cancel tx was already published, but Alice not yet in final state
|
||||||
AliceState::BtcCancelled { .. }
|
AliceState::BtcCancelled { .. }
|
||||||
| AliceState::BtcRefunded { .. }
|
| AliceState::BtcRefunded { .. }
|
||||||
@ -43,7 +46,7 @@ pub async fn cancel(
|
|||||||
| AliceState::BtcRedeemed
|
| AliceState::BtcRedeemed
|
||||||
| AliceState::XmrRefunded
|
| AliceState::XmrRefunded
|
||||||
| AliceState::BtcPunished
|
| AliceState::BtcPunished
|
||||||
| AliceState::SafelyAborted => bail!("Cannot cancel swap {} because it is in state {} which is not cancelable", swap_id, state),
|
| AliceState::SafelyAborted => bail!("Swap is is in state {} which is not cancelable", state),
|
||||||
};
|
};
|
||||||
|
|
||||||
tracing::info!(%swap_id, "Trying to manually cancel swap");
|
tracing::info!(%swap_id, "Trying to manually cancel swap");
|
||||||
|
@ -50,7 +50,8 @@ pub async fn punish(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the swap was refunded it cannot be punished
|
// If the swap was refunded it cannot be punished
|
||||||
AliceState::BtcRefunded {..}
|
AliceState::BtcRedeemTransactionPublished { .. }
|
||||||
|
| AliceState::BtcRefunded {..}
|
||||||
// Alice already in final state
|
// Alice already in final state
|
||||||
| AliceState::BtcRedeemed
|
| AliceState::BtcRedeemed
|
||||||
| AliceState::XmrRefunded
|
| AliceState::XmrRefunded
|
||||||
|
@ -50,19 +50,39 @@ pub async fn redeem(
|
|||||||
let redeem_tx = state3.signed_redeem_transaction(*encrypted_signature)?;
|
let redeem_tx = state3.signed_redeem_transaction(*encrypted_signature)?;
|
||||||
let (txid, subscription) = bitcoin_wallet.broadcast(redeem_tx, "redeem").await?;
|
let (txid, subscription) = bitcoin_wallet.broadcast(redeem_tx, "redeem").await?;
|
||||||
|
|
||||||
|
subscription.wait_until_seen().await?;
|
||||||
|
|
||||||
|
let state = AliceState::BtcRedeemTransactionPublished { state3 };
|
||||||
|
let db_state = (&state).into();
|
||||||
|
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
||||||
|
.await?;
|
||||||
|
|
||||||
if let Finality::Await = finality {
|
if let Finality::Await = finality {
|
||||||
subscription.wait_until_final().await?;
|
subscription.wait_until_final().await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = AliceState::BtcRedeemed;
|
let state = AliceState::BtcRedeemed;
|
||||||
let db_state = (&state).into();
|
let db_state = (&state).into();
|
||||||
|
|
||||||
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok((txid, state))
|
Ok((txid, state))
|
||||||
}
|
}
|
||||||
|
AliceState::BtcRedeemTransactionPublished { state3 } => {
|
||||||
|
let subscription = bitcoin_wallet.subscribe_to(state3.tx_redeem()).await;
|
||||||
|
if let Finality::Await = finality {
|
||||||
|
subscription.wait_until_final().await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = AliceState::BtcRedeemed;
|
||||||
|
let db_state = (&state).into();
|
||||||
|
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let txid = state3.tx_redeem().txid();
|
||||||
|
|
||||||
|
Ok((txid, state))
|
||||||
|
}
|
||||||
AliceState::Started { .. }
|
AliceState::Started { .. }
|
||||||
| AliceState::BtcLocked { .. }
|
| AliceState::BtcLocked { .. }
|
||||||
| AliceState::XmrLockTransactionSent { .. }
|
| AliceState::XmrLockTransactionSent { .. }
|
||||||
|
@ -56,7 +56,8 @@ pub async fn refund(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Alice already in final state
|
// Alice already in final state
|
||||||
AliceState::BtcRedeemed
|
AliceState::BtcRedeemTransactionPublished { .. }
|
||||||
|
| AliceState::BtcRedeemed
|
||||||
| AliceState::XmrRefunded
|
| AliceState::XmrRefunded
|
||||||
| AliceState::BtcPunished
|
| AliceState::BtcPunished
|
||||||
| AliceState::SafelyAborted => bail!(Error::SwapNotRefundable(state)),
|
| AliceState::SafelyAborted => bail!(Error::SwapNotRefundable(state)),
|
||||||
|
@ -22,6 +22,7 @@ pub async fn safely_abort(swap_id: Uuid, db: Arc<Database>) -> Result<AliceState
|
|||||||
| AliceState::XmrLocked { .. }
|
| AliceState::XmrLocked { .. }
|
||||||
| AliceState::XmrLockTransferProofSent { .. }
|
| AliceState::XmrLockTransferProofSent { .. }
|
||||||
| AliceState::EncSigLearned { .. }
|
| AliceState::EncSigLearned { .. }
|
||||||
|
| AliceState::BtcRedeemTransactionPublished { .. }
|
||||||
| AliceState::CancelTimelockExpired { .. }
|
| AliceState::CancelTimelockExpired { .. }
|
||||||
| AliceState::BtcCancelled { .. }
|
| AliceState::BtcCancelled { .. }
|
||||||
| AliceState::BtcRefunded { .. }
|
| AliceState::BtcRefunded { .. }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::bitcoin::{
|
use crate::bitcoin::{
|
||||||
current_epoch, CancelTimelock, ExpiredTimelocks, PunishTimelock, Transaction, TxCancel,
|
current_epoch, CancelTimelock, ExpiredTimelocks, PunishTimelock, Transaction, TxCancel,
|
||||||
TxPunish, TxRefund, Txid,
|
TxPunish, TxRedeem, TxRefund, Txid,
|
||||||
};
|
};
|
||||||
use crate::env::Config;
|
use crate::env::Config;
|
||||||
use crate::monero::wallet::{TransferRequest, WatchRequest};
|
use crate::monero::wallet::{TransferRequest, WatchRequest};
|
||||||
@ -45,6 +45,9 @@ pub enum AliceState {
|
|||||||
encrypted_signature: Box<bitcoin::EncryptedSignature>,
|
encrypted_signature: Box<bitcoin::EncryptedSignature>,
|
||||||
state3: Box<State3>,
|
state3: Box<State3>,
|
||||||
},
|
},
|
||||||
|
BtcRedeemTransactionPublished {
|
||||||
|
state3: Box<State3>,
|
||||||
|
},
|
||||||
BtcRedeemed,
|
BtcRedeemed,
|
||||||
BtcCancelled {
|
BtcCancelled {
|
||||||
monero_wallet_restore_blockheight: BlockHeight,
|
monero_wallet_restore_blockheight: BlockHeight,
|
||||||
@ -83,6 +86,9 @@ impl fmt::Display for AliceState {
|
|||||||
write!(f, "xmr lock transfer proof sent")
|
write!(f, "xmr lock transfer proof sent")
|
||||||
}
|
}
|
||||||
AliceState::EncSigLearned { .. } => write!(f, "encrypted signature is learned"),
|
AliceState::EncSigLearned { .. } => write!(f, "encrypted signature is learned"),
|
||||||
|
AliceState::BtcRedeemTransactionPublished { .. } => {
|
||||||
|
write!(f, "bitcoin redeem transaction published")
|
||||||
|
}
|
||||||
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"),
|
||||||
AliceState::BtcRefunded { .. } => write!(f, "btc is refunded"),
|
AliceState::BtcRefunded { .. } => write!(f, "btc is refunded"),
|
||||||
@ -449,6 +455,10 @@ impl State3 {
|
|||||||
bitcoin::TxRefund::new(&self.tx_cancel(), &self.refund_address, self.tx_refund_fee)
|
bitcoin::TxRefund::new(&self.tx_cancel(), &self.refund_address, self.tx_refund_fee)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tx_redeem(&self) -> TxRedeem {
|
||||||
|
TxRedeem::new(&self.tx_lock, &self.redeem_address, self.tx_redeem_fee)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn extract_monero_private_key(
|
pub fn extract_monero_private_key(
|
||||||
&self,
|
&self,
|
||||||
published_refund_tx: bitcoin::Transaction,
|
published_refund_tx: bitcoin::Transaction,
|
||||||
|
@ -204,10 +204,10 @@ where
|
|||||||
let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await;
|
let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await;
|
||||||
match state3.signed_redeem_transaction(*encrypted_signature) {
|
match state3.signed_redeem_transaction(*encrypted_signature) {
|
||||||
Ok(tx) => match bitcoin_wallet.broadcast(tx, "redeem").await {
|
Ok(tx) => match bitcoin_wallet.broadcast(tx, "redeem").await {
|
||||||
Ok((_, subscription)) => match subscription.wait_until_final().await {
|
Ok((_, subscription)) => match subscription.wait_until_seen().await {
|
||||||
Ok(_) => AliceState::BtcRedeemed,
|
Ok(_) => AliceState::BtcRedeemTransactionPublished { state3 },
|
||||||
Err(e) => {
|
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)
|
bail!("Waiting for Bitcoin redeem transaction to be in mempool failed with {}! The redeem transaction was published, but it is not ensured that the transaction was included! You're screwed.", e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
@ -247,6 +247,16 @@ where
|
|||||||
state3,
|
state3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
AliceState::BtcRedeemTransactionPublished { state3 } => {
|
||||||
|
let subscription = bitcoin_wallet.subscribe_to(state3.tx_redeem()).await;
|
||||||
|
|
||||||
|
match subscription.wait_until_final().await {
|
||||||
|
Ok(_) => AliceState::BtcRedeemed,
|
||||||
|
Err(e) => {
|
||||||
|
bail!("The Bitcoin redeem transaction was seen in mempool, but waiting for finality timed out with {}. Manual investigation might be needed to ensure that the transaction was included.", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
AliceState::CancelTimelockExpired {
|
AliceState::CancelTimelockExpired {
|
||||||
monero_wallet_restore_blockheight,
|
monero_wallet_restore_blockheight,
|
||||||
transfer_proof,
|
transfer_proof,
|
||||||
|
Loading…
Reference in New Issue
Block a user