• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bitcoindevkit / bdk / 5834188079

pending completion
5834188079

Pull #1071

github

web-flow
Merge 68b42331c into 0ba6bbe11
Pull Request #1071: Update rust bitcoin (BDK 0.28)

563 of 563 new or added lines in 28 files covered. (100.0%)

14625 of 18342 relevant lines covered (79.74%)

9267.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.52
/src/descriptor/mod.rs
1
// Bitcoin Dev Kit
2
// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
3
//
4
// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
5
//
6
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
7
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
9
// You may not use this file except in accordance with one or both of these
10
// licenses.
11

12
//! Descriptors
13
//!
14
//! This module contains generic utilities to work with descriptors, plus some re-exported types
15
//! from [`miniscript`].
16

17
use std::collections::BTreeMap;
18

19
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint, KeySource};
20
use bitcoin::{key::XOnlyPublicKey, secp256k1, PublicKey};
21
use bitcoin::{psbt, taproot};
22
use bitcoin::{Network, TxOut};
23

24
use miniscript::descriptor::{
25
    DefiniteDescriptorKey, DescriptorMultiXKey, DescriptorSecretKey, DescriptorType,
26
    DescriptorXKey, InnerXKey, KeyMap, SinglePubKey, Wildcard,
27
};
28
pub use miniscript::{
29
    Descriptor, DescriptorPublicKey, Legacy, Miniscript, ScriptContext, Segwitv0,
30
};
31
use miniscript::{ForEachKey, MiniscriptKey, TranslatePk};
32

33
use crate::descriptor::policy::BuildSatisfaction;
34

35
pub mod checksum;
36
#[doc(hidden)]
37
pub mod dsl;
38
pub mod error;
39
pub mod policy;
40
pub mod template;
41

42
pub use self::checksum::calc_checksum;
43
use self::checksum::calc_checksum_bytes;
44
pub use self::error::Error as DescriptorError;
45
pub use self::policy::Policy;
46
use self::template::DescriptorTemplateOut;
47
use crate::keys::{IntoDescriptorKey, KeyError};
48
use crate::wallet::signer::SignersContainer;
49
use crate::wallet::utils::SecpCtx;
50

51
/// Alias for a [`Descriptor`] that can contain extended keys using [`DescriptorPublicKey`]
52
pub type ExtendedDescriptor = Descriptor<DescriptorPublicKey>;
53

54
/// Alias for a [`Descriptor`] that contains extended **derived** keys
55
pub type DerivedDescriptor = Descriptor<DefiniteDescriptorKey>;
56

57
/// Alias for the type of maps that represent derivation paths in a [`psbt::Input`] or
58
/// [`psbt::Output`]
59
///
60
/// [`psbt::Input`]: bitcoin::psbt::Input
61
/// [`psbt::Output`]: bitcoin::psbt::Output
62
pub type HdKeyPaths = BTreeMap<secp256k1::PublicKey, KeySource>;
63

64
/// Alias for the type of maps that represent taproot key origins in a [`psbt::Input`] or
65
/// [`psbt::Output`]
66
///
67
/// [`psbt::Input`]: bitcoin::psbt::Input
68
/// [`psbt::Output`]: bitcoin::psbt::Output
69
pub type TapKeyOrigins = BTreeMap<XOnlyPublicKey, (Vec<taproot::TapLeafHash>, KeySource)>;
70

