zebra_chain/transparent/
script.rs

1//! Bitcoin script for Zebra
2
3use std::{fmt, io};
4
5use hex::{FromHex, FromHexError, ToHex};
6
7use crate::serialization::{
8    zcash_serialize_bytes, SerializationError, ZcashDeserialize, ZcashSerialize,
9};
10
11/// An encoding of a Bitcoin script.
12#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
13#[cfg_attr(
14    any(test, feature = "proptest-impl"),
15    derive(proptest_derive::Arbitrary)
16)]
17pub struct Script(
18    /// # Correctness
19    ///
20    /// Consensus-critical serialization uses [`ZcashSerialize`].
21    /// [`serde`]-based hex serialization must only be used for RPCs and testing.
22    #[serde(with = "hex")]
23    Vec<u8>,
24);
25
26impl Script {
27    /// Create a new Bitcoin script from its raw bytes.
28    /// The raw bytes must not contain the length prefix.
29    pub fn new(raw_bytes: &[u8]) -> Self {
30        Script(raw_bytes.to_vec())
31    }
32
33    /// Return the raw bytes of the script without the length prefix.
34    ///
35    /// # Correctness
36    ///
37    /// These raw bytes do not have a length prefix.
38    /// The Zcash serialization format requires a length prefix; use `zcash_serialize`
39    /// and `zcash_deserialize` to create byte data with a length prefix.
40    pub fn as_raw_bytes(&self) -> &[u8] {
41        &self.0
42    }
43}
44
45impl From<zcash_transparent::address::Script> for Script {
46    fn from(script: zcash_transparent::address::Script) -> Self {
47        Script(script.0)
48    }
49}
50
51impl fmt::Display for Script {
52    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53        f.write_str(&self.encode_hex::<String>())
54    }
55}
56
57impl fmt::Debug for Script {
58    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59        f.debug_tuple("Script")
60            .field(&hex::encode(&self.0))
61            .finish()
62    }
63}
64
65impl ToHex for &Script {
66    fn encode_hex<T: FromIterator<char>>(&self) -> T {
67        self.as_raw_bytes().encode_hex()
68    }
69
70    fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
71        self.as_raw_bytes().encode_hex_upper()
72    }
73}
74
75impl ToHex for Script {
76    fn encode_hex<T: FromIterator<char>>(&self) -> T {
77        (&self).encode_hex()
78    }
79
80    fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
81        (&self).encode_hex_upper()
82    }
83}
84
85impl FromHex for Script {
86    type Error = FromHexError;
87
88    fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
89        let bytes = Vec::from_hex(hex)?;
90        Ok(Script::new(&bytes))
91    }
92}
93
94impl ZcashSerialize for Script {
95    fn zcash_serialize<W: io::Write>(&self, writer: W) -> Result<(), io::Error> {
96        zcash_serialize_bytes(&self.0, writer)
97    }
98}
99
100impl ZcashDeserialize for Script {
101    fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
102        Vec::zcash_deserialize(reader).map(Script)
103    }
104}
105
106#[cfg(test)]
107mod proptests {
108    use std::io::Cursor;
109
110    use proptest::prelude::*;
111
112    use super::*;
113
114    proptest! {
115        #[test]
116        fn script_roundtrip(script in any::<Script>()) {
117            let _init_guard = zebra_test::init();
118
119            let mut bytes = Cursor::new(Vec::new());
120            script.zcash_serialize(&mut bytes)?;
121
122            bytes.set_position(0);
123            let other_script = Script::zcash_deserialize(&mut bytes)?;
124
125            prop_assert_eq![script, other_script];
126        }
127    }
128}