1use crate::{hashes::blake2b160, types::Script};
4use coins_core::{
5 hashes::{Digest, DigestOutput, Sha3_256},
6 impl_hex_serde,
7 ser::{self, ByteFormat},
8 types::tx::RecipientIdentifier,
9};
10use std::io::{Read, Write};
11use thiserror::Error;
12
13coins_core::wrap_prefixed_byte_vector!(
14 WitnessStackItem
22);
23
24pub type Witness = Vec<WitnessStackItem>;
30
31#[derive(Debug, Error)]
33pub enum LockingScriptError {
34 #[error("Invalid size of WitnessProgram")]
36 InvalidWitnessProgramSizeError,
37}
38
39coins_core::wrap_prefixed_byte_vector!(
40 WitnessProgram
46);
47
48impl WitnessProgram {
51 pub fn split(&self) -> (u8, Vec<u8>) {
54 let length = self.0.len();
55
56 let mut data = Vec::with_capacity(length);
57 data.clone_from_slice(&self.0[..]);
58
59 (length as u8, data)
60 }
61}
62
63impl From<[u8; 20]> for WitnessProgram {
64 fn from(v: [u8; 20]) -> Self {
65 Self::new(v.to_vec())
66 }
67}
68
69impl From<[u8; 32]> for WitnessProgram {
70 fn from(v: [u8; 32]) -> Self {
71 Self::new(v.to_vec())
72 }
73}
74
75impl From<DigestOutput<Sha3_256>> for WitnessProgram {
76 fn from(v: DigestOutput<Sha3_256>) -> Self {
77 Self::new(v.as_slice().to_vec())
78 }
79}
80
81impl From<WitnessProgram> for [u8; 20] {
82 fn from(w: WitnessProgram) -> [u8; 20] {
83 let mut data = [0; 20];
84 data.clone_from_slice(&w.0[..]);
85 data
86 }
87}
88
89impl From<WitnessProgram> for [u8; 32] {
90 fn from(w: WitnessProgram) -> [u8; 32] {
91 let mut data = [0; 32];
92 data.clone_from_slice(&w.0[..]);
93 data
94 }
95}
96
97impl From<WitnessProgram> for Vec<u8> {
98 fn from(w: WitnessProgram) -> Vec<u8> {
99 w.0[..].to_vec()
100 }
101}
102
103#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq)]
104pub struct LockingScript {
106 pub version: u8,
109 pub witness_program: WitnessProgram,
114}
115
116impl LockingScript {
117 pub fn null() -> Self {
119 Self {
120 version: 0,
121 witness_program: WitnessProgram(vec![0x00; 20]),
122 }
123 }
124
125 pub fn new(v: Vec<u8>) -> Result<Self, LockingScriptError> {
127 let (version_and_size, data) = v.split_at(2);
128 let version = version_and_size[0];
129 let size = version_and_size[1];
130
131 if size != data.len() as u8 {
132 return Err(LockingScriptError::InvalidWitnessProgramSizeError);
133 }
134
135 Ok(Self {
136 version,
137 witness_program: WitnessProgram::from(data),
138 })
139 }
140}
141
142impl coins_core::ser::ByteFormat for LockingScript {
143 type Error = coins_core::ser::SerError;
144
145 fn serialized_length(&self) -> usize {
146 let mut length = 2; length += self.witness_program.len() as usize;
148 length
149 }
150
151 fn read_from<R>(reader: &mut R) -> Result<Self, Self::Error>
152 where
153 R: Read,
154 {
155 let mut version = [0; 1];
156 reader.read_exact(&mut version)?;
157
158 Ok(LockingScript {
159 version: version[0],
160 witness_program: ser::read_prefix_vec(reader)?.into(),
161 })
162 }
163
164 fn write_to<W>(&self, writer: &mut W) -> Result<usize, <Self as ByteFormat>::Error>
165 where
166 W: Write,
167 {
168 let mut total: usize = 0;
169 total += writer.write(&self.version.to_le_bytes())?;
170 total += &self.witness_program.write_to(writer)?;
171 Ok(total)
172 }
173}
174
175impl From<Vec<u8>> for LockingScript {
176 fn from(mut raw: Vec<u8>) -> Self {
177 let version = raw[0];
178 let witness_program = raw.split_off(2);
179
180 LockingScript {
181 version,
182 witness_program: WitnessProgram::from(witness_program),
183 }
184 }
185}
186
187impl RecipientIdentifier for LockingScript {}
188
189#[derive(PartialEq, Eq, Clone, Debug)]
191pub enum LockingScriptType {
192 Wpkh([u8; 20]),
194 Wsh([u8; 32]),
196 OpReturn(Vec<u8>),
198 NonStandard,
200}
201
202impl LockingScript {
203 pub fn p2wpkh<T>(key: &T) -> Self
205 where
206 T: AsRef<coins_bip32::ecdsa::VerifyingKey>,
207 {
208 Self {
209 version: 0,
210 witness_program: blake2b160(&key.as_ref().to_bytes()).into(),
211 }
212 }
213
214 pub fn p2wsh(script: &Script) -> Self {
216 let mut w = Sha3_256::default();
217 w.write_all(script.items()).expect("No i/o error");
218 let digest = w.finalize();
219
220 Self {
221 version: 0,
222 witness_program: digest.into(),
223 }
224 }
225
226 pub fn extract_op_return_data(&self) -> Option<Vec<u8>> {
229 if self.version != 31 {
230 return None;
231 }
232
233 if self.witness_program.len() < 2 || self.witness_program.len() > 40 {
234 return None;
235 }
236
237 let mut v: Vec<u8> = vec![];
238 v.extend(self.witness_program.clone());
239
240 Some(v)
241 }
242
243 pub fn standard_type(&self) -> Result<LockingScriptType, LockingScriptError> {
246 if self.version == 31 {
247 return Ok(LockingScriptType::OpReturn(
248 self.witness_program.clone().into(),
249 ));
250 }
251
252 if self.version == 0 {
253 match self.witness_program.len() {
254 20 => {
255 let mut wpkh = [0x00; 20];
256 wpkh.copy_from_slice(self.witness_program.items());
257 return Ok(LockingScriptType::Wpkh(wpkh));
258 }
259
260 32 => {
261 let mut wsh = [0x00; 32];
262 wsh.copy_from_slice(self.witness_program.items());
263 return Ok(LockingScriptType::Wsh(wsh));
264 }
265 _ => return Err(LockingScriptError::InvalidWitnessProgramSizeError),
266 }
267 }
268
269 Ok(LockingScriptType::NonStandard)
271 }
272}
273
274#[cfg(test)]
275mod test {
276 use super::*;
277 use coins_bip32::prelude::XPriv;
278 use coins_core::ser::ByteFormat;
279
280 #[test]
281 fn it_creates_null_locking_script() {
282 let script = LockingScript::null();
283
284 assert_eq!(script.version, 0);
285 assert_eq!(script.witness_program, WitnessProgram(vec![00; 20]));
286 }
287
288 #[test]
289 fn it_generates_p2wpkh_locking_script() {
290 let xpriv_str = "xprv9s21ZrQH143K24iSk4AuKHKkRzWQBqPHV3bB7n1fFxQxitybVXAixpB72Um9DhrNumiR9YAmmXvPCdqM8s1XMM2inRiCvgND9cy7uHs1FCa";
291 let xpriv: XPriv = xpriv_str.parse().unwrap();
292 let xpub = xpriv.verify_key();
293
294 let pubkey = xpriv.verify_key();
295 let mut vec = Vec::new();
296 vec.extend(&pubkey.to_bytes());
297 assert_eq!(
298 "026180c26fb38078b5d5c717cd70e4b774f4ef56b8ae994599764a9156909aa437",
299 hex::encode(vec)
300 );
301
302 let p2wpkh = LockingScript::p2wpkh(&xpub);
303 assert_eq!(
304 "c5b0e4d623918b128716e588781cc277b003cda2",
305 hex::encode(p2wpkh.clone().witness_program)
306 );
307
308 let expected = LockingScript {
309 version: 0,
310 witness_program: hex::decode("c5b0e4d623918b128716e588781cc277b003cda2")
311 .unwrap()
312 .into(),
313 };
314
315 assert_eq!(expected, p2wpkh);
316 }
317
318 #[test]
319 fn it_generates_p2wsh_locking_script() {
320 let script = hex::decode("0087635168").unwrap();
322 let locking_script = LockingScript::p2wsh(&script.into());
323
324 let expect = "fdeefecb572acb4b4a86f568deb19bf8c872cce555d4e234e3a36235de2588d7";
326 assert_eq!(expect, hex::encode(locking_script.witness_program));
327 }
328
329 #[test]
330 fn it_serialized_locking_script() {
331 let hash = hex::decode("ae42d6793bd518239c1788ff28e7ed0c9ed06e56").unwrap();
332
333 let script = LockingScript {
334 version: 0,
335 witness_program: hash.into(),
336 };
337
338 let hex = script.serialize_hex();
339 assert_eq!(hex, "0014ae42d6793bd518239c1788ff28e7ed0c9ed06e56");
341 }
342
343 #[test]
344 fn it_creates_witness_program_from_slice_u8_20() {
345 let witness_program = WitnessProgram::from([
346 0x62, 0xf4, 0x40, 0xc8, 0xea, 0x82, 0x6c, 0x59, 0x6a, 0x6f, 0x89, 0x39, 0x42, 0x43,
347 0x59, 0x90, 0x30, 0xd3, 0xb2, 0x21,
348 ]);
349
350 assert_eq!(
351 hex::encode(witness_program.0.clone()),
352 "62f440c8ea826c596a6f89394243599030d3b221"
353 );
354
355 let mut prefix_actual = vec![];
356 witness_program.write_to(&mut prefix_actual).unwrap();
357
358 assert_eq!(
359 hex::encode(prefix_actual),
360 "1462f440c8ea826c596a6f89394243599030d3b221"
361 );
362 }
363
364 #[test]
365 fn it_creates_witness_program_from_slice_u8_32() {
366 let witness_program = WitnessProgram::from([
367 0xe3, 0xcd, 0x22, 0x5e, 0xdd, 0xa8, 0x5b, 0x9b, 0xda, 0x94, 0x7a, 0x5c, 0x4c, 0xe0,
368 0x8e, 0x9d, 0x4d, 0x1e, 0x11, 0x90, 0xc2, 0x47, 0x03, 0xf7, 0x56, 0x8e, 0x8e, 0x83,
369 0x37, 0xfc, 0x7e, 0x34,
370 ]);
371
372 assert_eq!(
373 hex::encode(witness_program.0.clone()),
374 "e3cd225edda85b9bda947a5c4ce08e9d4d1e1190c24703f7568e8e8337fc7e34"
375 );
376
377 let mut prefix_actual = vec![];
378 witness_program.write_to(&mut prefix_actual).unwrap();
379
380 assert_eq!(
381 hex::encode(prefix_actual),
382 "20e3cd225edda85b9bda947a5c4ce08e9d4d1e1190c24703f7568e8e8337fc7e34"
383 );
384 }
385
386 #[test]
387 fn it_creates_slice_u8_20_from_witness_program() {
388 let expected = [
389 0x62, 0xf4, 0x40, 0xc8, 0xea, 0x82, 0x6c, 0x59, 0x6a, 0x6f, 0x89, 0x39, 0x42, 0x43,
390 0x59, 0x90, 0x30, 0xd3, 0xb2, 0x21,
391 ];
392
393 let witness_program = WitnessProgram::from(expected);
394 let actual: [u8; 20] = witness_program.into();
395
396 assert_eq!(expected, actual);
397 }
398
399 #[test]
400 fn it_creates_slice_u8_32_from_witness_program() {
401 let expected = [
402 0xe3, 0xcd, 0x22, 0x5e, 0xdd, 0xa8, 0x5b, 0x9b, 0xda, 0x94, 0x7a, 0x5c, 0x4c, 0xe0,
403 0x8e, 0x9d, 0x4d, 0x1e, 0x11, 0x90, 0xc2, 0x47, 0x03, 0xf7, 0x56, 0x8e, 0x8e, 0x83,
404 0x37, 0xfc, 0x7e, 0x34,
405 ];
406
407 let witness_program = WitnessProgram::from(expected);
408 let actual: [u8; 32] = witness_program.into();
409
410 assert_eq!(expected, actual);
411 }
412
413 #[test]
414 fn it_creates_vec_u8_20_from_witness_program() {
415 let expected = vec![
416 0x62, 0xf4, 0x40, 0xc8, 0xea, 0x82, 0x6c, 0x59, 0x6a, 0x6f, 0x89, 0x39, 0x42, 0x43,
417 0x59, 0x90, 0x30, 0xd3, 0xb2, 0x21,
418 ];
419
420 let witness_program = WitnessProgram::from(expected.clone());
421 let actual: Vec<u8> = witness_program.into();
422
423 assert_eq!(expected, actual);
424 }
425
426 #[test]
427 fn it_creates_vec_u8_32_from_witness_program() {
428 let expected = vec![
429 0xe3, 0xcd, 0x22, 0x5e, 0xdd, 0xa8, 0x5b, 0x9b, 0xda, 0x94, 0x7a, 0x5c, 0x4c, 0xe0,
430 0x8e, 0x9d, 0x4d, 0x1e, 0x11, 0x90, 0xc2, 0x47, 0x03, 0xf7, 0x56, 0x8e, 0x8e, 0x83,
431 0x37, 0xfc, 0x7e, 0x34,
432 ];
433
434 let witness_program = WitnessProgram::from(expected.clone());
435 let actual: Vec<u8> = witness_program.into();
436
437 assert_eq!(expected, actual);
438 }
439
440 #[test]
441 fn it_creates_locking_script_from_vec_u8() {
442 let version = 0x00;
443 let raw_witness_program = vec![
444 0xe3, 0xcd, 0x22, 0x5e, 0xdd, 0xa8, 0x5b, 0x9b, 0xda, 0x94, 0x7a, 0x5c, 0x4c, 0xe0,
445 0x8e, 0x9d, 0x4d, 0x1e, 0x11, 0x90, 0xc2, 0x47, 0x03, 0xf7, 0x56, 0x8e, 0x8e, 0x83,
446 0x37, 0xfc, 0x7e, 0x34,
447 ];
448
449 let raw_locking_script = vec![
450 0x00, 0x20, 0xe3, 0xcd, 0x22, 0x5e, 0xdd, 0xa8, 0x5b, 0x9b, 0xda, 0x94, 0x7a, 0x5c,
451 0x4c, 0xe0, 0x8e, 0x9d, 0x4d, 0x1e, 0x11, 0x90, 0xc2, 0x47, 0x03, 0xf7, 0x56, 0x8e,
452 0x8e, 0x83, 0x37, 0xfc, 0x7e, 0x34,
453 ];
454
455 let expected = LockingScript {
456 version,
457 witness_program: WitnessProgram::from(raw_witness_program),
458 };
459
460 let actual = LockingScript::from(raw_locking_script);
461 assert_eq!(actual, expected);
462 }
463}