bitcoinkernel/core/
verify.rs1use libbitcoinkernel_sys::{
2 btck_ScriptVerificationFlags, btck_ScriptVerifyStatus, btck_TransactionOutput,
3 btck_script_pubkey_verify,
4};
5
6use crate::{
7 c_helpers,
8 ffi::{
9 BTCK_SCRIPT_VERIFICATION_FLAGS_ALL, BTCK_SCRIPT_VERIFICATION_FLAGS_CHECKLOCKTIMEVERIFY,
10 BTCK_SCRIPT_VERIFICATION_FLAGS_CHECKSEQUENCEVERIFY, BTCK_SCRIPT_VERIFICATION_FLAGS_DERSIG,
11 BTCK_SCRIPT_VERIFICATION_FLAGS_NONE, BTCK_SCRIPT_VERIFICATION_FLAGS_NULLDUMMY,
12 BTCK_SCRIPT_VERIFICATION_FLAGS_P2SH, BTCK_SCRIPT_VERIFICATION_FLAGS_TAPROOT,
13 BTCK_SCRIPT_VERIFICATION_FLAGS_WITNESS,
14 BTCK_SCRIPT_VERIFY_STATUS_ERROR_INVALID_FLAGS_COMBINATION,
15 BTCK_SCRIPT_VERIFY_STATUS_ERROR_SPENT_OUTPUTS_REQUIRED, BTCK_SCRIPT_VERIFY_STATUS_OK,
16 },
17 KernelError, ScriptPubkeyExt, TransactionExt, TxOutExt,
18};
19
20pub const VERIFY_NONE: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_NONE;
21
22pub const VERIFY_P2SH: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_P2SH;
23
24pub const VERIFY_DERSIG: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_DERSIG;
25
26pub const VERIFY_NULLDUMMY: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_NULLDUMMY;
27
28pub const VERIFY_CHECKLOCKTIMEVERIFY: btck_ScriptVerificationFlags =
29 BTCK_SCRIPT_VERIFICATION_FLAGS_CHECKLOCKTIMEVERIFY;
30
31pub const VERIFY_CHECKSEQUENCEVERIFY: btck_ScriptVerificationFlags =
32 BTCK_SCRIPT_VERIFICATION_FLAGS_CHECKSEQUENCEVERIFY;
33
34pub const VERIFY_WITNESS: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_WITNESS;
35
36pub const VERIFY_TAPROOT: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_TAPROOT;
37
38pub const VERIFY_ALL: btck_ScriptVerificationFlags = BTCK_SCRIPT_VERIFICATION_FLAGS_ALL;
39
40pub const VERIFY_ALL_PRE_TAPROOT: btck_ScriptVerificationFlags = VERIFY_P2SH
41 | VERIFY_DERSIG
42 | VERIFY_NULLDUMMY
43 | VERIFY_CHECKLOCKTIMEVERIFY
44 | VERIFY_CHECKSEQUENCEVERIFY
45 | VERIFY_WITNESS;
46
47pub fn verify(
61 script_pubkey: &impl ScriptPubkeyExt,
62 amount: Option<i64>,
63 tx_to: &impl TransactionExt,
64 input_index: usize,
65 flags: Option<u32>,
66 spent_outputs: &[impl TxOutExt],
67) -> Result<(), KernelError> {
68 let input_count = tx_to.input_count();
69
70 if input_index >= input_count {
71 return Err(KernelError::ScriptVerify(ScriptVerifyError::TxInputIndex));
72 }
73
74 if !spent_outputs.is_empty() && spent_outputs.len() != input_count {
75 return Err(KernelError::ScriptVerify(
76 ScriptVerifyError::SpentOutputsMismatch,
77 ));
78 }
79
80 let kernel_flags = if let Some(flag) = flags {
81 if (flag & !VERIFY_ALL) != 0 {
82 return Err(KernelError::ScriptVerify(ScriptVerifyError::InvalidFlags));
83 }
84 flag
85 } else {
86 VERIFY_ALL
87 };
88
89 let status = ScriptVerifyStatus::Ok;
90 let kernel_amount = amount.unwrap_or_default();
91 let kernel_spent_outputs: Vec<*const btck_TransactionOutput> =
92 spent_outputs.iter().map(|utxo| utxo.as_ptr()).collect();
93
94 let spent_outputs_ptr = if kernel_spent_outputs.is_empty() {
95 std::ptr::null_mut()
96 } else {
97 kernel_spent_outputs.as_ptr() as *mut *const btck_TransactionOutput
98 };
99
100 let ret = unsafe {
101 btck_script_pubkey_verify(
102 script_pubkey.as_ptr(),
103 kernel_amount,
104 tx_to.as_ptr(),
105 spent_outputs_ptr,
106 spent_outputs.len(),
107 input_index as u32,
108 kernel_flags,
109 &mut status.into(),
110 )
111 };
112
113 let script_status = ScriptVerifyStatus::try_from(status).map_err(|_| {
114 KernelError::Internal(format!("Invalid script verify status: {:?}", status))
115 })?;
116
117 if !c_helpers::verification_passed(ret) {
118 let err = match script_status {
119 ScriptVerifyStatus::ErrorInvalidFlagsCombination => {
120 ScriptVerifyError::InvalidFlagsCombination
121 }
122 ScriptVerifyStatus::ErrorSpentOutputsRequired => {
123 ScriptVerifyError::SpentOutputsRequired
124 }
125 _ => ScriptVerifyError::Invalid,
126 };
127 Err(KernelError::ScriptVerify(err))
128 } else {
129 Ok(())
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
138#[repr(u8)]
139pub enum ScriptVerifyStatus {
140 Ok = BTCK_SCRIPT_VERIFY_STATUS_OK,
142 ErrorInvalidFlagsCombination = BTCK_SCRIPT_VERIFY_STATUS_ERROR_INVALID_FLAGS_COMBINATION,
144 ErrorSpentOutputsRequired = BTCK_SCRIPT_VERIFY_STATUS_ERROR_SPENT_OUTPUTS_REQUIRED,
146}
147
148impl From<ScriptVerifyStatus> for btck_ScriptVerifyStatus {
149 fn from(status: ScriptVerifyStatus) -> Self {
150 status as btck_ScriptVerifyStatus
151 }
152}
153
154impl From<btck_ScriptVerifyStatus> for ScriptVerifyStatus {
155 fn from(value: btck_ScriptVerifyStatus) -> Self {
156 match value {
157 BTCK_SCRIPT_VERIFY_STATUS_OK => ScriptVerifyStatus::Ok,
158 BTCK_SCRIPT_VERIFY_STATUS_ERROR_INVALID_FLAGS_COMBINATION => {
159 ScriptVerifyStatus::ErrorInvalidFlagsCombination
160 }
161 BTCK_SCRIPT_VERIFY_STATUS_ERROR_SPENT_OUTPUTS_REQUIRED => {
162 ScriptVerifyStatus::ErrorSpentOutputsRequired
163 }
164 _ => panic!("Unknown script verify status: {}", value),
165 }
166 }
167}
168
169#[derive(Debug)]
171pub enum ScriptVerifyError {
172 TxInputIndex,
173 TxSizeMismatch,
174 TxDeserialize,
175 InvalidFlags,
176 InvalidFlagsCombination,
177 SpentOutputsMismatch,
178 SpentOutputsRequired,
179 Invalid,
180}