Skip to content

Commit

Permalink
Introduce Translator trait
Browse files Browse the repository at this point in the history
Overhaul TranslatePk triat

This is merely fixing compiler errors and creating structs wherever
necessary. This in preperation to allow associated enums for Hashes and
Timelocks. Users can then write "hash(H)" and translate API(TBD in
future commits will deal with it)

Also adds fields for Sha256 associated data
  • Loading branch information
sanket1729 committed Jun 6, 2022
1 parent d8c397d commit acbef2f
Show file tree
Hide file tree
Showing 25 changed files with 793 additions and 507 deletions.
4 changes: 2 additions & 2 deletions examples/xpub_descriptors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::str::FromStr;

use miniscript::bitcoin::secp256k1::{Secp256k1, Verification};
use miniscript::bitcoin::{Address, Network};
use miniscript::{Descriptor, DescriptorPublicKey, TranslatePk2};
use miniscript::{Descriptor, DescriptorPublicKey};

const XPUB_1: &str = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB";
const XPUB_2: &str = "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH";
Expand All @@ -42,7 +42,7 @@ fn p2wsh<C: Verification>(secp: &Secp256k1<C>) -> Address {

let address = Descriptor::<DescriptorPublicKey>::from_str(&s)
.unwrap()
.translate_pk2(|xpk| xpk.derive_public_key(secp))
.derived_descriptor(&secp, 0) // dummy index value if it not a wildcard
.unwrap()
.address(Network::Bitcoin)
.unwrap();
Expand Down
18 changes: 7 additions & 11 deletions src/descriptor/bare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use crate::prelude::*;
use crate::util::{varint_len, witness_to_scriptsig};
use crate::{
BareCtx, Error, ForEach, ForEachKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey,
TranslatePk,
TranslatePk, Translator,
};

/// Create a Bare Descriptor. That is descriptor that is
Expand Down Expand Up @@ -180,14 +180,11 @@ where
{
type Output = Bare<Q>;

fn translate_pk<Fpk, Fpkh, E>(&self, mut fpk: Fpk, mut fpkh: Fpkh) -> Result<Self::Output, E>
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
where
Fpk: FnMut(&P) -> Result<Q, E>,
Fpkh: FnMut(&P::Hash) -> Result<Q::Hash, E>,
Q: MiniscriptKey,
T: Translator<P, Q, E>,
{
Ok(Bare::new(self.ms.translate_pk(&mut fpk, &mut fpkh)?)
.expect("Translation cannot fail inside Bare"))
Ok(Bare::new(self.ms.translate_pk(t)?).expect("Translation cannot fail inside Bare"))
}
}

Expand Down Expand Up @@ -345,11 +342,10 @@ where
{
type Output = Pkh<Q>;

fn translate_pk<Fpk, Fpkh, E>(&self, mut fpk: Fpk, _fpkh: Fpkh) -> Result<Self::Output, E>
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
where
Fpk: FnMut(&P) -> Result<Q, E>,
Fpkh: FnMut(&P::Hash) -> Result<Q::Hash, E>,
T: Translator<P, Q, E>,
{
Ok(Pkh::new(fpk(&self.pk)?))
Ok(Pkh::new(t.f_pk(&self.pk)?))
}
}
8 changes: 7 additions & 1 deletion src/descriptor/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::str::FromStr;
use std::error;

use bitcoin::hashes::hex::FromHex;
use bitcoin::hashes::{hash160, Hash, HashEngine};
use bitcoin::hashes::{hash160, sha256, Hash, HashEngine};
use bitcoin::secp256k1::{Secp256k1, Signing, Verification};
use bitcoin::util::bip32;
use bitcoin::{self, XOnlyPublicKey, XpubIdentifier};
Expand Down Expand Up @@ -737,6 +737,7 @@ impl<K: InnerXKey> DescriptorXKey<K> {
impl MiniscriptKey for DescriptorPublicKey {
// This allows us to be able to derive public keys even for PkH s
type Hash = Self;
type Sha256Hash = bitcoin::hashes::sha256::Hash;

fn is_uncompressed(&self) -> bool {
match self {
Expand Down Expand Up @@ -802,6 +803,7 @@ impl fmt::Display for DerivedDescriptorKey {
impl MiniscriptKey for DerivedDescriptorKey {
// This allows us to be able to derive public keys even for PkH s
type Hash = Self;
type Sha256Hash = bitcoin::hashes::sha256::Hash;

fn is_uncompressed(&self) -> bool {
self.key.is_uncompressed()
Expand All @@ -825,6 +827,10 @@ impl ToPublicKey for DerivedDescriptorKey {
fn hash_to_hash160(hash: &Self) -> hash160::Hash {
hash.to_public_key().to_pubkeyhash()
}

fn to_sha256(hash: &sha256::Hash) -> sha256::Hash {
*hash
}
}

#[cfg(test)]
Expand Down
154 changes: 106 additions & 48 deletions src/descriptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use core::ops::Range;
use core::str::{self, FromStr};

use bitcoin::blockdata::witness::Witness;
use bitcoin::hashes::sha256;
use bitcoin::util::address::WitnessVersion;
use bitcoin::{self, secp256k1, Address, Network, Script, TxIn};
use sync::Arc;
Expand All @@ -36,8 +37,8 @@ use self::checksum::verify_checksum;
use crate::miniscript::{Legacy, Miniscript, Segwitv0};
use crate::prelude::*;
use crate::{
expression, miniscript, BareCtx, Error, ForEach, ForEachKey, MiniscriptKey, Satisfier,
ToPublicKey, TranslatePk, TranslatePk2,
expression, miniscript, BareCtx, Error, ForEach, ForEachKey, MiniscriptKey, PkTranslator,
Satisfier, ToPublicKey, TranslatePk, Translator,
};

mod bare;
Expand Down Expand Up @@ -475,25 +476,19 @@ where
Q: MiniscriptKey,
{
type Output = Descriptor<Q>;

/// Converts a descriptor using abstract keys to one using specific keys.
///
/// # Panics
///
/// If `fpk` returns an uncompressed key when converting to a Segwit descriptor.
/// To prevent this panic, ensure `fpk` returns an error in this case instead.
fn translate_pk<Fpk, Fpkh, E>(&self, mut fpk: Fpk, mut fpkh: Fpkh) -> Result<Descriptor<Q>, E>
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
where
Fpk: FnMut(&P) -> Result<Q, E>,
Fpkh: FnMut(&P::Hash) -> Result<Q::Hash, E>,
Q: MiniscriptKey,
T: Translator<P, Q, E>,
{
let desc = match *self {
Descriptor::Bare(ref bare) => Descriptor::Bare(bare.translate_pk(&mut fpk, &mut fpkh)?),
Descriptor::Pkh(ref pk) => Descriptor::Pkh(pk.translate_pk(&mut fpk, &mut fpkh)?),
Descriptor::Wpkh(ref pk) => Descriptor::Wpkh(pk.translate_pk(&mut fpk, &mut fpkh)?),
Descriptor::Sh(ref sh) => Descriptor::Sh(sh.translate_pk(&mut fpk, &mut fpkh)?),
Descriptor::Wsh(ref wsh) => Descriptor::Wsh(wsh.translate_pk(&mut fpk, &mut fpkh)?),
Descriptor::Tr(ref tr) => Descriptor::Tr(tr.translate_pk(&mut fpk, &mut fpkh)?),
Descriptor::Bare(ref bare) => Descriptor::Bare(bare.translate_pk(t)?),
Descriptor::Pkh(ref pk) => Descriptor::Pkh(pk.translate_pk(t)?),
Descriptor::Wpkh(ref pk) => Descriptor::Wpkh(pk.translate_pk(t)?),
Descriptor::Sh(ref sh) => Descriptor::Sh(sh.translate_pk(t)?),
Descriptor::Wsh(ref wsh) => Descriptor::Wsh(wsh.translate_pk(t)?),
Descriptor::Tr(ref tr) => Descriptor::Tr(tr.translate_pk(t)?),
};
Ok(desc)
}
Expand Down Expand Up @@ -529,7 +524,19 @@ impl Descriptor<DescriptorPublicKey> {
/// In most cases, you would want to use [`Self::derived_descriptor`] directly to obtain
/// a [`Descriptor<bitcoin::PublicKey>`]
pub fn derive(&self, index: u32) -> Descriptor<DerivedDescriptorKey> {
self.translate_pk2_infallible(|pk| pk.clone().derive(index))
struct Derivator(u32);

impl PkTranslator<DescriptorPublicKey, DerivedDescriptorKey, ()> for Derivator {
fn f_pk(&mut self, pk: &DescriptorPublicKey) -> Result<DerivedDescriptorKey, ()> {
Ok(pk.clone().derive(self.0))
}

fn f_pkh(&mut self, pkh: &DescriptorPublicKey) -> Result<DerivedDescriptorKey, ()> {
Ok(pkh.clone().derive(self.0))
}
}
self.translate_pk(&mut Derivator(index))
.expect("BIP 32 key index substitution cannot fail")
}

/// Derive a [`Descriptor`] with a concrete [`bitcoin::PublicKey`] at a given index
Expand Down Expand Up @@ -561,9 +568,28 @@ impl Descriptor<DescriptorPublicKey> {
secp: &secp256k1::Secp256k1<C>,
index: u32,
) -> Result<Descriptor<bitcoin::PublicKey>, ConversionError> {
let derived = self
.derive(index)
.translate_pk2(|xpk| xpk.derive_public_key(secp))?;
struct Derivator<'a, C: secp256k1::Verification>(&'a secp256k1::Secp256k1<C>);

impl<'a, C: secp256k1::Verification>
PkTranslator<DerivedDescriptorKey, bitcoin::PublicKey, ConversionError>
for Derivator<'a, C>
{
fn f_pk(
&mut self,
pk: &DerivedDescriptorKey,
) -> Result<bitcoin::PublicKey, ConversionError> {
pk.derive_public_key(&self.0)
}

fn f_pkh(
&mut self,
pkh: &DerivedDescriptorKey,
) -> Result<bitcoin::hashes::hash160::Hash, ConversionError> {
Ok(pkh.derive_public_key(&self.0)?.to_pubkeyhash())
}
}

let derived = self.derive(index).translate_pk(&mut Derivator(secp))?;
Ok(derived)
}

Expand All @@ -575,39 +601,79 @@ impl Descriptor<DescriptorPublicKey> {
secp: &secp256k1::Secp256k1<C>,
s: &str,
) -> Result<(Descriptor<DescriptorPublicKey>, KeyMap), Error> {
let parse_key = |s: &String,
key_map: &mut KeyMap|
-> Result<DescriptorPublicKey, DescriptorKeyParseError> {
fn parse_key<C: secp256k1::Signing>(
s: &String,
key_map: &mut KeyMap,
secp: &secp256k1::Secp256k1<C>,
) -> Result<DescriptorPublicKey, Error> {
let (public_key, secret_key) = match DescriptorSecretKey::from_str(s) {
Ok(sk) => (sk.to_public(secp)?, Some(sk)),
Err(_) => (DescriptorPublicKey::from_str(s)?, None),
Ok(sk) => (
sk.to_public(secp)
.map_err(|e| Error::Unexpected(e.to_string()))?,
Some(sk),
),
Err(_) => (
DescriptorPublicKey::from_str(s)
.map_err(|e| Error::Unexpected(e.to_string()))?,
None,
),
};

if let Some(secret_key) = secret_key {
key_map.insert(public_key.clone(), secret_key);
}

Ok(public_key)
};
}

let mut keymap_pk = KeyMapWrapper(HashMap::new(), secp);

let mut keymap_pk = KeyMap::new();
let mut keymap_pkh = KeyMap::new();
struct KeyMapWrapper<'a, C: secp256k1::Signing>(KeyMap, &'a secp256k1::Secp256k1<C>);

impl<'a, C: secp256k1::Signing> Translator<String, DescriptorPublicKey, Error>
for KeyMapWrapper<'a, C>
{
fn f_pk(&mut self, pk: &String) -> Result<DescriptorPublicKey, Error> {
parse_key(pk, &mut self.0, self.1)
}

fn f_pkh(&mut self, pkh: &String) -> Result<DescriptorPublicKey, Error> {
parse_key(pkh, &mut self.0, self.1)
}

fn f_sha256(&mut self, sha256: &String) -> Result<sha256::Hash, Error> {
let hash =
sha256::Hash::from_str(sha256).map_err(|e| Error::Unexpected(e.to_string()))?;
Ok(hash)
}
}

let descriptor = Descriptor::<String>::from_str(s)?;
let descriptor = descriptor
.translate_pk(
|pk| parse_key(pk, &mut keymap_pk),
|pkh| parse_key(pkh, &mut keymap_pkh),
)
.translate_pk(&mut keymap_pk)
.map_err(|e| Error::Unexpected(e.to_string()))?;

keymap_pk.extend(keymap_pkh.into_iter());

Ok((descriptor, keymap_pk))
Ok((descriptor, keymap_pk.0))
}

/// Serialize a descriptor to string with its secret keys
pub fn to_string_with_secret(&self, key_map: &KeyMap) -> String {
struct KeyMapLookUp<'a>(&'a KeyMap);

impl<'a> Translator<DescriptorPublicKey, String, ()> for KeyMapLookUp<'a> {
fn f_pk(&mut self, pk: &DescriptorPublicKey) -> Result<String, ()> {
key_to_string(pk, self.0)
}

fn f_pkh(&mut self, pkh: &DescriptorPublicKey) -> Result<String, ()> {
key_to_string(pkh, self.0)
}

fn f_sha256(&mut self, sha256: &sha256::Hash) -> Result<String, ()> {
Ok(sha256.to_string())
}
}

fn key_to_string(pk: &DescriptorPublicKey, key_map: &KeyMap) -> Result<String, ()> {
Ok(match key_map.get(pk) {
Some(secret) => secret.to_string(),
Expand All @@ -616,10 +682,7 @@ impl Descriptor<DescriptorPublicKey> {
}

let descriptor = self
.translate_pk::<_, _, ()>(
|pk| key_to_string(pk, key_map),
|pkh| key_to_string(pkh, key_map),
)
.translate_pk(&mut KeyMapLookUp(key_map))
.expect("Translation to string cannot fail");

descriptor.to_string()
Expand Down Expand Up @@ -731,7 +794,7 @@ mod tests {
use crate::descriptor::{DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey, SinglePub};
#[cfg(feature = "compiler")]
use crate::policy;
use crate::{hex_script, Descriptor, DummyKey, Error, Miniscript, Satisfier, TranslatePk2};
use crate::{hex_script, Descriptor, DummyKey, Error, Miniscript, Satisfier};

type StdDescriptor = Descriptor<PublicKey>;
const TEST_PK: &'static str =
Expand Down Expand Up @@ -1468,19 +1531,14 @@ mod tests {
assert_eq!(desc_one.to_string(), raw_desc_one);
assert_eq!(desc_two.to_string(), raw_desc_two);

// Derive a child in case the descriptor is ranged. If it's not this won't have any
// effect
let desc_one = desc_one.derive(index);
let desc_two = desc_two.derive(index);

// Same address
let addr_one = desc_one
.translate_pk2(|xpk| xpk.derive_public_key(&secp_ctx))
.derived_descriptor(&secp_ctx, index)
.unwrap()
.address(bitcoin::Network::Bitcoin)
.unwrap();
let addr_two = desc_two
.translate_pk2(|xpk| xpk.derive_public_key(&secp_ctx))
.derived_descriptor(&secp_ctx, index)
.unwrap()
.address(bitcoin::Network::Bitcoin)
.unwrap();
Expand Down
18 changes: 8 additions & 10 deletions src/descriptor/segwitv0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::prelude::*;
use crate::util::varint_len;
use crate::{
Error, ForEach, ForEachKey, Miniscript, MiniscriptKey, Satisfier, Segwitv0, ToPublicKey,
TranslatePk,
TranslatePk, Translator,
};
/// A Segwitv0 wsh descriptor
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
Expand Down Expand Up @@ -265,14 +265,13 @@ where
{
type Output = Wsh<Q>;

fn translate_pk<Fpk, Fpkh, E>(&self, mut fpk: Fpk, mut fpkh: Fpkh) -> Result<Self::Output, E>
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
where
Fpk: FnMut(&P) -> Result<Q, E>,
Fpkh: FnMut(&P::Hash) -> Result<Q::Hash, E>,
T: Translator<P, Q, E>,
{
let inner = match self.inner {
WshInner::SortedMulti(ref smv) => WshInner::SortedMulti(smv.translate_pk(&mut fpk)?),
WshInner::Ms(ref ms) => WshInner::Ms(ms.translate_pk(&mut fpk, &mut fpkh)?),
WshInner::SortedMulti(ref smv) => WshInner::SortedMulti(smv.translate_pk(t)?),
WshInner::Ms(ref ms) => WshInner::Ms(ms.translate_pk(t)?),
};
Ok(Wsh { inner })
}
Expand Down Expand Up @@ -456,11 +455,10 @@ where
{
type Output = Wpkh<Q>;

fn translate_pk<Fpk, Fpkh, E>(&self, mut fpk: Fpk, _fpkh: Fpkh) -> Result<Self::Output, E>
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
where
Fpk: FnMut(&P) -> Result<Q, E>,
Fpkh: FnMut(&P::Hash) -> Result<Q::Hash, E>,
T: Translator<P, Q, E>,
{
Ok(Wpkh::new(fpk(&self.pk)?).expect("Uncompressed keys in Wpkh"))
Ok(Wpkh::new(t.f_pk(&self.pk)?).expect("Uncompressed keys in Wpkh"))
}
}
Loading

0 comments on commit acbef2f

Please sign in to comment.