mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-12-16 17:14:13 -05:00
refactor(alice): concretize enc sig publication requirement (#664)
* refactor(alice): concretize enc sig publication requirement * add changelog entry
This commit is contained in:
parent
d9438c5913
commit
0fec5d556d
5 changed files with 26 additions and 19 deletions
|
|
@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
- ASB: Fix an issue where we would not redeem the Bitcoin and force a refund even though it was still possible to do so.
|
||||||
|
|
||||||
## [3.2.7] - 2025-10-28
|
## [3.2.7] - 2025-10-28
|
||||||
|
|
||||||
## [3.2.6] - 2025-10-27
|
## [3.2.6] - 2025-10-27
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ tor-hsservice = { workspace = true }
|
||||||
tor-rtcompat = { workspace = true }
|
tor-rtcompat = { workspace = true }
|
||||||
|
|
||||||
# Async
|
# Async
|
||||||
|
backoff = { workspace = true, features = ["tokio"] }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["rt-multi-thread", "time", "macros", "sync", "process", "fs", "net", "io-util"] }
|
tokio = { workspace = true, features = ["rt-multi-thread", "time", "macros", "sync", "process", "fs", "net", "io-util"] }
|
||||||
backoff = { workspace = true, features = ["tokio"] }
|
|
||||||
|
|
||||||
# Libp2p
|
# Libp2p
|
||||||
libp2p = { workspace = true, features = ["rendezvous", "tcp", "yamux", "dns", "noise", "ping", "websocket", "tokio", "macros"] }
|
libp2p = { workspace = true, features = ["rendezvous", "tcp", "yamux", "dns", "noise", "ping", "websocket", "tokio", "macros"] }
|
||||||
|
|
@ -28,6 +28,6 @@ tracing-subscriber = { workspace = true, default-features = false, features = ["
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
|
||||||
# Other
|
# Other
|
||||||
swap-env = { path = "../swap-env" }
|
|
||||||
atty = "0.2"
|
atty = "0.2"
|
||||||
structopt = { version = "0.3", default-features = false }
|
structopt = { version = "0.3", default-features = false }
|
||||||
|
swap-env = { path = "../swap-env" }
|
||||||
|
|
|
||||||
|
|
@ -45,10 +45,6 @@ impl CancelTimelock {
|
||||||
pub const fn new(number_of_blocks: u32) -> Self {
|
pub const fn new(number_of_blocks: u32) -> Self {
|
||||||
Self(number_of_blocks)
|
Self(number_of_blocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn half(&self) -> CancelTimelock {
|
|
||||||
Self(self.0 / 2)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add<CancelTimelock> for BlockHeight {
|
impl Add<CancelTimelock> for BlockHeight {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@ pub struct Config {
|
||||||
pub bitcoin_lock_mempool_timeout: Duration,
|
pub bitcoin_lock_mempool_timeout: Duration,
|
||||||
pub bitcoin_lock_confirmed_timeout: Duration,
|
pub bitcoin_lock_confirmed_timeout: Duration,
|
||||||
pub bitcoin_finality_confirmations: u32,
|
pub bitcoin_finality_confirmations: u32,
|
||||||
|
/// The upper bound for the number of blocks that will be mined before our
|
||||||
|
/// Bitcoin transaction is included in a block
|
||||||
|
pub bitcoin_blocks_till_confirmed_upper_bound_assumption: u32,
|
||||||
pub bitcoin_avg_block_time: Duration,
|
pub bitcoin_avg_block_time: Duration,
|
||||||
pub bitcoin_cancel_timelock: u32,
|
pub bitcoin_cancel_timelock: u32,
|
||||||
pub bitcoin_punish_timelock: u32,
|
pub bitcoin_punish_timelock: u32,
|
||||||
|
|
@ -52,6 +55,9 @@ impl GetConfig for Mainnet {
|
||||||
bitcoin_lock_mempool_timeout: 10.std_minutes(),
|
bitcoin_lock_mempool_timeout: 10.std_minutes(),
|
||||||
bitcoin_lock_confirmed_timeout: 2.std_hours(),
|
bitcoin_lock_confirmed_timeout: 2.std_hours(),
|
||||||
bitcoin_finality_confirmations: 1,
|
bitcoin_finality_confirmations: 1,
|
||||||
|
// We assume that a transaction that was constructed to be confirmed within one block
|
||||||
|
// will be confirmed within at most 6 blocks
|
||||||
|
bitcoin_blocks_till_confirmed_upper_bound_assumption: 6,
|
||||||
bitcoin_avg_block_time: 10.std_minutes(),
|
bitcoin_avg_block_time: 10.std_minutes(),
|
||||||
bitcoin_cancel_timelock: 72,
|
bitcoin_cancel_timelock: 72,
|
||||||
bitcoin_punish_timelock: 144,
|
bitcoin_punish_timelock: 144,
|
||||||
|
|
@ -73,6 +79,7 @@ impl GetConfig for Testnet {
|
||||||
bitcoin_lock_mempool_timeout: 10.std_minutes(),
|
bitcoin_lock_mempool_timeout: 10.std_minutes(),
|
||||||
bitcoin_lock_confirmed_timeout: 1.std_hours(),
|
bitcoin_lock_confirmed_timeout: 1.std_hours(),
|
||||||
bitcoin_finality_confirmations: 1,
|
bitcoin_finality_confirmations: 1,
|
||||||
|
bitcoin_blocks_till_confirmed_upper_bound_assumption: 6,
|
||||||
bitcoin_avg_block_time: 10.std_minutes(),
|
bitcoin_avg_block_time: 10.std_minutes(),
|
||||||
bitcoin_cancel_timelock: 12 * 3,
|
bitcoin_cancel_timelock: 12 * 3,
|
||||||
bitcoin_punish_timelock: 24 * 3,
|
bitcoin_punish_timelock: 24 * 3,
|
||||||
|
|
@ -92,6 +99,7 @@ impl GetConfig for Regtest {
|
||||||
bitcoin_lock_mempool_timeout: 30.std_seconds(),
|
bitcoin_lock_mempool_timeout: 30.std_seconds(),
|
||||||
bitcoin_lock_confirmed_timeout: 5.std_minutes(),
|
bitcoin_lock_confirmed_timeout: 5.std_minutes(),
|
||||||
bitcoin_finality_confirmations: 1,
|
bitcoin_finality_confirmations: 1,
|
||||||
|
bitcoin_blocks_till_confirmed_upper_bound_assumption: 6,
|
||||||
bitcoin_avg_block_time: 5.std_seconds(),
|
bitcoin_avg_block_time: 5.std_seconds(),
|
||||||
bitcoin_cancel_timelock: 100,
|
bitcoin_cancel_timelock: 100,
|
||||||
bitcoin_punish_timelock: 50,
|
bitcoin_punish_timelock: 50,
|
||||||
|
|
|
||||||
|
|
@ -451,26 +451,25 @@ where
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
match backoff::future::retry_notify(backoff.clone(), || async {
|
match backoff::future::retry_notify(backoff.clone(), || async {
|
||||||
// We can only redeem the Bitcoin if we are sure that our Bitcoin redeem transaction
|
|
||||||
// will be confirmed before the timelock expires
|
|
||||||
//
|
|
||||||
// We only publish the transaction if we have more than half of the timelock left.
|
|
||||||
let tx_lock_status = bitcoin_wallet
|
let tx_lock_status = bitcoin_wallet
|
||||||
.status_of_script(&state3.tx_lock.clone())
|
.status_of_script(&state3.tx_lock.clone())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if tx_lock_status.is_confirmed_with(state3.cancel_timelock.half()) {
|
// If the cancel timelock is expired, it it not safe to publish the Bitcoin redeem transaction anymore
|
||||||
|
//
|
||||||
|
// TODO: In practice this should be redundant because the logic above will trigger for a superset of the cases where this is true
|
||||||
|
if tx_lock_status.is_confirmed_with(state3.cancel_timelock) {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the cancel timelock is expired, it it not safe to publish the Bitcoin redeem transaction anymore
|
// We can only redeem the Bitcoin if we are fairly sure that our Bitcoin redeem transaction
|
||||||
|
// will be confirmed before the cancel timelock expires
|
||||||
//
|
//
|
||||||
// TODO: This should never be true if the logic above is not true but we do it anyway to be safe
|
// We make an assumption that it will take at most `env_config.bitcoin_blocks_till_confirmed_upper_bound_assumption` blocks
|
||||||
// TODO: Remove this
|
// until our transaction is included in a block. If this assumption is not satisfied, we will not publish the transaction.
|
||||||
if !matches!(
|
//
|
||||||
state3.expired_timelocks(&*bitcoin_wallet).await?,
|
// We will instead wait for the cancel timelock to expire and then refund.
|
||||||
ExpiredTimelocks::None { .. }
|
if tx_lock_status.blocks_left_until(state3.cancel_timelock) < env_config.bitcoin_blocks_till_confirmed_upper_bound_assumption {
|
||||||
) {
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -494,6 +493,7 @@ where
|
||||||
// We wait until we see the transaction in the mempool before transitioning to the next state
|
// We wait until we see the transaction in the mempool before transitioning to the next state
|
||||||
Some((txid, subscription)) => match subscription.wait_until_seen().await {
|
Some((txid, subscription)) => match subscription.wait_until_seen().await {
|
||||||
Ok(_) => AliceState::BtcRedeemTransactionPublished { state3, transfer_proof },
|
Ok(_) => AliceState::BtcRedeemTransactionPublished { state3, transfer_proof },
|
||||||
|
// TODO: No need to bail here, we should just retry?
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// We extract the txid and the hex representation of the transaction
|
// We extract the txid and the hex representation of the transaction
|
||||||
// this'll allow the user to manually re-publish the transaction
|
// this'll allow the user to manually re-publish the transaction
|
||||||
|
|
@ -503,7 +503,8 @@ where
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// More than half of the cancel timelock expired before we could publish the redeem transaction
|
// It is not safe to publish the Bitcoin redeem transaction anymore
|
||||||
|
// We wait for the cancel timelock to expire and then refund
|
||||||
None => {
|
None => {
|
||||||
tracing::error!("We were unable to publish the Bitcoin redeem transaction before the timelock expired.");
|
tracing::error!("We were unable to publish the Bitcoin redeem transaction before the timelock expired.");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue