@@ -17,7 +17,7 @@ mod types;
1717
1818use core:: fmt;
1919
20- use crate :: types:: c_uint;
20+ use crate :: types:: { c_int64 , c_uchar , c_uint} ;
2121
2222/// Do not enable any verification.
2323pub const VERIFY_NONE : c_uint = 0 ;
@@ -33,8 +33,10 @@ pub const VERIFY_CHECKLOCKTIMEVERIFY: c_uint = 1 << 9;
3333pub const VERIFY_CHECKSEQUENCEVERIFY : c_uint = 1 << 10 ;
3434/// Enable WITNESS (BIP141).
3535pub const VERIFY_WITNESS : c_uint = 1 << 11 ;
36+ /// Enable TAPROOT (BIPs 341 & 342)
37+ pub const VERIFY_TAPROOT : c_uint = 1 << 17 ;
3638
37- pub const VERIFY_ALL : c_uint = VERIFY_P2SH
39+ pub const VERIFY_ALL_PRE_TAPROOT : c_uint = VERIFY_P2SH
3840 | VERIFY_DERSIG
3941 | VERIFY_NULLDUMMY
4042 | VERIFY_CHECKLOCKTIMEVERIFY
@@ -60,6 +62,9 @@ pub fn height_to_flags(height: u32) -> u32 {
6062 if height >= 481824 {
6163 flag |= VERIFY_NULLDUMMY | VERIFY_WITNESS
6264 }
65+ if height > 709632 {
66+ flag |= VERIFY_TAPROOT
67+ }
6368
6469 flag
6570}
@@ -101,53 +106,98 @@ pub fn version() -> u32 { unsafe { ffi::bitcoinconsensus_version() as u32 } }
101106/// **Note** since the spent amount will only be checked for Segwit transactions and the above
102107/// example is not segwit, `verify` will succeed with any amount.
103108pub fn verify (
109+ // The script_pubkey of the output we are spending (e.g. `TxOut::script_pubkey.as_bytes()`).
104110 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.
107114 input_index : usize ,
108115) -> 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)
110122}
111123
112124/// Same as verify but with flags that turn past soft fork features on or off.
113125pub fn verify_with_flags (
114126 spent_output_script : & [ u8 ] ,
115127 amount : u64 ,
116128 spending_transaction : & [ u8 ] ,
129+ spent_outputs : Option < & [ Utxo ] > ,
117130 input_index : usize ,
118131 flags : u32 ,
119132) -> 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 ;
122136
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+ } ,
138174 }
139175}
140176
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+
141189pub 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 ;
144192
145193 extern "C" {
146194 /// Returns `libbitcoinconsensus` version.
147195 pub fn bitcoinconsensus_version ( ) -> c_int ;
148196
149197 /// Verifies that the transaction input correctly spends the previous
150198 /// output, considering any additional constraints specified by flags.
199+ ///
200+ /// This function does not verify Taproot inputs.
151201 pub fn bitcoinconsensus_verify_script_with_amount (
152202 script_pubkey : * const c_uchar ,
153203 script_pubkeylen : c_uint ,
@@ -158,6 +208,23 @@ pub mod ffi {
158208 flags : c_uint ,
159209 err : * mut Error ,
160210 ) -> 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 ;
161228 }
162229}
163230
@@ -171,7 +238,7 @@ pub mod ffi {
171238#[ repr( C ) ]
172239pub enum Error {
173240 /// Default value, passed to `libbitcoinconsensus` as a return parameter.
174- ERR_SCRIPT = 0 ,
241+ ERR_SCRIPT = 0 , // This is ERR_OK in Bitcoin Core.
175242 /// An invalid index for `txTo`.
176243 ERR_TX_INDEX ,
177244 /// `txToLen` did not match with the size of `txTo`.
@@ -182,6 +249,10 @@ pub enum Error {
182249 ERR_AMOUNT_REQUIRED ,
183250 /// Script verification `flags` are invalid (i.e. not part of the libconsensus interface).
184251 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 ,
185256}
186257
187258impl fmt:: Display for Error {
@@ -195,6 +266,8 @@ impl fmt::Display for Error {
195266 ERR_TX_DESERIALIZE => "an error deserializing txTo" ,
196267 ERR_AMOUNT_REQUIRED => "input amount is required if WITNESS is used" ,
197268 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" ,
198271 } ;
199272 f. write_str ( s)
200273 }
@@ -206,8 +279,14 @@ impl std::error::Error for Error {
206279 use self :: Error :: * ;
207280
208281 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 ,
211290 }
212291 }
213292}
@@ -268,10 +347,13 @@ mod tests {
268347 spent. from_hex ( ) . unwrap ( ) . as_slice ( ) ,
269348 amount,
270349 spending. from_hex ( ) . unwrap ( ) . as_slice ( ) ,
350+ None ,
271351 input,
272352 )
273353 }
274354
275355 #[ 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+ }
277359}
0 commit comments