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