Skip to main content

elements_miniscript/descriptor/pegin/
dynafed_pegin.rs

1// Miniscript
2// Written in 2020 by
3//     Rust Elements developers
4//
5// To the extent possible under law, the author(s) have dedicated all
6// copyright and related and neighboring rights to this software to
7// the public domain worldwide. This software is distributed without
8// any warranty.
9//
10// You should have received a copy of the CC0 Public Domain Dedication
11// along with this software.
12// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13//
14
15//! DynaFed Pegin Descriptor Support
16//!
17//! Traits and implementations for Dynafed Pegin descriptors.
18//! Note that this is a bitcoin descriptor and thus cannot be
19//! added to elements Descriptor.
20//! Unlike Pegin descriptors these are Miniscript, so dealing
21//! with these is easier.
22
23use std::convert::TryFrom;
24use std::fmt;
25
26use bitcoin::blockdata::script::{self, PushBytes};
27use bitcoin::{self, PublicKey, ScriptBuf as BtcScript, Weight};
28use bitcoin_miniscript::descriptor::DescriptorType;
29use elements::secp256k1_zkp;
30
31use crate::descriptor::checksum::{self, verify_checksum};
32use crate::expression::{self, FromTree};
33use crate::extensions::{CovExtArgs, CovenantExt};
34use crate::policy::{semantic, Liftable};
35use crate::{
36    tweak_key, BtcDescriptor, BtcError, BtcFromTree, BtcLiftable, BtcPolicy, BtcSatisfier, BtcTree,
37    Descriptor, DescriptorPublicKey, Error, MiniscriptKey, ToPublicKey,
38};
39
40/// New Pegin Descriptor with Miniscript support
41/// Useful with dynamic federations
42#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
43pub struct Pegin<Pk: MiniscriptKey> {
44    /// The untweaked pegin bitcoin descriptor
45    pub fed_desc: BtcDescriptor<PublicKey>,
46    /// The redeem elements descriptor
47    ///
48    /// TODO: Allow pegin redeem descriptor with extensions
49    pub elem_desc: Descriptor<Pk, CovenantExt<CovExtArgs>>,
50}
51
52impl<Pk: MiniscriptKey> Pegin<Pk> {
53    /// Create a new LegacyPegin descriptor
54    pub fn new(
55        fed_desc: BtcDescriptor<PublicKey>,
56        elem_desc: Descriptor<Pk, CovenantExt<CovExtArgs>>,
57    ) -> Self {
58        Self {
59            fed_desc,
60            elem_desc,
61        }
62    }
63}
64
65impl Pegin<DescriptorPublicKey> {
66    pub fn derived_descriptor<C: secp256k1_zkp::Verification>(
67        &self,
68        arg: u32,
69        secp: &secp256k1_zkp::Secp256k1<C>,
70    ) -> Result<Pegin<PublicKey>, Error> {
71        let elem_desc = self.elem_desc.at_derivation_index(arg)?;
72        let elem_desc = elem_desc.derived_descriptor(secp)?;
73        Ok(Pegin::new(self.fed_desc.clone(), elem_desc))
74    }
75}
76
77impl<Pk: MiniscriptKey> fmt::Debug for Pegin<Pk> {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        write!(f, "pegin({:?},{:?})", self.fed_desc, self.elem_desc)
80    }
81}
82
83impl<Pk: MiniscriptKey> fmt::Display for Pegin<Pk> {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        use fmt::Write;
86        let mut wrapped_f = checksum::Formatter::new(f);
87        write!(wrapped_f, "pegin({:#},{:#})", self.fed_desc, self.elem_desc)?;
88        wrapped_f.write_checksum_if_not_alt()
89    }
90}
91
92impl<Pk: MiniscriptKey> Liftable<PublicKey> for Pegin<Pk> {
93    fn lift(&self) -> Result<semantic::Policy<PublicKey>, Error> {
94        let btc_pol = BtcLiftable::lift(&self.fed_desc)?;
95        Liftable::lift(&btc_pol)
96    }
97}
98
99impl<Pk: MiniscriptKey> BtcLiftable<PublicKey> for Pegin<Pk> {
100    fn lift(&self) -> Result<BtcPolicy<PublicKey>, BtcError> {
101        self.fed_desc.lift()
102    }
103}
104
105impl_from_tree!(
106    Pegin<Pk>,
107    fn from_tree(top: &expression::Tree<'_>) -> Result<Self, Error> {
108        if top.name == "pegin" && top.args.len() == 2 {
109            // a roundtrip hack to use FromTree from bitcoin::Miniscript from
110            // expression::Tree in elements.
111            let ms_str = top.args[0].to_string();
112            let ms_expr = BtcTree::from_str(&ms_str)?;
113            //
114            // TODO: Confirm with Andrew about the descriptor type for dynafed
115            // Assuming sh(wsh) for now.
116
117            let fed_desc = BtcDescriptor::<PublicKey>::from_tree(&ms_expr)?;
118            let elem_desc = Descriptor::<Pk, CovenantExt<CovExtArgs>>::from_tree(&top.args[1])?;
119            Ok(Pegin::new(fed_desc, elem_desc))
120        } else {
121            Err(Error::Unexpected(format!(
122                "{}({} args) while parsing legacy_pegin descriptor",
123                top.name,
124                top.args.len(),
125            )))
126        }
127    }
128);
129
130impl_from_str!(
131    Pegin<Pk>,
132    type Err = Error;,
133    fn from_str(s: &str) -> Result<Self, Self::Err> {
134        let desc_str = verify_checksum(s)?;
135        let top = expression::Tree::from_str(desc_str)?;
136        Self::from_tree(&top)
137    }
138);
139
140impl<Pk: MiniscriptKey> Pegin<Pk> {
141    /// Checks whether the descriptor is safe.
142    pub fn sanity_check(&self) -> Result<(), Error> {
143        self.fed_desc
144            .sanity_check()
145            .map_err(|_| Error::Unexpected("Federation script sanity check failed".to_string()))?;
146        self.elem_desc
147            .sanity_check()
148            .map_err(|_| Error::Unexpected("Federation script sanity check failed".to_string()))?;
149        Ok(())
150    }
151
152    /// Computes the Bitcoin address of the pegin descriptor, if one exists.
153    /// Requires the secp context to compute the tweak
154    pub fn bitcoin_address<C: secp256k1_zkp::Verification>(
155        &self,
156        network: bitcoin::Network,
157        secp: &secp256k1_zkp::Secp256k1<C>,
158    ) -> Result<bitcoin::Address, Error>
159    where
160        Pk: ToPublicKey,
161    {
162        match self.fed_desc.desc_type() {
163            DescriptorType::Wsh => Ok(bitcoin::Address::p2wsh(
164                &self
165                    .bitcoin_witness_script(secp)
166                    .expect("DO this cleanly after TR. Pay to taproot pegins unspecified till now"),
167                network,
168            )),
169            DescriptorType::ShWsh => Ok(bitcoin::Address::p2shwsh(
170                &self
171                    .bitcoin_witness_script(secp)
172                    .expect("DO this cleanly after TR. Pay to taproot pegins unspecified till now"),
173                network,
174            )),
175            _ => Err(Error::UnsupportedAddressForPegin),
176        }
177    }
178
179    /// Computes the bitcoin scriptpubkey of the descriptor.
180    /// Requires the secp context to compute the tweak
181    pub fn bitcoin_script_pubkey<C: secp256k1_zkp::Verification>(
182        &self,
183        secp: &secp256k1_zkp::Secp256k1<C>,
184    ) -> BtcScript
185    where
186        Pk: ToPublicKey,
187    {
188        self.bitcoin_address(bitcoin::Network::Bitcoin, secp)
189            .expect("Address cannot fail for pegin")
190            .script_pubkey()
191    }
192
193    /// Computes the scriptSig that will be in place for an unsigned
194    /// input spending an output with this descriptor. For pre-segwit
195    /// descriptors, which use the scriptSig for signatures, this
196    /// returns the empty script.
197    ///
198    /// This is used in Segwit transactions to produce an unsigned
199    /// transaction whose txid will not change during signing (since
200    /// only the witness data will change).
201    /// Requires the secp context to compute the tweak
202    pub fn bitcoin_unsigned_script_sig<C: secp256k1_zkp::Verification>(
203        &self,
204        secp: &secp256k1_zkp::Secp256k1<C>,
205    ) -> BtcScript
206    where
207        Pk: ToPublicKey,
208    {
209        let witness_script = self
210            .bitcoin_witness_script(secp)
211            .expect("TODO after taproot");
212        let push_bytes = <&PushBytes>::try_from(witness_script.as_bytes())
213            .expect("Witness script is not too larg");
214        script::Builder::new().push_slice(push_bytes).into_script()
215    }
216
217    /// Computes the bitcoin "witness script" of the descriptor, i.e. the underlying
218    /// script before any hashing is done. For `Bare`, `Pkh` and `Wpkh` this
219    /// is the scriptPubkey; for `ShWpkh` and `Sh` this is the redeemScript;
220    /// for the others it is the witness script.
221    pub fn bitcoin_witness_script<C: secp256k1_zkp::Verification>(
222        &self,
223        secp: &secp256k1_zkp::Secp256k1<C>,
224    ) -> Result<BtcScript, Error>
225    where
226        Pk: ToPublicKey,
227    {
228        let tweak_vec = self
229            .elem_desc
230            .explicit_script()
231            .expect("Tr pegins unknown yet")
232            .into_bytes();
233        bitcoin_witness_script(&self.fed_desc, &tweak_vec[..], secp)
234    }
235
236    /// Returns satisfying witness and scriptSig to spend an
237    /// output controlled by the given descriptor if it possible to
238    /// construct one using the satisfier S.
239    pub fn get_bitcoin_satisfaction<S, C: secp256k1_zkp::Verification>(
240        &self,
241        secp: &secp256k1_zkp::Secp256k1<C>,
242        satisfier: S,
243    ) -> Result<(Vec<Vec<u8>>, BtcScript), Error>
244    where
245        S: BtcSatisfier<bitcoin::PublicKey>,
246        Pk: ToPublicKey,
247    {
248        let claim_script = self
249            .elem_desc
250            .explicit_script()
251            .expect("Tr pegins unknown yet")
252            .into_bytes();
253        let mut t = TranslateTweak(&claim_script[..], secp);
254
255        let tweaked_desc = bitcoin_miniscript::TranslatePk::translate_pk(&self.fed_desc, &mut t)
256            .expect("Tweaking must succeed");
257
258        let res = tweaked_desc.get_satisfaction(satisfier)?;
259        Ok(res)
260    }
261
262    /// Computes an upper bound on the weight of a satisfying witness to the
263    /// transaction. Assumes all signatures are 73 bytes, including push opcode
264    /// and sighash suffix. Includes the weight of the VarInts encoding the
265    /// scriptSig and witness stack length.
266    // FIXME: the ToPublicKey bound here should not needed. Fix after upstream
267    pub fn max_satisfaction_weight(&self) -> Result<Weight, Error>
268    where
269        Pk: ToPublicKey,
270    {
271        // tweaking does not change max satisfaction weight
272        let w = self.fed_desc.max_weight_to_satisfy()?;
273        Ok(w)
274    }
275
276    /// Get the `scriptCode` of a transaction output.
277    ///
278    /// The `scriptCode` is the Script of the previous transaction output being serialized in the
279    /// sighash when evaluating a `CHECKSIG` & co. OP code.
280    pub fn script_code<C: secp256k1_zkp::Verification>(
281        &self,
282        secp: &secp256k1_zkp::Secp256k1<C>,
283    ) -> Result<BtcScript, Error>
284    where
285        Pk: ToPublicKey,
286    {
287        self.bitcoin_witness_script(secp)
288    }
289
290    /// Get the corresponding elements descriptor that would be used
291    /// at redeem time by the user.
292    /// Users can use the DescrpitorTrait operations on the output Descriptor
293    /// to obtain the characteristics of the elements descriptor.
294    pub fn into_user_descriptor(self) -> Descriptor<Pk, CovenantExt<CovExtArgs>> {
295        self.elem_desc
296    }
297}
298
299fn bitcoin_witness_script<C: secp256k1_zkp::Verification, Pk: ToPublicKey>(
300    fed_desc: &BtcDescriptor<Pk>,
301    claim_script: &[u8],
302    secp: &secp256k1_zkp::Secp256k1<C>,
303) -> Result<BtcScript, Error> {
304    let mut t = TranslateTweak(claim_script, secp);
305
306    let tweaked_desc = bitcoin_miniscript::TranslatePk::translate_pk(fed_desc, &mut t)
307        .expect("Tweaking must succeed");
308    Ok(tweaked_desc.explicit_script()?)
309}
310
311struct TranslateTweak<'a, 'b, C: secp256k1_zkp::Verification>(
312    &'a [u8],
313    &'b secp256k1_zkp::Secp256k1<C>,
314);
315
316impl<'a, 'b, Pk, C> bitcoin_miniscript::Translator<Pk, bitcoin::PublicKey, ()>
317    for TranslateTweak<'a, 'b, C>
318where
319    Pk: MiniscriptKey + ToPublicKey,
320    C: secp256k1_zkp::Verification,
321{
322    fn pk(&mut self, pk: &Pk) -> Result<bitcoin::PublicKey, ()> {
323        Ok(tweak_key(&pk.to_public_key(), self.1, self.0))
324    }
325
326    // We don't need to implement these methods as we are not using them in the policy.
327    // Fail if we encounter any hash fragments. See also translate_hash_clone! macro.
328    translate_hash_fail!(Pk, bitcoin::PublicKey, ());
329}
330
331#[cfg(test)]
332mod tests {
333    use bitcoin::hex::FromHex;
334    use bitcoin::PublicKey;
335
336    use crate::descriptor::pegin::Pegin;
337    use crate::{BtcDescriptor, ConfidentialDescriptor, DescriptorPublicKey};
338
339    fn fed_peg_desc() -> BtcDescriptor<PublicKey> {
340        let s = bitcoin::ScriptBuf::from_hex("5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68").unwrap();
341
342        type Segwitv0Script =
343            bitcoin_miniscript::Miniscript<bitcoin::PublicKey, bitcoin_miniscript::Segwitv0>;
344
345        let m = Segwitv0Script::parse(&s).unwrap();
346        assert_eq!(m.encode(), s);
347        BtcDescriptor::<_>::new_wsh(m).unwrap()
348    }
349
350    // test vector created with:
351    // ```
352    // $ elements-cli getnetworkinfo | jq .version
353    // 230201
354    // $ elements-cli getblockchaininfo | jq .blocks
355    // 2976078
356    // elements-cli getsidechaininfo | jq '.current_fedpegscripts[0]'`
357    // "5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68"
358    // $ elements-cli getpeginaddress
359    // {
360    // "mainchain_address": "bc1qyya0twwz58kgfslpdgsygeq0r4nngl9tkt89v6phk8nqrwyenwrq5h0dk8",
361    // "claim_script": "0014a15906e643f2c9635527ab8658d370e8eaf149b5"
362    // }
363    // ```
364    #[test]
365    fn test_pegin() {
366        let d = fed_peg_desc();
367
368        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";
369        assert_eq!(&d.to_string(), fedpegdesc);
370
371        let claimscript =
372            Vec::<u8>::from_hex("0014de8e299d5347503f7ee33247e780b7f412727623").unwrap();
373        let secp = secp256k1::Secp256k1::new();
374
375        let mainchain_address = "bc1qssx7ha3zxpq25l6uukphlwj3jumvmcv8qr3dy6uy8l8j4vwa5fhswpcw3p";
376
377        let s = super::bitcoin_witness_script(&d, &claimscript, &secp).unwrap();
378        let b = bitcoin::Address::p2wsh(&s, bitcoin::Network::Bitcoin);
379        assert_eq!(mainchain_address, b.to_string());
380
381        let elem_desc = "ct(slip77(ab5824f4477b4ebb00a132adfd8eb0b7935cf24f6ac151add5d1913db374ce92),elwpkh([759db348/84'/1'/0']tpubDCRMaF33e44pcJj534LXVhFbHibPbJ5vuLhSSPFAw57kYURv4tzXFL6LSnd78bkjqdmE3USedkbpXJUPA1tdzKfuYSL7PianceqAhwL2UkA/0/*))";
382        let elem_desc: ConfidentialDescriptor<DescriptorPublicKey> = elem_desc.parse().unwrap();
383        let elem_desc = elem_desc.descriptor.at_derivation_index(0).unwrap();
384        let elem_desc = elem_desc.derived_descriptor(&secp).unwrap();
385        let pegin = Pegin::new(d.clone(), elem_desc);
386
387        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");
388    }
389
390    #[test]
391    fn test_pegin_derive() {
392        let elem_desc = "ct(slip77(ab5824f4477b4ebb00a132adfd8eb0b7935cf24f6ac151add5d1913db374ce92),elwpkh([759db348/84'/1'/0']tpubDCRMaF33e44pcJj534LXVhFbHibPbJ5vuLhSSPFAw57kYURv4tzXFL6LSnd78bkjqdmE3USedkbpXJUPA1tdzKfuYSL7PianceqAhwL2UkA/0/*))";
393        let elem_desc: ConfidentialDescriptor<DescriptorPublicKey> = elem_desc.parse().unwrap();
394        let fed_peg_desc = fed_peg_desc();
395        let pegin = Pegin::new(fed_peg_desc, elem_desc.descriptor);
396        let secp = secp256k1::Secp256k1::new();
397
398        let address_0 = pegin
399            .derived_descriptor(0, &secp)
400            .unwrap()
401            .bitcoin_address(bitcoin::Network::Bitcoin, &secp)
402            .unwrap();
403        assert_eq!(
404            address_0.to_string(),
405            "bc1qqkq6czql4zqwsylgrfzttjrn5wjeqmwfq5yn80p39amxtnkng9lsn6c5qr"
406        );
407
408        let address_1 = pegin
409            .derived_descriptor(1, &secp)
410            .unwrap()
411            .bitcoin_address(bitcoin::Network::Bitcoin, &secp)
412            .unwrap();
413        assert_ne!(address_0, address_1);
414        assert_eq!(
415            address_1.to_string(),
416            "bc1qmevs3n40t394230lptclz55rmxkmr7dmnqhuflxf0cezdupsmvdsk25n3m"
417        );
418    }
419}