Skip to main content

blvm_consensus/block/
script_cache.rs

1//! Script verification flags and script-exec cache helpers for block validation.
2//!
3//! Groups base/per-tx script flags, cache insertion/merge, and BIP143 precompute
4//! so block connect logic stays in the parent module.
5
6use 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// ---------------------------------------------------------------------------
24// Script flags (base, per-tx, and combined)
25// ---------------------------------------------------------------------------
26
27/// Base script flags for a block from activation context.
28/// Call once per block, then use `calculate_script_flags_for_block` or `add_per_tx_script_flags`.
29#[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; // SCRIPT_VERIFY_P2SH
39    }
40    // BIP66: strict DER for ECDSA signatures only. Bitcoin Core `GetBlockScriptFlags`
41    // adds SCRIPT_VERIFY_DERSIG here — not SCRIPT_VERIFY_STRICTENC or SCRIPT_VERIFY_LOW_S
42    // (those are standardness / mempool policy; legacy blocks may contain high-S sigs).
43    if activation.is_fork_active(ForkId::Bip66, height) {
44        flags |= 0x04; // SCRIPT_VERIFY_DERSIG
45    }
46    if activation.is_fork_active(ForkId::Bip65, height) {
47        flags |= 0x200; // CHECKLOCKTIMEVERIFY
48    }
49    // BIP112 (CSV): CHECKSEQUENCEVERIFY at CSV deployment height (mainnet 419328).
50    // Bitcoin Core `GetBlockScriptFlags`: DEPLOYMENT_CSV, not SegWit.
51    if activation.is_fork_active(ForkId::Bip112, height) {
52        flags |= 0x400; // SCRIPT_VERIFY_CHECKSEQUENCEVERIFY
53    }
54    // BIP147 NULLDUMMY: Bitcoin Core enables at DEPLOYMENT_SEGWIT (same height as BIP147 on mainnet).
55    if activation.is_fork_active(ForkId::Bip147, height) {
56        flags |= 0x10; // SCRIPT_VERIFY_NULLDUMMY
57    }
58    #[cfg(feature = "ctv")]
59    if activation.is_fork_active(ForkId::Ctv, height) {
60        flags |= 0x80000000; // CHECK_TEMPLATE_VERIFY_HASH
61    }
62
63    flags
64}
65
66/// Convenience: base script flags from (height, network) when no context is available (e.g. mempool).
67#[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/// Per-tx script flags (SegWit + Taproot). Add to base flags from `calculate_base_script_flags_for_block`.
77#[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/// Calculate script verification flags for a transaction in a block (with activation context).
108#[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/// Convenience: script flags from (height, network) when no context is available (e.g. mempool, bench tools).
120#[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/// Calculate script verification flags for a transaction in a block (with precomputed base flags).
132#[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
144// ---------------------------------------------------------------------------
145// §5.2.6 Script flag exceptions (Orange Paper; table from Bitcoin Core chainparams)
146// ---------------------------------------------------------------------------
147
148/// RPC / explorer 64-hex block id → canonical digest order used by `hash256(serialize(header))` here.
149fn 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
156/// BIP16 exception block (mainnet). `SCRIPT_VERIFY_NONE` in Core.
157static MAINNET_SCRIPT_FLAG_EXCEPTION_BIP16: LazyLock<Hash> = LazyLock::new(|| {
158    block_hash_from_rpc_hex("00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22")
159});
160/// Taproot exception block (mainnet). `SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS` in Core.
161static MAINNET_SCRIPT_FLAG_EXCEPTION_TAPROOT: LazyLock<Hash> = LazyLock::new(|| {
162    block_hash_from_rpc_hex("0000000000000000000f14c35b2d841e986ab5441de8c585d5ffe55ea1e395ad")
163});
164/// BIP16 exception block (testnet3). `SCRIPT_VERIFY_NONE` in Core.
165static TESTNET_SCRIPT_FLAG_EXCEPTION_BIP16: LazyLock<Hash> = LazyLock::new(|| {
166    block_hash_from_rpc_hex("00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105")
167});
168
169/// Consensus script-flag override for `block_hash` on `network`, if any (partial map entry).
170///
171/// Bitcoin Core: `Consensus::Params::script_flag_exceptions` (`src/kernel/chainparams.cpp`).
172#[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) // SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS
180            } 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
195/// Bitcoin Core `GetBlockScriptFlags` (`validation.cpp`): start from `SCRIPT_VERIFY_P2SH |
196/// SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_TAPROOT`, replace with the script-flag exception entry
197/// when present, then OR buried deployments (BIP66, BIP65, CSV, BIP147-at-SegWit). Same **block-level**
198/// bitmask is passed to script checks for every non-coinbase transaction.
199///
200/// This is **not** the Orange Paper §5.2.6 piecewise `GetBlockScriptFlags` (exception vs per-tx
201/// `CalculateScriptFlags`); use [`get_block_script_flags`] for that formulation. `connect_block`
202/// uses this function for **mainnet consensus parity** with Bitcoin Core.
203pub fn get_block_script_verify_flags_core(
204    block_hash: &Hash,
205    height: u64,
206    activation: &impl IsForkActive,
207    network: Network,
208) -> u32 {
209    // Baseline: P2SH + WITNESS + WITNESS_PUBKEYTYPE + TAPROOT.
210    // TAPROOT is 0x20000 (bit 17). Using 0x8000 here was a bug — that is WITNESS_PUBKEYTYPE.
211    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; // SCRIPT_VERIFY_DERSIG
220    }
221    if activation.is_fork_active(ForkId::Bip65, height) {
222        flags |= 0x200; // CHECKLOCKTIMEVERIFY
223    }
224    if activation.is_fork_active(ForkId::Bip112, height) {
225        flags |= 0x400; // CHECKSEQUENCEVERIFY
226    }
227    if activation.is_fork_active(ForkId::Bip147, height) {
228        flags |= 0x10; // NULLDUMMY (Core: `DEPLOYMENT_SEGWIT`)
229    }
230    #[cfg(feature = "ctv")]
231    if activation.is_fork_active(ForkId::Ctv, height) {
232        flags |= 0x80000000;
233    }
234    flags
235}
236
237/// Orange Paper §5.2.6: exception table wins when defined; otherwise per-tx `CalculateScriptFlags`.
238///
239/// **Note:** Bitcoin Core’s `GetBlockScriptFlags` (`validation.cpp`) starts from a default mask, applies
240/// this exception table, then ORs buried deployments (BIP66, BIP65, CSV, BIP147). **`connect_block`**
241/// uses [`get_block_script_verify_flags_core`] for that Core behavior; this function remains the
242/// Orange Paper §5.2.6 piecewise spec.
243#[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// ---------------------------------------------------------------------------
259// Script-exec cache and overlay merge
260// ---------------------------------------------------------------------------
261
262/// Insert script exec cache keys for all txs in block (call when block validation passes).
263#[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/// Merge overlay changes into the UTXO set and update BIP30 index / undo log.
292///
293/// `skip_utxo_set_mutation`: when `true` (IBD path), the `utxo_set` is a per-block
294/// scratch view that is discarded after this call. Skip all HashMap inserts/removes —
295/// only BIP30 coinbase deletion accounting is performed (via a non-mutating `get`).
296/// The non-IBD path (`false`) performs the full remove/insert cycle and builds the
297/// undo log entry. `undo_log` must be `None` when `skip_utxo_set_mutation` is `true`.
298#[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            // IBD fast path: utxo_set is discarded after this call.
313            // Use get (no remove) solely for the BIP30 coinbase check.
314            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            // undo_log is always None on the IBD path — nothing to do.
331        } 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/// Compute BIP143/precomputed sighash for CCheckQueue path. Uses local refs and specs Vecs
369/// (dropped before return) so buf borrow ends.
370#[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        // P2SH (0x1) | WITNESS (0x800) | WITNESS_PUBKEYTYPE (0x8000) | TAPROOT (0x20000)
584        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}