Skip to content

Commit 074a12e

Browse files
feat(core): improve documentation for verify function
1 parent 2543626 commit 074a12e

File tree

1 file changed

+280
-14
lines changed

1 file changed

+280
-14
lines changed

src/core/verify.rs

Lines changed: 280 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,149 @@
1+
//! Script verification and validation.
2+
//!
3+
//! This module provides functionality for verifying that transaction inputs satisfy
4+
//! the spending conditions defined by their corresponding output scripts.
5+
//!
6+
//! # Overview
7+
//!
8+
//! Script verification involves checking that a transaction input's
9+
//! unlocking script (scriptSig) and witness data satisfy the conditions
10+
//! specified in the output's locking script (scriptPubkey). The verification
11+
//! process depends on the script type and the consensus rules active at the
12+
//! time.
13+
//!
14+
//! # Verification Flags
15+
//!
16+
//! Consensus rules have evolved over time through soft forks. Verification flags
17+
//! allow you to specify which consensus rules to enforce:
18+
//!
19+
//! | Flag | Description | BIP |
20+
//! |------|-------------|-----|
21+
//! | [`VERIFY_P2SH`] | Pay-to-Script-Hash validation | BIP 16 |
22+
//! | [`VERIFY_DERSIG`] | Strict DER signature encoding | BIP 66 |
23+
//! | [`VERIFY_NULLDUMMY`] | Dummy stack element must be empty | BIP 147 |
24+
//! | [`VERIFY_CHECKLOCKTIMEVERIFY`] | CHECKLOCKTIMEVERIFY opcode | BIP 65 |
25+
//! | [`VERIFY_CHECKSEQUENCEVERIFY`] | CHECKSEQUENCEVERIFY opcode | BIP 112 |
26+
//! | [`VERIFY_WITNESS`] | Segregated Witness validation | BIP 141/143 |
27+
//! | [`VERIFY_TAPROOT`] | Taproot validation | BIP 341/342 |
28+
//!
29+
//! # Common Flag Combinations
30+
//!
31+
//! - [`VERIFY_ALL_PRE_TAPROOT`]: All rules except Taproot (for pre-Taproot blocks)
32+
//! - [`VERIFY_ALL`]: All consensus rules including Taproot
33+
//!
34+
//! # Examples
35+
//!
36+
//! ## Basic verification with all consensus rules
37+
//!
38+
//! ```no_run
39+
//! # use bitcoinkernel::{prelude::*, Transaction, verify, VERIFY_ALL};
40+
//! # let spending_tx_bytes = vec![];
41+
//! # let prev_tx_bytes = vec![];
42+
//! # let spending_tx = Transaction::new(&spending_tx_bytes).unwrap();
43+
//! # let prev_tx = Transaction::new(&prev_tx_bytes).unwrap();
44+
//! let prev_output = prev_tx.output(0).unwrap();
45+
//!
46+
//! let result = verify(
47+
//! &prev_output.script_pubkey(),
48+
//! Some(prev_output.value()),
49+
//! &spending_tx,
50+
//! 0,
51+
//! Some(VERIFY_ALL),
52+
//! &[prev_output],
53+
//! );
54+
//!
55+
//! match result {
56+
//! Ok(()) => println!("Script verification passed"),
57+
//! Err(e) => println!("Script verification failed: {}", e),
58+
//! }
59+
//! ```
60+
//!
61+
//! ## Verifying pre-Taproot transactions
62+
//!
63+
//! ```no_run
64+
//! # use bitcoinkernel::{prelude::*, Transaction, verify, VERIFY_ALL_PRE_TAPROOT};
65+
//! # let spending_tx_bytes = vec![];
66+
//! # let prev_tx_bytes = vec![];
67+
//! # let spending_tx = Transaction::new(&spending_tx_bytes).unwrap();
68+
//! # let prev_tx = Transaction::new(&prev_tx_bytes).unwrap();
69+
//! # let prev_output = prev_tx.output(0).unwrap();
70+
//! let result = verify(
71+
//! &prev_output.script_pubkey(),
72+
//! Some(prev_output.value()),
73+
//! &spending_tx,
74+
//! 0,
75+
//! Some(VERIFY_ALL_PRE_TAPROOT),
76+
//! &[prev_output],
77+
//! );
78+
//! ```
79+
//!
80+
//! ## Verifying with multiple spent outputs
81+
//!
82+
//! ```no_run
83+
//! # use bitcoinkernel::{prelude::*, Transaction, verify, VERIFY_ALL};
84+
//! # let spending_tx_bytes = vec![];
85+
//! # let prev_tx1_bytes = vec![];
86+
//! # let prev_tx2_bytes = vec![];
87+
//! # let spending_tx = Transaction::new(&spending_tx_bytes).unwrap();
88+
//! # let prev_tx1 = Transaction::new(&prev_tx1_bytes).unwrap();
89+
//! # let prev_tx2 = Transaction::new(&prev_tx2_bytes).unwrap();
90+
//! let spent_outputs = vec![
91+
//! prev_tx1.output(0).unwrap(),
92+
//! prev_tx2.output(1).unwrap(),
93+
//! ];
94+
//!
95+
//! let result = verify(
96+
//! &spent_outputs[0].script_pubkey(),
97+
//! Some(spent_outputs[0].value()),
98+
//! &spending_tx,
99+
//! 0,
100+
//! Some(VERIFY_ALL),
101+
//! &spent_outputs,
102+
//! );
103+
//! ```
104+
//!
105+
//! ## Handling verification errors
106+
//!
107+
//! ```no_run
108+
//! # use bitcoinkernel::{prelude::*, Transaction, verify, VERIFY_ALL, KernelError, ScriptVerifyError};
109+
//! # let spending_tx_bytes = vec![];
110+
//! # let prev_tx_bytes = vec![];
111+
//! # let spending_tx = Transaction::new(&spending_tx_bytes).unwrap();
112+
//! # let prev_tx = Transaction::new(&prev_tx_bytes).unwrap();
113+
//! # let prev_output = prev_tx.output(0).unwrap();
114+
//! let result = verify(
115+
//! &prev_output.script_pubkey(),
116+
//! Some(prev_output.value()),
117+
//! &spending_tx,
118+
//! 0,
119+
//! Some(VERIFY_ALL),
120+
//! &[prev_output],
121+
//! );
122+
//!
123+
//! match result {
124+
//! Ok(()) => {
125+
//! println!("Valid transaction");
126+
//! }
127+
//! Err(KernelError::ScriptVerify(ScriptVerifyError::SpentOutputsRequired)) => {
128+
//! println!("This script type requires spent outputs");
129+
//! }
130+
//! Err(KernelError::ScriptVerify(ScriptVerifyError::InvalidFlagsCombination)) => {
131+
//! println!("Invalid combination of verification flags");
132+
//! }
133+
//! Err(KernelError::ScriptVerify(ScriptVerifyError::Invalid)) => {
134+
//! println!("Script verification failed - invalid script");
135+
//! }
136+
//! Err(e) => {
137+
//! println!("Other error: {}", e);
138+
//! }
139+
//! }
140+
//! ```
141+
//!
142+
//! # Thread Safety
143+
//!
144+
//! The [`verify`] function is thread-safe and can be called concurrently from multiple
145+
//! threads. All types used in verification are `Send + Sync`.
146+
1147
use std::{
2148
error::Error,
3149
fmt::{self, Display, Formatter},
@@ -22,26 +168,36 @@ use crate::{
22168
KernelError, ScriptPubkeyExt, TransactionExt, TxOutExt,
23169
};
24170

171+
/// No verification flags.
25172
pub const VERIFY_NONE: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_NONE;
26173

174+
/// Validate Pay-to-Script-Hash (BIP 16).
27175
pub const VERIFY_P2SH: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_P2SH;
28176

177+
/// Require strict DER encoding for ECDSA signatures (BIP 66).
29178
pub const VERIFY_DERSIG: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_DERSIG;
30179

180+
/// Require the dummy element in OP_CHECKMULTISIG to be empty (BIP 147).
31181
pub const VERIFY_NULLDUMMY: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_NULLDUMMY;
32182

183+
/// Enable OP_CHECKLOCKTIMEVERIFY (BIP 65).
33184
pub const VERIFY_CHECKLOCKTIMEVERIFY: btck_ScriptVerificationFlags =
34185
BTCK_SCRIPT_VERIFICATION_FLAGS_CHECKLOCKTIMEVERIFY;
35186

187+
/// Enable OP_CHECKSEQUENCEVERIFY (BIP 112).
36188
pub const VERIFY_CHECKSEQUENCEVERIFY: btck_ScriptVerificationFlags =
37189
BTCK_SCRIPT_VERIFICATION_FLAGS_CHECKSEQUENCEVERIFY;
38190

191+
/// Validate Segregated Witness programs (BIP 141/143).
39192
pub const VERIFY_WITNESS: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_WITNESS;
40193

194+
/// Validate Taproot spends (BIP 341/342). Requires spent outputs.
41195
pub const VERIFY_TAPROOT: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_TAPROOT;
42196

197+
/// All consensus rules.
43198
pub const VERIFY_ALL: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_ALL;
44199

200+
/// All consensus rules except Taproot.
45201
pub const VERIFY_ALL_PRE_TAPROOT: btck_ScriptVerificationFlags = VERIFY_P2SH
46202
| VERIFY_DERSIG
47203
| VERIFY_NULLDUMMY
@@ -51,17 +207,84 @@ pub const VERIFY_ALL_PRE_TAPROOT: btck_ScriptVerificationFlags = VERIFY_P2SH
51207

52208
/// Verifies a transaction input against its corresponding output script.
53209
///
210+
/// This function checks that the transaction input at the specified index properly
211+
/// satisfies the spending conditions defined by the output script. The verification
212+
/// process depends on the script type and the consensus rules specified by the flags.
213+
///
54214
/// # Arguments
55-
/// * `script_pubkey` - The output script to verify against
56-
/// * `amount` - Needs to be set if the segwit flag is set
57-
/// * `tx_to` - The transaction containing the input to verify
58-
/// * `input_index` - The index of the input within `tx_to` to verify
59-
/// * `flags` - Defaults to all if none
60-
/// * `spent_output` - The outputs being spent by this transaction
215+
///
216+
/// * `script_pubkey` - The output script (locking script) to verify against
217+
/// * `amount` - The amount in satoshis of the output being spent. Required for SegWit
218+
/// and Taproot scripts (when [`VERIFY_WITNESS`] or [`VERIFY_TAPROOT`] flags are set).
219+
/// Optional for pre-SegWit scripts.
220+
/// * `tx_to` - The transaction containing the input to verify (the spending transaction)
221+
/// * `input_index` - The zero-based index of the input within `tx_to` to verify
222+
/// * `flags` - Verification flags specifying which consensus rules to enforce. If `None`,
223+
/// defaults to [`VERIFY_ALL`]. Combine multiple flags using bitwise OR (`|`).
224+
/// * `spent_outputs` - The outputs being spent by the transaction. For SegWit and Taproot,
225+
/// this should contain all outputs spent by all inputs in the transaction. For pre-SegWit,
226+
/// this can be empty or contain just the output being spent. The length must either be 0
227+
/// or match the number of inputs in the transaction.
61228
///
62229
/// # Returns
63-
/// * `Ok(())` if verification succeeds
64-
/// * [`KernelError::ScriptVerify`] an error describing the failure
230+
///
231+
/// * `Ok(())` - Verification succeeded; the input properly spends the output
232+
/// * `Err(KernelError::ScriptVerify(ScriptVerifyError::TxInputIndex))` - Input index out of bounds
233+
/// * `Err(KernelError::ScriptVerify(ScriptVerifyError::SpentOutputsMismatch))` - The spent_outputs
234+
/// length is non-zero but doesn't match the number of inputs
235+
/// * `Err(KernelError::ScriptVerify(ScriptVerifyError::InvalidFlags))` - Invalid verification flags
236+
/// * `Err(KernelError::ScriptVerify(ScriptVerifyError::InvalidFlagsCombination))` - Incompatible
237+
/// combination of flags
238+
/// * `Err(KernelError::ScriptVerify(ScriptVerifyError::SpentOutputsRequired))` - Spent outputs
239+
/// are required for this script type but were not provided
240+
/// * `Err(KernelError::ScriptVerify(ScriptVerifyError::Invalid))` - Script verification failed;
241+
/// the input does not properly satisfy the output's spending conditions
242+
///
243+
/// # Examples
244+
///
245+
/// ## Verifying a P2PKH transaction
246+
///
247+
/// ```no_run
248+
/// # use bitcoinkernel::{prelude::*, Transaction, TxOut, verify, VERIFY_ALL};
249+
/// # let tx_bytes = vec![];
250+
/// # let spending_tx = Transaction::new(&tx_bytes).unwrap();
251+
/// # let prev_tx = Transaction::new(&tx_bytes).unwrap();
252+
/// let prev_output = prev_tx.output(0).unwrap();
253+
///
254+
/// let result = verify(
255+
/// &prev_output.script_pubkey(),
256+
/// None,
257+
/// &spending_tx,
258+
/// 0,
259+
/// Some(VERIFY_ALL),
260+
/// &[] as &[TxOut],
261+
/// );
262+
/// ```
263+
///
264+
/// ## Using custom flags
265+
///
266+
/// ```no_run
267+
/// # use bitcoinkernel::{prelude::*, Transaction, TxOut, verify, VERIFY_P2SH, VERIFY_DERSIG};
268+
/// # let tx_bytes = vec![];
269+
/// # let spending_tx = Transaction::new(&tx_bytes).unwrap();
270+
/// # let prev_output = spending_tx.output(0).unwrap();
271+
/// // Only verify P2SH and DERSIG rules
272+
/// let custom_flags = VERIFY_P2SH | VERIFY_DERSIG;
273+
///
274+
/// let result = verify(
275+
/// &prev_output.script_pubkey(),
276+
/// None,
277+
/// &spending_tx,
278+
/// 0,
279+
/// Some(custom_flags),
280+
/// &[] as &[TxOut],
281+
/// );
282+
/// ```
283+
///
284+
/// # Panics
285+
///
286+
/// This function does not panic under normal circumstances. All error conditions
287+
/// are returned as `Result::Err`.
65288
pub fn verify(
66289
script_pubkey: &impl ScriptPubkeyExt,
67290
amount: Option<i64>,
@@ -136,18 +359,35 @@ pub fn verify(
136359
}
137360
}
138361

139-
/// Status of script verification operations.
362+
/// Internal status codes from the C verification function.
140363
///
141-
/// Indicates the result of verifying a transaction script, including any
142-
/// configuration errors that prevented verification from proceeding.
364+
/// These are used internally to distinguish between setup errors (invalid flags,
365+
/// missing data) and actual script verification failures. Converted to
366+
/// [`KernelError::ScriptVerify`] variants in the public API.
143367
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
144368
#[repr(u8)]
145369
enum ScriptVerifyStatus {
146370
/// Script verification completed successfully
147371
Ok = BTCK_SCRIPT_VERIFY_STATUS_OK,
148-
/// Invalid combination of verification flags was provided
372+
373+
/// Invalid or inconsistent verification flags were provided.
374+
///
375+
/// This occurs when the supplied `script_verify_flags` combination violates
376+
/// internal consistency rules. For example:
377+
///
378+
/// - `SCRIPT_VERIFY_CLEANSTACK` is set without also enabling either
379+
/// `SCRIPT_VERIFY_P2SH` or `SCRIPT_VERIFY_WITNESS`.
380+
/// - `SCRIPT_VERIFY_WITNESS` is set without also enabling `SCRIPT_VERIFY_P2SH`.
381+
///
382+
/// These combinations are considered invalid and result in an immediate
383+
/// verification setup failure rather than a script execution failure.
149384
ErrorInvalidFlagsCombination = BTCK_SCRIPT_VERIFY_STATUS_ERROR_INVALID_FLAGS_COMBINATION,
150-
/// Spent outputs are required for this type of verification but were not provided
385+
386+
/// Spent outputs are required but were not provided.
387+
///
388+
/// Taproot scripts require the complete set of outputs being spent to properly
389+
/// validate witness data. This occurs when the TAPROOT flag is set but no spent
390+
/// outputs were provided.
151391
ErrorSpentOutputsRequired = BTCK_SCRIPT_VERIFY_STATUS_ERROR_SPENT_OUTPUTS_REQUIRED,
152392
}
153393

@@ -172,14 +412,40 @@ impl From<btck_ScriptVerifyStatus> for ScriptVerifyStatus {
172412
}
173413
}
174414

