bitcoin_rs_script/
sigops.rs1use bitcoin::{Block, Script};
2
3pub fn count_legacy(script: &Script) -> u32 {
5 saturating_u32(script.count_sigops_legacy())
6}
7
8pub fn count_segwit(script: &Script, witness: &[Vec<u8>]) -> u32 {
10 if script.is_p2wpkh() {
11 return 1;
12 }
13 if !script.is_p2wsh() {
14 return 0;
15 }
16 witness.last().map_or(0, |witness_script| {
17 saturating_u32(Script::from_bytes(witness_script).count_sigops())
18 })
19}
20
21pub const fn count_taproot(_script: &Script, _witness: &[Vec<u8>]) -> u32 {
27 0
28}
29
30pub fn count_block(block: &Block) -> u32 {
36 block.txdata.iter().fold(0u32, |count, tx| {
37 count.saturating_add(saturating_u32(tx.total_sigop_cost(|_| None)))
38 })
39}
40
41fn saturating_u32(value: usize) -> u32 {
42 u32::try_from(value).unwrap_or(u32::MAX)
43}
44
45#[cfg(test)]
46mod tests {
47 use bitcoin::blockdata::opcodes::all::{
48 OP_CHECKMULTISIG, OP_CHECKSIG, OP_PUSHNUM_1, OP_PUSHNUM_3,
49 };
50 use bitcoin::script::Builder;
51
52 use super::{count_legacy, count_segwit, count_taproot};
53
54 #[test]
55 fn legacy_count_matches_core_multisig_legacy_rule() {
56 let script = Builder::new()
57 .push_opcode(OP_PUSHNUM_1)
58 .push_slice([3; 33])
59 .push_slice([3; 33])
60 .push_slice([3; 33])
61 .push_opcode(OP_PUSHNUM_3)
62 .push_opcode(OP_CHECKMULTISIG)
63 .into_script();
64 assert_eq!(count_legacy(script.as_script()), 20);
65 assert_eq!(script.count_sigops(), 3);
66 }
67
68 #[test]
69 fn segwit_counts_p2wpkh_and_p2wsh_witness_script() {
70 let p2wpkh = Builder::new().push_int(0).push_slice([7; 20]).into_script();
71 assert_eq!(count_segwit(p2wpkh.as_script(), &[]), 1);
72
73 let witness_script = Builder::new().push_opcode(OP_CHECKSIG).into_script();
74 let p2wsh = Builder::new().push_int(0).push_slice([9; 32]).into_script();
75 assert_eq!(
76 count_segwit(p2wsh.as_script(), &[witness_script.as_bytes().to_vec()]),
77 1
78 );
79 }
80
81 #[test]
82 fn taproot_has_no_legacy_block_sigop_charge() {
83 let p2tr = Builder::new().push_int(1).push_slice([9; 32]).into_script();
84 assert_eq!(count_taproot(p2tr.as_script(), &[]), 0);
85 }
86}