descriptors: move derived keys into their own submodule

This commit is contained in:
Antoine Poinsot 2023-03-23 14:35:15 +01:00
parent 9e78ac7e8d
commit 7772ae8d8a
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
2 changed files with 144 additions and 118 deletions

137
src/descriptors/keys.rs Normal file
View File

@ -0,0 +1,137 @@
use miniscript::{
bitcoin::{
self,
hashes::{hash160, ripemd160, sha256},
util::bip32,
},
hash256, MiniscriptKey, ToPublicKey,
};
use std::{error, fmt, str};
#[derive(Debug)]
pub enum DescKeyError {
DerivedKeyParsing,
}
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"),
}
}
}
impl error::Error for DescKeyError {}
/// A public key used in derived descriptors
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
pub struct DerivedPublicKey {
/// Fingerprint of the master xpub and the derivation index used. We don't use a path
/// since we never derive at more than one depth.
pub origin: (bip32::Fingerprint, bip32::DerivationPath),
/// The actual key
pub key: bitcoin::PublicKey,
}
impl fmt::Display for DerivedPublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (fingerprint, deriv_path) = &self.origin;
write!(f, "[")?;
for byte in fingerprint.as_bytes().iter() {
write!(f, "{:02x}", byte)?;
}
write!(f, "/{}", deriv_path)?;
write!(f, "]{}", self.key)
}
}
impl str::FromStr for DerivedPublicKey {
type Err = DescKeyError;
fn from_str(s: &str) -> Result<DerivedPublicKey, Self::Err> {
// The key is always of the form:
// [ fingerprint / index ]<key>
// 1 + 8 + 1 + 1 + 1 + 66 minimum
if s.len() < 78 {
return Err(DescKeyError::DerivedKeyParsing);
}
// Non-ASCII?
for ch in s.as_bytes() {
if *ch < 20 || *ch > 127 {
return Err(DescKeyError::DerivedKeyParsing);
}
}
if s.chars().next().expect("Size checked above") != '[' {
return Err(DescKeyError::DerivedKeyParsing);
}
let mut parts = s[1..].split(']');
let fg_deriv = parts.next().ok_or(DescKeyError::DerivedKeyParsing)?;
let key_str = parts.next().ok_or(DescKeyError::DerivedKeyParsing)?;
if fg_deriv.len() < 10 {
return Err(DescKeyError::DerivedKeyParsing);
}
let fingerprint = bip32::Fingerprint::from_str(&fg_deriv[..8])
.map_err(|_| DescKeyError::DerivedKeyParsing)?;
let deriv_path = bip32::DerivationPath::from_str(&fg_deriv[9..])
.map_err(|_| DescKeyError::DerivedKeyParsing)?;
if deriv_path.into_iter().any(bip32::ChildNumber::is_hardened) {
return Err(DescKeyError::DerivedKeyParsing);
}
let key =
bitcoin::PublicKey::from_str(key_str).map_err(|_| DescKeyError::DerivedKeyParsing)?;
Ok(DerivedPublicKey {
key,
origin: (fingerprint, deriv_path),
})
}
}
impl MiniscriptKey for DerivedPublicKey {
type Sha256 = sha256::Hash;
type Hash256 = hash256::Hash;
type Ripemd160 = ripemd160::Hash;
type Hash160 = hash160::Hash;
fn is_uncompressed(&self) -> bool {
self.key.is_uncompressed()
}
fn is_x_only_key(&self) -> bool {
false
}
fn num_der_paths(&self) -> usize {
0
}
}
impl ToPublicKey for DerivedPublicKey {
fn to_public_key(&self) -> bitcoin::PublicKey {
self.key
}
fn to_sha256(hash: &sha256::Hash) -> sha256::Hash {
*hash
}
fn to_hash256(hash: &hash256::Hash) -> hash256::Hash {
*hash
}
fn to_ripemd160(hash: &ripemd160::Hash) -> ripemd160::Hash {
*hash
}
fn to_hash160(hash: &hash160::Hash) -> hash160::Hash {
*hash
}
}

View File

