diff --git a/crates/aiken-project/src/tests/gen_uplc.rs b/crates/aiken-project/src/tests/gen_uplc.rs index c52e7a0d..095f1f93 100644 --- a/crates/aiken-project/src/tests/gen_uplc.rs +++ b/crates/aiken-project/src/tests/gen_uplc.rs @@ -9,6 +9,7 @@ use aiken_lang::{ }; use uplc::{ ast::{Constant, Data, DeBruijn, Name, Program, Term, Type}, + builder::CONSTR_GET_FIELD, machine::cost_model::ExBudget, optimize, }; @@ -1373,228 +1374,265 @@ fn acceptance_test_14_list_creation() { ); } -// #[test] -// fn when_tuple_deconstruction() { -// let src = r#" -// type Thing { -// idx: Int, -// } +#[test] +fn when_tuple_deconstruction() { + let src = r#" + type Thing { + idx: Int, + } -// type Datum { -// A(Thing) -// B -// } + type Datum { + A(Thing) + B + } -// type RedSpend { -// Spend(Int) -// Buy -// } + type RedSpend { + Spend(Int) + Buy + } -// validator { -// fn spend(dat: Datum, red: RedSpend, ctx: Data) { -// when (dat, red) is { -// (A(a), Spend(x)) -> -// (a.idx == x)? -// (_, _) -> -// True -// } -// } -// } -// "#; + validator { + fn spend(dat: Datum, red: RedSpend, ctx: Data) { + when (dat, red) is { + (A(a), Spend(x)) -> + (a.idx == x)? + (_, _) -> + True + } + } + } + "#; -// assert_uplc( -// src, -// Term::equals_integer() -// .apply(Term::integer(0.into())) -// .apply(Term::var("constr_index_exposer").apply(Term::var("dat"))) -// .if_else( -// Term::equals_integer() -// .apply(Term::integer(0.into())) -// .apply(Term::var("constr_index_exposer").apply(Term::var("red"))) -// .if_else( -// Term::equals_integer() -// .apply( -// Term::un_i_data().apply( -// Term::var("constr_get_field") -// .apply( -// Term::var("constr_fields_exposer") -// .apply(Term::var("a")), -// ) -// .apply(Term::integer(0.into())), -// ), -// ) -// .apply(Term::var("x")) -// .lambda("x") -// .apply( -// Term::un_i_data() -// .apply(Term::head_list().apply(Term::var("red_constr_fields"))), -// ) -// .lambda("red_constr_fields") -// .apply(Term::var("constr_fields_exposer").apply(Term::var("red"))) -// .delay(), -// Term::var("other_clauses"), -// ) -// .force() -// .lambda("a") -// .apply(Term::head_list().apply(Term::var("dat_constr_fields"))) -// .lambda("dat_constr_fields") -// .apply(Term::var("constr_fields_exposer").apply(Term::var("dat"))) -// .delay(), -// Term::var("other_clauses"), -// ) -// .force() -// .lambda("other_clauses") -// .apply(Term::bool(true).delay()) -// .lambda("dat") -// .apply(Term::fst_pair().apply(Term::var("pair_subject"))) -// .lambda("red") -// .apply(Term::snd_pair().apply(Term::var("pair_subject"))) -// .lambda("pair_subject") -// .apply( -// Term::mk_pair_data() -// .apply(Term::var("dat")) -// .apply(Term::var("red")), -// ) -// .delayed_if_else(Term::unit(), Term::Error) -// .lambda("dat") -// .apply( -// Term::var("expect_Datum") -// .lambda("expect_Datum") -// .apply( -// Term::equals_integer() -// .apply(Term::integer(0.into())) -// .apply(Term::var("constr_index_exposer").apply(Term::var("dat"))) -// .delayed_if_else( -// Term::tail_list() -// .apply(Term::var("dat_constr_fields")) -// .delayed_choose_list( -// Term::var("expect_Thing") -// .apply(Term::var("field_1")) -// .choose_unit(Term::unit()) -// .lambda("field_1") -// .apply( -// Term::head_list() -// .apply(Term::var("dat_constr_fields")), -// ), -// Term::Error.trace(Term::string("Expected...")), -// ) -// .lambda("dat_constr_fields") -// .apply( -// Term::var("constr_fields_exposer").apply(Term::var("dat")), -// ), -// Term::equals_integer() -// .apply(Term::integer(1.into())) -// .apply( -// Term::var("constr_index_exposer").apply(Term::var("dat")), -// ) -// .delayed_if_else( -// Term::unit().lambda("_").apply( -// Term::var("constr_fields_exposer") -// .apply(Term::var("dat")) -// .delayed_choose_list( -// Term::unit(), -// Term::Error -// .trace(Term::string("Expected......")), -// ), -// ), -// Term::Error.trace(Term::string("Expected...")), -// ), -// ) -// .lambda("dat"), -// ) -// .lambda("expect_Thing") -// .apply( -// Term::equals_integer() -// .apply(Term::integer(0.into())) -// .apply(Term::var("constr_index_exposer").apply(Term::var("field_1"))) -// .delayed_if_else( -// Term::tail_list() -// .apply(Term::var("field_1_constr_fields")) -// .delayed_choose_list( -// Term::unit().lambda("_").apply( -// Term::un_i_data().apply( -// Term::head_list() -// .apply(Term::var("field_1_constr_fields")), -// ), -// ), -// Term::Error.trace(Term::string("Expected...")), -// ) -// .lambda("field_1_constr_fields") -// .apply( -// Term::var("constr_fields_exposer") -// .apply(Term::var("field_1")), -// ), -// Term::Error.trace(Term::string("Expected...")), -// ) -// .lambda("field_1"), -// ) -// .apply(Term::var("dat")) -// .choose_unit(Term::var("dat")), -// ) -// .lambda("red") -// .apply( -// Term::var("expect_RedSpend") -// .lambda("expect_RedSpend") -// .apply( -// Term::equals_integer() -// .apply(Term::integer(0.into())) -// .apply(Term::var("constr_index_exposer").apply(Term::var("red"))) -// .delayed_if_else( -// Term::tail_list() -// .apply(Term::var("red_constr_fields")) -// .delayed_choose_list( -// Term::unit().lambda("_").apply(Term::un_i_data().apply( -// Term::head_list().apply(Term::var("red_constr_fields")), -// )), -// Term::Error.trace(Term::string("Too many items")), -// ) -// .lambda("red_constr_fields") -// .apply( -// Term::var("constr_fields_exposer").apply(Term::var("red")), -// ), -// Term::equals_integer() -// .apply(Term::integer(1.into())) -// .apply( -// Term::var("constr_index_exposer").apply(Term::var("red")), -// ) -// .delayed_if_else( -// Term::var("constr_fields_exposer") -// .apply(Term::var("red")) -// .delayed_choose_list( -// Term::unit(), -// Term::Error.trace(Term::string("Expected......")), -// ), -// Term::Error.trace(Term::string("Expected...")), -// ), -// ) -// .lambda("red"), -// ) -// .apply(Term::var("red")) -// .choose_unit(Term::var("red")), -// ) -// .lambda("ctx") -// .lambda("red") -// .lambda("dat") -// .lambda("constr_get_field") -// .apply( -// Term::var("constr_get_field") -// .apply(Term::var("constr_get_field")) -// .apply(Term::integer(0.into())), -// ) -// .lambda("constr_get_field") -// .apply(Term::bool(false).lambda("x")) -// .lambda("constr_fields_exposer") -// .apply( -// Term::snd_pair() -// .apply(Term::unconstr_data().apply(Term::var("x"))) -// .lambda("x"), -// ) -// .lambda("constr_index_exposer") -// .apply( -// Term::fst_pair() -// .apply(Term::unconstr_data().apply(Term::var("x"))) -// .lambda("x"), -// ), -// false, -// ); -// } + assert_uplc( + src, + Term::equals_integer() + .apply(Term::integer(0.into())) + .apply(Term::var("constr_index_exposer").apply(Term::var("dat"))) + .if_else( + Term::equals_integer() + .apply(Term::integer(0.into())) + .apply(Term::var("constr_index_exposer").apply(Term::var("red"))) + .if_else( + Term::equals_integer() + .apply( + Term::un_i_data().apply( + Term::var("constr_get_field") + .apply( + Term::var("constr_fields_exposer") + .apply(Term::var("a")), + ) + .apply(Term::integer(0.into())), + ), + ) + .apply(Term::var("x")) + .lambda("x") + .apply( + Term::un_i_data() + .apply(Term::head_list().apply(Term::var("red_constr_fields"))), + ) + .lambda("red_constr_fields") + .apply(Term::var("constr_fields_exposer").apply(Term::var("red"))) + .delay(), + Term::var("other_clauses"), + ) + .force() + .lambda("a") + .apply(Term::head_list().apply(Term::var("dat_constr_fields"))) + .lambda("dat_constr_fields") + .apply(Term::var("constr_fields_exposer").apply(Term::var("dat"))) + .delay(), + Term::var("other_clauses"), + ) + .force() + .lambda("other_clauses") + .apply(Term::bool(true).delay()) + .lambda("dat") + .apply(Term::fst_pair().apply(Term::var("pair_subject"))) + .lambda("red") + .apply(Term::snd_pair().apply(Term::var("pair_subject"))) + .lambda("pair_subject") + .apply( + Term::mk_pair_data() + .apply(Term::var("dat")) + .apply(Term::var("red")), + ) + .delayed_if_else(Term::unit(), Term::Error) + .lambda("dat") + .apply( + Term::var("dat").lambda("_").apply( + Term::var("expect_Datum") + .lambda("expect_Datum") + .apply( + Term::equals_integer() + .apply(Term::integer(0.into())) + .apply(Term::var("subject")) + .delayed_if_else( + Term::tail_list() + .apply(Term::var("dat_constr_fields")) + .delayed_choose_list( + Term::unit().lambda("_").apply( + Term::var("expect_Thing") + .apply(Term::var("field_1")), + ), + Term::Error.trace(Term::string("List/Tuple/Constr contains more items than expected")), + ) + .lambda("field_1") + .apply( + Term::head_list().apply(Term::var("dat_constr_fields")), + ) + .lambda("dat_constr_fields") + .apply( + Term::var("constr_fields_exposer") + .apply(Term::var("dat")), + ), + Term::equals_integer() + .apply(Term::integer(1.into())) + .apply(Term::var("subject")) + .delayed_if_else( + Term::var("constr_fields_exposer") + .apply(Term::var("dat")) + .delayed_choose_list( + Term::unit(), + Term::Error + .trace(Term::string("Expected no fields for Constr")) + ), + Term::Error.trace(Term::string("Constr index did not match any type variant")), + ), + ) + .lambda("subject") + .apply(Term::var("constr_index_exposer").apply(Term::var("dat"))) + .lambda("dat"), + ) + .lambda("expect_Thing") + .apply( + Term::equals_integer() + .apply(Term::integer(0.into())) + .apply(Term::var("subject")) + .delayed_if_else( + Term::tail_list() + .apply(Term::var("field_1_constr_fields")) + .delayed_choose_list( + Term::unit(), + Term::Error.trace(Term::string("List/Tuple/Constr contains more items than expected")), + ) + .lambda("idx") + .apply( + Term::un_i_data().apply( + Term::head_list() + .apply(Term::var("field_1_constr_fields")), + ), + ) + .lambda("field_1_constr_fields") + .apply( + Term::var("constr_fields_exposer") + .apply(Term::var("field_1")), + ), + Term::Error.trace(Term::string( + "Constr index did not match any type variant", + )), + ) + .lambda("subject") + .apply( + Term::var("constr_index_exposer").apply(Term::var("field_1")), + ) + .lambda("field_1"), + ) + .apply(Term::var("dat")), + ), + ) + .lambda("red") + .apply( + Term::var("red").lambda("_").apply( + Term::var("expect_RedSpend") + .lambda("expect_RedSpend") + .apply( + Term::equals_integer() + .apply(Term::integer(0.into())) + .apply(Term::var("subject")) + .delayed_if_else( + Term::tail_list() + .apply(Term::var("red_constr_fields")) + .delayed_choose_list( + Term::unit(), + Term::Error.trace(Term::string("List/Tuple/Constr contains more items than expected")), + ) + .lambda("field_1") + .apply(Term::un_i_data().apply( + Term::head_list().apply(Term::var("red_constr_fields")), + )) + .lambda("red_constr_fields") + .apply( + Term::var("constr_fields_exposer") + .apply(Term::var("red")), + ), + Term::equals_integer() + .apply(Term::integer(1.into())) + .apply(Term::var("subject")) + .delayed_if_else( + Term::var("constr_fields_exposer") + .apply(Term::var("red")) + .delayed_choose_list( + Term::unit(), + Term::Error + .trace(Term::string("Expected no fields for Constr")) + ), + Term::Error.trace(Term::string("Constr index did not match any type variant")), + ), + ) + .lambda("subject") + .apply(Term::var("constr_index_exposer").apply(Term::var("red"))) + .lambda("red"), + ) + .apply(Term::var("red")), + ), + ) + .lambda("ctx") + .lambda("red") + .lambda("dat") + .lambda("constr_get_field") + .apply( + Term::var("constr_get_field") + .apply(Term::var("constr_get_field")) + .apply(Term::integer(0.into())), + ) + .lambda("constr_get_field") + .apply( + Term::equals_integer() + .apply(Term::var("__wanted_arg".to_string())) + .apply(Term::var("__current_arg_number".to_string())) + .if_else( + Term::head_list(), + Term::var(CONSTR_GET_FIELD) + .apply(Term::var(CONSTR_GET_FIELD)) + .apply( + Term::add_integer() + .apply(Term::var("__current_arg_number")) + .apply(Term::integer(1.into())), + ) + .apply( + Term::tail_list().apply(Term::var("__current_list_of_constr_args")), + ) + .apply(Term::var("__wanted_arg")) + .lambda("__current_list_of_constr_args"), + ) + .apply(Term::var("__list_of_constr_args")) + .lambda("__wanted_arg") + .lambda("__list_of_constr_args") + .lambda("__current_arg_number") + .lambda(CONSTR_GET_FIELD), + ) + .lambda("constr_fields_exposer") + .apply( + Term::snd_pair() + .apply(Term::unconstr_data().apply(Term::var("x"))) + .lambda("x"), + ) + .lambda("constr_index_exposer") + .apply( + Term::fst_pair() + .apply(Term::unconstr_data().apply(Term::var("x"))) + .lambda("x"), + ), + false, + ); +} diff --git a/crates/uplc/src/builder.rs b/crates/uplc/src/builder.rs index 02831aca..25e2a7a3 100644 --- a/crates/uplc/src/builder.rs +++ b/crates/uplc/src/builder.rs @@ -10,6 +10,7 @@ pub const CONSTR_GET_FIELD: &str = "__constr_get_field"; pub const EXPECT_ON_LIST: &str = "__expect_on_list"; impl Term { + // Terms pub fn apply(self, arg: Self) -> Self { Term::Apply { function: self.into(), @@ -25,6 +26,7 @@ impl Term { Term::Delay(self.into()) } + // Primitives pub fn integer(i: num_bigint::BigInt) -> Self { Term::Constant(Constant::Integer(i).into()) } @@ -75,6 +77,7 @@ impl Term { ) } + // Builtins pub fn constr_data() -> Self { Term::Builtin(DefaultFunction::ConstrData) } @@ -135,6 +138,14 @@ impl Term { Term::Builtin(DefaultFunction::EqualsByteString) } + pub fn less_than_bytearray() -> Self { + Term::Builtin(DefaultFunction::LessThanByteString) + } + + pub fn less_than_equals_bytearray() -> Self { + Term::Builtin(DefaultFunction::LessThanEqualsByteString) + } + pub fn equals_data() -> Self { Term::Builtin(DefaultFunction::EqualsData) } @@ -159,6 +170,58 @@ impl Term { Term::Builtin(DefaultFunction::LengthOfByteString) } + pub fn cons_bytearray() -> Self { + Term::Builtin(DefaultFunction::ConsByteString) + } + + pub fn slice_bytearray() -> Self { + Term::Builtin(DefaultFunction::SliceByteString) + } + + pub fn append_bytearray() -> Self { + Term::Builtin(DefaultFunction::AppendByteString) + } + + pub fn index_bytearray() -> Self { + Term::Builtin(DefaultFunction::IndexByteString) + } + + pub fn sha2_256() -> Self { + Term::Builtin(DefaultFunction::Sha2_256) + } + + pub fn sha3_256() -> Self { + Term::Builtin(DefaultFunction::Sha3_256) + } + + pub fn blake2b_256() -> Self { + Term::Builtin(DefaultFunction::Blake2b_256) + } + + pub fn verify_ed25519_signature() -> Self { + Term::Builtin(DefaultFunction::VerifyEd25519Signature) + } + + pub fn verify_ecdsa_secp256k1_signature() -> Self { + Term::Builtin(DefaultFunction::VerifyEcdsaSecp256k1Signature) + } + + pub fn verify_schnorr_secp256k1_signature() -> Self { + Term::Builtin(DefaultFunction::VerifySchnorrSecp256k1Signature) + } + + pub fn decode_utf8() -> Self { + Term::Builtin(DefaultFunction::DecodeUtf8) + } + + pub fn append_string() -> Self { + Term::Builtin(DefaultFunction::AppendString) + } + + pub fn encode_utf8() -> Self { + Term::Builtin(DefaultFunction::EncodeUtf8) + } + pub fn head_list() -> Self { Term::Builtin(DefaultFunction::HeadList).force() } @@ -266,83 +329,75 @@ impl Term { } pub fn constr_fields_exposer(self) -> Self { - self.lambda(CONSTR_FIELDS_EXPOSER.to_string()).apply( + self.lambda(CONSTR_FIELDS_EXPOSER).apply( Term::snd_pair() - .apply(Term::unconstr_data().apply(Term::var("__constr_var".to_string()))) + .apply(Term::unconstr_data().apply(Term::var("__constr_var"))) .lambda("__constr_var"), ) } pub fn constr_index_exposer(self) -> Self { - self.lambda(CONSTR_INDEX_EXPOSER.to_string()).apply( + self.lambda(CONSTR_INDEX_EXPOSER).apply( Term::fst_pair() - .apply(Term::unconstr_data().apply(Term::var("__constr_var".to_string()))) - .lambda("__constr_var".to_string()), + .apply(Term::unconstr_data().apply(Term::var("__constr_var"))) + .lambda("__constr_var"), ) } pub fn constr_get_field(self) -> Self { - self.lambda(CONSTR_GET_FIELD.to_string()) + self.lambda(CONSTR_GET_FIELD) .apply( - Term::var(CONSTR_GET_FIELD.to_string()) - .apply(Term::var(CONSTR_GET_FIELD.to_string())) + Term::var(CONSTR_GET_FIELD) + .apply(Term::var(CONSTR_GET_FIELD)) .apply(Term::integer(0.into())), ) .lambda(CONSTR_GET_FIELD) .apply( Term::equals_integer() - .apply(Term::var("__wanted_arg".to_string())) - .apply(Term::var("__current_arg_number".to_string())) + .apply(Term::var("__wanted_arg")) + .apply(Term::var("__current_arg_number")) .if_else( Term::head_list(), Term::var(CONSTR_GET_FIELD) .apply(Term::var(CONSTR_GET_FIELD)) .apply( Term::add_integer() - .apply(Term::var("__current_arg_number".to_string())) + .apply(Term::var("__current_arg_number")) .apply(Term::integer(1.into())), ) .apply( - Term::tail_list() - .apply(Term::var("__current_list_of_constr_args".to_string())), + Term::tail_list().apply(Term::var("__current_list_of_constr_args")), ) .apply(Term::var("__wanted_arg")) - .lambda("__current_list_of_constr_args".to_string()), + .lambda("__current_list_of_constr_args"), ) - .apply(Term::var("__list_of_constr_args".to_string())) - .lambda("__wanted_arg".to_string()) + .apply(Term::var("__list_of_constr_args")) + .lambda("__wanted_arg") .lambda("__list_of_constr_args") - .lambda("__current_arg_number".to_string()) - .lambda(CONSTR_GET_FIELD.to_string()), + .lambda("__current_arg_number") + .lambda(CONSTR_GET_FIELD), ) } pub fn assert_on_list(self) -> Self { - self.lambda(EXPECT_ON_LIST.to_string()) + self.lambda(EXPECT_ON_LIST) + .apply(Term::var(EXPECT_ON_LIST).apply(Term::var(EXPECT_ON_LIST))) + .lambda(EXPECT_ON_LIST) .apply( - Term::var(EXPECT_ON_LIST.to_string()).apply(Term::var(EXPECT_ON_LIST.to_string())), - ) - .lambda(EXPECT_ON_LIST.to_string()) - .apply( - Term::var("__list_to_check".to_string()) + Term::var("__list_to_check") .delayed_choose_list( Term::unit(), - Term::var("__check_with".to_string()) - .apply( - Term::head_list().apply(Term::var("__list_to_check".to_string())), - ) + Term::var("__check_with") + .apply(Term::head_list().apply(Term::var("__list_to_check"))) .choose_unit( - Term::var(EXPECT_ON_LIST.to_string()) - .apply(Term::var(EXPECT_ON_LIST.to_string())) - .apply( - Term::tail_list() - .apply(Term::var("__list_to_check".to_string())), - ) - .apply(Term::var("__check_with".to_string())), + Term::var(EXPECT_ON_LIST) + .apply(Term::var(EXPECT_ON_LIST)) + .apply(Term::tail_list().apply(Term::var("__list_to_check"))) + .apply(Term::var("__check_with")), ), ) - .lambda("__check_with".to_string()) - .lambda("__list_to_check".to_string()) + .lambda("__check_with") + .lambda("__list_to_check") .lambda(EXPECT_ON_LIST), ) } diff --git a/crates/uplc/src/optimize/shrinker.rs b/crates/uplc/src/optimize/shrinker.rs index 0ee462a3..05eeff40 100644 --- a/crates/uplc/src/optimize/shrinker.rs +++ b/crates/uplc/src/optimize/shrinker.rs @@ -840,4 +840,36 @@ mod test { assert_eq!(actual, expected); } + + #[test] + fn inline_reduce_delay_sha() { + let mut program: Program = Program { + version: (1, 0, 0), + term: Term::sha2_256() + .apply(Term::var("x")) + .lambda("x") + .apply(Term::byte_string(vec![]).delay()), + }; + + let mut interner = Interner::new(); + + interner.program(&mut program); + + let mut expected = Program { + version: (1, 0, 0), + term: Term::sha2_256().apply(Term::byte_string(vec![]).delay()), + }; + + let mut interner = Interner::new(); + + interner.program(&mut expected); + + let expected: Program = expected.try_into().unwrap(); + + let actual = program.inline_reduce(); + + let actual: Program = actual.try_into().unwrap(); + + assert_eq!(actual, expected); + } }