blvm_consensus/script/
signature.rs1use crate::error::Result;
9use crate::types::Natural;
10
11use super::SigVersion;
12
13#[cfg(all(feature = "secp256k1-fallback", not(feature = "blvm-secp256k1")))]
16use secp256k1::Secp256k1;
17
18#[cfg(all(feature = "secp256k1-fallback", not(feature = "blvm-secp256k1")))]
23pub(crate) type SecpCtx = secp256k1::Secp256k1<secp256k1::All>;
24
25#[cfg(not(all(feature = "secp256k1-fallback", not(feature = "blvm-secp256k1"))))]
26#[derive(Default, Clone, Copy)]
27pub(crate) struct SecpCtx;
28
29pub(crate) fn new_secp() -> SecpCtx {
31 #[cfg(all(feature = "secp256k1-fallback", not(feature = "blvm-secp256k1")))]
32 {
33 secp256k1::Secp256k1::new()
34 }
35 #[cfg(not(all(feature = "secp256k1-fallback", not(feature = "blvm-secp256k1"))))]
36 {
37 SecpCtx
38 }
39}
40
41#[cfg(feature = "production")]
42use std::thread_local;
43
44#[cfg(feature = "production")]
45thread_local! {
46 static SECP256K1_CONTEXT: SecpCtx = new_secp();
47}
48
49#[cfg(feature = "production")]
51pub(crate) fn with_secp_context<F, R>(f: F) -> R
52where
53 F: FnOnce(&SecpCtx) -> R,
54{
55 SECP256K1_CONTEXT.with(f)
56}
57
58#[allow(clippy::too_many_arguments)]
65pub(crate) fn verify_signature(
66 _secp: &SecpCtx,
67 pubkey_bytes: &[u8],
68 signature_bytes: &[u8],
69 sighash: &[u8; 32],
70 flags: u32,
71 height: Natural,
72 network: crate::types::Network,
73 sigversion: SigVersion,
74) -> Result<bool> {
75 if signature_bytes.is_empty() {
76 return Ok(false);
77 }
78 let sig_len = signature_bytes.len();
79 let sighash_byte = signature_bytes[sig_len - 1];
80 let der_sig = &signature_bytes[..sig_len - 1];
81
82 if flags & 0x04 != 0
83 && !crate::bip_validation::check_bip66_network(signature_bytes, height, network)?
84 {
85 return Ok(false);
86 }
87
88 if flags & 0x02 != 0 {
89 let base_sighash = sighash_byte & !0x80;
90 if !(0x01..=0x03).contains(&base_sighash) {
91 return Ok(false);
92 }
93 }
94
95 if flags & 0x02 != 0 {
96 if pubkey_bytes.len() < 33 {
97 return Ok(false);
98 }
99 if pubkey_bytes[0] == 0x04 {
100 if pubkey_bytes.len() != 65 {
101 return Ok(false);
102 }
103 } else if pubkey_bytes[0] == 0x02 || pubkey_bytes[0] == 0x03 {
104 if pubkey_bytes.len() != 33 {
105 return Ok(false);
106 }
107 } else {
108 return Ok(false);
109 }
110 }
111
112 const SCRIPT_VERIFY_WITNESS_PUBKEYTYPE: u32 = 0x8000;
113 if (flags & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0
114 && sigversion == SigVersion::WitnessV0
115 && !(pubkey_bytes.len() == 33 && (pubkey_bytes[0] == 0x02 || pubkey_bytes[0] == 0x03))
116 {
117 return Ok(false);
118 }
119
120 let strict_der = flags & 0x04 != 0;
121 let enforce_low_s = flags & 0x08 != 0;
122
123 #[cfg(feature = "blvm-secp256k1")]
124 {
125 return Ok(blvm_secp256k1::ecdsa::verify_ecdsa_direct(
126 der_sig,
127 pubkey_bytes,
128 sighash,
129 strict_der,
130 enforce_low_s,
131 )
132 .unwrap_or(false));
133 }
134
135 #[cfg(all(feature = "secp256k1-fallback", not(feature = "blvm-secp256k1")))]
136 {
137 use secp256k1::{ecdsa::Signature, PublicKey};
138
139 let signature = if strict_der {
140 match Signature::from_der(der_sig) {
141 Ok(sig) => sig,
142 Err(_) => return Ok(false),
143 }
144 } else {
145 match Signature::from_der_lax(der_sig) {
146 Ok(sig) => sig,
147 Err(_) => return Ok(false),
148 }
149 };
150
151 if enforce_low_s {
152 let before = signature.serialize_compact();
153 let mut normalized = signature;
154 normalized.normalize_s();
155 if before != normalized.serialize_compact() {
156 return Ok(false);
157 }
158 }
159
160 let pubkey = match PublicKey::from_slice(pubkey_bytes) {
161 Ok(pk) => pk,
162 Err(_) => return Ok(false),
163 };
164
165 let normalized_signature = if enforce_low_s {
166 signature
167 } else {
168 let mut s = signature;
169 s.normalize_s();
170 s
171 };
172
173 let sig_compact = normalized_signature.serialize_compact();
174 let pk_compressed = pubkey.serialize();
175 return crate::secp256k1_backend::verify_ecdsa(sighash, &sig_compact, &pk_compressed);
176 }
177
178 #[allow(unreachable_code)]
179 Ok(false)
180}
181
182#[cfg(feature = "production")]
184pub fn verify_pre_extracted_ecdsa(
185 pubkey_bytes: &[u8],
186 signature_bytes: &[u8],
187 sighash: &[u8; 32],
188 flags: u32,
189 height: Natural,
190 network: crate::types::Network,
191) -> Result<bool> {
192 with_secp_context(|secp| {
193 verify_signature(
194 secp,
195 pubkey_bytes,
196 signature_bytes,
197 sighash,
198 flags,
199 height,
200 network,
201 SigVersion::Base,
202 )
203 })
204}
205
206#[cfg(feature = "production")]
207pub fn batch_verify_signatures(
208 verification_tasks: &[(&[u8], &[u8], [u8; 32])],
209 flags: u32,
210 height: Natural,
211 network: crate::types::Network,
212) -> Result<Vec<bool>> {
213 #[cfg(feature = "profile")]
214 let _t0 = std::time::Instant::now();
215
216 if verification_tasks.is_empty() {
217 #[cfg(feature = "profile")]
218 crate::script_profile::add_multisig_ns(_t0.elapsed().as_nanos() as u64);
219 return Ok(Vec::new());
220 }
221
222 let mut results = Vec::with_capacity(verification_tasks.len());
229 with_secp_context(|secp| -> Result<()> {
230 for (pubkey_bytes, signature_bytes, sighash) in verification_tasks {
231 let result = verify_signature(
232 secp,
233 pubkey_bytes,
234 signature_bytes,
235 sighash,
236 flags,
237 height,
238 network,
239 SigVersion::Base,
240 )?;
241 results.push(result);
242 }
243 Ok(())
244 })?;
245 #[cfg(feature = "profile")]
246 crate::script_profile::add_multisig_ns(_t0.elapsed().as_nanos() as u64);
247 Ok(results)
248}