Skip to content

POC: Add custom hash types to try and simplify all the associated hash type stuff #621

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
Closed
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ secp256k1 = {version = "0.27.0", features = ["rand-std"]}
name = "htlc"
required-features = ["std", "compiler"]

[[example]]
name = "htlc-string"
required-features = ["std", "compiler"]


[[example]]
name = "parse"
required-features = ["std"]
Expand Down
10 changes: 0 additions & 10 deletions bitcoind-tests/tests/setup/test_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,6 @@ impl<'a> Translator<String, DescriptorPublicKey, ()> for StrDescPubKeyTranslator
}
}

fn sha256(&mut self, sha256: &String) -> Result<sha256::Hash, ()> {
let sha = sha256::Hash::from_str(sha256).unwrap();
Ok(sha)
}

fn hash256(&mut self, hash256: &String) -> Result<hash256::Hash, ()> {
let hash256 = hash256::Hash::from_str(hash256).unwrap();
Ok(hash256)
Expand Down Expand Up @@ -239,11 +234,6 @@ impl<'a> Translator<String, DescriptorPublicKey, ()> for StrTranslatorLoose<'a>
}
}

fn sha256(&mut self, sha256: &String) -> Result<sha256::Hash, ()> {
let sha = sha256::Hash::from_str(sha256).unwrap();
Ok(sha)
}

fn hash256(&mut self, hash256: &String) -> Result<hash256::Hash, ()> {
let hash256 = hash256::Hash::from_str(hash256).unwrap();
Ok(hash256)
Expand Down
32 changes: 32 additions & 0 deletions examples/htlc-string.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Written by Thomas Eizinger <[email protected]>
// SPDX-License-Identifier: CC0-1.0

//! Example: Create an HTLC with miniscript using the policy compiler

use std::str::FromStr;

use miniscript::descriptor::Wsh;
use miniscript::policy::{Concrete, Liftable};

fn main() {
// HTLC policy with 10:1 odds for happy (co-operative) case compared to uncooperative case.
// let policy = Concrete::<String>::from_str(&format!("or(10@and(sha256(secret_hash),pk(redeem)),1@and(older(expiry),pk(refund)))")).unwrap();

let policy = Concrete::<String>::from_str(&format!(
"or(10@and(sha256(secret_hash),pk(redeem)),1@and(older(4444),pk(refund)))"
))
.unwrap();

let descriptor = Wsh::new(
policy
.compile()
.expect("policy compilation only fails on resource limits or mixed timelocks"),
)
.expect("resource limits");

println!("descriptor: {}", descriptor);
println!("lifted : {}", descriptor.lift().unwrap());
// println!("{}", descriptor.script_pubkey());
// println!("{}", descriptor.inner_script());
// println!("{}", descriptor.address(Network::Bitcoin));
}
10 changes: 1 addition & 9 deletions src/descriptor/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::error;
use bitcoin::bip32;
use bitcoin::hash_types::XpubIdentifier;
use bitcoin::hashes::hex::FromHex;
use bitcoin::hashes::{hash160, ripemd160, sha256, Hash, HashEngine};
use bitcoin::hashes::{hash160, ripemd160, Hash, HashEngine};
use bitcoin::key::XOnlyPublicKey;
use bitcoin::secp256k1::{Secp256k1, Signing, Verification};

Expand Down Expand Up @@ -974,7 +974,6 @@ impl<K: InnerXKey> DescriptorXKey<K> {
}

impl MiniscriptKey for DescriptorPublicKey {
type Sha256 = sha256::Hash;
type Hash256 = hash256::Hash;
type Ripemd160 = ripemd160::Hash;
type Hash160 = hash160::Hash;
Expand Down Expand Up @@ -1089,15 +1088,12 @@ impl fmt::Display for DefiniteDescriptorKey {
}

impl MiniscriptKey for DefiniteDescriptorKey {
type Sha256 = sha256::Hash;
type Hash256 = hash256::Hash;
type Ripemd160 = ripemd160::Hash;
type Hash160 = hash160::Hash;

fn is_uncompressed(&self) -> bool { self.0.is_uncompressed() }

fn is_x_only_key(&self) -> bool { self.0.is_x_only_key() }

fn num_der_paths(&self) -> usize { self.0.num_der_paths() }
}

Expand All @@ -1107,12 +1103,8 @@ impl ToPublicKey for DefiniteDescriptorKey {
self.derive_public_key(&secp).unwrap()
}

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 }
}

Expand Down
12 changes: 1 addition & 11 deletions src/descriptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use core::ops::Range;
use core::str::{self, FromStr};

use bitcoin::address::WitnessVersion;
use bitcoin::hashes::{hash160, ripemd160, sha256};
use bitcoin::hashes::{hash160, ripemd160};
use bitcoin::{secp256k1, Address, Network, Script, ScriptBuf, TxIn, Witness};
use sync::Arc;

Expand Down Expand Up @@ -700,12 +700,6 @@ impl Descriptor<DescriptorPublicKey> {
parse_key(pk, &mut self.0, self.1)
}

fn 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)
}

