rustywallet_descriptor/
script.rs1use crate::descriptor::Descriptor;
6use crate::error::DescriptorError;
7use rustywallet_keys::public_key::{PublicKey, PublicKeyFormat};
8use sha2::{Sha256, Digest};
9
10#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct ScriptPubkey {
13 pub script: Vec<u8>,
15 pub script_type: ScriptType,
17}
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum ScriptType {
22 P2pk,
24 P2pkh,
26 P2sh,
28 P2wpkh,
30 P2wsh,
32 P2tr,
34}
35
36impl ScriptPubkey {
37 pub fn p2pk(pubkey: &PublicKey) -> Self {
39 let pk_bytes = hex::decode(pubkey.to_hex(PublicKeyFormat::Compressed)).unwrap();
40 let mut script = Vec::with_capacity(pk_bytes.len() + 2);
41 script.push(pk_bytes.len() as u8); script.extend_from_slice(&pk_bytes);
43 script.push(0xac); Self {
46 script,
47 script_type: ScriptType::P2pk,
48 }
49 }
50
51 pub fn p2pkh(pubkey: &PublicKey) -> Self {
53 let pk_bytes = hex::decode(pubkey.to_hex(PublicKeyFormat::Compressed)).unwrap();
54 let pubkey_hash = hash160(&pk_bytes);
55
56 let mut script = Vec::with_capacity(25);
57 script.push(0x76); script.push(0xa9); script.push(0x14); script.extend_from_slice(&pubkey_hash);
61 script.push(0x88); script.push(0xac); Self {
65 script,
66 script_type: ScriptType::P2pkh,
67 }
68 }
69
70 pub fn p2sh(redeem_script: &[u8]) -> Self {
72 let script_hash = hash160(redeem_script);
73
74 let mut script = Vec::with_capacity(23);
75 script.push(0xa9); script.push(0x14); script.extend_from_slice(&script_hash);
78 script.push(0x87); Self {
81 script,
82 script_type: ScriptType::P2sh,
83 }
84 }
85
86 pub fn p2wpkh(pubkey: &PublicKey) -> Self {
88 let pk_bytes = hex::decode(pubkey.to_hex(PublicKeyFormat::Compressed)).unwrap();
89 let pubkey_hash = hash160(&pk_bytes);
90
91 let mut script = Vec::with_capacity(22);
92 script.push(0x00); script.push(0x14); script.extend_from_slice(&pubkey_hash);
95
96 Self {
97 script,
98 script_type: ScriptType::P2wpkh,
99 }
100 }
101
102 pub fn p2wsh(witness_script: &[u8]) -> Self {
104 let script_hash = sha256(witness_script);
105
106 let mut script = Vec::with_capacity(34);
107 script.push(0x00); script.push(0x20); script.extend_from_slice(&script_hash);
110
111 Self {
112 script,
113 script_type: ScriptType::P2wsh,
114 }
115 }
116
117 pub fn p2tr(output_key: &[u8; 32]) -> Self {
119 let mut script = Vec::with_capacity(34);
120 script.push(0x51); script.push(0x20); script.extend_from_slice(output_key);
123
124 Self {
125 script,
126 script_type: ScriptType::P2tr,
127 }
128 }
129
130 pub fn multisig(threshold: usize, pubkeys: &[PublicKey]) -> Result<Vec<u8>, DescriptorError> {
132 if threshold == 0 || threshold > pubkeys.len() || pubkeys.len() > 16 {
133 return Err(DescriptorError::InvalidThreshold {
134 k: threshold,
135 n: pubkeys.len(),
136 });
137 }
138
139 let mut script = Vec::new();
140
141 script.push(0x50 + threshold as u8); for pk in pubkeys {
146 let pk_bytes = hex::decode(pk.to_hex(PublicKeyFormat::Compressed)).unwrap();
147 script.push(pk_bytes.len() as u8);
148 script.extend_from_slice(&pk_bytes);
149 }
150
151 script.push(0x50 + pubkeys.len() as u8);
153
154 script.push(0xae);
156
157 Ok(script)
158 }
159
160 pub fn as_bytes(&self) -> &[u8] {
162 &self.script
163 }
164
165 pub fn script_type(&self) -> ScriptType {
167 self.script_type
168 }
169
170 pub fn is_witness(&self) -> bool {
172 matches!(
173 self.script_type,
174 ScriptType::P2wpkh | ScriptType::P2wsh | ScriptType::P2tr
175 )
176 }
177}
178
179pub fn generate_script_pubkey(
181 descriptor: &Descriptor,
182 index: u32,
183) -> Result<ScriptPubkey, DescriptorError> {
184 match descriptor {
185 Descriptor::Pk(key) => {
186 let pubkey = key.derive_public_key(index)?;
187 Ok(ScriptPubkey::p2pk(&pubkey))
188 }
189 Descriptor::Pkh(key) => {
190 let pubkey = key.derive_public_key(index)?;
191 Ok(ScriptPubkey::p2pkh(&pubkey))
192 }
193 Descriptor::Wpkh(key) => {
194 let pubkey = key.derive_public_key(index)?;
195 Ok(ScriptPubkey::p2wpkh(&pubkey))
196 }
197 Descriptor::Sh(inner) => {
198 let inner_script = generate_redeem_script(inner, index)?;
200 Ok(ScriptPubkey::p2sh(&inner_script))
201 }
202 Descriptor::Wsh(inner) => {
203 let witness_script = generate_witness_script(inner, index)?;
205 Ok(ScriptPubkey::p2wsh(&witness_script))
206 }
207 Descriptor::Tr(key) => {
208 let pubkey = key.derive_public_key(index)?;
209 let pk_hex = pubkey.to_hex(PublicKeyFormat::Compressed);
211 let pk_bytes = hex::decode(&pk_hex).unwrap();
212 let mut xonly = [0u8; 32];
213 xonly.copy_from_slice(&pk_bytes[1..33]); Ok(ScriptPubkey::p2tr(&xonly))
215 }
216 Descriptor::Multi { threshold, keys } => {
217 let pubkeys: Result<Vec<_>, _> = keys
218 .iter()
219 .map(|k| k.derive_public_key(index))
220 .collect();
221 let pubkeys = pubkeys?;
222 let script = ScriptPubkey::multisig(*threshold, &pubkeys)?;
223 Ok(ScriptPubkey::p2sh(&script))
225 }
226 Descriptor::SortedMulti { threshold, keys } => {
227 let mut pubkeys: Vec<_> = keys
228 .iter()
229 .map(|k| k.derive_public_key(index))
230 .collect::<Result<Vec<_>, _>>()?;
231 pubkeys.sort_by(|a, b| {
233 let a_hex = a.to_hex(PublicKeyFormat::Compressed);
234 let b_hex = b.to_hex(PublicKeyFormat::Compressed);
235 a_hex.cmp(&b_hex)
236 });
237 let script = ScriptPubkey::multisig(*threshold, &pubkeys)?;
238 Ok(ScriptPubkey::p2sh(&script))
240 }
241 }
242}
243
244fn generate_redeem_script(descriptor: &Descriptor, index: u32) -> Result<Vec<u8>, DescriptorError> {
246 match descriptor {
247 Descriptor::Wpkh(key) => {
248 let pubkey = key.derive_public_key(index)?;
250 let script = ScriptPubkey::p2wpkh(&pubkey);
251 Ok(script.script)
252 }
253 Descriptor::Wsh(inner) => {
254 let witness_script = generate_witness_script(inner, index)?;
256 let script = ScriptPubkey::p2wsh(&witness_script);
257 Ok(script.script)
258 }
259 Descriptor::Multi { threshold, keys } => {
260 let pubkeys: Result<Vec<_>, _> = keys
261 .iter()
262 .map(|k| k.derive_public_key(index))
263 .collect();
264 ScriptPubkey::multisig(*threshold, &pubkeys?)
265 }
266 Descriptor::SortedMulti { threshold, keys } => {
267 let mut pubkeys: Vec<_> = keys
268 .iter()
269 .map(|k| k.derive_public_key(index))
270 .collect::<Result<Vec<_>, _>>()?;
271 pubkeys.sort_by(|a, b| {
272 let a_hex = a.to_hex(PublicKeyFormat::Compressed);
273 let b_hex = b.to_hex(PublicKeyFormat::Compressed);
274 a_hex.cmp(&b_hex)
275 });
276 ScriptPubkey::multisig(*threshold, &pubkeys)
277 }
278 _ => Err(DescriptorError::ScriptError(format!(
279 "Cannot create redeem script for {}",
280 descriptor.descriptor_type()
281 ))),
282 }
283}
284
285fn generate_witness_script(descriptor: &Descriptor, index: u32) -> Result<Vec<u8>, DescriptorError> {
287 match descriptor {
288 Descriptor::Multi { threshold, keys } => {
289 let pubkeys: Result<Vec<_>, _> = keys
290 .iter()
291 .map(|k| k.derive_public_key(index))
292 .collect();
293 ScriptPubkey::multisig(*threshold, &pubkeys?)
294 }
295 Descriptor::SortedMulti { threshold, keys } => {
296 let mut pubkeys: Vec<_> = keys
297 .iter()
298 .map(|k| k.derive_public_key(index))
299 .collect::<Result<Vec<_>, _>>()?;
300 pubkeys.sort_by(|a, b| {
301 let a_hex = a.to_hex(PublicKeyFormat::Compressed);
302 let b_hex = b.to_hex(PublicKeyFormat::Compressed);
303 a_hex.cmp(&b_hex)
304 });
305 ScriptPubkey::multisig(*threshold, &pubkeys)
306 }
307 _ => Err(DescriptorError::ScriptError(format!(
308 "Cannot create witness script for {}",
309 descriptor.descriptor_type()
310 ))),
311 }
312}
313
314fn hash160(data: &[u8]) -> [u8; 20] {
316 use ripemd::Ripemd160;
317
318 let sha = Sha256::digest(data);
319 let ripemd = Ripemd160::digest(sha);
320
321 let mut result = [0u8; 20];
322 result.copy_from_slice(&ripemd);
323 result
324}
325
326fn sha256(data: &[u8]) -> [u8; 32] {
328 let hash = Sha256::digest(data);
329 let mut result = [0u8; 32];
330 result.copy_from_slice(&hash);
331 result
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337
338 #[test]
339 fn test_p2pkh_script() {
340 let pubkey_hex = "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5";
341 let pubkey = PublicKey::from_hex(pubkey_hex).unwrap();
342
343 let script = ScriptPubkey::p2pkh(&pubkey);
344
345 assert_eq!(script.script_type, ScriptType::P2pkh);
346 assert_eq!(script.script.len(), 25);
347 assert_eq!(script.script[0], 0x76); assert_eq!(script.script[1], 0xa9); }
350
351 #[test]
352 fn test_p2wpkh_script() {
353 let pubkey_hex = "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5";
354 let pubkey = PublicKey::from_hex(pubkey_hex).unwrap();
355
356 let script = ScriptPubkey::p2wpkh(&pubkey);
357
358 assert_eq!(script.script_type, ScriptType::P2wpkh);
359 assert_eq!(script.script.len(), 22);
360 assert_eq!(script.script[0], 0x00); assert_eq!(script.script[1], 0x14); }
363
364 #[test]
365 fn test_p2tr_script() {
366 let output_key = [0x42u8; 32];
367 let script = ScriptPubkey::p2tr(&output_key);
368
369 assert_eq!(script.script_type, ScriptType::P2tr);
370 assert_eq!(script.script.len(), 34);
371 assert_eq!(script.script[0], 0x51); assert_eq!(script.script[1], 0x20); }
374
375 #[test]
376 fn test_multisig_script() {
377 let pk1_hex = "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5";
378 let pk2_hex = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798";
379
380 let pk1 = PublicKey::from_hex(pk1_hex).unwrap();
381 let pk2 = PublicKey::from_hex(pk2_hex).unwrap();
382
383 let script = ScriptPubkey::multisig(2, &[pk1, pk2]).unwrap();
384
385 assert_eq!(script[0], 0x52); assert_eq!(*script.last().unwrap(), 0xae); }
388}