handshakes/types/
lockingscript.rs

1//! Handshake LockingScript and WitnessProgram
2
3use 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    /// A WitnessStackItem is a marked `Vec<u8>` intended for use in witnesses. Each
15    /// Witness is a `PrefixVec<WitnessStackItem>`. The Transactions `witnesses` is a non-prefixed
16    /// `Vec<Witness>.`
17    ///
18    /// `WitnessStackItem::null()` and `WitnessStackItem::default()` return the empty byte vector
19    /// with a 0 prefix, which represents numerical 0, or null bytestring.
20    ///
21    WitnessStackItem
22);
23
24/// A Witness is a `PrefixVec` of `WitnessStackItem`s. This witness corresponds to a single input.
25///
26/// # Note
27///
28/// The transaction's witness is composed of many of these `Witness`es in an UNPREFIXED vector.
29pub type Witness = Vec<WitnessStackItem>;
30
31/// Errors associated with WitnessProgram
32#[derive(Debug, Error)]
33pub enum LockingScriptError {
34    /// Indicates a WitnessProgram with an invalid size.
35    #[error("Invalid size of WitnessProgram")]
36    InvalidWitnessProgramSizeError,
37}
38
39coins_core::wrap_prefixed_byte_vector!(
40    /// A WitnessProgram represents the data field of a LockingScript.
41    /// Since Handshake is segwit only, the WitnessProgram doesn't contain
42    /// opcodes itself, it is templated into a script at runtime. The
43    /// size of the WitnessProgram determines how it is interpreted for
44    /// version 0 LockingScripts.
45    WitnessProgram
46);
47
48/// The WitnessProgram is a 20 or 32 byte hash. When network serialized,
49/// it is a prefixed length byte vector.
50impl WitnessProgram {
51    /// Split the WitnessProgram into a tuple with of version,
52    /// length and data.
53    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)]
104/// A LockingScript
105pub struct LockingScript {
106    /// The version field determines how the Witness Program is interpreted
107    /// by the virtual machine.
108    pub version: u8,
109    /// The witness_program is generally a committment to some data that is required
110    /// for virtual machine execution. For version 0, a witness_program that is 20
111    /// bytes is interpreted as a pay-to-witness-pubkeyhash and data that is 32 bytes
112    /// is interpreted as a pay-to-witness-scripthash.
113    pub witness_program: WitnessProgram,
114}
115
116impl LockingScript {
117    /// Returns a null LockingScript
118    pub fn null() -> Self {
119        Self {
120            version: 0,
121            witness_program: WitnessProgram(vec![0x00; 20]),
122        }
123    }
124
125    /// Create a new LockingScript
126    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; // version and length
147        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/// Standard script types, and a non-standard type for all other scripts.
190#[derive(PartialEq, Eq, Clone, Debug)]
191pub enum LockingScriptType {
192    /// Pay to Witness Pubkeyhash.
193    Wpkh([u8; 20]),
194    /// Pay to Witness Scripthash.
195    Wsh([u8; 32]),
196    /// OP_RETURN
197    OpReturn(Vec<u8>),
198    /// Nonstandard or unknown `Script` type. May be a newer witness version.
199    NonStandard,
200}
201
202impl LockingScript {
203    /// Instantiate a standard p2wpkh script pubkey from a pubkey.
204    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    /// Instantiate a standard p2wsh script pubkey from a script.
215    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    /// Extract data from an op_return output. In Handshake, the version must be
227    /// 31 for the output to be an op_return output.
228    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    /// Get the type of the LockingScript based on its version and the size of
244    /// the WitnessProgram.
245    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        // fallthrough
270        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        // very simple Script bytecode
321        let script = hex::decode("0087635168").unwrap();
322        let locking_script = LockingScript::p2wsh(&script.into());
323
324        // sha3 of the script bytecode
325        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        // version, size of witness program, witness program
340        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}