1use bumpalo::collections::Vec as BumpVec;
2use minicbor::data::{IanaTag, Tag};
3
4use crate::data::PlutusData;
5
6use super::Ctx;
7
8impl<'a, 'b> minicbor::decode::Decode<'b, Ctx<'a>> for &'a PlutusData<'a> {
9 fn decode(
10 decoder: &mut minicbor::Decoder<'b>,
11 ctx: &mut Ctx<'a>,
12 ) -> Result<Self, minicbor::decode::Error> {
13 let typ = decoder.datatype()?;
14
15 match typ {
16 minicbor::data::Type::Tag => {
17 let mut probe = decoder.probe();
18
19 let tag = probe.tag()?;
20
21 if matches!(tag.as_u64(), 121..=127 | 1280..=1400 | 102) {
22 let x = decoder.tag()?.as_u64();
23
24 return match x {
25 121..=127 => {
26 let mut fields = BumpVec::new_in(ctx.arena.as_bump());
27
28 for x in decoder.array_iter_with(ctx)? {
29 fields.push(x?);
30 }
31
32 let fields = ctx.arena.alloc(fields);
33
34 let data = PlutusData::constr(ctx.arena, x - 121, fields);
35
36 Ok(data)
37 }
38 1280..=1400 => {
39 let mut fields = BumpVec::new_in(ctx.arena.as_bump());
40
41 for x in decoder.array_iter_with(ctx)? {
42 fields.push(x?);
43 }
44
45 let fields = ctx.arena.alloc(fields);
46
47 let data = PlutusData::constr(ctx.arena, (x - 1280) + 7, fields);
48
49 Ok(data)
50 }
51 102 => {
52 let mut fields = BumpVec::new_in(ctx.arena.as_bump());
53
54 let count = decoder.array()?;
55 if count != Some(2) {
56 return Err(minicbor::decode::Error::message(
57 "expected array of length 2 following plutus data tag 102",
58 ));
59 }
60
61 let discriminator_i128: i128 = decoder.int()?.into();
62 let discriminator: u64 = match u64::try_from(discriminator_i128) {
63 Ok(n) => n,
64 Err(_) => {
65 return Err(minicbor::decode::Error::message(format!(
66 "could not cast discriminator from plutus data tag 102 into u64: {discriminator_i128}",
67 )));
68 }
69 };
70
71 for x in decoder.array_iter_with(ctx)? {
72 fields.push(x?);
73 }
74
75 let fields = ctx.arena.alloc(fields);
76
77 let data = PlutusData::constr(ctx.arena, discriminator, fields);
78
79 Ok(data)
80 }
81 _ => {
82 let e = minicbor::decode::Error::message(format!(
83 "unknown tag for plutus data tag: {tag}",
84 ));
85
86 Err(e)
87 }
88 };
89 }
90
91 match tag.try_into() {
92 Ok(x @ IanaTag::PosBignum | x @ IanaTag::NegBignum) => {
93 let _ = decoder.tag()?;
94 let mut bytes = BumpVec::new_in(ctx.arena.as_bump());
95
96 for chunk in decoder.bytes_iter()? {
97 let chunk = chunk?;
98
99 bytes.extend_from_slice(chunk);
100 }
101
102 let n = num::BigInt::from_bytes_be(num_bigint::Sign::Plus, &bytes);
103 let integer = ctx.arena.alloc_integer(if x == IanaTag::PosBignum {
104 n
105 } else {
106 -n - 1
107 });
108
109 Ok(PlutusData::integer(ctx.arena, integer))
110 }
111
112 _ => {
113 let e = minicbor::decode::Error::message(format!(
114 "unknown tag for plutus data tag: {tag}",
115 ));
116
117 Err(e)
118 }
119 }
120 }
121 minicbor::data::Type::Map | minicbor::data::Type::MapIndef => {
122 let mut fields = BumpVec::new_in(ctx.arena.as_bump());
123
124 for x in decoder.map_iter_with(ctx)? {
125 let x = x?;
126
127 fields.push(x);
128 }
129
130 let fields = ctx.arena.alloc(fields);
131
132 Ok(PlutusData::map(ctx.arena, fields))
133 }
134 minicbor::data::Type::Bytes | minicbor::data::Type::BytesIndef => {
135 let mut bs = BumpVec::new_in(ctx.arena.as_bump());
136
137 for chunk in decoder.bytes_iter()? {
138 let chunk = chunk?;
139
140 bs.extend_from_slice(chunk);
141 }
142
143 let bs = ctx.arena.alloc(bs);
144
145 Ok(PlutusData::byte_string(ctx.arena, bs))
146 }
147 minicbor::data::Type::Array | minicbor::data::Type::ArrayIndef => {
148 let mut fields = BumpVec::new_in(ctx.arena.as_bump());
149
150 for x in decoder.array_iter_with(ctx)? {
151 fields.push(x?);
152 }
153
154 let fields = ctx.arena.alloc(fields);
155
156 Ok(PlutusData::list(ctx.arena, fields))
157 }
158 minicbor::data::Type::U8
159 | minicbor::data::Type::U16
160 | minicbor::data::Type::U32
161 | minicbor::data::Type::U64
162 | minicbor::data::Type::I8
163 | minicbor::data::Type::I16
164 | minicbor::data::Type::I32
165 | minicbor::data::Type::I64
166 | minicbor::data::Type::Int => {
167 let i: i128 = decoder.int()?.into();
168
169 Ok(PlutusData::integer_from(ctx.arena, i))
170 }
171 any => {
172 let e = minicbor::decode::Error::message(format!(
173 "bad cbor data type ({any:?}) for plutus data"
174 ));
175
176 Err(e)
177 }
178 }
179 }
180}
181
182fn encode_bytestring<'a, W: minicbor::encode::Write>(
183 e: &'a mut minicbor::Encoder<W>,
184 bs: &[u8],
185) -> Result<&'a mut minicbor::Encoder<W>, minicbor::encode::Error<W::Error>> {
186 const CHUNK_SIZE: usize = 64;
187
188 if bs.len() <= 64 {
189 e.bytes(bs)?;
190 } else {
191 e.begin_bytes()?;
192
193 for b in bs.chunks(CHUNK_SIZE) {
194 e.bytes(b)?;
195 }
196
197 e.end()?;
198 }
199 Ok(e)
200}
201
202impl<C> minicbor::encode::Encode<C> for PlutusData<'_> {
203 fn encode<W: minicbor::encode::Write>(
204 &self,
205 e: &mut minicbor::Encoder<W>,
206 ctx: &mut C,
207 ) -> Result<(), minicbor::encode::Error<W::Error>> {
208 match self {
209 PlutusData::Constr { tag, fields } => {
210 if *tag < 7 {
211 e.tag(Tag::new(*tag + 121))?;
212 } else if *tag <= 127 {
213 e.tag(Tag::new((*tag - 7) + 1280))?;
214 } else {
215 e.tag(Tag::new(102))?;
216 e.array(2)?;
217 e.u64(*tag)?;
218 }
219
220 if fields.is_empty() {
223 e.array(0)?;
224 } else {
225 e.begin_array()?;
230 for f in fields.iter() {
231 f.encode(e, ctx)?;
232 }
233 e.end()?;
234 }
235 }
236 PlutusData::Map(map) => {
240 let len: u64 = map
241 .len()
242 .try_into()
243 .expect("setting map length should work fine");
244
245 e.map(len)?;
246
247 for (k, v) in map.iter() {
248 k.encode(e, ctx)?;
249 v.encode(e, ctx)?;
250 }
251 }
252 PlutusData::Integer(n) => {
253 let (sign, digits) = n.to_u64_digits();
254 match sign {
255 num_bigint::Sign::Plus => {
256 if digits.len() == 1 {
257 e.u64(digits[0])?;
258 } else {
259 e.tag(Tag::new(2))?;
260 let (_sign, bytes) = n.to_bytes_be();
261 encode_bytestring(e, &bytes)?;
262 }
263 }
264 num_bigint::Sign::Minus => {
265 if digits.len() == 1 {
266 let integer =
267 minicbor::data::Int::try_from(-(digits[0] as i128)).unwrap();
268 e.int(integer)?;
269 } else {
270 e.tag(Tag::new(3))?;
271 let abs_minus_one = (-*n) - num::BigInt::from(1);
272 let (_sign, bytes) = abs_minus_one.to_bytes_be();
273 encode_bytestring(e, &bytes)?;
274 }
275 }
276 num_bigint::Sign::NoSign => {
277 e.u8(0)?;
278 }
279 }
280 }
281 PlutusData::ByteString(bs) => {
284 encode_bytestring(e, bs)?;
285 }
286 PlutusData::List(xs) => {
287 if xs.is_empty() {
288 e.array(0)?;
289 } else {
290 e.begin_array()?;
291 for x in xs.iter() {
292 x.encode(e, ctx)?;
293 }
294 e.end()?;
295 }
296 }
297 }
298
299 Ok(())
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306 use crate::arena::Arena;
307
308 #[test]
309 fn encode_empty_record() {
310 let d = PlutusData::Constr {
311 tag: 0,
312 fields: &[],
313 };
314 let mut v = vec![];
315 minicbor::encode(d, &mut v).expect("invalid PlutusData");
316 assert_eq!(hex::encode(v), "d87980");
317 }
318
319 #[test]
320 fn encode_record() {
321 let b1 = PlutusData::ByteString(&[0x00]);
322 let b2 = PlutusData::ByteString(&[0x00, 0x01]);
323 let d = PlutusData::Constr {
324 tag: 1,
325 fields: &[&b1, &b2],
326 };
327 let mut v = vec![];
328 minicbor::encode(d, &mut v).expect("invalid PlutusData");
329 assert_eq!(hex::encode(v), "d87a9f4100420001ff");
330 }
331
332 #[test]
333 fn encode_record_integer() {
334 let zero = num::BigInt::from(0);
335 let one = num::BigInt::from(1);
336 let d = PlutusData::Constr {
337 tag: 128,
338 fields: &[&PlutusData::Integer(&zero), &PlutusData::Integer(&one)],
339 };
340 let mut v = vec![];
341 minicbor::encode(d, &mut v).expect("invalid PlutusData");
342 assert_eq!(hex::encode(v), "d8668218809f0001ff");
343 }
344
345 #[test]
346 fn encode_cbor_data_bigint() {
347 let big = num::BigInt::from_bytes_be(
348 num_bigint::Sign::Plus,
349 &hex::decode("033b2e3c9fd0803ce7ffffff").unwrap(),
350 );
351 let d = PlutusData::Constr {
352 tag: 0,
353 fields: &[&PlutusData::Integer(&big)],
354 };
355 let mut v = vec![];
356 minicbor::encode(d, &mut v).expect("invalid PlutusData");
357 assert_eq!(hex::encode(v), "d8799fc24c033b2e3c9fd0803ce7ffffffff");
358 }
359
360 #[test]
361 fn encode_cbor_data_negative_bigint() {
362 let n = -num::BigInt::from_bytes_be(
363 num_bigint::Sign::Plus,
364 &hex::decode("033b2e3c9fd0803ce7ffffff").unwrap(),
365 ) - num::BigInt::from(1);
366 let d = PlutusData::Constr {
367 tag: 0,
368 fields: &[&PlutusData::Integer(&n)],
369 };
370 let mut v = vec![];
371 minicbor::encode(d, &mut v).expect("invalid PlutusData");
372 assert_eq!(hex::encode(v), "d8799fc34c033b2e3c9fd0803ce7ffffffff");
373 }
374
375 #[test]
376 fn decode_cbor_data_negative_bigint() {
377 let cbor = hex::decode("c34c033b2e3c9fd0803ce7ffffff").unwrap();
378 let arena = Arena::new();
379 let decoded =
380 PlutusData::from_cbor(&arena, &cbor).expect("failed to decode negative bigint");
381 let expected = -num::BigInt::from_bytes_be(
382 num_bigint::Sign::Plus,
383 &hex::decode("033b2e3c9fd0803ce7ffffff").unwrap(),
384 ) - num::BigInt::from(1);
385 assert_eq!(decoded, &PlutusData::Integer(&expected));
386 }
387
388 #[test]
389 fn roundtrip_cbor_data_negative_bigint() {
390 let n = -num::BigInt::from_bytes_be(
391 num_bigint::Sign::Plus,
392 &hex::decode("033b2e3c9fd0803ce7ffffff").unwrap(),
393 ) - num::BigInt::from(1);
394 let encoded = minicbor::to_vec(&PlutusData::Integer(&n)).expect("encode failed");
395 let arena = Arena::new();
396 let decoded = PlutusData::from_cbor(&arena, &encoded).expect("decode failed");
397 assert_eq!(decoded, &PlutusData::Integer(&n));
398 }
399
400 #[test]
401 fn encode_cbor_data_list() {
402 let zero = num::BigInt::from(0);
403 let one = num::BigInt::from(1);
404 let list = [&PlutusData::Integer(&zero), &PlutusData::Integer(&one)];
405 let d = PlutusData::Constr {
406 tag: 0,
407 fields: &[&PlutusData::List(&list)],
408 };
409 let mut v = vec![];
410 minicbor::encode(d, &mut v).expect("invalid PlutusData");
411 assert_eq!(hex::encode(v), "d8799f9f0001ffff");
412 }
413}