zebra_chain/transparent/
script.rs

1//! Bitcoin script for Zebra
2
3use std::{fmt, io};
4
5use hex::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 fmt::Display for Script {
46    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
47        f.write_str(&self.encode_hex::<String>())
48    }
49}
50
51impl fmt::Debug for Script {
52    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53        f.debug_tuple("Script")
54            .field(&hex::encode(&self.0))
55            .finish()
56    }
57}
58
59impl ToHex for &Script {
60    fn encode_hex<T: FromIterator<char>>(&self) -> T {
61        self.as_raw_bytes().encode_hex()
62    }
63
64    fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
65        self.as_raw_bytes().encode_hex_upper()
66    }
67}
68
69impl ToHex for Script {
70    fn encode_hex<T: FromIterator<char>>(&self) -> T {
71        (&self).encode_hex()
72    }
73
74    fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
75        (&self).encode_hex_upper()
76    }
77}
78
79impl ZcashSerialize for Script {
80    fn zcash_serialize<W: io::Write>(&self, writer: W) -> Result<(), io::Error> {
81        zcash_serialize_bytes(&self.0, writer)
82    }
83}
84
85impl ZcashDeserialize for Script {
86    fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
87        Vec::zcash_deserialize(reader).map(Script)
88    }
89}
90
91#[cfg(test)]
92mod proptests {
93    use std::io::Cursor;
94
95    use proptest::prelude::*;
96
97    use super::*;
98
99    proptest! {
100        #[test]
101        fn script_roundtrip(script in any::<Script>()) {
102            let _init_guard = zebra_test::init();
103
104            let mut bytes = Cursor::new(Vec::new());
105            script.zcash_serialize(&mut bytes)?;
106
107            bytes.set_position(0);
108            let other_script = Script::zcash_deserialize(&mut bytes)?;
109
110            prop_assert_eq![script, other_script];
111        }
112    }
113}