descriptors: move derived keys into their own submodule
This commit is contained in:
parent
9e78ac7e8d
commit
7772ae8d8a
137
src/descriptors/keys.rs
Normal file
137
src/descriptors/keys.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user