Skip to content

Add support for string images in ripemd160 and hash160 #439

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

Merged
merged 1 commit into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion examples/taproot.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::str::FromStr;

use bitcoin::hashes::{hash160, sha256};
use bitcoin::hashes::{hash160, ripemd160, sha256};
use bitcoin::util::address::WitnessVersion;
use bitcoin::Network;
use miniscript::descriptor::DescriptorType;
Expand Down Expand Up @@ -32,6 +32,14 @@ impl Translator<String, bitcoin::XOnlyPublicKey, ()> for StrPkTranslator {
fn hash256(&mut self, _sha256: &String) -> Result<hash256::Hash, ()> {
unreachable!("Policy does not contain any hash256 fragment");
}

fn ripemd160(&mut self, _ripemd160: &String) -> Result<ripemd160::Hash, ()> {
unreachable!("Policy does not contain any ripemd160 fragment");
}

fn hash160(&mut self, _hash160: &String) -> Result<hash160::Hash, ()> {
unreachable!("Policy does not contain any hash160 fragment");
}
}

fn main() {
Expand All @@ -45,6 +53,7 @@ fn main() {
)"
.replace(&[' ', '\n', '\t'][..], "");

let _ms = Miniscript::<String, Tap>::from_str("and_v(v:ripemd160(H),pk(A))").unwrap();
let pol: Concrete<String> = Concrete::from_str(&pol_str).unwrap();
// In case we can't find an internal key for the given policy, we set the internal key to
// a random pubkey as specified by BIP341 (which are *unspendable* by any party :p)
Expand Down
14 changes: 13 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, sha256, Hash, HashEngine};
use bitcoin::hashes::{hash160, ripemd160, sha256, Hash, HashEngine};
use bitcoin::secp256k1::{Secp256k1, Signing, Verification};
use bitcoin::util::bip32;
use bitcoin::{self, XOnlyPublicKey, XpubIdentifier};
Expand Down Expand Up @@ -739,6 +739,8 @@ impl MiniscriptKey for DescriptorPublicKey {
type RawPkHash = Self;
type Sha256 = sha256::Hash;
type Hash256 = hash256::Hash;
type Ripemd160 = ripemd160::Hash;
type Hash160 = hash160::Hash;

fn is_uncompressed(&self) -> bool {
match self {
Expand Down Expand Up @@ -806,6 +808,8 @@ impl MiniscriptKey for DerivedDescriptorKey {
type RawPkHash = Self;
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()
Expand Down Expand Up @@ -837,6 +841,14 @@ impl ToPublicKey for DerivedDescriptorKey {
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
}
}

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

use bitcoin::blockdata::witness::Witness;
use bitcoin::hashes::sha256;
use bitcoin::hashes::{hash160, ripemd160, sha256};
use bitcoin::util::address::WitnessVersion;
use bitcoin::{self, secp256k1, Address, Network, Script, TxIn};
use sync::Arc;
Expand Down Expand Up @@ -652,6 +652,18 @@ impl Descriptor<DescriptorPublicKey> {
.map_err(|e| Error::Unexpected(e.to_string()))?;
Ok(hash)
}

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

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

let descriptor = Descriptor::<String>::from_str(s)?;
Expand Down Expand Up @@ -682,6 +694,14 @@ impl Descriptor<DescriptorPublicKey> {
fn hash256(&mut self, hash256: &hash256::Hash) -> Result<String, ()> {
Ok(hash256.to_string())
}

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

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

fn key_to_string(pk: &DescriptorPublicKey, key_map: &KeyMap) -> Result<String, ()> {
Expand Down
2 changes: 2 additions & 0 deletions src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ impl MiniscriptKey for BitcoinKey {
type RawPkHash = TypedHash160;
type Sha256 = sha256::Hash;
type Hash256 = hash256::Hash;
type Ripemd160 = ripemd160::Hash;
type Hash160 = hash160::Hash;

fn to_pubkeyhash(&self) -> Self::RawPkHash {
match self {
Expand Down
136 changes: 133 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ use core::{fmt, hash, str};
use std::error;

use bitcoin::blockdata::{opcodes, script};
use bitcoin::hashes::{hash160, sha256, Hash};
use bitcoin::hashes::{hash160, ripemd160, sha256, Hash};

pub use crate::descriptor::{Descriptor, DescriptorPublicKey};
pub use crate::interpreter::Interpreter;
Expand Down Expand Up @@ -165,6 +165,13 @@ pub trait MiniscriptKey: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Ha
/// The associated [`hash256::Hash`] for this [`MiniscriptKey`],
/// used in the hash256 fragment.
type Hash256: Clone + Eq + Ord + fmt::Display + fmt::Debug + hash::Hash;
/// The associated [`ripedmd160::Hash`] for this [`MiniscriptKey`] type.
/// used in the ripemd160 fragment
type Ripemd160: Clone + Eq + Ord + fmt::Display + fmt::Debug + hash::Hash;

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

/// Converts this key to the associated pubkey hash.
fn to_pubkeyhash(&self) -> Self::RawPkHash;
Expand All @@ -174,6 +181,8 @@ impl MiniscriptKey for bitcoin::secp256k1::PublicKey {
type RawPkHash = hash160::Hash;
type Sha256 = sha256::Hash;
type Hash256 = hash256::Hash;
type Ripemd160 = ripemd160::Hash;
type Hash160 = hash160::Hash;

fn to_pubkeyhash(&self) -> Self::RawPkHash {
hash160::Hash::hash(&self.serialize())
Expand All @@ -189,6 +198,8 @@ impl MiniscriptKey for bitcoin::PublicKey {
type RawPkHash = hash160::Hash;
type Sha256 = sha256::Hash;
type Hash256 = hash256::Hash;
type Ripemd160 = ripemd160::Hash;
type Hash160 = hash160::Hash;

fn to_pubkeyhash(&self) -> Self::RawPkHash {
hash160::Hash::hash(&self.to_bytes())
Expand All @@ -199,6 +210,8 @@ impl MiniscriptKey for bitcoin::secp256k1::XOnlyPublicKey {
type RawPkHash = hash160::Hash;
type Sha256 = sha256::Hash;
type Hash256 = hash256::Hash;
type Ripemd160 = ripemd160::Hash;
type Hash160 = hash160::Hash;

fn to_pubkeyhash(&self) -> Self::RawPkHash {
hash160::Hash::hash(&self.serialize())
Expand All @@ -213,6 +226,8 @@ impl MiniscriptKey for String {
type RawPkHash = String;
type Sha256 = String; // specify hashes as string
type Hash256 = String;
type Ripemd160 = String;
type Hash160 = String;

fn to_pubkeyhash(&self) -> Self::RawPkHash {
(&self).to_string()
Expand Down Expand Up @@ -243,6 +258,12 @@ pub trait ToPublicKey: MiniscriptKey {

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

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

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

impl ToPublicKey for bitcoin::PublicKey {
Expand All @@ -261,6 +282,14 @@ impl ToPublicKey for bitcoin::PublicKey {
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
}
}

impl ToPublicKey for bitcoin::secp256k1::PublicKey {
Expand All @@ -279,6 +308,14 @@ impl ToPublicKey for bitcoin::secp256k1::PublicKey {
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
}
}

impl ToPublicKey for bitcoin::secp256k1::XOnlyPublicKey {
Expand Down Expand Up @@ -306,6 +343,14 @@ impl ToPublicKey for bitcoin::secp256k1::XOnlyPublicKey {
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
}
}

/// Dummy key which de/serializes to the empty string; useful sometimes for testing
Expand All @@ -327,6 +372,8 @@ impl MiniscriptKey for DummyKey {
type RawPkHash = DummyKeyHash;
type Sha256 = DummySha256Hash;
type Hash256 = DummyHash256;
type Ripemd160 = DummyRipemd160Hash;
type Hash160 = DummyHash160Hash;

fn to_pubkeyhash(&self) -> Self::RawPkHash {
DummyKeyHash
Expand Down Expand Up @@ -366,6 +413,14 @@ impl ToPublicKey for DummyKey {
hash256::Hash::from_str("50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352")
.unwrap()
}

fn to_ripemd160(_: &DummyRipemd160Hash) -> ripemd160::Hash {
ripemd160::Hash::from_str("f54a5851e9372b87810a8e60cdd2e7cfd80b6e31").unwrap()
}

fn to_hash160(_: &DummyHash160Hash) -> hash160::Hash {
hash160::Hash::from_str("f54a5851e9372b87810a8e60cdd2e7cfd80b6e31").unwrap()
}
}

/// Dummy keyhash which de/serializes to the empty string; useful sometimes for testing
Expand Down Expand Up @@ -437,19 +492,72 @@ impl str::FromStr for DummyHash256 {
}
}

/// Dummy keyhash which de/serializes to the empty string; useful for testing
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
pub struct DummyRipemd160Hash;

impl str::FromStr for DummyRipemd160Hash {
type Err = &'static str;
fn from_str(x: &str) -> Result<DummyRipemd160Hash, &'static str> {
if x.is_empty() {
Ok(DummyRipemd160Hash)
} else {
Err("non empty dummy hash")
}
}
}

impl fmt::Display for DummyHash256 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("")
}
}
impl fmt::Display for DummyRipemd160Hash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("")
}
}

impl hash::Hash for DummyHash256 {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
"DummySha256Hash".hash(state);
}
}

/// Provides the conversion information required in [`TranslatePk`]
impl hash::Hash for DummyRipemd160Hash {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
"DummyRipemd160Hash".hash(state);
}
}

/// Dummy keyhash which de/serializes to the empty string; useful for testing
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
pub struct DummyHash160Hash;

impl str::FromStr for DummyHash160Hash {
type Err = &'static str;
fn from_str(x: &str) -> Result<DummyHash160Hash, &'static str> {
if x.is_empty() {
Ok(DummyHash160Hash)
} else {
Err("non empty dummy hash")
}
}
}

impl fmt::Display for DummyHash160Hash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("")
}
}

impl hash::Hash for DummyHash160Hash {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
"DummyHash160Hash".hash(state);
}
}
/// Describes an object that can translate various keys and hashes from one key to the type
/// associated with the other key. Used by the [`TranslatePk`] trait to do the actual translations.
pub trait Translator<P, Q, E>
where
P: MiniscriptKey,
Expand All @@ -466,6 +574,12 @@ where

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

/// Translates ripemd160 hashes from P::Ripemd160 -> Q::Ripemd160
fn ripemd160(&mut self, ripemd160: &P::Ripemd160) -> Result<Q::Ripemd160, E>;

/// Translates hash160 hashes from P::Hash160 -> Q::Hash160
fn hash160(&mut self, hash160: &P::Hash160) -> Result<Q::Hash160, E>;
}

/// Provides the conversion information required in [`TranslatePk`].
Expand All @@ -487,7 +601,12 @@ impl<P, Q, E, T> Translator<P, Q, E> for T
where
T: PkTranslator<P, Q, E>,
P: MiniscriptKey,
Q: MiniscriptKey<Sha256 = P::Sha256, Hash256 = P::Hash256>,
Q: MiniscriptKey<
Sha256 = P::Sha256,
Hash256 = P::Hash256,
Ripemd160 = P::Ripemd160,
Hash160 = P::Hash160,
>,
{
fn pk(&mut self, pk: &P) -> Result<Q, E> {
<Self as PkTranslator<P, Q, E>>::pk(self, pk)
Expand All @@ -507,6 +626,17 @@ where
fn hash256(&mut self, hash256: &<P as MiniscriptKey>::Hash256) -> Result<<Q>::Hash256, E> {
Ok(hash256.clone())
}

fn ripemd160(
&mut self,
ripemd160: &<P as MiniscriptKey>::Ripemd160,
) -> Result<<Q>::Ripemd160, E> {
Ok(ripemd160.clone())
}

fn hash160(&mut self, hash160: &<P as MiniscriptKey>::Hash160) -> Result<<Q>::Hash160, E> {
Ok(hash160.clone())
}
}

/// Converts a descriptor using abstract keys to one using specific keys. Uses translator `t` to do
Expand Down
Loading