175-
/// A collection of errors that may occur during script verification
415+
/// Errors that can occur during script verification.
416+
///
417+
/// These errors represent both configuration problems (incorrect parameters)
418+
/// and actual verification failures (invalid scripts).
176419
#[derive(Debug)]
177420
pub enum ScriptVerifyError {
421+
/// The specified input index is out of bounds.
422+
///
423+
/// The `input_index` parameter is greater than or equal to the number
424+
/// of inputs in the transaction.
178425
TxInputIndex,
426+
427+
/// Invalid verification flags were provided.
428+
///
429+
/// The flags parameter contains bits that don't correspond to any
430+
/// defined verification flag.
179431
InvalidFlags,
432+
433+
/// Invalid or inconsistent verification flags were provided.
434+
///
435+
/// This occurs when the supplied `script_verify_flags` combination violates
436+
/// internal consistency rules.
180437
InvalidFlagsCombination,
438+
439+
/// The spent_outputs array length doesn't match the input count.
440+
///
441+
/// When spent_outputs is non-empty, it must contain exactly one output
442+
/// for each input in the transaction.
181443
SpentOutputsMismatch,
444+
445+
/// Spent outputs are required but were not provided.
182446
SpentOutputsRequired,
447+
448+
/// Script verification failed.
183449
Invalid,
184450
}
185451

0 commit comments

Comments
 (0)