amaru_kernel/cardano/memoized/
native_script.rs1use crate::{Bytes, KeepRaw, NativeScript, cbor, from_cbor, utils::string::blanket_try_from_hex_bytes};
16
17#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
18#[serde(try_from = "&str")]
19pub struct MemoizedNativeScript {
20 original_bytes: Bytes,
21 expr: NativeScript,
25}
26
27impl MemoizedNativeScript {
28 pub fn original_bytes(&self) -> &[u8] {
29 &self.original_bytes
30 }
31}
32
33impl AsRef<NativeScript> for MemoizedNativeScript {
34 fn as_ref(&self) -> &NativeScript {
35 &self.expr
36 }
37}
38
39impl TryFrom<Bytes> for MemoizedNativeScript {
40 type Error = String;
41
42 fn try_from(original_bytes: Bytes) -> Result<Self, Self::Error> {
43 let expr = from_cbor(&original_bytes).ok_or_else(|| "invalid serialized native script".to_string())?;
44
45 Ok(Self { original_bytes, expr })
46 }
47}
48
49impl From<KeepRaw<'_, NativeScript>> for MemoizedNativeScript {
50 fn from(script: KeepRaw<'_, NativeScript>) -> Self {
51 Self { original_bytes: Bytes::from(script.raw_cbor().to_vec()), expr: script.unwrap() }
52 }
53}
54
55impl TryFrom<&str> for MemoizedNativeScript {
56 type Error = String;
57
58 fn try_from(s: &str) -> Result<Self, Self::Error> {
59 blanket_try_from_hex_bytes(s, |original_bytes, expr| Self { original_bytes, expr })
60 }
61}
62
63impl TryFrom<String> for MemoizedNativeScript {
64 type Error = String;
65
66 fn try_from(s: String) -> Result<Self, Self::Error> {
67 Self::try_from(s.as_str())
68 }
69}
70
71impl<'b, C> cbor::Decode<'b, C> for MemoizedNativeScript {
72 fn decode(d: &mut cbor::Decoder<'b>, ctx: &mut C) -> Result<Self, cbor::decode::Error> {
73 let start_pos = d.position();
74 let expr: NativeScript = d.decode_with(ctx)?;
75 let end_pos = d.position();
76 let original_bytes = Bytes::from(d.input()[start_pos..end_pos].to_vec());
77
78 Ok(Self { original_bytes, expr })
79 }
80}
81
82impl<C> cbor::Encode<C> for MemoizedNativeScript {
83 fn encode<W: cbor::encode::Write>(
84 &self,
85 e: &mut cbor::Encoder<W>,
86 _ctx: &mut C,
87 ) -> Result<(), cbor::encode::Error<W::Error>> {
88 e.writer_mut().write_all(&self.original_bytes[..]).map_err(cbor::encode::Error::write)
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use pallas_primitives::conway as pallas;
95 use proptest::prelude::*;
96
97 use super::*;
98 use crate::{Hash, MaybeIndefArray, cbor, size::KEY, to_cbor};
99
100 #[derive(Debug, Clone)]
104 enum NativeScript {
105 ScriptPubkey(Hash<KEY>),
106 ScriptAll(MaybeIndefArray<NativeScript>),
107 ScriptAny(MaybeIndefArray<NativeScript>),
108 ScriptNOfK(u32, MaybeIndefArray<NativeScript>),
109 InvalidBefore(u64),
110 InvalidHereafter(u64),
111 }
112
113 impl From<NativeScript> for pallas::NativeScript {
114 fn from(script: NativeScript) -> Self {
115 match script {
116 NativeScript::ScriptPubkey(sig) => Self::ScriptPubkey(sig),
117 NativeScript::ScriptAll(sigs) => Self::ScriptAll(sigs.to_vec().into_iter().map(|s| s.into()).collect()),
118 NativeScript::ScriptAny(sigs) => Self::ScriptAny(sigs.to_vec().into_iter().map(|s| s.into()).collect()),
119 NativeScript::ScriptNOfK(n, sigs) => {
120 Self::ScriptNOfK(n, sigs.to_vec().into_iter().map(|s| s.into()).collect())
121 }
122 NativeScript::InvalidBefore(n) => Self::InvalidBefore(n),
123 NativeScript::InvalidHereafter(n) => Self::InvalidHereafter(n),
124 }
125 }
126 }
127
128 impl<C> cbor::encode::Encode<C> for NativeScript {
129 fn encode<W: cbor::encode::Write>(
130 &self,
131 e: &mut cbor::Encoder<W>,
132 ctx: &mut C,
133 ) -> Result<(), cbor::encode::Error<W::Error>> {
134 match self {
135 Self::ScriptPubkey(sig) => {
136 e.array(2)?;
137 e.encode_with(0, ctx)?;
138 e.encode_with(sig, ctx)?;
139 }
140 Self::ScriptAll(sigs) => {
141 e.array(2)?;
142 e.encode_with(1, ctx)?;
143 e.encode_with(sigs, ctx)?;
144 }
145 Self::ScriptAny(sigs) => {
146 e.array(2)?;
147 e.encode_with(2, ctx)?;
148 e.encode_with(sigs, ctx)?;
149 }
150 Self::ScriptNOfK(n, sigs) => {
151 e.array(3)?;
152 e.encode_with(3, ctx)?;
153 e.encode_with(n, ctx)?;
154 e.encode_with(sigs, ctx)?;
155 }
156 Self::InvalidBefore(n) => {
157 e.array(2)?;
158 e.encode_with(4, ctx)?;
159 e.encode_with(n, ctx)?;
160 }
161 Self::InvalidHereafter(n) => {
162 e.array(2)?;
163 e.encode_with(5, ctx)?;
164 e.encode_with(n, ctx)?;
165 }
166 };
167
168 Ok(())
169 }
170 }
171
172 prop_compose! {
173 pub(crate) fn any_key_hash()(bytes in any::<[u8; 28]>()) -> Hash<28> {
174 Hash::from(bytes)
175 }
176 }
177
178 fn any_native_script(depth: u8) -> BoxedStrategy<NativeScript> {
179 let sig = any_key_hash().prop_map(NativeScript::ScriptPubkey);
180 let before = any::<u64>().prop_map(NativeScript::InvalidBefore);
181 let after = any::<u64>().prop_map(NativeScript::InvalidHereafter);
182 if depth > 0 {
183 let all = (any::<bool>(), prop::collection::vec(any_native_script(depth - 1), 0..depth as usize)).prop_map(
184 |(is_def, sigs)| {
185 NativeScript::ScriptAll(if is_def {
186 MaybeIndefArray::Def(sigs)
187 } else {
188 MaybeIndefArray::Indef(sigs)
189 })
190 },
191 );
192
193 let some = (any::<bool>(), prop::collection::vec(any_native_script(depth - 1), 0..depth as usize))
194 .prop_map(|(is_def, sigs)| {
195 NativeScript::ScriptAny(if is_def {
196 MaybeIndefArray::Def(sigs)
197 } else {
198 MaybeIndefArray::Indef(sigs)
199 })
200 });
201
202 let n_of_k =
203 (any::<bool>(), any::<u32>(), prop::collection::vec(any_native_script(depth - 1), 0..depth as usize))
204 .prop_map(|(is_def, n, sigs)| {
205 NativeScript::ScriptNOfK(
206 n,
207 if is_def { MaybeIndefArray::Def(sigs) } else { MaybeIndefArray::Indef(sigs) },
208 )
209 });
210
211 prop_oneof![sig, before, after, all, some, n_of_k,].boxed()
212 } else {
213 prop_oneof![sig, before, after].boxed()
214 }
215 }
216
217 proptest! {
218 #[test]
219 fn roundtrip_hex_encoded_str(original_script in any_native_script(3)) {
220 let original_bytes = to_cbor(&original_script);
221 let result = MemoizedNativeScript::try_from(hex::encode(&original_bytes)).unwrap();
222
223 assert_eq!(result.as_ref(), &pallas::NativeScript::from(original_script));
224 assert_eq!(result.original_bytes(), &original_bytes);
225 }
226 }
227
228 proptest! {
229 #[test]
230 fn roundtrip_cbor(original_script in any_native_script(3)) {
231 let original_bytes = to_cbor(&original_script);
232 let raw: KeepRaw<'_, pallas::NativeScript> = cbor::decode(&original_bytes).unwrap();
233 let result: MemoizedNativeScript = raw.into();
234
235 assert_eq!(result.as_ref(), &pallas::NativeScript::from(original_script));
236 assert_eq!(result.original_bytes(), &original_bytes);
237 }
238 }
239}