Skip to content

Commit b39486b

Browse files
committed
Cleanup iter APIs, retintroduce examples
1 parent 8356ef9 commit b39486b

File tree

5 files changed

+57
-41
lines changed

5 files changed

+57
-41
lines changed

Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,8 @@ name = "parse"
3333
[[example]]
3434
name = "sign_multisig"
3535

36-
# TODO: Re-enable in future commit after API re-design
37-
# [[example]]
38-
# name = "verify_tx"
36+
[[example]]
37+
name = "verify_tx"
3938

4039
[[example]]
4140
name = "psbt"

examples/verify_tx.rs

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ extern crate miniscript;
1919

2020
use bitcoin::consensus::Decodable;
2121
use bitcoin::secp256k1; // secp256k1 re-exported from rust-bitcoin
22+
use bitcoin::util::sighash;
23+
use miniscript::interpreter::KeySigPair;
2224
use std::str::FromStr;
2325

2426
fn main() {
@@ -83,7 +85,7 @@ fn main() {
8385
0xa9, 0x14, 0x92, 0x09, 0xa8, 0xf9, 0x0c, 0x58, 0x4b, 0xb5, 0x97, 0x4d, 0x58, 0x68, 0x72,
8486
0x49, 0xe5, 0x32, 0xde, 0x59, 0xf4, 0xbc, 0x87,
8587
]);
86-
let mut interpreter = miniscript::Interpreter::from_txdata(
88+
let interpreter = miniscript::Interpreter::from_txdata(
8789
&spk_input_1,
8890
&transaction.input[0].script_sig,
8991
&transaction.input[0].witness,
@@ -105,10 +107,14 @@ fn main() {
105107
// the blockchain, standardness would've required they be
106108
// either valid or 0-length.
107109
println!("\nExample one");
108-
for elem in interpreter.iter(|_, _| true) {
110+
for elem in interpreter.iter_assume_sigs() {
109111
// Don't bother checking signatures
110112
match elem.expect("no evaluation error") {
111-
miniscript::interpreter::SatisfiedConstraint::PublicKey { key, sig } => {
113+
miniscript::interpreter::SatisfiedConstraint::PublicKey { key_sig } => {
114+
// Check that the signature is ecdsa sig
115+
let (key, sig) = key_sig
116+
.as_ecdsa()
117+
.expect("Expected Ecdsa sig, found schnorr sig");
112118
println!("Signed with {}: {}", key, sig);
113119
}
114120
_ => {}
@@ -122,7 +128,7 @@ fn main() {
122128
// from the MiniscriptKey which can supplied by `to_pk_ctx` parameter. For example,
123129
// when calculating the script pubkey of a descriptor with xpubs, the secp context and
124130
// child information maybe required.
125-
let mut interpreter = miniscript::Interpreter::from_txdata(
131+
let interpreter = miniscript::Interpreter::from_txdata(
126132
&spk_input_1,
127133
&transaction.input[0].script_sig,
128134
&transaction.input[0].witness,
@@ -131,21 +137,15 @@ fn main() {
131137
)
132138
.unwrap();
133139

134-
// We can set the amount passed to `sighash_verify` to 0 because this is a legacy
135-
// transaction and so the amount won't actually be checked by the signature
136-
let vfyfn = interpreter
137-
.sighash_verify(&secp, &transaction, 0, 0)
138-
.expect("Can only fail in sighash single when corresponding output is not present");
139-
// Restrict to sighash_all just to demonstrate how to add additional filters
140-
// `&_` needed here because of https://github.com/rust-lang/rust/issues/79187
141-
let vfyfn = move |pk: &_, bitcoinsig: miniscript::bitcoin::EcdsaSig| {
142-
bitcoinsig.hash_ty == bitcoin::EcdsaSigHashType::All && vfyfn(pk, bitcoinsig)
143-
};
140+
// We can set prevouts to be empty list because this is a legacy transaction
141+
// and this information is not required for sighash computation.
142+
let prevouts = sighash::Prevouts::All(&[]);
144143

145144
println!("\nExample two");
146-
for elem in interpreter.iter(vfyfn) {
145+
for elem in interpreter.iter(&secp, &transaction, 0, &prevouts) {
147146
match elem.expect("no evaluation error") {
148-
miniscript::interpreter::SatisfiedConstraint::PublicKey { key, sig } => {
147+
miniscript::interpreter::SatisfiedConstraint::PublicKey { key_sig } => {
148+
let (key, sig) = key_sig.as_ecdsa().unwrap();
149149
println!("Signed with {}: {}", key, sig);
150150
}
151151
_ => {}
@@ -156,7 +156,7 @@ fn main() {
156156
// what happens given an apparently invalid script
157157
let secp = secp256k1::Secp256k1::new();
158158
let message = secp256k1::Message::from_slice(&[0x01; 32][..]).expect("32-byte hash");
159-
let mut interpreter = miniscript::Interpreter::from_txdata(
159+
let interpreter = miniscript::Interpreter::from_txdata(
160160
&spk_input_1,
161161
&transaction.input[0].script_sig,
162162
&transaction.input[0].witness,
@@ -165,12 +165,13 @@ fn main() {
165165
)
166166
.unwrap();
167167

168-
let iter = interpreter.iter(|pk, ecdsa_sig| {
168+
let iter = interpreter.iter_custom(Box::new(|key_sig: &KeySigPair| {
169+
let (pk, ecdsa_sig) = key_sig.as_ecdsa().expect("Ecdsa Sig");
169170
ecdsa_sig.hash_ty == bitcoin::EcdsaSigHashType::All
170171
&& secp
171172
.verify_ecdsa(&message, &ecdsa_sig.sig, &pk.inner)
172173
.is_ok()
173-
});
174+
}));
174175
println!("\nExample three");
175176
for elem in iter {
176177
let error = elem.expect_err("evaluation error");

src/interpreter/inner.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,6 @@ pub(super) fn pre_taproot_to_no_checks<Ctx: ScriptContext>(
376376
}
377377

378378
// Convert a miniscript from a specified context into an insane miniscript
379-
#[allow(dead_code)]
380379
pub(super) fn taproot_to_no_checks<Ctx: ScriptContext>(
381380
ms: &Miniscript<bitcoin::XOnlyPublicKey, Ctx>,
382381
) -> Miniscript<BitcoinKey, NoChecks> {

src/interpreter/mod.rs

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ use std::fmt;
2525
use std::str::FromStr;
2626

2727
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
28-
// use bitcoin::util::sighash;
29-
#[allow(unused_imports)]
3028
use bitcoin::{self, secp256k1};
3129
use miniscript::context::NoChecks;
3230
use miniscript::ScriptContext;
@@ -65,6 +63,24 @@ pub enum KeySigPair {
6563
Schnorr(bitcoin::XOnlyPublicKey, bitcoin::SchnorrSig),
6664
}
6765

66+
impl KeySigPair {
67+
/// Obtain a pair of ([`bitcoin::PublicKey`], [`bitcoin::EcdsaSig`]) from [`KeySigPair`]
68+
pub fn as_ecdsa(&self) -> Option<(bitcoin::PublicKey, bitcoin::EcdsaSig)> {
69+
match self {
70+
KeySigPair::Ecdsa(pk, sig) => Some((*pk, *sig)),
71+
KeySigPair::Schnorr(_, _) => None,
72+
}
73+
}
74+
75+
/// Obtain a pair of ([`bitcoin::XOnlyPublicKey`], [`bitcoin::SchnorrSig`]) from [`KeySigPair`]
76+
pub fn as_schnorr(&self) -> Option<(bitcoin::XOnlyPublicKey, bitcoin::SchnorrSig)> {
77+
match self {
78+
KeySigPair::Ecdsa(_, _) => None,
79+
KeySigPair::Schnorr(pk, sig) => Some((*pk, *sig)),
80+
}
81+
}
82+
}
83+
6884
// Internally used enum for different types of bitcoin keys
6985
// Even though we implement MiniscriptKey for BitcoinKey, we make sure that there
7086
// are little mis-use
@@ -165,19 +181,10 @@ impl<'txin> Interpreter<'txin> {
165181
})
166182
}
167183

168-
/// Creates an iterator over the satisfied spending conditions
169-
///
170-
/// Returns all satisfied constraints, even if they were redundant (i.e. did
171-
/// not contribute to the script being satisfied). For example, if a signature
172-
/// were provided for an `and_b(Pk,false)` fragment, that signature will be
173-
/// returned, even though the entire and_b must have failed and must not have
174-
/// been used.
175-
///
176-
/// In case the script is actually dissatisfied, this may return several values
177-
/// before ultimately returning an error.
184+
/// Same as [`Interpreter::iter`], but allows for a custom verification function.
178185
/// See [Self::iter_assume_sigs] for a simpler API without information about Prevouts
179186
/// but skips the signature verification
180-
pub fn iter<'iter>(
187+
pub fn iter_custom<'iter>(
181188
&'iter self,
182189
verify_sig: Box<dyn FnMut(&KeySigPair) -> bool + 'iter>,
183190
) -> Iter<'txin, 'iter> {
@@ -292,28 +299,38 @@ impl<'txin> Interpreter<'txin> {
292299
}
293300
}
294301

295-
/// Iterate over all satisfied constraints while checking signatures
302+
/// Creates an iterator over the satisfied spending conditions
303+
///
304+
/// Returns all satisfied constraints, even if they were redundant (i.e. did
305+
/// not contribute to the script being satisfied). For example, if a signature
306+
/// were provided for an `and_b(Pk,false)` fragment, that signature will be
307+
/// returned, even though the entire and_b must have failed and must not have
308+
/// been used.
309+
///
310+
/// In case the script is actually dissatisfied, this may return several values
311+
/// before ultimately returning an error.
312+
///
296313
/// Not all fields are used by legacy/segwitv0 descriptors; if you are sure this is a legacy
297314
/// spend (you can check with the `is_legacy\is_segwitv0` method) you can provide dummy data for
298315
/// the amount/prevouts.
299316
/// - For legacy outputs, no information about prevouts is required
300317
/// - For segwitv0 outputs, prevout at corresponding index with correct amount must be provided
301318
/// - For taproot outputs, information about all prevouts must be supplied
302-
pub fn iter_check_sigs<'iter, C: secp256k1::Verification>(
319+
pub fn iter<'iter, C: secp256k1::Verification>(
303320
&'iter self,
304321
secp: &'iter secp256k1::Secp256k1<C>,
305322
tx: &'txin bitcoin::Transaction,
306323
input_idx: usize,
307324
prevouts: &'iter sighash::Prevouts, // actually a 'prevouts, but 'prevouts: 'iter
308325
) -> Iter<'txin, 'iter> {
309-
self.iter(Box::new(move |sig| {
326+
self.iter_custom(Box::new(move |sig| {
310327
self.verify_sig(secp, tx, input_idx, prevouts, sig)
311328
}))
312329
}
313330

314331
/// Creates an iterator over the satisfied spending conditions without checking signatures
315332
pub fn iter_assume_sigs<'iter>(&'iter self) -> Iter<'txin, 'iter> {
316-
self.iter(Box::new(|_| true))
333+
self.iter_custom(Box::new(|_| true))
317334
}
318335

319336
/// Outputs a "descriptor" string which reproduces the spent coins

src/psbt/finalizer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ pub fn interpreter_check<C: secp256k1::Verification>(
313313
secp.ctx();
314314
true
315315
};
316-
let iter = interpreter.iter(Box::new(y));
316+
let iter = interpreter.iter_custom(Box::new(y));
317317
if let Some(error) = iter.filter_map(Result::err).next() {
318318
return Err(Error::InputError(InputError::Interpreter(error), index));
319319
};

0 commit comments

Comments
 (0)