Skip to main content

amaru_uplc/flat/decode/
mod.rs

1mod decoder;
2mod error;
3
4pub use decoder::Ctx;
5pub use decoder::Decoder;
6pub use error::FlatDecodeError;
7
8use bumpalo::collections::Vec as BumpVec;
9
10use crate::arena::Arena;
11use crate::binder::Binder;
12use crate::machine::PlutusVersion;
13use crate::typ::Type;
14use crate::{
15    constant::Constant,
16    program::{Program, Version},
17    term::Term,
18};
19
20use super::tag;
21use super::{
22    builtin,
23    tag::{BUILTIN_TAG_WIDTH, CONST_TAG_WIDTH, TERM_TAG_WIDTH},
24};
25
26/// Decode a flat-encoded UPLC program, validating builtins against the given
27/// Plutus language version and protocol version. Allows trailing bytes.
28pub fn decode<'a, V>(
29    arena: &'a Arena,
30    bytes: &[u8],
31    plutus_version: PlutusVersion,
32    protocol_version_major: u32,
33) -> Result<&'a Program<'a, V>, FlatDecodeError>
34where
35    V: Binder<'a>,
36{
37    let (program, _remainder) = decode_inner(arena, bytes, plutus_version, protocol_version_major)?;
38    Ok(program)
39}
40
41/// Decode a flat-encoded UPLC program, validating builtins and rejecting
42/// trailing bytes after the filler.
43pub fn decode_strict<'a, V>(
44    arena: &'a Arena,
45    bytes: &[u8],
46    plutus_version: PlutusVersion,
47    protocol_version: u32,
48) -> Result<&'a Program<'a, V>, FlatDecodeError>
49where
50    V: Binder<'a>,
51{
52    let (program, remainder) = decode_inner(arena, bytes, plutus_version, protocol_version)?;
53    if remainder > 0 {
54        return Err(FlatDecodeError::TrailingBytes(remainder));
55    }
56    Ok(program)
57}
58
59fn decode_inner<'a, V>(
60    arena: &'a Arena,
61    bytes: &[u8],
62    plutus_version: PlutusVersion,
63    protocol_version_major: u32,
64) -> Result<(&'a Program<'a, V>, usize), FlatDecodeError>
65where
66    V: Binder<'a>,
67{
68    let mut decoder = Decoder::new(bytes);
69
70    let major = decoder.word()?;
71    let minor = decoder.word()?;
72    let patch = decoder.word()?;
73
74    let version = Version::new(arena, major, minor, patch);
75
76    let mut ctx = Ctx { arena };
77
78    let term = decode_term(
79        &mut ctx,
80        &mut decoder,
81        (plutus_version, protocol_version_major),
82    )?;
83
84    decoder.filler()?;
85
86    let remainder = decoder.buffer.len() - decoder.pos;
87
88    Ok((Program::new(arena, version, term), remainder))
89}
90
91fn decode_term<'a, V>(
92    ctx: &mut Ctx<'a>,
93    decoder: &mut Decoder<'_>,
94    version_check: (PlutusVersion, u32),
95) -> Result<&'a Term<'a, V>, FlatDecodeError>
96where
97    V: Binder<'a>,
98{
99    let tag = decoder.bits8(TERM_TAG_WIDTH)?;
100
101    match tag {
102        // Var
103        tag::VAR => Ok(Term::var(ctx.arena, V::var_decode(ctx.arena, decoder)?)),
104        // Delay
105        tag::DELAY => {
106            let term = decode_term(ctx, decoder, version_check)?;
107
108            Ok(term.delay(ctx.arena))
109        }
110        // Lambda
111        tag::LAMBDA => {
112            let param = V::parameter_decode(ctx.arena, decoder)?;
113
114            let term = decode_term(ctx, decoder, version_check)?;
115
116            Ok(term.lambda(ctx.arena, param))
117        }
118        // Apply
119        tag::APPLY => {
120            let function = decode_term(ctx, decoder, version_check)?;
121            let argument = decode_term(ctx, decoder, version_check)?;
122
123            let term = function.apply(ctx.arena, argument);
124
125            Ok(term)
126        }
127        // Constant
128        tag::CONSTANT => {
129            let constant = decode_constant(ctx, decoder)?;
130
131            Ok(Term::constant(ctx.arena, constant))
132        }
133        // Force
134        tag::FORCE => {
135            let term = decode_term(ctx, decoder, version_check)?;
136
137            Ok(term.force(ctx.arena))
138        }
139        // Error
140        tag::ERROR => Ok(Term::error(ctx.arena)),
141        // Builtin
142        tag::BUILTIN => {
143            let builtin_tag = decoder.bits8(BUILTIN_TAG_WIDTH)?;
144
145            let function = builtin::try_from_tag(ctx.arena, builtin_tag)?;
146
147            let (plutus_version, protocol_version_major) = version_check;
148            if !function.is_available_in(plutus_version, protocol_version_major) {
149                return Err(FlatDecodeError::BuiltinNotAvailable(
150                    builtin_tag,
151                    format!("{:?}", function),
152                ));
153            }
154
155            let term = Term::builtin(ctx.arena, function);
156
157            Ok(term)
158        }
159        // Constr
160        tag::CONSTR => {
161            let tag = decoder.word()?;
162            let fields = decoder.list_with(ctx, |ctx, d| decode_term(ctx, d, version_check))?;
163            let fields = ctx.arena.alloc(fields);
164
165            let term = Term::constr(ctx.arena, tag, fields);
166
167            Ok(term)
168        }
169        // Case
170        tag::CASE => {
171            let constr = decode_term(ctx, decoder, version_check)?;
172            let branches = decoder.list_with(ctx, |ctx, d| decode_term(ctx, d, version_check))?;
173            let branches = ctx.arena.alloc(branches);
174
175            Ok(Term::case(ctx.arena, constr, branches))
176        }
177        _ => Err(FlatDecodeError::UnknownTermConstructor(tag)),
178    }
179}
180
181fn type_from_tags<'a>(
182    ctx: &Ctx<'a>,
183    tags: &[u8],
184) -> Result<(&'a Type<'a>, usize), FlatDecodeError> {
185    match tags {
186        [tag::INTEGER, ..] => Ok((Type::integer(ctx.arena), 1)),
187        [tag::BYTE_STRING, ..] => Ok((Type::byte_string(ctx.arena), 1)),
188        [tag::STRING, ..] => Ok((Type::string(ctx.arena), 1)),
189        [tag::UNIT, ..] => Ok((Type::unit(ctx.arena), 1)),
190        [tag::BOOL, ..] => Ok((Type::bool(ctx.arena), 1)),
191        [tag::DATA, ..] => Ok((Type::data(ctx.arena), 1)),
192        [tag::PROTO_LIST_ONE, tag::PROTO_LIST_TWO, rest @ ..] => {
193            let (sub_typ, consumed) = type_from_tags(ctx, rest)?;
194            Ok((Type::list(ctx.arena, sub_typ), 2 + consumed))
195        }
196        [tag::PROTO_ARRAY_ONE, tag::PROTO_ARRAY_TWO, rest @ ..] => {
197            let (sub_typ, consumed) = type_from_tags(ctx, rest)?;
198            Ok((Type::array(ctx.arena, sub_typ), 2 + consumed))
199        }
200        [tag::PROTO_PAIR_ONE, tag::PROTO_PAIR_TWO, tag::PROTO_PAIR_THREE, rest @ ..] => {
201            let (sub_typ1, consumed1) = type_from_tags(ctx, rest)?;
202            let rest2 = &rest[consumed1..];
203            let (sub_typ2, consumed2) = type_from_tags(ctx, rest2)?;
204
205            Ok((
206                Type::pair(ctx.arena, sub_typ1, sub_typ2),
207                3 + consumed1 + consumed2,
208            ))
209        }
210        [] => Err(FlatDecodeError::MissingTypeTag),
211        x => Err(FlatDecodeError::UnknownTypeTags(x.to_vec())),
212    }
213}
214
215// BLS literals not supported
216fn decode_constant<'a>(
217    ctx: &mut Ctx<'a>,
218    d: &mut Decoder,
219) -> Result<&'a Constant<'a>, FlatDecodeError> {
220    let tags = decode_constant_tags(ctx, d)?;
221    let (ty, _) = type_from_tags(ctx, tags.as_slice())?;
222
223    match ty {
224        Type::Integer => {
225            let v = d.integer()?;
226            let v = ctx.arena.alloc_integer(v);
227
228            Ok(Constant::integer(ctx.arena, v))
229        }
230        Type::ByteString => {
231            let b = d.bytes(ctx.arena)?;
232            let b = ctx.arena.alloc(b);
233
234            Ok(Constant::byte_string(ctx.arena, b))
235        }
236        Type::Bool => {
237            let v = d.bit()?;
238
239            Ok(Constant::bool(ctx.arena, v))
240        }
241        Type::String => {
242            let s = d.utf8(ctx.arena)?;
243            let s = ctx.arena.alloc(s);
244
245            Ok(Constant::string(ctx.arena, s))
246        }
247        Type::Unit => Ok(Constant::unit(ctx.arena)),
248        Type::List(sub_typ) => {
249            let fields = d.list_with(ctx, |ctx, d| decode_constant_with_type(ctx, d, sub_typ))?;
250            let fields = ctx.arena.alloc(fields);
251
252            Ok(Constant::proto_list(ctx.arena, sub_typ, fields))
253        }
254
255        Type::Array(sub_typ) => {
256            let fields = d.list_with(ctx, |ctx, d| decode_constant_with_type(ctx, d, sub_typ))?;
257            let fields = ctx.arena.alloc(fields);
258            Ok(Constant::proto_array(ctx.arena, sub_typ, fields))
259        }
260        Type::Pair(sub_typ1, sub_typ2) => {
261            let fst = decode_constant_with_type(ctx, d, sub_typ1)?;
262            let snd = decode_constant_with_type(ctx, d, sub_typ2)?;
263
264            Ok(Constant::proto_pair(
265                ctx.arena, sub_typ1, sub_typ2, fst, snd,
266            ))
267        }
268        Type::Data => {
269            let cbor = d.bytes(ctx.arena)?;
270            let data = minicbor::decode_with(&cbor, ctx)?;
271            Ok(Constant::data(ctx.arena, data))
272        }
273        Type::Bls12_381G1Element => Err(FlatDecodeError::BlsTypeNotSupported),
274        Type::Bls12_381G2Element => Err(FlatDecodeError::BlsTypeNotSupported),
275        Type::Bls12_381MlResult => Err(FlatDecodeError::BlsTypeNotSupported),
276    }
277}
278
279// BLS literals not supported
280fn decode_constant_with_type<'a>(
281    ctx: &mut Ctx<'a>,
282    d: &mut Decoder,
283    ty: &Type<'a>,
284) -> Result<&'a Constant<'a>, FlatDecodeError> {
285    match ty {
286        Type::Integer => {
287            let v = d.integer()?;
288            let v = ctx.arena.alloc_integer(v);
289
290            Ok(Constant::integer(ctx.arena, v))
291        }
292        Type::ByteString => {
293            let b = d.bytes(ctx.arena)?;
294            let b = ctx.arena.alloc(b);
295
296            Ok(Constant::byte_string(ctx.arena, b))
297        }
298        Type::Bool => {
299            let v = d.bit()?;
300
301            Ok(Constant::bool(ctx.arena, v))
302        }
303        Type::String => {
304            let s = d.utf8(ctx.arena)?;
305            let s = ctx.arena.alloc(s);
306
307            Ok(Constant::string(ctx.arena, s))
308        }
309        Type::Unit => Ok(Constant::unit(ctx.arena)),
310        Type::List(sub_typ) => {
311            let fields = d.list_with(ctx, |ctx, d| decode_constant_with_type(ctx, d, sub_typ))?;
312            let fields = ctx.arena.alloc(fields);
313
314            Ok(Constant::proto_list(ctx.arena, sub_typ, fields))
315        }
316        Type::Array(sub_typ) => {
317            let fields = d.list_with(ctx, |ctx, d| decode_constant_with_type(ctx, d, sub_typ))?;
318            let fields = ctx.arena.alloc(fields);
319            Ok(Constant::proto_array(ctx.arena, sub_typ, fields))
320        }
321        Type::Pair(sub_typ1, sub_typ2) => {
322            let fst = decode_constant_with_type(ctx, d, sub_typ1)?;
323            let snd = decode_constant_with_type(ctx, d, sub_typ2)?;
324
325            Ok(Constant::proto_pair(
326                ctx.arena, sub_typ1, sub_typ2, fst, snd,
327            ))
328        }
329        Type::Data => {
330            let cbor = d.bytes(ctx.arena)?;
331            let data = minicbor::decode_with(&cbor, ctx)?;
332
333            Ok(Constant::data(ctx.arena, data))
334        }
335        Type::Bls12_381G1Element => Err(FlatDecodeError::BlsTypeNotSupported),
336        Type::Bls12_381G2Element => Err(FlatDecodeError::BlsTypeNotSupported),
337        Type::Bls12_381MlResult => Err(FlatDecodeError::BlsTypeNotSupported),
338    }
339}
340
341fn decode_constant_tags<'a>(
342    ctx: &mut Ctx<'a>,
343    d: &mut Decoder,
344) -> Result<BumpVec<'a, u8>, FlatDecodeError> {
345    d.list_with(ctx, |_arena, d| decode_constant_tag(d))
346}
347
348fn decode_constant_tag(d: &mut Decoder) -> Result<u8, FlatDecodeError> {
349    d.bits8(CONST_TAG_WIDTH)
350}
351
352#[cfg(test)]
353mod tests {
354    use super::*;
355    use crate::{arena::Arena, binder::DeBruijn};
356    use hex;
357    use num::BigInt;
358
359    #[test]
360    fn decode_program_big_constr_tag() {
361        // (program 1.1.0
362        //   [
363        //     [
364        //       (builtin addInteger)
365        //       (con integer 1)
366        //     ]
367        //     [ (force (force (builtin fstPair)))
368        //       [ (builtin unConstrData)
369        //         (con data (Constr 128 [I 0, I 1]))
370        //       ]
371        //     ]
372        //   ])
373        let bytes = hex::decode("0101003370090011aab9d375498109d8668218809f0001ff0001").unwrap();
374        let arena = Arena::new();
375        let program: Result<&Program<DeBruijn>, _> = decode(&arena, &bytes, PlutusVersion::V3, 9);
376        match program {
377            Ok(program) => {
378                let eval_result = program.eval(&arena);
379                let term = eval_result.term.unwrap();
380                assert_eq!(
381                    term,
382                    &Term::Constant(&Constant::Integer(&BigInt::from(129)))
383                );
384            }
385            Err(_) => {
386                panic!();
387            }
388        }
389    }
390
391    #[test]
392    fn decode_program_bigint() {
393        // (program 1.1.0
394        //   [
395        //     [
396        //       (builtin addInteger)
397        //       (con integer 1)
398        //     ]
399        //     [ (builtin unIData)
400        //       [ (force (builtin headList))
401        //         [ (force (force (builtin sndPair)))
402        //           [ (builtin unConstrData)
403        //             (con data (Constr 0 [I 999999999999999999999999999]))
404        //           ]
405        //         ]
406        //       ]
407        //     ]
408        //   ])
409        let bytes = hex::decode(
410            "0101003370090011bad357426aae78dd526112d8799fc24c033b2e3c9fd0803ce7ffffffff0001",
411        )
412        .unwrap();
413        let arena = Arena::new();
414        let program: Result<&Program<DeBruijn>, _> = decode(&arena, &bytes, PlutusVersion::V3, 9);
415        match program {
416            Ok(program) => {
417                let eval_result = program.eval(&arena);
418                let term = eval_result.term.unwrap();
419                assert_eq!(
420                    term,
421                    &Term::Constant(&Constant::Integer(&BigInt::from(
422                        1_000_000_000_000_000_000_000_000_000i128
423                    )))
424                );
425            }
426            Err(e) => {
427                panic!("{}", e);
428            }
429        }
430    }
431
432    #[test]
433    fn decode_program_list() {
434        // (program 1.1.0
435        //   [
436        //     [
437        //       (builtin multiplyInteger)
438        //       (con integer 2)
439        //     ]
440        //     [ (builtin unIData)
441        //       [ (force (builtin headList))
442        //         [ (force (builtin tailList))
443        //           [ (builtin unListData)
444        //             (con data (List [I 7, I 14]))
445        //           ]
446        //         ]
447        //       ]
448        //     ]
449        //   ])
450        let bytes = hex::decode("0101003370490021bad357426ae88dd62601049f070eff0001").unwrap();
451        let arena = Arena::new();
452        let program: Result<&Program<DeBruijn>, _> = decode(&arena, &bytes, PlutusVersion::V3, 9);
453        match program {
454            Ok(program) => {
455                let eval_result = program.eval(&arena);
456                let term = eval_result.term.unwrap();
457                assert_eq!(term, &Term::Constant(&Constant::Integer(&BigInt::from(28))));
458            }
459            Err(e) => {
460                panic!("{}", e);
461            }
462        }
463    }
464}