@@ -17,7 +17,7 @@ mod types;
17
17
18
18
use core:: fmt;
19
19
20
- use crate :: types:: c_uint;
20
+ use crate :: types:: { c_int64 , c_uchar , c_uint} ;
21
21
22
22
/// Do not enable any verification.
23
23
pub const VERIFY_NONE : c_uint = 0 ;
@@ -33,8 +33,10 @@ pub const VERIFY_CHECKLOCKTIMEVERIFY: c_uint = 1 << 9;
33
33
pub const VERIFY_CHECKSEQUENCEVERIFY : c_uint = 1 << 10 ;
34
34
/// Enable WITNESS (BIP141).
35
35
pub const VERIFY_WITNESS : c_uint = 1 << 11 ;
36
+ /// Enable TAPROOT (BIPs 341 & 342)
37
+ pub const VERIFY_TAPROOT : c_uint = 1 << 17 ;
36
38
37
- pub const VERIFY_ALL : c_uint = VERIFY_P2SH
39
+ pub const VERIFY_ALL_PRE_TAPROOT : c_uint = VERIFY_P2SH
38
40
| VERIFY_DERSIG
39
41
| VERIFY_NULLDUMMY
40
42
| VERIFY_CHECKLOCKTIMEVERIFY
@@ -60,6 +62,9 @@ pub fn height_to_flags(height: u32) -> u32 {
60
62
if height >= 481824 {
61
63
flag |= VERIFY_NULLDUMMY | VERIFY_WITNESS
62
64
}
65
+ if height > 709632 {
66
+ flag |= VERIFY_TAPROOT
67
+ }
63
68
64
69
flag
65
70
}
@@ -101,53 +106,98 @@ pub fn version() -> u32 { unsafe { ffi::bitcoinconsensus_version() as u32 } }
101
106
/// **Note** since the spent amount will only be checked for Segwit transactions and the above
102
107
/// example is not segwit, `verify` will succeed with any amount.
103
108
pub fn verify (
109
+ // The script_pubkey of the output we are spending (e.g. `TxOut::script_pubkey.as_bytes()`).
104
110
spent_output : & [ u8 ] ,
105
- amount : u64 ,
106
- spending_transaction : & [ u8 ] ,
111
+ amount : u64 , // The amount of the output is spending (e.g. `TxOut::value`)
112
+ spending_transaction : & [ u8 ] , // Create using `tx.serialize().as_slice()`
113
+ spent_outputs : Option < & [ Utxo ] > , // None for pre-taproot.
107
114
input_index : usize ,
108
115
) -> Result < ( ) , Error > {
109
- verify_with_flags ( spent_output, amount, spending_transaction, input_index, VERIFY_ALL )
116
+ let flags = match spent_outputs {
117
+ Some ( _) => VERIFY_ALL_PRE_TAPROOT | VERIFY_TAPROOT ,
118
+ None => VERIFY_ALL_PRE_TAPROOT ,
119
+ } ;
120
+
121
+ verify_with_flags ( spent_output, amount, spending_transaction, spent_outputs, input_index, flags)
110
122
}
111
123
112
124
/// Same as verify but with flags that turn past soft fork features on or off.
113
125
pub fn verify_with_flags (
114
126
spent_output_script : & [ u8 ] ,
115
127
amount : u64 ,
116
128
spending_transaction : & [ u8 ] ,
129
+ spent_outputs : Option < & [ Utxo ] > ,
117
130
input_index : usize ,
118
131
flags : u32 ,
119
132
) -> Result < ( ) , Error > {
120
- unsafe {
121
- let mut error = Error :: ERR_SCRIPT ;
133
+ match spent_outputs {
134
+ Some ( spent_outputs) => unsafe {
135
+ let mut error = Error :: ERR_SCRIPT ;
122
136
123
- let ret = ffi:: bitcoinconsensus_verify_script_with_amount (
124
- spent_output_script. as_ptr ( ) ,
125
- spent_output_script. len ( ) as c_uint ,
126
- amount,
127
- spending_transaction. as_ptr ( ) ,
128
- spending_transaction. len ( ) as c_uint ,
129
- input_index as c_uint ,
130
- flags as c_uint ,
131
- & mut error,
132
- ) ;
133
- if ret != 1 {
134
- Err ( error)
135
- } else {
136
- Ok ( ( ) )
137
- }
137
+ let ret = ffi:: bitcoinconsensus_verify_script_with_spent_outputs (
138
+ spent_output_script. as_ptr ( ) ,
139
+ spent_output_script. len ( ) as c_uint ,
140
+ amount,
141
+ spending_transaction. as_ptr ( ) ,
142
+ spending_transaction. len ( ) as c_uint ,
143
+ spent_outputs. as_ptr ( ) as * const c_uchar ,
144
+ spent_outputs. len ( ) as c_uint ,
145
+ input_index as c_uint ,
146
+ flags as c_uint ,
147
+ & mut error,
148
+ ) ;
149
+ if ret != 1 {
150
+ Err ( error)
151
+ } else {
152
+ Ok ( ( ) )
153
+ }
154
+ } ,
155
+ None => unsafe {
156
+ let mut error = Error :: ERR_SCRIPT ;
157
+
158
+ let ret = ffi:: bitcoinconsensus_verify_script_with_amount (
159
+ spent_output_script. as_ptr ( ) ,
160
+ spent_output_script. len ( ) as c_uint ,
161
+ amount,
162
+ spending_transaction. as_ptr ( ) ,
163
+ spending_transaction. len ( ) as c_uint ,
164
+ input_index as c_uint ,
165
+ flags as c_uint ,
166
+ & mut error,
167
+ ) ;
168
+ if ret != 1 {
169
+ Err ( error)
170
+ } else {
171
+ Ok ( ( ) )
172
+ }
173
+ } ,
138
174
}
139
175
}
140
176
177
+ /// Mimics the Bitcoin Core UTXO typedef (bitcoinconsenus.h)
178
+ // This is the TxOut data for utxos being spent, i.e., previous outputs.
179
+ #[ repr( C ) ]
180
+ pub struct Utxo {
181
+ /// Pointer to the scriptPubkey bytes.
182
+ pub script_pubkey : * const c_uchar ,
183
+ /// The length of the scriptPubkey.
184
+ pub script_pubkey_len : c_uint ,
185
+ /// The value in sats.
186
+ pub value : c_int64 ,
187
+ }
188
+
141
189
pub mod ffi {
142
- use crate :: types :: { c_int , c_uchar , c_uint } ;
143
- use crate :: Error ;
190
+ use super :: * ;
191
+ use crate :: types :: c_int ;
144
192
145
193
extern "C" {
146
194
/// Returns `libbitcoinconsensus` version.
147
195
pub fn bitcoinconsensus_version ( ) -> c_int ;
148
196
149
197
/// Verifies that the transaction input correctly spends the previous
150
198
/// output, considering any additional constraints specified by flags.
199
+ ///
200
+ /// This function does not verify Taproot inputs.
151
201
pub fn bitcoinconsensus_verify_script_with_amount (
152
202
script_pubkey : * const c_uchar ,
153
203
script_pubkeylen : c_uint ,
@@ -158,6 +208,23 @@ pub mod ffi {
158
208
flags : c_uint ,
159
209
err : * mut Error ,
160
210
) -> c_int ;
211
+
212
+ /// Verifies that the transaction input correctly spends the previous
213
+ /// output, considering any additional constraints specified by flags.
214
+ ///
215
+ /// This function verifies Taproot inputs.
216
+ pub fn bitcoinconsensus_verify_script_with_spent_outputs (
217
+ script_pubkey : * const c_uchar ,
218
+ script_pubkeylen : c_uint ,
219
+ amount : u64 ,
220
+ tx_to : * const c_uchar ,
221
+ tx_tolen : c_uint ,
222
+ spent_outputs : * const c_uchar ,
223
+ num_spent_outputs : c_uint ,
224
+ n_in : c_uint ,
225
+ flags : c_uint ,
226
+ err : * mut Error ,
227
+ ) -> c_int ;
161
228
}
162
229
}
163
230
@@ -171,7 +238,7 @@ pub mod ffi {
171
238
#[ repr( C ) ]
172
239
pub enum Error {
173
240
/// Default value, passed to `libbitcoinconsensus` as a return parameter.
174
- ERR_SCRIPT = 0 ,
241
+ ERR_SCRIPT = 0 , // This is ERR_OK in Bitcoin Core.
175
242
/// An invalid index for `txTo`.
176
243
ERR_TX_INDEX ,
177
244
/// `txToLen` did not match with the size of `txTo`.
@@ -182,6 +249,10 @@ pub enum Error {
182
249
ERR_AMOUNT_REQUIRED ,
183
250
/// Script verification `flags` are invalid (i.e. not part of the libconsensus interface).
184
251
ERR_INVALID_FLAGS ,
252
+ /// Verifying Taproot input requires previous outputs.
253
+ ERR_SPENT_OUTPUTS_REQUIRED ,
254
+ /// Taproot outputs don't match.
255
+ ERR_SPENT_OUTPUTS_MISMATCH ,
185
256
}
186
257
187
258
impl fmt:: Display for Error {
@@ -195,6 +266,8 @@ impl fmt::Display for Error {
195
266
ERR_TX_DESERIALIZE => "an error deserializing txTo" ,
196
267
ERR_AMOUNT_REQUIRED => "input amount is required if WITNESS is used" ,
197
268
ERR_INVALID_FLAGS => "script verification flags are invalid" ,
269
+ ERR_SPENT_OUTPUTS_REQUIRED => "verifying taproot input requires previous outputs" ,
270
+ ERR_SPENT_OUTPUTS_MISMATCH => "taproot outputs don't match" ,
198
271
} ;
199
272
f. write_str ( s)
200
273
}
@@ -206,8 +279,14 @@ impl std::error::Error for Error {
206
279
use self :: Error :: * ;
207
280
208
281
match * self {
209
- ERR_SCRIPT | ERR_TX_INDEX | ERR_TX_SIZE_MISMATCH | ERR_TX_DESERIALIZE
210
- | ERR_AMOUNT_REQUIRED | ERR_INVALID_FLAGS => None ,
282
+ ERR_SCRIPT
283
+ | ERR_TX_INDEX
284
+ | ERR_TX_SIZE_MISMATCH
285
+ | ERR_TX_DESERIALIZE
286
+ | ERR_AMOUNT_REQUIRED
287
+ | ERR_INVALID_FLAGS
288
+ | ERR_SPENT_OUTPUTS_REQUIRED
289
+ | ERR_SPENT_OUTPUTS_MISMATCH => None ,
211
290
}
212
291
}
213
292
}
@@ -268,10 +347,13 @@ mod tests {
268
347
spent. from_hex ( ) . unwrap ( ) . as_slice ( ) ,
269
348
amount,
270
349
spending. from_hex ( ) . unwrap ( ) . as_slice ( ) ,
350
+ None ,
271
351
input,
272
352
)
273
353
}
274
354
275
355
#[ test]
276
- fn invalid_flags_test ( ) { verify_with_flags ( & [ ] , 0 , & [ ] , 0 , VERIFY_ALL + 1 ) . unwrap_err ( ) ; }
356
+ fn invalid_flags_test ( ) {
357
+ verify_with_flags ( & [ ] , 0 , & [ ] , None , 0 , VERIFY_ALL_PRE_TAPROOT + 1 ) . unwrap_err ( ) ;
358
+ }
277
359
}
0 commit comments