Skip to content

Commit b8d04ac

Browse files
committed
Merge #275: Miniscript tapscript support (Pr 2 of 3)
42e4c50 Cleanup context code with SigType enum (sanket1729) e70e6a3 Fix fuzzer crash while allocating keys for multi_a (sanket1729) f6b2536 Add satisfaction tests (sanket1729) 1a7e779 Change satisfier to support xonly sigs (sanket1729) 2c32c03 Use bitcoin::SchnorrSig type from rust-bitcoin (sanket1729) e26d4e6 Use bitcoin::EcdsaSig from rust-bitcoin (sanket1729) 89e7cb3 add multi_a (sanket1729) Pull request description: Adds support for MultiA script fragment. Builds on top of #255 . Do not merge this until we have a rust-bitcoin release ACKs for top commit: apoelstra: ACK 42e4c50 Tree-SHA512: 10701266cf6fe436fe07359e67d16bc925ebbcd767d4e5a8d325db61b979bd76b1e0291dd9eae5b7d58f6a385f8f2e16c2657957a597ce18736382b776e20502
2 parents 87c9849 + 42e4c50 commit b8d04ac

25 files changed

+751
-189
lines changed

examples/sign_multisig.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,19 @@ fn main() {
6161
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6262
]).expect("key 3"),
6363
];
64-
let bitcoin_sig = (
64+
let bitcoin_sig = bitcoin::EcdsaSig {
6565
// copied at random off the blockchain; this is not actually a valid
6666
// signature for this transaction; Miniscript does not verify
67-
secp256k1::ecdsa::Signature::from_str(
67+
sig: secp256k1::ecdsa::Signature::from_str(
6868
"3045\
6969
0221\
7070
00f7c3648c390d87578cd79c8016940aa8e3511c4104cb78daa8fb8e429375efc1\
7171
0220\
7272
531d75c136272f127a5dc14acc0722301cbddc222262934151f140da345af177",
7373
)
7474
.unwrap(),
75-
bitcoin::EcdsaSigHashType::All,
76-
);
75+
hash_ty: bitcoin::EcdsaSigHashType::All,
76+
};
7777

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

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

117117
// Doesn't work with no signatures
118118
assert!(my_descriptor.satisfy(&mut tx.input[0], &sigs).is_err());

examples/verify_tx.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ fn main() {
138138
.expect("Can only fail in sighash single when corresponding output is not present");
139139
// Restrict to sighash_all just to demonstrate how to add additional filters
140140
// `&_` needed here because of https://github.com/rust-lang/rust/issues/79187
141-
let vfyfn = move |pk: &_, bitcoinsig: miniscript::BitcoinSig| {
142-
bitcoinsig.1 == bitcoin::EcdsaSigHashType::All && vfyfn(pk, bitcoinsig)
141+
let vfyfn = move |pk: &_, bitcoinsig: miniscript::bitcoin::EcdsaSig| {
142+
bitcoinsig.hash_ty == bitcoin::EcdsaSigHashType::All && vfyfn(pk, bitcoinsig)
143143
};
144144

145145
println!("\nExample two");
@@ -165,9 +165,9 @@ fn main() {
165165
)
166166
.unwrap();
167167

168-
let iter = interpreter.iter(|pk, (sig, sighashtype)| {
169-
sighashtype == bitcoin::EcdsaSigHashType::All
170-
&& secp.verify_ecdsa(&message, &sig, &pk.key).is_ok()
168+
let iter = interpreter.iter(|pk, ecdsa_sig| {
169+
ecdsa_sig.hash_ty == bitcoin::EcdsaSigHashType::All
170+
&& secp.verify_ecdsa(&message, &ecdsa_sig.sig, &pk.key).is_ok()
171171
});
172172
println!("\nExample three");
173173
for elem in iter {

fuzz/fuzz_targets/roundtrip_miniscript_script.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,30 @@ fn main() {
3535
});
3636
}
3737
}
38+
39+
#[cfg(test)]
40+
mod tests {
41+
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
42+
let mut b = 0;
43+
for (idx, c) in hex.as_bytes().iter().enumerate() {
44+
b <<= 4;
45+
match *c {
46+
b'A'...b'F' => b |= c - b'A' + 10,
47+
b'a'...b'f' => b |= c - b'a' + 10,
48+
b'0'...b'9' => b |= c - b'0',
49+
_ => panic!("Bad hex"),
50+
}
51+
if (idx & 1) == 1 {
52+
out.push(b);
53+
b = 0;
54+
}
55+
}
56+
}
57+
58+
#[test]
59+
fn duplicate_crash3() {
60+
let mut a = Vec::new();
61+
extend_vec_from_hex("1479002d00000020323731363342740000004000000000000000000000000000000000000063630004636363639c00000000000000000000", &mut a);
62+
super::do_test(&a);
63+
}
64+
}

fuzz/fuzz_targets/roundtrip_miniscript_str.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,30 @@ fn main() {
3838
});
3939
}
4040
}
41+
42+
#[cfg(test)]
43+
mod tests {
44+
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
45+
let mut b = 0;
46+
for (idx, c) in hex.as_bytes().iter().enumerate() {
47+
b <<= 4;
48+
match *c {
49+
b'A'...b'F' => b |= c - b'A' + 10,
50+
b'a'...b'f' => b |= c - b'a' + 10,
51+
b'0'...b'9' => b |= c - b'0',
52+
_ => panic!("Bad hex"),
53+
}
54+
if (idx & 1) == 1 {
55+
out.push(b);
56+
b = 0;
57+
}
58+
}
59+
}
60+
61+
#[test]
62+
fn duplicate_crash() {
63+
let mut a = Vec::new();
64+
extend_vec_from_hex("1479002d00000020323731363342740000004000000000000000000000000000000000000063630004636363639c00000000000000000000", &mut a);
65+
super::do_test(&a);
66+
}
67+
}

