Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions src/swaps/bitcoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ pub(crate) fn find_utxo(tx: &Transaction, script_pubkey: &Script) -> Option<(Out
}

/// Bitcoin v2 swap script helper.
// TODO: This should encode the network at global level.
#[derive(Debug, PartialEq, Clone)]
pub struct BtcSwapScript {
pub network: BitcoinChain,
pub swap_type: SwapType,
// pub swap_id: String,
pub side: Option<Side>,
Expand All @@ -66,6 +66,7 @@ impl BtcSwapScript {
pub fn submarine_from_swap_resp(
create_swap_response: &CreateSubmarineResponse,
our_pubkey: PublicKey,
network: BitcoinChain,
) -> Result<Self, Error> {
let claim_script = ScriptBuf::from_hex(&create_swap_response.swap_tree.claim_leaf.output)?;
let refund_script =
Expand Down Expand Up @@ -116,8 +117,8 @@ impl BtcSwapScript {
let funding_addrs = Address::from_str(&create_swap_response.address)?.assume_checked();

Ok(BtcSwapScript {
network,
swap_type: SwapType::Submarine,
// swap_id: create_swap_response.id.clone(),
side: None,
funding_addrs: Some(funding_addrs),
hashlock,
Expand Down Expand Up @@ -147,6 +148,7 @@ impl BtcSwapScript {
pub fn reverse_from_swap_resp(
reverse_response: &CreateReverseResponse,
our_pubkey: PublicKey,
network: BitcoinChain,
) -> Result<Self, Error> {
let claim_script = ScriptBuf::from_hex(&reverse_response.swap_tree.claim_leaf.output)?;
let refund_script = ScriptBuf::from_hex(&reverse_response.swap_tree.refund_leaf.output)?;
Expand Down Expand Up @@ -196,8 +198,8 @@ impl BtcSwapScript {
let funding_addrs = Address::from_str(&reverse_response.lockup_address)?.assume_checked();

Ok(BtcSwapScript {
network,
swap_type: SwapType::ReverseSubmarine,
// swap_id: reverse_response.id.clone(),
side: None,
funding_addrs: Some(funding_addrs),
hashlock,
Expand All @@ -212,6 +214,7 @@ impl BtcSwapScript {
side: Side,
chain_swap_details: ChainSwapDetails,
our_pubkey: PublicKey,
network: BitcoinChain,
) -> Result<Self, Error> {
let claim_script = ScriptBuf::from_hex(&chain_swap_details.swap_tree.claim_leaf.output)?;
let refund_script = ScriptBuf::from_hex(&chain_swap_details.swap_tree.refund_leaf.output)?;
Expand Down Expand Up @@ -266,8 +269,8 @@ impl BtcSwapScript {
};

Ok(BtcSwapScript {
network,
swap_type: SwapType::Chain,
// swap_id: reverse_response.id.clone(),
side: Some(side),
funding_addrs: Some(funding_addrs),
hashlock,
Expand Down Expand Up @@ -364,7 +367,7 @@ impl BtcSwapScript {
if lockup_xonly_pubkey != claim_key.to_x_only_public_key() {
return Err(Error::Protocol(format!(
"Taproot construction Failed. Lockup Pubkey: {lockup_xonly_pubkey}, Claim Pubkey {claim_key}"
)));
)))
}

log::info!("Taproot creation and verification success!");
Expand All @@ -374,11 +377,11 @@ impl BtcSwapScript {
}

/// Get taproot address for the swap script.
pub fn to_address(&self, network: BitcoinChain) -> Result<Address, Error> {
pub fn to_address(&self) -> Result<Address, Error> {
let spend_info = self.taproot_spendinfo()?;
let output_key = spend_info.output_key();

let network = match network {
let network = match self.network {
BitcoinChain::Bitcoin => Network::Bitcoin,
BitcoinChain::BitcoinRegtest => Network::Regtest,
BitcoinChain::BitcoinTestnet => Network::Testnet,
Expand All @@ -387,8 +390,8 @@ impl BtcSwapScript {
Ok(Address::p2tr_tweaked(output_key, network))
}

pub fn validate_address(&self, chain: BitcoinChain, address: String) -> Result<(), Error> {
let to_address = self.to_address(chain)?;
pub fn validate_address(&self, address: String) -> Result<(), Error> {
let to_address = self.to_address()?;
if to_address.to_string() == address {
Ok(())
} else {
Expand All @@ -402,7 +405,7 @@ impl BtcSwapScript {
bitcoin_client: &BC,
) -> Result<(u64, i64), Error> {
bitcoin_client
.get_address_balance(&self.to_address(bitcoin_client.network())?)
.get_address_balance(&self.to_address()?)
.await
}

Expand All @@ -412,7 +415,7 @@ impl BtcSwapScript {
bitcoin_client: &BC,
) -> Result<Vec<(OutPoint, TxOut)>, Error> {
bitcoin_client
.get_address_utxos(&self.to_address(bitcoin_client.network())?)
.get_address_utxos(&self.to_address()?)
.await
}

Expand Down Expand Up @@ -450,7 +453,7 @@ impl BtcSwapScript {
tx: &Transaction,
network: BitcoinChain,
) -> Result<Option<(OutPoint, TxOut)>, Error> {
let address = self.to_address(network)?;
let address = self.to_address()?;
Ok(find_utxo(tx, &address.script_pubkey()))
}

Expand Down
24 changes: 12 additions & 12 deletions src/swaps/boltz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -906,19 +906,19 @@ impl CreateSubmarineResponse {

match chain {
Chain::Bitcoin(bitcoin_chain) => {
let boltz_sub_script = BtcSwapScript::submarine_from_swap_resp(self, *our_pubkey)?;
boltz_sub_script.validate_address(bitcoin_chain, self.address.clone())
let boltz_sub_script = BtcSwapScript::submarine_from_swap_resp(self, *our_pubkey, bitcoin_chain)?;
boltz_sub_script.validate_address(self.address.clone())
}
Chain::Liquid(liquid_chain) => {
let boltz_sub_script = LBtcSwapScript::submarine_from_swap_resp(self, *our_pubkey)?;
let boltz_sub_script = LBtcSwapScript::submarine_from_swap_resp(self, *our_pubkey, liquid_chain)?;
if boltz_sub_script.hashlock != preimage.hash160 {
return Err(Error::Protocol(format!(
"Hash160 mismatch: {},{}",
boltz_sub_script.hashlock, preimage.hash160
)));
}

boltz_sub_script.validate_address(liquid_chain, self.address.clone())
boltz_sub_script.validate_address(self.address.clone())
}
}
}
Expand Down Expand Up @@ -1228,12 +1228,12 @@ impl CreateReverseResponse {

match chain {
Chain::Bitcoin(bitcoin_chain) => {
let boltz_rev_script = BtcSwapScript::reverse_from_swap_resp(self, *our_pubkey)?;
boltz_rev_script.validate_address(bitcoin_chain, self.lockup_address.clone())
let boltz_rev_script = BtcSwapScript::reverse_from_swap_resp(self, *our_pubkey, bitcoin_chain)?;
boltz_rev_script.validate_address(self.lockup_address.clone())
}
Chain::Liquid(liquid_chain) => {
let boltz_rev_script = LBtcSwapScript::reverse_from_swap_resp(self, *our_pubkey)?;
boltz_rev_script.validate_address(liquid_chain, self.lockup_address.clone())
let boltz_rev_script = LBtcSwapScript::reverse_from_swap_resp(self, *our_pubkey, liquid_chain)?;
boltz_rev_script.validate_address(self.lockup_address.clone())
}
}
}
Expand Down Expand Up @@ -1320,13 +1320,13 @@ impl CreateChainResponse {
match chain {
Chain::Bitcoin(bitcoin_chain) => {
let boltz_chain_script =
BtcSwapScript::chain_from_swap_resp(side, details.clone(), *our_pubkey)?;
boltz_chain_script.validate_address(bitcoin_chain, details.lockup_address.clone())
BtcSwapScript::chain_from_swap_resp(side, details.clone(), *our_pubkey, bitcoin_chain)?;
boltz_chain_script.validate_address(details.lockup_address.clone())
}
Chain::Liquid(liquid_chain) => {
let boltz_chain_script =
LBtcSwapScript::chain_from_swap_resp(side, details.clone(), *our_pubkey)?;
boltz_chain_script.validate_address(liquid_chain, details.lockup_address.clone())
LBtcSwapScript::chain_from_swap_resp(side, details.clone(), *our_pubkey, liquid_chain)?;
boltz_chain_script.validate_address(details.lockup_address.clone())
}
}
}
Expand Down
54 changes: 40 additions & 14 deletions src/swaps/liquid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use bitcoin::{
Amount, Witness, XOnlyPublicKey,
};
use elements::{
confidential::{Asset, AssetBlindingFactor, ValueBlindingFactor},
confidential::{Asset, AssetBlindingFactor, Value, ValueBlindingFactor},
hex::FromHex,
secp256k1_zkp::{Secp256k1, SecretKey},
sighash::{Prevouts, SighashCache},
Expand Down Expand Up @@ -60,19 +60,39 @@ pub(crate) fn unblind_utxo(
blinding_key: SecretKey,
) -> Result<TxOutSecrets, Error> {
let secp = Secp256k1::new();
let secrets = utxo.unblind(&secp, blinding_key)?;
if secrets.asset != network.bitcoin() {
return Err(Error::Protocol(format!(
"Asset is not bitcoin: {}",
secrets.asset
)));

match (utxo.asset, utxo.value) {
(Asset::Explicit(asset_id), Value::Explicit(amount)) => {
if asset_id != network.bitcoin() {
return Err(Error::Protocol(format!(
"Asset is not bitcoin: {}",
asset_id
)));
}
Ok(TxOutSecrets {
asset: asset_id,
asset_bf: AssetBlindingFactor::zero(),
value: amount,
value_bf: ValueBlindingFactor::zero(),
})
}
_ => {
let secrets = utxo.unblind(&secp, blinding_key)?;
if secrets.asset != network.bitcoin() {
return Err(Error::Protocol(format!(
"Asset is not bitcoin: {}",
secrets.asset
)));
}
Ok(secrets)
}
}
Ok(secrets)
}

/// Liquid v2 swap script helper.
#[derive(Debug, Clone, PartialEq)]
pub struct LBtcSwapScript {
pub network: LiquidChain,
pub swap_type: SwapType,
pub side: Option<Side>,
pub funding_addrs: Option<Address>,
Expand All @@ -88,6 +108,7 @@ impl LBtcSwapScript {
pub fn submarine_from_swap_resp(
create_swap_response: &CreateSubmarineResponse,
our_pubkey: PublicKey,
network: LiquidChain,
) -> Result<Self, Error> {
let claim_script = Script::from_hex(&create_swap_response.swap_tree.claim_leaf.output)?;
let refund_script = Script::from_hex(&create_swap_response.swap_tree.refund_leaf.output)?;
Expand Down Expand Up @@ -144,6 +165,7 @@ impl LBtcSwapScript {
let blinding_key = ZKKeyPair::from_seckey_str(&Secp256k1::new(), blinding_str)?;

Ok(Self {
network,
swap_type: SwapType::Submarine,
side: None,
funding_addrs: Some(funding_addrs),
Expand All @@ -159,6 +181,7 @@ impl LBtcSwapScript {
pub fn reverse_from_swap_resp(
reverse_response: &CreateReverseResponse,
our_pubkey: PublicKey,
network: LiquidChain,
) -> Result<Self, Error> {
let claim_script = Script::from_hex(&reverse_response.swap_tree.claim_leaf.output)?;
let refund_script = Script::from_hex(&reverse_response.swap_tree.refund_leaf.output)?;
Expand Down Expand Up @@ -215,6 +238,7 @@ impl LBtcSwapScript {
let blinding_key = ZKKeyPair::from_seckey_str(&Secp256k1::new(), blinding_str)?;

Ok(Self {
network,
swap_type: SwapType::ReverseSubmarine,
side: None,
funding_addrs: Some(funding_addrs),
Expand All @@ -231,6 +255,7 @@ impl LBtcSwapScript {
side: Side,
chain_swap_details: ChainSwapDetails,
our_pubkey: PublicKey,
network: LiquidChain,
) -> Result<Self, Error> {
let claim_script = Script::from_hex(&chain_swap_details.swap_tree.claim_leaf.output)?;
let refund_script = Script::from_hex(&chain_swap_details.swap_tree.refund_leaf.output)?;
Expand Down Expand Up @@ -292,6 +317,7 @@ impl LBtcSwapScript {
let blinding_key = ZKKeyPair::from_seckey_str(&Secp256k1::new(), blinding_str)?;

Ok(Self {
network,
swap_type: SwapType::Chain,
side: Some(side),
funding_addrs: Some(funding_addrs),
Expand Down Expand Up @@ -406,20 +432,20 @@ impl LBtcSwapScript {

/// Get taproot address for the swap script.
/// Always returns a confidential address
pub fn to_address(&self, network: LiquidChain) -> Result<EAddress, Error> {
pub fn to_address(&self) -> Result<EAddress, Error> {
let taproot_spend_info = self.taproot_spendinfo()?;

Ok(EAddress::p2tr(
&Secp256k1::new(),
taproot_spend_info.internal_key(),
taproot_spend_info.merkle_root(),
Some(self.blinding_key.public_key()),
network.into(),
self.network.into(),
))
}

pub fn validate_address(&self, chain: LiquidChain, address: String) -> Result<(), Error> {
let to_address = self.to_address(chain)?;
pub fn validate_address(&self, address: String) -> Result<(), Error> {
let to_address = self.to_address()?;
if to_address.to_string() == address {
Ok(())
} else {
Expand All @@ -432,7 +458,7 @@ impl LBtcSwapScript {
&self,
liquid_client: &LC,
) -> Result<Option<(OutPoint, TxOut)>, Error> {
let address = self.to_address(liquid_client.network())?;
let address = self.to_address()?;
liquid_client.get_address_utxo(&address).await
}

Expand Down Expand Up @@ -467,7 +493,7 @@ impl LBtcSwapScript {
tx: &Transaction,
network: LiquidChain,
) -> Result<(OutPoint, TxOut), Error> {
let address = self.to_address(network)?;
let address = self.to_address()?;
find_utxo(tx, &address.script_pubkey()).ok_or(Error::Protocol(
"No Liquid UTXO detected for this script".to_string(),
))
Expand Down
Loading