[descriptor] Add descriptor templates, add DerivableKey

This commit is contained in:
Alekos Filini
2020-09-22 16:12:09 +02:00
parent c51ba4a99f
commit c93cd1414a
7 changed files with 553 additions and 44 deletions

View File

@@ -34,33 +34,45 @@ use miniscript::ScriptContext;
use bip39::{Mnemonic, Seed};
use super::{any_network, DescriptorKey, KeyError, ToDescriptorKey};
use super::{any_network, DerivableKey, DescriptorKey, KeyError};
pub type MnemonicWithPassphrase = (Mnemonic, Option<String>);
impl<Ctx: ScriptContext> ToDescriptorKey<Ctx> for (Seed, bip32::DerivationPath) {
fn to_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
let xprv = bip32::ExtendedPrivKey::new_master(Network::Bitcoin, &self.0.as_bytes())?;
let descriptor_key = (xprv, self.1).to_descriptor_key()?;
impl<Ctx: ScriptContext> DerivableKey<Ctx> for Seed {
fn add_metadata(
self,
source: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
derivation_path: bip32::DerivationPath,
) -> Result<DescriptorKey<Ctx>, KeyError> {
let xprv = bip32::ExtendedPrivKey::new_master(Network::Bitcoin, &self.as_bytes())?;
let descriptor_key = xprv.add_metadata(source, derivation_path)?;
// here we must choose one network to build the xpub, but since the bip39 standard doesn't
// encode the network the xpub we create is actually valid everywhere. so we override the
// encode the network, the xpub we create is actually valid everywhere. so we override the
// valid networks with `any_network()`.
Ok(descriptor_key.override_valid_networks(any_network()))
}
}
impl<Ctx: ScriptContext> ToDescriptorKey<Ctx> for (MnemonicWithPassphrase, bip32::DerivationPath) {
fn to_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
let (mnemonic, passphrase) = self.0;
impl<Ctx: ScriptContext> DerivableKey<Ctx> for MnemonicWithPassphrase {
fn add_metadata(
self,
source: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
derivation_path: bip32::DerivationPath,
) -> Result<DescriptorKey<Ctx>, KeyError> {
let (mnemonic, passphrase) = self;
let seed = Seed::new(&mnemonic, passphrase.as_deref().unwrap_or(""));
(seed, self.1).to_descriptor_key()
seed.add_metadata(source, derivation_path)
}
}
impl<Ctx: ScriptContext> ToDescriptorKey<Ctx> for (Mnemonic, bip32::DerivationPath) {
fn to_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
((self.0, None), self.1).to_descriptor_key()
impl<Ctx: ScriptContext> DerivableKey<Ctx> for Mnemonic {
fn add_metadata(
self,
source: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
derivation_path: bip32::DerivationPath,
) -> Result<DescriptorKey<Ctx>, KeyError> {
(self, None).add_metadata(source, derivation_path)
}
}

View File

@@ -31,7 +31,8 @@ use std::marker::PhantomData;
use bitcoin::util::bip32;
use bitcoin::{Network, PrivateKey, PublicKey};
use miniscript::descriptor::{DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey, KeyMap};
pub use miniscript::descriptor::{DescriptorPublicKey, DescriptorSecretKey};
use miniscript::descriptor::{DescriptorXKey, KeyMap};
pub use miniscript::ScriptContext;
use miniscript::{Miniscript, Terminal};
@@ -167,6 +168,10 @@ impl<Ctx: ScriptContext + 'static> ExtScriptContext for Ctx {
/// single `Ctx`), the "specialized" trait can be implemented to make the compiler handle the type
/// checking.
///
/// Keys also have control over the networks they support: constructing the return object with
/// [`DescriptorKey::from_public`] or [`DescriptorKey::from_secret`] allows to specify a set of
/// [`ValidNetworks`].
///
/// ## Examples
///
/// Key type valid in any context:
@@ -187,6 +192,24 @@ impl<Ctx: ScriptContext + 'static> ExtScriptContext for Ctx {
/// }
/// ```
///
/// Key type that is only valid on mainnet:
///
/// ```
/// use bdk::bitcoin::PublicKey;
///
/// use bdk::keys::{mainnet_network, ScriptContext, ToDescriptorKey, DescriptorKey, DescriptorPublicKey, KeyError};
///
/// pub struct MyKeyType {
/// pubkey: PublicKey,
/// }
///
/// impl<Ctx: ScriptContext> ToDescriptorKey<Ctx> for MyKeyType {
/// fn to_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
/// Ok(DescriptorKey::from_public(DescriptorPublicKey::PubKey(self.pubkey), mainnet_network()))
/// }
/// }
/// ```
///
/// Key type that internally encodes in which context it's valid. The context is checked at runtime:
///
/// ```
@@ -246,6 +269,77 @@ pub trait ToDescriptorKey<Ctx: ScriptContext>: Sized {
fn to_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError>;
}
/// Trait for keys that can be derived.
///
/// When extra metadata are provided, a [`DerivableKey`] can be transofrmed into a
/// [`DescriptorKey`]: the trait [`ToDescriptorKey`] is automatically implemented
/// for `(DerivableKey, DerivationPath)` and
/// `(DerivableKey, (Fingerprint, DerivationPath), DerivationPath)` tuples.
///
/// For key types that don't encode any indication about the path to use (like bip39), it's
/// generally recommended to implemented this trait instead of [`ToDescriptorKey`]. The same
/// rules regarding script context and valid networks apply.
///
/// [`DerivationPath`]: (bip32::DerivationPath)
pub trait DerivableKey<Ctx: ScriptContext> {
/// Add a extra metadata, consume `self` and turn it into a [`DescriptorKey`]
fn add_metadata(
self,
source: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
derivation_path: bip32::DerivationPath,
) -> Result<DescriptorKey<Ctx>, KeyError>;
}
impl<Ctx: ScriptContext> DerivableKey<Ctx> for bip32::ExtendedPubKey {
fn add_metadata(
self,
source: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
derivation_path: bip32::DerivationPath,
) -> Result<DescriptorKey<Ctx>, KeyError> {
DescriptorPublicKey::XPub(DescriptorXKey {
source,
xkey: self,
derivation_path,
is_wildcard: true,
})
.to_descriptor_key()
}
}
impl<Ctx: ScriptContext> DerivableKey<Ctx> for bip32::ExtendedPrivKey {
fn add_metadata(
self,
source: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
derivation_path: bip32::DerivationPath,
) -> Result<DescriptorKey<Ctx>, KeyError> {
DescriptorSecretKey::XPrv(DescriptorXKey {
source,
xkey: self,
derivation_path,
is_wildcard: true,
})
.to_descriptor_key()
}
}
impl<Ctx: ScriptContext, T: DerivableKey<Ctx>> ToDescriptorKey<Ctx> for (T, bip32::DerivationPath) {
fn to_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
self.0.add_metadata(None, self.1)
}
}
impl<Ctx: ScriptContext, T: DerivableKey<Ctx>> ToDescriptorKey<Ctx>
for (
T,
(bip32::Fingerprint, bip32::DerivationPath),
bip32::DerivationPath,
)
{
fn to_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
self.0.add_metadata(Some(self.1), self.2)
}
}
// Used internally by `bdk::fragment!` to build `pk_k()` fragments
#[doc(hidden)]
pub fn make_pk<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
@@ -320,19 +414,6 @@ impl<Ctx: ScriptContext> ToDescriptorKey<Ctx> for PublicKey {
}
}
/// This assumes that "is_wildcard" is true, since this is generally the way extended keys are used
impl<Ctx: ScriptContext> ToDescriptorKey<Ctx> for (bip32::ExtendedPubKey, bip32::DerivationPath) {
fn to_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
DescriptorPublicKey::XPub(DescriptorXKey {
source: None,
xkey: self.0,
derivation_path: self.1,
is_wildcard: true,
})
.to_descriptor_key()
}
}
impl<Ctx: ScriptContext> ToDescriptorKey<Ctx> for DescriptorSecretKey {
fn to_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
let networks = match self {
@@ -355,19 +436,7 @@ impl<Ctx: ScriptContext> ToDescriptorKey<Ctx> for PrivateKey {
}
}
/// This assumes that "is_wildcard" is true, since this is generally the way extended keys are used
impl<Ctx: ScriptContext> ToDescriptorKey<Ctx> for (bip32::ExtendedPrivKey, bip32::DerivationPath) {
fn to_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
DescriptorSecretKey::XPrv(DescriptorXKey {
source: None,
xkey: self.0,
derivation_path: self.1,
is_wildcard: true,
})
.to_descriptor_key()
}
}
/// Errors thrown while working with [`keys`](crate::keys)
#[derive(Debug)]
pub enum KeyError {
InvalidScriptContext,