From 1a2272527b8be2dd49ae163076c63628aed0c6fc Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Tue, 26 Nov 2024 11:53:45 +0100 Subject: [PATCH 1/5] Remove generic in Pegin descriptor for `fed_desc` Since the `Pk` generic on `fed_desc` is the same as in `elem_desc` this binds the kind of user descriptor accepted. `fed_desc` always come in the form with `PublicKey`, thus by removing the generic we can support different `elem_desc`. --- src/descriptor/pegin/dynafed_pegin.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index 37efd5ff..b746fef3 100644 --- a/src/descriptor/pegin/dynafed_pegin.rs +++ b/src/descriptor/pegin/dynafed_pegin.rs @@ -24,7 +24,7 @@ use std::convert::TryFrom; use std::fmt; use bitcoin::blockdata::script::{self, PushBytes}; -use bitcoin::{self, ScriptBuf as BtcScript, Weight}; +use bitcoin::{self, PublicKey, ScriptBuf as BtcScript, Weight}; use elements::secp256k1_zkp; use crate::descriptor::checksum::{self, verify_checksum}; @@ -41,7 +41,7 @@ use crate::{ #[derive(Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct Pegin { /// The untweaked pegin bitcoin descriptor - pub fed_desc: BtcDescriptor, + pub fed_desc: BtcDescriptor, /// The redeem elements descriptor /// /// TODO: Allow pegin redeem descriptor with extensions @@ -51,7 +51,7 @@ pub struct Pegin { impl Pegin { /// Create a new LegacyPegin descriptor pub fn new( - fed_desc: BtcDescriptor, + fed_desc: BtcDescriptor, elem_desc: Descriptor>, ) -> Self { Self { @@ -76,15 +76,15 @@ impl fmt::Display for Pegin { } } -impl Liftable for Pegin { - fn lift(&self) -> Result, Error> { +impl Liftable for Pegin { + fn lift(&self) -> Result, Error> { let btc_pol = BtcLiftable::lift(&self.fed_desc)?; Liftable::lift(&btc_pol) } } -impl BtcLiftable for Pegin { - fn lift(&self) -> Result, BtcError> { +impl BtcLiftable for Pegin { + fn lift(&self) -> Result, BtcError> { self.fed_desc.lift() } } @@ -101,7 +101,7 @@ impl_from_tree!( // TODO: Confirm with Andrew about the descriptor type for dynafed // Assuming sh(wsh) for now. - let fed_desc = BtcDescriptor::::from_tree(&ms_expr)?; + let fed_desc = BtcDescriptor::::from_tree(&ms_expr)?; let elem_desc = Descriptor::>::from_tree(&top.args[1])?; Ok(Pegin::new(fed_desc, elem_desc)) } else { From f77617bd3680dc17de06ea818bc121f897aad46a Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Tue, 26 Nov 2024 12:00:23 +0100 Subject: [PATCH 2/5] Refactor out the fed peg desc in a function --- src/descriptor/pegin/dynafed_pegin.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index b746fef3..e95b5479 100644 --- a/src/descriptor/pegin/dynafed_pegin.rs +++ b/src/descriptor/pegin/dynafed_pegin.rs @@ -309,11 +309,23 @@ where #[cfg(test)] mod tests { + use bitcoin::PublicKey; use elements::hex::FromHex; use crate::descriptor::pegin::Pegin; use crate::{BtcDescriptor, ConfidentialDescriptor, DescriptorPublicKey}; + fn fed_peg_desc() -> BtcDescriptor { + let s = bitcoin::ScriptBuf::from_hex("5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68").unwrap(); + + type Segwitv0Script = + bitcoin_miniscript::Miniscript; + + let m = Segwitv0Script::parse(&s).unwrap(); + assert_eq!(m.encode(), s); + BtcDescriptor::<_>::new_wsh(m).unwrap() + } + // test vector created with: // ``` // $ elements-cli getnetworkinfo | jq .version @@ -330,16 +342,7 @@ mod tests { // ``` #[test] fn test_pegin() { - let fedpegscript ="5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68"; - let s = bitcoin::ScriptBuf::from_hex(fedpegscript).unwrap(); - - type Segwitv0Script = - bitcoin_miniscript::Miniscript; - - let m = Segwitv0Script::parse(&s).unwrap(); - assert_eq!(m.encode(), s); - - let d = BtcDescriptor::<_>::new_wsh(m).unwrap(); + let d = fed_peg_desc(); let fedpegdesc = "wsh(or_d(multi(11,020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261,02675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af99,02896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d48,029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c,02a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc4010,031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb,03079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b,03111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2,0318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa0840174,03230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de1,035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a6,03bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c,03cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d17546,03d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d424828,03ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a),and_v(v:older(4032),multi(2,03aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79,0291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807,0386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb))))#7jwwklk4"; assert_eq!(&d.to_string(), fedpegdesc); From 2f1e07b38dd5bde7e7742c345e142a868cb015ba Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Tue, 26 Nov 2024 13:22:50 +0100 Subject: [PATCH 3/5] Add Pegin::derived_descriptor Allowing to generate a bunch of pegin addresses from a single pegin descriptor --- src/descriptor/pegin/dynafed_pegin.rs | 47 ++++++++++++++++++++++++++- src/lib.rs | 12 +++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index e95b5479..0811d6e6 100644 --- a/src/descriptor/pegin/dynafed_pegin.rs +++ b/src/descriptor/pegin/dynafed_pegin.rs @@ -33,7 +33,7 @@ use crate::extensions::{CovExtArgs, CovenantExt}; use crate::policy::{semantic, Liftable}; use crate::{ tweak_key, BtcDescriptor, BtcError, BtcFromTree, BtcLiftable, BtcPolicy, BtcSatisfier, BtcTree, - Descriptor, Error, MiniscriptKey, ToPublicKey, + Descriptor, DescriptorPublicKey, Error, MiniscriptKey, ToPublicKey, }; /// New Pegin Descriptor with Miniscript support @@ -61,6 +61,18 @@ impl Pegin { } } +impl Pegin { + pub fn derived_descriptor( + &self, + arg: u32, + secp: &secp256k1_zkp::Secp256k1, + ) -> Result, Error> { + let elem_desc = self.elem_desc.at_derivation_index(arg)?; + let elem_desc = elem_desc.derived_descriptor(&secp)?; + Ok(Pegin::new(self.fed_desc.clone(), elem_desc)) + } +} + impl fmt::Debug for Pegin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "pegin({:?},{:?})", self.fed_desc, self.elem_desc) @@ -148,6 +160,7 @@ impl Pegin { { // TODO Ok(bitcoin::Address::p2shwsh( + // Should the address type taken from the top level user desc? &self .bitcoin_witness_script(secp) .expect("DO this cleanly after TR. Pay to taproot pegins unspecified till now"), @@ -365,4 +378,36 @@ mod tests { assert_eq!(pegin.to_string(), "pegin(wsh(or_d(multi(11,020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261,02675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af99,02896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d48,029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c,02a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc4010,031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb,03079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b,03111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2,0318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa0840174,03230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de1,035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a6,03bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c,03cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d17546,03d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d424828,03ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a),and_v(v:older(4032),multi(2,03aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79,0291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807,0386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb)))),elwpkh(0321da398ca2ddc09be89caa26e6730ae84751b6ea3a1ca46aa365bb5e1c3d9620))#qp4fan9q"); } + + #[test] + fn test_pegin_derive() { + let elem_desc = "ct(slip77(ab5824f4477b4ebb00a132adfd8eb0b7935cf24f6ac151add5d1913db374ce92),elwpkh([759db348/84'/1'/0']tpubDCRMaF33e44pcJj534LXVhFbHibPbJ5vuLhSSPFAw57kYURv4tzXFL6LSnd78bkjqdmE3USedkbpXJUPA1tdzKfuYSL7PianceqAhwL2UkA/0/*))"; + let elem_desc: ConfidentialDescriptor = elem_desc.parse().unwrap(); + let fed_peg_desc = fed_peg_desc(); + let pegin = Pegin::new(fed_peg_desc, elem_desc.descriptor); + let secp = secp256k1::Secp256k1::new(); + + let witness_script_0 = pegin + .derived_descriptor(0, &secp) + .unwrap() + .bitcoin_witness_script(&secp) + .unwrap(); + let address_0 = bitcoin::Address::p2wsh(&witness_script_0, bitcoin::Network::Bitcoin); + assert_eq!( + address_0.to_string(), + "bc1qqkq6czql4zqwsylgrfzttjrn5wjeqmwfq5yn80p39amxtnkng9lsn6c5qr" + ); + + let witness_script_1 = pegin + .derived_descriptor(1, &secp) + .unwrap() + .bitcoin_witness_script(&secp) + .unwrap(); + let address_1 = bitcoin::Address::p2wsh(&witness_script_1, bitcoin::Network::Bitcoin); + assert_ne!(address_0, address_1); + assert_eq!( + address_1.to_string(), + "bc1qmevs3n40t394230lptclz55rmxkmr7dmnqhuflxf0cezdupsmvdsk25n3m" + ); + } } diff --git a/src/lib.rs b/src/lib.rs index 6ed2da2d..1127453f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -373,6 +373,9 @@ pub enum Error { /// At least two BIP389 key expressions in the descriptor contain tuples of /// derivation indexes of different lengths. MultipathDescLenMismatch, + + /// Conversion error in descriptor + Conversion(descriptor::ConversionError), } #[doc(hidden)] @@ -449,6 +452,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: descriptor::ConversionError) -> Error { + Error::Conversion(e) + } +} + fn errstr(s: &str) -> Error { Error::Unexpected(s.to_owned()) } @@ -530,6 +539,8 @@ impl fmt::Display for Error { Error::TrNoScriptCode => write!(f, "No script code for Tr descriptors"), Error::TrNoExplicitScript => write!(f, "No script code for Tr descriptors"), Error::MultipathDescLenMismatch => write!(f, "At least two BIP389 key expressions in the descriptor contain tuples of derivation indexes of different lengths"), + Error::Conversion(ref e) => e.fmt(f), + } } } @@ -584,6 +595,7 @@ impl error::Error for Error { ContextError(e) => Some(e), AnalysisError(e) => Some(e), PubKeyCtxError(e, _) => Some(e), + Conversion(e) => Some(e), } } } From 59362c8e9506db8c4d265ad8221408024439749d Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Mon, 2 Dec 2024 10:27:24 +0100 Subject: [PATCH 4/5] remove unused lint cfg causing warning --- Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 692e0d4d..f7596550 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,9 +35,6 @@ bitcoin = { version = "0.32.0", features = ["base64"] } secp256k1 = { version = "0.29.0", features = ["rand-std"] } actual-base64 = { package = "base64", version = "0.13.0" } -[lints.rust] -unexpected_cfgs = { level = "deny", check-cfg = ['cfg(miniscript_bench)'] } - [[example]] name = "htlc" required-features = ["compiler"] From 624e3f42ee78a753823feca951a84483a9755e75 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Wed, 4 Dec 2024 16:47:36 +0100 Subject: [PATCH 5/5] create an address matching the fed desc --- src/descriptor/pegin/dynafed_pegin.rs | 34 ++++++++++++++++----------- src/lib.rs | 5 ++++ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index 0811d6e6..a2f0805f 100644 --- a/src/descriptor/pegin/dynafed_pegin.rs +++ b/src/descriptor/pegin/dynafed_pegin.rs @@ -25,6 +25,7 @@ use std::fmt; use bitcoin::blockdata::script::{self, PushBytes}; use bitcoin::{self, PublicKey, ScriptBuf as BtcScript, Weight}; +use bitcoin_miniscript::descriptor::DescriptorType; use elements::secp256k1_zkp; use crate::descriptor::checksum::{self, verify_checksum}; @@ -158,14 +159,21 @@ impl Pegin { where Pk: ToPublicKey, { - // TODO - Ok(bitcoin::Address::p2shwsh( - // Should the address type taken from the top level user desc? - &self - .bitcoin_witness_script(secp) - .expect("DO this cleanly after TR. Pay to taproot pegins unspecified till now"), - network, - )) + match self.fed_desc.desc_type() { + DescriptorType::Wsh => Ok(bitcoin::Address::p2wsh( + &self + .bitcoin_witness_script(secp) + .expect("DO this cleanly after TR. Pay to taproot pegins unspecified till now"), + network, + )), + DescriptorType::ShWsh => Ok(bitcoin::Address::p2shwsh( + &self + .bitcoin_witness_script(secp) + .expect("DO this cleanly after TR. Pay to taproot pegins unspecified till now"), + network, + )), + _ => return Err(Error::UnsupportedAddressForPegin), + } } /// Computes the bitcoin scriptpubkey of the descriptor. @@ -387,23 +395,21 @@ mod tests { let pegin = Pegin::new(fed_peg_desc, elem_desc.descriptor); let secp = secp256k1::Secp256k1::new(); - let witness_script_0 = pegin + let address_0 = pegin .derived_descriptor(0, &secp) .unwrap() - .bitcoin_witness_script(&secp) + .bitcoin_address(bitcoin::Network::Bitcoin, &secp) .unwrap(); - let address_0 = bitcoin::Address::p2wsh(&witness_script_0, bitcoin::Network::Bitcoin); assert_eq!( address_0.to_string(), "bc1qqkq6czql4zqwsylgrfzttjrn5wjeqmwfq5yn80p39amxtnkng9lsn6c5qr" ); - let witness_script_1 = pegin + let address_1 = pegin .derived_descriptor(1, &secp) .unwrap() - .bitcoin_witness_script(&secp) + .bitcoin_address(bitcoin::Network::Bitcoin, &secp) .unwrap(); - let address_1 = bitcoin::Address::p2wsh(&witness_script_1, bitcoin::Network::Bitcoin); assert_ne!(address_0, address_1); assert_eq!( address_1.to_string(), diff --git a/src/lib.rs b/src/lib.rs index 1127453f..b3826088 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -374,6 +374,9 @@ pub enum Error { /// derivation indexes of different lengths. MultipathDescLenMismatch, + /// The federation descriptor has a type not supported + UnsupportedAddressForPegin, + /// Conversion error in descriptor Conversion(descriptor::ConversionError), } @@ -540,6 +543,7 @@ impl fmt::Display for Error { Error::TrNoExplicitScript => write!(f, "No script code for Tr descriptors"), Error::MultipathDescLenMismatch => write!(f, "At least two BIP389 key expressions in the descriptor contain tuples of derivation indexes of different lengths"), Error::Conversion(ref e) => e.fmt(f), + Error::UnsupportedAddressForPegin => write!(f, "Cannot create the address from the pegin descriptor, the federation descriptor is of an unsuppported type"), } } @@ -579,6 +583,7 @@ impl error::Error for Error { | BareDescriptorAddr | TaprootSpendInfoUnavialable | TrNoScriptCode + | UnsupportedAddressForPegin | TrNoExplicitScript => None, MultipathDescLenMismatch => None, BtcError(e) => Some(e),