71
/// Trait for types which can be converted into an [`ExtendedDescriptor`] and a [`KeyMap`] usable by a wallet in a specific [`Network`]
72
pub trait IntoWalletDescriptor {
73
    /// Convert to wallet descriptor
74
    fn into_wallet_descriptor(
75
        self,
76
        secp: &SecpCtx,
77
        network: Network,
78
    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError>;
79
}
80

81
impl IntoWalletDescriptor for &str {
82
    fn into_wallet_descriptor(
964✔
83
        self,
964✔
84
        secp: &SecpCtx,
964✔
85
        network: Network,
964✔
86
    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
964✔
87
        let descriptor = match self.split_once('#') {
964✔
88
            Some((desc, original_checksum)) => {
742✔
89
                let checksum = calc_checksum_bytes(desc)?;
742✔
90
                if original_checksum.as_bytes() != checksum {
742✔
91
                    return Err(DescriptorError::InvalidDescriptorChecksum);
4✔
92
                }
738✔
93
                desc
738✔
94
            }
95
            None => self,
222✔
96
        };
97

98
        ExtendedDescriptor::parse_descriptor(secp, descriptor)?
960✔
99
            .into_wallet_descriptor(secp, network)
960✔
100
    }
964✔
101
}
102

103
impl IntoWalletDescriptor for &String {
104
    fn into_wallet_descriptor(
792✔
105
        self,
792✔
106
        secp: &SecpCtx,
792✔
107
        network: Network,
792✔
108
    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
792✔
109
        self.as_str().into_wallet_descriptor(secp, network)
792✔
110
    }
792✔
111
}
112

113
impl IntoWalletDescriptor for ExtendedDescriptor {
114
    fn into_wallet_descriptor(
2✔
115
        self,
2✔
116
        secp: &SecpCtx,
2✔
117
        network: Network,
2✔
118
    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
2✔
119
        (self, KeyMap::default()).into_wallet_descriptor(secp, network)
2✔
120
    }
2✔
121
}
122

123
impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
124
    fn into_wallet_descriptor(
125
        self,
126
        secp: &SecpCtx,
127
        network: Network,
128
    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
129
        use crate::keys::DescriptorKey;
130

131
        struct Translator<'s, 'd> {
132
            secp: &'s SecpCtx,
133
            descriptor: &'d ExtendedDescriptor,
134
            network: Network,
135
        }
136

137
        impl<'s, 'd> miniscript::Translator<DescriptorPublicKey, String, DescriptorError>
138
            for Translator<'s, 'd>
139
        {
140
            fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<String, DescriptorError> {
1,066✔
141
                let secp = &self.secp;
1,066✔
142

143
                let (_, _, networks) = if self.descriptor.is_taproot() {
1,066✔
144
                    let descriptor_key: DescriptorKey<miniscript::Tap> =
152✔
145
                        pk.clone().into_descriptor_key()?;
152✔
146
                    descriptor_key.extract(secp)?
152✔
147
                } else if self.descriptor.is_witness() {
914✔
148
                    let descriptor_key: DescriptorKey<miniscript::Segwitv0> =
902✔
149
                        pk.clone().into_descriptor_key()?;
902✔
150
                    descriptor_key.extract(secp)?
902✔
151
                } else {
152
                    let descriptor_key: DescriptorKey<miniscript::Legacy> =
12✔
153
                        pk.clone().into_descriptor_key()?;
12✔
154
                    descriptor_key.extract(secp)?
12✔
155
                };
156

157
                if networks.contains(&self.network) {
1,066✔
158
                    Ok(Default::default())
1,062✔
159
                } else {
160
                    Err(DescriptorError::Key(KeyError::InvalidNetwork))
4✔
161
                }
162
            }
1,066✔
163
            fn sha256(
×
164
                &mut self,
×
165
                _sha256: &<DescriptorPublicKey as MiniscriptKey>::Sha256,
×
166
            ) -> Result<String, DescriptorError> {
×
167
                Ok(Default::default())
×
168
            }
×
169
            fn hash256(
×
170
                &mut self,
×
171
                _hash256: &<DescriptorPublicKey as MiniscriptKey>::Hash256,
×
172
            ) -> Result<String, DescriptorError> {
×
173
                Ok(Default::default())
×
174
            }
×
175
            fn ripemd160(
×
176
                &mut self,
×
177
                _ripemd160: &<DescriptorPublicKey as MiniscriptKey>::Ripemd160,
×
178
            ) -> Result<String, DescriptorError> {
×
179
                Ok(Default::default())
×
180
            }
×
181
            fn hash160(
×
182
                &mut self,
×
183
                _hash160: &<DescriptorPublicKey as MiniscriptKey>::Hash160,
×
184
            ) -> Result<String, DescriptorError> {
×
185
                Ok(Default::default())
×
186
            }
×
187
        }
188

189
        // check the network for the keys
190
        use miniscript::TranslateErr;
191
        match self.0.translate_pk(&mut Translator {
962✔
192
            secp,
962✔
193
            network,
962✔
194
            descriptor: &self.0,
962✔
195
        }) {
962✔
196
            Ok(_) => {}
958✔
197
            Err(TranslateErr::TranslatorErr(e)) => return Err(e),
4✔
198
            Err(TranslateErr::OuterError(e)) => return Err(e.into()),
×
199
        }
200

201
        Ok(self)
958✔
202
    }
962✔
203
}
204

205
impl IntoWalletDescriptor for DescriptorTemplateOut {
206
    fn into_wallet_descriptor(
344✔
207
        self,
344✔
208
        _secp: &SecpCtx,
344✔
209
        network: Network,
344✔
210
    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
344✔
211
        struct Translator {
344✔
212
            network: Network,
344✔
213
        }
344✔
214

344✔
215
        impl miniscript::Translator<DescriptorPublicKey, DescriptorPublicKey, DescriptorError>
344✔
216
            for Translator
344✔
217
        {
344✔
218
            fn pk(
408✔
219
                &mut self,
408✔
220
                pk: &DescriptorPublicKey,
408✔
221
            ) -> Result<DescriptorPublicKey, DescriptorError> {
408✔
222
                // workaround for xpubs generated by other key types, like bip39: since when the
344✔
223
                // conversion is made one network has to be chosen, what we generally choose
344✔
224
                // "mainnet", but then override the set of valid networks to specify that all of
344✔
225
                // them are valid. here we reset the network to make sure the wallet struct gets a
344✔
226
                // descriptor with the right network everywhere.
344✔
227
                let pk = match pk {
408✔
228
                    DescriptorPublicKey::XPub(ref xpub) => {
404✔
229
                        let mut xpub = xpub.clone();
348✔
230
                        xpub.xkey.network = self.network;
348✔
231

348✔
232
                        DescriptorPublicKey::XPub(xpub)
348✔
233
                    }
344✔
234
                    other => other.clone(),
348✔
235
                };
344✔
236

344✔
237
                Ok(pk)
408✔
238
            }
408✔
239
            miniscript::translate_hash_clone!(
344✔
240
                DescriptorPublicKey,
344✔
241
                DescriptorPublicKey,
344✔
242
                DescriptorError
344✔
243
            );
344✔
244
        }
344✔
245

344✔
246
        let (desc, keymap, networks) = self;
344✔
247

344✔
248
        if !networks.contains(&network) {
344✔
249
            return Err(DescriptorError::Key(KeyError::InvalidNetwork));
×
250
        }
344✔
251

252
        // fixup the network for keys that need it in the descriptor
253
        use miniscript::TranslateErr;
254
        let translated = match desc.translate_pk(&mut Translator { network }) {
344✔
255
            Ok(descriptor) => descriptor,
344✔
256
            Err(TranslateErr::TranslatorErr(e)) => return Err(e),
×
257
            Err(TranslateErr::OuterError(e)) => return Err(e.into()),
×
258
        };
259
        // ...and in the key map
260
        let fixed_keymap = keymap
344✔
261
            .into_iter()
344✔
262
            .map(|(mut k, mut v)| {
344✔
263
                match (&mut k, &mut v) {
222✔
264
                    (DescriptorPublicKey::XPub(xpub), DescriptorSecretKey::XPrv(xprv)) => {
162✔
265
                        xpub.xkey.network = network;
162✔
266
                        xprv.xkey.network = network;
162✔
267
                    }
162✔
268
                    (_, DescriptorSecretKey::Single(key)) => {
60✔
269
                        key.key.network = network;
60✔
270
                    }
60✔
271
                    _ => {}
×
272
                }
273

274
                (k, v)
222✔
275
            })
377✔
276
            .collect();
344✔
277

344✔
278
        Ok((translated, fixed_keymap))
344✔
279
    }
344✔
280
}
281

