Skip to content

Miniscript tapscript support (Pr 2 of 3) #275

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 7 commits into from
Jan 13, 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
10 changes: 5 additions & 5 deletions examples/sign_multisig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,19 @@ fn main() {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]).expect("key 3"),
];
let bitcoin_sig = (
let bitcoin_sig = bitcoin::EcdsaSig {
// copied at random off the blockchain; this is not actually a valid
// signature for this transaction; Miniscript does not verify
secp256k1::ecdsa::Signature::from_str(
sig: secp256k1::ecdsa::Signature::from_str(
"3045\
0221\
00f7c3648c390d87578cd79c8016940aa8e3511c4104cb78daa8fb8e429375efc1\
0220\
531d75c136272f127a5dc14acc0722301cbddc222262934151f140da345af177",
)
.unwrap(),
bitcoin::EcdsaSigHashType::All,
);
hash_ty: bitcoin::EcdsaSigHashType::All,
};

let descriptor_str = format!(
"wsh(multi(2,{},{},{}))",
Expand Down Expand Up @@ -112,7 +112,7 @@ fn main() {
// Attempt to satisfy at age 0, height 0
let original_txin = tx.input[0].clone();

let mut sigs = HashMap::<bitcoin::PublicKey, miniscript::BitcoinSig>::new();
let mut sigs = HashMap::<bitcoin::PublicKey, miniscript::bitcoin::EcdsaSig>::new();

// Doesn't work with no signatures
assert!(my_descriptor.satisfy(&mut tx.input[0], &sigs).is_err());
Expand Down
10 changes: 5 additions & 5 deletions examples/verify_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ fn main() {
.expect("Can only fail in sighash single when corresponding output is not present");
// Restrict to sighash_all just to demonstrate how to add additional filters
// `&_` needed here because of https://github.com/rust-lang/rust/issues/79187
let vfyfn = move |pk: &_, bitcoinsig: miniscript::BitcoinSig| {
bitcoinsig.1 == bitcoin::EcdsaSigHashType::All && vfyfn(pk, bitcoinsig)
let vfyfn = move |pk: &_, bitcoinsig: miniscript::bitcoin::EcdsaSig| {
bitcoinsig.hash_ty == bitcoin::EcdsaSigHashType::All && vfyfn(pk, bitcoinsig)
};

println!("\nExample two");
Expand All @@ -165,9 +165,9 @@ fn main() {
)
.unwrap();

let iter = interpreter.iter(|pk, (sig, sighashtype)| {
sighashtype == bitcoin::EcdsaSigHashType::All
&& secp.verify_ecdsa(&message, &sig, &pk.key).is_ok()
let iter = interpreter.iter(|pk, ecdsa_sig| {
ecdsa_sig.hash_ty == bitcoin::EcdsaSigHashType::All
&& secp.verify_ecdsa(&message, &ecdsa_sig.sig, &pk.key).is_ok()
});
println!("\nExample three");
for elem in iter {
Expand Down
27 changes: 27 additions & 0 deletions fuzz/fuzz_targets/roundtrip_miniscript_script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,30 @@ fn main() {
});
}
}

#[cfg(test)]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;
for (idx, c) in hex.as_bytes().iter().enumerate() {
b <<= 4;
match *c {
b'A'...b'F' => b |= c - b'A' + 10,
b'a'...b'f' => b |= c - b'a' + 10,
b'0'...b'9' => b |= c - b'0',
_ => panic!("Bad hex"),
}
if (idx & 1) == 1 {
out.push(b);
b = 0;
}
}
}

#[test]
fn duplicate_crash3() {
let mut a = Vec::new();
extend_vec_from_hex("1479002d00000020323731363342740000004000000000000000000000000000000000000063630004636363639c00000000000000000000", &mut a);
super::do_test(&a);
}
}
27 changes: 27 additions & 0 deletions fuzz/fuzz_targets/roundtrip_miniscript_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,30 @@ fn main() {
});
}
}

#[cfg(test)]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;
for (idx, c) in hex.as_bytes().iter().enumerate() {
b <<= 4;
match *c {
b'A'...b'F' => b |= c - b'A' + 10,
b'a'...b'f' => b |= c - b'a' + 10,
b'0'...b'9' => b |= c - b'0',
_ => panic!("Bad hex"),
}
if (idx & 1) == 1 {
out.push(b);
b = 0;
}
}
}

