pallas_primitives/
plutus_data.rs

1use crate::KeyValuePairs;
2use pallas_codec::utils::Int;
3use pallas_codec::{
4    minicbor::{
5        self,
6        data::{IanaTag, Tag},
7        Encode,
8    },
9    utils::MaybeIndefArray,
10};
11use serde::{Deserialize, Serialize};
12use std::{fmt, ops::Deref};
13
14#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
15pub enum PlutusData {
16    Constr(Constr<PlutusData>),
17    Map(KeyValuePairs<PlutusData, PlutusData>),
18    BigInt(BigInt),
19    BoundedBytes(BoundedBytes),
20    Array(MaybeIndefArray<PlutusData>),
21}
22
23impl<'b, C> minicbor::decode::Decode<'b, C> for PlutusData {
24    fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
25        let type_ = d.datatype()?;
26
27        match type_ {
28            minicbor::data::Type::Tag => {
29                let mut probe = d.probe();
30                let tag = probe.tag()?;
31
32                if tag == IanaTag::PosBignum.tag() || tag == IanaTag::NegBignum.tag() {
33                    Ok(Self::BigInt(d.decode_with(ctx)?))
34                } else {
35                    match tag.as_u64() {
36                        (121..=127) | (1280..=1400) | 102 => Ok(Self::Constr(d.decode_with(ctx)?)),
37                        _ => Err(minicbor::decode::Error::message(
38                            "unknown tag for plutus data tag",
39                        )),
40                    }
41                }
42            }
43            minicbor::data::Type::U8
44            | minicbor::data::Type::U16
45            | minicbor::data::Type::U32
46            | minicbor::data::Type::U64
47            | minicbor::data::Type::I8
48            | minicbor::data::Type::I16
49            | minicbor::data::Type::I32
50            | minicbor::data::Type::I64
51            | minicbor::data::Type::Int => Ok(Self::BigInt(d.decode_with(ctx)?)),
52            minicbor::data::Type::Map | minicbor::data::Type::MapIndef => {
53                Ok(Self::Map(d.decode_with(ctx)?))
54            }
55            minicbor::data::Type::Bytes => Ok(Self::BoundedBytes(d.decode_with(ctx)?)),
56            minicbor::data::Type::BytesIndef => {
57                let mut full = Vec::new();
58
59                for slice in d.bytes_iter()? {
60                    full.extend(slice?);
61                }
62
63                Ok(Self::BoundedBytes(BoundedBytes::from(full)))
64            }
65            minicbor::data::Type::Array | minicbor::data::Type::ArrayIndef => {
66                Ok(Self::Array(d.decode_with(ctx)?))
67            }
68
69            any => Err(minicbor::decode::Error::message(format!(
70                "bad cbor data type ({any:?}) for plutus data"
71            ))),
72        }
73    }
74}
75
76impl<C> minicbor::encode::Encode<C> for PlutusData {
77    fn encode<W: minicbor::encode::Write>(
78        &self,
79        e: &mut minicbor::Encoder<W>,
80        ctx: &mut C,
81    ) -> Result<(), minicbor::encode::Error<W::Error>> {
82        match self {
83            Self::Constr(a) => {
84                e.encode_with(a, ctx)?;
85            }
86            Self::Map(a) => {
87                // we use definite array to match the approach used by haskell's plutus
88                // implementation https://github.com/input-output-hk/plutus/blob/9538fc9829426b2ecb0628d352e2d7af96ec8204/plutus-core/plutus-core/src/PlutusCore/Data.hs#L152
89                e.map(a.len().try_into().unwrap())?;
90                for (k, v) in a.iter() {
91                    k.encode(e, ctx)?;
92                    v.encode(e, ctx)?;
93                }
94            }
95            Self::BigInt(a) => {
96                e.encode_with(a, ctx)?;
97            }
98            Self::BoundedBytes(a) => {
99                e.encode_with(a, ctx)?;
100            }
101            Self::Array(a) => {
102                e.encode_with(a, ctx)?;
103            }
104        };
105
106        Ok(())
107    }
108}
109
110/*
111big_int = int / big_uint / big_nint ; New
112big_uint = #6.2(bounded_bytes) ; New
113big_nint = #6.3(bounded_bytes) ; New
114 */
115
116#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
117pub enum BigInt {
118    Int(Int),
119    BigUInt(BoundedBytes),
120    BigNInt(BoundedBytes),
121}
122
123impl<'b, C> minicbor::decode::Decode<'b, C> for BigInt {
124    fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
125        let datatype = d.datatype()?;
126
127        match datatype {
128            minicbor::data::Type::U8
129            | minicbor::data::Type::U16
130            | minicbor::data::Type::U32
131            | minicbor::data::Type::U64
132            | minicbor::data::Type::I8
133            | minicbor::data::Type::I16
134            | minicbor::data::Type::I32
135            | minicbor::data::Type::I64
136            | minicbor::data::Type::Int => Ok(Self::Int(d.decode_with(ctx)?)),
137            minicbor::data::Type::Tag => {
138                let tag = d.tag()?;
139                if tag == IanaTag::PosBignum.tag() {
140                    Ok(Self::BigUInt(d.decode_with(ctx)?))
141                } else if tag == IanaTag::NegBignum.tag() {
142                    Ok(Self::BigNInt(d.decode_with(ctx)?))
143                } else {
144                    Err(minicbor::decode::Error::message(
145                        "invalid cbor tag for big int",
146                    ))
147                }
148            }
149            _ => Err(minicbor::decode::Error::message(
150                "invalid cbor data type for big int",
151            )),
152        }
153    }
154}
155
156impl<C> minicbor::encode::Encode<C> for BigInt {
157    fn encode<W: minicbor::encode::Write>(
158        &self,
159        e: &mut minicbor::Encoder<W>,
160        ctx: &mut C,
161    ) -> Result<(), minicbor::encode::Error<W::Error>> {
162        match self {
163            BigInt::Int(x) => {
164                e.encode_with(x, ctx)?;
165            }
166            BigInt::BigUInt(x) => {
167                e.tag(IanaTag::PosBignum)?;
168                e.encode_with(x, ctx)?;
169            }
170            BigInt::BigNInt(x) => {
171                e.tag(IanaTag::NegBignum)?;
172                e.encode_with(x, ctx)?;
173            }
174        };
175
176        Ok(())
177    }
178}
179
180#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
181pub struct Constr<A> {
182    pub tag: u64,
183    pub any_constructor: Option<u64>,
184    pub fields: MaybeIndefArray<A>,
185}
186
187impl<'b, C, A> minicbor::decode::Decode<'b, C> for Constr<A>
188where
189    A: minicbor::decode::Decode<'b, C>,
190{
191    fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
192        let tag = d.tag()?;
193        let x = tag.as_u64();
194        match x {
195            121..=127 | 1280..=1400 => Ok(Constr {
196                tag: x,
197                fields: d.decode_with(ctx)?,
198                any_constructor: None,
199            }),
200            102 => {
201                d.array()?;
202
203                Ok(Constr {
204                    tag: x,
205                    any_constructor: Some(d.decode_with(ctx)?),
206                    fields: d.decode_with(ctx)?,
207                })
208            }
209            _ => Err(minicbor::decode::Error::message(
210                "bad tag code for plutus data",
211            )),
212        }
213    }
214}
215
216impl<C, A> minicbor::encode::Encode<C> for Constr<A>
217where
218    A: minicbor::encode::Encode<C>,
219{
220    fn encode<W: minicbor::encode::Write>(
221        &self,
222        e: &mut minicbor::Encoder<W>,
223        ctx: &mut C,
224    ) -> Result<(), minicbor::encode::Error<W::Error>> {
225        e.tag(Tag::new(self.tag))?;
226
227        match self.tag {
228            102 => {
229                let x = (self.any_constructor.unwrap_or_default(), &self.fields);
230                e.encode_with(x, ctx)?;
231                Ok(())
232            }
233            _ => {
234                e.encode_with(&self.fields, ctx)?;
235                Ok(())
236            }
237        }
238    }
239}
240
241/// Defined to encode PlutusData bytestring as it is done in the canonical
242/// plutus implementation
243#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
244#[serde(into = "String")]
245#[serde(try_from = "String")]
246pub struct BoundedBytes(Vec<u8>);
247
248impl From<Vec<u8>> for BoundedBytes {
249    fn from(xs: Vec<u8>) -> Self {
250        BoundedBytes(xs)
251    }
252}
253
254impl From<BoundedBytes> for Vec<u8> {
255    fn from(b: BoundedBytes) -> Self {
256        b.0
257    }
258}
259
260impl Deref for BoundedBytes {
261    type Target = Vec<u8>;
262
263    fn deref(&self) -> &Self::Target {
264        &self.0
265    }
266}
267
268impl TryFrom<String> for BoundedBytes {
269    type Error = hex::FromHexError;
270
271    fn try_from(value: String) -> Result<Self, Self::Error> {
272        let v = hex::decode(value)?;
273        Ok(BoundedBytes(v))
274    }
275}
276
277impl From<BoundedBytes> for String {
278    fn from(b: BoundedBytes) -> Self {
279        hex::encode(b.deref())
280    }
281}
282
283impl fmt::Display for BoundedBytes {
284    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285        let bytes: Vec<u8> = self.clone().into();
286
287        f.write_str(&hex::encode(bytes))
288    }
289}
290
291impl<C> Encode<C> for BoundedBytes {
292    fn encode<W: minicbor::encode::Write>(
293        &self,
294        e: &mut minicbor::Encoder<W>,
295        _: &mut C,
296    ) -> Result<(), minicbor::encode::Error<W::Error>> {
297        // we match the haskell implementation by encoding bytestrings longer than 64
298        // bytes as indefinite lists of bytes
299        const CHUNK_SIZE: usize = 64;
300        let bs: &Vec<u8> = self.deref();
301        if bs.len() <= 64 {
302            e.bytes(bs)?;
303        } else {
304            e.begin_bytes()?;
305            for b in bs.chunks(CHUNK_SIZE) {
306                e.bytes(b)?;
307            }
308            e.end()?;
309        }
310        Ok(())
311    }
312}
313
314impl<'b, C> minicbor::decode::Decode<'b, C> for BoundedBytes {
315    fn decode(d: &mut minicbor::Decoder<'b>, _: &mut C) -> Result<Self, minicbor::decode::Error> {
316        let mut res = Vec::new();
317        for chunk in d.bytes_iter()? {
318            let bs = chunk?;
319            res.extend_from_slice(bs);
320        }
321        Ok(BoundedBytes::from(res))
322    }
323}