282
/// Wrapper for `IntoWalletDescriptor` that performs additional checks on the keys contained in the
283
/// descriptor
284
pub(crate) fn into_wallet_descriptor_checked<T: IntoWalletDescriptor>(
852✔
285
    inner: T,
852✔
286
    secp: &SecpCtx,
852✔
287
    network: Network,
852✔
288
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
852✔
289
    let (descriptor, keymap) = inner.into_wallet_descriptor(secp, network)?;
852✔
290

291
    // Ensure the keys don't contain any hardened derivation steps or hardened wildcards
292
    let descriptor_contains_hardened_steps = descriptor.for_any_key(|k| {
852✔
293
        if let DescriptorPublicKey::XPub(DescriptorXKey {
294
            derivation_path,
448✔
295
            wildcard,
448✔
296
            ..
297
        }) = k
956✔
298
        {
299
            return *wildcard == Wildcard::Hardened
448✔
300
                || derivation_path.into_iter().any(ChildNumber::is_hardened);
448✔
301
        }
508✔
302

508✔
303
        false
508✔
304
    });
956✔
305
    if descriptor_contains_hardened_steps {
852✔
306
        return Err(DescriptorError::HardenedDerivationXpub);
2✔
307
    }
850✔
308

850✔
309
    if descriptor.is_multipath() {
850✔
310
        return Err(DescriptorError::MultiPath);
2✔
311
    }
848✔
312

848✔
313
    // Run miniscript's sanity check, which will look for duplicated keys and other potential
848✔
314
    // issues
848✔
315
    descriptor.sanity_check()?;
848✔
316

317
    Ok((descriptor, keymap))
846✔
318
}
852✔
319

320
#[doc(hidden)]
321
/// Used internally mainly by the `descriptor!()` and `fragment!()` macros
322
pub trait CheckMiniscript<Ctx: miniscript::ScriptContext> {
323
    fn check_miniscript(&self) -> Result<(), miniscript::Error>;
324
}
325

326
impl<Ctx: miniscript::ScriptContext, Pk: miniscript::MiniscriptKey> CheckMiniscript<Ctx>
327
    for miniscript::Miniscript<Pk, Ctx>
328
{
329
    fn check_miniscript(&self) -> Result<(), miniscript::Error> {
330
        Ctx::check_global_validity(self)?;
124✔
331

332
        Ok(())
124✔
333
    }
124✔
334
}
335

336
/// Trait implemented on [`Descriptor`]s to add a method to extract the spending [`policy`]
337
pub trait ExtractPolicy {
338
    /// Extract the spending [`policy`]
339
    fn extract_policy(
340
        &self,
341
        signers: &SignersContainer,
342
        psbt: BuildSatisfaction,
343
        secp: &SecpCtx,
344
    ) -> Result<Option<Policy>, DescriptorError>;
345
}
346

347
pub(crate) trait XKeyUtils {
348
    fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint;
349
}
350

351
impl<T> XKeyUtils for DescriptorMultiXKey<T>
352
where
353
    T: InnerXKey,
354
{
355
    fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint {
×
356
        match self.origin {
×
357
            Some((fingerprint, _)) => fingerprint,
×
358
            None => self.xkey.xkey_fingerprint(secp),
×
359
        }
360
    }
×
361
}
362

363
impl<T> XKeyUtils for DescriptorXKey<T>
364
where
365
    T: InnerXKey,
366
{
367
    fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint {
1,163✔
368
        match self.origin {
1,163✔
369
            Some((fingerprint, _)) => fingerprint,
472✔
370
            None => self.xkey.xkey_fingerprint(secp),
691✔
371
        }
372
    }
1,163✔
373
}
374

375
pub(crate) trait DescriptorMeta {
376
    fn is_witness(&self) -> bool;
377
    fn is_taproot(&self) -> bool;
378
    fn get_extended_keys(&self) -> Result<Vec<DescriptorXKey<ExtendedPubKey>>, DescriptorError>;
379
    fn derive_from_hd_keypaths<'s>(
380
        &self,
381
        hd_keypaths: &HdKeyPaths,
382
        secp: &'s SecpCtx,
383
    ) -> Option<DerivedDescriptor>;
384
    fn derive_from_tap_key_origins<'s>(
385
        &self,
386
        tap_key_origins: &TapKeyOrigins,
387
        secp: &'s SecpCtx,
388
    ) -> Option<DerivedDescriptor>;
389
    fn derive_from_psbt_key_origins<'s>(
390
        &self,
391
        key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>,
392
        secp: &'s SecpCtx,
393
    ) -> Option<DerivedDescriptor>;
394
    fn derive_from_psbt_input<'s>(
395
        &self,
396
        psbt_input: &psbt::Input,
397
        utxo: Option<TxOut>,
398
        secp: &'s SecpCtx,
399
    ) -> Option<DerivedDescriptor>;
400
}
401

