fix: incoming Chain swap refund (#522)

This commit is contained in:
yse
2024-10-07 15:53:09 +02:00
committed by GitHub
parent 7e193aafac
commit 950d4243e6
5 changed files with 92 additions and 66 deletions

View File

@@ -179,7 +179,6 @@ impl BitcoinChainService for HybridBitcoinChainService {
}
async fn get_script_utxos(&self, script: &Script) -> Result<Vec<Utxo>> {
let script_pubkey = script.to_p2sh();
let utxos = self
.client
.script_list_unspent(script)?
@@ -195,7 +194,7 @@ impl BitcoinChainService for HybridBitcoinChainService {
OutPoint::new(*tx_hash, *tx_pos as u32),
TxOut {
value: Amount::from_sat(*value),
script_pubkey: script_pubkey.clone(),
script_pubkey: script.into(),
},
))
},

View File

@@ -129,12 +129,17 @@ impl LiquidChainService for HybridLiquidChainService {
}
async fn get_transaction_hex(&self, txid: &Txid) -> Result<Option<Transaction>> {
let url = format!("{}/tx/{}/hex", LIQUID_ESPLORA_URL, txid.to_hex());
let response = get_with_retry(&url, 3).await?;
Ok(match response.status() {
StatusCode::OK => Some(utils::deserialize_tx_hex(&response.text().await?)?),
_ => None,
})
match self.network {
LiquidNetwork::Mainnet => {
let url = format!("{}/tx/{}/hex", LIQUID_ESPLORA_URL, txid.to_hex());
let response = get_with_retry(&url, 3).await?;
Ok(match response.status() {
StatusCode::OK => Some(utils::deserialize_tx_hex(&response.text().await?)?),
_ => None,
})
}
LiquidNetwork::Testnet => Ok(self.get_transactions(&[*txid]).await?.first().cloned()),
}
}
async fn get_transactions(&self, txids: &[Txid]) -> Result<Vec<Transaction>> {

View File

@@ -4,7 +4,7 @@ use boltz_client::{
bitcoin::{address::Address, Transaction},
boltz::SwapTxKind,
util::secrets::Preimage,
BtcSwapScript, BtcSwapTx, Keypair,
BtcSwapTx,
};
use crate::{
@@ -54,17 +54,21 @@ impl BoltzSwapper {
Ok(refund_wrapper)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn new_btc_refund_tx(
&self,
swap_id: String,
swap_script: BtcSwapScript,
swap: &ChainSwap,
refund_address: &str,
refund_keypair: &Keypair,
utxos: Vec<Utxo>,
broadcast_fee_rate_sat_per_vb: f64,
is_cooperative: bool,
) -> Result<Transaction, SdkError> {
ensure_sdk!(
swap.direction == Direction::Incoming,
SdkError::Generic {
err: "Cannot create BTC refund tx for outgoing Chain swaps.".to_string()
}
);
let address = Address::from_str(refund_address).map_err(|err| SdkError::Generic {
err: format!("Could not parse address: {err:?}"),
})?;
@@ -83,6 +87,7 @@ impl BoltzSwapper {
err: "No UTXO found".to_string(),
})?;
let swap_script = swap.get_lockup_swap_script()?.as_bitcoin_script()?;
let refund_tx = BtcSwapTx {
kind: SwapTxKind::Refund,
swap_script,
@@ -90,15 +95,17 @@ impl BoltzSwapper {
utxo,
};
let refund_tx_size = refund_tx.size(refund_keypair, &Preimage::new())?;
let refund_keypair = swap.get_refund_keypair()?;
let preimage = Preimage::from_str(&swap.preimage)?;
let refund_tx_size = refund_tx.size(&refund_keypair, &preimage)?;
let broadcast_fees_sat = (refund_tx_size as f64 * broadcast_fee_rate_sat_per_vb) as u64;
let cooperative = match is_cooperative {
true => self.get_cooperative_details(swap_id, None, None),
true => self.get_cooperative_details(swap.id.clone(), None, None),
false => None,
};
let signed_tx = refund_tx.sign_refund(refund_keypair, broadcast_fees_sat, cooperative)?;
let signed_tx = refund_tx.sign_refund(&refund_keypair, broadcast_fees_sat, cooperative)?;
Ok(signed_tx)
}

View File

@@ -4,13 +4,17 @@ use boltz_client::{
boltz::SwapTxKind,
elements::Transaction,
util::{liquid_genesis_hash, secrets::Preimage},
Amount, Bolt11Invoice, ElementsAddress as Address, Keypair, LBtcSwapScript, LBtcSwapTx,
Amount, Bolt11Invoice, ElementsAddress as Address, LBtcSwapTx,
};
use log::info;
use crate::{
ensure_sdk,
error::{PaymentError, SdkError},
prelude::{ChainSwap, Direction, ReceiveSwap, Swap, Utxo, LOWBALL_FEE_RATE_SAT_PER_VBYTE},
prelude::{
ChainSwap, Direction, LiquidNetwork, ReceiveSwap, Swap, Utxo,
LOWBALL_FEE_RATE_SAT_PER_VBYTE, STANDARD_FEE_RATE_SAT_PER_VBYTE,
},
};
use super::BoltzSwapper;
@@ -92,8 +96,10 @@ impl BoltzSwapper {
}
fn calculate_refund_fees(&self, refund_tx_size: usize) -> u64 {
// Testnet not supports lowball as well, see https://blog.blockstream.com/elements-23-2-3-discounted-fees-for-confidential-transactions/
let fee_rate = LOWBALL_FEE_RATE_SAT_PER_VBYTE;
let fee_rate = match self.config.network {
LiquidNetwork::Mainnet => LOWBALL_FEE_RATE_SAT_PER_VBYTE,
LiquidNetwork::Testnet => STANDARD_FEE_RATE_SAT_PER_VBYTE,
};
(refund_tx_size as f64 * fee_rate).ceil() as u64
}
@@ -147,13 +153,39 @@ impl BoltzSwapper {
pub(crate) fn new_lbtc_refund_tx(
&self,
swap_id: String,
swap_script: LBtcSwapScript,
swap: &Swap,
refund_address: &str,
refund_keypair: &Keypair,
utxos: Vec<Utxo>,
is_cooperative: bool,
) -> Result<Transaction, SdkError> {
let (swap_script, refund_keypair, preimage) = match swap {
Swap::Chain(swap) => {
ensure_sdk!(
swap.direction == Direction::Outgoing,
SdkError::Generic {
err: "Cannot create LBTC refund tx for incoming Chain swaps".to_string()
}
);
(
swap.get_lockup_swap_script()?.as_liquid_script()?,
swap.get_refund_keypair()?,
Preimage::from_str(&swap.preimage)?,
)
}
Swap::Send(swap) => (
swap.get_swap_script()?,
swap.get_refund_keypair()?,
Preimage::new(),
),
Swap::Receive(_) => {
return Err(SdkError::Generic {
err: "Cannot create LBTC refund tx for Receive swaps.".to_string(),
});
}
};
let swap_id = swap.id();
let address = Address::from_str(refund_address).map_err(|err| SdkError::Generic {
err: format!("Could not parse address: {err:?}"),
})?;
@@ -176,16 +208,16 @@ impl BoltzSwapper {
genesis_hash,
};
let refund_tx_size = refund_tx.size(refund_keypair, &Preimage::new())?;
let refund_tx_size = refund_tx.size(&refund_keypair, &preimage)?;
let broadcast_fees_sat = self.calculate_refund_fees(refund_tx_size);
let cooperative = match is_cooperative {
true => self.get_cooperative_details(swap_id, None, None),
true => self.get_cooperative_details(swap_id.clone(), None, None),
false => None,
};
let signed_tx = refund_tx.sign_refund(
refund_keypair,
&refund_keypair,
Amount::from_sat(broadcast_fees_sat),
cooperative,
)?;

View File

@@ -329,60 +329,43 @@ impl Swapper for BoltzSwapper {
broadcast_fee_rate_sat_per_vb: Option<f64>,
is_cooperative: bool,
) -> Result<Transaction, PaymentError> {
let swap_id = swap.id();
let refund_address = &refund_address.to_string();
let tx = match &swap {
Swap::Chain(swap) => {
let swap_id = swap.id.clone();
let swap_script = swap.get_lockup_swap_script()?;
let refund_keypair = swap.get_refund_keypair()?;
match swap.direction {
Direction::Incoming => {
let Some(broadcast_fee_rate_sat_per_vb) = broadcast_fee_rate_sat_per_vb
else {
return Err(PaymentError::Generic {
let tx = match &swap {
Swap::Chain(chain_swap) => match chain_swap.direction {
Direction::Incoming => {
let Some(broadcast_fee_rate_sat_per_vb) = broadcast_fee_rate_sat_per_vb else {
return Err(PaymentError::Generic {
err: format!("No broadcast fee rate provided when refunding incoming Chain Swap {swap_id}")
});
};
};
Transaction::Bitcoin(self.new_btc_refund_tx(
swap_id,
swap_script.as_bitcoin_script()?,
refund_address,
&refund_keypair,
utxos,
broadcast_fee_rate_sat_per_vb,
is_cooperative,
)?)
}
Direction::Outgoing => Transaction::Liquid(self.new_lbtc_refund_tx(
swap_id,
swap_script.as_liquid_script()?,
Transaction::Bitcoin(self.new_btc_refund_tx(
chain_swap,
refund_address,
&refund_keypair,
utxos,
broadcast_fee_rate_sat_per_vb,
is_cooperative,
)?),
)?)
}
}
Swap::Send(swap) => {
let swap_script = swap.get_swap_script()?;
let refund_keypair = swap.get_refund_keypair()?;
Transaction::Liquid(self.new_lbtc_refund_tx(
swap.id.clone(),
swap_script,
Direction::Outgoing => Transaction::Liquid(self.new_lbtc_refund_tx(
&swap,
refund_address,
&refund_keypair,
utxos,
is_cooperative,
)?)
}
Swap::Receive(swap) => {
)?),
},
Swap::Send(_) => Transaction::Liquid(self.new_lbtc_refund_tx(
&swap,
refund_address,
utxos,
is_cooperative,
)?),
Swap::Receive(_) => {
return Err(PaymentError::Generic {
err: format!(
"Failed to create refund tx for Receive swap {}: invalid swap type",
swap.id
"Failed to create refund tx for Receive swap {swap_id}: invalid swap type",
),
});
}