descriptors: move the LianaDescKey to the keys submodules
This commit is contained in:
parent
7772ae8d8a
commit
c0dd63dfb2
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user