402
impl DescriptorMeta for ExtendedDescriptor {
403
    fn is_witness(&self) -> bool {
2,758✔
404
        matches!(
187✔
405
            self.desc_type(),
2,758✔
406
            DescriptorType::Wpkh
407
                | DescriptorType::ShWpkh
408
                | DescriptorType::Wsh
409
                | DescriptorType::ShWsh
410
                | DescriptorType::ShWshSortedMulti
411
                | DescriptorType::WshSortedMulti
412
        )
413
    }
2,758✔
414

415
    fn is_taproot(&self) -> bool {
1,622✔
416
        self.desc_type() == DescriptorType::Tr
1,622✔
417
    }
1,622✔
418

419
    fn get_extended_keys(&self) -> Result<Vec<DescriptorXKey<ExtendedPubKey>>, DescriptorError> {
6✔
420
        let mut answer = Vec::new();
6✔
421

6✔
422
        self.for_each_key(|pk| {
6✔
423
            if let DescriptorPublicKey::XPub(xpub) = pk {
6✔
424
                answer.push(xpub.clone());
6✔
425
            }
6✔
426

427
            true
6✔
428
        });
9✔
429

6✔
430
        Ok(answer)
6✔
431
    }
6✔
432

433
    fn derive_from_psbt_key_origins<'s>(
26✔
434
        &self,
26✔
435
        key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>,
26✔
436
        secp: &'s SecpCtx,
26✔
437
    ) -> Option<DerivedDescriptor> {
26✔
438
        // Ensure that deriving `xpub` with `path` yields `expected`
26✔
439
        let verify_key = |xpub: &DescriptorXKey<ExtendedPubKey>,
26✔
440
                          path: &DerivationPath,
441
                          expected: &SinglePubKey| {
4✔
442
            let derived = xpub
4✔
443
                .xkey
4✔
444
                .derive_pub(secp, path)
4✔
445
                .expect("The path should never contain hardened derivation steps")
4✔
446
                .public_key;
4✔
447

448
            match expected {
2✔
449
                SinglePubKey::FullKey(pk) if &PublicKey::new(derived) == pk => true,
2✔
450
                SinglePubKey::XOnly(pk) if &XOnlyPublicKey::from(derived) == pk => true,
2✔
451
                _ => false,
×
452
            }
453
        };
4✔
454

455
        let mut path_found = None;
26✔
456

26✔
457
        // using `for_any_key` should make this stop as soon as we return `true`
26✔
458
        self.for_any_key(|key| {
26✔
459
            if let DescriptorPublicKey::XPub(xpub) = key {
26✔
460
                // Check if the key matches one entry in our `key_origins`. If it does, `matches()` will
461
                // return the "prefix" that matched, so we remove that prefix from the full path
462
                // found in `key_origins` and save it in `derive_path`. We expect this to be a derivation
463
                // path of length 1 if the key is `wildcard` and an empty path otherwise.
464
                let root_fingerprint = xpub.root_fingerprint(secp);
6✔
465
                let derive_path = key_origins
6✔
466
                    .get_key_value(&root_fingerprint)
6✔
467
                    .and_then(|(fingerprint, (path, expected))| {
6✔
468
                        xpub.matches(&(*fingerprint, (*path).clone()), secp)
4✔
469
                            .zip(Some((path, expected)))
4✔
470
                    })
6✔
471
                    .and_then(|(prefix, (full_path, expected))| {
6✔
472
                        let derive_path = full_path
4✔
473
                            .into_iter()
4✔
474
                            .skip(prefix.into_iter().count())
4✔
475
                            .cloned()
4✔
476
                            .collect::<DerivationPath>();
4✔
477

4✔
478
                        // `derive_path` only contains the replacement index for the wildcard, if present, or
4✔
479
                        // an empty path for fixed descriptors. To verify the key we also need the normal steps
4✔
480
                        // that come before the wildcard, so we take them directly from `xpub` and then append
4✔
481
                        // the final index
4✔
482
                        if verify_key(
4✔
483
                            xpub,
4✔
484
                            &xpub.derivation_path.extend(derive_path.clone()),
4✔
485
                            expected,
4✔
486
                        ) {
4✔
487
                            Some(derive_path)
4✔
488
                        } else {
489
                            log::debug!(
×
490
                                "Key `{}` derived with {} yields an unexpected key",
×
491
                                root_fingerprint,
492
                                derive_path
493
                            );
494
                            None
×
495
                        }
496
                    });
6✔
497

498
                match derive_path {
×
499
                    Some(path) if xpub.wildcard != Wildcard::None && path.len() == 1 => {
4✔
500
                        // Ignore hardened wildcards
501
                        if let ChildNumber::Normal { index } = path[0] {
4✔
502
                            path_found = Some(index);
4✔
503
                            return true;
4✔
504
                        }
×
505
                    }
506
                    Some(path) if xpub.wildcard == Wildcard::None && path.is_empty() => {
×
507
                        path_found = Some(0);
×
508
                        return true;
×
509
                    }
510
                    _ => {}
2✔
511
                }
512
            }
20✔
513

514
            false
22✔
515
        });
39✔
516

26✔
517
        path_found.map(|path| {
28✔
518
            self.at_derivation_index(path)
4✔
519
                .expect("We ignore hardened wildcards")
4✔
520
        })
28✔
521
    }
26✔
522

523
    fn derive_from_hd_keypaths<'s>(
14✔
524
        &self,
14✔
525
        hd_keypaths: &HdKeyPaths,
14✔
526
        secp: &'s SecpCtx,