#[test]
fn duplicate_crash() {
let mut a = Vec::new();
extend_vec_from_hex("1479002d00000020323731363342740000004000000000000000000000000000000000000063630004636363639c00000000000000000000", &mut a);
super::do_test(&a);
}
}
5 changes: 2 additions & 3 deletions src/descriptor/bare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,9 +335,8 @@ impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Pkh<Pk> {
Pk: ToPublicKey,
S: Satisfier<Pk>,
{
if let Some(sig) = satisfier.lookup_sig(&self.pk) {
let mut sig_vec = sig.0.serialize_der().to_vec();
sig_vec.push(sig.1.as_u32() as u8);
if let Some(sig) = satisfier.lookup_ecdsa_sig(&self.pk) {
let sig_vec = sig.to_vec();
let script_sig = script::Builder::new()
.push_slice(&sig_vec[..])
.push_key(&self.pk.to_public_key())
Expand Down
26 changes: 20 additions & 6 deletions src/descriptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,13 +655,12 @@ mod tests {
use bitcoin::hashes::hex::FromHex;
use bitcoin::hashes::{hash160, sha256};
use bitcoin::util::bip32;
use bitcoin::{self, secp256k1, PublicKey};
use bitcoin::{self, secp256k1, EcdsaSigHashType, PublicKey};
use descriptor::key::Wildcard;
use descriptor::{
DescriptorPublicKey, DescriptorSecretKey, DescriptorSinglePub, DescriptorXKey,
};
use hex_script;
use miniscript::satisfy::BitcoinSig;
use std::cmp;
use std::collections::HashMap;
use std::str::FromStr;
Expand Down Expand Up @@ -950,9 +949,12 @@ mod tests {
}

impl Satisfier<bitcoin::PublicKey> for SimpleSat {
fn lookup_sig(&self, pk: &bitcoin::PublicKey) -> Option<BitcoinSig> {
fn lookup_ecdsa_sig(&self, pk: &bitcoin::PublicKey) -> Option<bitcoin::EcdsaSig> {
if *pk == self.pk {
Some((self.sig, bitcoin::EcdsaSigHashType::All))
Some(bitcoin::EcdsaSig {
sig: self.sig,
hash_ty: bitcoin::EcdsaSigHashType::All,
})
} else {
None
}
Expand Down Expand Up @@ -1161,8 +1163,20 @@ mod tests {
let satisfier = {
let mut satisfier = HashMap::with_capacity(2);

satisfier.insert(a, (sig_a.clone(), ::bitcoin::EcdsaSigHashType::All));
satisfier.insert(b, (sig_b.clone(), ::bitcoin::EcdsaSigHashType::All));
satisfier.insert(
a,
bitcoin::EcdsaSig {
sig: sig_a,
hash_ty: EcdsaSigHashType::All,
},
);
satisfier.insert(
b,
bitcoin::EcdsaSig {
sig: sig_b,
hash_ty: EcdsaSigHashType::All,
},
);

satisfier
};
Expand Down
5 changes: 2 additions & 3 deletions src/descriptor/segwitv0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,9 +436,8 @@ impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Wpkh<Pk> {
Pk: ToPublicKey,
S: Satisfier<Pk>,
{
if let Some(sig) = satisfier.lookup_sig(&self.pk) {
let mut sig_vec = sig.0.serialize_der().to_vec();
sig_vec.push(sig.1.as_u32() as u8);
if let Some(sig) = satisfier.lookup_ecdsa_sig(&self.pk) {
let sig_vec = sig.to_vec();
let script_sig = Script::new();
let witness = vec![sig_vec, self.pk.to_public_key().to_bytes()];
Ok((witness, script_sig))
Expand Down
9 changes: 9 additions & 0 deletions src/interpreter/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub enum Error {
IncorrectWScriptHash,
/// MultiSig missing at least `1` witness elements out of `k + 1` required
InsufficientSignaturesMultiSig,
/// Invalid Sighash type
InvalidSchnorrSigHashType(Vec<u8>),
/// Signature failed to verify
InvalidSignature(bitcoin::PublicKey),
/// Last byte of this signature isn't a standard sighash type
Expand Down Expand Up @@ -138,6 +140,13 @@ impl fmt::Display for Error {
}
Error::IncorrectWScriptHash => f.write_str("witness script did not match scriptpubkey"),
Error::InsufficientSignaturesMultiSig => f.write_str("Insufficient signatures for CMS"),
Error::InvalidSchnorrSigHashType(ref sig) => {
write!(
f,
"Invalid sighash type for schnorr signature '{}'",
sig.to_hex()
)
}
Error::InvalidSignature(pk) => write!(f, "bad signature with pk {}", pk),
Error::NonStandardSigHash(ref sig) => {
write!(
Expand Down
14 changes: 7 additions & 7 deletions src/interpreter/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use bitcoin::blockdata::witness::Witness;
use bitcoin::hashes::{hash160, sha256, Hash};

use super::{stack, Error, Stack};
use miniscript::context::NoChecks;
use miniscript::context::NoChecksEcdsa;
use {Miniscript, MiniscriptKey};

/// Attempts to parse a slice as a Bitcoin public key, checking compressedness
Expand Down Expand Up @@ -48,7 +48,7 @@ fn pk_from_stackelem<'a>(

fn script_from_stackelem<'a>(
elem: &stack::Element<'a>,
) -> Result<Miniscript<bitcoin::PublicKey, NoChecks>, Error> {
) -> Result<Miniscript<bitcoin::PublicKey, NoChecksEcdsa>, Error> {
match *elem {
stack::Element::Push(sl) => {
Miniscript::<bitcoin::PublicKey, _>::parse_insane(&bitcoin::Script::from(sl.to_owned()))
Expand Down Expand Up @@ -86,7 +86,7 @@ pub enum Inner {
/// pay-to-pkhash or pay-to-witness-pkhash)
PublicKey(bitcoin::PublicKey, PubkeyType),
/// The script being evaluated is an actual script
Script(Miniscript<bitcoin::PublicKey, NoChecks>, ScriptType),
Script(Miniscript<bitcoin::PublicKey, NoChecksEcdsa>, ScriptType),
}

// The `Script` returned by this method is always generated/cloned ... when
Expand Down Expand Up @@ -598,7 +598,7 @@ mod tests {
fn script_bare() {
let preimage = b"12345678----____12345678----____";
let hash = hash160::Hash::hash(&preimage[..]);
let miniscript: ::Miniscript<bitcoin::PublicKey, NoChecks> =
let miniscript: ::Miniscript<bitcoin::PublicKey, NoChecksEcdsa> =
::Miniscript::from_str_insane(&format!("hash160({})", hash)).unwrap();

let spk = miniscript.encode();
Expand All @@ -625,7 +625,7 @@ mod tests {
fn script_sh() {
let preimage = b"12345678----____12345678----____";
let hash = hash160::Hash::hash(&preimage[..]);
let miniscript: ::Miniscript<bitcoin::PublicKey, NoChecks> =
let miniscript: ::Miniscript<bitcoin::PublicKey, NoChecksEcdsa> =
::Miniscript::from_str_insane(&format!("hash160({})", hash)).unwrap();

let redeem_script = miniscript.encode();
Expand Down Expand Up @@ -663,7 +663,7 @@ mod tests {
fn script_wsh() {
let preimage = b"12345678----____12345678----____";
let hash = hash160::Hash::hash(&preimage[..]);
let miniscript: ::Miniscript<bitcoin::PublicKey, NoChecks> =
let miniscript: ::Miniscript<bitcoin::PublicKey, NoChecksEcdsa> =
::Miniscript::from_str_insane(&format!("hash160({})", hash)).unwrap();

let witness_script = miniscript.encode();
Expand Down Expand Up @@ -701,7 +701,7 @@ mod tests {
fn script_sh_wsh() {
let preimage = b"12345678----____12345678----____";
let hash = hash160::Hash::hash(&preimage[..]);
let miniscript: ::Miniscript<bitcoin::PublicKey, NoChecks> =
let miniscript: ::Miniscript<bitcoin::PublicKey, NoChecksEcdsa> =
::Miniscript::from_str_insane(&format!("hash160({})", hash)).unwrap();

let witness_script = miniscript.encode();
Expand Down
Loading