amaru_kernel/cardano/memoized/
plutus_data.rs1use crate::{Bytes, Hash, Hasher, KeepRaw, PlutusData, cbor, utils::string::blanket_try_from_hex_bytes};
16
17#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
18#[serde(try_from = "&str")]
19#[serde(into = "String")]
20pub struct MemoizedPlutusData {
21 original_bytes: Bytes,
22 data: PlutusData,
26}
27
28impl MemoizedPlutusData {
29 pub fn new(data: PlutusData) -> Result<Self, String> {
30 let mut buf = Vec::new();
31 cbor::encode(&data, &mut buf).map_err(|_| "failed to encode PlutusData".to_string())?;
32
33 Ok(Self { original_bytes: Bytes::from(buf), data })
34 }
35
36 pub fn original_bytes(&self) -> &[u8] {
37 &self.original_bytes
38 }
39
40 pub fn hash(&self) -> Hash<32> {
41 Hasher::<256>::hash(&self.original_bytes)
42 }
43}
44
45impl AsRef<PlutusData> for MemoizedPlutusData {
46 fn as_ref(&self) -> &PlutusData {
47 &self.data
48 }
49}
50
51impl From<MemoizedPlutusData> for String {
52 fn from(plutus_data: MemoizedPlutusData) -> Self {
53 hex::encode(&plutus_data.original_bytes[..])
54 }
55}
56
57impl<'b> From<KeepRaw<'b, PlutusData>> for MemoizedPlutusData {
58 fn from(data: KeepRaw<'b, PlutusData>) -> Self {
59 Self { original_bytes: Bytes::from(data.raw_cbor().to_vec()), data: data.unwrap() }
60 }
61}
62
63impl TryFrom<&str> for MemoizedPlutusData {
64 type Error = String;
65
66 fn try_from(s: &str) -> Result<Self, Self::Error> {
67 blanket_try_from_hex_bytes(s, |original_bytes, data| Self { original_bytes, data })
68 }
69}
70
71impl TryFrom<String> for MemoizedPlutusData {
72 type Error = String;
73
74 fn try_from(s: String) -> Result<Self, Self::Error> {
75 Self::try_from(s.as_str())
76 }
77}
78
79impl TryFrom<Vec<u8>> for MemoizedPlutusData {
80 type Error = cbor::decode::Error;
81
82 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
83 let data = cbor::decode(&bytes)?;
84 Ok(Self { original_bytes: Bytes::from(bytes), data })
85 }
86}
87
88impl<'b, C> cbor::Decode<'b, C> for MemoizedPlutusData {
89 fn decode(d: &mut cbor::Decoder<'b>, ctx: &mut C) -> Result<Self, cbor::decode::Error> {
90 let keep_raw: KeepRaw<'b, PlutusData> = d.decode_with(ctx)?;
91 Ok(Self::from(keep_raw))
92 }
93}
94
95impl<C> cbor::Encode<C> for MemoizedPlutusData {
96 fn encode<W: cbor::encode::Write>(
97 &self,
98 e: &mut cbor::Encoder<W>,
99 _ctx: &mut C,
100 ) -> Result<(), cbor::encode::Error<W::Error>> {
101 e.writer_mut().write_all(&self.original_bytes[..]).map_err(cbor::encode::Error::write)
102 }
103}
104
105#[cfg(test)]
106pub(crate) mod tests {
107 use pallas_primitives::{self as pallas, BigInt, BoundedBytes, KeyValuePairs};
108 use proptest::{prelude::*, strategy::Just};
109
110 use super::*;
111 use crate::{Constr, MaybeIndefArray, to_cbor};
112
113 #[derive(Debug, Clone)]
116 enum PlutusData {
117 Constr(Constr<PlutusData>),
118 Map(KeyValuePairs<PlutusData, PlutusData>),
119 BigInt(BigInt),
120 BoundedBytes(BoundedBytes),
121 Array(MaybeIndefArray<PlutusData>),
122 }
123 impl From<PlutusData> for pallas::PlutusData {
124 fn from(data: PlutusData) -> Self {
125 match data {
126 PlutusData::BigInt(i) => Self::BigInt(i),
127 PlutusData::BoundedBytes(i) => Self::BoundedBytes(i),
128 PlutusData::Array(xs) => Self::Array(match xs {
129 MaybeIndefArray::Def(xs) => MaybeIndefArray::Def(xs.into_iter().map(|x| x.into()).collect()),
130 MaybeIndefArray::Indef(xs) => MaybeIndefArray::Indef(xs.into_iter().map(|x| x.into()).collect()),
131 }),
132 PlutusData::Map(xs) => Self::Map(match xs {
133 KeyValuePairs::Def(xs) => {
134 KeyValuePairs::Def(xs.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
135 }
136 KeyValuePairs::Indef(xs) => {
137 KeyValuePairs::Indef(xs.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
138 }
139 }),
140 PlutusData::Constr(Constr { tag, any_constructor, fields }) => Self::Constr(Constr {
141 tag,
142 any_constructor,
143 fields: match fields {
144 MaybeIndefArray::Def(xs) => MaybeIndefArray::Def(xs.into_iter().map(|x| x.into()).collect()),
145 MaybeIndefArray::Indef(xs) => {
146 MaybeIndefArray::Indef(xs.into_iter().map(|x| x.into()).collect())
147 }
148 },
149 }),
150 }
151 }
152 }
153
154 impl<C> cbor::encode::Encode<C> for PlutusData {
155 fn encode<W: cbor::encode::Write>(
156 &self,
157 e: &mut cbor::Encoder<W>,
158 ctx: &mut C,
159 ) -> Result<(), cbor::encode::Error<W::Error>> {
160 match self {
161 Self::Constr(a) => {
162 e.encode_with(a, ctx)?;
163 }
164 Self::Map(a) => {
165 e.encode_with(a, ctx)?;
166 }
167 Self::BigInt(a) => {
168 e.encode_with(a, ctx)?;
169 }
170 Self::BoundedBytes(a) => {
171 e.encode_with(a, ctx)?;
172 }
173 Self::Array(a) => {
174 e.encode_with(a, ctx)?;
175 }
176 };
177
178 Ok(())
179 }
180 }
181
182 prop_compose! {
183 pub(crate) fn any_bounded_bytes()(
184 bytes in any::<Vec<u8>>(),
185 ) -> BoundedBytes {
186 BoundedBytes::from(bytes)
187 }
188 }
189
190 pub(crate) fn any_bigint() -> impl Strategy<Value = BigInt> {
191 prop_oneof![
192 any::<i64>().prop_map(|i| BigInt::Int(i.into())),
193 any_bounded_bytes().prop_map(BigInt::BigUInt),
194 any_bounded_bytes().prop_map(BigInt::BigNInt),
195 ]
196 }
197
198 fn any_constr(depth: u8) -> impl Strategy<Value = Constr<PlutusData>> {
199 let any_constr_tag = prop_oneof![
200 (Just(102), any::<u64>().prop_map(Some)),
201 (121_u64..=127, Just(None)),
202 (1280_u64..=1400, Just(None))
203 ];
204
205 let any_fields = prop::collection::vec(any_plutus_data(depth - 1), 0..depth as usize);
206
207 (any_constr_tag, any_fields, any::<bool>()).prop_map(|((tag, any_constructor), fields, is_def)| Constr {
208 tag,
209 any_constructor,
210 fields: if is_def { MaybeIndefArray::Def(fields) } else { MaybeIndefArray::Indef(fields) },
211 })
212 }
213
214 fn any_plutus_data(depth: u8) -> BoxedStrategy<PlutusData> {
215 let int = any_bigint().prop_map(PlutusData::BigInt);
216
217 let bytes = any_bounded_bytes().prop_map(PlutusData::BoundedBytes);
218
219 if depth > 0 {
220 let constr = any_constr(depth).prop_map(PlutusData::Constr);
221
222 let array = (any::<bool>(), prop::collection::vec(any_plutus_data(depth - 1), 0..depth as usize)).prop_map(
223 |(is_def, xs)| {
224 PlutusData::Array(if is_def { MaybeIndefArray::Def(xs) } else { MaybeIndefArray::Indef(xs) })
225 },
226 );
227
228 let map = (
229 any::<bool>(),
230 prop::collection::vec((any_plutus_data(depth - 1), any_plutus_data(depth - 1)), 0..depth as usize),
231 )
232 .prop_map(|(is_def, kvs)| {
233 PlutusData::Map(if is_def { KeyValuePairs::Def(kvs) } else { KeyValuePairs::Indef(kvs) })
234 });
235
236 prop_oneof![int, bytes, constr, array, map].boxed()
237 } else {
238 prop_oneof![int, bytes].boxed()
239 }
240 }
241
242 proptest! {
243 #[test]
244 fn roundtrip_hex_encoded_str(original_data in any_plutus_data(3)) {
245 let original_bytes = to_cbor(&original_data);
246 let result = MemoizedPlutusData::try_from(hex::encode(&original_bytes)).unwrap();
247
248 assert_eq!(result.as_ref(), &pallas::PlutusData::from(original_data));
249 assert_eq!(result.original_bytes(), &original_bytes);
250 }
251 }
252
253 proptest! {
254 #[test]
255 fn roundtrip_cbor(original_data in any_plutus_data(3)) {
256 let original_bytes = to_cbor(&original_data);
257 let raw: KeepRaw<'_, pallas::PlutusData> = cbor::decode(&original_bytes).unwrap();
258 let result: MemoizedPlutusData = raw.into();
259
260 assert_eq!(result.as_ref(), &pallas::PlutusData::from(original_data));
261 assert_eq!(result.original_bytes(), &original_bytes);
262 }
263 }
264
265 #[test]
266 fn invalid_string() {
267 assert!(MemoizedPlutusData::try_from("foo".to_string()).is_err());
268 }
269}