14✔
527
    ) -> Option<DerivedDescriptor> {
14✔
528
        // "Convert" an hd_keypaths map to the format required by `derive_from_psbt_key_origins`
14✔
529
        let key_origins = hd_keypaths
14✔
530
            .iter()
14✔
531
            .map(|(pk, (fingerprint, path))| {
15✔
532
                (
2✔
533
                    *fingerprint,
2✔
534
                    (path, SinglePubKey::FullKey(PublicKey::new(*pk))),
2✔
535
                )
2✔
536
            })
15✔
537
            .collect();
14✔
538
        self.derive_from_psbt_key_origins(key_origins, secp)
14✔
539
    }
14✔
540

541
    fn derive_from_tap_key_origins<'s>(
12✔
542
        &self,
12✔
543
        tap_key_origins: &TapKeyOrigins,
12✔
544
        secp: &'s SecpCtx,
12✔
545
    ) -> Option<DerivedDescriptor> {
12✔
546
        // "Convert" a tap_key_origins map to the format required by `derive_from_psbt_key_origins`
12✔
547
        let key_origins = tap_key_origins
12✔
548
            .iter()
12✔
549
            .map(|(pk, (_, (fingerprint, path)))| (*fingerprint, (path, SinglePubKey::XOnly(*pk))))
13✔
550
            .collect();
12✔
551
        self.derive_from_psbt_key_origins(key_origins, secp)
12✔
552
    }
12✔
553

554
    fn derive_from_psbt_input<'s>(
555
        &self,
556
        psbt_input: &psbt::Input,
557
        utxo: Option<TxOut>,
558
        secp: &'s SecpCtx,
559
    ) -> Option<DerivedDescriptor> {
560
        if let Some(derived) = self.derive_from_hd_keypaths(&psbt_input.bip32_derivation, secp) {
14✔
561
            return Some(derived);
2✔
562
        }
12✔
563
        if let Some(derived) = self.derive_from_tap_key_origins(&psbt_input.tap_key_origins, secp) {
12✔
564
            return Some(derived);
2✔
565
        }
10✔
566
        if self.has_wildcard() {
10✔
567
            // We can't try to bruteforce the derivation index, exit here
568
            return None;
×
569
        }
10✔
570

10✔
571
        let descriptor = self.at_derivation_index(0).expect("0 is not hardened");
10✔
572
        match descriptor.desc_type() {
10✔
573
            // TODO: add pk() here
574
            DescriptorType::Pkh
575
            | DescriptorType::Wpkh
576
            | DescriptorType::ShWpkh
577
            | DescriptorType::Tr
578
                if utxo.is_some()
6✔
579
                    && descriptor.script_pubkey() == utxo.as_ref().unwrap().script_pubkey =>
6✔
580
            {
581
                Some(descriptor)
2✔
582
            }
583
            DescriptorType::Bare | DescriptorType::Sh | DescriptorType::ShSortedMulti
584
                if psbt_input.redeem_script.is_some()
2✔
585
                    && &descriptor.explicit_script().unwrap()
2✔
586
                        == psbt_input.redeem_script.as_ref().unwrap() =>
2✔
587
            {
588
                Some(descriptor)
2✔
589
            }
590
            DescriptorType::Wsh
591
            | DescriptorType::ShWsh
592
            | DescriptorType::ShWshSortedMulti
593
            | DescriptorType::WshSortedMulti
594
                if psbt_input.witness_script.is_some()
2✔
595
                    && &descriptor.explicit_script().unwrap()
2✔
596
                        == psbt_input.witness_script.as_ref().unwrap() =>
2✔
597
            {
598
                Some(descriptor)
2✔
599
            }
600
            _ => None,
4✔
601
        }
602
    }
14✔
603
}
604

605
#[cfg(test)]
606
mod test {
607
    use std::str::FromStr;
608

609
    use assert_matches::assert_matches;
610
    use bitcoin::hashes::hex::FromHex;
611
    use bitcoin::secp256k1::Secp256k1;
612
    use bitcoin::ScriptBuf;
613
    use bitcoin::{bip32, psbt::Psbt};
614

615
    use super::*;
616
    use crate::psbt::PsbtUtils;
617

618
    #[test]
2✔
619
    fn test_derive_from_psbt_input_wpkh_wif() {
2✔
620
        let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
2✔
621
            "wpkh(02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737)",
2✔
622
        )
2✔
623
        .unwrap();
2✔
624
        let psbt = Psbt::deserialize(
2✔
625
            &Vec::<u8>::from_hex(
2✔
626
                "70736274ff010052010000000162307be8e431fbaff807cdf9cdc3fde44d7402\
2✔
627
                 11bc8342c31ffd6ec11fe35bcc0100000000ffffffff01328601000000000016\
2✔
628
                 001493ce48570b55c42c2af816aeaba06cfee1224fae000000000001011fa086\
2✔
629
                 01000000000016001493ce48570b55c42c2af816aeaba06cfee1224fae010304\
2✔
630
                 010000000000",
2✔
631
            )
2✔
632
            .unwrap(),
2✔
633
        )
2✔
634
        .unwrap();
2✔
635

2✔
636
        assert!(descriptor
2✔
637
            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
2✔
638
            .is_some());
2✔
639
    }
2✔
640

641
    #[test]
2✔
642
    fn test_derive_from_psbt_input_pkh_tpub() {
2✔
643
        let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
2✔
644
            "pkh([0f056943/44h/0h/0h]tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd/10/*)",
2✔
645
        )
2✔
646
        .unwrap();
