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 blvm_spec_lock::spec_locked;
13#[cfg(feature = "production")]
14use rustc_hash::{FxHashMap, FxHashSet};
15use std::sync::LazyLock;
16
17use super::BlockValidationContext;
18
19#[spec_locked("5.2.5", "CalculateScriptFlags")]
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", "CalculateScriptFlags")]
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", "CalculateScriptFlags")]
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", "CalculateScriptFlags")]
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", "CalculateScriptFlags")]
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
140fn block_hash_from_rpc_hex(hex_str: &str) -> Hash {
146 let mut bytes = hex::decode(hex_str).expect("valid 64-char block hash hex");
147 assert_eq!(bytes.len(), 32);
148 bytes.reverse();
149 bytes.try_into().expect("length 32")
150}
151
152static MAINNET_SCRIPT_FLAG_EXCEPTION_BIP16: LazyLock<Hash> = LazyLock::new(|| {
154 block_hash_from_rpc_hex("00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22")
155});
156static MAINNET_SCRIPT_FLAG_EXCEPTION_TAPROOT: LazyLock<Hash> = LazyLock::new(|| {
158 block_hash_from_rpc_hex("0000000000000000000f14c35b2d841e986ab5441de8c585d5ffe55ea1e395ad")
159});
160static TESTNET_SCRIPT_FLAG_EXCEPTION_BIP16: LazyLock<Hash> = LazyLock::new(|| {
162 block_hash_from_rpc_hex("00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105")
163});
164
165#[spec_locked("5.2.6", "ScriptFlagExceptions")]
169pub fn script_flag_exceptions_lookup(block_hash: &Hash, network: Network) -> Option<u32> {
170 match network {
171 Network::Mainnet => {
172 if block_hash == &*MAINNET_SCRIPT_FLAG_EXCEPTION_BIP16 {
173 Some(0)
174 } else if block_hash == &*MAINNET_SCRIPT_FLAG_EXCEPTION_TAPROOT {
175 Some(0x01 | 0x800) } else {
177 None
178 }
179 }
180 Network::Testnet => {
181 if block_hash == &*TESTNET_SCRIPT_FLAG_EXCEPTION_BIP16 {
182 Some(0)
183 } else {
184 None
185 }
186 }
187 Network::Regtest => None,
188 }
189}
190
191pub fn get_block_script_verify_flags_core(
200 block_hash: &Hash,
201 height: u64,
202 activation: &impl IsForkActive,
203 network: Network,
204) -> u32 {
205 let mut flags = 0x01u32 | 0x800 | 0x8000; if let Some(v) = script_flag_exceptions_lookup(block_hash, network) {
207 flags = v;
208 }
209 if activation.is_fork_active(ForkId::Bip66, height) {
210 flags |= 0x04; }
212 if activation.is_fork_active(ForkId::Bip65, height) {
213 flags |= 0x200; }
215 if activation.is_fork_active(ForkId::Bip112, height) {
216 flags |= 0x400; }
218 if activation.is_fork_active(ForkId::Bip147, height) {
219 flags |= 0x10; }
221 #[cfg(feature = "ctv")]
222 if activation.is_fork_active(ForkId::Ctv, height) {
223 flags |= 0x80000000;
224 }
225 flags
226}
227
228#[spec_locked("5.2.6", "GetBlockScriptFlags")]
235pub fn get_block_script_flags(
236 block_hash: &Hash,
237 tx: &Transaction,
238 has_witness: bool,
239 height: u64,
240 network: Network,
241) -> u32 {
242 if let Some(flags) = script_flag_exceptions_lookup(block_hash, network) {
243 flags
244 } else {
245 calculate_script_flags_for_block_network(tx, has_witness, height, network)
246 }
247}
248
249#[cfg(all(feature = "production", feature = "rayon"))]
255pub(super) fn insert_script_exec_cache_for_block(
256 block: &Block,
257 witnesses: &[Vec<Witness>],
258 height: u64,
259 context: &BlockValidationContext,
260) {
261 let block_hash = crate::crypto::OptimizedSha256::new().hash256(
262 &crate::serialization::block::serialize_block_header(&block.header),
263 );
264 let block_script_verify_flags =
265 get_block_script_verify_flags_core(&block_hash, height, context, context.network);
266 for (i, tx) in block.transactions.iter().enumerate() {
267 if is_coinbase(tx) {
268 continue;
269 }
270 let wits = witnesses.get(i).map(|w| w.as_slice()).unwrap_or(&[]);
271 let witnesses_vec: Vec<_> = if wits.len() == tx.inputs.len() {
272 wits.to_vec()
273 } else {
274 (0..tx.inputs.len()).map(|_| Vec::new()).collect()
275 };
276 let key =
277 crate::script_exec_cache::compute_key(tx, &witnesses_vec, block_script_verify_flags);
278 crate::script_exec_cache::insert(&key);
279 }
280}
281
282#[cfg(feature = "production")]
285pub(super) fn merge_overlay_changes_to_cache(
286 additions: &FxHashMap<OutPoint, std::sync::Arc<UTXO>>,
287 deletions: &FxHashSet<crate::utxo_overlay::UtxoDeletionKey>,
288 utxo_set: &mut UtxoSet,
289 mut bip30_index: Option<&mut crate::bip_validation::Bip30Index>,
290 mut undo_log: Option<&mut crate::reorganization::BlockUndoLog>,
291) {
292 use crate::reorganization::UndoEntry;
293
294 for del_key in deletions {
295 let outpoint = crate::utxo_overlay::utxo_deletion_key_to_outpoint(del_key);
296 if let Some(arc) = utxo_set.remove(&outpoint) {
297 if let Some(idx) = bip30_index.as_deref_mut() {
298 if arc.is_coinbase {
299 if let std::collections::hash_map::Entry::Occupied(mut o) =
300 idx.entry(outpoint.hash)
301 {
302 *o.get_mut() = o.get().saturating_sub(1);
303 if *o.get() == 0 {
304 o.remove();
305 }
306 }
307 }
308 }
309 if let Some(ref mut log) = undo_log {
310 log.entries.push(UndoEntry {
311 outpoint,
312 previous_utxo: Some(arc),
313 new_utxo: None,
314 });
315 }
316 }
317 }
318 for (outpoint, arc) in additions {
319 if let Some(ref mut log) = undo_log {
320 log.entries.push(UndoEntry {
321 outpoint: *outpoint,
322 previous_utxo: None,
323 new_utxo: Some(std::sync::Arc::clone(arc)),
324 });
325 }
326 utxo_set.insert(*outpoint, std::sync::Arc::clone(arc));
327 }
328}
329
330#[cfg(all(feature = "production", feature = "rayon"))]
333pub(super) fn compute_bip143_and_precomp(
334 tx: &Transaction,
335 prevout_values: &[i64],
336 script_pubkey_indices: &[(usize, usize)],
337 script_pubkey_buffer: &[u8],
338 has_witness: bool,
339) -> (
340 Option<crate::transaction_hash::Bip143PrecomputedHashes>,
341 Vec<Option<[u8; 32]>>,
342) {
343 let buf = script_pubkey_buffer;
344 let refs: Vec<&[u8]> = script_pubkey_indices
345 .iter()
346 .map(|&(s, l)| buf[s..s + l].as_ref())
347 .collect();
348 let refs: &[&[u8]] = &refs;
349 if has_witness {
350 let bip =
351 crate::transaction_hash::Bip143PrecomputedHashes::compute(tx, prevout_values, refs);
352 let mut precomp = vec![None; script_pubkey_indices.len()];
353 let mut specs: Vec<(usize, u8, &[u8])> = Vec::new();
354 for (j, &(s, l)) in script_pubkey_indices.iter().enumerate() {
355 let spk = &buf[s..s + l];
356 if spk.len() == 22 && spk[0] == OP_0 && spk[1] == PUSH_20_BYTES {
357 let mut script_code = [0u8; 25];
358 script_code[0] = OP_DUP;
359 script_code[1] = OP_HASH160;
360 script_code[2] = PUSH_20_BYTES;
361 script_code[3..23].copy_from_slice(&spk[2..22]);
362 script_code[23] = OP_EQUALVERIFY;
363 script_code[24] = OP_CHECKSIG;
364 let amount = prevout_values.get(j).copied().unwrap_or(0);
365 if let Ok(h) = crate::transaction_hash::calculate_bip143_sighash(
366 tx,
367 j,
368 &script_code,
369 amount,
370 0x01,
371 Some(&bip),
372 ) {
373 precomp[j] = Some(h);
374 }
375 } else if spk.len() == 23
376 && spk[0] == OP_HASH160
377 && spk[1] == PUSH_20_BYTES
378 && spk[22] == OP_EQUAL
379 {
380 if let Some((sighash_byte, redeem)) =
381 crate::script::parse_p2sh_p2pkh_for_precompute(&tx.inputs[j].script_sig)
382 {
383 specs.push((j, sighash_byte, redeem));
384 }
385 }
386 }
387 if !specs.is_empty() {
388 if let Ok(hashes) = crate::transaction_hash::batch_compute_legacy_sighashes(
389 tx,
390 prevout_values,
391 refs,
392 &specs,
393 ) {
394 for (k, &(j, _, _)) in specs.iter().enumerate() {
395 precomp[j] = Some(hashes[k]);
396 }
397 }
398 }
399 (Some(bip), precomp)
400 } else {
401 let mut precomp = vec![None; script_pubkey_indices.len()];
402 let mut specs: Vec<(usize, u8, &[u8])> = Vec::new();
403 for (j, &(s, l)) in script_pubkey_indices.iter().enumerate() {
404 let spk = &buf[s..s + l];
405 if spk.len() == 25
406 && spk[0] == OP_DUP
407 && spk[1] == OP_HASH160
408 && spk[2] == PUSH_20_BYTES
409 && spk[23] == OP_EQUALVERIFY
410 && spk[24] == OP_CHECKSIG
411 {
412 let script_sig = &tx.inputs[j].script_sig;
413 if let Some((sig, _pubkey)) = crate::script::parse_p2pkh_script_sig(script_sig) {
414 if !sig.is_empty() {
415 specs.push((j, sig[sig.len() - 1], spk));
416 }
417 }
418 } else if spk.len() == 23
419 && spk[0] == OP_HASH160
420 && spk[1] == PUSH_20_BYTES
421 && spk[22] == OP_EQUAL
422 {
423 if let Some((sighash_byte, redeem)) =
424 crate::script::parse_p2sh_p2pkh_for_precompute(&tx.inputs[j].script_sig)
425 {
426 specs.push((j, sighash_byte, redeem));
427 }
428 }
429 }
430 if !specs.is_empty() {
431 if let Ok(hashes) = crate::transaction_hash::batch_compute_legacy_sighashes(
432 tx,
433 prevout_values,
434 refs,
435 &specs,
436 ) {
437 for (k, &(j, _, _)) in specs.iter().enumerate() {
438 precomp[j] = Some(hashes[k]);
439 }
440 }
441 }
442 (None, precomp)
443 }
444}
445
446#[cfg(test)]
447mod script_flag_exceptions_tests {
448 use super::{
449 calculate_script_flags_for_block_network, get_block_script_flags,
450 get_block_script_verify_flags_core, script_flag_exceptions_lookup,
451 };
452 use crate::activation::ForkActivationTable;
453 use crate::crypto::OptimizedSha256;
454 use crate::serialization::block::{deserialize_block_header, serialize_block_header};
455 use crate::types::{Network, Transaction};
456
457 fn hash_from_rpc_hex(hex_str: &str) -> [u8; 32] {
458 let mut bytes = hex::decode(hex_str).unwrap();
459 assert_eq!(bytes.len(), 32);
460 bytes.reverse();
461 bytes.try_into().unwrap()
462 }
463
464 #[test]
465 fn mainnet_bip16_exception_zero() {
466 let h =
467 hash_from_rpc_hex("00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22");
468 assert_eq!(script_flag_exceptions_lookup(&h, Network::Mainnet), Some(0));
469 }
470
471 #[test]
472 fn mainnet_taproot_exception_p2sh_witness() {
473 let h =
474 hash_from_rpc_hex("0000000000000000000f14c35b2d841e986ab5441de8c585d5ffe55ea1e395ad");
475 assert_eq!(
476 script_flag_exceptions_lookup(&h, Network::Mainnet),
477 Some(0x01 | 0x800)
478 );
479 }
480
481 #[test]
482 fn testnet_bip16_exception_zero() {
483 let h =
484 hash_from_rpc_hex("00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105");
485 assert_eq!(script_flag_exceptions_lookup(&h, Network::Testnet), Some(0));
486 assert_eq!(script_flag_exceptions_lookup(&h, Network::Mainnet), None);
487 }
488
489 #[test]
490 fn regtest_has_no_exceptions() {
491 let h =
492 hash_from_rpc_hex("00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22");
493 assert_eq!(script_flag_exceptions_lookup(&h, Network::Regtest), None);
494 }
495
496 #[test]
497 fn get_block_script_flags_uses_exception() {
498 let h =
499 hash_from_rpc_hex("00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22");
500 let tx = Transaction {
501 version: 1,
502 inputs: Default::default(),
503 outputs: Default::default(),
504 lock_time: 0,
505 };
506 let flags = get_block_script_flags(&h, &tx, false, 1_000_000, Network::Mainnet);
507 assert_eq!(flags, 0);
508 }
509
510 #[test]
511 fn get_block_script_flags_falls_back_to_calculate() {
512 let h = [7u8; 32];
513 let tx = Transaction {
514 version: 1,
515 inputs: Default::default(),
516 outputs: Default::default(),
517 lock_time: 0,
518 };
519 assert_eq!(
520 get_block_script_flags(&h, &tx, false, 100, Network::Mainnet),
521 calculate_script_flags_for_block_network(&tx, false, 100, Network::Mainnet),
522 );
523 }
524
525 #[test]
526 fn genesis_rpc_hex_matches_hash256_of_header() {
527 let genesis_block_hex = "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c010100000001000000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000";
528 let bytes = hex::decode(genesis_block_hex).unwrap();
529 let header = deserialize_block_header(&bytes[..80]).unwrap();
530 let digest = OptimizedSha256::new().hash256(&serialize_block_header(&header));
531 let expected =
532 hash_from_rpc_hex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
533 assert_eq!(digest, expected);
534 }
535
536 #[test]
537 fn core_block_flags_non_exception_includes_p2sh_witness_taproot_and_deployments() {
538 let table = ForkActivationTable::from_network(Network::Mainnet);
539 let h = [0xabu8; 32];
540 let flags = get_block_script_verify_flags_core(&h, 800_000, &table, Network::Mainnet);
541 assert_eq!(flags & 0x8801, 0x8801, "P2SH | WITNESS | TAPROOT baseline");
542 assert_ne!(flags & 0x04, 0, "DERSIG");
543 assert_ne!(flags & 0x200, 0, "CLTV");
544 assert_ne!(flags & 0x400, 0, "CSV");
545 assert_ne!(flags & 0x10, 0, "NULLDUMMY");
546 }
547
548 #[test]
549 fn core_block_flags_bip16_exception_orrs_buried_deployments() {
550 let table = ForkActivationTable::from_network(Network::Mainnet);
551 let h =
552 hash_from_rpc_hex("00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22");
553 assert_eq!(script_flag_exceptions_lookup(&h, Network::Mainnet), Some(0));
554 let flags = get_block_script_verify_flags_core(&h, 800_000, &table, Network::Mainnet);
555 let deployment_mask = 0x04 | 0x200 | 0x400 | 0x10;
556 assert_eq!(flags & 0x8801, 0);
557 assert_eq!(flags & deployment_mask, deployment_mask);
558 assert_eq!(flags & 0x800, 0);
559 assert_eq!(flags & 0x8000, 0);
560 assert_eq!(flags & 0x01, 0);
561 }
562
563 #[test]
564 fn core_block_flags_taproot_exception_orrs_deployments() {
565 let table = ForkActivationTable::from_network(Network::Mainnet);
566 let h =
567 hash_from_rpc_hex("0000000000000000000f14c35b2d841e986ab5441de8c585d5ffe55ea1e395ad");
568 let flags = get_block_script_verify_flags_core(&h, 800_000, &table, Network::Mainnet);
569 assert_eq!(flags & 0x8801, 0x801);
570 assert_eq!(flags & 0x8000, 0);
571 assert_ne!(flags & 0x800, 0);
572 assert_ne!(flags & 0x04, 0);
573 }
574}