1use crate::activation::{ForkActivationTable, IsForkActive};
7use crate::constants::*;
8use crate::opcodes::*;
9use crate::script::flags::{
10 SCRIPT_VERIFY_P2SH, SCRIPT_VERIFY_TAPROOT, SCRIPT_VERIFY_WITNESS,
11 SCRIPT_VERIFY_WITNESS_PUBKEYTYPE,
12};
13use crate::segwit::{Witness, is_segwit_transaction};
14use crate::transaction::is_coinbase;
15use crate::types::*;
16use crate::witness::is_witness_empty;
17use blvm_spec_lock::spec_locked;
18#[cfg(feature = "production")]
19use rustc_hash::{FxHashMap, FxHashSet};
20use std::sync::LazyLock;
21
22use super::BlockValidationContext;
23
24#[spec_locked("5.2.5", "CalculateScriptFlags")]
31#[inline]
32pub(crate) fn calculate_base_script_flags_for_block(
33 height: u64,
34 activation: &impl IsForkActive,
35) -> u32 {
36 let mut flags: u32 = 0;
37
38 if activation.is_fork_active(ForkId::Bip16, height) {
39 flags |= 0x01; }
41 if activation.is_fork_active(ForkId::Bip66, height) {
45 flags |= 0x04; }
47 if activation.is_fork_active(ForkId::Bip65, height) {
48 flags |= 0x200; }
50 if activation.is_fork_active(ForkId::Bip112, height) {
53 flags |= 0x400; }
55 if activation.is_fork_active(ForkId::Bip147, height) {
57 flags |= 0x10; }
59 #[cfg(feature = "ctv")]
60 if activation.is_fork_active(ForkId::Ctv, height) {
61 flags |= 0x80000000; }
63
64 flags
65}
66
67#[inline]
69pub fn calculate_base_script_flags_for_block_network(
70 height: u64,
71 network: crate::types::Network,
72) -> u32 {
73 let table = ForkActivationTable::from_network(network);
74 calculate_base_script_flags_for_block(height, &table)
75}
76
77#[spec_locked("5.2.5", "HasNonEmptyInputWitness")]
82pub fn tx_has_nonempty_input_witness(per_input_witnesses: Option<&[Witness]>) -> bool {
83 per_input_witnesses
84 .map(|ws| ws.iter().any(|w| !is_witness_empty(w)))
85 .unwrap_or(false)
86}
87
88pub fn tx_requires_witness_script_flags(tx: &Transaction, has_witness: bool) -> bool {
90 has_witness || is_segwit_transaction(tx)
91}
92
93#[spec_locked("5.2.5", "CalculateScriptFlags")]
95#[inline]
96fn add_per_tx_script_flags(
97 base_flags: u32,
98 tx: &Transaction,
99 has_witness: bool,
100 height: u64,
101 activation: &impl IsForkActive,
102) -> u32 {
103 let mut flags = base_flags;
104 if activation.is_fork_active(ForkId::SegWit, height)
105 && tx_requires_witness_script_flags(tx, has_witness)
106 {
107 flags |= 0x800;
108 }
109 if activation.is_fork_active(ForkId::Taproot, height) {
110 for output in &tx.outputs {
111 let script = &output.script_pubkey;
112 if script.len() == TAPROOT_SCRIPT_LENGTH
113 && script[0] == OP_1
114 && script[1] == PUSH_32_BYTES
115 {
116 flags |= 0x8000;
117 break;
118 }
119 }
120 }
121 flags
122}
123
124#[spec_locked("5.2.5", "CalculateScriptFlags")]
126pub(crate) fn calculate_script_flags_for_block(
127 tx: &Transaction,
128 has_witness: bool,
129 height: u64,
130 activation: &impl IsForkActive,
131) -> u32 {
132 let base = calculate_base_script_flags_for_block(height, activation);
133 add_per_tx_script_flags(base, tx, has_witness, height, activation)
134}
135
136#[spec_locked("5.2.5", "CalculateScriptFlags")]
138pub fn calculate_script_flags_for_block_network(
139 tx: &Transaction,
140 has_witness: bool,
141 height: u64,
142 network: crate::types::Network,
143) -> u32 {
144 let table = ForkActivationTable::from_network(network);
145 calculate_script_flags_for_block(tx, has_witness, height, &table)
146}
147
148#[spec_locked("5.2.5", "CalculateScriptFlags")]
150#[inline]
151pub(crate) fn calculate_script_flags_for_block_with_base(
152 tx: &Transaction,
153 has_witness: bool,
154 base_flags: u32,
155 height: u64,
156 activation: &impl IsForkActive,
157) -> u32 {
158 add_per_tx_script_flags(base_flags, tx, has_witness, height, activation)
159}
160
161fn block_hash_from_rpc_hex(hex_str: &str) -> Hash {
167 let mut bytes = hex::decode(hex_str).expect("valid 64-char block hash hex");
168 assert_eq!(bytes.len(), 32);
169 bytes.reverse();
170 bytes.try_into().expect("length 32")
171}
172
173static MAINNET_SCRIPT_FLAG_EXCEPTION_BIP16: LazyLock<Hash> = LazyLock::new(|| {
175 block_hash_from_rpc_hex("00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22")
176});
177static MAINNET_SCRIPT_FLAG_EXCEPTION_TAPROOT: LazyLock<Hash> = LazyLock::new(|| {
179 block_hash_from_rpc_hex("0000000000000000000f14c35b2d841e986ab5441de8c585d5ffe55ea1e395ad")
180});
181static TESTNET_SCRIPT_FLAG_EXCEPTION_BIP16: LazyLock<Hash> = LazyLock::new(|| {
183 block_hash_from_rpc_hex("00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105")
184});
185
186#[spec_locked("5.2.6", "ScriptFlagExceptions")]
190pub fn script_flag_exceptions_lookup(block_hash: &Hash, network: Network) -> Option<u32> {
191 match network {
192 Network::Mainnet => {
193 if block_hash == &*MAINNET_SCRIPT_FLAG_EXCEPTION_BIP16 {
194 Some(0)
195 } else if block_hash == &*MAINNET_SCRIPT_FLAG_EXCEPTION_TAPROOT {
196 Some(0x01 | 0x800) } else {
198 None
199 }
200 }
201 Network::Testnet => {
202 if block_hash == &*TESTNET_SCRIPT_FLAG_EXCEPTION_BIP16 {
203 Some(0)
204 } else {
205 None
206 }
207 }
208 Network::Regtest => None,
209 }
210}
211
212pub fn get_block_script_verify_flags_core(
221 block_hash: &Hash,
222 height: u64,
223 activation: &impl IsForkActive,
224 network: Network,
225) -> u32 {
226 let mut flags = SCRIPT_VERIFY_P2SH
229 | SCRIPT_VERIFY_WITNESS
230 | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE
231 | SCRIPT_VERIFY_TAPROOT;
232 if let Some(v) = script_flag_exceptions_lookup(block_hash, network) {
233 flags = v;
234 }
235 if activation.is_fork_active(ForkId::Bip66, height) {
236 flags |= 0x04; }
238 if activation.is_fork_active(ForkId::Bip65, height) {
239 flags |= 0x200; }
241 if activation.is_fork_active(ForkId::Bip112, height) {
242 flags |= 0x400; }
244 if activation.is_fork_active(ForkId::Bip147, height) {
245 flags |= 0x10; }
247 #[cfg(feature = "ctv")]
248 if activation.is_fork_active(ForkId::Ctv, height) {
249 flags |= 0x80000000;
250 }
251 flags
252}
253
254#[spec_locked("5.2.6", "GetBlockScriptFlags")]
261pub fn get_block_script_flags(
262 block_hash: &Hash,
263 tx: &Transaction,
264 has_witness: bool,
265 height: u64,
266 network: Network,
267) -> u32 {
268 if let Some(flags) = script_flag_exceptions_lookup(block_hash, network) {
269 flags
270 } else {
271 calculate_script_flags_for_block_network(tx, has_witness, height, network)
272 }
273}
274
275#[cfg(all(feature = "production", feature = "rayon"))]
281pub(super) fn insert_script_exec_cache_for_block(
282 block: &Block,
283 witnesses: &[Vec<Witness>],
284 height: u64,
285 context: &BlockValidationContext,
286) {
287 let block_hash = crate::crypto::OptimizedSha256::new().hash256(
288 &crate::serialization::block::serialize_block_header(&block.header),
289 );
290 let block_script_verify_flags =
291 get_block_script_verify_flags_core(&block_hash, height, context, context.network);
292 for (i, tx) in block.transactions.iter().enumerate() {
293 if is_coinbase(tx) {
294 continue;
295 }
296 let wits = witnesses.get(i).map(|w| w.as_slice()).unwrap_or(&[]);
297 let witnesses_vec: Vec<_> = if wits.len() == tx.inputs.len() {
298 wits.to_vec()
299 } else {
300 (0..tx.inputs.len()).map(|_| Vec::new()).collect()
301 };
302 let key =
303 crate::script_exec_cache::compute_key(tx, &witnesses_vec, block_script_verify_flags);
304 crate::script_exec_cache::insert(&key);
305 }
306}
307
308#[cfg(feature = "production")]
316pub(super) fn merge_overlay_changes_to_cache(
317 additions: &FxHashMap<OutPoint, std::sync::Arc<UTXO>>,
318 deletions: &FxHashSet<crate::utxo_overlay::UtxoDeletionKey>,
319 utxo_set: &mut UtxoSet,
320 mut bip30_index: Option<&mut crate::bip_validation::Bip30Index>,
321 mut undo_log: Option<&mut crate::reorganization::BlockUndoLog>,
322 skip_utxo_set_mutation: bool,
323) {
324 use crate::reorganization::UndoEntry;
325
326 for del_key in deletions {
327 let outpoint = crate::utxo_overlay::utxo_deletion_key_to_outpoint(del_key);
328 if skip_utxo_set_mutation {
329 if let Some(idx) = bip30_index.as_deref_mut() {
332 if utxo_set
333 .get(&outpoint)
334 .map(|arc| arc.is_coinbase)
335 .unwrap_or(false)
336 {
337 if let std::collections::hash_map::Entry::Occupied(mut o) =
338 idx.entry(outpoint.hash)
339 {
340 *o.get_mut() = o.get().saturating_sub(1);
341 if *o.get() == 0 {
342 o.remove();
343 }
344 }
345 }
346 }
347 } else if let Some(arc) = utxo_set.remove(&outpoint) {
349 if let Some(idx) = bip30_index.as_deref_mut() {
350 if arc.is_coinbase {
351 if let std::collections::hash_map::Entry::Occupied(mut o) =
352 idx.entry(outpoint.hash)
353 {
354 *o.get_mut() = o.get().saturating_sub(1);
355 if *o.get() == 0 {
356 o.remove();
357 }
358 }
359 }
360 }
361 if let Some(ref mut log) = undo_log {
362 log.entries.push(UndoEntry {
363 outpoint,
364 previous_utxo: Some(arc),
365 new_utxo: None,
366 });
367 }
368 }
369 }
370
371 if !skip_utxo_set_mutation {
372 for (outpoint, arc) in additions {
373 if let Some(ref mut log) = undo_log {
374 log.entries.push(UndoEntry {
375 outpoint: *outpoint,
376 previous_utxo: None,
377 new_utxo: Some(std::sync::Arc::clone(arc)),
378 });
379 }
380 utxo_set.insert(*outpoint, std::sync::Arc::clone(arc));
381 }
382 }
383}
384
385#[cfg(all(feature = "production", feature = "rayon"))]
388pub(super) fn compute_bip143_and_precomp(
389 tx: &Transaction,
390 prevout_values: &[i64],
391 script_pubkey_indices: &[(usize, usize)],
392 script_pubkey_buffer: &[u8],
393 has_witness: bool,
394) -> (
395 Option<crate::transaction_hash::Bip143PrecomputedHashes>,
396 Vec<Option<[u8; 32]>>,
397) {
398 let buf = script_pubkey_buffer;
399 let refs: Vec<&[u8]> = script_pubkey_indices
400 .iter()
401 .map(|&(s, l)| buf[s..s + l].as_ref())
402 .collect();
403 let refs: &[&[u8]] = &refs;
404 if has_witness {
405 let bip =
406 crate::transaction_hash::Bip143PrecomputedHashes::compute(tx, prevout_values, refs);
407 let mut precomp = vec![None; script_pubkey_indices.len()];
408 let mut specs: Vec<(usize, u8, &[u8])> = Vec::new();
409 for (j, &(s, l)) in script_pubkey_indices.iter().enumerate() {
410 let spk = &buf[s..s + l];
411 if spk.len() == 22 && spk[0] == OP_0 && spk[1] == PUSH_20_BYTES {
412 let mut script_code = [0u8; 25];
413 script_code[0] = OP_DUP;
414 script_code[1] = OP_HASH160;
415 script_code[2] = PUSH_20_BYTES;
416 script_code[3..23].copy_from_slice(&spk[2..22]);
417 script_code[23] = OP_EQUALVERIFY;
418 script_code[24] = OP_CHECKSIG;
419 let amount = prevout_values.get(j).copied().unwrap_or(0);
420 if let Ok(h) = crate::transaction_hash::calculate_bip143_sighash(
421 tx,
422 j,
423 &script_code,
424 amount,
425 0x01,
426 Some(&bip),
427 ) {
428 precomp[j] = Some(h);
429 }
430 } else if spk.len() == 23
431 && spk[0] == OP_HASH160
432 && spk[1] == PUSH_20_BYTES
433 && spk[22] == OP_EQUAL
434 {
435 if let Some((sighash_byte, redeem)) =
436 crate::script::parse_p2sh_p2pkh_for_precompute(&tx.inputs[j].script_sig)
437 {
438 specs.push((j, sighash_byte, redeem));
439 }
440 }
441 }
442 if !specs.is_empty() {
443 if let Ok(hashes) = crate::transaction_hash::batch_compute_legacy_sighashes(
444 tx,
445 prevout_values,
446 refs,
447 &specs,
448 ) {
449 for (k, &(j, _, _)) in specs.iter().enumerate() {
450 precomp[j] = Some(hashes[k]);
451 }
452 }
453 }
454 (Some(bip), precomp)
455 } else {
456 let mut precomp = vec![None; script_pubkey_indices.len()];
457 let mut specs: Vec<(usize, u8, &[u8])> = Vec::new();
458 for (j, &(s, l)) in script_pubkey_indices.iter().enumerate() {
459 let spk = &buf[s..s + l];
460 if spk.len() == 25
461 && spk[0] == OP_DUP
462 && spk[1] == OP_HASH160
463 && spk[2] == PUSH_20_BYTES
464 && spk[23] == OP_EQUALVERIFY
465 && spk[24] == OP_CHECKSIG
466 {
467 let script_sig = &tx.inputs[j].script_sig;
468 if let Some((sig, _pubkey)) = crate::script::parse_p2pkh_script_sig(script_sig) {
469 if !sig.is_empty() {
470 specs.push((j, sig[sig.len() - 1], spk));
471 }
472 }
473 } else if spk.len() == 23
474 && spk[0] == OP_HASH160
475 && spk[1] == PUSH_20_BYTES
476 && spk[22] == OP_EQUAL
477 {
478 if let Some((sighash_byte, redeem)) =
479 crate::script::parse_p2sh_p2pkh_for_precompute(&tx.inputs[j].script_sig)
480 {
481 specs.push((j, sighash_byte, redeem));
482 }
483 }
484 }
485 if !specs.is_empty() {
486 if let Ok(hashes) = crate::transaction_hash::batch_compute_legacy_sighashes(
487 tx,
488 prevout_values,
489 refs,
490 &specs,
491 ) {
492 for (k, &(j, _, _)) in specs.iter().enumerate() {
493 precomp[j] = Some(hashes[k]);
494 }
495 }
496 }
497 (None, precomp)
498 }
499}
500
501#[cfg(test)]
502mod script_flag_exceptions_tests {
503 use super::{
504 calculate_script_flags_for_block_network, get_block_script_flags,
505 get_block_script_verify_flags_core, script_flag_exceptions_lookup,
506 };
507 use crate::activation::ForkActivationTable;
508 use crate::crypto::OptimizedSha256;
509 use crate::script::flags::{
510 SCRIPT_VERIFY_P2SH, SCRIPT_VERIFY_TAPROOT, SCRIPT_VERIFY_WITNESS,
511 SCRIPT_VERIFY_WITNESS_PUBKEYTYPE,
512 };
513 use crate::serialization::block::{deserialize_block_header, serialize_block_header};
514 use crate::types::{Network, Transaction};
515
516 fn hash_from_rpc_hex(hex_str: &str) -> [u8; 32] {
517 let mut bytes = hex::decode(hex_str).unwrap();
518 assert_eq!(bytes.len(), 32);
519 bytes.reverse();
520 bytes.try_into().unwrap()
521 }
522
523 #[test]
524 fn mainnet_bip16_exception_zero() {
525 let h =
526 hash_from_rpc_hex("00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22");
527 assert_eq!(script_flag_exceptions_lookup(&h, Network::Mainnet), Some(0));
528 }
529
530 #[test]
531 fn mainnet_taproot_exception_p2sh_witness() {
532 let h =
533 hash_from_rpc_hex("0000000000000000000f14c35b2d841e986ab5441de8c585d5ffe55ea1e395ad");
534 assert_eq!(
535 script_flag_exceptions_lookup(&h, Network::Mainnet),
536 Some(0x01 | 0x800)
537 );
538 }
539
540 #[test]
541 fn testnet_bip16_exception_zero() {
542 let h =
543 hash_from_rpc_hex("00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105");
544 assert_eq!(script_flag_exceptions_lookup(&h, Network::Testnet), Some(0));
545 assert_eq!(script_flag_exceptions_lookup(&h, Network::Mainnet), None);
546 }
547
548 #[test]
549 fn regtest_has_no_exceptions() {
550 let h =
551 hash_from_rpc_hex("00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22");
552 assert_eq!(script_flag_exceptions_lookup(&h, Network::Regtest), None);
553 }
554
555 #[test]
556 fn get_block_script_flags_uses_exception() {
557 let h =
558 hash_from_rpc_hex("00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22");
559 let tx = Transaction {
560 version: 1,
561 inputs: Default::default(),
562 outputs: Default::default(),
563 lock_time: 0,
564 };
565 let flags = get_block_script_flags(&h, &tx, false, 1_000_000, Network::Mainnet);
566 assert_eq!(flags, 0);
567 }
568
569 #[test]
570 fn get_block_script_flags_falls_back_to_calculate() {
571 let h = [7u8; 32];
572 let tx = Transaction {
573 version: 1,
574 inputs: Default::default(),
575 outputs: Default::default(),
576 lock_time: 0,
577 };
578 assert_eq!(
579 get_block_script_flags(&h, &tx, false, 100, Network::Mainnet),
580 calculate_script_flags_for_block_network(&tx, false, 100, Network::Mainnet),
581 );
582 }
583
584 #[test]
585 fn genesis_rpc_hex_matches_hash256_of_header() {
586 let genesis_block_hex = "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c010100000001000000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000";
587 let bytes = hex::decode(genesis_block_hex).unwrap();
588 let header = deserialize_block_header(&bytes[..80]).unwrap();
589 let digest = OptimizedSha256::new().hash256(&serialize_block_header(&header));
590 let expected =
591 hash_from_rpc_hex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
592 assert_eq!(digest, expected);
593 }
594
595 #[test]
596 fn core_block_flags_non_exception_includes_p2sh_witness_taproot_and_deployments() {
597 let table = ForkActivationTable::from_network(Network::Mainnet);
598 let h = [0xabu8; 32];
599 let flags = get_block_script_verify_flags_core(&h, 800_000, &table, Network::Mainnet);
600 assert_eq!(
602 flags
603 & (SCRIPT_VERIFY_P2SH
604 | SCRIPT_VERIFY_WITNESS
605 | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE
606 | SCRIPT_VERIFY_TAPROOT),
607 SCRIPT_VERIFY_P2SH
608 | SCRIPT_VERIFY_WITNESS
609 | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE
610 | SCRIPT_VERIFY_TAPROOT,
611 "P2SH | WITNESS | WITNESS_PUBKEYTYPE | TAPROOT baseline"
612 );
613 assert_ne!(flags & 0x04, 0, "DERSIG");
614 assert_ne!(flags & 0x200, 0, "CLTV");
615 assert_ne!(flags & 0x400, 0, "CSV");
616 assert_ne!(flags & 0x10, 0, "NULLDUMMY");
617 }
618
619 #[test]
620 fn core_block_flags_bip16_exception_orrs_buried_deployments() {
621 let table = ForkActivationTable::from_network(Network::Mainnet);
622 let h =
623 hash_from_rpc_hex("00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22");
624 assert_eq!(script_flag_exceptions_lookup(&h, Network::Mainnet), Some(0));
625 let flags = get_block_script_verify_flags_core(&h, 800_000, &table, Network::Mainnet);
626 let deployment_mask = 0x04 | 0x200 | 0x400 | 0x10;
627 assert_eq!(flags & 0x8801, 0);
628 assert_eq!(flags & deployment_mask, deployment_mask);
629 assert_eq!(flags & 0x800, 0);
630 assert_eq!(flags & 0x8000, 0);
631 assert_eq!(flags & 0x01, 0);
632 }
633
634 #[test]
635 fn core_block_flags_taproot_exception_orrs_deployments() {
636 let table = ForkActivationTable::from_network(Network::Mainnet);
637 let h =
638 hash_from_rpc_hex("0000000000000000000f14c35b2d841e986ab5441de8c585d5ffe55ea1e395ad");
639 let flags = get_block_script_verify_flags_core(&h, 800_000, &table, Network::Mainnet);
640 assert_eq!(flags & 0x8801, 0x801);
641 assert_eq!(flags & 0x8000, 0);
642 assert_ne!(flags & 0x800, 0);
643 assert_ne!(flags & 0x04, 0);
644 }
645}