|
12 | 12 | // If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
13 | 13 | //
|
14 | 14 |
|
15 |
| -//! Example: Verifying a signed transaction |
| 15 | +//! Example: Verifying a signed transaction. |
16 | 16 |
|
17 | 17 | use bitcoin;
|
18 | 18 | use miniscript;
|
19 | 19 |
|
20 | 20 | use bitcoin::consensus::Decodable;
|
| 21 | +use bitcoin::secp256k1::{self, Secp256k1}; |
21 | 22 | use bitcoin::util::sighash;
|
22 |
| -use bitcoin::{secp256k1, TxOut}; // secp256k1 re-exported from rust-bitcoin |
23 | 23 | use miniscript::interpreter::KeySigPair;
|
24 | 24 | use std::str::FromStr;
|
25 | 25 |
|
26 | 26 | 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 { |
27 | 128 | // tx `f27eba163c38ad3f34971198687a3f1882b7ec818599ffe469a8440d82261c98`
|
28 | 129 | #[cfg_attr(feature="cargo-fmt", rustfmt_skip)]
|
29 | 130 | let tx_bytes = vec![
|
@@ -78,103 +179,13 @@ fn main() {
|
78 | 179 | 0xd7, 0xc0, 0x28, 0xb7, 0x90, 0xb0, 0xcf, 0x43, 0xe0, 0x27, 0xd9, 0x1d,
|
79 | 180 | 0xe7, 0x87, 0x09, 0x5d, 0x07, 0x00,
|
80 | 181 | ];
|
81 |
| - let transaction = |
82 |
| - bitcoin::Transaction::consensus_decode(&mut &tx_bytes[..]).expect("decode transaction"); |
83 | 182 |
|
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![ |
85 | 188 | 0xa9, 0x14, 0x92, 0x09, 0xa8, 0xf9, 0x0c, 0x58, 0x4b, 0xb5, 0x97, 0x4d, 0x58, 0x68, 0x72,
|
86 | 189 | 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 | + ]) |
180 | 191 | }
|
0 commit comments