Skip to content

Commit 5ad6339

Browse files
committed
examples: Refactor verify_tx
In an effort to make the example more clear; refactor the `verify_tx` example, improving docs and adding separation between setup, and each example section of the main function. Refactor only, no logic changes.
1 parent 1198be7 commit 5ad6339

File tree

1 file changed

+109
-98
lines changed

1 file changed

+109
-98
lines changed

examples/verify_tx.rs

Lines changed: 109 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,119 @@
1212
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
1313
//
1414

15-
//! Example: Verifying a signed transaction
15+
//! Example: Verifying a signed transaction.
1616
1717
use bitcoin;
1818
use miniscript;
1919

2020
use bitcoin::consensus::Decodable;
21+
use bitcoin::secp256k1::{self, Secp256k1};
2122
use bitcoin::util::sighash;
22-
use bitcoin::{secp256k1, TxOut}; // secp256k1 re-exported from rust-bitcoin
2323
use miniscript::interpreter::KeySigPair;
2424
use std::str::FromStr;
2525

2626
fn main() {
27+
//
28+
// Setup
29+
//
30+
31+
let tx = hard_coded_transaction();
32+
let spk_input_1 = hard_coded_script_pubkey();
33+
34+
let interpreter = miniscript::Interpreter::from_txdata(
35+
&spk_input_1,
36+
&tx.input[0].script_sig,
37+
&tx.input[0].witness,
38+
0,
39+
0,
40+
)
41+
.unwrap();
42+
43+
let desc_string = interpreter.inferred_descriptor_string();
44+
println!("Descriptor: {}", desc_string);
45+
46+
// To do sanity checks on the transaction using the interpreter parse the
47+
// descriptor with `from_str`.
48+
let _ = miniscript::Descriptor::<bitcoin::PublicKey>::from_str(&desc_string)
49+
.expect("sanity checks to pass");
50+
// Alternately, use `inferred_descriptor` which does sanity checks for us also.
51+
let _ = interpreter.inferred_descriptor().expect("same as from_str");
52+
53+
// Example one
54+
//
55+
// Learn which keys were used, not bothering to verify the signatures
56+
// (trusting that if they're on the blockchain, standardness would've
57+
// required they be either valid or 0-length.
58+
59+
println!("\n\nExample one:\n");
60+
61+
for elem in interpreter.iter_assume_sigs() {
62+
// Don't bother checking signatures.
63+
match elem.expect("no evaluation error") {
64+
miniscript::interpreter::SatisfiedConstraint::PublicKey { key_sig } => {
65+
let (key, sig) = key_sig
66+
.as_ecdsa()
67+
.expect("expected ecdsa sig, found schnorr sig");
68+
69+
println!("Signed with:\n key: {}\n sig: {}", key, sig);
70+
}
71+
_ => {}
72+
}
73+
}
74+
75+
// Example two
76+
//
77+
// Verify the signatures to ensure that invalid signatures are not treated
78+
// as having participated in the script
79+
80+
println!("\n\nExample two:\n");
81+
let secp = Secp256k1::new();
82+
83+
// Sometimes it is necessary to have additional information to get the
84+
// `bitcoin::PublicKey` from the `MiniscriptKey` which can be supplied by
85+
// the `to_pk_ctx` parameter. For example, when calculating the script
86+
// pubkey of a descriptor with xpubs, the secp context and child information
87+
// maybe required.
88+
89+
// We can set prevouts to be empty list because this is a legacy transaction
90+
// and this information is not required for sighash computation.
91+
let prevouts = sighash::Prevouts::All::<bitcoin::TxOut>(&[]);
92+
93+
for elem in interpreter.iter(&secp, &tx, 0, &prevouts) {
94+
match elem.expect("no evaluation error") {
95+
miniscript::interpreter::SatisfiedConstraint::PublicKey { key_sig } => {
96+
let (key, sig) = key_sig.as_ecdsa().unwrap();
97+
println!("Signed with:\n key: {}\n sig: {}", key, sig);
98+
}
99+
_ => {}
100+
}
101+
}
102+
103+
// Example three
104+
//
105+
// Same, but with the wrong signature hash, to demonstrate what happens
106+
// given an apparently invalid script.
107+
let secp = Secp256k1::new();
108+
let message = secp256k1::Message::from_slice(&[0x01; 32][..]).expect("32-byte hash");
109+
110+
let iter = interpreter.iter_custom(Box::new(|key_sig: &KeySigPair| {
111+
let (pk, ecdsa_sig) = key_sig.as_ecdsa().expect("Ecdsa Sig");
112+
ecdsa_sig.hash_ty == bitcoin::EcdsaSighashType::All
113+
&& secp
114+
.verify_ecdsa(&message, &ecdsa_sig.sig, &pk.inner)
115+
.is_ok()
116+
}));
117+
118+
println!("\n\nExample three:\n");
119+
120+
for elem in iter {
121+
let error = elem.expect_err("evaluation error");
122+
println!("Evaluation error: {}", error);
123+
}
124+
}
125+
126+
/// Returns an arbitrary transaction.
127+
fn hard_coded_transaction() -> bitcoin::Transaction {
27128
// tx `f27eba163c38ad3f34971198687a3f1882b7ec818599ffe469a8440d82261c98`
28129
#[cfg_attr(feature="cargo-fmt", rustfmt_skip)]
29130
let tx_bytes = vec![
@@ -78,103 +179,13 @@ fn main() {
78179
0xd7, 0xc0, 0x28, 0xb7, 0x90, 0xb0, 0xcf, 0x43, 0xe0, 0x27, 0xd9, 0x1d,
79180
0xe7, 0x87, 0x09, 0x5d, 0x07, 0x00,
80181
];
81-
let transaction =
82-
bitcoin::Transaction::consensus_decode(&mut &tx_bytes[..]).expect("decode transaction");
83182

84-
let spk_input_1 = bitcoin::Script::from(vec![
183+
bitcoin::Transaction::consensus_decode(&mut &tx_bytes[..]).expect("decode transaction")
184+
}
185+
186+
fn hard_coded_script_pubkey() -> bitcoin::Script {
187+
bitcoin::Script::from(vec![
85188
0xa9, 0x14, 0x92, 0x09, 0xa8, 0xf9, 0x0c, 0x58, 0x4b, 0xb5, 0x97, 0x4d, 0x58, 0x68, 0x72,
86189
0x49, 0xe5, 0x32, 0xde, 0x59, 0xf4, 0xbc, 0x87,
87-
]);
88-
let interpreter = miniscript::Interpreter::from_txdata(
89-
&spk_input_1,
90-
&transaction.input[0].script_sig,
91-
&transaction.input[0].witness,
92-
0,
93-
0,
94-
)
95-
.unwrap();
96-
97-
let desc_string = interpreter.inferred_descriptor_string();
98-
println!("Descriptor: {}", desc_string);
99-
miniscript::Descriptor::<bitcoin::PublicKey>::from_str(&desc_string)
100-
.expect("this descriptor can be reparsed with sanity checks passing");
101-
interpreter
102-
.inferred_descriptor()
103-
.expect("we can use this method to do the above from_str for us");
104-
105-
// 1. Example one: learn which keys were used, not bothering
106-
// to verify the signatures (trusting that if they're on
107-
// the blockchain, standardness would've required they be
108-
// either valid or 0-length.
109-
println!("\nExample one");
110-
for elem in interpreter.iter_assume_sigs() {
111-
// Don't bother checking signatures
112-
match elem.expect("no evaluation error") {
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");
118-
println!("Signed with {}: {}", key, sig);
119-
}
120-
_ => {}
121-
}
122-
}
123-
124-
// 2. Example two: verify the signatures to ensure that invalid
125-
// signatures are not treated as having participated in the script
126-
let secp = secp256k1::Secp256k1::new();
127-
// Sometimes it is necessary to have additional information to get the bitcoin::PublicKey
128-
// from the MiniscriptKey which can supplied by `to_pk_ctx` parameter. For example,
129-
// when calculating the script pubkey of a descriptor with xpubs, the secp context and
130-
// child information maybe required.
131-
let interpreter = miniscript::Interpreter::from_txdata(
132-
&spk_input_1,
133-
&transaction.input[0].script_sig,
134-
&transaction.input[0].witness,
135-
0,
136-
0,
137-
)
138-
.unwrap();
139-
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::<TxOut>(&[]);
143-
144-
println!("\nExample two");
145-
for elem in interpreter.iter(&secp, &transaction, 0, &prevouts) {
146-
match elem.expect("no evaluation error") {
147-
miniscript::interpreter::SatisfiedConstraint::PublicKey { key_sig } => {
148-
let (key, sig) = key_sig.as_ecdsa().unwrap();
149-
println!("Signed with {}: {}", key, sig);
150-
}
151-
_ => {}
152-
}
153-
}
154-
155-
// 3. Example three: same, but with the wrong signature hash, to demonstrate
156-
// what happens given an apparently invalid script
157-
let secp = secp256k1::Secp256k1::new();
158-
let message = secp256k1::Message::from_slice(&[0x01; 32][..]).expect("32-byte hash");
159-
let interpreter = miniscript::Interpreter::from_txdata(
160-
&spk_input_1,
161-
&transaction.input[0].script_sig,
162-
&transaction.input[0].witness,
163-
0,
164-
0,
165-
)
166-
.unwrap();
167-
168-
let iter = interpreter.iter_custom(Box::new(|key_sig: &KeySigPair| {
169-
let (pk, ecdsa_sig) = key_sig.as_ecdsa().expect("Ecdsa Sig");
170-
ecdsa_sig.hash_ty == bitcoin::EcdsaSighashType::All
171-
&& secp
172-
.verify_ecdsa(&message, &ecdsa_sig.sig, &pk.inner)
173-
.is_ok()
174-
}));
175-
println!("\nExample three");
176-
for elem in iter {
177-
let error = elem.expect_err("evaluation error");
178-
println!("Evaluation error: {}", error);
179-
}
190+
])
180191
}

0 commit comments

Comments
 (0)