From 4c2042ab012fb435f72330ab32cae1c3ad614e4f Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Tue, 23 Feb 2021 15:57:43 +0100 Subject: [PATCH] [descriptor] Ensure that there are no duplicated keys --- src/descriptor/error.rs | 2 ++ src/descriptor/mod.rs | 29 ++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/descriptor/error.rs b/src/descriptor/error.rs index 64605061..185552f7 100644 --- a/src/descriptor/error.rs +++ b/src/descriptor/error.rs @@ -33,6 +33,8 @@ pub enum Error { InvalidDescriptorChecksum, /// The descriptor contains hardened derivation steps on public extended keys HardenedDerivationXpub, + /// The descriptor contains multiple keys with the same BIP32 fingerprint + DuplicatedKeys, /// Error thrown while working with [`keys`](crate::keys) Key(crate::keys::KeyError), diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index ee4f0629..350426dc 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -27,7 +27,7 @@ //! This module contains generic utilities to work with descriptors, plus some re-exported types //! from [`miniscript`]. -use std::collections::{BTreeMap, HashMap}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::ops::Deref; use bitcoin::util::bip32::{ @@ -225,6 +225,24 @@ pub(crate) fn into_wallet_descriptor_checked( return Err(DescriptorError::HardenedDerivationXpub); } + // Ensure that there are no duplicated keys + let mut found_keys = HashSet::new(); + let descriptor_contains_duplicated_keys = descriptor.for_any_key(|k| { + if let DescriptorPublicKey::XPub(xkey) = k.as_key() { + let fingerprint = xkey.root_fingerprint(secp); + if found_keys.contains(&fingerprint) { + return true; + } + + found_keys.insert(fingerprint); + } + + false + }); + if descriptor_contains_duplicated_keys { + return Err(DescriptorError::DuplicatedKeys); + } + Ok((descriptor, keymap)) } @@ -783,5 +801,14 @@ mod test { result.unwrap_err(), DescriptorError::HardenedDerivationXpub )); + + let descriptor = "wsh(multi(2,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/*))"; + let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + DescriptorError::DuplicatedKeys + )); } }