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