]> Untitled Git - bdk/commitdiff
[descriptor] Perform additional checks before using a descriptor
authorAlekos Filini <alekos.filini@gmail.com>
Mon, 15 Feb 2021 16:33:47 +0000 (11:33 -0500)
committerSteve Myers <steve@notmandatory.org>
Wed, 17 Feb 2021 20:08:31 +0000 (12:08 -0800)
Fixes #287

src/descriptor/error.rs
src/descriptor/mod.rs
src/wallet/mod.rs

index 495f999c91cf17b0cb7fb6d01b8b59f8c837b1e4..64605061573c6bf91b4516d242f5ae4cf1c3cf91 100644 (file)
@@ -31,6 +31,8 @@ pub enum Error {
     InvalidHDKeyPath,
     /// The provided descriptor doesn't match its checksum
     InvalidDescriptorChecksum,
+    /// The descriptor contains hardened derivation steps on public extended keys
+    HardenedDerivationXpub,
 
     /// Error thrown while working with [`keys`](crate::keys)
     Key(crate::keys::KeyError),
index 10000786f131058d19023e6e46a540f94bb9ee2a..ee4f06294a9fc907f3e4ea69f63483d780befefa 100644 (file)
@@ -198,6 +198,36 @@ impl IntoWalletDescriptor for DescriptorTemplateOut {
     }
 }
 
+/// Wrapper for `IntoWalletDescriptor` that performs additional checks on the keys contained in the
+/// descriptor
+pub(crate) fn into_wallet_descriptor_checked<T: IntoWalletDescriptor>(
+    inner: T,
+    secp: &SecpCtx,
+    network: Network,
+) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
+    let (descriptor, keymap) = inner.into_wallet_descriptor(secp, network)?;
+
+    // Ensure the keys don't contain any hardened derivation steps or hardened wildcards
+    let descriptor_contains_hardened_steps = descriptor.for_any_key(|k| {
+        if let DescriptorPublicKey::XPub(DescriptorXKey {
+            derivation_path,
+            wildcard,
+            ..
+        }) = k.as_key()
+        {
+            return *wildcard == Wildcard::Hardened
+                || derivation_path.into_iter().any(ChildNumber::is_hardened);
+        }
+
+        false
+    });
+    if descriptor_contains_hardened_steps {
+        return Err(DescriptorError::HardenedDerivationXpub);
+    }
+
+    Ok((descriptor, keymap))
+}
+
 #[doc(hidden)]
 /// Used internally mainly by the `descriptor!()` and `fragment!()` macros
 pub trait CheckMiniscript<Ctx: miniscript::ScriptContext> {
@@ -740,4 +770,18 @@ mod test {
             .unwrap();
         assert_eq!(wallet_desc, wallet_desc2)
     }
+
+    #[test]
+    fn test_into_wallet_descriptor_checked() {
+        let secp = Secp256k1::new();
+
+        let descriptor = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0'/1/2/*)";
+        let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet);
+
+        assert!(result.is_err());
+        assert!(matches!(
+            result.unwrap_err(),
+            DescriptorError::HardenedDerivationXpub
+        ));
+    }
 }
index b2729ec77fd52a2aa500f6bc7e3f078acbd58436..8bb254c488b3c7e142c0a4d55ad037bfe5a75b35 100644 (file)
@@ -66,8 +66,9 @@ use crate::blockchain::{Blockchain, Progress};
 use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
 use crate::descriptor::derived::AsDerived;
 use crate::descriptor::{
-    get_checksum, DerivedDescriptor, DerivedDescriptorMeta, DescriptorMeta, DescriptorScripts,
-    ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor, Policy, XKeyUtils,
+    get_checksum, into_wallet_descriptor_checked, DerivedDescriptor, DerivedDescriptorMeta,
+    DescriptorMeta, DescriptorScripts, ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor,
+    Policy, XKeyUtils,
 };
 use crate::error::Error;
 use crate::psbt::PSBTUtils;
@@ -134,7 +135,7 @@ where
     ) -> Result<Self, Error> {
         let secp = Secp256k1::new();
 
-        let (descriptor, keymap) = descriptor.into_wallet_descriptor(&secp, network)?;
+        let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, &secp, network)?;
         database.check_descriptor_checksum(
             KeychainKind::External,
             get_checksum(&descriptor.to_string())?.as_bytes(),
@@ -143,7 +144,7 @@ where
         let (change_descriptor, change_signers) = match change_descriptor {
             Some(desc) => {
                 let (change_descriptor, change_keymap) =
-                    desc.into_wallet_descriptor(&secp, network)?;
+                    into_wallet_descriptor_checked(desc, &secp, network)?;
                 database.check_descriptor_checksum(
                     KeychainKind::Internal,
                     get_checksum(&change_descriptor.to_string())?.as_bytes(),