fn hash256(&mut self, hash256: &String) -> Result<hash256::Hash, Error> {
let hash = hash256::Hash::from_str(hash256)
.map_err(|e| Error::Unexpected(e.to_string()))?;
Expand Down Expand Up @@ -745,10 +739,6 @@ impl Descriptor<DescriptorPublicKey> {
key_to_string(pk, self.0)
}

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

fn hash256(&mut self, hash256: &hash256::Hash) -> Result<String, ()> {
Ok(hash256.to_string())
}
Expand Down
10 changes: 9 additions & 1 deletion src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,18 @@ impl From<bitcoin::key::XOnlyPublicKey> for BitcoinKey {
}

impl MiniscriptKey for BitcoinKey {
type Sha256 = sha256::Hash;
type Hash256 = hash256::Hash;
type Ripemd160 = ripemd160::Hash;
type Hash160 = hash160::Hash;

fn is_uncompressed(&self) -> bool {
match *self {
BitcoinKey::Fullkey(pk) => !pk.compressed,
BitcoinKey::XOnlyPublicKey(_) => false,
}
}
fn is_x_only_key(&self) -> bool { false }
fn num_der_paths(&self) -> usize { 0 }
}

impl<'txin> Interpreter<'txin> {
Expand Down
8 changes: 7 additions & 1 deletion src/interpreter/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,14 @@ impl<'txin> Stack<'txin> {
/// `SIZE 32 EQUALVERIFY SHA256 h EQUAL`
pub(super) fn evaluate_sha256(
&mut self,
hash: &sha256::Hash,
hash: &crate::miniscript::Sha256,
) -> Option<Result<SatisfiedConstraint, Error>> {
use crate::miniscript::Sha256;
let hash = match hash {
Sha256::HumanReadable(..) => return None,
Sha256::Hash(hash) => hash,
};

if let Some(Element::Push(preimage)) = self.pop() {
if preimage.len() != 32 {
return Some(Err(Error::HashPreimageLengthMismatch));
Expand Down
99 changes: 45 additions & 54 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,110 +148,108 @@ pub use crate::miniscript::satisfy::{Preimage32, Satisfier};
pub use crate::miniscript::{hash256, Miniscript};
use crate::prelude::*;

///Public key trait which can be converted to Hash type
/// Trait representing a key which can be converted to a hash type.
pub trait MiniscriptKey: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Hash {
/// Returns true if the pubkey is uncompressed. Defaults to `false`.
fn is_uncompressed(&self) -> bool { false }

/// Returns true if the pubkey is an x-only pubkey. Defaults to `false`.
// This is required to know what in DescriptorPublicKey to know whether the inner
// key in allowed in descriptor context
fn is_x_only_key(&self) -> bool { false }

/// Returns the number of different derivation paths in this key. Only >1 for keys
/// in BIP389 multipath descriptors.
fn num_der_paths(&self) -> usize { 0 }

/// The associated [`bitcoin::hashes::sha256::Hash`] for this [`MiniscriptKey`], used in the
/// sha256 fragment.
type Sha256: Clone + Eq + Ord + fmt::Display + fmt::Debug + hash::Hash;

/// The associated [`miniscript::hash256::Hash`] for this [`MiniscriptKey`], used in the
/// hash256 fragment.
/// The type used in the hash256 fragment.
type Hash256: Clone + Eq + Ord + fmt::Display + fmt::Debug + hash::Hash;

/// The associated [`bitcoin::hashes::ripemd160::Hash`] for this [`MiniscriptKey`] type, used
/// in the ripemd160 fragment.
/// The type used in the ripemd160 fragment.
type Ripemd160: Clone + Eq + Ord + fmt::Display + fmt::Debug + hash::Hash;

/// The associated [`bitcoin::hashes::hash160::Hash`] for this [`MiniscriptKey`] type, used in
/// the hash160 fragment.
/// The type used in the hash160 fragment.
type Hash160: Clone + Eq + Ord + fmt::Display + fmt::Debug + hash::Hash;

/// Returns true if the key is serialized uncompressed, defaults to `false`.
fn is_uncompressed(&self) -> bool;

/// Returns true if the key is an x-only pubkey, defaults to `false`.
fn is_x_only_key(&self) -> bool;

/// Returns the number of different derivation paths in this key, defaults to `0`.
///
/// Only >1 for keys in BIP389 multipath descriptors.
fn num_der_paths(&self) -> usize;
}

impl MiniscriptKey for bitcoin::secp256k1::PublicKey {
type Sha256 = sha256::Hash;
type Hash256 = hash256::Hash;
type Ripemd160 = ripemd160::Hash;
type Hash160 = hash160::Hash;

fn is_uncompressed(&self) -> bool { false }
fn is_x_only_key(&self) -> bool { false }
fn num_der_paths(&self) -> usize { 0 }
}

impl MiniscriptKey for bitcoin::PublicKey {
/// Returns the compressed-ness of the underlying secp256k1 key.
fn is_uncompressed(&self) -> bool { !self.compressed }

type Sha256 = sha256::Hash;
type Hash256 = hash256::Hash;
type Ripemd160 = ripemd160::Hash;
type Hash160 = hash160::Hash;

fn is_uncompressed(&self) -> bool { !self.compressed }
fn is_x_only_key(&self) -> bool { false }
fn num_der_paths(&self) -> usize { 0 }
}

impl MiniscriptKey for bitcoin::secp256k1::XOnlyPublicKey {
type Sha256 = sha256::Hash;
type Hash256 = hash256::Hash;
type Ripemd160 = ripemd160::Hash;
type Hash160 = hash160::Hash;

fn is_uncompressed(&self) -> bool { false }
fn is_x_only_key(&self) -> bool { true }
fn num_der_paths(&self) -> usize { 0 }
}

impl MiniscriptKey for String {
type Sha256 = String; // specify hashes as string
type Hash256 = String;
type Ripemd160 = String;
type Hash160 = String;

fn is_uncompressed(&self) -> bool { false }
fn is_x_only_key(&self) -> bool { false }
fn num_der_paths(&self) -> usize { 0 }
}

/// Trait describing public key types which can be converted to bitcoin pubkeys
/// Trait describing key types that can be converted to bitcoin public keys.
pub trait ToPublicKey: MiniscriptKey {
/// Converts an object to a public key
/// Converts key to a public key.
fn to_public_key(&self) -> bitcoin::PublicKey;

/// Convert an object to x-only pubkey
/// Converts key to an x-only public key.
fn to_x_only_pubkey(&self) -> bitcoin::secp256k1::XOnlyPublicKey {
let pk = self.to_public_key();
bitcoin::secp256k1::XOnlyPublicKey::from(pk.inner)
}

/// Obtain the public key hash for this MiniscriptKey
/// Expects an argument to specify the signature type.
/// This would determine whether to serialize the key as 32 byte x-only pubkey
/// or regular public key when computing the hash160
/// Obtains the pubkey hash for this key (as a `MiniscriptKey`).
///
/// Expects an argument to specify the signature type. This determines whether to serialize
/// the key as 32 byte x-only pubkey or regular public key when computing the hash160.
fn to_pubkeyhash(&self, sig_type: SigType) -> hash160::Hash {
match sig_type {
SigType::Ecdsa => hash160::Hash::hash(&self.to_public_key().to_bytes()),
SigType::Schnorr => hash160::Hash::hash(&self.to_x_only_pubkey().serialize()),
}
}

/// Converts the generic associated [`MiniscriptKey::Sha256`] to [`sha256::Hash`]
fn to_sha256(hash: &<Self as MiniscriptKey>::Sha256) -> sha256::Hash;

/// Converts the generic associated [`MiniscriptKey::Hash256`] to [`hash256::Hash`]
/// Converts the associated [`MiniscriptKey::Hash256`] type to a [`hash256::Hash`].
///
/// [`hash256::Hash`]: crate::hash256::Hash
fn to_hash256(hash: &<Self as MiniscriptKey>::Hash256) -> hash256::Hash;

/// Converts the generic associated [`MiniscriptKey::Ripemd160`] to [`ripemd160::Hash`]
/// Converts the associated [`MiniscriptKey::Ripemd160`] type to a [`ripemd160::Hash`].
///
/// [`ripemd160::Hash`]: bitcoin::hashes::ripemd160::Hash
fn to_ripemd160(hash: &<Self as MiniscriptKey>::Ripemd160) -> ripemd160::Hash;

/// Converts the generic associated [`MiniscriptKey::Hash160`] to [`hash160::Hash`]
/// Converts the associated [`MiniscriptKey::Hash160`] type to a [`hash160::Hash`].
///
/// [`hash160::Hash`]: bitcoin::hashes::hash160::Hash
fn to_hash160(hash: &<Self as MiniscriptKey>::Hash160) -> hash160::Hash;
}

impl ToPublicKey for bitcoin::PublicKey {
fn to_public_key(&self) -> bitcoin::PublicKey { *self }

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 }
Expand All @@ -262,8 +260,6 @@ impl ToPublicKey for bitcoin::PublicKey {
impl ToPublicKey for bitcoin::secp256k1::PublicKey {
fn to_public_key(&self) -> bitcoin::PublicKey { bitcoin::PublicKey::new(*self) }

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 }
Expand All @@ -283,8 +279,6 @@ impl ToPublicKey for bitcoin::secp256k1::XOnlyPublicKey {

fn to_x_only_pubkey(&self) -> bitcoin::secp256k1::XOnlyPublicKey { *self }

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 }
Expand All @@ -302,9 +296,6 @@ where
/// Translates public keys P -> Q.
fn pk(&mut self, pk: &P) -> Result<Q, E>;

/// Provides the translation from P::Sha256 -> Q::Sha256
fn sha256(&mut self, sha256: &P::Sha256) -> Result<Q::Sha256, E>;

/// Provides the translation from P::Hash256 -> Q::Hash256
fn hash256(&mut self, hash256: &P::Hash256) -> Result<Q::Hash256, E>;

Expand Down
Loading