descriptors: move the LianaDescKey to the keys submodules

This commit is contained in:
Antoine Poinsot 2023-03-23 14:40:45 +01:00
parent 7772ae8d8a
commit c0dd63dfb2
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
2 changed files with 96 additions and 96 deletions

View File

@ -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<usize>,
keys: Vec<descriptor::DescriptorPublicKey>,
}
impl LianaDescKeys {
pub fn from_single(key: descriptor::DescriptorPublicKey) -> LianaDescKeys {
LianaDescKeys {
thresh: None,
keys: vec![key],
}
}
pub fn from_multi(
thresh: usize,
keys: Vec<descriptor::DescriptorPublicKey>,
) -> Result<LianaDescKeys, DescKeyError> {
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<descriptor::DescriptorPublicKey> {
&self.keys
}
pub fn into_miniscript(
mut self,
as_hash: bool,
) -> Miniscript<descriptor::DescriptorPublicKey, miniscript::Segwitv0> {
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")
}
}
}

View File

@ -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<u16, LianaDescError> {
}
}
// 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<usize>,
keys: Vec<descriptor::DescriptorPublicKey>,
}
impl LianaDescKeys {
pub fn from_single(key: descriptor::DescriptorPublicKey) -> LianaDescKeys {
LianaDescKeys {
thresh: None,
keys: vec![key],
}
}
pub fn from_multi(
thresh: usize,
keys: Vec<descriptor::DescriptorPublicKey>,
) -> Result<LianaDescKeys, LianaDescError> {
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<descriptor::DescriptorPublicKey> {
&self.keys
}
pub fn into_miniscript(
mut self,
as_hash: bool,
) -> Miniscript<descriptor::DescriptorPublicKey, miniscript::Segwitv0> {
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)]