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"] diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index 37efd5ff..a2f0805f 100644 --- a/src/descriptor/pegin/dynafed_pegin.rs +++ b/src/descriptor/pegin/dynafed_pegin.rs @@ -24,7 +24,8 @@ 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 bitcoin_miniscript::descriptor::DescriptorType; use elements::secp256k1_zkp; use crate::descriptor::checksum::{self, verify_checksum}; @@ -33,7 +34,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 @@ -41,7 +42,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 +52,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 { @@ -61,6 +62,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) @@ -76,15 +89,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 +114,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 { @@ -146,13 +159,21 @@ impl Pegin { where Pk: ToPublicKey, { - // TODO - Ok(bitcoin::Address::p2shwsh( - &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. @@ -309,11 +330,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 +363,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); @@ -362,4 +386,34 @@ 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 address_0 = pegin + .derived_descriptor(0, &secp) + .unwrap() + .bitcoin_address(bitcoin::Network::Bitcoin, &secp) + .unwrap(); + assert_eq!( + address_0.to_string(), + "bc1qqkq6czql4zqwsylgrfzttjrn5wjeqmwfq5yn80p39amxtnkng9lsn6c5qr" + ); + + let address_1 = pegin + .derived_descriptor(1, &secp) + .unwrap() + .bitcoin_address(bitcoin::Network::Bitcoin, &secp) + .unwrap(); + 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..b3826088 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -373,6 +373,12 @@ pub enum Error { /// At least two BIP389 key expressions in the descriptor contain tuples of /// derivation indexes of different lengths. MultipathDescLenMismatch, + + /// The federation descriptor has a type not supported + UnsupportedAddressForPegin, + + /// Conversion error in descriptor + Conversion(descriptor::ConversionError), } #[doc(hidden)] @@ -449,6 +455,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 +542,9 @@ 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), + Error::UnsupportedAddressForPegin => write!(f, "Cannot create the address from the pegin descriptor, the federation descriptor is of an unsuppported type"), + } } } @@ -568,6 +583,7 @@ impl error::Error for Error { | BareDescriptorAddr | TaprootSpendInfoUnavialable | TrNoScriptCode + | UnsupportedAddressForPegin | TrNoExplicitScript => None, MultipathDescLenMismatch => None, BtcError(e) => Some(e), @@ -584,6 +600,7 @@ impl error::Error for Error { ContextError(e) => Some(e), AnalysisError(e) => Some(e), PubKeyCtxError(e, _) => Some(e), + Conversion(e) => Some(e), } } }