From 0e9a0414472615e5aa7adbf4e40c72e54a3c23ea Mon Sep 17 00:00:00 2001 From: rajarshimaitra Date: Fri, 26 Apr 2024 18:33:34 +0530 Subject: [PATCH 1/9] Some fmt fixes --- src/lib.rs | 13 +++++++++++-- src/network/electrum.rs | 9 ++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 19d082e..8b9c284 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,8 +12,17 @@ pub mod swaps; /// utilities (key, preimage, error) pub mod util; -pub use bitcoin::{PublicKey,secp256k1::{Keypair, Secp256k1}, Address,blockdata::locktime::absolute::LockTime,hashes::hash160::Hash, Amount}; -pub use elements::{secp256k1_zkp::{Keypair as ZKKeyPair, Secp256k1 as ZKSecp256k1}, address::Address as ElementsAddress, locktime::LockTime as ElementsLockTime}; +pub use bitcoin::{ + blockdata::locktime::absolute::LockTime, + hashes::hash160::Hash, + secp256k1::{Keypair, Secp256k1}, + Address, Amount, PublicKey, +}; +pub use elements::{ + address::Address as ElementsAddress, + locktime::LockTime as ElementsLockTime, + secp256k1_zkp::{Keypair as ZKKeyPair, Secp256k1 as ZKSecp256k1}, +}; pub use lightning_invoice::Bolt11Invoice; pub use swaps::boltz::{SwapTxKind, SwapType}; diff --git a/src/network/electrum.rs b/src/network/electrum.rs index 55f49bb..03ca7d4 100644 --- a/src/network/electrum.rs +++ b/src/network/electrum.rs @@ -39,10 +39,13 @@ pub struct ElectrumConfig { } impl ElectrumConfig { - pub fn default(chain: Chain, regtest_url: Option) -> Result { - if (chain == Chain::LiquidRegtest || chain == Chain::BitcoinRegtest ) && regtest_url.is_none() { - return Err(Error::Electrum(electrum_client::Error::Message("Regtest requires using a custom url".to_string()))) + if (chain == Chain::LiquidRegtest || chain == Chain::BitcoinRegtest) + && regtest_url.is_none() + { + return Err(Error::Electrum(electrum_client::Error::Message( + "Regtest requires using a custom url".to_string(), + ))); } match chain { Chain::Bitcoin => Ok(ElectrumConfig::new( From bd2366e11f72d939c8108fca9c2d3385bd255061 Mon Sep 17 00:00:00 2001 From: rajarshimaitra Date: Fri, 26 Apr 2024 18:33:47 +0530 Subject: [PATCH 2/9] update boltz api --- src/swaps/boltzv2.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/swaps/boltzv2.rs b/src/swaps/boltzv2.rs index 202011d..839d605 100644 --- a/src/swaps/boltzv2.rs +++ b/src/swaps/boltzv2.rs @@ -6,7 +6,7 @@ use bitcoin::{ use lightning_invoice::Bolt11Invoice; use serde::{Deserialize, Serialize}; use serde_json::Value; -use tungstenite::{connect, stream::MaybeTlsStream, WebSocket}; +use tungstenite::{connect, http::response, stream::MaybeTlsStream, WebSocket}; use ureq::json; use crate::{error::Error, network::Chain, util::secrets::Preimage}; @@ -148,7 +148,10 @@ impl BoltzApiClientV2 { Ok(serde_json::from_str(&self.post(&endpoint, data)?)?) } - pub fn post_reverse_req(&self, req: CreateReverseRequest) -> Result { + pub fn post_reverse_req( + &self, + req: CreateReverseRequest, + ) -> Result { Ok(serde_json::from_str(&self.post("swap/reverse", req)?)?) } @@ -184,6 +187,24 @@ impl BoltzApiClientV2 { Ok(serde_json::from_str(&self.post(&endpoint, data)?)?) } + pub fn get_submarine_partial_sig( + &self, + id: &String, + pub_nonce: &MusigPubNonce, + refund_tx_hex: &String, + ) -> Result { + let data = json!( + { + "pubNonce": pub_nonce.serialize().to_lower_hex_string(), + "transaction": refund_tx_hex, + "index": 0 + } + ); + + let endpoint = format!("swap/submarine/{}/claim", id); + Ok(serde_json::from_str(&self.post(&endpoint, data)?)?) + } + pub fn broadcast_tx(&self, chain: Chain, tx_hex: &String) -> Result { let data = json!( { From ee4bef48b79a597ec66dc1f0ed1f4b8875ddbbce Mon Sep 17 00:00:00 2001 From: rajarshimaitra Date: Fri, 26 Apr 2024 18:58:22 +0530 Subject: [PATCH 3/9] add submarine cooperative refund and lowball braodcast --- src/swaps/bitcoinv2.rs | 270 +++++++++++++++++++++++++++++------------ src/swaps/liquidv2.rs | 209 ++++++++++++++++++++++++------- tests/bitcoin_v2.rs | 54 ++++++++- tests/liquid_v2.rs | 50 +++++++- tests/regtest_v2.rs | 4 +- 5 files changed, 465 insertions(+), 122 deletions(-) diff --git a/src/swaps/bitcoinv2.rs b/src/swaps/bitcoinv2.rs index 3b8b931..d54c919 100644 --- a/src/swaps/bitcoinv2.rs +++ b/src/swaps/bitcoinv2.rs @@ -17,6 +17,7 @@ use bitcoin::{sighash::SighashCache, Network, Sequence, Transaction, TxIn, TxOut use bitcoin::{Amount, EcdsaSighashType, TapLeafHash, TapSighashType, Txid, XOnlyPublicKey}; use electrum_client::ElectrumApi; use elements::encode::serialize; +use elements::pset::serialize::Serialize; use std::ops::{Add, Index}; use std::str::FromStr; @@ -31,7 +32,9 @@ use crate::{ use bitcoin::{blockdata::locktime::absolute::LockTime, hashes::hash160}; use super::boltz::SwapType; -use super::boltzv2::{BoltzApiClientV2, ClaimTxResponse, CreateSubmarineResponse, CreateReverseResponse}; +use super::boltzv2::{ + BoltzApiClientV2, ClaimTxResponse, CreateReverseResponse, CreateSubmarineResponse, +}; use elements::secp256k1_zkp::{ MusigAggNonce, MusigKeyAggCache, MusigPartialSignature, MusigPubNonce, MusigSession, @@ -241,12 +244,10 @@ impl BtcSwapScriptV2 { let taproot_builder = TaprootBuilder::new(); - let taproot_builder = taproot_builder - .add_leaf_with_ver(1, self.claim_script(), LeafVersion::TapScript) - ?; - let taproot_builder = taproot_builder - .add_leaf_with_ver(1, self.refund_script(), LeafVersion::TapScript) - ?; + let taproot_builder = + taproot_builder.add_leaf_with_ver(1, self.claim_script(), LeafVersion::TapScript)?; + let taproot_builder = + taproot_builder.add_leaf_with_ver(1, self.refund_script(), LeafVersion::TapScript)?; let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap(); @@ -448,9 +449,7 @@ impl BtcSwapTxV2 { let session_id = MusigSessionId::new(&mut thread_rng()); - let msg = Message::from_digest_slice( - &Vec::from_hex(&claim_tx_response.transaction_hash)?, - )?; + let msg = Message::from_digest_slice(&Vec::from_hex(&claim_tx_response.transaction_hash)?)?; // Step 4: Start the Musig2 Signing session let mut extra_rand = [0u8; 32]; @@ -534,8 +533,7 @@ impl BtcSwapTxV2 { 0, &Prevouts::All(&[&self.utxo.1]), bitcoin::TapSighashType::Default, - ) - ?; + )?; let msg = Message::from_digest_slice(claim_tx_taproot_hash.as_byte_array())?; @@ -557,24 +555,29 @@ impl BtcSwapTxV2 { let mut extra_rand = [0u8; 32]; OsRng.fill_bytes(&mut extra_rand); - let (sec_nonce, pub_nonce) = key_agg_cache - .nonce_gen(&secp, session_id, keys.public_key(), msg, Some(extra_rand)) - ?; + let (sec_nonce, pub_nonce) = key_agg_cache.nonce_gen( + &secp, + session_id, + keys.public_key(), + msg, + Some(extra_rand), + )?; // Step 7: Get boltz's partail sig let claim_tx_hex = serialize(&claim_tx).to_lower_hex_string(); - let partial_sig_resp = boltz_api - .get_reverse_partial_sig(&swap_id, &preimage, &pub_nonce, &claim_tx_hex) - ?; + let partial_sig_resp = boltz_api.get_reverse_partial_sig( + &swap_id, + &preimage, + &pub_nonce, + &claim_tx_hex, + )?; let boltz_public_nonce = - MusigPubNonce::from_slice(&Vec::from_hex(&partial_sig_resp.pub_nonce)?) - ?; + MusigPubNonce::from_slice(&Vec::from_hex(&partial_sig_resp.pub_nonce)?)?; - let boltz_partial_sig = MusigPartialSignature::from_slice( - &Vec::from_hex(&partial_sig_resp.partial_signature)?, - ) - ?; + let boltz_partial_sig = MusigPartialSignature::from_slice(&Vec::from_hex( + &partial_sig_resp.partial_signature, + )?)?; // Aggregate Our's and Other's Nonce and start the Musig session. let agg_nonce = MusigAggNonce::new(&secp, &[boltz_public_nonce, pub_nonce]); @@ -590,11 +593,14 @@ impl BtcSwapTxV2 { self.swap_script.sender_pubkey.inner, ); - assert!(boltz_partial_sig_verify == true); + if !boltz_partial_sig_verify { + return Err(Error::Protocol( + "Invalid partial-sig received from Boltz".to_string(), + )); + } - let our_partial_sig = musig_session - .partial_sign(&secp, sec_nonce, &keys, &key_agg_cache) - ?; + let our_partial_sig = + musig_session.partial_sign(&secp, sec_nonce, &keys, &key_agg_cache)?; let schnorr_sig = musig_session.partial_sig_agg(&[boltz_partial_sig, our_partial_sig]); @@ -611,21 +617,17 @@ impl BtcSwapTxV2 { witness.push(final_schnorr_sig.to_vec()); claim_tx.input[0].witness = witness; - - let claim_tx_hex = serialize(&claim_tx).to_lower_hex_string(); } else { // If Non-Cooperative claim use the Script Path spending let leaf_hash = TapLeafHash::from_script(&self.swap_script.claim_script(), LeafVersion::TapScript); - let sighash = SighashCache::new(claim_tx.clone()) - .taproot_script_spend_signature_hash( - 0, - &Prevouts::All(&[&self.utxo.1]), - leaf_hash, - TapSighashType::Default, - ) - ?; + let sighash = SighashCache::new(claim_tx.clone()).taproot_script_spend_signature_hash( + 0, + &Prevouts::All(&[&self.utxo.1]), + leaf_hash, + TapSighashType::Default, + )?; let msg = Message::from_digest_slice(sighash.as_byte_array())?; @@ -657,7 +659,12 @@ impl BtcSwapTxV2 { /// Sign a submarine swap refund transaction. /// Panics if called on Reverse Swap, Claim type. - pub fn sign_refund(&self, keys: &Keypair, absolute_fees: u64) -> Result { + pub fn sign_refund( + &self, + keys: &Keypair, + absolute_fees: u64, + is_cooperative: Option<(&BoltzApiClientV2, &String)>, + ) -> Result { if self.swap_script.swap_type == SwapType::ReverseSubmarine { return Err(Error::Protocol( "Cannot sign refund tx, for a reverse-swap".to_string(), @@ -710,52 +717,144 @@ impl BtcSwapTxV2 { .next() .unwrap(); - let mut spending_tx = Transaction { + let mut refund_tx = Transaction { version: Version::TWO, lock_time, input: vec![input], output: vec![output], }; - let leaf_hash = - TapLeafHash::from_script(&self.swap_script.refund_script(), LeafVersion::TapScript); + let secp = Secp256k1::new(); - let sighash = SighashCache::new(spending_tx.clone()) - .taproot_script_spend_signature_hash( - 0, - &Prevouts::All(&[&self.utxo.1]), - leaf_hash, - TapSighashType::Default, - ) - ?; + if let Some((boltz_api, swap_id)) = is_cooperative { + // Start the Musig session + + // Step 1: Get the sighash + let refund_tx_taproot_hash = SighashCache::new(refund_tx.clone()) + .taproot_key_spend_signature_hash( + 0, + &Prevouts::All(&[&self.utxo.1]), + bitcoin::TapSighashType::Default, + )?; - let msg = Message::from_digest_slice(sighash.as_byte_array())?; + let msg = Message::from_digest_slice(refund_tx_taproot_hash.as_byte_array())?; - let sig = Secp256k1::new().sign_schnorr(&msg, &keys); + // Step 2: Get the Public and Secret nonces - let final_sig = Signature { - sig, - hash_ty: TapSighashType::Default, - }; + let mut key_agg_cache = self.swap_script.musig_keyagg_cache(); - let control_block = self - .swap_script - .taproot_spendinfo()? - .control_block(&( - self.swap_script.refund_script().clone(), - LeafVersion::TapScript, - )) - .expect("Control block calculation failed"); + let tweak = SecretKey::from_slice( + self.swap_script + .taproot_spendinfo()? + .tap_tweak() + .as_byte_array(), + )?; + + let _ = key_agg_cache.pubkey_xonly_tweak_add(&secp, tweak)?; + + let session_id = MusigSessionId::new(&mut thread_rng()); + + let mut extra_rand = [0u8; 32]; + OsRng.fill_bytes(&mut extra_rand); + + let (sec_nonce, pub_nonce) = key_agg_cache.nonce_gen( + &secp, + session_id, + keys.public_key(), + msg, + Some(extra_rand), + )?; + + // Step 7: Get boltz's partail sig + let refund_tx_hex = serialize(&refund_tx).to_lower_hex_string(); + let partial_sig_resp = + boltz_api.get_submarine_partial_sig(&swap_id, &pub_nonce, &refund_tx_hex)?; + + let boltz_public_nonce = + MusigPubNonce::from_slice(&Vec::from_hex(&partial_sig_resp.pub_nonce)?)?; + + let boltz_partial_sig = MusigPartialSignature::from_slice(&Vec::from_hex( + &partial_sig_resp.partial_signature, + )?)?; + + // Aggregate Our's and Other's Nonce and start the Musig session. + let agg_nonce = MusigAggNonce::new(&secp, &[boltz_public_nonce, pub_nonce]); + + let musig_session = MusigSession::new(&secp, &key_agg_cache, agg_nonce, msg); + + // Verify the Boltz's sig. + let boltz_partial_sig_verify = musig_session.partial_verify( + &secp, + &key_agg_cache, + boltz_partial_sig, + boltz_public_nonce, + self.swap_script.sender_pubkey.inner, + ); + + if !boltz_partial_sig_verify { + return Err(Error::Protocol( + "Invalid partial-sig received from Boltz".to_string(), + )); + } + + let our_partial_sig = + musig_session.partial_sign(&secp, sec_nonce, &keys, &key_agg_cache)?; + + let schnorr_sig = musig_session.partial_sig_agg(&[boltz_partial_sig, our_partial_sig]); + + let final_schnorr_sig = Signature { + sig: schnorr_sig, + hash_ty: TapSighashType::Default, + }; + + let output_key = self.swap_script.taproot_spendinfo()?.output_key(); + + let _ = secp.verify_schnorr(&final_schnorr_sig.sig, &msg, &output_key.to_inner())?; + + let mut witness = Witness::new(); + witness.push(final_schnorr_sig.to_vec()); - let mut witness = Witness::new(); + refund_tx.input[0].witness = witness; + } else { + let leaf_hash = + TapLeafHash::from_script(&self.swap_script.refund_script(), LeafVersion::TapScript); + + let sighash = SighashCache::new(refund_tx.clone()) + .taproot_script_spend_signature_hash( + 0, + &Prevouts::All(&[&self.utxo.1]), + leaf_hash, + TapSighashType::Default, + )?; - witness.push(final_sig.to_vec()); - witness.push(self.swap_script.refund_script().as_bytes()); - witness.push(control_block.serialize()); + let msg = Message::from_digest_slice(sighash.as_byte_array())?; + + let sig = Secp256k1::new().sign_schnorr(&msg, &keys); - spending_tx.input[0].witness = witness; + let final_sig = Signature { + sig, + hash_ty: TapSighashType::Default, + }; - Ok(spending_tx) + let control_block = self + .swap_script + .taproot_spendinfo()? + .control_block(&( + self.swap_script.refund_script().clone(), + LeafVersion::TapScript, + )) + .expect("Control block calculation failed"); + + let mut witness = Witness::new(); + + witness.push(final_sig.to_vec()); + witness.push(self.swap_script.refund_script().as_bytes()); + witness.push(control_block.serialize()); + + refund_tx.input[0].witness = witness; + } + + Ok(refund_tx) } /// Calculate the size of a transaction. @@ -765,18 +864,39 @@ impl BtcSwapTxV2 { let dummy_abs_fee = 5_000; let tx = match self.kind { SwapTxKind::Claim => self.sign_claim(keys, preimage, dummy_abs_fee, None)?, // Can only calculate non-coperative claims - SwapTxKind::Refund => self.sign_refund(keys, dummy_abs_fee)?, + SwapTxKind::Refund => self.sign_refund(keys, dummy_abs_fee, None)?, }; Ok(tx.vsize()) } - /// Broadcast transaction to the network + + /// Broadcast transaction to the network. + /// + /// Lowall sending can be enabled with `is_lowball` option and by providing a [BoltzApiClientV2] and a [Chain]. pub fn broadcast( &self, signed_tx: &Transaction, network_config: &ElectrumConfig, + is_lowball: Option<(&BoltzApiClientV2, Chain)>, ) -> Result { - Ok(network_config - .build_client()? - .transaction_broadcast(signed_tx)?) + if let Some((boltz_api, chain)) = is_lowball { + log::info!("Attempting lowball braodcast"); + let tx_hex = serialize(signed_tx).to_lower_hex_string(); + let response = boltz_api.broadcast_tx(chain, &tx_hex)?; + let txid = Txid::from_str( + &response + .as_object() + .unwrap() + .get("id") + .unwrap() + .as_str() + .unwrap(), + )?; + log::info!("Broadcasted transaction via Boltz: {}", txid); + return Ok(txid); + } else { + Ok(network_config + .build_client()? + .transaction_broadcast(signed_tx)?) + } } } diff --git a/src/swaps/liquidv2.rs b/src/swaps/liquidv2.rs index 88fbdc8..9d95886 100644 --- a/src/swaps/liquidv2.rs +++ b/src/swaps/liquidv2.rs @@ -44,7 +44,7 @@ use elements::{ use super::{ boltz::SwapType, - boltzv2::{BoltzApiClientV2, ClaimTxResponse, CreateSubmarineResponse, CreateReverseResponse}, + boltzv2::{BoltzApiClientV2, ClaimTxResponse, CreateReverseResponse, CreateSubmarineResponse}, }; /// Liquid v2 swap script helper. @@ -675,8 +675,10 @@ impl LBtcSwapTxV2 { self.swap_script.sender_pubkey.inner, ); - if (!boltz_partial_sig_verify){ - return Err(Error::Taproot(("Unable to verify Partial Signature".to_string()))) + if (!boltz_partial_sig_verify) { + return Err(Error::Taproot( + ("Unable to verify Partial Signature".to_string()), + )); } let our_partial_sig = @@ -757,7 +759,12 @@ impl LBtcSwapTxV2 { } /// Sign a refund transaction for a submarine swap - pub fn sign_refund(&self, keys: &Keypair, absolute_fees: Amount) -> Result { + pub fn sign_refund( + &self, + keys: &Keypair, + absolute_fees: Amount, + is_cooperative: Option<(&BoltzApiClientV2, &String)>, + ) -> Result { if self.swap_script.swap_type == SwapType::ReverseSubmarine { return Err(Error::Protocol( "Cannot sign refund tx, for a reverse-swap".to_string(), @@ -869,46 +876,146 @@ impl LBtcSwapTxV2 { output: vec![fee_output, payment_output], }; - let leaf_hash = TapLeafHash::from_script(&refund_script, LeafVersion::default()); + if let Some((boltz_api, swap_id)) = is_cooperative { + let claim_tx_taproot_hash = SighashCache::new(&refund_tx) + .taproot_key_spend_signature_hash( + 0, + &Prevouts::All(&[&self.funding_utxo]), + SchnorrSighashType::Default, + self.genesis_hash, + ) + .unwrap(); + + let msg = Message::from_digest_slice(claim_tx_taproot_hash.as_byte_array())?; - let sighash = SighashCache::new(&refund_tx) - .taproot_script_spend_signature_hash( - 0, - &Prevouts::All(&[&self.funding_utxo]), - leaf_hash, - SchnorrSighashType::Default, - self.genesis_hash, - ) - .unwrap(); + let mut key_agg_cache = MusigKeyAggCache::new( + &secp, + &[ + self.swap_script.receiver_pubkey.inner, + self.swap_script.sender_pubkey.inner, + ], + ); - let msg = Message::from_digest_slice(sighash.as_byte_array())?; + let tweak = SecretKey::from_slice( + self.swap_script + .taproot_spendinfo()? + .tap_tweak() + .as_byte_array(), + )?; - let sig = secp.sign_schnorr(&msg, &keys); + let _ = key_agg_cache.pubkey_xonly_tweak_add(&secp, tweak)?; - let final_sig = SchnorrSig { - sig, - hash_ty: SchnorrSighashType::Default, - }; + let session_id = MusigSessionId::new(&mut thread_rng()); - let control_block = self - .swap_script - .taproot_spendinfo()? - .control_block(&(refund_script.clone(), LeafVersion::default())) - .unwrap(); + let mut extra_rand = [0u8; 32]; + OsRng.fill_bytes(&mut extra_rand); + + let (sec_nonce, pub_nonce) = key_agg_cache.nonce_gen( + &secp, + session_id, + keys.public_key(), + msg, + Some(extra_rand), + )?; - let mut script_witness = Witness::new(); - script_witness.push(final_sig.to_vec()); - script_witness.push(refund_script.as_bytes()); - script_witness.push(control_block.serialize()); + // Step 7: Get boltz's partial sig + let claim_tx_hex = serialize(&refund_tx).to_lower_hex_string(); + let partial_sig_resp = + boltz_api.get_submarine_partial_sig(&swap_id, &pub_nonce, &claim_tx_hex)?; - let witness = TxInWitness { - amount_rangeproof: None, - inflation_keys_rangeproof: None, - script_witness: script_witness.to_vec(), - pegin_witness: vec![], - }; + let boltz_public_nonce = + MusigPubNonce::from_slice(&Vec::from_hex(&partial_sig_resp.pub_nonce)?)?; + + let boltz_partial_sig = MusigPartialSignature::from_slice(&Vec::from_hex( + &partial_sig_resp.partial_signature, + )?)?; + + let agg_nonce = MusigAggNonce::new(&secp, &[boltz_public_nonce, pub_nonce]); + + let musig_session = MusigSession::new(&secp, &key_agg_cache, agg_nonce, msg); + + // Verify the sigs. + let boltz_partial_sig_verify = musig_session.partial_verify( + &secp, + &key_agg_cache, + boltz_partial_sig, + boltz_public_nonce, + self.swap_script.sender_pubkey.inner, + ); + + if (!boltz_partial_sig_verify) { + return Err(Error::Taproot( + ("Unable to verify Partial Signature".to_string()), + )); + } + + let our_partial_sig = + musig_session.partial_sign(&secp, sec_nonce, &keys, &key_agg_cache)?; + + let schnorr_sig = musig_session.partial_sig_agg(&[boltz_partial_sig, our_partial_sig]); + + let final_schnorr_sig = SchnorrSig { + sig: schnorr_sig, + hash_ty: SchnorrSighashType::Default, + }; + + let output_key = self.swap_script.taproot_spendinfo()?.output_key(); + + let _ = secp.verify_schnorr(&final_schnorr_sig.sig, &msg, &output_key.into_inner())?; + + let mut script_witness = Witness::new(); + script_witness.push(final_schnorr_sig.to_vec()); + + let witness = TxInWitness { + amount_rangeproof: None, + inflation_keys_rangeproof: None, + script_witness: script_witness.to_vec(), + pegin_witness: vec![], + }; + + refund_tx.input[0].witness = witness; + } else { + let leaf_hash = TapLeafHash::from_script(&refund_script, LeafVersion::default()); + + let sighash = SighashCache::new(&refund_tx) + .taproot_script_spend_signature_hash( + 0, + &Prevouts::All(&[&self.funding_utxo]), + leaf_hash, + SchnorrSighashType::Default, + self.genesis_hash, + ) + .unwrap(); + + let msg = Message::from_digest_slice(sighash.as_byte_array())?; + + let sig = secp.sign_schnorr(&msg, &keys); + + let final_sig = SchnorrSig { + sig, + hash_ty: SchnorrSighashType::Default, + }; + + let control_block = self + .swap_script + .taproot_spendinfo()? + .control_block(&(refund_script.clone(), LeafVersion::default())) + .unwrap(); + + let mut script_witness = Witness::new(); + script_witness.push(final_sig.to_vec()); + script_witness.push(refund_script.as_bytes()); + script_witness.push(control_block.serialize()); + + let witness = TxInWitness { + amount_rangeproof: None, + inflation_keys_rangeproof: None, + script_witness: script_witness.to_vec(), + pegin_witness: vec![], + }; - refund_tx.input[0].witness = witness; + refund_tx.input[0].witness = witness; + } Ok(refund_tx) } @@ -920,7 +1027,7 @@ impl LBtcSwapTxV2 { let dummy_abs_fee = Amount::from_sat(5_000); let tx = match self.kind { SwapTxKind::Claim => self.sign_claim(keys, preimage, dummy_abs_fee, None)?, // TODO: Hardcode cooperative spend size - SwapTxKind::Refund => self.sign_refund(keys, dummy_abs_fee)?, + SwapTxKind::Refund => self.sign_refund(keys, dummy_abs_fee, None)?, }; Ok(tx.vsize()) } @@ -930,11 +1037,29 @@ impl LBtcSwapTxV2 { &self, signed_tx: &Transaction, network_config: &ElectrumConfig, + is_lowball: Option<(&BoltzApiClientV2, Chain)>, ) -> Result { - let electrum_client = network_config.build_client()?; - let serialized = serialize(signed_tx); - Ok(electrum_client - .transaction_broadcast_raw(&serialized)? - .to_string()) + if let Some((boltz_api, chain)) = is_lowball { + log::info!("Attempting lowball braodcast"); + let tx_hex = serialize(signed_tx).to_lower_hex_string(); + let response = boltz_api.broadcast_tx(chain, &tx_hex)?; + let txid = response + .as_object() + .unwrap() + .get("id") + .unwrap() + .as_str() + .unwrap() + .to_string(); + log::info!("Broadcasted transaction via Boltz: {}", txid); + + return Ok(txid); + } else { + let electrum_client = network_config.build_client()?; + let serialized = serialize(signed_tx); + Ok(electrum_client + .transaction_broadcast_raw(&serialized)? + .to_string()) + } } } diff --git a/tests/bitcoin_v2.rs b/tests/bitcoin_v2.rs index bbabbe7..a56f104 100644 --- a/tests/bitcoin_v2.rs +++ b/tests/bitcoin_v2.rs @@ -111,6 +111,9 @@ fn bitcoin_v2_submarine() { create_swap_response.expected_amount, create_swap_response.address ); + + // Test Cooperative Refund. + // Send 1 sat less to than expected amount to Boltz, and let Boltz fail the swap. } // Boltz has paid the invoice, and waiting for our partial sig. @@ -154,6 +157,44 @@ fn bitcoin_v2_submarine() { log::info!("Successfully completed submarine swap"); break; } + + // This means the funding transaction was rejected by Boltz for whatever reason, and we need to get + // fund back via refund. + if update.status == "transaction.lockup.failed" { + let swap_tx = BtcSwapTxV2::new_refund( + swap_script.clone(), + &refund_address, + &ElectrumConfig::default_bitcoin(), + ) + .unwrap() + .expect("Funding UTXO not found"); + + match swap_tx.sign_refund( + &our_keys, + 1000, + Some((&boltz_api_v2, &create_swap_response.id)), + ) { + Ok(tx) => { + let txid = swap_tx + .broadcast(&tx, &ElectrumConfig::default_bitcoin(), None) + .unwrap(); + log::info!("Cooperative Refund Successfully broadcasted: {}", txid); + } + Err(e) => { + log::info!("Cooperative refund failed. {:?}", e); + log::info!("Attempting Non-cooperative refund."); + + let tx = swap_tx.sign_refund(&our_keys, 1000, None).unwrap(); + let txid = swap_tx + .broadcast(&tx, &ElectrumConfig::default_bitcoin(), None) + .unwrap(); + log::info!( + "Non-cooperative Refund Successfully broadcasted: {}", + txid + ); + } + } + } } SwapUpdate::Error { @@ -281,10 +322,19 @@ fn bitcoin_v2_reverse() { .unwrap(); claim_tx - .broadcast(&tx, &ElectrumConfig::default_bitcoin()) + .broadcast(&tx, &ElectrumConfig::default_bitcoin(), None) .unwrap(); - log::info!("Succesfully broadcasted claim tx!"); + // To test Lowball broadcast uncomment below line + // claim_tx + // .broadcast( + // &tx, + // &ElectrumConfig::default_bitcoin(), + // Some((&boltz_api_v2, boltz_client::network::Chain::BitcoinTestnet)), + // ) + // .unwrap(); + + log::info!("Successfully broadcasted claim tx!"); log::debug!("Claim Tx {:?}", tx); } diff --git a/tests/liquid_v2.rs b/tests/liquid_v2.rs index 4dc2b5e..bcd0497 100644 --- a/tests/liquid_v2.rs +++ b/tests/liquid_v2.rs @@ -148,6 +148,45 @@ fn liquid_v2_submarine() { log::info!("Successfully Sent partial signature"); } + // This means the funding transaction was rejected by Boltz for whatever reason, and we need to get + // fund back via refund. + if update.status == "transaction.lockup.failed" { + let swap_tx = LBtcSwapTxV2::new_refund( + swap_script.clone(), + &refund_address, + &ElectrumConfig::default_bitcoin(), + ) + .unwrap(); + + match swap_tx.sign_refund( + &our_keys, + Amount::from_sat(1000), + Some((&boltz_api_v2, &create_swap_response.id)), + ) { + Ok(tx) => { + let txid = swap_tx + .broadcast(&tx, &ElectrumConfig::default_bitcoin(), None) + .unwrap(); + log::info!("Cooperative Refund Successfully broadcasted: {}", txid); + } + Err(e) => { + log::info!("Cooperative refund failed. {:?}", e); + log::info!("Attempting Non-cooperative refund."); + + let tx = swap_tx + .sign_refund(&our_keys, Amount::from_sat(1000), None) + .unwrap(); + let txid = swap_tx + .broadcast(&tx, &ElectrumConfig::default_bitcoin(), None) + .unwrap(); + log::info!( + "Non-cooperative Refund Successfully broadcasted: {}", + txid + ); + } + } + } + if update.status == "transaction.claimed" { log::info!("Successfully completed submarine swap"); break; @@ -278,9 +317,18 @@ fn liquid_v2_reverse() { .unwrap(); claim_tx - .broadcast(&tx, &ElectrumConfig::default_bitcoin()) + .broadcast(&tx, &ElectrumConfig::default_bitcoin(), None) .unwrap(); + // To test Lowball broadcast uncomment below line + // claim_tx + // .broadcast( + // &tx, + // &ElectrumConfig::default_bitcoin(), + // Some((&boltz_api_v2, boltz_client::network::Chain::LiquidTestnet)), + // ) + // .unwrap(); + log::info!("Succesfully broadcasted claim tx!"); log::debug!("Claim Tx {:?}", tx); } diff --git a/tests/regtest_v2.rs b/tests/regtest_v2.rs index 86f4549..b830ed3 100644 --- a/tests/regtest_v2.rs +++ b/tests/regtest_v2.rs @@ -179,7 +179,7 @@ fn btc_submarine_refund() { utxo, }; - let refund_tx = swap_tx.sign_refund(&sender_keypair, 1000).unwrap(); + let refund_tx = swap_tx.sign_refund(&sender_keypair, 1000, None).unwrap(); // Make the timelock matured and broadcast the spend test_framework.generate_blocks(100); @@ -316,7 +316,7 @@ fn lbtc_submarine_refund() { }; let refund_tx = swap_tx - .sign_refund(&sender_keypair, Amount::from_sat(1000)) + .sign_refund(&sender_keypair, Amount::from_sat(1000), None) .unwrap(); // Make the timelock matured and broadcast the spend From c478faec72ebe63225736dba5f6c29ff2abfba76 Mon Sep 17 00:00:00 2001 From: rajarshimaitra Date: Sat, 27 Apr 2024 12:20:06 +0530 Subject: [PATCH 4/9] fix endpoint and locktime --- src/swaps/bitcoinv2.rs | 2 ++ src/swaps/boltzv2.rs | 2 +- src/swaps/liquidv2.rs | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/swaps/bitcoinv2.rs b/src/swaps/bitcoinv2.rs index d54c919..404d83f 100644 --- a/src/swaps/bitcoinv2.rs +++ b/src/swaps/bitcoinv2.rs @@ -815,6 +815,8 @@ impl BtcSwapTxV2 { witness.push(final_schnorr_sig.to_vec()); refund_tx.input[0].witness = witness; + + refund_tx.lock_time = LockTime::ZERO; // No locktime for cooperative spend } else { let leaf_hash = TapLeafHash::from_script(&self.swap_script.refund_script(), LeafVersion::TapScript); diff --git a/src/swaps/boltzv2.rs b/src/swaps/boltzv2.rs index 839d605..9233337 100644 --- a/src/swaps/boltzv2.rs +++ b/src/swaps/boltzv2.rs @@ -201,7 +201,7 @@ impl BoltzApiClientV2 { } ); - let endpoint = format!("swap/submarine/{}/claim", id); + let endpoint = format!("swap/submarine/{}/refund", id); Ok(serde_json::from_str(&self.post(&endpoint, data)?)?) } diff --git a/src/swaps/liquidv2.rs b/src/swaps/liquidv2.rs index 9d95886..9057253 100644 --- a/src/swaps/liquidv2.rs +++ b/src/swaps/liquidv2.rs @@ -974,6 +974,8 @@ impl LBtcSwapTxV2 { }; refund_tx.input[0].witness = witness; + + refund_tx.lock_time = LockTime::ZERO; } else { let leaf_hash = TapLeafHash::from_script(&refund_script, LeafVersion::default()); From 147ab3f06c8c491bde108beaad966c97c0f2c9f7 Mon Sep 17 00:00:00 2001 From: rajarshimaitra Date: Sat, 27 Apr 2024 12:33:03 +0530 Subject: [PATCH 5/9] remove lowball from btc, fix update status message --- src/swaps/bitcoinv2.rs | 30 +++++------------------------- src/swaps/liquidv2.rs | 5 +++++ tests/bitcoin_v2.rs | 19 ++++++------------- tests/liquid_v2.rs | 4 +++- 4 files changed, 19 insertions(+), 39 deletions(-) diff --git a/src/swaps/bitcoinv2.rs b/src/swaps/bitcoinv2.rs index 404d83f..797f017 100644 --- a/src/swaps/bitcoinv2.rs +++ b/src/swaps/bitcoinv2.rs @@ -563,7 +563,7 @@ impl BtcSwapTxV2 { Some(extra_rand), )?; - // Step 7: Get boltz's partail sig + // Step 7: Get boltz's partial sig let claim_tx_hex = serialize(&claim_tx).to_lower_hex_string(); let partial_sig_resp = boltz_api.get_reverse_partial_sig( &swap_id, @@ -765,7 +765,7 @@ impl BtcSwapTxV2 { Some(extra_rand), )?; - // Step 7: Get boltz's partail sig + // Step 7: Get boltz's partial sig let refund_tx_hex = serialize(&refund_tx).to_lower_hex_string(); let partial_sig_resp = boltz_api.get_submarine_partial_sig(&swap_id, &pub_nonce, &refund_tx_hex)?; @@ -872,33 +872,13 @@ impl BtcSwapTxV2 { } /// Broadcast transaction to the network. - /// - /// Lowall sending can be enabled with `is_lowball` option and by providing a [BoltzApiClientV2] and a [Chain]. pub fn broadcast( &self, signed_tx: &Transaction, network_config: &ElectrumConfig, - is_lowball: Option<(&BoltzApiClientV2, Chain)>, ) -> Result { - if let Some((boltz_api, chain)) = is_lowball { - log::info!("Attempting lowball braodcast"); - let tx_hex = serialize(signed_tx).to_lower_hex_string(); - let response = boltz_api.broadcast_tx(chain, &tx_hex)?; - let txid = Txid::from_str( - &response - .as_object() - .unwrap() - .get("id") - .unwrap() - .as_str() - .unwrap(), - )?; - log::info!("Broadcasted transaction via Boltz: {}", txid); - return Ok(txid); - } else { - Ok(network_config - .build_client()? - .transaction_broadcast(signed_tx)?) - } + Ok(network_config + .build_client()? + .transaction_broadcast(signed_tx)?) } } diff --git a/src/swaps/liquidv2.rs b/src/swaps/liquidv2.rs index 9057253..7cf1357 100644 --- a/src/swaps/liquidv2.rs +++ b/src/swaps/liquidv2.rs @@ -1042,6 +1042,11 @@ impl LBtcSwapTxV2 { is_lowball: Option<(&BoltzApiClientV2, Chain)>, ) -> Result { if let Some((boltz_api, chain)) = is_lowball { + if chain == Chain::Liquid { + return Err(Error::Protocol( + "Lowball broadcast is not active for main chain".to_string(), + )); + } log::info!("Attempting lowball braodcast"); let tx_hex = serialize(signed_tx).to_lower_hex_string(); let response = boltz_api.broadcast_tx(chain, &tx_hex)?; diff --git a/tests/bitcoin_v2.rs b/tests/bitcoin_v2.rs index a56f104..86b8b65 100644 --- a/tests/bitcoin_v2.rs +++ b/tests/bitcoin_v2.rs @@ -160,7 +160,9 @@ fn bitcoin_v2_submarine() { // This means the funding transaction was rejected by Boltz for whatever reason, and we need to get // fund back via refund. - if update.status == "transaction.lockup.failed" { + if update.status == "transaction.lockupFailed" + || update.status == "invoice.failedToPay" + { let swap_tx = BtcSwapTxV2::new_refund( swap_script.clone(), &refund_address, @@ -176,7 +178,7 @@ fn bitcoin_v2_submarine() { ) { Ok(tx) => { let txid = swap_tx - .broadcast(&tx, &ElectrumConfig::default_bitcoin(), None) + .broadcast(&tx, &ElectrumConfig::default_bitcoin()) .unwrap(); log::info!("Cooperative Refund Successfully broadcasted: {}", txid); } @@ -186,7 +188,7 @@ fn bitcoin_v2_submarine() { let tx = swap_tx.sign_refund(&our_keys, 1000, None).unwrap(); let txid = swap_tx - .broadcast(&tx, &ElectrumConfig::default_bitcoin(), None) + .broadcast(&tx, &ElectrumConfig::default_bitcoin()) .unwrap(); log::info!( "Non-cooperative Refund Successfully broadcasted: {}", @@ -322,18 +324,9 @@ fn bitcoin_v2_reverse() { .unwrap(); claim_tx - .broadcast(&tx, &ElectrumConfig::default_bitcoin(), None) + .broadcast(&tx, &ElectrumConfig::default_bitcoin()) .unwrap(); - // To test Lowball broadcast uncomment below line - // claim_tx - // .broadcast( - // &tx, - // &ElectrumConfig::default_bitcoin(), - // Some((&boltz_api_v2, boltz_client::network::Chain::BitcoinTestnet)), - // ) - // .unwrap(); - log::info!("Successfully broadcasted claim tx!"); log::debug!("Claim Tx {:?}", tx); } diff --git a/tests/liquid_v2.rs b/tests/liquid_v2.rs index bcd0497..279a5d5 100644 --- a/tests/liquid_v2.rs +++ b/tests/liquid_v2.rs @@ -150,7 +150,9 @@ fn liquid_v2_submarine() { // This means the funding transaction was rejected by Boltz for whatever reason, and we need to get // fund back via refund. - if update.status == "transaction.lockup.failed" { + if update.status == "transaction.lockupFailed" + || update.status == "invoice.failedToPay" + { let swap_tx = LBtcSwapTxV2::new_refund( swap_script.clone(), &refund_address, From dfb35ee7c5a60b33609eb0620044c8c787a12cc4 Mon Sep 17 00:00:00 2001 From: rajarshimaitra Date: Sat, 27 Apr 2024 12:35:51 +0530 Subject: [PATCH 6/9] verify with receiver pubkey --- src/swaps/bitcoinv2.rs | 2 +- src/swaps/liquidv2.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/swaps/bitcoinv2.rs b/src/swaps/bitcoinv2.rs index 797f017..07e2552 100644 --- a/src/swaps/bitcoinv2.rs +++ b/src/swaps/bitcoinv2.rs @@ -788,7 +788,7 @@ impl BtcSwapTxV2 { &key_agg_cache, boltz_partial_sig, boltz_public_nonce, - self.swap_script.sender_pubkey.inner, + self.swap_script.receiver_pubkey.inner, ); if !boltz_partial_sig_verify { diff --git a/src/swaps/liquidv2.rs b/src/swaps/liquidv2.rs index 7cf1357..0d51930 100644 --- a/src/swaps/liquidv2.rs +++ b/src/swaps/liquidv2.rs @@ -940,7 +940,7 @@ impl LBtcSwapTxV2 { &key_agg_cache, boltz_partial_sig, boltz_public_nonce, - self.swap_script.sender_pubkey.inner, + self.swap_script.receiver_pubkey.inner, ); if (!boltz_partial_sig_verify) { From eacfe4e782bacd7a25fea384dd08e76596f45a3f Mon Sep 17 00:00:00 2001 From: rajarshimaitra Date: Tue, 30 Apr 2024 00:19:18 +0530 Subject: [PATCH 7/9] use musig keyagg function in liquid --- src/swaps/liquidv2.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/swaps/liquidv2.rs b/src/swaps/liquidv2.rs index 0d51930..cfecbad 100644 --- a/src/swaps/liquidv2.rs +++ b/src/swaps/liquidv2.rs @@ -229,14 +229,26 @@ impl LBtcSwapScriptV2 { .into_script() } + pub fn musig_keyagg_cache(&self) -> MusigKeyAggCache { + match self.swap_type { + SwapType::Submarine => { + let pubkeys = [self.receiver_pubkey.inner, self.sender_pubkey.inner]; + MusigKeyAggCache::new(&Secp256k1::new(), &pubkeys) + } + + SwapType::ReverseSubmarine => { + let pubkeys = [self.sender_pubkey.inner, self.receiver_pubkey.inner]; + MusigKeyAggCache::new(&Secp256k1::new(), &pubkeys) + } + } + } + /// Internally used to convert struct into a bitcoin::Script type fn taproot_spendinfo(&self) -> Result { let secp = Secp256k1::new(); // Setup Key Aggregation cache - let pubkeys = [self.receiver_pubkey.inner, self.sender_pubkey.inner]; - - let mut key_agg_cache = MusigKeyAggCache::new(&secp, &pubkeys); + let mut key_agg_cache = self.musig_keyagg_cache(); // Construct the Taproot let internal_key = key_agg_cache.agg_pk(); @@ -616,13 +628,7 @@ impl LBtcSwapTxV2 { let msg = Message::from_digest_slice(claim_tx_taproot_hash.as_byte_array())?; - let mut key_agg_cache = MusigKeyAggCache::new( - &secp, - &[ - self.swap_script.receiver_pubkey.inner, - self.swap_script.sender_pubkey.inner, - ], - ); + let mut key_agg_cache = self.swap_script.musig_keyagg_cache(); let tweak = SecretKey::from_slice( self.swap_script @@ -888,13 +894,7 @@ impl LBtcSwapTxV2 { let msg = Message::from_digest_slice(claim_tx_taproot_hash.as_byte_array())?; - let mut key_agg_cache = MusigKeyAggCache::new( - &secp, - &[ - self.swap_script.receiver_pubkey.inner, - self.swap_script.sender_pubkey.inner, - ], - ); + let mut key_agg_cache = self.swap_script.musig_keyagg_cache(); let tweak = SecretKey::from_slice( self.swap_script From 36792855d292b2dc342632b6715e164151c46dcf Mon Sep 17 00:00:00 2001 From: rajarshimaitra Date: Tue, 30 Apr 2024 00:27:03 +0530 Subject: [PATCH 8/9] its deafult_liquid stupid --- tests/liquid_v2.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/liquid_v2.rs b/tests/liquid_v2.rs index 279a5d5..de57405 100644 --- a/tests/liquid_v2.rs +++ b/tests/liquid_v2.rs @@ -119,7 +119,7 @@ fn liquid_v2_submarine() { let swap_tx = LBtcSwapTxV2::new_refund( swap_script.clone(), &refund_address, - &ElectrumConfig::default_bitcoin(), + &ElectrumConfig::default_liquid(), ) .unwrap(); @@ -156,7 +156,7 @@ fn liquid_v2_submarine() { let swap_tx = LBtcSwapTxV2::new_refund( swap_script.clone(), &refund_address, - &ElectrumConfig::default_bitcoin(), + &ElectrumConfig::default_liquid(), ) .unwrap(); @@ -167,7 +167,7 @@ fn liquid_v2_submarine() { ) { Ok(tx) => { let txid = swap_tx - .broadcast(&tx, &ElectrumConfig::default_bitcoin(), None) + .broadcast(&tx, &ElectrumConfig::default_liquid(), None) .unwrap(); log::info!("Cooperative Refund Successfully broadcasted: {}", txid); } @@ -179,7 +179,7 @@ fn liquid_v2_submarine() { .sign_refund(&our_keys, Amount::from_sat(1000), None) .unwrap(); let txid = swap_tx - .broadcast(&tx, &ElectrumConfig::default_bitcoin(), None) + .broadcast(&tx, &ElectrumConfig::default_liquid(), None) .unwrap(); log::info!( "Non-cooperative Refund Successfully broadcasted: {}", @@ -305,7 +305,7 @@ fn liquid_v2_reverse() { let claim_tx = LBtcSwapTxV2::new_claim( swap_script.clone(), claim_address.clone(), - &ElectrumConfig::default_bitcoin(), + &ElectrumConfig::default_liquid(), ) .unwrap(); @@ -319,14 +319,14 @@ fn liquid_v2_reverse() { .unwrap(); claim_tx - .broadcast(&tx, &ElectrumConfig::default_bitcoin(), None) + .broadcast(&tx, &ElectrumConfig::default_liquid(), None) .unwrap(); // To test Lowball broadcast uncomment below line // claim_tx // .broadcast( // &tx, - // &ElectrumConfig::default_bitcoin(), + // &ElectrumConfig::default_liquid(), // Some((&boltz_api_v2, boltz_client::network::Chain::LiquidTestnet)), // ) // .unwrap(); From 9f1eab1d96aa83e910b17e0435eff770ff6a7fdd Mon Sep 17 00:00:00 2001 From: rajarshimaitra Date: Wed, 1 May 2024 20:19:55 +0530 Subject: [PATCH 9/9] fix liquid genesis hash --- src/swaps/liquidv2.rs | 8 +++----- src/util/mod.rs | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/swaps/liquidv2.rs b/src/swaps/liquidv2.rs index cfecbad..04bf4d5 100644 --- a/src/swaps/liquidv2.rs +++ b/src/swaps/liquidv2.rs @@ -28,7 +28,7 @@ use elements::secp256k1_zkp::Message; use crate::{ network::{electrum::ElectrumConfig, Chain}, swaps::boltz::{self, SwapTxKind}, - util::secrets::Preimage, + util::{liquid_genesis_hash, secrets::Preimage}, }; use crate::error::Error; @@ -418,8 +418,7 @@ impl LBtcSwapTxV2 { let (funding_outpoint, funding_utxo) = swap_script.fetch_utxo(network_config)?; let electrum = network_config.build_client()?; - let genesis_hash = - elements::BlockHash::from_raw_hash(electrum.block_header(0)?.block_hash().into()); + let genesis_hash = liquid_genesis_hash(&network_config)?; Ok(LBtcSwapTxV2 { kind: SwapTxKind::Claim, @@ -447,8 +446,7 @@ impl LBtcSwapTxV2 { let (funding_outpoint, funding_utxo) = swap_script.fetch_utxo(network_config)?; let electrum = network_config.build_client()?; - let genesis_hash = - elements::BlockHash::from_raw_hash(electrum.block_header(0)?.block_hash().into()); + let genesis_hash = liquid_genesis_hash(&network_config)?; Ok(LBtcSwapTxV2 { kind: SwapTxKind::Refund, diff --git a/src/util/mod.rs b/src/util/mod.rs index 5fd31a3..1ec7f66 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,9 +1,29 @@ use std::{env, sync::Once}; +use electrum_client::ElectrumApi; +use elements::encode::Decodable; + +use crate::{error::Error, network::electrum::ElectrumConfig}; + pub mod ec; pub mod secrets; /// Setup function that will only run once, even if called multiple times. + +pub fn liquid_genesis_hash(electrum_config: &ElectrumConfig) -> Result { + let electrum = electrum_config.build_client()?; + println!("ELECTRUM NETWORK: {:?}", electrum_config.network()); + + let response = electrum.block_header_raw(0)?; + println!("{:#?}", response); + let block_header = elements::BlockHeader::consensus_decode(&*response)?; + println!("{:#?}", block_header); + + Ok(elements::BlockHash::from_raw_hash( + block_header.block_hash().into(), + )) +} + pub fn setup_logger() { Once::new().call_once(|| { env_logger::Builder::from_env(