1use sha2::{Sha256, Digest};
4use ripemd::Ripemd160;
5
6pub mod opcodes {
8 pub const OP_0: u8 = 0x00;
9 pub const OP_PUSHBYTES_20: u8 = 0x14;
10 pub const OP_PUSHBYTES_32: u8 = 0x20;
11 pub const OP_DUP: u8 = 0x76;
12 pub const OP_HASH160: u8 = 0xa9;
13 pub const OP_EQUALVERIFY: u8 = 0x88;
14 pub const OP_CHECKSIG: u8 = 0xac;
15 pub const OP_EQUAL: u8 = 0x87;
16}
17
18pub fn build_p2pkh_script(pubkey_hash: &[u8; 20]) -> Vec<u8> {
22 let mut script = Vec::with_capacity(25);
23 script.push(opcodes::OP_DUP);
24 script.push(opcodes::OP_HASH160);
25 script.push(opcodes::OP_PUSHBYTES_20);
26 script.extend_from_slice(pubkey_hash);
27 script.push(opcodes::OP_EQUALVERIFY);
28 script.push(opcodes::OP_CHECKSIG);
29 script
30}
31
32pub fn build_p2wpkh_script(pubkey_hash: &[u8; 20]) -> Vec<u8> {
36 let mut script = Vec::with_capacity(22);
37 script.push(opcodes::OP_0);
38 script.push(opcodes::OP_PUSHBYTES_20);
39 script.extend_from_slice(pubkey_hash);
40 script
41}
42
43pub fn build_p2tr_script(x_only_pubkey: &[u8; 32]) -> Vec<u8> {
47 let mut script = Vec::with_capacity(34);
48 script.push(0x51); script.push(opcodes::OP_PUSHBYTES_32);
50 script.extend_from_slice(x_only_pubkey);
51 script
52}
53
54pub fn hash160(data: &[u8]) -> [u8; 20] {
56 let sha = Sha256::digest(data);
57 let ripemd = Ripemd160::digest(sha);
58 ripemd.into()
59}
60
61pub fn p2pkh_script_from_pubkey(pubkey: &[u8; 33]) -> Vec<u8> {
63 let pubkey_hash = hash160(pubkey);
64 build_p2pkh_script(&pubkey_hash)
65}
66
67pub fn p2wpkh_script_from_pubkey(pubkey: &[u8; 33]) -> Vec<u8> {
69 let pubkey_hash = hash160(pubkey);
70 build_p2wpkh_script(&pubkey_hash)
71}
72
73pub fn extract_p2pkh_hash(script: &[u8]) -> Option<[u8; 20]> {
75 if script.len() == 25
76 && script[0] == opcodes::OP_DUP
77 && script[1] == opcodes::OP_HASH160
78 && script[2] == opcodes::OP_PUSHBYTES_20
79 && script[23] == opcodes::OP_EQUALVERIFY
80 && script[24] == opcodes::OP_CHECKSIG
81 {
82 let mut hash = [0u8; 20];
83 hash.copy_from_slice(&script[3..23]);
84 Some(hash)
85 } else {
86 None
87 }
88}
89
90pub fn extract_p2wpkh_hash(script: &[u8]) -> Option<[u8; 20]> {
92 if script.len() == 22
93 && script[0] == opcodes::OP_0
94 && script[1] == opcodes::OP_PUSHBYTES_20
95 {
96 let mut hash = [0u8; 20];
97 hash.copy_from_slice(&script[2..22]);
98 Some(hash)
99 } else {
100 None
101 }
102}
103
104pub fn is_p2pkh(script: &[u8]) -> bool {
106 extract_p2pkh_hash(script).is_some()
107}
108
109pub fn is_p2wpkh(script: &[u8]) -> bool {
111 extract_p2wpkh_hash(script).is_some()
112}
113
114pub fn is_p2tr(script: &[u8]) -> bool {
116 script.len() == 34 && script[0] == 0x51 && script[1] == opcodes::OP_PUSHBYTES_32
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_build_p2pkh_script() {
125 let hash = [0u8; 20];
126 let script = build_p2pkh_script(&hash);
127 assert_eq!(script.len(), 25);
128 assert!(is_p2pkh(&script));
129 }
130
131 #[test]
132 fn test_build_p2wpkh_script() {
133 let hash = [0u8; 20];
134 let script = build_p2wpkh_script(&hash);
135 assert_eq!(script.len(), 22);
136 assert!(is_p2wpkh(&script));
137 }
138
139 #[test]
140 fn test_build_p2tr_script() {
141 let pubkey = [0u8; 32];
142 let script = build_p2tr_script(&pubkey);
143 assert_eq!(script.len(), 34);
144 assert!(is_p2tr(&script));
145 }
146
147 #[test]
148 fn test_extract_p2pkh_hash() {
149 let hash = [1u8; 20];
150 let script = build_p2pkh_script(&hash);
151 let extracted = extract_p2pkh_hash(&script).unwrap();
152 assert_eq!(extracted, hash);
153 }
154}