blvm_consensus/script/
signature.rs1use crate::error::Result;
6use crate::types::Natural;
7use blvm_spec_lock::spec_locked;
8use secp256k1::{ecdsa::Signature, Context, Message, PublicKey, Secp256k1, Verification};
9
10use super::SigVersion;
11
12#[cfg(feature = "production")]
13use std::thread_local;
14
15#[cfg(feature = "production")]
16thread_local! {
17 static SECP256K1_CONTEXT: Secp256k1<secp256k1::All> = Secp256k1::new();
18}
19
20#[cfg(feature = "production")]
22pub(crate) fn with_secp_context<F, R>(f: F) -> R
23where
24 F: FnOnce(&Secp256k1<secp256k1::All>) -> R,
25{
26 SECP256K1_CONTEXT.with(f)
27}
28
29#[allow(clippy::too_many_arguments)]
32pub(crate) fn verify_signature<C: Context + Verification>(
33 secp: &Secp256k1<C>,
34 pubkey_bytes: &[u8],
35 signature_bytes: &[u8],
36 sighash: &[u8; 32],
37 flags: u32,
38 height: Natural,
39 network: crate::types::Network,
40 sigversion: SigVersion,
41) -> Result<bool> {
42 if signature_bytes.is_empty() {
43 return Ok(false);
44 }
45 let sig_len = signature_bytes.len();
46 let sighash_byte = signature_bytes[sig_len - 1];
47 let der_sig = &signature_bytes[..sig_len - 1];
48
49 if flags & 0x04 != 0
50 && !crate::bip_validation::check_bip66_network(signature_bytes, height, network)?
51 {
52 return Ok(false);
53 }
54
55 if flags & 0x02 != 0 {
56 let base_sighash = sighash_byte & !0x80;
57 if !(0x01..=0x03).contains(&base_sighash) {
58 return Ok(false);
59 }
60 }
61
62 let signature = if flags & 0x04 != 0 {
63 match Signature::from_der(der_sig) {
64 Ok(sig) => sig,
65 Err(_) => return Ok(false),
66 }
67 } else {
68 match Signature::from_der_lax(der_sig) {
69 Ok(sig) => sig,
70 Err(_) => return Ok(false),
71 }
72 };
73
74 if flags & 0x08 != 0 {
75 let before = signature.serialize_compact();
76 let mut normalized = signature;
77 normalized.normalize_s();
78 if before != normalized.serialize_compact() {
79 return Ok(false);
80 }
81 }
82
83 if flags & 0x02 != 0 {
84 if pubkey_bytes.len() < 33 {
85 return Ok(false);
86 }
87 if pubkey_bytes[0] == 0x04 {
88 if pubkey_bytes.len() != 65 {
89 return Ok(false);
90 }
91 } else if pubkey_bytes[0] == 0x02 || pubkey_bytes[0] == 0x03 {
92 if pubkey_bytes.len() != 33 {
93 return Ok(false);
94 }
95 } else {
96 return Ok(false);
97 }
98 }
99
100 const SCRIPT_VERIFY_WITNESS_PUBKEYTYPE: u32 = 0x8000;
101 if (flags & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0
102 && sigversion == SigVersion::WitnessV0
103 && !(pubkey_bytes.len() == 33 && (pubkey_bytes[0] == 0x02 || pubkey_bytes[0] == 0x03))
104 {
105 return Ok(false);
106 }
107
108 let pubkey = match PublicKey::from_slice(pubkey_bytes) {
109 Ok(pk) => pk,
110 Err(_) => return Ok(false),
111 };
112
113 let normalized_signature = if flags & 0x08 != 0 {
114 signature
115 } else {
116 let mut s = signature;
117 s.normalize_s();
118 s
119 };
120
121 let sig_compact = normalized_signature.serialize_compact();
122 let pk_compressed = pubkey.serialize();
123 crate::secp256k1_backend::verify_ecdsa(sighash, &sig_compact, &pk_compressed)
124}
125
126#[cfg(feature = "production")]
128pub fn verify_pre_extracted_ecdsa(
129 pubkey_bytes: &[u8],
130 signature_bytes: &[u8],
131 sighash: &[u8; 32],
132 flags: u32,
133 height: Natural,
134 network: crate::types::Network,
135) -> Result<bool> {
136 SECP256K1_CONTEXT.with(|secp| {
137 verify_signature(
138 secp,
139 pubkey_bytes,
140 signature_bytes,
141 sighash,
142 flags,
143 height,
144 network,
145 SigVersion::Base,
146 )
147 })
148}
149
150#[cfg(feature = "production")]
151#[allow(dead_code)]
152fn parse_task_for_batch(
153 pubkey_bytes: &[u8],
154 signature_bytes: &[u8],
155 sighash: &[u8; 32],
156 flags: u32,
157 height: Natural,
158 network: crate::types::Network,
159) -> Result<Option<(PublicKey, secp256k1::ecdsa::Signature, Message)>> {
160 use secp256k1::ecdsa::Signature;
161
162 if signature_bytes.is_empty() {
163 return Ok(None);
164 }
165 let sig_len = signature_bytes.len();
166 let sighash_byte = signature_bytes[sig_len - 1];
167 let der_sig = &signature_bytes[..sig_len - 1];
168
169 if flags & 0x04 != 0
170 && !crate::bip_validation::check_bip66_network(signature_bytes, height, network)?
171 {
172 return Ok(None);
173 }
174 if flags & 0x02 != 0 {
175 let base_sighash = sighash_byte & !0x80;
176 if !(0x01..=0x03).contains(&base_sighash) {
177 return Ok(None);
178 }
179 }
180
181 let signature = if flags & 0x04 != 0 {
182 match Signature::from_der(der_sig) {
183 Ok(sig) => sig,
184 Err(_) => return Ok(None),
185 }
186 } else {
187 match Signature::from_der_lax(der_sig) {
188 Ok(sig) => sig,
189 Err(_) => return Ok(None),
190 }
191 };
192
193 let original_compact = signature.serialize_compact();
194 let mut normalized_signature = signature;
195 normalized_signature.normalize_s();
196 let norm_compact = normalized_signature.serialize_compact();
197 if flags & 0x08 != 0 && original_compact != norm_compact {
198 return Ok(None);
199 }
200
201 if flags & 0x02 != 0 {
202 if pubkey_bytes.len() < 33 {
203 return Ok(None);
204 }
205 if pubkey_bytes[0] == 0x04 {
206 if pubkey_bytes.len() != 65 {
207 return Ok(None);
208 }
209 } else if pubkey_bytes[0] == 0x02 || pubkey_bytes[0] == 0x03 {
210 if pubkey_bytes.len() != 33 {
211 return Ok(None);
212 }
213 } else {
214 return Ok(None);
215 }
216 }
217
218 let pubkey = match PublicKey::from_slice(pubkey_bytes) {
219 Ok(pk) => pk,
220 Err(_) => return Ok(None),
221 };
222 let message = match Message::from_digest_slice(sighash) {
223 Ok(m) => m,
224 Err(_) => return Ok(None),
225 };
226
227 Ok(Some((pubkey, normalized_signature, message)))
228}
229
230#[cfg(feature = "production")]
231#[spec_locked("5.2")]
232pub fn batch_verify_signatures(
233 verification_tasks: &[(&[u8], &[u8], [u8; 32])],
234 flags: u32,
235 height: Natural,
236 network: crate::types::Network,
237) -> Result<Vec<bool>> {
238 #[cfg(feature = "profile")]
239 let _t0 = std::time::Instant::now();
240
241 if verification_tasks.is_empty() {
242 #[cfg(feature = "profile")]
243 crate::script_profile::add_multisig_ns(_t0.elapsed().as_nanos() as u64);
244 return Ok(Vec::new());
245 }
246
247 #[cfg(feature = "rayon")]
248 {
249 use rayon::prelude::*;
250 let r: Result<Vec<bool>> = verification_tasks
251 .par_iter()
252 .map(|(pubkey_bytes, signature_bytes, sighash)| {
253 SECP256K1_CONTEXT.with(|secp| {
254 verify_signature(
255 secp,
256 pubkey_bytes,
257 signature_bytes,
258 sighash,
259 flags,
260 height,
261 network,
262 SigVersion::Base,
263 )
264 })
265 })
266 .collect();
267 #[cfg(feature = "profile")]
268 crate::script_profile::add_multisig_ns(_t0.elapsed().as_nanos() as u64);
269 r
270 }
271
272 #[cfg(not(feature = "rayon"))]
273 {
274 let mut results = Vec::with_capacity(verification_tasks.len());
275 for (pubkey_bytes, signature_bytes, sighash) in verification_tasks {
276 let secp = Secp256k1::new();
277 let result = verify_signature(
278 &secp,
279 pubkey_bytes,
280 signature_bytes,
281 sighash,
282 flags,
283 height,
284 network,
285 SigVersion::Base,
286 )?;
287 results.push(result);
288 }
289 #[cfg(feature = "profile")]
290 crate::script_profile::add_multisig_ns(_t0.elapsed().as_nanos() as u64);
291 Ok(results)
292 }
293}