From 78d2049d7b3d3b2a094e621731a8bed778ec7d38 Mon Sep 17 00:00:00 2001 From: microproofs Date: Thu, 25 Jan 2024 14:18:36 -0500 Subject: [PATCH] fix: Using the wrong match string for discards in FieldsExpose Also need to return a lambda wrapped term from list_access_to_uplc under all conditions --- crates/aiken-lang/src/gen_uplc.rs | 2 +- crates/aiken-lang/src/gen_uplc/builder.rs | 68 +++++++++------- examples/acceptance_tests/089/aiken.lock | 2 +- .../acceptance_tests/089/validators/test2.ak | 77 +++++++++++++++++++ 4 files changed, 118 insertions(+), 31 deletions(-) create mode 100644 examples/acceptance_tests/089/validators/test2.ak diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index 876f8298..b8d490fb 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -4985,7 +4985,7 @@ impl<'a> CodeGenerator<'a> { let named_indices = names_types .iter() - .skip_while(|(name, _, _)| name.is_empty()) + .skip_while(|(name, _, _)| name == "_") .collect_vec(); if !named_indices.is_empty() || is_expect { diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index 54bf1444..7bf38564 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -1538,13 +1538,14 @@ pub fn list_access_to_uplc( .collect_vec(); // If the the is just discards and check_last_item then we check for empty list - if no_tailing_discards.is_empty() - && !tail_present - && matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items) - { - return Term::var("empty_list") - .delayed_choose_list(term, error_term) - .lambda("empty_list"); + if no_tailing_discards.is_empty() { + if !tail_present && matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items) { + return Term::var("empty_list") + .delayed_choose_list(term, error_term) + .lambda("empty_list"); + } else { + return term.lambda("_"); + } } // reverse back to original order @@ -1585,32 +1586,41 @@ pub fn list_access_to_uplc( // case for no tail // name is guaranteed to not be discard at this point - if matches!(expect_level, ExpectLevel::None) - || (error_term == Term::Error && tail_present) - { - acc.lambda(name).apply(head_list).lambda(tail_name) - } else if tail_present { - Term::var(tail_name.to_string()) - .delayed_choose_list(error_term.clone(), acc.lambda(name).apply(head_list)) - .lambda(tail_name) - } else if error_term == Term::Error { - Term::tail_list() - .apply(Term::var(tail_name.to_string())) - .delayed_choose_list(acc, error_term.clone()) - .lambda(name) - .apply(head_list) - .lambda(tail_name) - } else { - Term::var(tail_name.to_string()) - .delayed_choose_list( - error_term.clone(), + match expect_level { + ExpectLevel::None => acc.lambda(name).apply(head_list).lambda(tail_name), + ExpectLevel::Full | ExpectLevel::Items => { + if error_term == Term::Error && tail_present { + acc.lambda(name).apply(head_list).lambda(tail_name) + } else if tail_present { + // Custom error instead of trying to do head_list on a possibly empty list. + Term::var(tail_name.to_string()) + .delayed_choose_list( + error_term.clone(), + acc.lambda(name).apply(head_list), + ) + .lambda(tail_name) + } else if error_term == Term::Error { + // Check head is last item in this list Term::tail_list() .apply(Term::var(tail_name.to_string())) .delayed_choose_list(acc, error_term.clone()) .lambda(name) - .apply(head_list), - ) - .lambda(tail_name) + .apply(head_list) + .lambda(tail_name) + } else { + // Custom error if list is not empty after this head + Term::var(tail_name.to_string()) + .delayed_choose_list( + error_term.clone(), + Term::tail_list() + .apply(Term::var(tail_name.to_string())) + .delayed_choose_list(acc, error_term.clone()) + .lambda(name) + .apply(head_list), + ) + .lambda(tail_name) + } + } } } else if name == "_" { if matches!(expect_level, ExpectLevel::None) || error_term == Term::Error { diff --git a/examples/acceptance_tests/089/aiken.lock b/examples/acceptance_tests/089/aiken.lock index 6361d985..11259dcd 100644 --- a/examples/acceptance_tests/089/aiken.lock +++ b/examples/acceptance_tests/089/aiken.lock @@ -13,4 +13,4 @@ requirements = [] source = "github" [etags] -"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1705743642, nanos_since_epoch = 175324000 }, "cf946239d3dd481ed41f20e56bf24910b5229ea35aa171a708edc2a47fc20a7b"] +"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1706206922, nanos_since_epoch = 401031000 }, "cf946239d3dd481ed41f20e56bf24910b5229ea35aa171a708edc2a47fc20a7b"] diff --git a/examples/acceptance_tests/089/validators/test2.ak b/examples/acceptance_tests/089/validators/test2.ak new file mode 100644 index 00000000..4347739a --- /dev/null +++ b/examples/acceptance_tests/089/validators/test2.ak @@ -0,0 +1,77 @@ +use aiken/list +use aiken/transaction.{ + Input, Mint, NoDatum, Output, OutputReference, ScriptContext, Transaction, + TransactionId, +} +use aiken/transaction/credential.{Address, VerificationKeyCredential} +use aiken/transaction/value + +pub const own_hash = #"01020304050607080910111213140102030405060708091011121314" + +pub const other_hash = + #"02030405060708091011121314150203040506070809101112131415" + +pub const beneficiary_keyhash = + #"03040506070809101112131415160304050607080910111213141516" + +pub fn beneficiary_address() { + keyhash_address(beneficiary_keyhash) +} + +pub fn keyhash_address(keyhash: ByteArray) { + Address { + payment_credential: VerificationKeyCredential(keyhash), + stake_credential: None, + } +} + +validator(utxo_ref: OutputReference) { + fn simple_oneshot(_r: Void, ctx: ScriptContext) -> Bool { + let ScriptContext { transaction, purpose } = ctx + let Transaction { inputs, .. } = transaction + + trace @"Here" + + + expect Mint(_policy_id) = purpose + + trace @"Here1" + + + expect Some(_input) = + list.find(inputs, fn(input) { input.output_reference == utxo_ref }) + + trace @"Here2" + + True + + + } +} + +test test_simple_oneshot() { + let output = + Output { + address: beneficiary_address(), + value: value.zero(), + datum: NoDatum, + reference_script: None, + } + + let val = + value.from_asset(policy_id: own_hash, asset_name: "testtoken", quantity: 1) + let utxo = + OutputReference { transaction_id: TransactionId(""), output_index: 0 } + + let input = Input { output_reference: utxo, output } + + let tx = + Transaction { + ..transaction.placeholder(), + mint: value.to_minted_value(val), + extra_signatories: [other_hash], + inputs: [input], + } + let context = ScriptContext { purpose: Mint(own_hash), transaction: tx } + simple_oneshot(utxo, Void, context) == True +}