From c0dd63dfb2b6831666fb260581dce36e6f7601fa Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Thu, 23 Mar 2023 14:40:45 +0100 Subject: [PATCH] descriptors: move the LianaDescKey to the keys submodules --- src/descriptors/keys.rs | 96 ++++++++++++++++++++++++++++++++++++++++- src/descriptors/mod.rs | 96 +---------------------------------------- 2 files changed, 96 insertions(+), 96 deletions(-) diff --git a/src/descriptors/keys.rs b/src/descriptors/keys.rs index 29e1d13e..0cdc8f1f 100644 --- a/src/descriptors/keys.rs +++ b/src/descriptors/keys.rs @@ -4,20 +4,24 @@ use miniscript::{ hashes::{hash160, ripemd160, sha256}, util::bip32, }, - hash256, MiniscriptKey, ToPublicKey, + descriptor, hash256, Miniscript, MiniscriptKey, Terminal, ToPublicKey, }; -use std::{error, fmt, str}; +use std::{error, fmt, str, sync}; #[derive(Debug)] pub enum DescKeyError { DerivedKeyParsing, + InvalidMultiThresh(usize), + InvalidMultiKeys(usize), } impl std::fmt::Display for DescKeyError { fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { match self { DescKeyError::DerivedKeyParsing => write!(f, "Parsing derived key"), + Self::InvalidMultiThresh(thresh) => write!(f, "Invalid threshold value '{}'. The threshold must be > to 0 and <= to the number of keys.", thresh), + Self::InvalidMultiKeys(n_keys) => write!(f, "Invalid number of keys '{}'. Between 2 and 20 keys must be given to use multiple keys in a specific path.", n_keys), } } } @@ -135,3 +139,91 @@ impl ToPublicKey for DerivedPublicKey { *hash } } + +/// We require the descriptor key to: +/// - Be deriveable (to contain a wildcard) +/// - Be multipath (to contain a step in the derivation path with multiple indexes) +/// - The multipath step to only contain two indexes, 0 and 1. +/// - Be 'signable' by an external signer (to contain an origin) +pub fn is_valid_desc_key(key: &descriptor::DescriptorPublicKey) -> bool { + match *key { + descriptor::DescriptorPublicKey::Single(..) | descriptor::DescriptorPublicKey::XPub(..) => { + false + } + descriptor::DescriptorPublicKey::MultiXPub(ref xpub) => { + let der_paths = xpub.derivation_paths.paths(); + // Rust-miniscript enforces BIP389 which states that all paths must have the same len. + let len = der_paths.get(0).expect("Cannot be empty").len(); + // Technically the xpub could be for the master xpub and not have an origin. But it's + // no unlikely (and easily fixable) while users shooting themselves in the foot by + // forgetting to provide the origin is so likely that it's worth ruling out xpubs + // without origin entirely. + xpub.origin.is_some() + && xpub.wildcard == descriptor::Wildcard::Unhardened + && der_paths.len() == 2 + && der_paths[0][len - 1] == 0.into() + && der_paths[1][len - 1] == 1.into() + } + } +} + +/// The keys in one of the two spending paths of a Liana descriptor. +/// May either be a single key, or between 2 and 20 keys along with a threshold (between two and +/// the number of keys). +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LianaDescKeys { + thresh: Option, + keys: Vec, +} + +impl LianaDescKeys { + pub fn from_single(key: descriptor::DescriptorPublicKey) -> LianaDescKeys { + LianaDescKeys { + thresh: None, + keys: vec![key], + } + } + + pub fn from_multi( + thresh: usize, + keys: Vec, + ) -> Result { + if keys.len() < 2 || keys.len() > 20 { + return Err(DescKeyError::InvalidMultiKeys(keys.len())); + } + if thresh == 0 || thresh > keys.len() { + return Err(DescKeyError::InvalidMultiThresh(thresh)); + } + Ok(LianaDescKeys { + thresh: Some(thresh), + keys, + }) + } + + pub fn keys(&self) -> &Vec { + &self.keys + } + + pub fn into_miniscript( + mut self, + as_hash: bool, + ) -> Miniscript { + if let Some(thresh) = self.thresh { + assert!(self.keys.len() >= 2 && self.keys.len() <= 20); + Miniscript::from_ast(Terminal::Multi(thresh, self.keys)) + .expect("multi is a valid Miniscript") + } else { + assert_eq!(self.keys.len(), 1); + let key = self.keys.pop().expect("Length was just asserted"); + Miniscript::from_ast(Terminal::Check(sync::Arc::from( + Miniscript::from_ast(if as_hash { + Terminal::PkH(key) + } else { + Terminal::PkK(key) + }) + .expect("pk_k is a valid Miniscript"), + ))) + .expect("Well typed") + } + } +} diff --git a/src/descriptors/mod.rs b/src/descriptors/mod.rs index 0981b2a6..d01ae580 100644 --- a/src/descriptors/mod.rs +++ b/src/descriptors/mod.rs @@ -17,7 +17,7 @@ use miniscript::{ use std::{ collections::{BTreeMap, HashMap, HashSet}, convert::TryFrom, - error, fmt, str, sync, + error, fmt, str, }; use serde::{Deserialize, Serialize}; @@ -42,8 +42,6 @@ pub enum LianaDescError { Miniscript(miniscript::Error), IncompatibleDesc, DescKey(DescKeyError), - InvalidMultiThresh(usize), - InvalidMultiKeys(usize), /// Different number of PSBT vs tx inputs, etc.. InsanePsbt, /// Not all inputs' sequence the same, not all inputs signed with the same key, .. @@ -69,10 +67,8 @@ impl std::fmt::Display for LianaDescError { Self::Miniscript(e) => write!(f, "Miniscript error: '{}'.", e), Self::IncompatibleDesc => write!(f, "Descriptor is not compatible."), Self::DescKey(e) => write!(f, "{}", e), - Self::InvalidMultiThresh(thresh) => write!(f, "Invalid threshold value '{}'. The threshold must be > to 0 and <= to the number of keys.", thresh), - Self::InvalidMultiKeys(n_keys) => write!(f, "Invalid number of keys '{}'. Between 2 and 20 keys must be given to use multiple keys in a specific path.", n_keys), Self::InsanePsbt => write!(f, "Analyzed PSBT is empty or malformed."), - Self::InconsistentPsbt => write!(f, "Analyzed PSBT is inconsistent across inputs.") + Self::InconsistentPsbt => write!(f, "Analyzed PSBT is inconsistent across inputs."), } } } @@ -94,33 +90,6 @@ fn csv_check(csv_value: u32) -> Result { } } -// We require the descriptor key to: -// - Be deriveable (to contain a wildcard) -// - Be multipath (to contain a step in the derivation path with multiple indexes) -// - The multipath step to only contain two indexes, 0 and 1. -// - Be 'signable' by an external signer (to contain an origin) -fn is_valid_desc_key(key: &descriptor::DescriptorPublicKey) -> bool { - match *key { - descriptor::DescriptorPublicKey::Single(..) | descriptor::DescriptorPublicKey::XPub(..) => { - false - } - descriptor::DescriptorPublicKey::MultiXPub(ref xpub) => { - let der_paths = xpub.derivation_paths.paths(); - // Rust-miniscript enforces BIP389 which states that all paths must have the same len. - let len = der_paths.get(0).expect("Cannot be empty").len(); - // Technically the xpub could be for the master xpub and not have an origin. But it's - // no unlikely (and easily fixable) while users shooting themselves in the foot by - // forgetting to provide the origin is so likely that it's worth ruling out xpubs - // without origin entirely. - xpub.origin.is_some() - && xpub.wildcard == descriptor::Wildcard::Unhardened - && der_paths.len() == 2 - && der_paths[0][len - 1] == 0.into() - && der_paths[1][len - 1] == 1.into() - } - } -} - // Get the origin of a key in a multipath descriptors. // Returns None if the given key isn't a multixpub. fn key_origin( @@ -132,67 +101,6 @@ fn key_origin( } } -/// The keys in one of the two spending paths of a Liana descriptor. -/// May either be a single key, or between 2 and 20 keys along with a threshold (between two and -/// the number of keys). -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct LianaDescKeys { - thresh: Option, - keys: Vec, -} - -impl LianaDescKeys { - pub fn from_single(key: descriptor::DescriptorPublicKey) -> LianaDescKeys { - LianaDescKeys { - thresh: None, - keys: vec![key], - } - } - - pub fn from_multi( - thresh: usize, - keys: Vec, - ) -> Result { - if keys.len() < 2 || keys.len() > 20 { - return Err(LianaDescError::InvalidMultiKeys(keys.len())); - } - if thresh == 0 || thresh > keys.len() { - return Err(LianaDescError::InvalidMultiThresh(thresh)); - } - Ok(LianaDescKeys { - thresh: Some(thresh), - keys, - }) - } - - pub fn keys(&self) -> &Vec { - &self.keys - } - - pub fn into_miniscript( - mut self, - as_hash: bool, - ) -> Miniscript { - if let Some(thresh) = self.thresh { - assert!(self.keys.len() >= 2 && self.keys.len() <= 20); - Miniscript::from_ast(Terminal::Multi(thresh, self.keys)) - .expect("multi is a valid Miniscript") - } else { - assert_eq!(self.keys.len(), 1); - let key = self.keys.pop().expect("Length was just asserted"); - Miniscript::from_ast(Terminal::Check(sync::Arc::from( - Miniscript::from_ast(if as_hash { - Terminal::PkH(key) - } else { - Terminal::PkK(key) - }) - .expect("pk_k is a valid Miniscript"), - ))) - .expect("Well typed") - } - } -} - /// An [InheritanceDescriptor] that contains multipath keys for (and only for) the receive keychain /// and the change keychain. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]