Skip to main content

blvm_consensus/
mining.rs

1//! Mining and block creation functions from Orange Paper Section 10.1
2
3#[cfg(test)]
4use crate::economic::get_block_subsidy;
5use crate::economic::get_block_subsidy_for_network;
6use crate::error::Result;
7use crate::pow::{check_proof_of_work, next_required_bits};
8use crate::transaction::check_transaction;
9use crate::types::*;
10#[cfg(feature = "spec-lock")]
11use blvm_spec_lock::spec_locked;
12
13#[cfg(test)]
14use crate::transaction::is_coinbase;
15
16/// Compact bits for a block template.
17///
18/// `prev_headers` is oldest-to-newest and ends at `prev_header`'s height.
19/// Height 0 uses the network proof-of-work limit.
20fn template_bits(
21    network: Network,
22    height: Natural,
23    block_timestamp: u64,
24    parent: &BlockHeader,
25    prev_headers: &[BlockHeader],
26) -> Result<Natural> {
27    if height == 0 {
28        return Ok(match network {
29            Network::Regtest => 0x207fffff,
30            Network::Signet => 0x1e0377ae,
31            Network::Mainnet | Network::Testnet | Network::Testnet4 => {
32                crate::constants::MAX_TARGET as u64
33            }
34        });
35    }
36    let parent_height = height - 1;
37    let window_end = parent_height;
38    let window_start = if prev_headers.is_empty() {
39        window_end
40    } else {
41        window_end.saturating_sub(prev_headers.len() as u64 - 1)
42    };
43    next_required_bits(network, height, block_timestamp, &|h| {
44        if h == parent_height {
45            return Some((parent.bits, parent.timestamp));
46        }
47        if prev_headers.is_empty() || h < window_start || h > window_end {
48            return None;
49        }
50        prev_headers
51            .get((h - window_start) as usize)
52            .map(|hdr| (hdr.bits, hdr.timestamp))
53    })?
54    .ok_or_else(|| {
55        crate::error::ConsensusError::InvalidProofOfWork(
56            "genesis template has no parent bits".into(),
57        )
58    })
59}
60
61/// Minimum header version `check_bip90` accepts at `height`.
62///
63/// Height 1 on mainnet is still version 1. After BIP65 the minimum is 4.
64pub fn template_block_version(network: Network, height: Natural) -> i64 {
65    use crate::activation::{ForkActivationTable, IsForkActive};
66    let table = ForkActivationTable::from_network(network);
67    if table.is_fork_active(ForkId::Bip65, height) {
68        4
69    } else if table.is_fork_active(ForkId::Bip66, height) {
70        3
71    } else if table.is_fork_active(ForkId::Bip34, height) {
72        2
73    } else {
74        1
75    }
76}
77
78/// CreateNewBlock: 𝒰𝒮 × 𝒯𝒳* → ℬ
79///
80/// For UTXO set us and mempool transactions txs:
81/// 1. Create coinbase transaction with appropriate subsidy
82/// 2. Select transactions from mempool based on fee rate
83/// 3. Calculate merkle root
84/// 4. Create block header with appropriate difficulty
85/// 5. Return new block
86pub fn create_new_block(
87    utxo_set: &UtxoSet,
88    mempool_txs: &[Transaction],
89    height: Natural,
90    prev_header: &BlockHeader,
91    prev_headers: &[BlockHeader],
92    coinbase_script: &ByteString,
93    coinbase_address: &ByteString,
94) -> Result<Block> {
95    // For backward compatibility, derive block_time from system clock here.
96    let block_time = get_current_timestamp();
97    create_new_block_with_time(
98        utxo_set,
99        mempool_txs,
100        height,
101        prev_header,
102        prev_headers,
103        coinbase_script,
104        coinbase_address,
105        block_time,
106        Network::Mainnet,
107        None,
108    )
109}
110
111/// CreateNewBlock variant that accepts an explicit block_time.
112///
113/// This allows callers (e.g., node layer) to provide a median time-past or
114/// adjusted network time instead of relying on `SystemTime::now()` inside
115/// consensus code.
116#[allow(clippy::too_many_arguments)]
117pub fn create_new_block_with_time(
118    utxo_set: &UtxoSet,
119    mempool_txs: &[Transaction],
120    height: Natural,
121    prev_header: &BlockHeader,
122    prev_headers: &[BlockHeader],
123    coinbase_script: &ByteString,
124    coinbase_address: &ByteString,
125    block_time: u64,
126    network: Network,
127    mempool_witnesses: Option<&[Option<Vec<crate::segwit::Witness>>]>,
128) -> Result<Block> {
129    use crate::bip113::get_median_time_past;
130    use crate::mempool::{Mempool, MempoolResult, accept_to_memory_pool};
131
132    let median_time_past = get_median_time_past(prev_headers);
133    let block_time = block_time.max(median_time_past.saturating_add(1));
134
135    // 1. Create coinbase transaction
136    let coinbase_tx = create_coinbase_transaction(
137        height,
138        get_block_subsidy_for_network(height, network),
139        coinbase_script,
140        coinbase_address,
141    )?;
142
143    // Header (80 bytes plus a 3-byte transaction count) and the coinbase are in the
144    // block before any mempool transaction. The witness commitment is appended later.
145    const WITNESS_COMMITMENT_RESERVE: u64 = 4_000;
146    let header_weight = 83u64.saturating_mul(4);
147    let coinbase_weight =
148        crate::segwit::transaction_weight_from_stacks(&coinbase_tx, None).unwrap_or(0);
149    let mut running_weight = header_weight
150        .saturating_add(coinbase_weight)
151        .saturating_add(WITNESS_COMMITMENT_RESERVE);
152    // Block sigop cost counts witness programs. P2SH is 0x01. Witness is 0x800.
153    const BLOCK_SIGOP_FLAGS: u32 = 0x01 | 0x800;
154    let mut running_sigops = crate::sigop::get_transaction_sigop_cost_with_witness_slices(
155        &coinbase_tx,
156        utxo_set,
157        None,
158        BLOCK_SIGOP_FLAGS,
159    )
160    .unwrap_or(0);
161    let max_weight = crate::constants::MAX_BLOCK_WEIGHT as u64;
162    let max_sigops = crate::constants::MAX_BLOCK_SIGOPS_COST;
163
164    // 2. Select transactions from mempool with proper validation.
165    // Outputs of a selected transaction are spendable by a later one in this template.
166    // A child that sorts ahead of its parent is tried again after that parent is selected.
167    let mut selected_txs = Vec::new();
168    let mut temp_mempool = Mempool::new();
169    let mut available = utxo_set.clone();
170    let mut waiting: Vec<usize> = (0..mempool_txs.len()).collect();
171
172    while !waiting.is_empty() {
173        let mut still_waiting = Vec::new();
174        let selected_before = selected_txs.len();
175        for idx in waiting {
176            let tx = &mempool_txs[idx];
177            if check_transaction(tx)? != ValidationResult::Valid {
178                continue;
179            }
180
181            let time_context = Some(TimeContext {
182                network_time: block_time,
183                median_time_past,
184            });
185            let tx_witnesses = mempool_witnesses
186                .and_then(|all| all.get(idx))
187                .and_then(|w| w.as_deref());
188            match accept_to_memory_pool(
189                tx,
190                tx_witnesses,
191                &available,
192                &temp_mempool,
193                height,
194                time_context,
195                network,
196                None,
197            )? {
198                MempoolResult::Accepted => {
199                    let tx_weight =
200                        match crate::segwit::transaction_weight_from_stacks(tx, tx_witnesses) {
201                            Ok(weight) => weight,
202                            Err(_) => continue,
203                        };
204                    let tx_sigops =
205                        match crate::sigop::get_transaction_sigop_cost_with_witness_slices(
206                            tx,
207                            &available,
208                            tx_witnesses,
209                            BLOCK_SIGOP_FLAGS,
210                        ) {
211                            Ok(cost) => cost,
212                            Err(_) => continue,
213                        };
214                    if running_weight.saturating_add(tx_weight) > max_weight
215                        || running_sigops.saturating_add(tx_sigops) > max_sigops
216                    {
217                        continue;
218                    }
219                    running_weight = running_weight.saturating_add(tx_weight);
220                    running_sigops = running_sigops.saturating_add(tx_sigops);
221                    temp_mempool.insert_transaction_with_witness(tx, tx_witnesses);
222                    let txid = crate::block::calculate_tx_id(tx);
223                    for (index, output) in tx.outputs.iter().enumerate() {
224                        available.insert(
225                            OutPoint {
226                                hash: txid,
227                                index: index as u32,
228                            },
229                            std::sync::Arc::new(UTXO {
230                                value: output.value,
231                                script_pubkey: output.script_pubkey.clone().into(),
232                                height,
233                                is_coinbase: false,
234                            }),
235                        );
236                    }
237                    selected_txs.push(tx.clone());
238                }
239                MempoolResult::Rejected(_reason) => {
240                    // The parent may appear later in this fee order.
241                    #[cfg(test)]
242                    eprintln!("Transaction rejected: {_reason}");
243                    still_waiting.push(idx);
244                }
245            }
246        }
247        if selected_txs.len() == selected_before {
248            break;
249        }
250        waiting = still_waiting;
251    }
252
253    // 3. Build transaction list (coinbase first)
254    let mut transactions = vec![coinbase_tx];
255    transactions.extend(selected_txs);
256
257    // 4. Calculate merkle root
258    let merkle_root = calculate_merkle_root(&transactions)?;
259
260    // 5. Required compact bits for this height. Off a period boundary this is the
261    // parent's bits (or the minimum-difficulty exception). The retarget formula
262    // runs only when this height starts a new difficulty period.
263    let next_work = template_bits(network, height, block_time, prev_header, prev_headers)?;
264
265    // 6. Create block header
266    let header = BlockHeader {
267        version: template_block_version(network, height),
268        prev_block_hash: calculate_block_hash(prev_header),
269        merkle_root,
270        timestamp: block_time,
271        bits: next_work,
272        nonce: 0, // Will be set during mining
273    };
274
275    Ok(Block {
276        header,
277        transactions: transactions.into_boxed_slice(),
278    })
279}
280
281/// MineBlock: ℬ × ℕ → ℬ × {success, failure}
282///
283/// Attempt to mine a block by finding a valid nonce:
284/// 1. Try different nonce values
285/// 2. Check if resulting hash meets difficulty target
286/// 3. Return mined block or failure
287#[track_caller] // Better error messages showing caller location
288pub fn mine_block(mut block: Block, max_attempts: Natural) -> Result<(Block, MiningResult)> {
289    for nonce in 0..max_attempts {
290        block.header.nonce = nonce;
291
292        if check_proof_of_work(&block.header).unwrap_or(false) {
293            return Ok((block, MiningResult::Success));
294        }
295    }
296
297    Ok((block, MiningResult::Failure))
298}
299
300/// BlockTemplate: Interface for mining software
301///
302/// Provides a template for mining software to work with:
303/// 1. Block header with current difficulty
304/// 2. Coinbase transaction template
305/// 3. Selected transactions
306/// 4. Mining parameters
307#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
308pub struct BlockTemplate {
309    pub header: BlockHeader,
310    pub coinbase_tx: Transaction,
311    pub transactions: Vec<Transaction>,
312    pub target: crate::pow::U256,
313    pub height: Natural,
314    pub timestamp: Natural,
315}
316
317/// Create a block template for mining
318pub fn create_block_template(
319    utxo_set: &UtxoSet,
320    mempool_txs: &[Transaction],
321    height: Natural,
322    prev_header: &BlockHeader,
323    prev_headers: &[BlockHeader],
324    coinbase_script: &ByteString,
325    coinbase_address: &ByteString,
326    network: Network,
327    mempool_witnesses: Option<&[Option<Vec<crate::segwit::Witness>>]>,
328) -> Result<BlockTemplate> {
329    let block_time = get_current_timestamp();
330    let block = create_new_block_with_time(
331        utxo_set,
332        mempool_txs,
333        height,
334        prev_header,
335        prev_headers,
336        coinbase_script,
337        coinbase_address,
338        block_time,
339        network,
340        mempool_witnesses,
341    )?;
342
343    let target = crate::pow::expand_target(block.header.bits)?;
344
345    let header = block.header.clone();
346
347    // Use proven bounds for coinbase access (block has been validated)
348    #[cfg(feature = "production")]
349    let coinbase_tx = {
350        use crate::optimizations::_optimized_access::get_proven_by_;
351        get_proven_by_(&block.transactions, 0)
352            .ok_or_else(|| {
353                crate::error::ConsensusError::BlockValidation("Block has no transactions".into())
354            })?
355            .clone()
356    };
357
358    #[cfg(not(feature = "production"))]
359    let coinbase_tx = block.transactions[0].clone();
360
361    Ok(BlockTemplate {
362        header: block.header.clone(),
363        coinbase_tx,
364        transactions: block.transactions[1..].to_vec(),
365        target,
366        height,
367        timestamp: header.timestamp,
368    })
369}
370
371// ============================================================================
372// HELPER FUNCTIONS
373// ============================================================================
374
375/// Result of mining attempt
376#[derive(Debug, Clone, PartialEq, Eq)]
377pub enum MiningResult {
378    Success,
379    Failure,
380}
381
382/// Create coinbase transaction
383/// Orange Paper 12.2: Coinbase transaction structure.
384/// BIP54: When BIP54 is active, coinbase must have nLockTime = height - 1 and nSequence != 0xffff_ffff.
385/// This implementation sets those so that blocks are valid under BIP54 when activated.
386fn create_coinbase_transaction(
387    height: Natural,
388    subsidy: Integer,
389    script: &ByteString,
390    address: &ByteString,
391) -> Result<Transaction> {
392    create_coinbase_with_outputs(height, script, &[(subsidy, address.clone())])
393}
394
395/// Multi-output coinbase. Callers set values (subsidy + fees). Empty list is an error.
396/// Witness commitment is appended by `create_block_template_with_outputs` / Stratum.
397pub fn create_coinbase_with_outputs(
398    height: Natural,
399    script: &ByteString,
400    outputs: &[(Integer, ByteString)],
401) -> Result<Transaction> {
402    if outputs.is_empty() {
403        return Err(crate::error::ConsensusError::InvalidProofOfWork(
404            "coinbase requires at least one output".into(),
405        ));
406    }
407    let lock_time = height.saturating_sub(1);
408    let coinbase_input = TransactionInput {
409        prevout: OutPoint {
410            hash: [0u8; 32],
411            index: 0xffffffff,
412        },
413        script_sig: script.clone(),
414        sequence: 0xfffffffe, // BIP54: not 0xffffffff so coinbase is unique
415    };
416    let outs: Vec<TransactionOutput> = outputs
417        .iter()
418        .map(|(value, script_pubkey)| TransactionOutput {
419            value: *value,
420            script_pubkey: script_pubkey.clone(),
421        })
422        .collect();
423
424    Ok(Transaction {
425        version: 1,
426        inputs: crate::tx_inputs![coinbase_input],
427        outputs: outs.into(),
428        lock_time,
429    })
430}
431
432/// Same as `create_block_template`, then replace the coinbase and merkle root.
433#[allow(clippy::too_many_arguments)]
434pub fn create_block_template_with_outputs(
435    utxo_set: &UtxoSet,
436    mempool_txs: &[Transaction],
437    height: Natural,
438    prev_header: &BlockHeader,
439    prev_headers: &[BlockHeader],
440    coinbase_script: &ByteString,
441    coinbase_outputs: &[(Integer, ByteString)],
442    network: Network,
443    mempool_witnesses: Option<&[Option<Vec<crate::segwit::Witness>>]>,
444) -> Result<BlockTemplate> {
445    let address = coinbase_outputs
446        .first()
447        .map(|(_, s)| s.clone())
448        .unwrap_or_default();
449    let mut tmpl = create_block_template(
450        utxo_set,
451        mempool_txs,
452        height,
453        prev_header,
454        prev_headers,
455        coinbase_script,
456        &address,
457        network,
458        mempool_witnesses,
459    )?;
460    let subsidy = get_block_subsidy_for_network(height, network);
461    let fees = sum_selected_fees(utxo_set, &tmpl.transactions);
462    let fitted = fit_payouts_to_reward(coinbase_outputs, subsidy, fees)?;
463    tmpl.coinbase_tx = create_coinbase_with_outputs(height, coinbase_script, &fitted)?;
464    let mut txs = Vec::with_capacity(1 + tmpl.transactions.len());
465    txs.push(tmpl.coinbase_tx.clone());
466    txs.extend(tmpl.transactions.iter().cloned());
467    let nested = nested_witnesses_for_selected(&txs, mempool_txs, mempool_witnesses)?;
468    append_witness_commitment_from_nested(&mut tmpl.coinbase_tx, &mut txs, Some(&nested))?;
469    tmpl.header.merkle_root = calculate_merkle_root(&txs)?;
470    Ok(tmpl)
471}
472
473/// Resolve named txids from a mempool list, in declaration order.
474/// Unknown or duplicate txids are errors. An empty list is allowed (coinbase-only).
475pub fn resolve_declared_txs(
476    mempool_txs: &[Transaction],
477    txids: &[Hash],
478) -> Result<Vec<Transaction>> {
479    use crate::block::calculate_tx_id;
480    use std::collections::{HashMap, HashSet};
481
482    let mut by_id: HashMap<Hash, &Transaction> = HashMap::new();
483    for tx in mempool_txs {
484        by_id.entry(calculate_tx_id(tx)).or_insert(tx);
485    }
486    let mut seen = HashSet::new();
487    let mut out = Vec::with_capacity(txids.len());
488    for id in txids {
489        if !seen.insert(*id) {
490            return Err(crate::error::ConsensusError::BlockValidation(
491                format!("duplicate declared txid {}", hex::encode(id)).into(),
492            ));
493        }
494        let tx = by_id.get(id).ok_or_else(|| {
495            crate::error::ConsensusError::BlockValidation(
496                format!("unknown declared txid {}", hex::encode(id)).into(),
497            )
498        })?;
499        out.push((*tx).clone());
500    }
501    Ok(out)
502}
503
504fn declared_tx_spends_witness_utxo(tx: &Transaction, utxo_set: &UtxoSet) -> bool {
505    tx.inputs.iter().any(|input| {
506        utxo_set.get(&input.prevout).is_some_and(|utxo| {
507            crate::witness::is_standard_witness_program(utxo.script_pubkey.as_ref())
508        })
509    })
510}
511
512/// Align mempool witness slots to the declared subset by txid.
513/// A witness spend without stored stacks is an error (no empty-stack BIP141).
514fn align_declared_witnesses(
515    declared: &[Transaction],
516    mempool_txs: &[Transaction],
517    mempool_witnesses: Option<&[Option<Vec<crate::segwit::Witness>>]>,
518    utxo_set: &UtxoSet,
519) -> Result<Vec<Option<Vec<crate::segwit::Witness>>>> {
520    use crate::block::calculate_tx_id;
521    use std::collections::HashMap;
522
523    let mut by_txid = HashMap::new();
524    if let Some(wits) = mempool_witnesses {
525        for (tx, w) in mempool_txs.iter().zip(wits.iter()) {
526            by_txid.insert(calculate_tx_id(tx), w.clone());
527        }
528    }
529    let mut out = Vec::with_capacity(declared.len());
530    for tx in declared {
531        let id = calculate_tx_id(tx);
532        let slot = by_txid.get(&id).cloned().flatten();
533        if let Some(ref stacks) = slot {
534            if stacks.len() != tx.inputs.len() {
535                return Err(crate::error::ConsensusError::BlockValidation(
536                    format!(
537                        "witness count {} != input count {} for declared tx {}",
538                        stacks.len(),
539                        tx.inputs.len(),
540                        hex::encode(id)
541                    )
542                    .into(),
543                ));
544            }
545        } else if declared_tx_spends_witness_utxo(tx, utxo_set) {
546            return Err(crate::error::ConsensusError::BlockValidation(
547                format!(
548                    "missing mempool witnesses for declared witness spend {}",
549                    hex::encode(id)
550                )
551                .into(),
552            ));
553        }
554        // A legacy spend has no witness. Store empty stacks so the commitment
555        // lookup finds this txid. A None slot stays an error for a witness spend.
556        let slot = slot.or_else(|| Some(vec![Vec::new(); tx.inputs.len()]));
557        out.push(slot);
558    }
559    Ok(out)
560}
561
562/// Stage 3b slice 1: miner-declared mempool subset.
563/// Resolves `declared_txids`, builds the Stage 3a template, and errors if the
564/// selected non-coinbase txids are not exactly that list (no silent skip).
565/// Empty `declared_txids` is coinbase-only. Spec-locked builders are unchanged.
566/// Overweight uses Bitcoin `MAX_BLOCK_WEIGHT` (not a pool-invented cap).
567#[allow(clippy::too_many_arguments)]
568pub fn create_block_template_declared(
569    utxo_set: &UtxoSet,
570    mempool_txs: &[Transaction],
571    declared_txids: &[Hash],
572    height: Natural,
573    prev_header: &BlockHeader,
574    prev_headers: &[BlockHeader],
575    coinbase_script: &ByteString,
576    coinbase_outputs: &[(Integer, ByteString)],
577    network: Network,
578    mempool_witnesses: Option<&[Option<Vec<crate::segwit::Witness>>]>,
579) -> Result<BlockTemplate> {
580    use crate::block::calculate_tx_id;
581
582    let declared = resolve_declared_txs(mempool_txs, declared_txids)?;
583    let aligned = align_declared_witnesses(&declared, mempool_txs, mempool_witnesses, utxo_set)?;
584    let tmpl = create_block_template_with_outputs(
585        utxo_set,
586        &declared,
587        height,
588        prev_header,
589        prev_headers,
590        coinbase_script,
591        coinbase_outputs,
592        network,
593        Some(aligned.as_slice()),
594    )?;
595    let selected: Vec<Hash> = tmpl.transactions.iter().map(calculate_tx_id).collect();
596    if selected.as_slice() != declared_txids {
597        return Err(crate::error::ConsensusError::BlockValidation(
598            "declared transaction was not selected (rejected or skipped)".into(),
599        ));
600    }
601    check_template_weight(&tmpl, crate::constants::MAX_BLOCK_WEIGHT as u64)?;
602    Ok(tmpl)
603}
604
605/// Weight of coinbase + selected txs (empty stacks if none stored).
606/// Uses Bitcoin `MAX_BLOCK_WEIGHT` (4_000_000). Not an invented pool cap.
607pub fn check_template_weight(tmpl: &BlockTemplate, max_weight: u64) -> Result<()> {
608    let mut txs = Vec::with_capacity(1 + tmpl.transactions.len());
609    txs.push(tmpl.coinbase_tx.clone());
610    txs.extend(tmpl.transactions.iter().cloned());
611    let block = Block {
612        header: tmpl.header.clone(),
613        transactions: txs.clone().into_boxed_slice(),
614    };
615    let nested: Vec<Vec<crate::segwit::Witness>> = txs
616        .iter()
617        .map(|tx| vec![Vec::new(); tx.inputs.len()])
618        .collect();
619    let weight = crate::segwit::calculate_block_weight_from_nested(&block, &nested)?;
620    if weight > max_weight {
621        return Err(crate::error::ConsensusError::BlockValidation(
622            format!("declared template weight {weight} exceeds max {max_weight}").into(),
623        ));
624    }
625    Ok(())
626}
627
628/// Coinbase empty stacks, then mempool witnesses aligned to selected txs by txid.
629fn nested_witnesses_for_selected(
630    txs: &[Transaction],
631    mempool_txs: &[Transaction],
632    mempool_witnesses: Option<&[Option<Vec<crate::segwit::Witness>>]>,
633) -> Result<Vec<Vec<crate::segwit::Witness>>> {
634    use crate::block::calculate_tx_id;
635    use std::collections::HashMap;
636
637    let mut by_txid = HashMap::new();
638    if let Some(wits) = mempool_witnesses {
639        for (tx, w) in mempool_txs.iter().zip(wits.iter()) {
640            if let Some(stacks) = w {
641                by_txid.insert(calculate_tx_id(tx), stacks.clone());
642            }
643        }
644    }
645    let mut nested = Vec::with_capacity(txs.len());
646    for (i, tx) in txs.iter().enumerate() {
647        if i == 0 || mempool_witnesses.is_none() {
648            nested.push(vec![Vec::new(); tx.inputs.len()]);
649            continue;
650        }
651        let stacks = by_txid.get(&calculate_tx_id(tx)).cloned().ok_or_else(|| {
652            crate::error::ConsensusError::BlockValidation(
653                "missing witness for selected transaction".into(),
654            )
655        })?;
656        nested.push(stacks);
657    }
658    Ok(nested)
659}
660
661/// Fees of selected (non-coinbase) txs.
662/// An input missing from the chain set is priced from an earlier selected transaction.
663/// Any other missing input contributes nothing.
664pub fn sum_selected_fees(utxo_set: &UtxoSet, txs: &[Transaction]) -> Integer {
665    use crate::block::calculate_tx_id;
666
667    let mut created: std::collections::HashMap<Hash, usize> = std::collections::HashMap::new();
668    let mut total = 0i64;
669    for (i, tx) in txs.iter().enumerate() {
670        if !crate::transaction::is_coinbase(tx) {
671            total = total.saturating_add(selected_tx_fee(tx, utxo_set, txs, &created));
672        }
673        created.insert(calculate_tx_id(tx), i);
674    }
675    total
676}
677
678fn selected_tx_fee(
679    tx: &Transaction,
680    utxo_set: &UtxoSet,
681    txs: &[Transaction],
682    created: &std::collections::HashMap<Hash, usize>,
683) -> Integer {
684    let mut total_input = 0i64;
685    for input in &tx.inputs {
686        let value = if let Some(utxo) = utxo_set.get(&input.prevout) {
687            utxo.value
688        } else if let Some(&parent_i) = created.get(&input.prevout.hash) {
689            let Some(output) = txs[parent_i].outputs.get(input.prevout.index as usize) else {
690                return 0;
691            };
692            output.value
693        } else {
694            return 0;
695        };
696        total_input = match total_input.checked_add(value) {
697            Some(sum) => sum,
698            None => return 0,
699        };
700    }
701    let total_output = tx
702        .outputs
703        .iter()
704        .try_fold(0i64, |acc, output| acc.checked_add(output.value))
705        .unwrap_or(0);
706    total_input.saturating_sub(total_output).max(0)
707}
708
709/// If payouts exceed subsidy+fees, error. Shortfall is added to the first output.
710pub fn fit_payouts_to_reward(
711    payouts: &[(Integer, ByteString)],
712    subsidy: Integer,
713    fees: Integer,
714) -> Result<Vec<(Integer, ByteString)>> {
715    if payouts.is_empty() {
716        return Err(crate::error::ConsensusError::InvalidProofOfWork(
717            "coinbase requires at least one output".into(),
718        ));
719    }
720    let max = subsidy.saturating_add(fees);
721    let sum: Integer = payouts
722        .iter()
723        .map(|(v, _)| *v)
724        .fold(0, |a, b| a.saturating_add(b));
725    if sum > max {
726        return Err(crate::error::ConsensusError::EconomicValidation(
727            format!("payouts {sum} exceed subsidy+fees {max}").into(),
728        ));
729    }
730    let mut out = payouts.to_vec();
731    if sum < max {
732        out[0].0 = out[0].0.saturating_add(max - sum);
733    }
734    Ok(out)
735}
736
737/// BIP141: coinbase wtxid is 0, so appending the OP_RETURN does not change the witness root.
738/// Empty stacks when `nested` is `None` (legacy). Prefer
739/// [`append_witness_commitment_from_nested`] when mempool witnesses are known.
740pub fn append_witness_commitment(
741    coinbase: &mut Transaction,
742    txs: &mut [Transaction],
743) -> Result<()> {
744    append_witness_commitment_from_nested(coinbase, txs, None)
745}
746
747/// Same as [`append_witness_commitment`], using per-tx per-input stacks.
748/// `nested[i]` is the witness for `txs[i]`. Coinbase wtxid stays 0.
749pub fn append_witness_commitment_from_nested(
750    coinbase: &mut Transaction,
751    txs: &mut [Transaction],
752    nested: Option<&[Vec<crate::segwit::Witness>]>,
753) -> Result<()> {
754    if txs.is_empty() {
755        return Ok(());
756    }
757    let owned: Vec<Vec<crate::segwit::Witness>>;
758    let nested = if let Some(n) = nested {
759        if n.len() < txs.len() {
760            return Err(crate::error::ConsensusError::BlockValidation(
761                "missing witness for selected transaction".into(),
762            ));
763        }
764        n
765    } else {
766        owned = txs
767            .iter()
768            .map(|tx| vec![Vec::new(); tx.inputs.len()])
769            .collect();
770        &owned
771    };
772    let header = BlockHeader {
773        version: 1,
774        prev_block_hash: [0u8; 32],
775        merkle_root: [0u8; 32],
776        timestamp: 0,
777        bits: 0,
778        nonce: 0,
779    };
780    let block = Block {
781        header,
782        transactions: txs.to_vec().into_boxed_slice(),
783    };
784    let root = crate::segwit::compute_witness_merkle_root_from_nested(&block, nested, None)?;
785    let script = crate::segwit::witness_commitment_script(&root, &[0u8; 32]);
786    let mut outs = coinbase.outputs.to_vec();
787    outs.push(TransactionOutput {
788        value: 0,
789        script_pubkey: script,
790    });
791    coinbase.outputs = outs.into();
792    txs[0] = coinbase.clone();
793    Ok(())
794}
795
796/// Calculate merkle root using proper Bitcoin Merkle tree construction
797#[track_caller] // Better error messages showing caller location
798#[cfg_attr(feature = "production", inline(always))]
799#[cfg_attr(not(feature = "production"), inline)]
800#[cfg_attr(feature = "spec-lock", spec_locked("8.4.1", "ComputeMerkleRoot"))]
801pub fn calculate_merkle_root(transactions: &[Transaction]) -> Result<Hash> {
802    if transactions.is_empty() {
803        return Err(crate::error::ConsensusError::InvalidProofOfWork(
804            "Cannot calculate merkle root for empty transaction list".into(),
805        ));
806    }
807
808    // Calculate transaction hashes with batch optimization (if available)
809    // Uses SIMD vectorization + proven bounds for optimal performance
810    // Use cache-aligned structures throughout merkle tree building
811    #[cfg(feature = "production")]
812    let mut hashes: Vec<crate::optimizations::CacheAlignedHash> = {
813        use crate::optimizations::simd_vectorization;
814
815        // Serialize all transactions in parallel (if rayon available)
816        // Then batch hash all serialized forms using double SHA256
817        // Pre-allocate serialization buffers
818        let serialized_txs: Vec<Vec<u8>> = {
819            #[cfg(feature = "rayon")]
820            {
821                use rayon::prelude::*;
822                transactions
823                    .par_iter()
824                    .map(serialize_tx_for_hash) // Uses prealloc_tx_buffer internally
825                    .collect()
826            }
827            #[cfg(not(feature = "rayon"))]
828            {
829                transactions
830                    .iter()
831                    .map(serialize_tx_for_hash) // Uses prealloc_tx_buffer internally
832                    .collect()
833            }
834        };
835
836        // Batch hash all serialized transactions using double SHA256
837        // Keep cache-aligned structures for better cache locality
838        let tx_data_refs: Vec<&[u8]> = serialized_txs.iter().map(|v| v.as_slice()).collect();
839        simd_vectorization::batch_double_sha256_aligned(&tx_data_refs)
840    };
841
842    #[cfg(not(feature = "production"))]
843    let mut hashes: Vec<Hash> = {
844        // Sequential fallback for non-production builds
845        let mut hashes = Vec::with_capacity(transactions.len());
846        for tx in transactions {
847            hashes.push(calculate_tx_hash(tx));
848        }
849        hashes
850    };
851
852    // Build Merkle tree bottom-up
853    // Pre-allocate next level and combined buffers
854    // Optimization: Process multiple tree levels in parallel where safe
855    // Use cache-aligned structures in production mode for better cache locality
856    #[cfg(feature = "production")]
857    {
858        use crate::optimizations::CacheAlignedHash;
859
860        let mut mutated = false;
861
862        while hashes.len() > 1 {
863            // CVE-2012-2459: Detect mutations (duplicate hashes at same level)
864            let mut level_mutated = false;
865            let mut pos = 0;
866            while pos + 1 < hashes.len() {
867                if hashes[pos].as_bytes() == hashes[pos + 1].as_bytes() {
868                    level_mutated = true;
869                }
870                pos += 2;
871            }
872            if level_mutated {
873                mutated = true;
874            }
875
876            // Duplicate last hash if odd number of hashes (Bitcoin's special rule)
877            if hashes.len() & 1 != 0 {
878                let last = hashes[hashes.len() - 1].clone();
879                hashes.push(last);
880            }
881
882            // Stack-allocated 64-byte buffer, double SHA256 at each level
883            let next_level: Vec<CacheAlignedHash> = hashes
884                .chunks(2)
885                .map(|chunk| {
886                    let mut combined = [0u8; 64];
887                    combined[..32].copy_from_slice(chunk[0].as_bytes());
888                    combined[32..].copy_from_slice(if chunk.len() == 2 {
889                        chunk[1].as_bytes()
890                    } else {
891                        chunk[0].as_bytes()
892                    });
893                    CacheAlignedHash::new(double_sha256_hash(&combined))
894                })
895                .collect();
896
897            hashes = next_level;
898        }
899
900        if mutated {
901            return Err(crate::error::ConsensusError::InvalidProofOfWork(
902                "Merkle root mutation detected (CVE-2012-2459)".into(),
903            ));
904        }
905
906        Ok(*hashes[0].as_bytes())
907    }
908
909    #[cfg(not(feature = "production"))]
910    {
911        let (root, mutated) = merkle_tree_from_hashes(&mut hashes)?;
912        if mutated {
913            return Err(crate::error::ConsensusError::InvalidProofOfWork(
914                "Merkle root mutation detected (CVE-2012-2459)".into(),
915            ));
916        }
917        Ok(root)
918    }
919}
920
921/// Build merkle tree from pre-computed leaf hashes.
922///
923/// Also usable directly when tx_ids are already computed, avoiding
924/// redundant serialization and hashing.
925/// Implements ComputeMerkleRoot (Orange Paper 8.4.1).
926///
927/// Returns `Err` if `tx_ids` is empty **or** if a CVE-2012-2459 mutation is detected
928/// (duplicate adjacent hashes at any merkle level). Use [`compute_merkle_root_and_mutated`]
929/// when you need the root regardless of mutation (Orange Paper §8.4.1 merkle root).
930#[cfg_attr(feature = "spec-lock", spec_locked("8.4.1", "ComputeMerkleRoot"))]
931pub fn calculate_merkle_root_from_tx_ids(tx_ids: &[Hash]) -> Result<Hash> {
932    let (root, mutated) = compute_merkle_root_and_mutated(tx_ids)?;
933    if mutated {
934        return Err(crate::error::ConsensusError::InvalidProofOfWork(
935            "Merkle root mutation detected (CVE-2012-2459)".into(),
936        ));
937    }
938    Ok(root)
939}
940
941/// Compute merkle root **and** CVE-2012-2459 mutation flag without aborting on mutation.
942/// Computes merkle root from leaves with optional mutation detection — always returns a root.
943/// Callers decide policy: `CheckBlock` rejects mutated blocks; `ConnectBlock` may skip
944/// the check for already-accepted blocks.
945#[cfg_attr(feature = "spec-lock", spec_locked("8.4.1", "ComputeMerkleRoot"))]
946pub fn compute_merkle_root_and_mutated(tx_ids: &[Hash]) -> Result<(Hash, bool)> {
947    if tx_ids.is_empty() {
948        return Err(crate::error::ConsensusError::InvalidProofOfWork(
949            "Cannot calculate merkle root for empty transaction list".into(),
950        ));
951    }
952    let mut hashes = tx_ids.to_vec();
953    merkle_tree_from_hashes(&mut hashes)
954}
955
956/// Merkle tree building logic. Uses double SHA256 at each level (Bitcoin standard).
957/// Stack-allocates the 64-byte pair buffer to avoid heap allocation per node.
958/// Orange Paper 8.4.1: ComputeMerkleRoot pair-and-hash construction.
959/// Returns `(root, mutated)` — caller decides policy on mutation.
960#[cfg_attr(feature = "spec-lock", spec_locked("8.4.1", "ComputeMerkleRoot"))]
961fn merkle_tree_from_hashes(hashes: &mut Vec<Hash>) -> Result<(Hash, bool)> {
962    let mut mutated = false;
963
964    while hashes.len() > 1 {
965        // CVE-2012-2459: Detect mutations (duplicate hashes at same level)
966        for pos in (0..hashes.len().saturating_sub(1)).step_by(2) {
967            if hashes[pos] == hashes[pos + 1] {
968                mutated = true;
969            }
970        }
971
972        // Duplicate last hash if odd number of hashes (Bitcoin's special rule)
973        if hashes.len() & 1 != 0 {
974            hashes.push(hashes[hashes.len() - 1]);
975        }
976
977        let mut next_level = Vec::with_capacity(hashes.len() / 2);
978
979        // Stack-allocated 64-byte buffer for combining hash pairs
980        for chunk in hashes.chunks(2) {
981            let mut combined = [0u8; 64];
982            combined[..32].copy_from_slice(&chunk[0]);
983            combined[32..].copy_from_slice(if chunk.len() == 2 {
984                &chunk[1]
985            } else {
986                &chunk[0]
987            });
988            next_level.push(double_sha256_hash(&combined));
989        }
990
991        *hashes = next_level;
992    }
993
994    Ok((hashes[0], mutated))
995}
996
997/// Serialize transaction for hashing (used for batch hashing optimization)
998///
999/// This is the same serialization as calculate_tx_hash but returns the serialized bytes
1000/// instead of hashing them, allowing batch hashing to be applied.
1001fn serialize_tx_for_hash(tx: &Transaction) -> Vec<u8> {
1002    // Pre-allocate buffer using proven maximum size
1003    #[cfg(feature = "production")]
1004    let mut data = {
1005        use crate::optimizations::prealloc_tx_buffer;
1006        prealloc_tx_buffer()
1007    };
1008
1009    #[cfg(not(feature = "production"))]
1010    let mut data = Vec::new();
1011
1012    // Version (4 bytes, little-endian)
1013    data.extend_from_slice(&(tx.version as u32).to_le_bytes());
1014
1015    // Input count (varint)
1016    data.extend_from_slice(&encode_varint(tx.inputs.len() as u64));
1017
1018    // Inputs
1019    for input in &tx.inputs {
1020        // Previous output hash (32 bytes)
1021        data.extend_from_slice(&input.prevout.hash);
1022        // Previous output index (4 bytes, little-endian)
1023        data.extend_from_slice(&input.prevout.index.to_le_bytes());
1024        // Script length (varint)
1025        data.extend_from_slice(&encode_varint(input.script_sig.len() as u64));
1026        // Script
1027        data.extend_from_slice(&input.script_sig);
1028        // Sequence (4 bytes, little-endian)
1029        data.extend_from_slice(&(input.sequence as u32).to_le_bytes());
1030    }
1031
1032    // Output count (varint)
1033    data.extend_from_slice(&encode_varint(tx.outputs.len() as u64));
1034
1035    // Outputs
1036    for output in &tx.outputs {
1037        // Value (8 bytes, little-endian)
1038        data.extend_from_slice(&(output.value as u64).to_le_bytes());
1039        // Script length (varint)
1040        data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
1041        // Script
1042        data.extend_from_slice(&output.script_pubkey);
1043    }
1044
1045    // Lock time (4 bytes, little-endian)
1046    data.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
1047
1048    data
1049}
1050
1051/// Calculate transaction hash using proper Bitcoin serialization
1052///
1053/// This function computes the double SHA256 hash of the serialized transaction.
1054/// For batch operations, use serialize_tx_for_hash + batch_double_sha256 instead.
1055#[allow(dead_code)] // Used in tests
1056fn calculate_tx_hash(tx: &Transaction) -> Hash {
1057    let data = serialize_tx_for_hash(tx);
1058    // Double SHA256 (Bitcoin standard)
1059    let hash1 = sha256_hash(&data);
1060    sha256_hash(&hash1)
1061}
1062
1063/// Encode a number as a Bitcoin varint
1064fn encode_varint(value: u64) -> Vec<u8> {
1065    if value < 0xfd {
1066        vec![value as u8]
1067    } else if value <= 0xffff {
1068        let mut result = vec![0xfd];
1069        result.extend_from_slice(&(value as u16).to_le_bytes());
1070        result
1071    } else if value <= 0xffffffff {
1072        let mut result = vec![0xfe];
1073        result.extend_from_slice(&(value as u32).to_le_bytes());
1074        result
1075    } else {
1076        let mut result = vec![0xff];
1077        result.extend_from_slice(&value.to_le_bytes());
1078        result
1079    }
1080}
1081
1082/// Orange Paper 7.2: Block hash = SHA256d(serialize(header)) — same as blockstore / PoW.
1083fn calculate_block_hash(header: &BlockHeader) -> Hash {
1084    let wire = crate::serialization::block::serialize_block_header(header);
1085    double_sha256_hash(&wire)
1086}
1087
1088/// Simple SHA256 hash function
1089///
1090/// Performance optimization: Uses OptimizedSha256 (SHA-NI or AVX2) instead of sha2 crate
1091/// for faster hashing in Merkle tree construction.
1092#[inline(always)]
1093fn sha256_hash(data: &[u8]) -> Hash {
1094    use crate::crypto::OptimizedSha256;
1095    OptimizedSha256::new().hash(data)
1096}
1097
1098/// Double SHA256 hash (Bitcoin standard for merkle tree nodes and txids)
1099#[inline(always)]
1100fn double_sha256_hash(data: &[u8]) -> Hash {
1101    use crate::crypto::OptimizedSha256;
1102    OptimizedSha256::new().hash256(data)
1103}
1104
1105/// Wall-clock Unix timestamp for block template creation.
1106fn get_current_timestamp() -> Natural {
1107    std::time::SystemTime::now()
1108        .duration_since(std::time::UNIX_EPOCH)
1109        .unwrap_or(std::time::Duration::ZERO)
1110        .as_secs()
1111}
1112
1113#[cfg(test)]
1114mod tests {
1115    use super::*;
1116    use crate::opcodes::*;
1117
1118    #[test]
1119    fn test_create_new_block() {
1120        let mut utxo_set = UtxoSet::default();
1121        // Add UTXO for the transaction input
1122        let outpoint = OutPoint {
1123            hash: [1; 32],
1124            index: 0,
1125        };
1126        let utxo = UTXO {
1127            value: 10000,
1128            // Empty script_pubkey - script_sig (OP_1) will push 1, final stack [1] passes
1129            script_pubkey: vec![].into(),
1130            height: 0,
1131            is_coinbase: false,
1132        };
1133        utxo_set.insert(outpoint, std::sync::Arc::new(utxo));
1134
1135        let mempool_txs = vec![create_valid_transaction()];
1136        let height = 100;
1137        let prev_header = create_valid_block_header();
1138        // Create headers with different timestamps to ensure valid difficulty adjustment
1139        let mut prev_header2 = prev_header.clone();
1140        prev_header2.timestamp = prev_header.timestamp + 600; // 10 minutes later
1141        let prev_headers = vec![prev_header.clone(), prev_header2];
1142        let coinbase_script = vec![OP_1];
1143        let coinbase_address = vec![OP_1];
1144
1145        // get_next_work_required can fail in some cases (e.g., invalid target expansion)
1146        // Handle errors gracefully like test_create_block_template_comprehensive does
1147        let block_time = 1_700_000_000u64;
1148        let result = create_new_block_with_time(
1149            &utxo_set,
1150            &mempool_txs,
1151            height,
1152            &prev_header,
1153            &prev_headers,
1154            &coinbase_script,
1155            &coinbase_address,
1156            block_time,
1157            Network::Mainnet,
1158            None,
1159        );
1160
1161        if let Ok(block) = result {
1162            assert_eq!(block.transactions.len(), 2); // coinbase + 1 mempool tx
1163            assert!(is_coinbase(&block.transactions[0]));
1164            assert_eq!(block.header.version, 1);
1165            assert_eq!(block.header.timestamp, block_time);
1166        } else {
1167            // Accept that it might fail due to target expansion or other validation issues
1168            // This can happen when get_next_work_required returns an error
1169            assert!(result.is_err());
1170        }
1171    }
1172
1173    #[test]
1174    fn test_mine_block_success() {
1175        let block = create_test_block();
1176        let result = mine_block(block, 1000);
1177
1178        // Should succeed now that we fixed the target expansion
1179        assert!(result.is_ok());
1180        let (mined_block, mining_result) = result.unwrap();
1181        assert!(matches!(
1182            mining_result,
1183            MiningResult::Success | MiningResult::Failure
1184        ));
1185        assert_eq!(mined_block.header.version, 1);
1186    }
1187
1188    #[test]
1189    fn test_create_block_template() {
1190        let utxo_set = UtxoSet::default();
1191        let mempool_txs = vec![create_valid_transaction()];
1192        let height = 100;
1193        let prev_header = create_valid_block_header();
1194        let prev_headers = vec![prev_header.clone()];
1195        let coinbase_script = vec![OP_1];
1196        let coinbase_address = vec![OP_1];
1197
1198        // Height 100 is not a retarget boundary, so the template copies the parent bits.
1199        let template = create_block_template(
1200            &utxo_set,
1201            &mempool_txs,
1202            height,
1203            &prev_header,
1204            &prev_headers,
1205            &coinbase_script,
1206            &coinbase_address,
1207            Network::Mainnet,
1208            None,
1209        )
1210        .expect("off-boundary template");
1211        assert_eq!(template.header.bits, prev_header.bits);
1212        assert_eq!(template.height, height);
1213    }
1214
1215    #[test]
1216    fn test_coinbase_transaction() {
1217        let height = 100;
1218        let subsidy = get_block_subsidy(height);
1219        let script = vec![OP_1];
1220        let address = vec![OP_1];
1221
1222        let coinbase_tx = create_coinbase_transaction(height, subsidy, &script, &address).unwrap();
1223
1224        assert!(is_coinbase(&coinbase_tx));
1225        assert_eq!(coinbase_tx.outputs[0].value, subsidy);
1226        assert_eq!(coinbase_tx.inputs[0].prevout.hash, [0u8; 32]);
1227        assert_eq!(coinbase_tx.inputs[0].prevout.index, 0xffffffff);
1228    }
1229
1230    #[test]
1231    fn test_merkle_root_calculation() {
1232        let txs = vec![create_valid_transaction(), create_valid_transaction()];
1233
1234        let merkle_root = calculate_merkle_root(&txs).unwrap();
1235        assert_ne!(merkle_root, [0u8; 32]);
1236    }
1237
1238    #[test]
1239    fn test_merkle_root_empty() {
1240        let txs = vec![];
1241        let result = calculate_merkle_root(&txs);
1242        assert!(result.is_err());
1243    }
1244
1245    // ============================================================================
1246    // COMPREHENSIVE MINING TESTS
1247    // ============================================================================
1248
1249    #[test]
1250    fn test_create_block_template_comprehensive() {
1251        let mut utxo_set = UtxoSet::default();
1252        // Add UTXO for the transaction input
1253        let outpoint = OutPoint {
1254            hash: [1; 32],
1255            index: 0,
1256        };
1257        let utxo = UTXO {
1258            value: 10000,
1259            // Empty script_pubkey - script_sig (OP_1) will push 1, final stack [1] passes
1260            script_pubkey: vec![].into(),
1261            height: 0,
1262            is_coinbase: false,
1263        };
1264        utxo_set.insert(outpoint, std::sync::Arc::new(utxo));
1265
1266        let mempool_txs = vec![create_valid_transaction()];
1267        let height = 100;
1268        let prev_header = create_valid_block_header();
1269        // Create headers with different timestamps to ensure valid difficulty adjustment
1270        let mut prev_header2 = prev_header.clone();
1271        prev_header2.timestamp = prev_header.timestamp + 600; // 10 minutes later
1272        let prev_headers = vec![prev_header.clone(), prev_header2];
1273        let coinbase_script = vec![OP_1];
1274        let coinbase_address = vec![OP_2];
1275
1276        let result = create_block_template(
1277            &utxo_set,
1278            &mempool_txs,
1279            height,
1280            &prev_header,
1281            &prev_headers,
1282            &coinbase_script,
1283            &coinbase_address,
1284            Network::Mainnet,
1285            None,
1286        );
1287
1288        // If get_next_work_required returns a target that's too large, this will fail
1289        // That's ok for testing the error path
1290        if let Ok(template) = result {
1291            assert_eq!(template.height, height);
1292            assert!(!template.target.is_zero());
1293            assert!(is_coinbase(&template.coinbase_tx));
1294            assert_eq!(template.transactions.len(), 1);
1295        } else {
1296            // Accept that it might fail due to target expansion or other validation issues
1297            assert!(result.is_err());
1298        }
1299    }
1300
1301    #[test]
1302    fn test_mine_block_attempts() {
1303        let block = create_test_block();
1304        let (mined_block, result) = mine_block(block, 1000).unwrap();
1305
1306        // Result depends on whether we found a valid nonce
1307        assert!(matches!(
1308            result,
1309            MiningResult::Success | MiningResult::Failure
1310        ));
1311        assert_eq!(mined_block.header.version, 1);
1312    }
1313
1314    #[test]
1315    fn test_mine_block_failure() {
1316        let block = create_test_block();
1317        let (mined_block, result) = mine_block(block, 0).unwrap();
1318
1319        // With 0 attempts, should always fail
1320        assert_eq!(result, MiningResult::Failure);
1321        assert_eq!(mined_block.header.nonce, 0);
1322    }
1323
1324    #[test]
1325    fn test_create_coinbase_transaction() {
1326        let height = 100;
1327        let subsidy = 5000000000;
1328        let script = vec![0x51, 0x52];
1329        let address = vec![0x53, 0x54];
1330
1331        let coinbase_tx = create_coinbase_transaction(height, subsidy, &script, &address).unwrap();
1332
1333        assert!(is_coinbase(&coinbase_tx));
1334        assert_eq!(coinbase_tx.outputs.len(), 1);
1335        assert_eq!(coinbase_tx.outputs[0].value, subsidy);
1336        assert_eq!(coinbase_tx.outputs[0].script_pubkey, address);
1337        assert_eq!(coinbase_tx.inputs[0].script_sig, script);
1338        assert_eq!(coinbase_tx.inputs[0].prevout.hash, [0u8; 32]);
1339        assert_eq!(coinbase_tx.inputs[0].prevout.index, 0xffffffff);
1340    }
1341
1342    #[test]
1343    fn test_create_coinbase_with_outputs_preserves_order() {
1344        let script = vec![OP_1];
1345        let a = vec![OP_1];
1346        let b = vec![OP_2];
1347        let tx = create_coinbase_with_outputs(100, &script, &[(10, a.clone()), (20, b.clone())])
1348            .unwrap();
1349        assert!(is_coinbase(&tx));
1350        assert_eq!(tx.outputs.len(), 2);
1351        assert_eq!(tx.outputs[0].value, 10);
1352        assert_eq!(tx.outputs[0].script_pubkey, a);
1353        assert_eq!(tx.outputs[1].value, 20);
1354        assert_eq!(tx.outputs[1].script_pubkey, b);
1355    }
1356
1357    #[test]
1358    fn test_create_coinbase_with_outputs_empty_is_error() {
1359        let script = vec![OP_1];
1360        assert!(create_coinbase_with_outputs(1, &script, &[]).is_err());
1361    }
1362
1363    #[test]
1364    fn fit_payouts_tops_up_first_with_fees() {
1365        let a = vec![OP_1];
1366        let b = vec![OP_2];
1367        let fitted = fit_payouts_to_reward(&[(10, a.clone()), (20, b.clone())], 100, 5).unwrap();
1368        assert_eq!(fitted[0].0, 85);
1369        assert_eq!(fitted[1].0, 20);
1370        assert!(fit_payouts_to_reward(&[(90, a)], 50, 0).is_err());
1371    }
1372
1373    #[test]
1374    fn witness_commitment_script_roundtrips_extract() {
1375        let root = [7u8; 32];
1376        let nonce = [0u8; 32];
1377        let script = crate::segwit::witness_commitment_script(&root, &nonce);
1378        let got = crate::segwit::extract_witness_commitment(&script);
1379        assert!(got.is_some());
1380        assert_eq!(script[0], 0x6a);
1381        assert_eq!(script[1], 0x24);
1382        assert_eq!(&script[2..6], &[0xaa, 0x21, 0xa9, 0xed]);
1383    }
1384
1385    #[test]
1386    fn append_witness_commitment_adds_zero_value_op_return() {
1387        let script = vec![OP_1];
1388        let mut cb = create_coinbase_with_outputs(1, &script, &[(50, vec![OP_1])]).unwrap();
1389        let mut txs = vec![cb.clone()];
1390        append_witness_commitment(&mut cb, &mut txs).unwrap();
1391        assert_eq!(cb.outputs.len(), 2);
1392        assert_eq!(cb.outputs[1].value, 0);
1393        assert_eq!(cb.outputs[1].script_pubkey[0], 0x6a);
1394        assert!(crate::economic::check_coinbase_subsidy(&cb, 50, 0));
1395    }
1396
1397    #[test]
1398    fn append_from_nested_differs_from_empty_stacks() {
1399        let script = vec![OP_1];
1400        let cb = create_coinbase_with_outputs(1, &script, &[(50, vec![OP_1])]).unwrap();
1401        let spend = Transaction {
1402            version: 2,
1403            inputs: crate::tx_inputs![TransactionInput {
1404                prevout: OutPoint {
1405                    hash: [0x11u8; 32],
1406                    index: 0,
1407                },
1408                script_sig: vec![],
1409                sequence: 0xfffffffe,
1410            }],
1411            outputs: crate::tx_outputs![TransactionOutput {
1412                value: 90_000,
1413                script_pubkey: vec![OP_1],
1414            }],
1415            lock_time: 0,
1416        };
1417        let mut empty_cb = cb.clone();
1418        let mut empty_txs = vec![cb.clone(), spend.clone()];
1419        append_witness_commitment(&mut empty_cb, &mut empty_txs).unwrap();
1420
1421        let nested = vec![vec![Vec::new()], vec![vec![vec![OP_1]]]];
1422        let mut real_cb = cb.clone();
1423        let mut real_txs = vec![cb, spend];
1424        append_witness_commitment_from_nested(&mut real_cb, &mut real_txs, Some(&nested)).unwrap();
1425
1426        assert_ne!(
1427            empty_cb.outputs.last().unwrap().script_pubkey,
1428            real_cb.outputs.last().unwrap().script_pubkey,
1429            "mempool witness must change the commitment"
1430        );
1431        let header = BlockHeader {
1432            version: 1,
1433            prev_block_hash: [0u8; 32],
1434            merkle_root: [0u8; 32],
1435            timestamp: 0,
1436            bits: 0,
1437            nonce: 0,
1438        };
1439        let block = Block {
1440            header,
1441            transactions: real_txs.clone().into_boxed_slice(),
1442        };
1443        let root =
1444            crate::segwit::compute_witness_merkle_root_from_nested(&block, &nested, None).unwrap();
1445        let expect = crate::segwit::witness_commitment_script(&root, &[0u8; 32]);
1446        assert_eq!(real_cb.outputs.last().unwrap().script_pubkey, expect);
1447    }
1448
1449    #[test]
1450    fn missing_witness_does_not_commit_empty_stacks() {
1451        let script = vec![OP_1];
1452        let mut cb = create_coinbase_with_outputs(1, &script, &[(50, vec![OP_1])]).unwrap();
1453        let spend = Transaction {
1454            version: 2,
1455            inputs: crate::tx_inputs![TransactionInput {
1456                prevout: OutPoint {
1457                    hash: [0x11u8; 32],
1458                    index: 0,
1459                },
1460                script_sig: vec![],
1461                sequence: 0xfffffffe,
1462            }],
1463            outputs: crate::tx_outputs![TransactionOutput {
1464                value: 90_000,
1465                script_pubkey: vec![OP_1],
1466            }],
1467            lock_time: 0,
1468        };
1469        let mut txs = vec![cb.clone(), spend.clone()];
1470        let short = vec![vec![Vec::new()]];
1471        assert!(append_witness_commitment_from_nested(&mut cb, &mut txs, Some(&short)).is_err());
1472        assert_eq!(cb.outputs.len(), 1);
1473
1474        let txs = vec![cb, spend.clone()];
1475        assert!(nested_witnesses_for_selected(&txs, &[spend], Some(&[None])).is_err());
1476    }
1477
1478    #[test]
1479    fn test_calculate_tx_hash() {
1480        let tx = create_valid_transaction();
1481        let hash = calculate_tx_hash(&tx);
1482
1483        // Should be a 32-byte hash
1484        assert_eq!(hash.len(), 32);
1485
1486        // Same transaction should produce same hash
1487        let hash2 = calculate_tx_hash(&tx);
1488        assert_eq!(hash, hash2);
1489    }
1490
1491    #[test]
1492    fn test_calculate_tx_hash_different_txs() {
1493        let tx1 = create_valid_transaction();
1494        let mut tx2 = tx1.clone();
1495        tx2.version = 2; // Different version
1496
1497        let hash1 = calculate_tx_hash(&tx1);
1498        let hash2 = calculate_tx_hash(&tx2);
1499
1500        // Different transactions should produce different hashes
1501        assert_ne!(hash1, hash2);
1502    }
1503
1504    #[test]
1505    fn test_encode_varint_small() {
1506        let encoded = encode_varint(0x42);
1507        assert_eq!(encoded, vec![0x42]);
1508    }
1509
1510    #[test]
1511    fn test_encode_varint_medium() {
1512        let encoded = encode_varint(0x1234);
1513        assert_eq!(encoded.len(), 3);
1514        assert_eq!(encoded[0], 0xfd);
1515    }
1516
1517    #[test]
1518    fn test_encode_varint_large() {
1519        let encoded = encode_varint(0x12345678);
1520        assert_eq!(encoded.len(), 5);
1521        assert_eq!(encoded[0], 0xfe);
1522    }
1523
1524    #[test]
1525    fn test_encode_varint_huge() {
1526        let encoded = encode_varint(0x123456789abcdef0);
1527        assert_eq!(encoded.len(), 9);
1528        assert_eq!(encoded[0], 0xff);
1529    }
1530
1531    #[test]
1532    fn test_calculate_block_hash() {
1533        let header = create_valid_block_header();
1534        let hash = calculate_block_hash(&header);
1535
1536        // Should be a 32-byte hash
1537        assert_eq!(hash.len(), 32);
1538
1539        // Same header should produce same hash
1540        let hash2 = calculate_block_hash(&header);
1541        assert_eq!(hash, hash2);
1542    }
1543
1544    #[test]
1545    fn test_calculate_block_hash_different_headers() {
1546        let header1 = create_valid_block_header();
1547        let mut header2 = header1.clone();
1548        header2.version = 2; // Different version
1549
1550        let hash1 = calculate_block_hash(&header1);
1551        let hash2 = calculate_block_hash(&header2);
1552
1553        // Different headers should produce different hashes
1554        assert_ne!(hash1, hash2);
1555    }
1556
1557    #[test]
1558    fn test_sha256_hash() {
1559        let data = b"hello world";
1560        let hash = sha256_hash(data);
1561
1562        // Should be a 32-byte hash
1563        assert_eq!(hash.len(), 32);
1564
1565        // Same data should produce same hash
1566        let hash2 = sha256_hash(data);
1567        assert_eq!(hash, hash2);
1568    }
1569
1570    #[test]
1571    fn test_sha256_hash_different_data() {
1572        let data1 = b"hello";
1573        let data2 = b"world";
1574
1575        let hash1 = sha256_hash(data1);
1576        let hash2 = sha256_hash(data2);
1577
1578        // Different data should produce different hashes
1579        assert_ne!(hash1, hash2);
1580    }
1581
1582    #[test]
1583    fn test_pow_expand_target_regtest_minimum_difficulty() {
1584        let target = crate::pow::expand_target(0x207fffff).expect("regtest nBits");
1585        assert!(!target.is_zero());
1586        assert_eq!(target.gbt_target_hex().len(), 64);
1587    }
1588
1589    #[test]
1590    fn test_create_block_template_regtest_nbits() {
1591        let utxo_set = UtxoSet::default();
1592        let prev_header = BlockHeader {
1593            version: 4,
1594            prev_block_hash: [0u8; 32],
1595            merkle_root: [0u8; 32],
1596            timestamp: 1_600_000_000,
1597            bits: 0x207fffff,
1598            nonce: 0,
1599        };
1600        let prev_headers = vec![prev_header.clone(), prev_header.clone()];
1601
1602        let template = create_block_template(
1603            &utxo_set,
1604            &[],
1605            1,
1606            &prev_header,
1607            &prev_headers,
1608            &vec![],
1609            &vec![0x51],
1610            Network::Regtest,
1611            None,
1612        )
1613        .expect("create_block_template must not fail on regtest nBits");
1614
1615        assert_eq!(template.header.bits, 0x207fffff);
1616        let expanded = crate::pow::expand_target(0x207fffff).unwrap();
1617        assert_eq!(template.target, expanded);
1618        assert_ne!(template.target, crate::pow::U256::one());
1619    }
1620
1621    #[test]
1622    fn test_get_current_timestamp() {
1623        let timestamp = get_current_timestamp();
1624        // Must be a plausible post-genesis wall-clock value, not a hardcoded constant.
1625        assert!(timestamp > 1_231_006_505);
1626        assert!(timestamp < 4_000_000_000);
1627    }
1628
1629    #[test]
1630    fn test_merkle_root_single_transaction() {
1631        let txs = vec![create_valid_transaction()];
1632        let merkle_root = calculate_merkle_root(&txs).unwrap();
1633
1634        // Should be a 32-byte hash
1635        assert_eq!(merkle_root.len(), 32);
1636        assert_ne!(merkle_root, [0u8; 32]);
1637    }
1638
1639    #[test]
1640    fn test_merkle_root_three_transactions() {
1641        let txs = vec![
1642            create_valid_transaction(),
1643            create_valid_transaction(),
1644            create_valid_transaction(),
1645        ];
1646        let merkle_root = calculate_merkle_root(&txs).unwrap();
1647
1648        // Should be a 32-byte hash
1649        assert_eq!(merkle_root.len(), 32);
1650        assert_ne!(merkle_root, [0u8; 32]);
1651    }
1652
1653    #[test]
1654    fn test_merkle_root_five_transactions() {
1655        let txs = vec![
1656            create_valid_transaction(),
1657            create_valid_transaction(),
1658            create_valid_transaction(),
1659            create_valid_transaction(),
1660            create_valid_transaction(),
1661        ];
1662        let merkle_root = calculate_merkle_root(&txs).unwrap();
1663
1664        // Should be a 32-byte hash
1665        assert_eq!(merkle_root.len(), 32);
1666        assert_ne!(merkle_root, [0u8; 32]);
1667    }
1668
1669    #[test]
1670    fn test_block_template_fields() {
1671        let mut utxo_set = UtxoSet::default();
1672        // Add UTXO for the transaction input
1673        let outpoint = OutPoint {
1674            hash: [1; 32],
1675            index: 0,
1676        };
1677        let utxo = UTXO {
1678            value: 10000,
1679            // Empty script_pubkey - script_sig (OP_1) will push 1, final stack [1] passes
1680            script_pubkey: vec![].into(),
1681            height: 0,
1682            is_coinbase: false,
1683        };
1684        utxo_set.insert(outpoint, std::sync::Arc::new(utxo));
1685
1686        let mempool_txs = vec![create_valid_transaction()];
1687        let height = 100;
1688        let prev_header = create_valid_block_header();
1689        let prev_headers = vec![prev_header.clone(), prev_header.clone()];
1690        let coinbase_script = vec![OP_1];
1691        let coinbase_address = vec![OP_2];
1692
1693        let result = create_block_template(
1694            &utxo_set,
1695            &mempool_txs,
1696            height,
1697            &prev_header,
1698            &prev_headers,
1699            &coinbase_script,
1700            &coinbase_address,
1701            Network::Mainnet,
1702            None,
1703        );
1704
1705        // If get_next_work_required returns a target that's too large, this will fail
1706        // That's ok for testing the error path
1707        if let Ok(template) = result {
1708            // Test all fields
1709            assert_eq!(template.height, height);
1710            assert!(!template.target.is_zero());
1711            assert!(template.timestamp > 0);
1712            assert!(is_coinbase(&template.coinbase_tx));
1713            assert_eq!(template.transactions.len(), 1);
1714            assert_eq!(template.header.version, 1);
1715        } else {
1716            // Accept that it might fail due to target expansion
1717            assert!(result.is_err());
1718        }
1719    }
1720
1721    // Helper functions for tests
1722    #[test]
1723    fn a_child_of_a_selected_parent_is_in_the_template() {
1724        use crate::block::calculate_tx_id;
1725
1726        let funding = OutPoint {
1727            hash: [4; 32],
1728            index: 0,
1729        };
1730        let mut utxo_set = UtxoSet::default();
1731        utxo_set.insert(
1732            funding,
1733            std::sync::Arc::new(UTXO {
1734                value: 50_000,
1735                script_pubkey: vec![].into(),
1736                height: 0,
1737                is_coinbase: false,
1738            }),
1739        );
1740        let parent = Transaction {
1741            version: 1,
1742            inputs: vec![TransactionInput {
1743                prevout: funding,
1744                script_sig: vec![OP_1],
1745                sequence: 0xffffffff,
1746            }]
1747            .into(),
1748            outputs: vec![TransactionOutput {
1749                value: 40_000,
1750                script_pubkey: vec![],
1751            }]
1752            .into(),
1753            lock_time: 0,
1754        };
1755        let child = Transaction {
1756            version: 1,
1757            inputs: vec![TransactionInput {
1758                prevout: OutPoint {
1759                    hash: calculate_tx_id(&parent),
1760                    index: 0,
1761                },
1762                script_sig: vec![OP_1],
1763                sequence: 0xffffffff,
1764            }]
1765            .into(),
1766            outputs: vec![TransactionOutput {
1767                value: 25_000,
1768                script_pubkey: vec![],
1769            }]
1770            .into(),
1771            lock_time: 0,
1772        };
1773        let height = 100;
1774        let prev_header = create_valid_block_header();
1775        let mut prev_header2 = prev_header.clone();
1776        prev_header2.timestamp = prev_header.timestamp + 600;
1777        let tmpl = create_block_template_with_outputs(
1778            &utxo_set,
1779            &[parent, child.clone()],
1780            height,
1781            &prev_header,
1782            &[prev_header.clone(), prev_header2],
1783            &vec![OP_1],
1784            &[(0, vec![OP_1])],
1785            Network::Regtest,
1786            None,
1787        )
1788        .unwrap();
1789        assert!(
1790            tmpl.transactions
1791                .iter()
1792                .any(|tx| calculate_tx_id(tx) == calculate_tx_id(&child)),
1793            "child must be selected with its parent"
1794        );
1795        let subsidy = get_block_subsidy_for_network(height, Network::Regtest);
1796        assert_eq!(tmpl.coinbase_tx.outputs[0].value, subsidy + 25_000);
1797    }
1798
1799    fn create_valid_transaction() -> Transaction {
1800        // Use thread-local counter to avoid non-determinism across tests
1801        use std::cell::Cell;
1802        thread_local! {
1803            static COUNTER: Cell<u64> = const { Cell::new(0) };
1804        }
1805        let counter = COUNTER.with(|c| {
1806            let val = c.get();
1807            c.set(val + 1);
1808            val
1809        });
1810
1811        Transaction {
1812            version: 1,
1813            inputs: vec![TransactionInput {
1814                prevout: OutPoint {
1815                    hash: [1; 32], // Keep consistent hash for UTXO matching
1816                    index: 0,
1817                },
1818                // Use OP_1 in script_sig to push 1, script_pubkey will be OP_1 which also pushes 1
1819                // But wait, that gives [1, 1] which doesn't pass (needs exactly one value)
1820                // Try: OP_1 script_sig + empty script_pubkey, or empty script_sig + OP_1 script_pubkey
1821                // Actually, let's use OP_1 in script_sig and empty script_pubkey
1822                // Make script_sig unique by adding counter as extra data (OP_PUSHDATA + counter bytes)
1823                // This ensures transaction hash is unique without affecting script execution
1824                script_sig: {
1825                    let mut sig = vec![OP_1]; // OP_1 pushes 1
1826                    // Add counter as extra push data (will be on stack but script_pubkey is empty, so it doesn't matter)
1827                    if counter > 0 {
1828                        sig.push(PUSH_1_BYTE); // Push 1 byte
1829                        sig.push((counter & 0xff) as u8); // Push counter byte
1830                    }
1831                    sig
1832                },
1833                sequence: 0xffffffff,
1834            }]
1835            .into(),
1836            outputs: vec![TransactionOutput {
1837                value: 1000 + counter as i64, // Make each transaction unique
1838                // Empty script_pubkey - script_sig already pushed 1, so final stack is [1].into()
1839                script_pubkey: vec![],
1840            }]
1841            .into(),
1842            lock_time: 0,
1843        }
1844    }
1845
1846    fn create_valid_block_header() -> BlockHeader {
1847        BlockHeader {
1848            version: 1,
1849            prev_block_hash: [0; 32],
1850            merkle_root: [0; 32],
1851            timestamp: 1231006505,
1852            bits: 0x0600ffff, // Safe target - exponent 6
1853            nonce: 0,
1854        }
1855    }
1856
1857    fn create_test_block() -> Block {
1858        Block {
1859            header: create_valid_block_header(),
1860            transactions: vec![create_valid_transaction()].into_boxed_slice(),
1861        }
1862    }
1863
1864    #[test]
1865    fn test_create_coinbase_transaction_zero_subsidy() {
1866        let height = 100;
1867        let subsidy = 0; // Zero subsidy
1868        let script = vec![OP_1];
1869        let address = vec![OP_1];
1870
1871        let coinbase_tx = create_coinbase_transaction(height, subsidy, &script, &address).unwrap();
1872
1873        assert!(is_coinbase(&coinbase_tx));
1874        assert_eq!(coinbase_tx.outputs[0].value, 0);
1875    }
1876
1877    #[test]
1878    fn test_create_coinbase_transaction_large_subsidy() {
1879        let height = 100;
1880        let subsidy = 2100000000000000; // Large subsidy
1881        let script = vec![OP_1];
1882        let address = vec![OP_1];
1883
1884        let coinbase_tx = create_coinbase_transaction(height, subsidy, &script, &address).unwrap();
1885
1886        assert!(is_coinbase(&coinbase_tx));
1887        assert_eq!(coinbase_tx.outputs[0].value, subsidy);
1888    }
1889
1890    #[test]
1891    fn test_create_coinbase_transaction_empty_script() {
1892        let height = 100;
1893        let subsidy = 5000000000;
1894        let script = vec![]; // Empty script
1895        let address = vec![OP_1];
1896
1897        let coinbase_tx = create_coinbase_transaction(height, subsidy, &script, &address).unwrap();
1898
1899        assert!(is_coinbase(&coinbase_tx));
1900        assert_eq!(coinbase_tx.outputs[0].value, subsidy);
1901    }
1902
1903    #[test]
1904    fn test_create_coinbase_transaction_empty_address() {
1905        let height = 100;
1906        let subsidy = 5000000000;
1907        let script = vec![OP_1];
1908        let address = vec![]; // Empty address
1909
1910        let coinbase_tx = create_coinbase_transaction(height, subsidy, &script, &address).unwrap();
1911
1912        assert!(is_coinbase(&coinbase_tx));
1913        assert_eq!(coinbase_tx.outputs[0].value, subsidy);
1914    }
1915
1916    #[test]
1917    fn test_calculate_merkle_root_single_transaction() {
1918        let txs = vec![create_valid_transaction()];
1919        let merkle_root = calculate_merkle_root(&txs).unwrap();
1920
1921        assert_eq!(merkle_root.len(), 32);
1922        assert_ne!(merkle_root, [0u8; 32]);
1923    }
1924
1925    #[test]
1926    fn test_calculate_merkle_root_three_transactions() {
1927        let txs = vec![
1928            create_valid_transaction(),
1929            create_valid_transaction(),
1930            create_valid_transaction(),
1931        ];
1932
1933        let merkle_root = calculate_merkle_root(&txs).unwrap();
1934        assert_eq!(merkle_root.len(), 32);
1935        assert_ne!(merkle_root, [0u8; 32]);
1936    }
1937
1938    #[test]
1939    fn test_calculate_merkle_root_five_transactions() {
1940        let txs = vec![
1941            create_valid_transaction(),
1942            create_valid_transaction(),
1943            create_valid_transaction(),
1944            create_valid_transaction(),
1945            create_valid_transaction(),
1946        ];
1947
1948        let merkle_root = calculate_merkle_root(&txs).unwrap();
1949        assert_eq!(merkle_root.len(), 32);
1950        assert_ne!(merkle_root, [0u8; 32]);
1951    }
1952
1953    #[test]
1954    fn test_calculate_tx_hash_different_transactions() {
1955        let tx1 = create_valid_transaction();
1956        let mut tx2 = create_valid_transaction();
1957        tx2.version = 2; // Different version
1958
1959        let hash1 = calculate_tx_hash(&tx1);
1960        let hash2 = calculate_tx_hash(&tx2);
1961
1962        assert_ne!(hash1, hash2);
1963    }
1964
1965    #[test]
1966    fn test_sha256_hash_empty_data() {
1967        let data = vec![];
1968        let hash = sha256_hash(&data);
1969
1970        assert_eq!(hash.len(), 32);
1971    }
1972
1973    // ==========================================================================
1974    // REGRESSION TESTS: Merkle tree must use double SHA256 (critical fix)
1975    // ==========================================================================
1976    // Bitcoin's Merkle tree uses double SHA256 (SHA256(SHA256(x))) at each level,
1977    // NOT single SHA256. Using single SHA256 produces wrong merkle roots that
1978    // cause all blocks to fail verification.
1979
1980    #[test]
1981    fn test_merkle_tree_uses_double_sha256_not_single() {
1982        // Compute two known hashes
1983        let hash_a = [0x01u8; 32];
1984        let hash_b = [0x02u8; 32];
1985
1986        // Manually compute expected double SHA256 of the concatenation
1987        let mut combined = [0u8; 64];
1988        combined[..32].copy_from_slice(&hash_a);
1989        combined[32..].copy_from_slice(&hash_b);
1990
1991        let expected_double = double_sha256_hash(&combined);
1992        let wrong_single = sha256_hash(&combined);
1993
1994        // They must be different (proving single vs double matters)
1995        assert_ne!(
1996            expected_double, wrong_single,
1997            "Double SHA256 and single SHA256 must produce different results"
1998        );
1999
2000        // Now test via calculate_merkle_root_from_tx_ids
2001        let root = calculate_merkle_root_from_tx_ids(&[hash_a, hash_b]).unwrap();
2002        assert_eq!(
2003            root, expected_double,
2004            "Merkle root must use double SHA256, not single SHA256"
2005        );
2006        assert_ne!(
2007            root, wrong_single,
2008            "Merkle root must NOT match single SHA256 result"
2009        );
2010    }
2011
2012    #[test]
2013    fn test_merkle_root_single_tx_equals_txid() {
2014        // For a single transaction, the merkle root IS the txid itself
2015        let tx = create_valid_transaction();
2016        let txid = calculate_tx_hash(&tx);
2017
2018        let root_from_txs = calculate_merkle_root(&[tx]).unwrap();
2019        let root_from_ids = calculate_merkle_root_from_tx_ids(&[txid]).unwrap();
2020
2021        assert_eq!(root_from_txs, txid, "Single tx merkle root must equal txid");
2022        assert_eq!(
2023            root_from_ids, txid,
2024            "Single txid merkle root must equal txid"
2025        );
2026    }
2027
2028    #[test]
2029    fn test_calculate_merkle_root_from_tx_ids_matches_calculate_merkle_root() {
2030        // Both functions must produce identical results for the same transactions
2031        let tx1 = create_valid_transaction();
2032        let tx2 = create_valid_transaction();
2033        let tx3 = create_valid_transaction();
2034
2035        let txid1 = calculate_tx_hash(&tx1);
2036        let txid2 = calculate_tx_hash(&tx2);
2037        let txid3 = calculate_tx_hash(&tx3);
2038
2039        let root_from_txs = calculate_merkle_root(&[tx1, tx2, tx3]).unwrap();
2040        let root_from_ids = calculate_merkle_root_from_tx_ids(&[txid1, txid2, txid3]).unwrap();
2041
2042        assert_eq!(
2043            root_from_txs, root_from_ids,
2044            "calculate_merkle_root and calculate_merkle_root_from_tx_ids must produce identical results"
2045        );
2046    }
2047
2048    #[test]
2049    fn test_calculate_merkle_root_from_tx_ids_two_txs() {
2050        let tx1 = create_valid_transaction();
2051        let tx2 = create_valid_transaction();
2052
2053        let txid1 = calculate_tx_hash(&tx1);
2054        let txid2 = calculate_tx_hash(&tx2);
2055
2056        let root_from_txs = calculate_merkle_root(&[tx1, tx2]).unwrap();
2057        let root_from_ids = calculate_merkle_root_from_tx_ids(&[txid1, txid2]).unwrap();
2058
2059        assert_eq!(root_from_txs, root_from_ids);
2060    }
2061
2062    #[test]
2063    fn test_calculate_merkle_root_from_tx_ids_four_txs() {
2064        let tx1 = create_valid_transaction();
2065        let tx2 = create_valid_transaction();
2066        let tx3 = create_valid_transaction();
2067        let tx4 = create_valid_transaction();
2068
2069        let txid1 = calculate_tx_hash(&tx1);
2070        let txid2 = calculate_tx_hash(&tx2);
2071        let txid3 = calculate_tx_hash(&tx3);
2072        let txid4 = calculate_tx_hash(&tx4);
2073
2074        let root_from_txs = calculate_merkle_root(&[tx1, tx2, tx3, tx4]).unwrap();
2075        let root_from_ids =
2076            calculate_merkle_root_from_tx_ids(&[txid1, txid2, txid3, txid4]).unwrap();
2077
2078        assert_eq!(root_from_txs, root_from_ids);
2079    }
2080
2081    #[test]
2082    fn test_calculate_merkle_root_from_tx_ids_empty() {
2083        let result = calculate_merkle_root_from_tx_ids(&[]);
2084        assert!(result.is_err(), "Empty tx_ids list should fail");
2085    }
2086
2087    #[test]
2088    fn test_merkle_root_deterministic() {
2089        // Same inputs must always produce the same output
2090        let tx1 = create_valid_transaction();
2091        let tx2 = create_valid_transaction();
2092
2093        let txid1 = calculate_tx_hash(&tx1);
2094        let txid2 = calculate_tx_hash(&tx2);
2095
2096        let root1 = calculate_merkle_root_from_tx_ids(&[txid1, txid2]).unwrap();
2097        let root2 = calculate_merkle_root_from_tx_ids(&[txid1, txid2]).unwrap();
2098
2099        assert_eq!(root1, root2, "Merkle root must be deterministic");
2100    }
2101
2102    #[test]
2103    fn test_merkle_root_order_matters() {
2104        // Swapping tx order must produce a different merkle root
2105        let txid1 = [0x01u8; 32];
2106        let txid2 = [0x02u8; 32];
2107
2108        let root_ab = calculate_merkle_root_from_tx_ids(&[txid1, txid2]).unwrap();
2109        let root_ba = calculate_merkle_root_from_tx_ids(&[txid2, txid1]).unwrap();
2110
2111        assert_ne!(root_ab, root_ba, "Tx order must affect merkle root");
2112    }
2113
2114    #[test]
2115    fn test_double_sha256_hash_known_value() {
2116        // Verify double_sha256_hash produces correct result for empty input
2117        // SHA256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
2118        // SHA256(SHA256("")) = 5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456
2119        let result = double_sha256_hash(&[]);
2120        // Just verify it's 32 bytes and deterministic
2121        assert_eq!(result.len(), 32);
2122        let result2 = double_sha256_hash(&[]);
2123        assert_eq!(result, result2);
2124
2125        // Also verify it's different from single SHA256
2126        let single = sha256_hash(&[]);
2127        assert_ne!(
2128            result, single,
2129            "double SHA256 must differ from single SHA256"
2130        );
2131    }
2132
2133    fn declared_spend(prev_hash: [u8; 32], extra: u8) -> Transaction {
2134        Transaction {
2135            version: 1,
2136            inputs: vec![TransactionInput {
2137                prevout: OutPoint {
2138                    hash: prev_hash,
2139                    index: 0,
2140                },
2141                script_sig: vec![OP_1, PUSH_1_BYTE, extra],
2142                sequence: 0xffffffff,
2143            }]
2144            .into(),
2145            outputs: vec![TransactionOutput {
2146                value: 1000,
2147                script_pubkey: vec![],
2148            }]
2149            .into(),
2150            lock_time: 0,
2151        }
2152    }
2153
2154    fn insert_legacy_utxo(utxo_set: &mut UtxoSet, hash: [u8; 32]) {
2155        utxo_set.insert(
2156            OutPoint { hash, index: 0 },
2157            std::sync::Arc::new(UTXO {
2158                value: 10_000,
2159                script_pubkey: vec![].into(),
2160                height: 0,
2161                is_coinbase: false,
2162            }),
2163        );
2164    }
2165
2166    fn declared_template_header() -> (BlockHeader, Vec<BlockHeader>) {
2167        let prev = BlockHeader {
2168            version: 4,
2169            prev_block_hash: [0u8; 32],
2170            merkle_root: [0u8; 32],
2171            timestamp: 1_600_000_000,
2172            bits: 0x207fffff,
2173            nonce: 0,
2174        };
2175        let prev_headers = vec![prev.clone(), prev.clone()];
2176        (prev, prev_headers)
2177    }
2178
2179    #[test]
2180    fn resolve_declared_txs_empty_ok() {
2181        let tx = declared_spend([1u8; 32], 1);
2182        assert!(resolve_declared_txs(&[tx], &[]).unwrap().is_empty());
2183    }
2184
2185    #[test]
2186    fn resolve_declared_txs_unknown_is_error() {
2187        let tx = declared_spend([1u8; 32], 1);
2188        let err = resolve_declared_txs(&[tx], &[[9u8; 32]]).unwrap_err();
2189        assert!(err.to_string().contains("unknown declared txid"));
2190    }
2191
2192    #[test]
2193    fn resolve_declared_txs_duplicate_is_error() {
2194        let tx = declared_spend([1u8; 32], 1);
2195        let id = crate::block::calculate_tx_id(&tx);
2196        let err = resolve_declared_txs(&[tx], &[id, id]).unwrap_err();
2197        assert!(err.to_string().contains("duplicate declared txid"));
2198    }
2199
2200    #[test]
2201    fn declared_template_selects_named_subset_and_commons_bip141() {
2202        let mut utxo_set = UtxoSet::default();
2203        insert_legacy_utxo(&mut utxo_set, [1u8; 32]);
2204        insert_legacy_utxo(&mut utxo_set, [2u8; 32]);
2205        let a = declared_spend([1u8; 32], 1);
2206        let b = declared_spend([2u8; 32], 2);
2207        let id_b = crate::block::calculate_tx_id(&b);
2208        let (prev, prev_headers) = declared_template_header();
2209        let outputs = [(1, vec![OP_1]), (2, vec![OP_2])];
2210        let tmpl = create_block_template_declared(
2211            &utxo_set,
2212            &[a, b.clone()],
2213            &[id_b],
2214            1,
2215            &prev,
2216            &prev_headers,
2217            &vec![OP_1],
2218            &outputs,
2219            Network::Regtest,
2220            None,
2221        )
2222        .expect("declared subset");
2223        assert_eq!(tmpl.transactions.len(), 1);
2224        assert_eq!(crate::block::calculate_tx_id(&tmpl.transactions[0]), id_b);
2225        assert!(is_coinbase(&tmpl.coinbase_tx));
2226        assert!(tmpl.coinbase_tx.outputs.len() >= 3);
2227        let last = tmpl.coinbase_tx.outputs.last().unwrap();
2228        assert_eq!(last.value, 0);
2229        assert_eq!(
2230            &last.script_pubkey[0..6],
2231            &[0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed]
2232        );
2233        assert_eq!(tmpl.coinbase_tx.outputs[1].script_pubkey, vec![OP_2]);
2234    }
2235
2236    #[test]
2237    fn declared_template_empty_is_coinbase_only() {
2238        let utxo_set = UtxoSet::default();
2239        let extra = declared_spend([1u8; 32], 1);
2240        let (prev, prev_headers) = declared_template_header();
2241        let tmpl = create_block_template_declared(
2242            &utxo_set,
2243            &[extra],
2244            &[],
2245            1,
2246            &prev,
2247            &prev_headers,
2248            &vec![OP_1],
2249            &[(1, vec![OP_1])],
2250            Network::Regtest,
2251            None,
2252        )
2253        .expect("empty declaration");
2254        assert!(tmpl.transactions.is_empty());
2255        assert!(is_coinbase(&tmpl.coinbase_tx));
2256        assert!(check_template_weight(&tmpl, crate::constants::MAX_BLOCK_WEIGHT as u64).is_ok());
2257        let err = check_template_weight(&tmpl, 1).unwrap_err();
2258        assert!(err.to_string().contains("exceeds max"));
2259    }
2260
2261    #[test]
2262    fn declared_template_unknown_txid_errors() {
2263        let utxo_set = UtxoSet::default();
2264        let tx = declared_spend([1u8; 32], 1);
2265        let (prev, prev_headers) = declared_template_header();
2266        let err = create_block_template_declared(
2267            &utxo_set,
2268            &[tx],
2269            &[[9u8; 32]],
2270            1,
2271            &prev,
2272            &prev_headers,
2273            &vec![OP_1],
2274            &[(1, vec![OP_1])],
2275            Network::Regtest,
2276            None,
2277        )
2278        .unwrap_err();
2279        assert!(err.to_string().contains("unknown declared txid"));
2280    }
2281
2282    #[test]
2283    fn declared_template_duplicate_txid_errors() {
2284        let mut utxo_set = UtxoSet::default();
2285        insert_legacy_utxo(&mut utxo_set, [1u8; 32]);
2286        let tx = declared_spend([1u8; 32], 1);
2287        let id = crate::block::calculate_tx_id(&tx);
2288        let (prev, prev_headers) = declared_template_header();
2289        let err = create_block_template_declared(
2290            &utxo_set,
2291            &[tx],
2292            &[id, id],
2293            1,
2294            &prev,
2295            &prev_headers,
2296            &vec![OP_1],
2297            &[(1, vec![OP_1])],
2298            Network::Regtest,
2299            None,
2300        )
2301        .unwrap_err();
2302        assert!(err.to_string().contains("duplicate declared txid"));
2303    }
2304
2305    #[test]
2306    fn declared_template_rejected_tx_errors() {
2307        let utxo_set = UtxoSet::default();
2308        let rejected = declared_spend([9u8; 32], 9);
2309        let id = crate::block::calculate_tx_id(&rejected);
2310        let (prev, prev_headers) = declared_template_header();
2311        let err = create_block_template_declared(
2312            &utxo_set,
2313            &[rejected],
2314            &[id],
2315            1,
2316            &prev,
2317            &prev_headers,
2318            &vec![OP_1],
2319            &[(1, vec![OP_1])],
2320            Network::Regtest,
2321            None,
2322        )
2323        .unwrap_err();
2324        let msg = err.to_string();
2325        assert!(
2326            msg.contains("not selected") || msg.contains("rejected"),
2327            "expected rejected-declared error, got {msg}"
2328        );
2329    }
2330
2331    #[test]
2332    fn declared_template_missing_witness_stack_errors() {
2333        let mut utxo_set = UtxoSet::default();
2334        let mut p2wpkh = vec![OP_0, PUSH_20_BYTES];
2335        p2wpkh.extend_from_slice(&[0u8; 20]);
2336        utxo_set.insert(
2337            OutPoint {
2338                hash: [0xAAu8; 32],
2339                index: 0,
2340            },
2341            std::sync::Arc::new(UTXO {
2342                value: 10_000,
2343                script_pubkey: p2wpkh.into(),
2344                height: 0,
2345                is_coinbase: false,
2346            }),
2347        );
2348        let spend = Transaction {
2349            version: 2,
2350            inputs: vec![TransactionInput {
2351                prevout: OutPoint {
2352                    hash: [0xAAu8; 32],
2353                    index: 0,
2354                },
2355                script_sig: vec![],
2356                sequence: 0xffffffff,
2357            }]
2358            .into(),
2359            outputs: vec![TransactionOutput {
2360                value: 1000,
2361                script_pubkey: vec![OP_1],
2362            }]
2363            .into(),
2364            lock_time: 0,
2365        };
2366        let id = crate::block::calculate_tx_id(&spend);
2367        let (prev, prev_headers) = declared_template_header();
2368        let err = create_block_template_declared(
2369            &utxo_set,
2370            &[spend],
2371            &[id],
2372            1,
2373            &prev,
2374            &prev_headers,
2375            &vec![OP_1],
2376            &[(1, vec![OP_1])],
2377            Network::Regtest,
2378            None,
2379        )
2380        .unwrap_err();
2381        assert!(
2382            err.to_string().contains("missing mempool witnesses"),
2383            "got {}",
2384            err
2385        );
2386    }
2387
2388    #[test]
2389    fn template_version_follows_bip90() {
2390        assert_eq!(template_block_version(Network::Mainnet, 1), 1);
2391        assert!(template_block_version(Network::Mainnet, 388_381) >= 4);
2392    }
2393
2394    #[test]
2395    fn template_time_is_at_least_the_median_plus_one() {
2396        let prev = BlockHeader {
2397            version: 1,
2398            prev_block_hash: [0; 32],
2399            merkle_root: [0; 32],
2400            timestamp: 999,
2401            bits: 0x1d00ffff,
2402            nonce: 0,
2403        };
2404        let prev_headers = vec![prev.clone(); 11];
2405        let utxo = UtxoSet::default();
2406        let script = vec![OP_1];
2407        let early = create_new_block_with_time(
2408            &utxo,
2409            &[],
2410            1,
2411            &prev,
2412            &prev_headers,
2413            &script,
2414            &script,
2415            900,
2416            Network::Mainnet,
2417            None,
2418        )
2419        .unwrap();
2420        assert_eq!(early.header.timestamp, 1_000);
2421        assert_eq!(early.header.version, 1);
2422        let late = create_new_block_with_time(
2423            &utxo,
2424            &[],
2425            1,
2426            &prev,
2427            &prev_headers,
2428            &script,
2429            &script,
2430            1_100,
2431            Network::Mainnet,
2432            None,
2433        )
2434        .unwrap();
2435        assert_eq!(late.header.timestamp, 1_100);
2436    }
2437}