2✔
647
        let psbt = Psbt::deserialize(
2✔
648
            &Vec::<u8>::from_hex(
2✔
649
                "70736274ff010053010000000145843b86be54a3cd8c9e38444e1162676c00df\
2✔
650
                 e7964122a70df491ea12fd67090100000000ffffffff01c19598000000000017\
2✔
651
                 a91432bb94283282f72b2e034709e348c44d5a4db0ef8700000000000100f902\
2✔
652
                 0000000001010167e99c0eb67640f3a1b6805f2d8be8238c947f8aaf49eb0a9c\
2✔
653
                 bee6a42c984200000000171600142b29a22019cca05b9c2b2d283a4c4489e1cf\
2✔
654
                 9f8ffeffffff02a01dced06100000017a914e2abf033cadbd74f0f4c74946201\
2✔
655
                 decd20d5c43c8780969800000000001976a9148b0fce5fb1264e599a65387313\
2✔
656
                 3c95478b902eb288ac02473044022015d9211576163fa5b001e84dfa3d44efd9\
2✔
657
                 86b8f3a0d3d2174369288b2b750906022048dacc0e5d73ae42512fd2b97e2071\
2✔
658
                 a8d0bce443b390b1fe0b8128fe70ec919e01210232dad1c5a67dcb0116d407e2\
2✔
659
                 52584228ab7ec00e8b9779d0c3ffe8114fc1a7d2c80600000103040100000022\
2✔
660
                 0603433b83583f8c4879b329dd08bbc7da935e4cc02f637ff746e05f0466ffb2\
2✔
661
                 a6a2180f0569432c00008000000080000000800a000000000000000000",
2✔
662
            )
2✔
663
            .unwrap(),
2✔
664
        )
2✔
665
        .unwrap();
2✔
666

2✔
667
        assert!(descriptor
2✔
668
            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
2✔
669
            .is_some());
2✔
670
    }
2✔
671

672
    #[test]
2✔
673
    fn test_derive_from_psbt_input_wsh() {
2✔
674
        let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
2✔
675
            "wsh(and_v(v:pk(03b6633fef2397a0a9de9d7b6f23aef8368a6e362b0581f0f0af70d5ecfd254b14),older(6)))",
2✔
676
        )
2✔
677
        .unwrap();
2✔
678
        let psbt = Psbt::deserialize(
2✔
679
            &Vec::<u8>::from_hex(
2✔
680
                "70736274ff01005302000000011c8116eea34408ab6529223c9a176606742207\
2✔
681
                 67a1ff1d46a6e3c4a88243ea6e01000000000600000001109698000000000017\
2✔
682
                 a914ad105f61102e0d01d7af40d06d6a5c3ae2f7fde387000000000001012b80\
2✔
683
                 969800000000002200203ca72f106a72234754890ca7640c43f65d2174e44d33\
2✔
684
                 336030f9059345091044010304010000000105252103b6633fef2397a0a9de9d\
2✔
685
                 7b6f23aef8368a6e362b0581f0f0af70d5ecfd254b14ad56b20000",
2✔
686
            )
2✔
687
            .unwrap(),
2✔
688
        )
2✔
689
        .unwrap();
2✔
690

2✔
691
        assert!(descriptor
2✔
692
            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
2✔
693
            .is_some());
2✔
694
    }
2✔
695

696
    #[test]
2✔
697
    fn test_derive_from_psbt_input_sh() {
2✔
698
        let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
2✔
699
            "sh(and_v(v:pk(021403881a5587297818fcaf17d239cefca22fce84a45b3b1d23e836c4af671dbb),after(630000)))",
2✔
700
        )
2✔
701
        .unwrap();
2✔
702
        let psbt = Psbt::deserialize(
2✔
703
            &Vec::<u8>::from_hex(
2✔
704
                "70736274ff0100530100000001bc8c13df445dfadcc42afa6dc841f85d22b01d\
2✔
705
                 a6270ebf981740f4b7b1d800390000000000feffffff01ba9598000000000017\
2✔
706
                 a91457b148ba4d3e5fa8608a8657875124e3d1c9390887f09c0900000100e002\
2✔
707
                 0000000001016ba1bbe05cc93574a0d611ec7d93ad0ab6685b28d0cd80e8a82d\
2✔
708
                 debb326643c90100000000feffffff02809698000000000017a914d9a6e8c455\
2✔
709
                 8e16c8253afe53ce37ad61cf4c38c487403504cf6100000017a9144044fb6e0b\
2✔
710
                 757dfc1b34886b6a95aef4d3db137e870247304402202a9b72d939bcde8ba2a1\
2✔
711
                 e0980597e47af4f5c152a78499143c3d0a78ac2286a602207a45b1df9e93b8c9\
2✔
712
                 6f09f5c025fe3e413ca4b905fe65ee55d32a3276439a9b8f012102dc1fcc2636\
2✔
713
                 4da1aa718f03d8d9bd6f2ff410ed2cf1245a168aa3bcc995ac18e0a806000001\
2✔
714
                 03040100000001042821021403881a5587297818fcaf17d239cefca22fce84a4\
2✔
715
                 5b3b1d23e836c4af671dbbad03f09c09b10000",
2✔
716
            )
2✔
717
            .unwrap(),
2✔
718
        )
2✔
719
        .unwrap();
2✔
720

2✔
721
        assert!(descriptor
2✔
722
            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
2✔
723
            .is_some());
2✔
724
    }
2✔
725

726
    #[test]