src/descriptor/bare.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,9 +335,8 @@ impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Pkh<Pk> {
335335
Pk: ToPublicKey,
336336
S: Satisfier<Pk>,
337337
{
338-
if let Some(sig) = satisfier.lookup_sig(&self.pk) {
339-
let mut sig_vec = sig.0.serialize_der().to_vec();
340-
sig_vec.push(sig.1.as_u32() as u8);
338+
if let Some(sig) = satisfier.lookup_ecdsa_sig(&self.pk) {
339+
let sig_vec = sig.to_vec();
341340
let script_sig = script::Builder::new()
342341
.push_slice(&sig_vec[..])
343342
.push_key(&self.pk.to_public_key())

src/descriptor/mod.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -655,13 +655,12 @@ mod tests {
655655
use bitcoin::hashes::hex::FromHex;
656656
use bitcoin::hashes::{hash160, sha256};
657657
use bitcoin::util::bip32;
658-
use bitcoin::{self, secp256k1, PublicKey};
658+
use bitcoin::{self, secp256k1, EcdsaSigHashType, PublicKey};
659659
use descriptor::key::Wildcard;
660660
use descriptor::{
661661
DescriptorPublicKey, DescriptorSecretKey, DescriptorSinglePub, DescriptorXKey,
662662
};
663663
use hex_script;
664-
use miniscript::satisfy::BitcoinSig;
665664
use std::cmp;
666665
use std::collections::HashMap;
667666
use std::str::FromStr;
@@ -950,9 +949,12 @@ mod tests {
950949
}
951950

952951
impl Satisfier<bitcoin::PublicKey> for SimpleSat {
953-
fn lookup_sig(&self, pk: &bitcoin::PublicKey) -> Option<BitcoinSig> {
952+
fn lookup_ecdsa_sig(&self, pk: &bitcoin::PublicKey) -> Option<bitcoin::EcdsaSig> {
954953
if *pk == self.pk {
955-
Some((self.sig, bitcoin::EcdsaSigHashType::All))
954+
Some(bitcoin::EcdsaSig {
955+
sig: self.sig,
956+
hash_ty: bitcoin::EcdsaSigHashType::All,
957+
})
956958
} else {
957959
None
958960
}
@@ -1161,8 +1163,20 @@ mod tests {
11611163
let satisfier = {
11621164
let mut satisfier = HashMap::with_capacity(2);
11631165

1164-
satisfier.insert(a, (sig_a.clone(), ::bitcoin::EcdsaSigHashType::All));
1165-
satisfier.insert(b, (sig_b.clone(), ::bitcoin::EcdsaSigHashType::All));
1166+
satisfier.insert(
1167+
a,
1168+
bitcoin::EcdsaSig {
1169+
sig: sig_a,
1170+
hash_ty: EcdsaSigHashType::All,
1171+
},
1172+
);
1173+
satisfier.insert(
1174+
b,
1175+
bitcoin::EcdsaSig {
1176+
sig: sig_b,
1177+
hash_ty: EcdsaSigHashType::All,
1178+
},
1179+
);
11661180

11671181
satisfier
11681182
};

src/descriptor/segwitv0.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -436,9 +436,8 @@ impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Wpkh<Pk> {
436436
Pk: ToPublicKey,
437437
S: Satisfier<Pk>,
438438
{
439-
if let Some(sig) = satisfier.lookup_sig(&self.pk) {
440-
let mut sig_vec = sig.0.serialize_der().to_vec();
441-
sig_vec.push(sig.1.as_u32() as u8);
439+
if let Some(sig) = satisfier.lookup_ecdsa_sig(&self.pk) {
440+
let sig_vec = sig.to_vec();
442441
let script_sig = Script::new();
443442
let witness = vec![sig_vec, self.pk.to_public_key().to_bytes()];
444443
Ok((witness, script_sig))

src/interpreter/error.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ pub enum Error {
3737
IncorrectWScriptHash,
3838
/// MultiSig missing at least `1` witness elements out of `k + 1` required
3939
InsufficientSignaturesMultiSig,
40+
/// Invalid Sighash type
41+
InvalidSchnorrSigHashType(Vec<u8>),
4042
/// Signature failed to verify
4143
InvalidSignature(bitcoin::PublicKey),
4244
/// Last byte of this signature isn't a standard sighash type
@@ -138,6 +140,13 @@ impl fmt::Display for Error {
138140
}
139141
Error::IncorrectWScriptHash => f.write_str("witness script did not match scriptpubkey"),
140142
Error::InsufficientSignaturesMultiSig => f.write_str("Insufficient signatures for CMS"),
143+
Error::InvalidSchnorrSigHashType(ref sig) => {
144+
write!(
145+
f,
146+
"Invalid sighash type for schnorr signature '{}'",
147+
sig.to_hex()
148+
)
149+
}
141150
Error::InvalidSignature(pk) => write!(f, "bad signature with pk {}", pk),
142151
Error::NonStandardSigHash(ref sig) => {
143152
write!(

src/interpreter/inner.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use bitcoin::blockdata::witness::Witness;
1717
use bitcoin::hashes::{hash160, sha256, Hash};
1818

1919
use super::{stack, Error, Stack};
20-
use miniscript::context::NoChecks;
20+
use miniscript::context::NoChecksEcdsa;
2121
use {Miniscript, MiniscriptKey};
2222

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

4949
fn script_from_stackelem<'a>(
5050
elem: &stack::Element<'a>,
51-
) -> Result<Miniscript<bitcoin::PublicKey, NoChecks>, Error> {
51+
) -> Result<Miniscript<bitcoin::PublicKey, NoChecksEcdsa>, Error> {
5252
match *elem {
5353
stack::Element::Push(sl) => {
5454
Miniscript::<bitcoin::PublicKey, _>::parse_insane(&bitcoin::Script::from(sl.to_owned()))
@@ -86,7 +86,7 @@ pub enum Inner {
8686
/// pay-to-pkhash or pay-to-witness-pkhash)
8787
PublicKey(bitcoin::PublicKey, PubkeyType),
8888
/// The script being evaluated is an actual script
89-
Script(Miniscript<bitcoin::PublicKey, NoChecks>, ScriptType),
89+
Script(Miniscript<bitcoin::PublicKey, NoChecksEcdsa>, ScriptType),
9090
}
9191

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

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

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

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

707707
let witness_script = miniscript.encode();

0 commit comments

Comments
 (0)