dpp/identity/
core_script.rs

1use bincode::de::{BorrowDecoder, Decoder};
2use bincode::enc::Encoder;
3use bincode::error::{DecodeError, EncodeError};
4use bincode::{BorrowDecode, Decode, Encode};
5use dashcore::blockdata::opcodes;
6use std::fmt;
7use std::ops::Deref;
8
9use dashcore::{ScriptBuf as DashcoreScript, ScriptBuf};
10use platform_value::string_encoding::{self, Encoding};
11use rand::rngs::StdRng;
12use rand::Rng;
13
14use serde::de::Visitor;
15use serde::{Deserialize, Serialize};
16
17use crate::ProtocolError;
18use bincode::de::read::Reader;
19
20#[derive(Clone, Debug, Eq, PartialEq, Default)]
21pub struct CoreScript(DashcoreScript);
22
23impl CoreScript {
24    pub fn new(script: DashcoreScript) -> Self {
25        CoreScript(script)
26    }
27
28    pub fn to_string(&self, encoding: Encoding) -> String {
29        string_encoding::encode(&self.0.to_bytes(), encoding)
30    }
31
32    pub fn from_string(encoded_value: &str, encoding: Encoding) -> Result<Self, ProtocolError> {
33        let vec = string_encoding::decode(encoded_value, encoding)?;
34
35        Ok(Self(vec.into()))
36    }
37
38    pub fn from_bytes(bytes: Vec<u8>) -> Self {
39        Self(bytes.into())
40    }
41
42    pub fn new_p2pkh(key_hash: [u8; 20]) -> Self {
43        let mut bytes: Vec<u8> = vec![
44            opcodes::all::OP_DUP.to_u8(),
45            opcodes::all::OP_HASH160.to_u8(),
46            opcodes::all::OP_PUSHBYTES_20.to_u8(),
47        ];
48        bytes.extend_from_slice(&key_hash);
49        bytes.push(opcodes::all::OP_EQUALVERIFY.to_u8());
50        bytes.push(opcodes::all::OP_CHECKSIG.to_u8());
51        Self::from_bytes(bytes)
52    }
53
54    pub fn new_p2sh(script_hash: [u8; 20]) -> Self {
55        let mut bytes = vec![
56            opcodes::all::OP_HASH160.to_u8(),
57            opcodes::all::OP_PUSHBYTES_20.to_u8(),
58        ];
59        bytes.extend_from_slice(&script_hash);
60        bytes.push(opcodes::all::OP_EQUAL.to_u8());
61        Self::from_bytes(bytes)
62    }
63
64    pub fn random_p2sh(rng: &mut StdRng) -> Self {
65        Self::new_p2sh(rng.gen())
66    }
67
68    pub fn random_p2pkh(rng: &mut StdRng) -> Self {
69        Self::new_p2pkh(rng.gen())
70    }
71}
72
73impl From<Vec<u8>> for CoreScript {
74    fn from(value: Vec<u8>) -> Self {
75        CoreScript::from_bytes(value)
76    }
77}
78
79impl Deref for CoreScript {
80    type Target = DashcoreScript;
81
82    fn deref(&self) -> &Self::Target {
83        &self.0
84    }
85}
86
87// Implement the bincode::Encode trait for CoreScript
88impl Encode for CoreScript {
89    fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
90        self.0.as_bytes().encode(encoder)
91    }
92}
93
94// Implement the bincode::Decode trait for CoreScript
95impl<C> Decode<C> for CoreScript {
96    fn decode<D: Decoder<Context = C>>(decoder: &mut D) -> Result<Self, DecodeError> {
97        let bytes = Vec::<u8>::decode(decoder)?;
98        // Create a CoreScript instance using the decoded DashCoreScript
99        Ok(CoreScript(ScriptBuf(bytes)))
100    }
101}
102
103impl<'de, C> BorrowDecode<'de, C> for CoreScript {
104    fn borrow_decode<D: BorrowDecoder<'de, Context = C>>(
105        decoder: &mut D,
106    ) -> Result<Self, DecodeError> {
107        // Read the serialized bytes from the decoder into a Vec<u8>
108        let mut bytes = Vec::new();
109        loop {
110            let buf_len = 1024; // Adjust the buffer size as needed
111            let mut buf = vec![0; buf_len];
112
113            match decoder.reader().read(&mut buf) {
114                Ok(()) => {
115                    let read_bytes = buf.iter().position(|&x| x == 0).unwrap_or(buf.len());
116                    bytes.extend_from_slice(&buf[..read_bytes]);
117                    if read_bytes < buf_len {
118                        break;
119                    }
120                }
121                Err(DecodeError::Io { inner, additional })
122                    if inner.kind() == std::io::ErrorKind::UnexpectedEof =>
123                {
124                    if additional > 0 {
125                        return Err(DecodeError::Io { inner, additional });
126                    } else {
127                        break;
128                    }
129                }
130                Err(e) => return Err(e),
131            }
132        }
133
134        // Convert Vec<u8> to Box<[u8]> and create a DashCoreScript instance
135        let dash_core_script = DashcoreScript(bytes);
136
137        // Create a CoreScript instance using the decoded DashCoreScript
138        Ok(CoreScript(dash_core_script))
139    }
140}
141
142impl Serialize for CoreScript {
143    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
144    where
145        S: serde::Serializer,
146    {
147        if serializer.is_human_readable() {
148            serializer.serialize_str(&self.to_string(Encoding::Base64))
149        } else {
150            serializer.serialize_bytes(self.as_bytes())
151        }
152    }
153}
154
155impl<'de> Deserialize<'de> for CoreScript {
156    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
157    where
158        D: serde::Deserializer<'de>,
159    {
160        if deserializer.is_human_readable() {
161            let data: String = Deserialize::deserialize(deserializer)?;
162
163            Self::from_string(&data, Encoding::Base64).map_err(|e| {
164                serde::de::Error::custom(format!(
165                    "expected to be able to deserialize core script from string: {}",
166                    e
167                ))
168            })
169        } else {
170            struct BytesVisitor;
171
172            impl Visitor<'_> for BytesVisitor {
173                type Value = CoreScript;
174
175                fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
176                    formatter.write_str("a byte array")
177                }
178
179                fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
180                where
181                    E: serde::de::Error,
182                {
183                    Ok(CoreScript::from_bytes(v.to_vec()))
184                }
185            }
186
187            deserializer.deserialize_bytes(BytesVisitor)
188        }
189    }
190}
191
192impl std::fmt::Display for CoreScript {
193    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194        write!(f, "{}", self.to_string(Encoding::Base64))
195    }
196}