2✔
727
    fn test_to_wallet_descriptor_fixup_networks() {
2✔
728
        use crate::keys::{any_network, IntoDescriptorKey};
2✔
729

2✔
730
        let secp = Secp256k1::new();
2✔
731

2✔
732
        let xprv = bip32::ExtendedPrivKey::from_str("xprv9s21ZrQH143K3c3gF1DUWpWNr2SG2XrG8oYPpqYh7hoWsJy9NjabErnzriJPpnGHyKz5NgdXmq1KVbqS1r4NXdCoKitWg5e86zqXHa8kxyB").unwrap();
2✔
733
        let path = bip32::DerivationPath::from_str("m/0").unwrap();
2✔
734

2✔
735
        // here `to_descriptor_key` will set the valid networks for the key to only mainnet, since
2✔
736
        // we are using an "xpub"
2✔
737
        let key = (xprv, path.clone()).into_descriptor_key().unwrap();
2✔
738
        // override it with any. this happens in some key conversions, like bip39
2✔
739
        let key = key.override_valid_networks(any_network());
2✔
740

2✔
741
        // make a descriptor out of it
2✔
742
        let desc = crate::descriptor!(wpkh(key)).unwrap();
9✔
743
        // this should convert the key that supports "any_network" to the right network (testnet)
2✔
744
        let (wallet_desc, keymap) = desc
2✔
745
            .into_wallet_descriptor(&secp, Network::Testnet)
2✔
746
            .unwrap();
2✔
747

2✔
748
        let mut xprv_testnet = xprv;
2✔
749
        xprv_testnet.network = Network::Testnet;
2✔
750

2✔
751
        let xpub_testnet = bip32::ExtendedPubKey::from_priv(&secp, &xprv_testnet);
2✔
752
        let desc_pubkey = DescriptorPublicKey::XPub(DescriptorXKey {
2✔
753
            xkey: xpub_testnet,
2✔
754
            origin: None,
2✔
755
            derivation_path: path,
2✔
756
            wildcard: Wildcard::Unhardened,
2✔
757
        });
2✔
758

2✔
759
        assert_eq!(wallet_desc.to_string(), "wpkh(tpubD6NzVbkrYhZ4XtJzoDja5snUjBNQRP5B3f4Hyn1T1x6PVPxzzVjvw6nJx2D8RBCxog9GEVjZoyStfepTz7TtKoBVdkCtnc7VCJh9dD4RAU9/0/*)#a3svx0ha");
2✔
760
        assert_eq!(
2✔
761
            keymap
2✔
762
                .get(&desc_pubkey)
2✔
763
                .map(|key| key.to_public(&secp).unwrap()),
3✔
764
            Some(desc_pubkey)
2✔
765
        );
2✔
766
    }
2✔
767

768
    // test IntoWalletDescriptor trait from &str with and without checksum appended
769
    #[test]
2✔
770
    fn test_descriptor_from_str_with_checksum() {
2✔
771
        let secp = Secp256k1::new();
2✔
772

2✔
773
        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#tqz0nc62"
2✔
774
            .into_wallet_descriptor(&secp, Network::Testnet);
2✔
775
        assert!(desc.is_ok());
2✔
776

777
        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
2✔
778
            .into_wallet_descriptor(&secp, Network::Testnet);
2✔
779
        assert!(desc.is_ok());
2✔
780

781
        let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)#67ju93jw"
2✔
782
            .into_wallet_descriptor(&secp, Network::Testnet);
2✔
783
        assert!(desc.is_ok());
2✔
784

785
        let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
2✔
786
            .into_wallet_descriptor(&secp, Network::Testnet);
2✔
787
        assert!(desc.is_ok());
2✔
788

789
        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
2✔
790
            .into_wallet_descriptor(&secp, Network::Testnet);
2✔
791
        assert_matches!(desc, Err(DescriptorError::InvalidDescriptorChecksum));
2✔
792

793
        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
2✔
794
            .into_wallet_descriptor(&secp, Network::Testnet);
2✔
795
        assert_matches!(desc, Err(DescriptorError::InvalidDescriptorChecksum));
2✔
796
    }
2✔
797

798
    // test IntoWalletDescriptor trait from &str with keys from right and wrong network
799
    #[test]
2✔
800
    fn test_descriptor_from_str_with_keys_network() {
2✔
801
        let secp = Secp256k1::new();
2✔
802

2✔
803
        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
2✔
804
            .into_wallet_descriptor(&secp, Network::Testnet);
2✔
805
        assert!(desc.is_ok());
2✔
806

807
        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
2✔
808
            .into_wallet_descriptor(&secp, Network::Regtest);
2✔
809
        assert!(desc.is_ok());
2✔
810

811
        let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
2✔
812
            .into_wallet_descriptor(&secp, Network::Testnet);
2✔
813
        assert!(desc.is_ok());
2✔
814

815
        let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
2✔
816
            .into_wallet_descriptor(&secp, Network::Regtest);
2✔
817
        assert!(desc.is_ok());
2✔
818

819
        let desc = "sh(wpkh(02864bb4ad00cefa806098a69e192bbda937494e69eb452b87bb3f20f6283baedb))"
2✔
820
            .into_wallet_descriptor(&secp, Network::Testnet);
2✔
821
        assert!(desc.is_ok());
2✔
822

823
        let desc = "sh(wpkh(02864bb4ad00cefa806098a69e192bbda937494e69eb452b87bb3f20f6283baedb))"
2✔
824
            .into_wallet_descriptor(&secp, Network::Bitcoin);
2✔
825
        assert!(desc.is_ok());
2✔
826

827
        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
2✔
828
            .into_wallet_descriptor(&secp, Network::Bitcoin);
2✔
829
        assert_matches!(desc, Err(DescriptorError::Key(KeyError::InvalidNetwork)));
2✔
830

831
        let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
2✔
832
            .into_wallet_descriptor(&secp, Network::Bitcoin);
2✔
833
        assert_matches!(desc, Err(DescriptorError::Key(KeyError::InvalidNetwork)));
2✔
834
    }
2✔
835

836
    // test IntoWalletDescriptor trait from the output of the descriptor!() macro
837
    #[test]
