From 11ee99c19936558cf80bd115c0ffec690db9068d Mon Sep 17 00:00:00 2001 From: Kasey White Date: Sun, 7 Aug 2022 22:12:26 -0400 Subject: [PATCH] Add several builtins for strings and bytestring Co-authored-by: rvcas --- Cargo.lock | 1 + add_integers.uplc | 2 +- crates/uplc/Cargo.toml | 1 + crates/uplc/src/machine.rs | 4 +- crates/uplc/src/machine/cost_model.rs | 124 ++++++++++-- crates/uplc/src/machine/error.rs | 4 + crates/uplc/src/machine/runtime.rs | 264 ++++++++++++++++++++------ 7 files changed, 324 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21055a6f6..b4e75fef4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -642,6 +642,7 @@ checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" name = "uplc" version = "0.0.7" dependencies = [ + "cryptoxide", "flat-rs", "hex", "minicbor 0.18.0", diff --git a/add_integers.uplc b/add_integers.uplc index 09219e469..8da11bcc5 100644 --- a/add_integers.uplc +++ b/add_integers.uplc @@ -1,5 +1,5 @@ (program 1.0.0 - (con (list (list integer)) [[7], [5]]) + [ (builtin decodeUtf8) [ (builtin encodeUtf8) (con string "hello world") ] ] ) diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index b5e7179c7..6bc177ab0 100644 --- a/crates/uplc/Cargo.toml +++ b/crates/uplc/Cargo.toml @@ -13,6 +13,7 @@ exclude = ["test_data/*"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +cryptoxide = "0.4.2" flat-rs = { path = "../flat", version = "0.0.7" } hex = "0.4.3" minicbor = { version = "0.18.0", features = ["std"] } diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs index 992d32f74..8c4f56b6a 100644 --- a/crates/uplc/src/machine.rs +++ b/crates/uplc/src/machine.rs @@ -334,7 +334,7 @@ impl Machine { self.spend_budget(cost)?; - runtime.call() + runtime.call(&mut self.logs) } else { Ok(Value::Builtin { fun, term, runtime }) } @@ -430,7 +430,7 @@ impl Value { ((i.abs() as f64).log2().floor() as i64 / 64) + 1 } } - Constant::ByteString(b) => (((b.len() - 1) / 8) + 1) as i64, + Constant::ByteString(b) => (((b.len() as i64 - 1) / 8) + 1), Constant::String(s) => s.chars().count() as i64, Constant::Unit => 1, Constant::Bool(_) => 1, diff --git a/crates/uplc/src/machine/cost_model.rs b/crates/uplc/src/machine/cost_model.rs index 9e19eb0e7..7e4e9d448 100644 --- a/crates/uplc/src/machine/cost_model.rs +++ b/crates/uplc/src/machine/cost_model.rs @@ -552,13 +552,40 @@ impl BuiltinCosts { .cpu .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), }, - DefaultFunction::MultiplyInteger => todo!(), + DefaultFunction::MultiplyInteger => ExBudget { + mem: self + .multiply_integer + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .multiply_integer + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, DefaultFunction::DivideInteger => todo!(), DefaultFunction::QuotientInteger => todo!(), DefaultFunction::RemainderInteger => todo!(), DefaultFunction::ModInteger => todo!(), - DefaultFunction::EqualsInteger => todo!(), - DefaultFunction::LessThanInteger => todo!(), + DefaultFunction::EqualsInteger => ExBudget { + mem: self + .equals_integer + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .equals_integer + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::LessThanInteger => ExBudget { + mem: self + .less_than_integer + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .less_than_integer + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, DefaultFunction::LessThanEqualsInteger => ExBudget { mem: self .less_than_equals_integer @@ -569,24 +596,75 @@ impl BuiltinCosts { .cpu .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), }, - DefaultFunction::AppendByteString => todo!(), + DefaultFunction::AppendByteString => ExBudget { + mem: self + .append_byte_string + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .append_byte_string + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, DefaultFunction::ConsByteString => todo!(), DefaultFunction::SliceByteString => todo!(), DefaultFunction::LengthOfByteString => todo!(), DefaultFunction::IndexByteString => todo!(), - DefaultFunction::EqualsByteString => todo!(), + DefaultFunction::EqualsByteString => ExBudget { + mem: self + .equals_byte_string + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .equals_byte_string + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, DefaultFunction::LessThanByteString => todo!(), DefaultFunction::LessThanEqualsByteString => todo!(), - DefaultFunction::Sha2_256 => todo!(), - DefaultFunction::Sha3_256 => todo!(), - DefaultFunction::Blake2b_256 => todo!(), + DefaultFunction::Sha2_256 => ExBudget { + mem: self.sha2_256.mem.cost(args[0].to_ex_mem()), + cpu: self.sha2_256.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::Sha3_256 => ExBudget { + mem: self.sha3_256.mem.cost(args[0].to_ex_mem()), + cpu: self.sha3_256.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::Blake2b_256 => ExBudget { + mem: self.blake2b_256.mem.cost(args[0].to_ex_mem()), + cpu: self.blake2b_256.cpu.cost(args[0].to_ex_mem()), + }, DefaultFunction::VerifySignature => todo!(), DefaultFunction::VerifyEcdsaSecp256k1Signature => todo!(), DefaultFunction::VerifySchnorrSecp256k1Signature => todo!(), - DefaultFunction::AppendString => todo!(), - DefaultFunction::EqualsString => todo!(), - DefaultFunction::EncodeUtf8 => todo!(), - DefaultFunction::DecodeUtf8 => todo!(), + DefaultFunction::AppendString => ExBudget { + mem: self + .append_string + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .append_string + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::EqualsString => ExBudget { + mem: self + .equals_string + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .equals_string + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::EncodeUtf8 => ExBudget { + mem: self.encode_utf8.mem.cost(args[0].to_ex_mem()), + cpu: self.encode_utf8.cpu.cost(args[0].to_ex_mem()), + }, + DefaultFunction::DecodeUtf8 => ExBudget { + mem: self.decode_utf8.mem.cost(args[0].to_ex_mem()), + cpu: self.decode_utf8.cpu.cost(args[0].to_ex_mem()), + }, DefaultFunction::IfThenElse => ExBudget { mem: self.if_then_else.mem.cost( args[0].to_ex_mem(), @@ -599,8 +677,26 @@ impl BuiltinCosts { args[2].to_ex_mem(), ), }, - DefaultFunction::ChooseUnit => todo!(), - DefaultFunction::Trace => todo!(), + DefaultFunction::ChooseUnit => ExBudget { + mem: self + .choose_unit + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .choose_unit + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, + DefaultFunction::Trace => ExBudget { + mem: self + .trace + .mem + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + cpu: self + .trace + .cpu + .cost(args[0].to_ex_mem(), args[1].to_ex_mem()), + }, DefaultFunction::FstPair => todo!(), DefaultFunction::SndPair => todo!(), DefaultFunction::ChooseList => todo!(), diff --git a/crates/uplc/src/machine/error.rs b/crates/uplc/src/machine/error.rs index 4d7c102c8..3b8f27450 100644 --- a/crates/uplc/src/machine/error.rs +++ b/crates/uplc/src/machine/error.rs @@ -1,3 +1,5 @@ +use std::string::FromUtf8Error; + use thiserror::Error; use crate::ast::{NamedDeBruijn, Term, Type}; @@ -28,4 +30,6 @@ pub enum Error { NotAConstant(Value), #[error("The evaluation never reached a final state")] MachineNeverReachedDone, + #[error("Decoding utf8")] + Utf8(#[from] FromUtf8Error), } diff --git a/crates/uplc/src/machine/runtime.rs b/crates/uplc/src/machine/runtime.rs index 41eb74542..a6f1b085a 100644 --- a/crates/uplc/src/machine/runtime.rs +++ b/crates/uplc/src/machine/runtime.rs @@ -46,8 +46,8 @@ impl BuiltinRuntime { self.forces += 1; } - pub fn call(&self) -> Result { - self.fun.call(&self.args) + pub fn call(&self, logs: &mut Vec) -> Result { + self.fun.call(&self.args, logs) } pub fn push(&mut self, arg: Value) -> Result<(), Error> { @@ -74,35 +74,35 @@ impl DefaultFunction { match self { DefaultFunction::AddInteger => 2, DefaultFunction::SubtractInteger => 2, - DefaultFunction::MultiplyInteger => todo!(), + DefaultFunction::MultiplyInteger => 2, DefaultFunction::DivideInteger => todo!(), DefaultFunction::QuotientInteger => todo!(), DefaultFunction::RemainderInteger => todo!(), DefaultFunction::ModInteger => todo!(), - DefaultFunction::EqualsInteger => todo!(), - DefaultFunction::LessThanInteger => todo!(), + DefaultFunction::EqualsInteger => 2, + DefaultFunction::LessThanInteger => 2, DefaultFunction::LessThanEqualsInteger => 2, - DefaultFunction::AppendByteString => todo!(), + DefaultFunction::AppendByteString => 2, DefaultFunction::ConsByteString => todo!(), DefaultFunction::SliceByteString => todo!(), DefaultFunction::LengthOfByteString => todo!(), DefaultFunction::IndexByteString => todo!(), - DefaultFunction::EqualsByteString => todo!(), + DefaultFunction::EqualsByteString => 2, DefaultFunction::LessThanByteString => todo!(), DefaultFunction::LessThanEqualsByteString => todo!(), - DefaultFunction::Sha2_256 => todo!(), - DefaultFunction::Sha3_256 => todo!(), - DefaultFunction::Blake2b_256 => todo!(), + DefaultFunction::Sha2_256 => 1, + DefaultFunction::Sha3_256 => 1, + DefaultFunction::Blake2b_256 => 1, DefaultFunction::VerifySignature => todo!(), DefaultFunction::VerifyEcdsaSecp256k1Signature => todo!(), DefaultFunction::VerifySchnorrSecp256k1Signature => todo!(), - DefaultFunction::AppendString => todo!(), - DefaultFunction::EqualsString => todo!(), - DefaultFunction::EncodeUtf8 => todo!(), - DefaultFunction::DecodeUtf8 => todo!(), + DefaultFunction::AppendString => 2, + DefaultFunction::EqualsString => 2, + DefaultFunction::EncodeUtf8 => 1, + DefaultFunction::DecodeUtf8 => 1, DefaultFunction::IfThenElse => 3, - DefaultFunction::ChooseUnit => todo!(), - DefaultFunction::Trace => todo!(), + DefaultFunction::ChooseUnit => 2, + DefaultFunction::Trace => 2, DefaultFunction::FstPair => todo!(), DefaultFunction::SndPair => todo!(), DefaultFunction::ChooseList => todo!(), @@ -133,35 +133,35 @@ impl DefaultFunction { match self { DefaultFunction::AddInteger => 0, DefaultFunction::SubtractInteger => 0, - DefaultFunction::MultiplyInteger => todo!(), + DefaultFunction::MultiplyInteger => 0, DefaultFunction::DivideInteger => todo!(), DefaultFunction::QuotientInteger => todo!(), DefaultFunction::RemainderInteger => todo!(), DefaultFunction::ModInteger => todo!(), - DefaultFunction::EqualsInteger => todo!(), - DefaultFunction::LessThanInteger => todo!(), + DefaultFunction::EqualsInteger => 0, + DefaultFunction::LessThanInteger => 0, DefaultFunction::LessThanEqualsInteger => 0, - DefaultFunction::AppendByteString => todo!(), + DefaultFunction::AppendByteString => 0, DefaultFunction::ConsByteString => todo!(), DefaultFunction::SliceByteString => todo!(), DefaultFunction::LengthOfByteString => todo!(), DefaultFunction::IndexByteString => todo!(), - DefaultFunction::EqualsByteString => todo!(), + DefaultFunction::EqualsByteString => 0, DefaultFunction::LessThanByteString => todo!(), DefaultFunction::LessThanEqualsByteString => todo!(), - DefaultFunction::Sha2_256 => todo!(), - DefaultFunction::Sha3_256 => todo!(), - DefaultFunction::Blake2b_256 => todo!(), + DefaultFunction::Sha2_256 => 0, + DefaultFunction::Sha3_256 => 0, + DefaultFunction::Blake2b_256 => 0, DefaultFunction::VerifySignature => todo!(), DefaultFunction::VerifyEcdsaSecp256k1Signature => todo!(), DefaultFunction::VerifySchnorrSecp256k1Signature => todo!(), - DefaultFunction::AppendString => todo!(), - DefaultFunction::EqualsString => todo!(), - DefaultFunction::EncodeUtf8 => todo!(), - DefaultFunction::DecodeUtf8 => todo!(), + DefaultFunction::AppendString => 0, + DefaultFunction::EqualsString => 0, + DefaultFunction::EncodeUtf8 => 0, + DefaultFunction::DecodeUtf8 => 0, DefaultFunction::IfThenElse => 1, DefaultFunction::ChooseUnit => 1, - DefaultFunction::Trace => todo!(), + DefaultFunction::Trace => 1, DefaultFunction::FstPair => todo!(), DefaultFunction::SndPair => todo!(), DefaultFunction::ChooseList => todo!(), @@ -192,41 +192,53 @@ impl DefaultFunction { match self { DefaultFunction::AddInteger => arg.expect_type(Type::Integer), DefaultFunction::SubtractInteger => arg.expect_type(Type::Integer), - DefaultFunction::MultiplyInteger => todo!(), + DefaultFunction::MultiplyInteger => arg.expect_type(Type::Integer), DefaultFunction::DivideInteger => todo!(), DefaultFunction::QuotientInteger => todo!(), DefaultFunction::RemainderInteger => todo!(), DefaultFunction::ModInteger => todo!(), - DefaultFunction::EqualsInteger => todo!(), - DefaultFunction::LessThanInteger => todo!(), + DefaultFunction::EqualsInteger => arg.expect_type(Type::Integer), + DefaultFunction::LessThanInteger => arg.expect_type(Type::Integer), DefaultFunction::LessThanEqualsInteger => arg.expect_type(Type::Integer), - DefaultFunction::AppendByteString => todo!(), + DefaultFunction::AppendByteString => arg.expect_type(Type::ByteString), DefaultFunction::ConsByteString => todo!(), DefaultFunction::SliceByteString => todo!(), DefaultFunction::LengthOfByteString => todo!(), DefaultFunction::IndexByteString => todo!(), - DefaultFunction::EqualsByteString => todo!(), + DefaultFunction::EqualsByteString => arg.expect_type(Type::ByteString), DefaultFunction::LessThanByteString => todo!(), DefaultFunction::LessThanEqualsByteString => todo!(), - DefaultFunction::Sha2_256 => todo!(), - DefaultFunction::Sha3_256 => todo!(), - DefaultFunction::Blake2b_256 => todo!(), + DefaultFunction::Sha2_256 => arg.expect_type(Type::ByteString), + DefaultFunction::Sha3_256 => arg.expect_type(Type::ByteString), + DefaultFunction::Blake2b_256 => arg.expect_type(Type::ByteString), DefaultFunction::VerifySignature => todo!(), DefaultFunction::VerifyEcdsaSecp256k1Signature => todo!(), DefaultFunction::VerifySchnorrSecp256k1Signature => todo!(), - DefaultFunction::AppendString => todo!(), - DefaultFunction::EqualsString => todo!(), - DefaultFunction::EncodeUtf8 => todo!(), - DefaultFunction::DecodeUtf8 => todo!(), + DefaultFunction::AppendString => arg.expect_type(Type::String), + DefaultFunction::EqualsString => arg.expect_type(Type::String), + DefaultFunction::EncodeUtf8 => arg.expect_type(Type::String), + DefaultFunction::DecodeUtf8 => arg.expect_type(Type::ByteString), DefaultFunction::IfThenElse => { - if args.is_empty() && !arg.is_bool() { + if args.is_empty() { arg.expect_type(Type::Bool) } else { Ok(()) } } - DefaultFunction::ChooseUnit => todo!(), - DefaultFunction::Trace => todo!(), + DefaultFunction::ChooseUnit => { + if args.is_empty() { + arg.expect_type(Type::Unit) + } else { + Ok(()) + } + } + DefaultFunction::Trace => { + if args.is_empty() { + arg.expect_type(Type::String) + } else { + Ok(()) + } + } DefaultFunction::FstPair => todo!(), DefaultFunction::SndPair => todo!(), DefaultFunction::ChooseList => todo!(), @@ -256,7 +268,7 @@ impl DefaultFunction { // This should be safe because we've already checked // the types of the args as they were pushed. Although // the unreachables look ugly, it's the reality of the situation. - pub fn call(&self, args: &[Value]) -> Result { + pub fn call(&self, args: &[Value], logs: &mut Vec) -> Result { match self { DefaultFunction::AddInteger => { let args = (&args[0], &args[1]); @@ -278,13 +290,40 @@ impl DefaultFunction { _ => unreachable!(), } } - DefaultFunction::MultiplyInteger => todo!(), + DefaultFunction::MultiplyInteger => { + let args = (&args[0], &args[1]); + + match args { + (Value::Con(Constant::Integer(arg1)), Value::Con(Constant::Integer(arg2))) => { + Ok(Value::Con(Constant::Integer(arg1 * arg2))) + } + _ => unreachable!(), + } + } DefaultFunction::DivideInteger => todo!(), DefaultFunction::QuotientInteger => todo!(), DefaultFunction::RemainderInteger => todo!(), DefaultFunction::ModInteger => todo!(), - DefaultFunction::EqualsInteger => todo!(), - DefaultFunction::LessThanInteger => todo!(), + DefaultFunction::EqualsInteger => { + let args = (&args[0], &args[1]); + + match args { + (Value::Con(Constant::Integer(arg1)), Value::Con(Constant::Integer(arg2))) => { + Ok(Value::Con(Constant::Bool(arg1 == arg2))) + } + _ => unreachable!(), + } + } + DefaultFunction::LessThanInteger => { + let args = (&args[0], &args[1]); + + match args { + (Value::Con(Constant::Integer(arg1)), Value::Con(Constant::Integer(arg2))) => { + Ok(Value::Con(Constant::Bool(arg1 < arg2))) + } + _ => unreachable!(), + } + } DefaultFunction::LessThanEqualsInteger => { let args = (&args[0], &args[1]); @@ -295,24 +334,121 @@ impl DefaultFunction { _ => unreachable!(), } } - DefaultFunction::AppendByteString => todo!(), + DefaultFunction::AppendByteString => { + let args = (&args[0], &args[1]); + + match args { + ( + Value::Con(Constant::ByteString(arg1)), + Value::Con(Constant::ByteString(arg2)), + ) => Ok(Value::Con(Constant::ByteString( + arg1.iter().copied().chain(arg2.iter().copied()).collect(), + ))), + _ => unreachable!(), + } + } DefaultFunction::ConsByteString => todo!(), DefaultFunction::SliceByteString => todo!(), DefaultFunction::LengthOfByteString => todo!(), DefaultFunction::IndexByteString => todo!(), - DefaultFunction::EqualsByteString => todo!(), + DefaultFunction::EqualsByteString => { + let args = (&args[0], &args[1]); + + match args { + ( + Value::Con(Constant::ByteString(arg1)), + Value::Con(Constant::ByteString(arg2)), + ) => Ok(Value::Con(Constant::Bool(arg1 == arg2))), + _ => unreachable!(), + } + } DefaultFunction::LessThanByteString => todo!(), DefaultFunction::LessThanEqualsByteString => todo!(), - DefaultFunction::Sha2_256 => todo!(), - DefaultFunction::Sha3_256 => todo!(), - DefaultFunction::Blake2b_256 => todo!(), + DefaultFunction::Sha2_256 => match &args[0] { + Value::Con(Constant::ByteString(arg1)) => { + use cryptoxide::{digest::Digest, sha2::Sha256}; + + let mut hasher = Sha256::new(); + + hasher.input(arg1); + + let mut bytes = vec![0; hasher.output_bytes()]; + + hasher.result(&mut bytes); + + Ok(Value::Con(Constant::ByteString(bytes))) + } + _ => unreachable!(), + }, + DefaultFunction::Sha3_256 => match &args[0] { + Value::Con(Constant::ByteString(arg1)) => { + use cryptoxide::{digest::Digest, sha3::Sha3_256}; + + let mut hasher = Sha3_256::new(); + + hasher.input(arg1); + + let mut bytes = vec![0; hasher.output_bytes()]; + + hasher.result(&mut bytes); + + Ok(Value::Con(Constant::ByteString(bytes))) + } + _ => unreachable!(), + }, + DefaultFunction::Blake2b_256 => match &args[0] { + Value::Con(Constant::ByteString(arg1)) => { + use cryptoxide::{blake2b::Blake2b, digest::Digest}; + + let mut digest = [0u8; 32]; + let mut context = Blake2b::new(32); + + context.input(arg1); + context.result(&mut digest); + + Ok(Value::Con(Constant::ByteString(digest.to_vec()))) + } + _ => unreachable!(), + }, DefaultFunction::VerifySignature => todo!(), DefaultFunction::VerifyEcdsaSecp256k1Signature => todo!(), DefaultFunction::VerifySchnorrSecp256k1Signature => todo!(), - DefaultFunction::AppendString => todo!(), - DefaultFunction::EqualsString => todo!(), - DefaultFunction::EncodeUtf8 => todo!(), - DefaultFunction::DecodeUtf8 => todo!(), + DefaultFunction::AppendString => { + let args = (&args[0], &args[1]); + + match args { + (Value::Con(Constant::String(arg1)), Value::Con(Constant::String(arg2))) => { + Ok(Value::Con(Constant::String(format!("{}{}", arg1, arg2)))) + } + _ => unreachable!(), + } + } + DefaultFunction::EqualsString => { + let args = (&args[0], &args[1]); + + match args { + (Value::Con(Constant::String(arg1)), Value::Con(Constant::String(arg2))) => { + Ok(Value::Con(Constant::Bool(arg1 == arg2))) + } + _ => unreachable!(), + } + } + DefaultFunction::EncodeUtf8 => match &args[0] { + Value::Con(Constant::String(arg1)) => { + let bytes = arg1.as_bytes().to_vec(); + + Ok(Value::Con(Constant::ByteString(bytes))) + } + _ => unreachable!(), + }, + DefaultFunction::DecodeUtf8 => match &args[0] { + Value::Con(Constant::ByteString(arg1)) => { + let string = String::from_utf8(arg1.clone())?; + + Ok(Value::Con(Constant::String(string))) + } + _ => unreachable!(), + }, DefaultFunction::IfThenElse => match args[0] { Value::Con(Constant::Bool(condition)) => { if condition { @@ -323,8 +459,18 @@ impl DefaultFunction { } _ => unreachable!(), }, - DefaultFunction::ChooseUnit => todo!(), - DefaultFunction::Trace => todo!(), + DefaultFunction::ChooseUnit => match &args[0] { + Value::Con(Constant::Unit) => Ok(args[1].clone()), + _ => unreachable!(), + }, + DefaultFunction::Trace => match &args[0] { + Value::Con(Constant::String(arg1)) => { + logs.push(arg1.clone()); + + Ok(args[1].clone()) + } + _ => unreachable!(), + }, DefaultFunction::FstPair => todo!(), DefaultFunction::SndPair => todo!(), DefaultFunction::ChooseList => todo!(),