1use crate::activation::IsForkActive;
9use crate::block::calculate_tx_id;
10use crate::error::Result;
11use crate::opcodes::{
12 OP_0, OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY, OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4,
13};
14use crate::transaction::is_coinbase;
15use crate::types::*;
16#[cfg(feature = "spec-lock")]
17use blvm_spec_lock::spec_locked;
18
19#[cfg(feature = "production")]
23pub type Bip30Index = rustc_hash::FxHashMap<crate::types::Hash, usize>;
24#[cfg(not(feature = "production"))]
25pub type Bip30Index = std::collections::HashMap<crate::types::Hash, usize>;
26
27pub fn build_bip30_index(utxo_set: &UtxoSet) -> Bip30Index {
30 let mut index = Bip30Index::default();
31 for (outpoint, utxo) in utxo_set.iter() {
32 if utxo.is_coinbase {
33 *index.entry(outpoint.hash).or_insert(0) += 1;
34 }
35 }
36 index
37}
38
39const BIP30_REPEAT_91842: Hash = [
41 0xec, 0xca, 0xe0, 0x00, 0xe3, 0xc8, 0xe4, 0xe0, 0x93, 0x93, 0x63, 0x60, 0x43, 0x1f, 0x3b, 0x76,
42 0x03, 0xc5, 0x63, 0xc1, 0xff, 0x61, 0x81, 0x39, 0x0a, 0x4d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
43];
44const BIP30_REPEAT_91880: Hash = [
46 0x21, 0xd7, 0x7c, 0xcb, 0x4c, 0x08, 0x38, 0x6a, 0x04, 0xac, 0x01, 0x96, 0xae, 0x10, 0xf6, 0xa1,
47 0xd2, 0xc2, 0xa3, 0x77, 0x55, 0x8c, 0xa1, 0x90, 0xf1, 0x43, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
48];
49const BIP34_MAINNET_HASH: Hash = [
51 0xb8, 0x08, 0x08, 0x9c, 0x75, 0x6a, 0xdd, 0x15, 0x91, 0xb1, 0xd1, 0x7b, 0xab, 0x44, 0xbb, 0xa3,
52 0xfe, 0xd9, 0xe0, 0x2f, 0x94, 0x2a, 0xb4, 0x89, 0x4b, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53];
54const BIP34_TESTNET_HASH: Hash = [
56 0xf8, 0x8e, 0xcd, 0x99, 0x12, 0xd0, 0x0d, 0x3f, 0x5c, 0x2a, 0x8e, 0x0f, 0x50, 0x41, 0x7d, 0x3e,
57 0x41, 0x5c, 0x75, 0xb3, 0xab, 0xe5, 0x84, 0x34, 0x6d, 0xa9, 0xb3, 0x23, 0x00, 0x00, 0x00, 0x00,
58];
59
60pub const BIP34_IMPLIES_BIP30_LIMIT: u64 = 1_983_702;
64
65pub fn is_bip30_repeat_block(height: u64, block_hash: &Hash) -> bool {
67 match height {
68 91_842 => block_hash == &BIP30_REPEAT_91842,
69 91_880 => block_hash == &BIP30_REPEAT_91880,
70 _ => false,
71 }
72}
73
74pub fn is_known_bip34_header(network: Network, height: u64, header: &BlockHeader) -> bool {
77 let expected = match network {
78 Network::Mainnet if height == crate::constants::BIP34_ACTIVATION_MAINNET => {
79 &BIP34_MAINNET_HASH
80 }
81 Network::Testnet if height == crate::constants::BIP34_ACTIVATION_TESTNET => {
82 &BIP34_TESTNET_HASH
83 }
84 _ => return false,
85 };
86 &crate::block::block_header_hash(header) == expected
87}
88
89pub fn is_known_bip34_block(network: Network, height: u64, block_hash: &Hash) -> bool {
91 match network {
92 Network::Mainnet => {
93 height == crate::constants::BIP34_ACTIVATION_MAINNET
94 && block_hash == &BIP34_MAINNET_HASH
95 }
96 Network::Testnet => {
97 height == crate::constants::BIP34_ACTIVATION_TESTNET
98 && block_hash == &BIP34_TESTNET_HASH
99 }
100 Network::Regtest | Network::Signet | Network::Testnet4 => false,
101 }
102}
103
104pub fn bip30_lookup_skippable(network: Network, height: u64, bip34_hash_matches: bool) -> bool {
107 if !bip34_hash_matches || height >= BIP34_IMPLIES_BIP30_LIMIT {
108 return false;
109 }
110 let activation = match network {
111 Network::Mainnet => crate::constants::BIP34_ACTIVATION_MAINNET,
112 Network::Testnet => crate::constants::BIP34_ACTIVATION_TESTNET,
113 Network::Regtest | Network::Signet | Network::Testnet4 => return false,
114 };
115 height > activation
116}
117
118#[cfg_attr(feature = "spec-lock", spec_locked("5.4.1", "BIP30Check"))]
133pub fn check_bip30(
134 block: &Block,
135 utxo_set: &UtxoSet,
136 bip30_index: Option<&Bip30Index>,
137 height: Natural,
138 activation: &impl IsForkActive,
139 coinbase_txid: Option<&Hash>,
140) -> Result<bool> {
141 if !activation.is_fork_active(ForkId::Bip30, height) {
142 return Ok(true);
143 }
144 if height == 91_842 || height == 91_880 {
147 let hash = crate::block::block_header_hash(&block.header);
148 if is_bip30_repeat_block(height, &hash) {
149 return Ok(true);
150 }
151 }
152 let coinbase = block.transactions.first();
154
155 if let Some(tx) = coinbase {
156 if !is_coinbase(tx) {
157 return Ok(true);
159 }
160
161 let txid = coinbase_txid
162 .copied()
163 .unwrap_or_else(|| calculate_tx_id(tx));
164
165 if let Some(index) = bip30_index {
167 if index.get(&txid).is_some_and(|&c| c > 0) {
168 return Ok(false);
169 }
170 return Ok(true);
171 }
172
173 for (outpoint, _utxo) in utxo_set.iter() {
176 if outpoint.hash == txid {
177 return Ok(false);
178 }
179 }
180 }
181
182 Ok(true)
183}
184
185#[cfg_attr(feature = "spec-lock", spec_locked("5.4.2", "BIP34Check"))]
198pub fn check_bip34(block: &Block, height: Natural, activation: &impl IsForkActive) -> Result<bool> {
199 if !activation.is_fork_active(ForkId::Bip34, height) {
200 return Ok(true);
201 }
202
203 let coinbase = block.transactions.first();
205
206 if let Some(tx) = coinbase {
207 if !is_coinbase(tx) {
208 return Ok(true);
209 }
210
211 if !script_sig_starts_with_height(&tx.inputs[0].script_sig, height) {
214 return Ok(false);
215 }
216 }
217
218 Ok(true)
219}
220
221fn bip34_magnitude(height: u64) -> ([u8; 9], usize) {
223 let mut raw = [0u8; 9];
224 let mut n = height;
225 let mut len = 0usize;
226 while n > 0 {
227 raw[len] = (n & 0xff) as u8;
228 n >>= 8;
229 len += 1;
230 }
231 if len > 0 && raw[len - 1] & 0x80 != 0 {
232 raw[len] = 0x00;
233 len += 1;
234 }
235 (raw, len)
236}
237
238pub fn encode_bip34_coinbase_script(height: u64) -> Vec<u8> {
244 if height == 0 {
245 return vec![0x00, 0xff];
246 }
247 if (1..=16).contains(&height) {
248 return vec![0x50 + height as u8, 0xff];
249 }
250 let (raw, len) = bip34_magnitude(height);
251 let mut script = Vec::with_capacity(len + 2);
252 script.push(len as u8);
253 script.extend_from_slice(&raw[..len]);
254 if script.len() < 2 {
255 script.push(0xff);
256 }
257 script
258}
259
260fn script_sig_starts_with_height(script_sig: &[u8], height: u64) -> bool {
269 if height == 0 {
270 return script_sig.first().copied() == Some(0x00);
271 }
272 if (1..=16).contains(&height) {
273 return script_sig.first().copied() == Some(0x50 + height as u8);
275 }
276 let (raw, len) = bip34_magnitude(height);
277 script_sig.len() > len && script_sig[0] == len as u8 && script_sig[1..1 + len] == raw[..len]
278}
279
280#[cfg_attr(feature = "spec-lock", spec_locked("5.4.9", "IsBip54ActiveAt"))]
287pub fn is_bip54_active_at(
288 height: Natural,
289 network: crate::types::Network,
290 activation_override: Option<u64>,
291) -> bool {
292 let activation = match activation_override {
293 Some(h) => h,
294 None => match network {
295 crate::types::Network::Mainnet => crate::constants::BIP54_ACTIVATION_MAINNET,
296 crate::types::Network::Testnet => crate::constants::BIP54_ACTIVATION_TESTNET,
297 crate::types::Network::Regtest
298 | crate::types::Network::Signet
299 | crate::types::Network::Testnet4 => crate::constants::BIP54_ACTIVATION_REGTEST,
300 },
301 };
302 activation != u64::MAX && height >= activation
303}
304
305#[cfg_attr(feature = "spec-lock", spec_locked("5.4.9", "IsBip54Active"))]
312pub fn is_bip54_active(height: Natural, network: crate::types::Network) -> bool {
313 is_bip54_active_at(height, network, None)
314}
315
316#[cfg_attr(feature = "spec-lock", spec_locked("5.4.9", "BIP54TimewarpCheck"))]
318pub fn check_bip54_timewarp(
319 header: &BlockHeader,
320 height: Natural,
321 boundary: Option<&Bip54BoundaryTimestamps>,
322 bip54_active: bool,
323) -> bool {
324 if !bip54_active {
325 return true;
326 }
327 let rem = height % 2016;
328 if rem == 2015 {
329 let Some(b) = boundary else {
330 return false;
331 };
332 header.timestamp >= b.timestamp_n_minus_2015
333 } else if rem == 0 {
334 let Some(b) = boundary else {
335 return false;
336 };
337 const TWOHOURS: u64 = 7200;
338 let min_ts = b.timestamp_n_minus_1.saturating_sub(TWOHOURS);
339 header.timestamp >= min_ts
340 } else {
341 true
342 }
343}
344
345#[cfg_attr(
347 feature = "spec-lock",
348 spec_locked("5.4.9", "CheckBip54TxStrippedSize")
349)]
350pub fn check_bip54_tx_stripped_size(tx: &Transaction) -> bool {
351 is_coinbase(tx) || crate::transaction::calculate_transaction_size(tx) != 64
352}
353
354#[cfg_attr(feature = "spec-lock", spec_locked("5.4.9", "CheckBip54SigOpLimit"))]
356pub fn check_bip54_sigop_limit<U: crate::utxo_overlay::UtxoLookup>(
357 bip54_active: bool,
358 tx: &Transaction,
359 utxo_lookup: &U,
360 wits: Option<&[Witness]>,
361 tx_flags: u32,
362) -> Result<Option<&'static str>> {
363 if !bip54_active || is_coinbase(tx) {
364 return Ok(None);
365 }
366 let sigop_count =
367 crate::sigop::get_transaction_sigop_count_for_bip54(tx, utxo_lookup, wits, tx_flags)?;
368 if sigop_count > crate::constants::BIP54_MAX_SIGOPS_PER_TX {
369 return Ok(Some("BIP54: Transaction sigop count exceeds 2500"));
370 }
371 Ok(None)
372}
373
374#[cfg_attr(feature = "spec-lock", spec_locked("5.4.9", "CheckBip54Coinbase"))]
379pub fn check_bip54_coinbase(coinbase: &Transaction, height: Natural) -> bool {
380 let required_lock_time = height.saturating_sub(1);
381 if coinbase.lock_time != required_lock_time {
382 return false;
383 }
384 if coinbase.inputs.is_empty() {
385 return false;
386 }
387 if coinbase.inputs[0].sequence == 0xffff_ffff {
388 return false;
389 }
390 true
391}
392
393#[cfg_attr(feature = "spec-lock", spec_locked("5.4.3", "BIP66Check"))]
407pub fn check_bip66(
408 signature: &[u8],
409 height: Natural,
410 activation: &impl IsForkActive,
411) -> Result<bool> {
412 if !activation.is_fork_active(ForkId::Bip66, height) {
413 return Ok(true);
414 }
415
416 is_strict_der(signature)
420}
421
422pub(crate) fn is_strict_der(signature: &[u8]) -> Result<bool> {
430 if signature.len() < 9 {
444 return Ok(false);
445 }
446 if signature.len() > 73 {
447 return Ok(false);
448 }
449
450 if signature[0] != 0x30 {
452 return Ok(false);
453 }
454
455 if signature[1] != (signature.len() - 3) as u8 {
457 return Ok(false);
458 }
459
460 let len_r = signature[3] as usize;
462
463 if 5 + len_r >= signature.len() {
465 return Ok(false);
466 }
467
468 let len_s = signature[5 + len_r] as usize;
470
471 if (len_r + len_s + 7) != signature.len() {
474 return Ok(false);
475 }
476
477 if signature[2] != 0x02 {
479 return Ok(false);
480 }
481
482 if len_r == 0 {
484 return Ok(false);
485 }
486
487 if (signature[4] & 0x80) != 0 {
489 return Ok(false);
490 }
491
492 if len_r > 1 && signature[4] == 0x00 && (signature[5] & 0x80) == 0 {
495 return Ok(false);
496 }
497
498 if signature[len_r + 4] != 0x02 {
500 return Ok(false);
501 }
502
503 if len_s == 0 {
505 return Ok(false);
506 }
507
508 if (signature[len_r + 6] & 0x80) != 0 {
510 return Ok(false);
511 }
512
513 if len_s > 1 && signature[len_r + 6] == 0x00 && (signature[len_r + 7] & 0x80) == 0 {
516 return Ok(false);
517 }
518
519 Ok(true)
520}
521
522#[cfg_attr(feature = "spec-lock", spec_locked("5.4.4", "BIP90Check"))]
536pub fn check_bip90(
537 block_version: i64,
538 height: Natural,
539 activation: &impl IsForkActive,
540) -> Result<bool> {
541 if activation.is_fork_active(ForkId::Bip34, height) && block_version < 2 {
542 return Ok(false);
543 }
544 if activation.is_fork_active(ForkId::Bip66, height) && block_version < 3 {
545 return Ok(false);
546 }
547 if activation.is_fork_active(ForkId::Bip65, height) && block_version < 4 {
548 return Ok(false);
549 }
550
551 Ok(true)
552}
553
554pub fn check_bip30_network(
556 block: &Block,
557 utxo_set: &UtxoSet,
558 bip30_index: Option<&Bip30Index>,
559 height: Natural,
560 network: crate::types::Network,
561 coinbase_txid: Option<&Hash>,
562) -> Result<bool> {
563 let table = crate::activation::ForkActivationTable::from_network(network);
564 check_bip30(block, utxo_set, bip30_index, height, &table, coinbase_txid)
565}
566
567pub fn check_bip34_network(
569 block: &Block,
570 height: Natural,
571 network: crate::types::Network,
572) -> Result<bool> {
573 let table = crate::activation::ForkActivationTable::from_network(network);
574 check_bip34(block, height, &table)
575}
576
577pub fn check_bip66_network(
579 signature: &[u8],
580 height: Natural,
581 network: crate::types::Network,
582) -> Result<bool> {
583 let table = crate::activation::ForkActivationTable::from_network(network);
584 check_bip66(signature, height, &table)
585}
586
587pub fn check_bip90_network(
589 block_version: i64,
590 height: Natural,
591 network: crate::types::Network,
592) -> Result<bool> {
593 let table = crate::activation::ForkActivationTable::from_network(network);
594 check_bip90(block_version, height, &table)
595}
596
597pub fn check_bip147_network(
599 script_sig: &[u8],
600 script_pubkey: &[u8],
601 height: Natural,
602 network: Bip147Network,
603) -> Result<bool> {
604 let table = match network {
605 Bip147Network::Mainnet => {
606 crate::activation::ForkActivationTable::from_network(crate::types::Network::Mainnet)
607 }
608 Bip147Network::Testnet => {
609 crate::activation::ForkActivationTable::from_network(crate::types::Network::Testnet)
610 }
611 Bip147Network::Regtest => {
612 crate::activation::ForkActivationTable::from_network(crate::types::Network::Regtest)
613 }
614 };
615 check_bip147(script_sig, script_pubkey, height, &table)
616}
617
618pub(crate) fn script_contains_executable_opcode(script: &[u8], opcode: u8) -> bool {
620 let mut i = 0;
621 while i < script.len() {
622 let op = script[i];
623 if op > 0 && op < OP_PUSHDATA1 {
624 i += 1 + op as usize;
625 continue;
626 }
627 match op {
628 OP_PUSHDATA1 => {
629 if i + 1 >= script.len() {
630 break;
631 }
632 let len = script[i + 1] as usize;
633 i += 2 + len;
634 }
635 OP_PUSHDATA2 => {
636 if i + 2 >= script.len() {
637 break;
638 }
639 let len = u16::from_le_bytes([script[i + 1], script[i + 2]]) as usize;
640 i += 3 + len;
641 }
642 OP_PUSHDATA4 => {
643 if i + 4 >= script.len() {
644 break;
645 }
646 let len = u32::from_le_bytes([
647 script[i + 1],
648 script[i + 2],
649 script[i + 3],
650 script[i + 4],
651 ]) as usize;
652 i += 5 + len;
653 }
654 _ => {
655 if op == opcode {
656 return true;
657 }
658 i += 1;
659 }
660 }
661 }
662 false
663}
664
665fn script_has_checkmultisig(script_pubkey: &[u8]) -> bool {
666 script_contains_executable_opcode(script_pubkey, OP_CHECKMULTISIG)
667 || script_contains_executable_opcode(script_pubkey, OP_CHECKMULTISIGVERIFY)
668}
669
670#[cfg_attr(feature = "spec-lock", spec_locked("5.4.5", "BIP147Check"))]
682pub fn check_bip147(
683 script_sig: &[u8],
684 script_pubkey: &[u8],
685 height: Natural,
686 activation: &impl IsForkActive,
687) -> Result<bool> {
688 if !activation.is_fork_active(ForkId::Bip147, height) {
689 return Ok(true);
690 }
691
692 if !script_has_checkmultisig(script_pubkey) {
694 return Ok(true);
695 }
696
697 is_null_dummy(script_sig)
699}
700
701fn is_null_dummy(script_sig: &[u8]) -> Result<bool> {
704 if script_sig.is_empty() {
705 return Ok(false);
706 }
707
708 let mut pc = 0usize;
709 let mut first_push_empty = false;
710 let mut saw_push = false;
711
712 while pc < script_sig.len() {
713 let opcode = script_sig[pc];
714 pc += 1;
715 match opcode {
716 OP_0 => {
717 if !saw_push {
718 first_push_empty = true;
719 saw_push = true;
720 }
721 }
722 0x01..=0x4b => {
723 let len = opcode as usize;
724 if pc + len > script_sig.len() {
725 return Ok(false);
726 }
727 if !saw_push {
728 first_push_empty = len == 0;
729 saw_push = true;
730 }
731 pc += len;
732 }
733 OP_PUSHDATA1 => {
734 if pc >= script_sig.len() {
735 return Ok(false);
736 }
737 let len = script_sig[pc] as usize;
738 pc += 1;
739 if pc + len > script_sig.len() {
740 return Ok(false);
741 }
742 if !saw_push {
743 first_push_empty = len == 0;
744 saw_push = true;
745 }
746 pc += len;
747 }
748 OP_PUSHDATA2 => {
749 if pc + 2 > script_sig.len() {
750 return Ok(false);
751 }
752 let len = u16::from_le_bytes([script_sig[pc], script_sig[pc + 1]]) as usize;
753 pc += 2;
754 if pc + len > script_sig.len() {
755 return Ok(false);
756 }
757 if !saw_push {
758 first_push_empty = len == 0;
759 saw_push = true;
760 }
761 pc += len;
762 }
763 OP_PUSHDATA4 => {
764 if pc + 4 > script_sig.len() {
765 return Ok(false);
766 }
767 let len = u32::from_le_bytes([
768 script_sig[pc],
769 script_sig[pc + 1],
770 script_sig[pc + 2],
771 script_sig[pc + 3],
772 ]) as usize;
773 pc += 4;
774 if pc + len > script_sig.len() {
775 return Ok(false);
776 }
777 if !saw_push {
778 first_push_empty = len == 0;
779 saw_push = true;
780 }
781 pc += len;
782 }
783 _ => return Ok(false),
784 }
785 }
786
787 Ok(first_push_empty)
788}
789
790#[derive(Debug, Clone, Copy, PartialEq, Eq)]
792pub enum Bip147Network {
793 Mainnet,
794 Testnet,
795 Regtest,
796}
797
798#[cfg(test)]
799mod tests {
800 use super::*;
801 use crate::constants::BIP147_ACTIVATION_MAINNET;
802
803 use crate::opcodes::{OP_0, OP_1, OP_2, OP_CHECKMULTISIG, OP_CHECKSIG};
804
805 #[test]
806 fn bip34_height_1_through_16_is_a_single_opcode() {
807 let block1: &[u8] = &[0x51, 0x00, 0x06, 0x2f, 0x40, 0x77, 0x69, 0x7a, 0x2f];
809 assert!(script_sig_starts_with_height(block1, 1));
810 assert!(!script_sig_starts_with_height(&[0x01, 0x01], 1));
811 assert!(script_sig_starts_with_height(&[0x52, 0x00], 2));
812 assert!(script_sig_starts_with_height(&[0x60], 16));
813 assert!(script_sig_starts_with_height(&[0x01, 0x11, 0xff], 17));
815 assert!(!script_sig_starts_with_height(&[0x51], 17));
816 assert!(script_sig_starts_with_height(&[0x00], 0));
817 assert_eq!(OP_1, 0x51);
818 }
819
820 #[test]
821 fn encode_bip34_coinbase_script_satisfies_the_prefix_check() {
822 for height in [0u64, 1, 16, 17, 127, 128, 150, 255, 256, 210_000] {
823 let script = encode_bip34_coinbase_script(height);
824 assert!(
825 script_sig_starts_with_height(&script, height),
826 "height {height} script {script:?}"
827 );
828 assert!(
829 (2..=100).contains(&script.len()),
830 "height {height} len {}",
831 script.len()
832 );
833 }
834 assert_eq!(encode_bip34_coinbase_script(0), vec![0x00, 0xff]);
835 assert_eq!(encode_bip34_coinbase_script(1), vec![0x51, 0xff]);
836 assert_eq!(encode_bip34_coinbase_script(16), vec![0x60, 0xff]);
837 assert_eq!(encode_bip34_coinbase_script(17), vec![0x01, 0x11]);
838 assert_eq!(encode_bip34_coinbase_script(127), vec![0x01, 0x7f]);
839 assert_eq!(encode_bip34_coinbase_script(128), vec![0x02, 0x80, 0x00]);
840 assert_eq!(encode_bip34_coinbase_script(150), vec![0x02, 0x96, 0x00]);
841 assert_eq!(encode_bip34_coinbase_script(255), vec![0x02, 0xff, 0x00]);
842 assert_eq!(encode_bip34_coinbase_script(256), vec![0x02, 0x00, 0x01]);
843 assert_eq!(
844 encode_bip34_coinbase_script(210_000),
845 vec![0x03, 0x50, 0x34, 0x03]
846 );
847 }
848
849 #[test]
850 fn test_bip30_repeat_hashes_are_display_ids_reversed() {
851 fn digest(display: &str) -> Hash {
852 let mut bytes = hex::decode(display).unwrap();
853 bytes.reverse();
854 bytes.try_into().unwrap()
855 }
856 assert!(is_bip30_repeat_block(
857 91_842,
858 &digest("00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")
859 ));
860 assert!(is_bip30_repeat_block(
861 91_880,
862 &digest("00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")
863 ));
864 assert!(!is_bip30_repeat_block(91_842, &[0; 32]));
865 assert!(!is_bip30_repeat_block(
866 91_722,
867 &digest("00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")
868 ));
869 assert!(is_known_bip34_block(
870 Network::Mainnet,
871 crate::constants::BIP34_ACTIVATION_MAINNET,
872 &digest("000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8")
873 ));
874 assert!(!is_known_bip34_block(
875 Network::Mainnet,
876 crate::constants::BIP34_ACTIVATION_MAINNET,
877 &[0; 32]
878 ));
879 }
880
881 #[test]
882 fn test_bip30_basic() {
883 let transactions: Vec<Transaction> = vec![Transaction {
885 version: 1,
886 inputs: vec![TransactionInput {
887 prevout: OutPoint {
888 hash: [0; 32],
889 index: 0xffffffff,
890 },
891 script_sig: vec![0x04, 0x00, 0x00, 0x00, 0x00],
892 sequence: 0xffffffff,
893 }]
894 .into(),
895 outputs: vec![TransactionOutput {
896 value: 50_0000_0000,
897 script_pubkey: vec![],
898 }]
899 .into(),
900 lock_time: 0,
901 }];
902 let block = Block {
903 header: BlockHeader {
904 version: 1,
905 prev_block_hash: [0; 32],
906 merkle_root: [0; 32],
907 timestamp: 1231006505,
908 bits: 0x1d00ffff,
909 nonce: 0,
910 },
911 transactions: transactions.into_boxed_slice(),
912 };
913
914 let utxo_set = UtxoSet::default();
915 let result = check_bip30_network(
916 &block,
917 &utxo_set,
918 None,
919 0,
920 crate::types::Network::Mainnet,
921 None,
922 )
923 .unwrap();
924 assert!(result, "BIP30 should pass for new coinbase");
925 }
926
927 #[test]
928 fn test_bip34_before_activation() {
929 let transactions: Vec<Transaction> = vec![Transaction {
930 version: 1,
931 inputs: vec![TransactionInput {
932 prevout: OutPoint {
933 hash: [0; 32],
934 index: 0xffffffff,
935 },
936 script_sig: vec![0x04, 0x00, 0x00, 0x00, 0x00],
937 sequence: 0xffffffff,
938 }]
939 .into(),
940 outputs: vec![TransactionOutput {
941 value: 50_0000_0000,
942 script_pubkey: vec![],
943 }]
944 .into(),
945 lock_time: 0,
946 }];
947 let block = Block {
948 header: BlockHeader {
949 version: 1,
950 prev_block_hash: [0; 32],
951 merkle_root: [0; 32],
952 timestamp: 1231006505,
953 bits: 0x1d00ffff,
954 nonce: 0,
955 },
956 transactions: transactions.into_boxed_slice(),
957 };
958
959 let result = check_bip34_network(&block, 100_000, crate::types::Network::Mainnet).unwrap();
961 assert!(result, "BIP34 should pass before activation");
962 }
963
964 #[test]
965 fn test_bip34_after_activation() {
966 let height = crate::constants::BIP34_ACTIVATION_MAINNET;
967 let transactions: Vec<Transaction> = vec![Transaction {
968 version: 1,
969 inputs: vec![TransactionInput {
970 prevout: OutPoint {
971 hash: [0; 32],
972 index: 0xffffffff,
973 },
974 script_sig: vec![
976 0x03,
977 (height & 0xff) as u8,
978 ((height >> 8) & 0xff) as u8,
979 ((height >> 16) & 0xff) as u8,
980 ],
981 sequence: 0xffffffff,
982 }]
983 .into(),
984 outputs: vec![TransactionOutput {
985 value: 50_0000_0000,
986 script_pubkey: vec![],
987 }]
988 .into(),
989 lock_time: 0,
990 }];
991 let block = Block {
992 header: BlockHeader {
993 version: 2, prev_block_hash: [0; 32],
995 merkle_root: [0; 32],
996 timestamp: 1231006505,
997 bits: 0x1d00ffff,
998 nonce: 0,
999 },
1000 transactions: transactions.into_boxed_slice(),
1001 };
1002
1003 let result = check_bip34_network(&block, height, crate::types::Network::Mainnet).unwrap();
1004 assert!(result, "BIP34 should pass with correct height encoding");
1005 }
1006
1007 #[test]
1008 fn test_bip90_version_enforcement() {
1009 let result = check_bip90_network(1, 100_000, crate::types::Network::Mainnet).unwrap();
1011 assert!(result, "Version 1 should be valid before BIP34");
1012
1013 let result = check_bip90_network(
1015 1,
1016 crate::constants::BIP34_ACTIVATION_MAINNET,
1017 crate::types::Network::Mainnet,
1018 )
1019 .unwrap();
1020 assert!(
1021 !result,
1022 "Version 1 should be invalid after BIP34 activation"
1023 );
1024
1025 let result = check_bip90_network(
1027 2,
1028 crate::constants::BIP34_ACTIVATION_MAINNET,
1029 crate::types::Network::Mainnet,
1030 )
1031 .unwrap();
1032 assert!(result, "Version 2 should be valid after BIP34 activation");
1033
1034 let result = check_bip90_network(2, 363_725, crate::types::Network::Mainnet).unwrap();
1037 assert!(
1038 !result,
1039 "Version 2 should be invalid after BIP66 activation"
1040 );
1041
1042 let result = check_bip90_network(3, 363_725, crate::types::Network::Mainnet).unwrap();
1045 assert!(result, "Version 3 should be valid after BIP66 activation");
1046 }
1047
1048 #[test]
1049 fn test_bip30_duplicate_coinbase() {
1050 use crate::block::calculate_tx_id;
1051
1052 let coinbase_tx = Transaction {
1054 version: 1,
1055 inputs: vec![TransactionInput {
1056 prevout: OutPoint {
1057 hash: [0; 32],
1058 index: 0xffffffff,
1059 },
1060 script_sig: vec![0x04, 0x00, 0x00, 0x00, 0x00],
1061 sequence: 0xffffffff,
1062 }]
1063 .into(),
1064 outputs: vec![TransactionOutput {
1065 value: 50_0000_0000,
1066 script_pubkey: vec![],
1067 }]
1068 .into(),
1069 lock_time: 0,
1070 };
1071
1072 let txid = calculate_tx_id(&coinbase_tx);
1073
1074 let mut utxo_set = UtxoSet::default();
1076 utxo_set.insert(
1077 OutPoint {
1078 hash: txid,
1079 index: 0,
1080 },
1081 std::sync::Arc::new(UTXO {
1082 value: 50_0000_0000,
1083 script_pubkey: vec![].into(),
1084 height: 0,
1085 is_coinbase: false,
1086 }),
1087 );
1088
1089 let transactions: Vec<Transaction> = vec![coinbase_tx];
1091 let block = Block {
1092 header: BlockHeader {
1093 version: 1,
1094 prev_block_hash: [0; 32],
1095 merkle_root: [0; 32],
1096 timestamp: 1231006505,
1097 bits: 0x1d00ffff,
1098 nonce: 0,
1099 },
1100 transactions: transactions.into_boxed_slice(),
1101 };
1102
1103 let result = check_bip30_network(
1105 &block,
1106 &utxo_set,
1107 None,
1108 0,
1109 crate::types::Network::Mainnet,
1110 None,
1111 )
1112 .unwrap();
1113 assert!(!result, "BIP30 should fail for duplicate coinbase");
1114 }
1115
1116 #[test]
1117 fn test_bip34_invalid_height() {
1118 let height = crate::constants::BIP34_ACTIVATION_MAINNET;
1119 let transactions: Vec<Transaction> = vec![Transaction {
1120 version: 1,
1121 inputs: vec![TransactionInput {
1122 prevout: OutPoint {
1123 hash: [0; 32],
1124 index: 0xffffffff,
1125 },
1126 script_sig: vec![0x03, 0x00, 0x00, 0x00], sequence: 0xffffffff,
1129 }]
1130 .into(),
1131 outputs: vec![TransactionOutput {
1132 value: 50_0000_0000,
1133 script_pubkey: vec![],
1134 }]
1135 .into(),
1136 lock_time: 0,
1137 }];
1138 let block = Block {
1139 header: BlockHeader {
1140 version: 2,
1141 prev_block_hash: [0; 32],
1142 merkle_root: [0; 32],
1143 timestamp: 1231006505,
1144 bits: 0x1d00ffff,
1145 nonce: 0,
1146 },
1147 transactions: transactions.into_boxed_slice(),
1148 };
1149
1150 let result = check_bip34_network(&block, height, crate::types::Network::Mainnet).unwrap();
1152 assert!(!result, "BIP34 should fail with incorrect height encoding");
1153 }
1154
1155 #[test]
1156 fn test_bip66_strict_der() {
1157 use crate::constants::BIP66_ACTIVATION_MAINNET;
1158 use crate::types::Network;
1159
1160 let accept: Vec<u8> = vec![0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01];
1162 assert_eq!(
1163 check_bip66_network(&accept, BIP66_ACTIVATION_MAINNET, Network::Mainnet).unwrap(),
1164 true,
1165 "9-byte strict DER must pass at activation"
1166 );
1167
1168 let mut high_bit_r = accept.clone();
1169 high_bit_r[4] = 0x81;
1170 let leading_zero_r: Vec<u8> =
1172 vec![0x30, 0x07, 0x02, 0x02, 0x00, 0x01, 0x02, 0x01, 0x01, 0x01];
1173 let mut bad_length = accept.clone();
1174 bad_length[1] = 0x05;
1175 let rejects = [
1176 vec![0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01],
1177 vec![0u8; 74],
1178 high_bit_r.clone(),
1179 leading_zero_r,
1180 bad_length,
1181 ];
1182 for sig in &rejects {
1183 assert_eq!(
1184 check_bip66_network(sig, BIP66_ACTIVATION_MAINNET, Network::Mainnet).unwrap(),
1185 false,
1186 "non-strict encoding must fail at activation: {sig:?}"
1187 );
1188 }
1189
1190 assert_eq!(
1191 check_bip66_network(&high_bit_r, BIP66_ACTIVATION_MAINNET - 1, Network::Mainnet)
1192 .unwrap(),
1193 true,
1194 "pre-activation must accept a non-strict signature"
1195 );
1196 }
1197
1198 #[test]
1199 fn test_bip147_skips_checkmultisig_byte_in_push_data() {
1200 let script_pubkey = vec![0x01, OP_CHECKMULTISIG, OP_CHECKSIG];
1202 let script_sig = vec![1, OP_1];
1203 let result = check_bip147_network(
1204 &script_sig,
1205 &script_pubkey,
1206 BIP147_ACTIVATION_MAINNET,
1207 Bip147Network::Mainnet,
1208 )
1209 .unwrap();
1210 assert!(
1211 result,
1212 "BIP147 must not apply when CHECKMULTISIG is only in push data"
1213 );
1214 }
1215
1216 #[test]
1217 fn test_bip147_null_dummy() {
1218 let script_pubkey = vec![OP_CHECKMULTISIG];
1220 let script_sig_valid = vec![OP_0];
1221 let result = check_bip147_network(
1222 &script_sig_valid,
1223 &script_pubkey,
1224 BIP147_ACTIVATION_MAINNET,
1225 Bip147Network::Mainnet,
1226 )
1227 .unwrap();
1228 assert!(result, "BIP147 should pass with NULLDUMMY");
1229
1230 let script_sig_invalid = vec![1, OP_1];
1231 let result = check_bip147_network(
1232 &script_sig_invalid,
1233 &script_pubkey,
1234 BIP147_ACTIVATION_MAINNET,
1235 Bip147Network::Mainnet,
1236 )
1237 .unwrap();
1238 assert!(!result, "BIP147 should fail without NULLDUMMY");
1239
1240 let result = check_bip147_network(
1242 &script_sig_invalid,
1243 &script_pubkey,
1244 100_000,
1245 Bip147Network::Mainnet,
1246 )
1247 .unwrap();
1248 assert!(result, "BIP147 should pass before activation");
1249 }
1250}