2✔
838
    fn test_descriptor_from_str_from_output_of_macro() {
2✔
839
        let secp = Secp256k1::new();
2✔
840

2✔
841
        let tpub = bip32::ExtendedPubKey::from_str("tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK").unwrap();
2✔
842
        let path = bip32::DerivationPath::from_str("m/1/2").unwrap();
2✔
843
        let key = (tpub, path).into_descriptor_key().unwrap();
2✔
844

2✔
845
        // make a descriptor out of it
2✔
846
        let desc = crate::descriptor!(wpkh(key)).unwrap();
9✔
847

2✔
848
        let (wallet_desc, _) = desc
2✔
849
            .into_wallet_descriptor(&secp, Network::Testnet)
2✔
850
            .unwrap();
2✔
851
        let wallet_desc_str = wallet_desc.to_string();
2✔
852
        assert_eq!(wallet_desc_str, "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)#67ju93jw");
2✔
853

854
        let (wallet_desc2, _) = wallet_desc_str
2✔
855
            .into_wallet_descriptor(&secp, Network::Testnet)
2✔
856
            .unwrap();
2✔
857
        assert_eq!(wallet_desc, wallet_desc2)
2✔
858
    }
2✔
859

860
    #[test]
2✔
861
    fn test_into_wallet_descriptor_checked() {
2✔
862
        let secp = Secp256k1::new();
2✔
863

2✔
864
        let descriptor = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0'/1/2/*)";
2✔
865
        let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet);
2✔
866

867
        assert_matches!(result, Err(DescriptorError::HardenedDerivationXpub));
2✔
868

869
        let descriptor = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/<0;1>/*)";
2✔
870
        let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet);
2✔
871

872
        assert_matches!(result, Err(DescriptorError::MultiPath));
2✔
873

874
        // repeated pubkeys
875
        let descriptor = "wsh(multi(2,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*))";
2✔
876
        let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet);
2✔
877

2✔
878
        assert!(result.is_err());
2✔
879
    }
2✔
880

881
    #[test]
2✔
882
    fn test_sh_wsh_sortedmulti_redeemscript() {
2✔
883
        use miniscript::psbt::PsbtInputExt;
2✔
884

2✔
885
        let secp = Secp256k1::new();
2✔
886

2✔
887
        let descriptor = "sh(wsh(sortedmulti(3,tpubDEsqS36T4DVsKJd9UH8pAKzrkGBYPLEt9jZMwpKtzh1G6mgYehfHt9WCgk7MJG5QGSFWf176KaBNoXbcuFcuadAFKxDpUdMDKGBha7bY3QM/0/*,tpubDF3cpwfs7fMvXXuoQbohXtLjNM6ehwYT287LWtmLsd4r77YLg6MZg4vTETx5MSJ2zkfigbYWu31VA2Z2Vc1cZugCYXgS7FQu6pE8V6TriEH/0/*,tpubDE1SKfcW76Tb2AASv5bQWMuScYNAdoqLHoexw13sNDXwmUhQDBbCD3QAedKGLhxMrWQdMDKENzYtnXPDRvexQPNuDrLj52wAjHhNEm8sJ4p/0/*,tpubDFLc6oXwJmhm3FGGzXkfJNTh2KitoY3WhmmQvuAjMhD8YbyWn5mAqckbxXfm2etM3p5J6JoTpSrMqRSTfMLtNW46poDaEZJ1kjd3csRSjwH/0/*,tpubDEWD9NBeWP59xXmdqSNt4VYdtTGwbpyP8WS962BuqpQeMZmX9Pur14dhXdZT5a7wR1pK6dPtZ9fP5WR493hPzemnBvkfLLYxnUjAKj1JCQV/0/*,tpubDEHyZkkwd7gZWCTgQuYQ9C4myF2hMEmyHsBCCmLssGqoqUxeT3gzohF5uEVURkf9TtmeepJgkSUmteac38FwZqirjApzNX59XSHLcwaTZCH/0/*,tpubDEqLouCekwnMUWN486kxGzD44qVgeyuqHyxUypNEiQt5RnUZNJe386TKPK99fqRV1vRkZjYAjtXGTECz98MCsdLcnkM67U6KdYRzVubeCgZ/0/*)))";
2✔
888
        let (descriptor, _) =
2✔
889
            into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet).unwrap();
2✔
890

2✔
891
        let descriptor = descriptor.at_derivation_index(0).unwrap();
2✔
892

2✔
893
        let script = ScriptBuf::from_hex("5321022f533b667e2ea3b36e21961c9fe9dca340fbe0af5210173a83ae0337ab20a57621026bb53a98e810bd0ee61a0ed1164ba6c024786d76554e793e202dc6ce9c78c4ea2102d5b8a7d66a41ffdb6f4c53d61994022e886b4f45001fb158b95c9164d45f8ca3210324b75eead2c1f9c60e8adeb5e7009fec7a29afcdb30d829d82d09562fe8bae8521032d34f8932200833487bd294aa219dcbe000b9f9b3d824799541430009f0fa55121037468f8ea99b6c64788398b5ad25480cad08f4b0d65be54ce3a55fd206b5ae4722103f72d3d96663b0ea99b0aeb0d7f273cab11a8de37885f1dddc8d9112adb87169357ae").unwrap();
2✔
894

2✔
895
        let mut psbt_input = psbt::Input::default();
2✔
896
        psbt_input
2✔
897
            .update_with_descriptor_unchecked(&descriptor)
2✔
898
            .unwrap();
2✔
899

2✔
900
        assert_eq!(psbt_input.redeem_script, Some(script.to_v0_p2wsh()));
2✔
901
        assert_eq!(psbt_input.witness_script, Some(script));
2✔
902
    }
2✔
903
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc