1#[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
16fn 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
61pub 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
78pub 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 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#[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 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 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 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 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 #[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 let mut transactions = vec![coinbase_tx];
255 transactions.extend(selected_txs);
256
257 let merkle_root = calculate_merkle_root(&transactions)?;
259
260 let next_work = template_bits(network, height, block_time, prev_header, prev_headers)?;
264
265 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, };
274
275 Ok(Block {
276 header,
277 transactions: transactions.into_boxed_slice(),
278 })
279}
280
281#[track_caller] pub 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#[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
317pub 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 #[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#[derive(Debug, Clone, PartialEq, Eq)]
377pub enum MiningResult {
378 Success,
379 Failure,
380}
381
382fn 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
395pub 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, };
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#[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
473pub 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
512fn 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 let slot = slot.or_else(|| Some(vec![Vec::new(); tx.inputs.len()]));
557 out.push(slot);
558 }
559 Ok(out)
560}
561
562#[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
605pub 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
628fn 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
661pub 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
709pub 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
737pub 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
747pub 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#[track_caller] #[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 #[cfg(feature = "production")]
812 let mut hashes: Vec<crate::optimizations::CacheAlignedHash> = {
813 use crate::optimizations::simd_vectorization;
814
815 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) .collect()
826 }
827 #[cfg(not(feature = "rayon"))]
828 {
829 transactions
830 .iter()
831 .map(serialize_tx_for_hash) .collect()
833 }
834 };
835
836 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 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 #[cfg(feature = "production")]
857 {
858 use crate::optimizations::CacheAlignedHash;
859
860 let mut mutated = false;
861
862 while hashes.len() > 1 {
863 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 if hashes.len() & 1 != 0 {
878 let last = hashes[hashes.len() - 1].clone();
879 hashes.push(last);
880 }
881
882 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#[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#[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#[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 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 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 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
997fn serialize_tx_for_hash(tx: &Transaction) -> Vec<u8> {
1002 #[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 data.extend_from_slice(&(tx.version as u32).to_le_bytes());
1014
1015 data.extend_from_slice(&encode_varint(tx.inputs.len() as u64));
1017
1018 for input in &tx.inputs {
1020 data.extend_from_slice(&input.prevout.hash);
1022 data.extend_from_slice(&input.prevout.index.to_le_bytes());
1024 data.extend_from_slice(&encode_varint(input.script_sig.len() as u64));
1026 data.extend_from_slice(&input.script_sig);
1028 data.extend_from_slice(&(input.sequence as u32).to_le_bytes());
1030 }
1031
1032 data.extend_from_slice(&encode_varint(tx.outputs.len() as u64));
1034
1035 for output in &tx.outputs {
1037 data.extend_from_slice(&(output.value as u64).to_le_bytes());
1039 data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
1041 data.extend_from_slice(&output.script_pubkey);
1043 }
1044
1045 data.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
1047
1048 data
1049}
1050
1051#[allow(dead_code)] fn calculate_tx_hash(tx: &Transaction) -> Hash {
1057 let data = serialize_tx_for_hash(tx);
1058 let hash1 = sha256_hash(&data);
1060 sha256_hash(&hash1)
1061}
1062
1063fn 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
1082fn calculate_block_hash(header: &BlockHeader) -> Hash {
1084 let wire = crate::serialization::block::serialize_block_header(header);
1085 double_sha256_hash(&wire)
1086}
1087
1088#[inline(always)]
1093fn sha256_hash(data: &[u8]) -> Hash {
1094 use crate::crypto::OptimizedSha256;
1095 OptimizedSha256::new().hash(data)
1096}
1097
1098#[inline(always)]
1100fn double_sha256_hash(data: &[u8]) -> Hash {
1101 use crate::crypto::OptimizedSha256;
1102 OptimizedSha256::new().hash256(data)
1103}
1104
1105fn 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 let outpoint = OutPoint {
1123 hash: [1; 32],
1124 index: 0,
1125 };
1126 let utxo = UTXO {
1127 value: 10000,
1128 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 let mut prev_header2 = prev_header.clone();
1140 prev_header2.timestamp = prev_header.timestamp + 600; 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 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); assert!(is_coinbase(&block.transactions[0]));
1164 assert_eq!(block.header.version, 1);
1165 assert_eq!(block.header.timestamp, block_time);
1166 } else {
1167 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 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 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 #[test]
1250 fn test_create_block_template_comprehensive() {
1251 let mut utxo_set = UtxoSet::default();
1252 let outpoint = OutPoint {
1254 hash: [1; 32],
1255 index: 0,
1256 };
1257 let utxo = UTXO {
1258 value: 10000,
1259 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 let mut prev_header2 = prev_header.clone();
1271 prev_header2.timestamp = prev_header.timestamp + 600; 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 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 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 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 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 assert_eq!(hash.len(), 32);
1485
1486 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; let hash1 = calculate_tx_hash(&tx1);
1498 let hash2 = calculate_tx_hash(&tx2);
1499
1500 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 assert_eq!(hash.len(), 32);
1538
1539 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; let hash1 = calculate_block_hash(&header1);
1551 let hash2 = calculate_block_hash(&header2);
1552
1553 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 assert_eq!(hash.len(), 32);
1564
1565 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 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 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 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 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 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 let outpoint = OutPoint {
1674 hash: [1; 32],
1675 index: 0,
1676 };
1677 let utxo = UTXO {
1678 value: 10000,
1679 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 let Ok(template) = result {
1708 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 assert!(result.is_err());
1718 }
1719 }
1720
1721 #[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 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], index: 0,
1817 },
1818 script_sig: {
1825 let mut sig = vec![OP_1]; if counter > 0 {
1828 sig.push(PUSH_1_BYTE); sig.push((counter & 0xff) as u8); }
1831 sig
1832 },
1833 sequence: 0xffffffff,
1834 }]
1835 .into(),
1836 outputs: vec![TransactionOutput {
1837 value: 1000 + counter as i64, 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, 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; 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; 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![]; 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![]; 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; 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 #[test]
1981 fn test_merkle_tree_uses_double_sha256_not_single() {
1982 let hash_a = [0x01u8; 32];
1984 let hash_b = [0x02u8; 32];
1985
1986 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 assert_ne!(
1996 expected_double, wrong_single,
1997 "Double SHA256 and single SHA256 must produce different results"
1998 );
1999
2000 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 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 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 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 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 let result = double_sha256_hash(&[]);
2120 assert_eq!(result.len(), 32);
2122 let result2 = double_sha256_hash(&[]);
2123 assert_eq!(result, result2);
2124
2125 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}