@ -2,18 +2,16 @@ use miniscript::{
bitcoin::{
self,
blockdata::transaction::Sequence,
hashes::{hash160, ripemd160, sha256},
secp256k1,
util::{
bip32,
psbt::{Input as PsbtIn, Psbt},
},
},
descriptor, hash256,
descriptor,
miniscript::{decode::Terminal, Miniscript},
policy::{Liftable, Semantic as SemanticPolicy},
translate_hash_clone, ForEachKey, MiniscriptKey, ScriptContext, ToPublicKey, TranslatePk,
Translator,
translate_hash_clone, ForEachKey, ScriptContext, TranslatePk, Translator,
};
use std::{
@ -24,6 +22,9 @@ use std::{
use serde::{Deserialize, Serialize};
pub mod keys;
pub use keys::*;
const WITNESS_FACTOR: usize = 4;
// Convert a size in weight units to a size in virtual bytes, rounding up.
@ -40,7 +41,7 @@ pub enum LianaDescError {
DuplicateKey(Box<descriptor::DescriptorPublicKey>),
Miniscript(miniscript::Error),
IncompatibleDesc,
DerivedKeyParsing,
DescKey(DescKeyError),
InvalidMultiThresh(usize),
InvalidMultiKeys(usize),
/// Different number of PSBT vs tx inputs, etc..
@ -67,7 +68,7 @@ impl std::fmt::Display for LianaDescError {
}
Self::Miniscript(e) => write!(f, "Miniscript error: '{}'.", e),
Self::IncompatibleDesc => write!(f, "Descriptor is not compatible."),
Self::DerivedKeyParsing => write!(f, "Parsing derived key,"),
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."),
@ -78,118 +79,6 @@ impl std::fmt::Display for LianaDescError {
impl error::Error for LianaDescError {}
/// A public key used in derived descriptors
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
pub struct DerivedPublicKey {
/// Fingerprint of the master xpub and the derivation index used. We don't use a path
/// since we never derive at more than one depth.
pub origin: (bip32::Fingerprint, bip32::DerivationPath),
/// The actual key
pub key: bitcoin::PublicKey,
}
impl fmt::Display for DerivedPublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (fingerprint, deriv_path) = &self.origin;
write!(f, "[")?;
for byte in fingerprint.as_bytes().iter() {
write!(f, "{:02x}", byte)?;
}
write!(f, "/{}", deriv_path)?;
write!(f, "]{}", self.key)
}
}
impl str::FromStr for DerivedPublicKey {
type Err = LianaDescError;
fn from_str(s: &str) -> Result<DerivedPublicKey, Self::Err> {
// The key is always of the form:
// [ fingerprint / index ]<key>
// 1 + 8 + 1 + 1 + 1 + 66 minimum
if s.len() < 78 {
return Err(LianaDescError::DerivedKeyParsing);
}
// Non-ASCII?
for ch in s.as_bytes() {
if *ch < 20 || *ch > 127 {
return Err(LianaDescError::DerivedKeyParsing);
}
}
if s.chars().next().expect("Size checked above") != '[' {
return Err(LianaDescError::DerivedKeyParsing);
}
let mut parts = s[1..].split(']');
let fg_deriv = parts.next().ok_or(LianaDescError::DerivedKeyParsing)?;
let key_str = parts.next().ok_or(LianaDescError::DerivedKeyParsing)?;
if fg_deriv.len() < 10 {
return Err(LianaDescError::DerivedKeyParsing);
}
let fingerprint = bip32::Fingerprint::from_str(&fg_deriv[..8])
.map_err(|_| LianaDescError::DerivedKeyParsing)?;
let deriv_path = bip32::DerivationPath::from_str(&fg_deriv[9..])
.map_err(|_| LianaDescError::DerivedKeyParsing)?;
if deriv_path.into_iter().any(bip32::ChildNumber::is_hardened) {
return Err(LianaDescError::DerivedKeyParsing);
}
let key =
bitcoin::PublicKey::from_str(key_str).map_err(|_| LianaDescError::DerivedKeyParsing)?;
Ok(DerivedPublicKey {
key,
origin: (fingerprint, deriv_path),
})
}
}
impl MiniscriptKey for DerivedPublicKey {
type Sha256 = sha256::Hash;
type Hash256 = hash256::Hash;
type Ripemd160 = ripemd160::Hash;
type Hash160 = hash160::Hash;
fn is_uncompressed(&self) -> bool {
self.key.is_uncompressed()
}
fn is_x_only_key(&self) -> bool {
false
}
fn num_der_paths(&self) -> usize {
0
}
}
impl ToPublicKey for DerivedPublicKey {
fn to_public_key(&self) -> bitcoin::PublicKey {
self.key
}
fn to_sha256(hash: &sha256::Hash) -> sha256::Hash {
*hash
}
fn to_hash256(hash: &hash256::Hash) -> hash256::Hash {
*hash
}
fn to_ripemd160(hash: &ripemd160::Hash) -> ripemd160::Hash {
*hash
}
fn to_hash160(hash: &hash160::Hash) -> hash160::Hash {
*hash
}
}
// We require the locktime to:
// - not be disabled
// - be in number of blocks