From d14920265e6eff86e117281a36cc0cb62fe8a79f Mon Sep 17 00:00:00 2001 From: Kasey White Date: Thu, 4 Aug 2022 02:36:39 -0400 Subject: [PATCH] flat encoding for list and pairs Co-authored-by: rvcas --- add_integers.uplc | 2 +- crates/flat/src/encode/encoder.rs | 11 ++- crates/uplc/src/ast.rs | 33 ++++++- crates/uplc/src/flat.rs | 142 ++++++++++++++++++++++++++--- crates/uplc/src/machine.rs | 12 ++- crates/uplc/src/machine/error.rs | 25 +---- crates/uplc/src/machine/runtime.rs | 6 +- crates/uplc/src/pretty.rs | 3 + 8 files changed, 187 insertions(+), 47 deletions(-) diff --git a/add_integers.uplc b/add_integers.uplc index f4f299d5..09219e46 100644 --- a/add_integers.uplc +++ b/add_integers.uplc @@ -1,5 +1,5 @@ (program 1.0.0 - [ (builtin subtractInteger ) (con integer 7) (con integer 4) ] + (con (list (list integer)) [[7], [5]]) ) diff --git a/crates/flat/src/encode/encoder.rs b/crates/flat/src/encode/encoder.rs index 3b8c3252..eabb45e6 100644 --- a/crates/flat/src/encode/encoder.rs +++ b/crates/flat/src/encode/encoder.rs @@ -156,11 +156,14 @@ impl Encoder { /// This is byte alignment agnostic. /// If there are bytes in a list then write 1 bit followed by the functions encoding. /// After the last item write a 0 bit. If the list is empty only encode a 0 bit. - pub fn encode_list_with( + pub fn encode_list_with( &mut self, - list: Vec, - encoder_func: for<'r> fn(u8, &'r mut Encoder) -> Result<(), Error>, - ) -> Result<&mut Self, Error> { + list: &[T], + encoder_func: for<'r> fn(&T, &'r mut Encoder) -> Result<(), Error>, + ) -> Result<&mut Self, Error> + where + T: Encode, + { for item in list { self.one(); encoder_func(item, self)?; diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index 5f64d602..5ccfcbca 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -103,13 +103,42 @@ pub enum Constant { // tag: 4 Bool(bool), // tag: 5 - ProtoList(Vec), + ProtoList(Type, Vec), // tag: 6 - ProtoPair(Box, Box), + ProtoPair(Type, Type, Box, Box), + // tag: 7 + // Apply(Box, Type), // tag: 8 Data(Data), } +#[derive(Debug, Clone, PartialEq)] +pub enum Type { + Bool, + Integer, + String, + ByteString, + Unit, + List(Box), + Pair(Box, Box), + Data, +} + +impl Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Type::Bool => write!(f, "bool"), + Type::Integer => write!(f, "integer"), + Type::String => write!(f, "string"), + Type::ByteString => write!(f, "bytestring"), + Type::Unit => write!(f, "unit"), + Type::List(t) => write!(f, "list {}", t), + Type::Pair(t1, t2) => write!(f, "pair {} {}", t1, t2), + Type::Data => write!(f, "data"), + } + } +} + #[derive(Debug, Clone, PartialEq)] pub enum Data { Constr(isize, Vec), diff --git a/crates/uplc/src/flat.rs b/crates/uplc/src/flat.rs index 497bccbf..04f191aa 100644 --- a/crates/uplc/src/flat.rs +++ b/crates/uplc/src/flat.rs @@ -7,7 +7,9 @@ use flat_rs::{ }; use crate::{ - ast::{Constant, DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term, Unique}, + ast::{ + Constant, DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term, Type, Unique, + }, builtins::DefaultFunction, }; @@ -161,7 +163,7 @@ where } } -impl Encode for &Constant { +impl Encode for Constant { fn encode(&self, e: &mut Encoder) -> Result<(), en::Error> { match self { // Integers are typically smaller so we save space @@ -171,29 +173,93 @@ impl Encode for &Constant { // i.e. A 17 or greater length byte array loses efficiency being encoded as // a unsigned integer instead of a byte array Constant::Integer(i) => { - encode_constant(0, e)?; + encode_constant(&[0], e)?; i.encode(e)?; } Constant::ByteString(bytes) => { - encode_constant(1, e)?; + encode_constant(&[1], e)?; bytes.encode(e)?; } Constant::String(s) => { - encode_constant(2, e)?; + encode_constant(&[2], e)?; s.encode(e)?; } - Constant::Unit => encode_constant(3, e)?, + Constant::Unit => encode_constant(&[3], e)?, Constant::Bool(b) => { - encode_constant(4, e)?; + encode_constant(&[4], e)?; b.encode(e)?; } + Constant::ProtoList(typ, list) => { + let mut type_encode = vec![7, 5]; + + encode_type(typ, &mut type_encode); + + encode_constant(&type_encode, e)?; + + e.encode_list_with(list, encode_constant_value)?; + } + Constant::ProtoPair(type1, type2, a, b) => { + let mut type_encode = vec![7, 7, 6]; + + encode_type(type1, &mut type_encode); + + encode_type(type2, &mut type_encode); + + encode_constant(&type_encode, e)?; + encode_constant_value(a, e)?; + encode_constant_value(b, e)?; + } + Constant::Data(_) => { + encode_constant(&[8], e)?; + todo!() + } } Ok(()) } } +fn encode_constant_value(x: &Constant, e: &mut Encoder) -> Result<(), en::Error> { + match x { + Constant::Integer(x) => x.encode(e), + Constant::ByteString(b) => b.encode(e), + Constant::String(s) => s.encode(e), + Constant::Unit => Ok(()), + Constant::Bool(b) => b.encode(e), + Constant::ProtoList(_, list) => { + e.encode_list_with(list, encode_constant_value)?; + Ok(()) + } + Constant::ProtoPair(_, _, a, b) => { + encode_constant_value(a, e)?; + encode_constant_value(b, e)?; + Ok(()) + } + Constant::Data(_) => todo!(), + } +} + +fn encode_type(typ: &Type, bytes: &mut Vec) { + match typ { + Type::Bool => bytes.push(4), + Type::Integer => bytes.push(0), + Type::String => bytes.push(2), + Type::ByteString => bytes.push(1), + Type::Unit => bytes.push(3), + Type::List(sub_typ) => { + bytes.extend(vec![7, 5]); + encode_type(sub_typ, bytes); + } + Type::Pair(type1, type2) => { + bytes.extend(vec![7, 7, 6]); + encode_type(type1, bytes); + encode_type(type2, bytes); + } + Type::Data => todo!(), + } +} + impl<'b> Decode<'b> for Constant { fn decode(d: &mut Decoder) -> Result { match decode_constant(d)? { @@ -392,8 +458,8 @@ fn safe_encode_bits(num_bits: u32, byte: u8, e: &mut Encoder) -> Result<(), en:: } } -pub fn encode_constant(tag: u8, e: &mut Encoder) -> Result<(), en::Error> { - e.encode_list_with([tag].to_vec(), encode_constant_tag)?; +pub fn encode_constant(tag: &[u8], e: &mut Encoder) -> Result<(), en::Error> { + e.encode_list_with(tag, encode_constant_tag)?; Ok(()) } @@ -410,8 +476,8 @@ pub fn decode_constant(d: &mut Decoder) -> Result { } } -pub fn encode_constant_tag(tag: u8, e: &mut Encoder) -> Result<(), en::Error> { - safe_encode_bits(CONST_TAG_WIDTH, tag, e) +pub fn encode_constant_tag(tag: &u8, e: &mut Encoder) -> Result<(), en::Error> { + safe_encode_bits(CONST_TAG_WIDTH, *tag, e) } pub fn decode_constant_tag(d: &mut Decoder) -> Result { @@ -422,7 +488,7 @@ pub fn decode_constant_tag(d: &mut Decoder) -> Result { mod test { use flat_rs::Flat; - use crate::ast::Name; + use crate::ast::{Name, Type}; use super::{Constant, Program, Term}; @@ -441,6 +507,58 @@ mod test { ) } + #[test] + fn flat_encode_list_list_integer() { + let program = Program:: { + version: (1, 0, 0), + term: Term::Constant(Constant::ProtoList( + Type::List(Box::new(Type::Integer)), + vec![ + Constant::ProtoList(Type::Integer, vec![Constant::Integer(7)]), + Constant::ProtoList(Type::Integer, vec![Constant::Integer(5)]), + ], + )), + }; + + let bytes = program.to_flat().unwrap(); + + assert_eq!( + bytes, + vec![ + 0b00000001, 0b00000000, 0b00000000, 0b01001011, 0b11010110, 0b11110101, 0b10000011, + 0b00001110, 0b01100001, 0b01000001 + ] + ) + } + + #[test] + fn flat_encode_pair_pair_integer_bool_integer() { + let program = Program:: { + version: (1, 0, 0), + term: Term::Constant(Constant::ProtoPair( + Type::Pair(Box::new(Type::Integer), Box::new(Type::Bool)), + Type::Integer, + Box::new(Constant::ProtoPair( + Type::Integer, + Type::Bool, + Box::new(Constant::Integer(11)), + Box::new(Constant::Bool(true)), + )), + Box::new(Constant::Integer(11)), + )), + }; + + let bytes = program.to_flat().unwrap(); + + assert_eq!( + bytes, + vec![ + 0b00000001, 0b00000000, 0b00000000, 0b01001011, 0b11011110, 0b11010111, 0b10111101, + 0b10100001, 0b01001000, 0b00000101, 0b10100010, 0b11000001 + ] + ) + } + #[test] fn flat_decode_integer() { let flat_encoded = vec![ diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs index 5f6a3626..992d32f7 100644 --- a/crates/uplc/src/machine.rs +++ b/crates/uplc/src/machine.rs @@ -1,5 +1,5 @@ use crate::{ - ast::{Constant, NamedDeBruijn, Term}, + ast::{Constant, NamedDeBruijn, Term, Type}, builtins::DefaultFunction, }; @@ -10,7 +10,7 @@ mod runtime; use cost_model::{ExBudget, StepKind}; pub use error::Error; -use self::{cost_model::CostModel, error::Type, runtime::BuiltinRuntime}; +use self::{cost_model::CostModel, runtime::BuiltinRuntime}; enum MachineStep { Return(Context, Value), @@ -434,6 +434,9 @@ impl Value { Constant::String(s) => s.chars().count() as i64, Constant::Unit => 1, Constant::Bool(_) => 1, + Constant::ProtoList(_, _) => todo!(), + Constant::ProtoPair(_, _, _, _) => todo!(), + Constant::Data(_) => todo!(), }, Value::Delay(_, _) => 1, Value::Lambda { .. } => 1, @@ -465,6 +468,11 @@ impl From<&Constant> for Type { Constant::String(_) => Type::String, Constant::Unit => Type::Unit, Constant::Bool(_) => Type::Bool, + Constant::ProtoList(t, _) => Type::List(Box::new(t.clone())), + Constant::ProtoPair(t1, t2, _, _) => { + Type::Pair(Box::new(t1.clone()), Box::new(t2.clone())) + } + Constant::Data(_) => todo!(), } } } diff --git a/crates/uplc/src/machine/error.rs b/crates/uplc/src/machine/error.rs index 590a06bf..4d7c102c 100644 --- a/crates/uplc/src/machine/error.rs +++ b/crates/uplc/src/machine/error.rs @@ -1,8 +1,6 @@ -use std::fmt::Display; - use thiserror::Error; -use crate::ast::{NamedDeBruijn, Term}; +use crate::ast::{NamedDeBruijn, Term, Type}; use super::{ExBudget, Value}; @@ -31,24 +29,3 @@ pub enum Error { #[error("The evaluation never reached a final state")] MachineNeverReachedDone, } - -#[derive(Debug, Clone, PartialEq)] -pub enum Type { - Bool, - Integer, - String, - ByteString, - Unit, -} - -impl Display for Type { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Type::Bool => write!(f, "bool"), - Type::Integer => write!(f, "integer"), - Type::String => write!(f, "string"), - Type::ByteString => write!(f, "bytestring"), - Type::Unit => write!(f, "unit"), - } - } -} diff --git a/crates/uplc/src/machine/runtime.rs b/crates/uplc/src/machine/runtime.rs index dc46b1d9..41eb7454 100644 --- a/crates/uplc/src/machine/runtime.rs +++ b/crates/uplc/src/machine/runtime.rs @@ -1,8 +1,10 @@ -use crate::{ast::Constant, builtins::DefaultFunction}; +use crate::{ + ast::{Constant, Type}, + builtins::DefaultFunction, +}; use super::{ cost_model::{BuiltinCosts, ExBudget}, - error::Type, Error, Value, }; diff --git a/crates/uplc/src/pretty.rs b/crates/uplc/src/pretty.rs index cf4ddc54..94461883 100644 --- a/crates/uplc/src/pretty.rs +++ b/crates/uplc/src/pretty.rs @@ -170,6 +170,9 @@ impl Constant { Constant::Bool(b) => RcDoc::text("bool") .append(RcDoc::line()) .append(RcDoc::text(if *b { "True" } else { "False" })), + Constant::ProtoList(_, _) => todo!(), + Constant::ProtoPair(_, _, _, _) => todo!(), + Constant::Data(_) => todo!(), } } }