1use crate::activation::{ForkActivationTable, IsForkActive};
7use crate::constants::*;
8use crate::opcodes::*;
9use crate::segwit::{is_segwit_transaction, Witness};
10use crate::transaction::is_coinbase;
11use crate::types::*;
12use crate::witness::is_witness_empty;
13use blvm_spec_lock::spec_locked;
14#[cfg(feature = "production")]
15use rustc_hash::{FxHashMap, FxHashSet};
16
17use super::BlockValidationContext;
18
19#[spec_locked("5.2.5")]
26#[inline]
27pub(crate) fn calculate_base_script_flags_for_block(
28 height: u64,
29 activation: &impl IsForkActive,
30) -> u32 {
31 let mut flags: u32 = 0;
32
33 if activation.is_fork_active(ForkId::Bip16, height) {
34 flags |= 0x01; }
36 if activation.is_fork_active(ForkId::Bip66, height) {
40 flags |= 0x04; }
42 if activation.is_fork_active(ForkId::Bip65, height) {
43 flags |= 0x200; }
45 if activation.is_fork_active(ForkId::Bip112, height) {
48 flags |= 0x400; }
50 if activation.is_fork_active(ForkId::Bip147, height) {
52 flags |= 0x10; }
54 #[cfg(feature = "ctv")]
55 if activation.is_fork_active(ForkId::Ctv, height) {
56 flags |= 0x80000000; }
58
59 flags
60}
61
62#[inline]
64pub fn calculate_base_script_flags_for_block_network(
65 height: u64,
66 network: crate::types::Network,
67) -> u32 {
68 let table = ForkActivationTable::from_network(network);
69 calculate_base_script_flags_for_block(height, &table)
70}
71
72#[spec_locked("5.2.5")]
74#[inline]
75fn add_per_tx_script_flags(
76 base_flags: u32,
77 tx: &Transaction,
78 has_witness: bool,
79 height: u64,
80 activation: &impl IsForkActive,
81) -> u32 {
82 let mut flags = base_flags;
83 if activation.is_fork_active(ForkId::SegWit, height)
84 && (has_witness || is_segwit_transaction(tx))
85 {
86 flags |= 0x800;
87 }
88 if activation.is_fork_active(ForkId::Taproot, height) {
89 for output in &tx.outputs {
90 let script = &output.script_pubkey;
91 if script.len() == TAPROOT_SCRIPT_LENGTH
92 && script[0] == OP_1
93 && script[1] == PUSH_32_BYTES
94 {
95 flags |= 0x8000;
96 break;
97 }
98 }
99 }
100 flags
101}
102
103#[spec_locked("5.2.5")]
105pub(crate) fn calculate_script_flags_for_block(
106 tx: &Transaction,
107 has_witness: bool,
108 height: u64,
109 activation: &impl IsForkActive,
110) -> u32 {
111 let base = calculate_base_script_flags_for_block(height, activation);
112 add_per_tx_script_flags(base, tx, has_witness, height, activation)
113}
114
115#[spec_locked("5.2.5")]
117pub fn calculate_script_flags_for_block_network(
118 tx: &Transaction,
119 has_witness: bool,
120 height: u64,
121 network: crate::types::Network,
122) -> u32 {
123 let table = ForkActivationTable::from_network(network);
124 calculate_script_flags_for_block(tx, has_witness, height, &table)
125}
126
127#[spec_locked("5.2.5")]
129#[inline]
130pub(crate) fn calculate_script_flags_for_block_with_base(
131 tx: &Transaction,
132 has_witness: bool,
133 base_flags: u32,
134 height: u64,
135 activation: &impl IsForkActive,
136) -> u32 {
137 add_per_tx_script_flags(base_flags, tx, has_witness, height, activation)
138}
139
140#[cfg(all(feature = "production", feature = "rayon"))]
146pub(super) fn insert_script_exec_cache_for_block(
147 block: &Block,
148 witnesses: &[Vec<Witness>],
149 height: u64,
150 context: &BlockValidationContext,
151) {
152 let base_script_flags = calculate_base_script_flags_for_block(height, context);
153 for (i, tx) in block.transactions.iter().enumerate() {
154 if is_coinbase(tx) {
155 continue;
156 }
157 let wits = witnesses.get(i).map(|w| w.as_slice()).unwrap_or(&[]);
158 let has_witness = wits.iter().any(|wit| !is_witness_empty(wit));
159 let flags = calculate_script_flags_for_block_with_base(
160 tx,
161 has_witness,
162 base_script_flags,
163 height,
164 context,
165 );
166 let witnesses_vec: Vec<_> = if wits.len() == tx.inputs.len() {
167 wits.to_vec()
168 } else {
169 (0..tx.inputs.len()).map(|_| Vec::new()).collect()
170 };
171 let key = crate::script_exec_cache::compute_key(tx, &witnesses_vec, flags);
172 crate::script_exec_cache::insert(&key);
173 }
174}
175
176#[cfg(feature = "production")]
179pub(super) fn merge_overlay_changes_to_cache(
180 additions: &FxHashMap<OutPoint, std::sync::Arc<UTXO>>,
181 deletions: &FxHashSet<crate::utxo_overlay::UtxoDeletionKey>,
182 utxo_set: &mut UtxoSet,
183 mut bip30_index: Option<&mut crate::bip_validation::Bip30Index>,
184 mut undo_log: Option<&mut crate::reorganization::BlockUndoLog>,
185) {
186 use crate::reorganization::UndoEntry;
187
188 for del_key in deletions {
189 let outpoint = crate::utxo_overlay::utxo_deletion_key_to_outpoint(del_key);
190 if let Some(arc) = utxo_set.remove(&outpoint) {
191 if let Some(idx) = bip30_index.as_deref_mut() {
192 if arc.is_coinbase {
193 if let std::collections::hash_map::Entry::Occupied(mut o) =
194 idx.entry(outpoint.hash)
195 {
196 *o.get_mut() = o.get().saturating_sub(1);
197 if *o.get() == 0 {
198 o.remove();
199 }
200 }
201 }
202 }
203 if let Some(ref mut log) = undo_log {
204 log.entries.push(UndoEntry {
205 outpoint,
206 previous_utxo: Some(arc),
207 new_utxo: None,
208 });
209 }
210 }
211 }
212 for (outpoint, arc) in additions {
213 if let Some(ref mut log) = undo_log {
214 log.entries.push(UndoEntry {
215 outpoint: *outpoint,
216 previous_utxo: None,
217 new_utxo: Some(std::sync::Arc::clone(arc)),
218 });
219 }
220 utxo_set.insert(*outpoint, std::sync::Arc::clone(arc));
221 }
222}
223
224#[cfg(all(feature = "production", feature = "rayon"))]
227pub(super) fn compute_bip143_and_precomp(
228 tx: &Transaction,
229 prevout_values: &[i64],
230 script_pubkey_indices: &[(usize, usize)],
231 script_pubkey_buffer: &[u8],
232 has_witness: bool,
233) -> (
234 Option<crate::transaction_hash::Bip143PrecomputedHashes>,
235 Vec<Option<[u8; 32]>>,
236) {
237 let buf = script_pubkey_buffer;
238 let refs: Vec<&[u8]> = script_pubkey_indices
239 .iter()
240 .map(|&(s, l)| buf[s..s + l].as_ref())
241 .collect();
242 let refs: &[&[u8]] = &refs;
243 if has_witness {
244 let bip =
245 crate::transaction_hash::Bip143PrecomputedHashes::compute(tx, prevout_values, refs);
246 let mut precomp = vec![None; script_pubkey_indices.len()];
247 let mut specs: Vec<(usize, u8, &[u8])> = Vec::new();
248 for (j, &(s, l)) in script_pubkey_indices.iter().enumerate() {
249 let spk = &buf[s..s + l];
250 if spk.len() == 22 && spk[0] == OP_0 && spk[1] == PUSH_20_BYTES {
251 let mut script_code = [0u8; 25];
252 script_code[0] = OP_DUP;
253 script_code[1] = OP_HASH160;
254 script_code[2] = PUSH_20_BYTES;
255 script_code[3..23].copy_from_slice(&spk[2..22]);
256 script_code[23] = OP_EQUALVERIFY;
257 script_code[24] = OP_CHECKSIG;
258 let amount = prevout_values.get(j).copied().unwrap_or(0);
259 if let Ok(h) = crate::transaction_hash::calculate_bip143_sighash(
260 tx,
261 j,
262 &script_code,
263 amount,
264 0x01,
265 Some(&bip),
266 ) {
267 precomp[j] = Some(h);
268 }
269 } else if spk.len() == 23
270 && spk[0] == OP_HASH160
271 && spk[1] == PUSH_20_BYTES
272 && spk[22] == OP_EQUAL
273 {
274 if let Some((sighash_byte, redeem)) =
275 crate::script::parse_p2sh_p2pkh_for_precompute(&tx.inputs[j].script_sig)
276 {
277 specs.push((j, sighash_byte, redeem));
278 }
279 }
280 }
281 if !specs.is_empty() {
282 if let Ok(hashes) = crate::transaction_hash::batch_compute_legacy_sighashes(
283 tx,
284 prevout_values,
285 refs,
286 &specs,
287 ) {
288 for (k, &(j, _, _)) in specs.iter().enumerate() {
289 precomp[j] = Some(hashes[k]);
290 }
291 }
292 }
293 (Some(bip), precomp)
294 } else {
295 let mut precomp = vec![None; script_pubkey_indices.len()];
296 let mut specs: Vec<(usize, u8, &[u8])> = Vec::new();
297 for (j, &(s, l)) in script_pubkey_indices.iter().enumerate() {
298 let spk = &buf[s..s + l];
299 if spk.len() == 25
300 && spk[0] == OP_DUP
301 && spk[1] == OP_HASH160
302 && spk[2] == PUSH_20_BYTES
303 && spk[23] == OP_EQUALVERIFY
304 && spk[24] == OP_CHECKSIG
305 {
306 let script_sig = &tx.inputs[j].script_sig;
307 if let Some((sig, _pubkey)) = crate::script::parse_p2pkh_script_sig(script_sig) {
308 if !sig.is_empty() {
309 specs.push((j, sig[sig.len() - 1], spk));
310 }
311 }
312 } else if spk.len() == 23
313 && spk[0] == OP_HASH160
314 && spk[1] == PUSH_20_BYTES
315 && spk[22] == OP_EQUAL
316 {
317 if let Some((sighash_byte, redeem)) =
318 crate::script::parse_p2sh_p2pkh_for_precompute(&tx.inputs[j].script_sig)
319 {
320 specs.push((j, sighash_byte, redeem));
321 }
322 }
323 }
324 if !specs.is_empty() {
325 if let Ok(hashes) = crate::transaction_hash::batch_compute_legacy_sighashes(
326 tx,
327 prevout_values,
328 refs,
329 &specs,
330 ) {
331 for (k, &(j, _, _)) in specs.iter().enumerate() {
332 precomp[j] = Some(hashes[k]);
333 }
334 }
335 }
336 (None, precomp)
337 }
338}