From ed92869fb9efba98a114e8cc5f5ac899b23952ee Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 16 Mar 2023 18:33:57 -0400 Subject: [PATCH 01/17] feat(validator): parsing and typechecking for double validators --- crates/aiken-lang/src/ast.rs | 1 + crates/aiken-lang/src/format.rs | 25 ++-- crates/aiken-lang/src/parser.rs | 97 ++++++++------- crates/aiken-lang/src/tests/check.rs | 16 +-- crates/aiken-lang/src/tests/format.rs | 8 +- crates/aiken-lang/src/tests/parser.rs | 114 +++++++++++++++++- crates/aiken-lang/src/tipo/infer.rs | 52 +++++++- .../aiken-project/src/blueprint/validator.rs | 40 +++--- 8 files changed, 259 insertions(+), 94 deletions(-) diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 2dba649c..2d5102fe 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -322,6 +322,7 @@ pub struct Validator { pub doc: Option, pub end_position: usize, pub fun: Function, + pub other_fun: Option>, pub location: Span, pub params: Vec>, } diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index 53f132dd..521263f2 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -504,12 +504,17 @@ impl<'comments> Formatter<'comments> { &mut self, params: &'a [UntypedArg], fun: &'a UntypedFunction, + other_fun: &'a Option, end_position: usize, ) -> Document<'a> { // Fn and args - let head = "fn".to_doc().append(wrap_args( - fun.arguments.iter().map(|e| (self.fn_arg(e), false)), - )); + let head = "fn" + .to_doc() + .append(" ") + .append(fun.name.to_doc()) + .append(wrap_args( + fun.arguments.iter().map(|e| (self.fn_arg(e), false)), + )); // Add return annotation let head = match &fun.return_annotation { @@ -528,15 +533,11 @@ impl<'comments> Formatter<'comments> { }; // validator name(params) - let v_head = "validator" - .to_doc() - .append(" ") - .append(fun.name.as_str()) - .append(if !params.is_empty() { - wrap_args(params.iter().map(|e| (self.fn_arg(e), false))) - } else { - "".to_doc() - }); + let v_head = "validator".to_doc().append(if !params.is_empty() { + wrap_args(params.iter().map(|e| (self.fn_arg(e), false))) + } else { + "".to_doc() + }); // Stick it all together let inner_fn = head diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 90e75a65..672765ae 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -242,9 +242,43 @@ pub fn type_alias_parser() -> impl Parser impl Parser { - just(Token::Validator) - .ignore_then(select! {Token::Name {name} => name}.map_with_span(|name, span| (name, span))) + let func_parser = just(Token::Fn) + .ignore_then(select! {Token::Name {name} => name}) .then( + fn_param_parser() + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .map_with_span(|arguments, span| (arguments, span)), + ) + .then(just(Token::RArrow).ignore_then(type_parser()).or_not()) + .then( + expr_seq_parser() + .or_not() + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), + ) + .map_with_span( + |(((name, (arguments, args_span)), return_annotation), body), span| ast::Function { + arguments, + body: body.unwrap_or_else(|| expr::UntypedExpr::todo(span, None)), + doc: None, + location: Span { + start: span.start, + end: return_annotation + .as_ref() + .map(|l| l.location().end) + .unwrap_or_else(|| args_span.end), + }, + end_position: span.end - 1, + name, + public: false, + return_annotation, + return_type: (), + }, + ); + + just(Token::Validator) + .ignore_then( fn_param_parser() .separated_by(just(Token::Comma)) .allow_trailing() @@ -253,51 +287,32 @@ pub fn validator_parser() -> impl Parser Bool { + validator ( ) { + fn foo (d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool { True } } "#}; let expected = indoc! {r#" - validator foo { - fn(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool { + validator { + fn foo(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool { True } } diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index cc83922f..e723cca9 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -41,8 +41,8 @@ fn windows_newline() { #[test] fn validator() { let code = indoc! {r#" - validator foo { - fn(datum, rdmr, ctx) { + validator { + fn foo(datum, rdmr, ctx) { True } } @@ -91,14 +91,120 @@ fn validator() { name: "True".to_string(), }, doc: None, - location: Span::new((), 18..38), + location: Span::new((), 14..38), name: "foo".to_string(), public: false, return_annotation: None, return_type: (), end_position: 52, }, - location: Span::new((), 0..13), + other_fun: None, + location: Span::new((), 0..9), + params: vec![], + })], + ) +} + +#[test] +fn double_validator() { + let code = indoc! {r#" + validator { + fn foo(datum, rdmr, ctx) { + True + } + + fn bar(rdmr, ctx) { + True + } + } + "#}; + + assert_definitions( + code, + vec![ast::UntypedDefinition::Validator(ast::Validator { + doc: None, + end_position: 90, + fun: Function { + arguments: vec![ + ast::Arg { + arg_name: ast::ArgName::Named { + name: "datum".to_string(), + label: "datum".to_string(), + location: Span::new((), 21..26), + }, + location: Span::new((), 21..26), + annotation: None, + tipo: (), + }, + ast::Arg { + arg_name: ast::ArgName::Named { + name: "rdmr".to_string(), + label: "rdmr".to_string(), + location: Span::new((), 28..32), + }, + location: Span::new((), 28..32), + annotation: None, + tipo: (), + }, + ast::Arg { + arg_name: ast::ArgName::Named { + name: "ctx".to_string(), + label: "ctx".to_string(), + location: Span::new((), 34..37), + }, + location: Span::new((), 34..37), + annotation: None, + tipo: (), + }, + ], + body: expr::UntypedExpr::Var { + location: Span::new((), 45..49), + name: "True".to_string(), + }, + doc: None, + location: Span::new((), 14..38), + name: "foo".to_string(), + public: false, + return_annotation: None, + return_type: (), + end_position: 52, + }, + other_fun: Some(Function { + arguments: vec![ + ast::Arg { + arg_name: ast::ArgName::Named { + name: "rdmr".to_string(), + label: "rdmr".to_string(), + location: Span::new((), 64..68), + }, + location: Span::new((), 64..68), + annotation: None, + tipo: (), + }, + ast::Arg { + arg_name: ast::ArgName::Named { + name: "ctx".to_string(), + label: "ctx".to_string(), + location: Span::new((), 70..73), + }, + location: Span::new((), 70..73), + annotation: None, + tipo: (), + }, + ], + body: expr::UntypedExpr::Var { + location: Span::new((), 81..85), + name: "True".to_string(), + }, + doc: None, + location: Span::new((), 57..74), + name: "bar".to_string(), + public: false, + return_annotation: None, + return_type: (), + end_position: 88, + }), + location: Span::new((), 0..9), params: vec![], })], ) diff --git a/crates/aiken-lang/src/tipo/infer.rs b/crates/aiken-lang/src/tipo/infer.rs index 53a395c6..25bf7458 100644 --- a/crates/aiken-lang/src/tipo/infer.rs +++ b/crates/aiken-lang/src/tipo/infer.rs @@ -3,8 +3,8 @@ use std::collections::HashMap; use crate::{ ast::{ DataType, Definition, Function, Layer, ModuleConstant, ModuleKind, RecordConstructor, - RecordConstructorArg, Span, Tracing, TypeAlias, TypedDefinition, TypedModule, - UntypedDefinition, UntypedModule, Use, Validator, + RecordConstructorArg, Span, Tracing, TypeAlias, TypedDefinition, TypedFunction, + TypedModule, UntypedDefinition, UntypedModule, Use, Validator, }, builtins, builtins::function, @@ -255,11 +255,12 @@ fn infer_definition( location, end_position, mut fun, - mut params, + other_fun, + params, }) => { let params_length = params.len(); - params.append(&mut fun.arguments); - fun.arguments = params; + let temp_params = params.iter().cloned().chain(fun.arguments); + fun.arguments = temp_params.collect(); if let Definition::Fn(mut typed_fun) = infer_definition( Definition::Fn(fun), @@ -285,10 +286,51 @@ fn infer_definition( }); } + let typed_other_fun = other_fun + .map(|mut other| -> Result { + let params = params.into_iter().chain(other.arguments); + other.arguments = params.collect(); + + if let Definition::Fn(mut other_typed_fun) = infer_definition( + Definition::Fn(other), + module_name, + hydrators, + environment, + tracing, + kind, + )? { + if !other_typed_fun.return_type.is_bool() { + return Err(Error::ValidatorMustReturnBool { + return_type: other_typed_fun.return_type.clone(), + location: other_typed_fun.location, + }); + } + + other_typed_fun.arguments.drain(0..params_length); + + if other_typed_fun.arguments.len() < 2 + || other_typed_fun.arguments.len() > 3 + { + return Err(Error::IncorrectValidatorArity { + count: typed_fun.arguments.len() as u32, + location: typed_fun.location, + }); + } + + Ok(other_typed_fun) + } else { + unreachable!( + "validator definition inferred as something other than a function?" + ) + } + }) + .transpose(); + Ok(Definition::Validator(Validator { doc, end_position, fun: typed_fun, + other_fun: typed_other_fun?, location, params: typed_params, })) diff --git a/crates/aiken-project/src/blueprint/validator.rs b/crates/aiken-project/src/blueprint/validator.rs index 7a945413..1cb03deb 100644 --- a/crates/aiken-project/src/blueprint/validator.rs +++ b/crates/aiken-project/src/blueprint/validator.rs @@ -272,8 +272,8 @@ mod test { fn mint_basic() { assert_validator( r#" - validator mint { - fn(redeemer: Data, ctx: Data) { + validator { + fn mint(redeemer: Data, ctx: Data) { True } } @@ -302,8 +302,8 @@ mod test { fn mint_parameterized() { assert_validator( r#" - validator mint(utxo_ref: Int) { - fn(redeemer: Data, ctx: Data) { + validator(utxo_ref: Int) { + fn mint(redeemer: Data, ctx: Data) { True } } @@ -373,8 +373,8 @@ mod test { Abort } - validator simplified_hydra { - fn(datum: State, redeemer: Input, ctx: Data) { + validator { + fn simplified_hydra(datum: State, redeemer: Input, ctx: Data) { True } } @@ -485,8 +485,8 @@ mod test { fn tuples() { assert_validator( r#" - validator tuples { - fn(datum: (Int, ByteArray), redeemer: (Int, Int, Int), ctx: Void) { + validator { + fn tuples(datum: (Int, ByteArray), redeemer: (Int, Int, Int), ctx: Void) { True } } @@ -560,8 +560,8 @@ mod test { Infinite } - validator generics { - fn(redeemer: Either>, ctx: Void) { + validator { + fn generics(redeemer: Either>, ctx: Void) { True } } @@ -644,8 +644,8 @@ mod test { type UUID { UUID } - validator list_2_tuples_as_map { - fn(redeemer: Dict, ctx: Void) { + validator { + fn list_2_tuples_as_map(redeemer: Dict, ctx: Void) { True } } @@ -707,8 +707,8 @@ mod test { type UUID { UUID } - validator opaque_singleton_variants { - fn(redeemer: Dict, ctx: Void) { + validator { + fn opaque_singleton_variants(redeemer: Dict, ctx: Void) { True } } @@ -753,8 +753,8 @@ mod test { foo: Data } - validator nested_data { - fn(datum: Foo, redeemer: Int, ctx: Void) { + validator { + fn nested_data(datum: Foo, redeemer: Int, ctx: Void) { True } } @@ -814,8 +814,8 @@ mod test { Mul(Expr, Expr) } - validator recursive_types { - fn(redeemer: Expr, ctx: Void) { + validator { + fn recursive_types(redeemer: Expr, ctx: Void) { True } } @@ -899,8 +899,8 @@ mod test { } } - validator recursive_generic_types { - fn(datum: Foo, redeemer: LinkedList, ctx: Void) { + validator { + fn recursive_generic_types(datum: Foo, redeemer: LinkedList, ctx: Void) { True } } From 15bdb6972d7ed5964753e4d555201bcbe3133b12 Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 16 Mar 2023 19:34:16 -0400 Subject: [PATCH 02/17] feat(fmt): better validator formatting with double supported --- crates/aiken-lang/src/format.rs | 87 +++++++++++++++------------ crates/aiken-lang/src/tests/format.rs | 26 ++++++++ 2 files changed, 73 insertions(+), 40 deletions(-) diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index 521263f2..cf20497f 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -240,10 +240,11 @@ impl<'comments> Formatter<'comments> { Definition::Validator(Validator { end_position, - fun: function, + fun, + other_fun, params, .. - }) => self.definition_validator(params, function, *end_position), + }) => self.definition_validator(params, fun, other_fun, *end_position), Definition::Test(Function { name, @@ -507,53 +508,59 @@ impl<'comments> Formatter<'comments> { other_fun: &'a Option, end_position: usize, ) -> Document<'a> { - // Fn and args - let head = "fn" - .to_doc() - .append(" ") - .append(fun.name.to_doc()) - .append(wrap_args( - fun.arguments.iter().map(|e| (self.fn_arg(e), false)), - )); - - // Add return annotation - let head = match &fun.return_annotation { - Some(anno) => head.append(" -> ").append(self.annotation(anno)), - None => head, - } - .group(); - - // Format body - let body = self.expr(&fun.body); - - // Add any trailing comments - let body = match printed_comments(self.pop_comments(fun.end_position), false) { - Some(comments) => body.append(line()).append(comments), - None => body, - }; - - // validator name(params) - let v_head = "validator".to_doc().append(if !params.is_empty() { - wrap_args(params.iter().map(|e| (self.fn_arg(e), false))) - } else { - "".to_doc() - }); - // Stick it all together - let inner_fn = head - .append(" {") - .append(line().append(body).nest(INDENT).group()) - .append(line()) - .append("}"); + let inner_fn = line() + .append(self.definition_fn( + &false, + "fn", + &fun.name, + &fun.arguments, + &fun.return_annotation, + &fun.body, + fun.end_position, + )) + .nest(INDENT) + .group() + .append(if other_fun.is_some() { + docvec![line()] + } else { + nil() + }) + .append( + other_fun + .as_ref() + .map(|other| { + line() + .append(self.definition_fn( + &false, + "fn", + &other.name, + &other.arguments, + &other.return_annotation, + &other.body, + other.end_position, + )) + .nest(INDENT) + .group() + }) + .unwrap_or_else(nil), + ); let inner_fn = match printed_comments(self.pop_comments(end_position), false) { Some(comments) => inner_fn.append(line()).append(comments), None => inner_fn, }; + // validator(params) + let v_head = "validator".to_doc().append(if !params.is_empty() { + wrap_args(params.iter().map(|e| (self.fn_arg(e), false))) + } else { + nil() + }); + v_head .append(" {") - .append(line().append(inner_fn).nest(INDENT).group()) + .append(inner_fn) .append(line()) .append("}") } diff --git a/crates/aiken-lang/src/tests/format.rs b/crates/aiken-lang/src/tests/format.rs index 7f28c8c4..c91dc156 100644 --- a/crates/aiken-lang/src/tests/format.rs +++ b/crates/aiken-lang/src/tests/format.rs @@ -76,6 +76,32 @@ fn test_format_validator() { assert_fmt(src, expected) } +#[test] +fn test_format_double_validator() { + let src = indoc! {r#" + validator ( param1 : ByteArray ) { + fn foo (d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool { + True + } + fn bar(r: Redeemer, ctx : ScriptContext ) -> Bool { True } + } + "#}; + + let expected = indoc! {r#" + validator(param1: ByteArray) { + fn foo(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool { + True + } + + fn bar(r: Redeemer, ctx: ScriptContext) -> Bool { + True + } + } + "#}; + + assert_fmt(src, expected) +} + #[test] fn test_format_when() { let src = indoc! {r#" From 87493bbba97b2cfd2da2370a8f1338405f81bea3 Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 16 Mar 2023 21:30:20 -0400 Subject: [PATCH 03/17] feat(register_values): refactor repeated code into register_function and register other_fun --- crates/aiken-lang/src/tipo/environment.rs | 204 +++++++++++----------- 1 file changed, 104 insertions(+), 100 deletions(-) diff --git a/crates/aiken-lang/src/tipo/environment.rs b/crates/aiken-lang/src/tipo/environment.rs index 5ae517fb..8739a3b4 100644 --- a/crates/aiken-lang/src/tipo/environment.rs +++ b/crates/aiken-lang/src/tipo/environment.rs @@ -10,7 +10,7 @@ use crate::{ ast::{ Annotation, CallArg, DataType, Definition, Function, ModuleConstant, ModuleKind, Pattern, RecordConstructor, RecordConstructorArg, Span, TypeAlias, TypedDefinition, - UnqualifiedImport, UntypedDefinition, Use, Validator, PIPE_VARIABLE, + UnqualifiedImport, UntypedArg, UntypedDefinition, Use, Validator, PIPE_VARIABLE, }, builtins::{self, function, generic_var, tuple, unbound_var}, tipo::fields::FieldMap, @@ -1007,6 +1007,66 @@ impl<'a> Environment<'a> { Ok(()) } + #[allow(clippy::too_many_arguments)] + fn register_function( + &mut self, + name: &'a str, + arguments: &[UntypedArg], + return_annotation: &Option, + module_name: &String, + hydrators: &mut HashMap, + names: &mut HashMap<&'a str, &'a Span>, + location: &'a Span, + ) -> Result<(), Error> { + assert_unique_value_name(names, name, location)?; + + self.ungeneralised_functions.insert(name.to_string()); + + // Create the field map so we can reorder labels for usage of this function + let mut field_map = FieldMap::new(arguments.len(), true); + + for (i, arg) in arguments.iter().enumerate() { + field_map.insert(arg.arg_name.get_label().clone(), i, &arg.location)?; + } + let field_map = field_map.into_option(); + + // Construct type from annotations + let mut hydrator = Hydrator::new(); + + let mut arg_types = Vec::new(); + + for arg in arguments { + let tipo = hydrator.type_from_option_annotation(&arg.annotation, self)?; + + arg_types.push(tipo); + } + + let return_type = hydrator.type_from_option_annotation(return_annotation, self)?; + + let tipo = function(arg_types, return_type); + + // Keep track of which types we create from annotations so we can know + // which generic types not to instantiate later when performing + // inference of the function body. + hydrators.insert(name.to_string(), hydrator); + + // Insert the function into the environment + self.insert_variable( + name.to_string(), + ValueConstructorVariant::ModuleFn { + name: name.to_string(), + field_map, + module: module_name.to_owned(), + arity: arguments.len(), + location: *location, + builtin: None, + }, + tipo, + ); + + Ok(()) + } + pub fn register_values( &mut self, def: &'a UntypedDefinition, @@ -1016,117 +1076,61 @@ impl<'a> Environment<'a> { kind: ModuleKind, ) -> Result<(), Error> { match def { - Definition::Fn(Function { - name, - arguments: args, - location, - return_annotation, - public, - .. - }) => { - assert_unique_value_name(names, name, location)?; + Definition::Fn(fun) => { + self.register_function( + &fun.name, + &fun.arguments, + &fun.return_annotation, + module_name, + hydrators, + names, + &fun.location, + )?; - self.ungeneralised_functions.insert(name.to_string()); - - // Create the field map so we can reorder labels for usage of this function - let mut field_map = FieldMap::new(args.len(), true); - - for (i, arg) in args.iter().enumerate() { - field_map.insert(arg.arg_name.get_label().clone(), i, &arg.location)?; - } - let field_map = field_map.into_option(); - - // Construct type from annotations - let mut hydrator = Hydrator::new(); - - let mut arg_types = Vec::new(); - - for arg in args { - let tipo = hydrator.type_from_option_annotation(&arg.annotation, self)?; - - arg_types.push(tipo); - } - - let return_type = hydrator.type_from_option_annotation(return_annotation, self)?; - - let tipo = function(arg_types, return_type); - - // Keep track of which types we create from annotations so we can know - // which generic types not to instantiate later when performing - // inference of the function body. - hydrators.insert(name.clone(), hydrator); - - // Insert the function into the environment - self.insert_variable( - name.clone(), - ValueConstructorVariant::ModuleFn { - name: name.clone(), - field_map, - module: module_name.to_owned(), - arity: args.len(), - location: *location, - builtin: None, - }, - tipo, - ); - - if !public && kind.is_lib() { - self.init_usage(name.clone(), EntityKind::PrivateFunction, *location); + if !fun.public && kind.is_lib() { + self.init_usage(fun.name.clone(), EntityKind::PrivateFunction, fun.location); } } Definition::Validator(Validator { fun, - location, + other_fun, params, .. }) if kind.is_validator() => { - assert_unique_value_name(names, &fun.name, location)?; + let temp_params: Vec = params + .iter() + .cloned() + .chain(fun.arguments.clone()) + .collect(); - // Create the field map so we can reorder labels for usage of this function - let mut field_map = FieldMap::new(fun.arguments.len() + params.len(), true); + self.register_function( + &fun.name, + &temp_params, + &fun.return_annotation, + module_name, + hydrators, + names, + &fun.location, + )?; - // Chain together extra params and function.arguments - for (i, arg) in params.iter().chain(fun.arguments.iter()).enumerate() { - field_map.insert(arg.arg_name.get_label().clone(), i, &arg.location)?; + if let Some(other) = other_fun { + let temp_params: Vec = params + .iter() + .cloned() + .chain(other.arguments.clone()) + .collect(); + + self.register_function( + &other.name, + &temp_params, + &other.return_annotation, + module_name, + hydrators, + names, + &other.location, + )?; } - - let field_map = field_map.into_option(); - - // Construct type from annotations - let mut hydrator = Hydrator::new(); - - let mut arg_types = Vec::new(); - - for arg in params.iter().chain(fun.arguments.iter()) { - let tipo = hydrator.type_from_option_annotation(&arg.annotation, self)?; - - arg_types.push(tipo); - } - - let return_type = - hydrator.type_from_option_annotation(&fun.return_annotation, self)?; - - let tipo = function(arg_types, return_type); - - // Keep track of which types we create from annotations so we can know - // which generic types not to instantiate later when performing - // inference of the function body. - hydrators.insert(fun.name.clone(), hydrator); - - // Insert the function into the environment - self.insert_variable( - fun.name.clone(), - ValueConstructorVariant::ModuleFn { - name: fun.name.clone(), - field_map, - module: module_name.to_owned(), - arity: params.len() + fun.arguments.len(), - location: fun.location, - builtin: None, - }, - tipo, - ); } Definition::Validator(Validator { location, .. }) => { From c3870e340ea7a6bc24d2e8f93833669689382c42 Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 16 Mar 2023 23:58:22 -0400 Subject: [PATCH 04/17] feat(codegen): support multi-validators * rename force_wrap to force * add a bunch of builder methods to Term * refactor one tiny location to show off builder methods * split generate into `generate` and `generate_test` * create wrap_as_multi_validator function Co-authored-by: Kasey White --- crates/aiken-lang/src/builder.rs | 76 ++++--- crates/aiken-lang/src/uplc.rs | 211 +++++++++++------- .../aiken-project/src/blueprint/validator.rs | 5 +- crates/aiken-project/src/lib.rs | 6 +- crates/uplc/src/ast.rs | 17 +- crates/uplc/src/ast/builder.rs | 88 ++++++-- crates/uplc/src/optimize/shrinker.rs | 4 +- .../acceptance_tests/036/validators/spend.ak | 8 +- .../acceptance_tests/047/validators/foo.ak | 4 +- .../acceptance_tests/048/validators/foo.ak | 4 +- .../acceptance_tests/071/validators/spend.ak | 4 +- .../acceptance_tests/077/validators/spend.ak | 4 +- .../acceptance_tests/077/validators/spend2.ak | 4 +- .../script_context/plutus.json | 110 +++++---- .../script_context/validators/basic.ak | 4 +- .../script_context/validators/deploy.ak | 4 +- .../script_context/validators/mint.ak | 4 +- .../script_context/validators/withdrawals.ak | 4 +- 18 files changed, 327 insertions(+), 234 deletions(-) diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index 458b4e09..04f32ff7 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -4,7 +4,10 @@ use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use uplc::{ ast::{ - builder::{apply_wrap, delayed_choose_list, delayed_if_else, if_else}, + builder::{ + apply_wrap, constr_index_exposer, delayed_choose_list, delayed_if_else, if_else, + CONSTR_FIELDS_EXPOSER, + }, Constant as UplcConstant, Name, Term, Type as UplcType, }, builtins::DefaultFunction, @@ -193,10 +196,7 @@ pub fn convert_type_to_data(term: Term, field_type: &Arc) -> Term, field_type: &Arc) -> Term, field_type: &Arc) -> Term, field_type: &Arc) -> Term, field_type: &Arc) -> Term, field_type: &Arc) -> Term, field_type: &Arc) -> Term, field_type: &Arc) -> Term, arguments: &[TypedArg]) -> Term, mint: Term) -> Term { + Term::equals_integer() + .apply(Term::integer(0.into())) + .apply(constr_index_exposer(Term::var("__second_arg"))) + .delayed_if_else( + mint.apply(Term::var("__first_arg")) + .apply(Term::var("__second_arg")), + spend.apply(Term::var("__first_arg")).apply( + Term::head_list() + .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("__second_arg"))), + ), + ) + .lambda("__second_arg") + .lambda("__first_arg") +} + pub fn monomorphize( ir: Vec, mono_types: IndexMap>, diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 1863d09c..1a6db0a6 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -21,15 +21,15 @@ use crate::{ air::Air, ast::{ ArgName, AssignmentKind, BinOp, Pattern, Span, TypedArg, TypedClause, TypedDataType, - TypedFunction, UnOp, + TypedFunction, TypedValidator, UnOp, }, builder::{ check_replaceable_opaque_type, check_when_pattern_needs, constants_ir, convert_constants_to_data, convert_data_to_type, convert_type_to_data, get_common_ancestor, get_generic_id_and_type, handle_clause_guard, handle_func_dependencies_ir, handle_recursion_ir, list_access_to_uplc, lookup_data_type_by_tipo, monomorphize, - rearrange_clauses, replace_opaque_type, wrap_validator_args, AssignmentProperties, - ClauseProperties, DataTypeKey, FuncComponents, FunctionAccessKey, + rearrange_clauses, replace_opaque_type, wrap_as_multi_validator, wrap_validator_args, + AssignmentProperties, ClauseProperties, DataTypeKey, FuncComponents, FunctionAccessKey, }, builtins::bool, expr::TypedExpr, @@ -80,14 +80,17 @@ impl<'a> CodeGenerator<'a> { pub fn generate( &mut self, - body: &TypedExpr, - arguments: &[TypedArg], - wrap_as_validator: bool, + TypedValidator { + fun, + other_fun, + params, + .. + }: &TypedValidator, ) -> Program { let mut ir_stack = vec![]; let scope = vec![self.id_gen.next()]; - self.build_ir(body, &mut ir_stack, scope); + self.build_ir(&fun.body, &mut ir_stack, scope); self.define_ir(&mut ir_stack); @@ -102,15 +105,75 @@ impl<'a> CodeGenerator<'a> { } // Wrap the validator body if ifThenElse term unit error - term = if wrap_as_validator { - final_wrapper(term) - } else { - term - }; + term = final_wrapper(term); + + term = wrap_validator_args(term, &fun.arguments); + + if let Some(other) = other_fun { + self.reset(); + + let mut other_ir_stack = vec![]; + + let scope = vec![self.id_gen.next()]; + + self.build_ir(&other.body, &mut other_ir_stack, scope); + + self.define_ir(&mut other_ir_stack); + + self.convert_opaque_type_to_inner_ir(&mut other_ir_stack); + + let other_term = self.uplc_code_gen(&mut other_ir_stack); + + let other_term = final_wrapper(other_term); + + let other_term = wrap_validator_args(other_term, &other.arguments); + + let (spend, mint) = if other.arguments.len() > fun.arguments.len() { + (other_term, term) + } else { + (term, other_term) + }; + + term = wrap_as_multi_validator(spend, mint); + term = builder::constr_get_field(term); + + term = builder::constr_fields_exposer(term); + } + + term = wrap_validator_args(term, params); + + self.finalize(term, true) + } + + pub fn generate_test( + &mut self, + test_body: &TypedExpr, + arguments: &[TypedArg], + ) -> Program { + let mut ir_stack = vec![]; + let scope = vec![self.id_gen.next()]; + + self.build_ir(test_body, &mut ir_stack, scope); + + self.define_ir(&mut ir_stack); + + self.convert_opaque_type_to_inner_ir(&mut ir_stack); + + let mut term = self.uplc_code_gen(&mut ir_stack); + + if self.needs_field_access { + term = builder::constr_get_field(term); + + term = builder::constr_fields_exposer(term); + } term = wrap_validator_args(term, arguments); - term = if wrap_as_validator || self.used_data_assert_on_list { + self.finalize(term, false) + } + + fn finalize(&mut self, term: Term, wrap_as_validator: bool) -> Program { + let term = if wrap_as_validator || self.used_data_assert_on_list { assert_on_list(term) } else { term @@ -4029,16 +4092,9 @@ impl<'a> CodeGenerator<'a> { UplcConstant::ProtoList(UplcType::Data, vec![]).into(), ); - let term = apply_wrap( - apply_wrap( - Term::Builtin(DefaultFunction::ConstrData), - Term::Constant( - UplcConstant::Integer(constr_index.try_into().unwrap()) - .into(), - ), - ), - fields, - ); + let term = Term::constr_data() + .apply(Term::integer(constr_index.try_into().unwrap())) + .apply(fields); arg_stack.push(term); } @@ -4134,10 +4190,7 @@ impl<'a> CodeGenerator<'a> { convert_type_to_data(arg, &list_type) }; term = apply_wrap( - apply_wrap( - Term::Builtin(DefaultFunction::MkCons).force_wrap(), - list_item, - ), + apply_wrap(Term::Builtin(DefaultFunction::MkCons).force(), list_item), term, ); } @@ -4204,7 +4257,7 @@ impl<'a> CodeGenerator<'a> { body: term.into(), }, apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Builtin(DefaultFunction::TailList).force(), Term::Var( Name { text: tail_var, @@ -4231,7 +4284,7 @@ impl<'a> CodeGenerator<'a> { } else { convert_data_to_type( apply_wrap( - Term::Builtin(DefaultFunction::HeadList).force_wrap(), + Term::Builtin(DefaultFunction::HeadList).force(), Term::Var( Name { text: tail_var, @@ -4340,7 +4393,7 @@ impl<'a> CodeGenerator<'a> { } => { let mut term: Term = Term::Builtin(func); for _ in 0..func.force_count() { - term = term.force_wrap(); + term = term.force(); } let mut arg_vec = vec![]; @@ -4421,8 +4474,8 @@ impl<'a> CodeGenerator<'a> { Term::Builtin(DefaultFunction::IData), apply_wrap( Term::Builtin(DefaultFunction::FstPair) - .force_wrap() - .force_wrap(), + .force() + .force(), Term::Var( Name { text: temp_tuple.clone(), @@ -4436,9 +4489,7 @@ impl<'a> CodeGenerator<'a> { apply_wrap( Term::Builtin(DefaultFunction::ListData), apply_wrap( - Term::Builtin(DefaultFunction::SndPair) - .force_wrap() - .force_wrap(), + Term::Builtin(DefaultFunction::SndPair).force().force(), Term::Var( Name { text: temp_tuple, @@ -4504,7 +4555,7 @@ impl<'a> CodeGenerator<'a> { } } - term = term.force_wrap(); + term = term.force(); if count == 0 { for temp_var in temp_vars.into_iter().rev() { @@ -4583,7 +4634,7 @@ impl<'a> CodeGenerator<'a> { DefaultFunction::MapData.into(), apply_wrap( apply_wrap( - Term::Builtin(DefaultFunction::MkCons).force_wrap(), + Term::Builtin(DefaultFunction::MkCons).force(), left, ), Term::Constant( @@ -4603,7 +4654,7 @@ impl<'a> CodeGenerator<'a> { DefaultFunction::MapData.into(), apply_wrap( apply_wrap( - Term::Builtin(DefaultFunction::MkCons).force_wrap(), + Term::Builtin(DefaultFunction::MkCons).force(), right, ), Term::Constant( @@ -4678,7 +4729,7 @@ impl<'a> CodeGenerator<'a> { DefaultFunction::MapData.into(), apply_wrap( apply_wrap( - Term::Builtin(DefaultFunction::MkCons).force_wrap(), + Term::Builtin(DefaultFunction::MkCons).force(), left, ), Term::Constant( @@ -4698,7 +4749,7 @@ impl<'a> CodeGenerator<'a> { DefaultFunction::MapData.into(), apply_wrap( apply_wrap( - Term::Builtin(DefaultFunction::MkCons).force_wrap(), + Term::Builtin(DefaultFunction::MkCons).force(), right, ), Term::Constant( @@ -4919,7 +4970,7 @@ impl<'a> CodeGenerator<'a> { let error_term = apply_wrap( apply_wrap( - Term::Builtin(DefaultFunction::Trace).force_wrap(), + Term::Builtin(DefaultFunction::Trace).force(), Term::Constant( UplcConstant::String( "Expected on incorrect constructor variant.".to_string(), @@ -4929,7 +4980,7 @@ impl<'a> CodeGenerator<'a> { ), Term::Delay(Term::Error.into()), ) - .force_wrap(); + .force(); term = delayed_if_else( apply_wrap( @@ -4951,7 +5002,7 @@ impl<'a> CodeGenerator<'a> { let error_term = apply_wrap( apply_wrap( - Term::Builtin(DefaultFunction::Trace).force_wrap(), + Term::Builtin(DefaultFunction::Trace).force(), Term::Constant( UplcConstant::String( "Expected on incorrect boolean variant.".to_string(), @@ -4961,7 +5012,7 @@ impl<'a> CodeGenerator<'a> { ), Term::Delay(Term::Error.into()), ) - .force_wrap(); + .force(); if is_true { term = delayed_if_else(value, term, error_term); @@ -5048,7 +5099,7 @@ impl<'a> CodeGenerator<'a> { .into(), ), ) - .force_wrap(); + .force(); } else { term = if_else( Term::Var( @@ -5067,7 +5118,7 @@ impl<'a> CodeGenerator<'a> { ), Term::Delay(body.into()), ) - .force_wrap(); + .force(); } term = apply_wrap( @@ -5175,7 +5226,7 @@ impl<'a> CodeGenerator<'a> { .into(), ), ) - .force_wrap() + .force() .into(), }, Term::Delay(term.into()), @@ -5210,7 +5261,7 @@ impl<'a> CodeGenerator<'a> { body: term.into(), }, apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Builtin(DefaultFunction::TailList).force(), Term::Var( Name { text: tail_name.clone(), @@ -5242,7 +5293,7 @@ impl<'a> CodeGenerator<'a> { .into(), ), ) - .force_wrap(); + .force(); term = apply_wrap( Term::Lambda { @@ -5318,7 +5369,7 @@ impl<'a> CodeGenerator<'a> { Term::Delay(then.into()), term, ) - .force_wrap(); + .force(); } else { term = if_else( Term::Var( @@ -5331,7 +5382,7 @@ impl<'a> CodeGenerator<'a> { term, Term::Delay(then.into()), ) - .force_wrap(); + .force(); } arg_stack.push(term); } else { @@ -5394,7 +5445,7 @@ impl<'a> CodeGenerator<'a> { .into(), ), ) - .force_wrap(); + .force(); arg_stack.push(term); } } @@ -5422,7 +5473,7 @@ impl<'a> CodeGenerator<'a> { body: term.into(), }, apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Builtin(DefaultFunction::TailList).force(), Term::Var( Name { text: tail_name.clone(), @@ -5454,7 +5505,7 @@ impl<'a> CodeGenerator<'a> { .into(), ), ) - .force_wrap(); + .force(); } else { term = choose_list( Term::Var( @@ -5473,7 +5524,7 @@ impl<'a> CodeGenerator<'a> { ), Term::Delay(term.into()), ) - .force_wrap(); + .force(); } arg_stack.push(term); @@ -5507,7 +5558,7 @@ impl<'a> CodeGenerator<'a> { for (index, arg) in arg_vec.iter().enumerate().rev() { term = apply_wrap( apply_wrap( - Term::Builtin(DefaultFunction::MkCons).force_wrap(), + Term::Builtin(DefaultFunction::MkCons).force(), convert_type_to_data(arg.clone(), &tipo.arg_types().unwrap()[index]), ), term, @@ -5677,7 +5728,7 @@ impl<'a> CodeGenerator<'a> { for (arg, tipo) in args.into_iter().zip(tuple_sub_types.into_iter()).rev() { term = apply_wrap( apply_wrap( - Term::Builtin(DefaultFunction::MkCons).force_wrap(), + Term::Builtin(DefaultFunction::MkCons).force(), convert_type_to_data(arg, &tipo), ), term, @@ -5715,7 +5766,7 @@ impl<'a> CodeGenerator<'a> { unchanged_field_indices.reverse(); let mut term = apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Builtin(DefaultFunction::TailList).force(), Term::Var( Name { text: format!("{tail_name_prefix}_{highest_index}"), @@ -5731,7 +5782,7 @@ impl<'a> CodeGenerator<'a> { if let Some((tipo, arg)) = args.get(¤t_index) { term = apply_wrap( apply_wrap( - Term::Builtin(DefaultFunction::MkCons).force_wrap(), + Term::Builtin(DefaultFunction::MkCons).force(), convert_type_to_data(arg.clone(), tipo), ), term, @@ -5739,9 +5790,9 @@ impl<'a> CodeGenerator<'a> { } else { term = apply_wrap( apply_wrap( - Term::Builtin(DefaultFunction::MkCons).force_wrap(), + Term::Builtin(DefaultFunction::MkCons).force(), apply_wrap( - Term::Builtin(DefaultFunction::HeadList).force_wrap(), + Term::Builtin(DefaultFunction::HeadList).force(), Term::Var( Name { text: tail_name, @@ -5781,7 +5832,7 @@ impl<'a> CodeGenerator<'a> { if index < prev_index { for _ in index..prev_index { tail_list = apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Builtin(DefaultFunction::TailList).force(), tail_list, ); } @@ -5813,10 +5864,8 @@ impl<'a> CodeGenerator<'a> { ); for _ in 0..prev_index { - tail_list = apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - tail_list, - ); + tail_list = + apply_wrap(Term::Builtin(DefaultFunction::TailList).force(), tail_list); } if prev_index != 0 { term = apply_wrap( @@ -5884,9 +5933,7 @@ impl<'a> CodeGenerator<'a> { if tuple_index == 0 { term = convert_data_to_type( apply_wrap( - Term::Builtin(DefaultFunction::FstPair) - .force_wrap() - .force_wrap(), + Term::Builtin(DefaultFunction::FstPair).force().force(), term, ), &tipo.get_inner_types()[0], @@ -5894,9 +5941,7 @@ impl<'a> CodeGenerator<'a> { } else { term = convert_data_to_type( apply_wrap( - Term::Builtin(DefaultFunction::SndPair) - .force_wrap() - .force_wrap(), + Term::Builtin(DefaultFunction::SndPair).force().force(), term, ), &tipo.get_inner_types()[1], @@ -5962,8 +6007,8 @@ impl<'a> CodeGenerator<'a> { convert_data_to_type( apply_wrap( Term::Builtin(DefaultFunction::SndPair) - .force_wrap() - .force_wrap(), + .force() + .force(), Term::Var( Name { text: format!("__tuple_{list_id}"), @@ -5979,9 +6024,7 @@ impl<'a> CodeGenerator<'a> { }, convert_data_to_type( apply_wrap( - Term::Builtin(DefaultFunction::FstPair) - .force_wrap() - .force_wrap(), + Term::Builtin(DefaultFunction::FstPair).force().force(), Term::Var( Name { text: format!("__tuple_{list_id}"), @@ -6028,10 +6071,10 @@ impl<'a> CodeGenerator<'a> { let term = arg_stack.pop().unwrap(); let term = apply_wrap( - apply_wrap(Term::Builtin(DefaultFunction::Trace).force_wrap(), text), + apply_wrap(Term::Builtin(DefaultFunction::Trace).force(), text), Term::Delay(term.into()), ) - .force_wrap(); + .force(); arg_stack.push(term); } @@ -6077,9 +6120,7 @@ impl<'a> CodeGenerator<'a> { }, convert_data_to_type( apply_wrap( - Term::Builtin(DefaultFunction::FstPair) - .force_wrap() - .force_wrap(), + Term::Builtin(DefaultFunction::FstPair).force().force(), Term::Var( Name { text: subject_name.clone(), @@ -6103,9 +6144,7 @@ impl<'a> CodeGenerator<'a> { }, convert_data_to_type( apply_wrap( - Term::Builtin(DefaultFunction::SndPair) - .force_wrap() - .force_wrap(), + Term::Builtin(DefaultFunction::SndPair).force().force(), Term::Var( Name { text: subject_name.clone(), @@ -6132,7 +6171,7 @@ impl<'a> CodeGenerator<'a> { }, convert_data_to_type( apply_wrap( - Term::Builtin(DefaultFunction::HeadList).force_wrap(), + Term::Builtin(DefaultFunction::HeadList).force(), repeat_tail_list( Term::Var( Name { diff --git a/crates/aiken-project/src/blueprint/validator.rs b/crates/aiken-project/src/blueprint/validator.rs index 1cb03deb..d04ab0b2 100644 --- a/crates/aiken-project/src/blueprint/validator.rs +++ b/crates/aiken-project/src/blueprint/validator.rs @@ -111,10 +111,7 @@ impl Validator> { title: Some(redeemer.arg_name.get_label()), schema, })?, - program: generator - .generate(&def.fun.body, &arguments, true) - .try_into() - .unwrap(), + program: generator.generate(def).try_into().unwrap(), definitions, }) } diff --git a/crates/aiken-project/src/lib.rs b/crates/aiken-project/src/lib.rs index 7f9abdab..498a7d07 100644 --- a/crates/aiken-project/src/lib.rs +++ b/crates/aiken-project/src/lib.rs @@ -699,13 +699,13 @@ where { let left = generator .clone() - .generate(&left_src, &[], false) + .generate_test(&left_src, &[]) .try_into() .unwrap(); let right = generator .clone() - .generate(&right_src, &[], false) + .generate_test(&right_src, &[]) .try_into() .unwrap(); @@ -718,7 +718,7 @@ where None }; - let program = generator.generate(body, arguments, false); + let program = generator.generate_test(body, arguments); let script = Script::new( input_path, diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index 1570ae5f..fd44753a 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -206,14 +206,6 @@ impl Term { pub fn is_unit(&self) -> bool { matches!(self, Term::Constant(c) if c.as_ref() == &Constant::Unit) } - - pub fn force_wrap(self) -> Self { - Term::Force(self.into()) - } - - pub fn delay_wrap(self) -> Self { - Term::Delay(self.into()) - } } impl<'a, T> Display for Term @@ -285,6 +277,15 @@ pub struct Name { pub unique: Unique, } +impl Name { + pub fn text(t: impl ToString) -> Name { + Name { + text: t.to_string(), + unique: 0.into(), + } + } +} + impl hash::Hash for Name { fn hash(&self, state: &mut H) { self.text.hash(state); diff --git a/crates/uplc/src/ast/builder.rs b/crates/uplc/src/ast/builder.rs index ea8e9274..9bef8037 100644 --- a/crates/uplc/src/ast/builder.rs +++ b/crates/uplc/src/ast/builder.rs @@ -7,6 +7,66 @@ pub const CONSTR_INDEX_EXPOSER: &str = "__constr_index_exposer"; pub const CONSTR_GET_FIELD: &str = "__constr_get_field"; pub const ASSERT_ON_LIST: &str = "__assert_on_list"; +impl Term { + pub fn apply(self, arg: Self) -> Self { + Term::Apply { + function: self.into(), + argument: arg.into(), + } + } + + pub fn lambda(self, parameter_name: impl ToString) -> Self { + Term::Lambda { + parameter_name: Name::text(parameter_name).into(), + body: self.into(), + } + } + + pub fn force(self) -> Self { + Term::Force(self.into()) + } + + pub fn delay(self) -> Self { + Term::Delay(self.into()) + } + + pub fn var(name: impl ToString) -> Self { + Term::Var(Name::text(name).into()) + } + + pub fn integer(i: num_bigint::BigInt) -> Self { + Term::Constant(Constant::Integer(i).into()) + } + + pub fn constr_data() -> Self { + Term::Builtin(DefaultFunction::ConstrData) + } + + pub fn equals_integer() -> Self { + Term::Builtin(DefaultFunction::EqualsInteger) + } + + pub fn head_list() -> Self { + Term::Builtin(DefaultFunction::HeadList).force() + } + + pub fn delayed_if_else(self, then_term: Self, else_term: Self) -> Self { + Term::Apply { + function: Term::Apply { + function: Term::Apply { + function: Term::Builtin(DefaultFunction::IfThenElse).force().into(), + argument: self.into(), + } + .into(), + argument: Term::Delay(then_term.into()).into(), + } + .into(), + argument: Term::Delay(else_term.into()).into(), + } + .force() + } +} + pub fn apply_wrap(function: Term, arg: Term) -> Term { Term::Apply { function: function.into(), @@ -97,7 +157,7 @@ pub fn assert_on_list(term: Term) -> Term { Term::Constant(Constant::Unit.into()), apply_wrap( apply_wrap( - Term::Builtin(DefaultFunction::ChooseUnit).force_wrap(), + Term::Builtin(DefaultFunction::ChooseUnit).force(), apply_wrap( Term::Var( Name { @@ -107,7 +167,7 @@ pub fn assert_on_list(term: Term) -> Term { .into(), ), apply_wrap( - Term::Builtin(DefaultFunction::HeadList).force_wrap(), + Term::Builtin(DefaultFunction::HeadList).force(), Term::Var( Name { text: "list_to_check".to_string(), @@ -137,7 +197,7 @@ pub fn assert_on_list(term: Term) -> Term { ), ), apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), + Term::Builtin(DefaultFunction::TailList).force(), Term::Var( Name { text: "list_to_check".to_string(), @@ -461,9 +521,7 @@ pub fn delayed_if_else( Term::Apply { function: Term::Apply { function: Term::Apply { - function: Term::Builtin(DefaultFunction::IfThenElse) - .force_wrap() - .into(), + function: Term::Builtin(DefaultFunction::IfThenElse).force().into(), argument: condition.into(), } .into(), @@ -472,16 +530,14 @@ pub fn delayed_if_else( .into(), argument: Term::Delay(else_term.into()).into(), } - .force_wrap() + .force() } pub fn if_else(condition: Term, then_term: Term, else_term: Term) -> Term { Term::Apply { function: Term::Apply { function: Term::Apply { - function: Term::Builtin(DefaultFunction::IfThenElse) - .force_wrap() - .into(), + function: Term::Builtin(DefaultFunction::IfThenElse).force().into(), argument: condition.into(), } .into(), @@ -501,8 +557,8 @@ pub fn delayed_choose_list( function: Term::Apply { function: Term::Apply { function: Term::Builtin(DefaultFunction::ChooseList) - .force_wrap() - .force_wrap() + .force() + .force() .into(), argument: list.into(), } @@ -512,7 +568,7 @@ pub fn delayed_choose_list( .into(), argument: Term::Delay(else_term.into()).into(), } - .force_wrap() + .force() } pub fn choose_list( @@ -524,8 +580,8 @@ pub fn choose_list( function: Term::Apply { function: Term::Apply { function: Term::Builtin(DefaultFunction::ChooseList) - .force_wrap() - .force_wrap() + .force() + .force() .into(), argument: list.into(), } @@ -542,7 +598,7 @@ pub fn repeat_tail_list(term: Term, repeat: usize) -> Term { for _ in 0..repeat { term = Term::Apply { - function: Term::Builtin(DefaultFunction::TailList).force_wrap().into(), + function: Term::Builtin(DefaultFunction::TailList).force().into(), argument: term.into(), }; } diff --git a/crates/uplc/src/optimize/shrinker.rs b/crates/uplc/src/optimize/shrinker.rs index eb7181f5..38b18f85 100644 --- a/crates/uplc/src/optimize/shrinker.rs +++ b/crates/uplc/src/optimize/shrinker.rs @@ -43,9 +43,9 @@ impl Program { body: term.into(), }, if default_func.force_count() == 1 { - Term::Builtin(default_func).force_wrap() + Term::Builtin(default_func).force() } else { - Term::Builtin(default_func).force_wrap().force_wrap() + Term::Builtin(default_func).force().force() }, ); } diff --git a/examples/acceptance_tests/036/validators/spend.ak b/examples/acceptance_tests/036/validators/spend.ak index ba555c1a..eee9d633 100644 --- a/examples/acceptance_tests/036/validators/spend.ak +++ b/examples/acceptance_tests/036/validators/spend.ak @@ -13,15 +13,15 @@ pub fn has_policy_id(self: Output, policy_id: PolicyId) -> Bool { |> not } -validator spend { - fn(_datum: Data, _redeemer: Data, ctx: ScriptContext) -> Bool { +validator { + fn spend(_datum: Data, _redeemer: Data, ctx: ScriptContext) -> Bool { ctx.transaction.outputs |> list.any(has_policy_id(_, my_policy_id)) } } -validator mint(output_reference: OutputReference) { - fn(_redeemer: Void, ctx: ScriptContext) -> Bool { +validator (output_reference: OutputReference) { + fn mint(_redeemer: Void, ctx: ScriptContext) -> Bool { when list.find( ctx.transaction.inputs, diff --git a/examples/acceptance_tests/047/validators/foo.ak b/examples/acceptance_tests/047/validators/foo.ak index 55e122e5..4b750f78 100644 --- a/examples/acceptance_tests/047/validators/foo.ak +++ b/examples/acceptance_tests/047/validators/foo.ak @@ -1,5 +1,5 @@ -validator spend { - fn(_datum: Void, _redeemer: Void, _ctx: Void) -> Bool { +validator { + fn spend(_datum: Void, _redeemer: Void, _ctx: Void) -> Bool { True } } diff --git a/examples/acceptance_tests/048/validators/foo.ak b/examples/acceptance_tests/048/validators/foo.ak index 54906e7f..e41c9c8b 100644 --- a/examples/acceptance_tests/048/validators/foo.ak +++ b/examples/acceptance_tests/048/validators/foo.ak @@ -5,8 +5,8 @@ fn when_tuple(a: (Int, Int)) -> Int { } } -validator spend { - fn(a: Data, b: Data, c) -> Bool { +validator { + fn spend(a: Data, b: Data, c) -> Bool { when_tuple((4, 1)) == 4 } } diff --git a/examples/acceptance_tests/071/validators/spend.ak b/examples/acceptance_tests/071/validators/spend.ak index b1d87a6d..d039e0e5 100644 --- a/examples/acceptance_tests/071/validators/spend.ak +++ b/examples/acceptance_tests/071/validators/spend.ak @@ -96,8 +96,8 @@ pub fn validate_pool_borrow( True } -validator pool_contract { - fn(datum: PoolDatum, redeemer: PoolRedeemer, ctx: ScriptContext) { +validator { + fn pool_contract(datum: PoolDatum, redeemer: PoolRedeemer, ctx: ScriptContext) { when redeemer.action is { PoolWithdraw(_) -> True diff --git a/examples/acceptance_tests/077/validators/spend.ak b/examples/acceptance_tests/077/validators/spend.ak index 22446615..191db356 100644 --- a/examples/acceptance_tests/077/validators/spend.ak +++ b/examples/acceptance_tests/077/validators/spend.ak @@ -2,8 +2,8 @@ use aiken/list.{find, foldr} use aiken/transaction.{Input, ScriptContext, Spend} use aiken/transaction/value.{add, zero} -validator staking { - fn(_datum: Void, _redeemer: Void, context: ScriptContext) -> Bool { +validator { + fn staking(_datum: Void, _redeemer: Void, context: ScriptContext) -> Bool { expect Spend(ref) = context.purpose diff --git a/examples/acceptance_tests/077/validators/spend2.ak b/examples/acceptance_tests/077/validators/spend2.ak index 033989be..52b51d94 100644 --- a/examples/acceptance_tests/077/validators/spend2.ak +++ b/examples/acceptance_tests/077/validators/spend2.ak @@ -1,8 +1,8 @@ use aiken/list use aiken/transaction.{Output, ScriptContext} -validator backtrace { - fn(_datum: Void, _redeemer: Void, context: ScriptContext) -> Bool { +validator { + fn backtrace(_datum: Void, _redeemer: Void, context: ScriptContext) -> Bool { expect Some(_) = list.find(context.transaction.outputs, fn(_) { True }) let _ = diff --git a/examples/acceptance_tests/script_context/plutus.json b/examples/acceptance_tests/script_context/plutus.json index 0e44a9a5..ad83746e 100644 --- a/examples/acceptance_tests/script_context/plutus.json +++ b/examples/acceptance_tests/script_context/plutus.json @@ -6,91 +6,83 @@ }, "validators": [ { - "title": "deploy.spend", + "title": "basic.spend", "datum": { - "title": "Data", - "description": "Any Plutus data.", - "schema": {} + "title": "_datum", + "schema": { + "$ref": "#/definitions/Void" + } }, "redeemer": { - "title": "Data", - "description": "Any Plutus data.", - "schema": {} + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/Void" + } }, - "compiledCode": "5903e101000032323232323232323232322225333006323232323230020013301033300a32323375e0040026601893260103d87980000074c103d87a80004c0103d87980003301033300a32323232323232330123253330123370e002900009919299980c980e0010a4c2a6602c921364c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2069742065787065637465640016375a603400260180042a660289212b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e74001630143754002a66602266ebd30106d8799f182aff0000113370e64600a00200690020a50301700130093253330103370e900018099baa0011001153301249012a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e00163322330060020010013237280026ecd30106d8799f182aff0037566600e60106600e6010012900024028600200244a6660260022900009919b8048008cc00c00c004c058004c0040048894ccc0480084cdd2a400497ae013232323253330113371e00a002266e952000330170024bd7009998038038018029bae30130033013002301600330140024c0103d87a80004c0103d87980003301033300a32533301000116132533301100116132323232533301032323009001330173330113375e660146016002900226126d87a9f5820fcaa61fb85676101d9e3398a484674e71c45c3fd41b492682f3b0054f4cf3273ff004c0103d87a80004c0103d8798000330173330113375e6601460160029003260122d8799f581ce37db487fbd58c45d059bcbf5cd6b1604d3bec16cf888f1395a4ebc4ff004c0103d87a80004c0103d87980004bd700010991918048009980b99980899baf3300a300b3300a300b0014800120024c012ad8799fd8799fd8799f581c66666666666666666666666666666666666666666666666666666666ffffff004c0103d87a80004c0103d879800033017333011323253330133370e0029002099251300d00216301537540026601460160029002260103d87a80004c0103d87980004bd700008a50301600430150041630140013013001375866006600866006600800a900024008980103d87a80004c0103d87980004bd7018008009129998078008a5113232533300c00213300400400114a0602600466e1d2002300d3754602200244646660020029000001911199980619b870040020132333004004337000069001180a800800918059baa001149858c0040048894ccc0240085261330053001300b002333003003300c0020015734ae6d5ce2ab9d5573caae7d5d02ba15745", - "hash": "3326c651fce284b443e23da9cc6f5864a1e496f2fc7774799fb897f9" + "compiledCode": "5904600100003232323232323232323222253330063232323232300200132323232323233015333010323330113375e660146016002900b26126d8799f58200000000000000000000000000000000000000000000000000000000000000000ff004a0944cc024c02802d20004c0103d87a80004c0103d879800033015333010323253330123370e002900109919299980a19baf3300d300e00148001300126d8799f58200000000000000000000000000000000000000000000000000000000000000000ff0013370e6eb4cc034c038005200248000528180c80098060010b18099baa00133009300a00b48009300103d87a80004c0103d8798000330153330103232533301600116132533301700113232300c001330193330143375e6e98dd5998069807000a40046e98c0152080a8d6b9074c0103d87a80004c0103d8798000330193330143375e6601a601c6601a601c002900024000980122d8799f581c11111111111111111111111111111111111111111111111111111111ff004c0103d87a80004c0103d879800033019333014323253330163370e0029000099250301000214a2602e6ea8004cc034c038cc034c038005200048009300103d87a80004c0103d8798000330193330143375e6601a601c002900219ba5480012f5c098103d87a80004c0103d8798000330193330143375e6601a601c002900319ba5480092f5c098103d87a80004c0103d87980004bd70180c8010b180c8009bac3300a300b00148010cc024c02802d20004c0103d87a80004c0103d879800033015333010323375e6e98dd5998051805800a400c6e98c009205433009300a00b4800130103d87a80004c0103d87980004bd7011999111919000999991111999805002001801000a5eb7bdb180010004020cccc8888cccc03001000c0080052f5bded8c000400200e9101004881000013001001222225333016004133017337606ea400cdd300125eb7bdb1804c8c8c8c94ccc058cdd79980280380099ba5480012f5c026603666ec0dd48039ba6006008153330163371e00e00226603666ec0dd48039ba600600313301b337606ea4004dd3001199998048048018038030029bae30170033756602e004603400a603000844a66601c66e400080044cdd2a400097ae01533300e3371e004002266e9520024bd70099ba5480112f5c0600200244444a66602600826602866ec0dd48019ba80024bd6f7b630099191919299980999baf330050070013374a900025eb804cc060cdd81ba9007375000c0102a66602666e3c01c0044cc060cdd81ba9007375000c00626603066ec0dd48009ba800233333009009003007006005375c60280066eb4c050008c05c014c054010c004004894ccc0380045288991929998060010998020020008a5030120023370e900118061baa301000122323330010014800000c888cccc030cdc3802001009119980200219b8000348008c0500040048c028dd50008a4c2c6002002444a666010004293099802980098050011998018019805801000ab9a5736aae7555cf2ab9f5740ae855d101", + "hash": "42428cc55092a182161081f528491b8ec1fbd908c40eca9069c4d1be" + }, + { + "title": "mint.mint", + "redeemer": { + "title": "redeemer", + "schema": { + "$ref": "#/definitions/Data" + } + }, + "compiledCode": "590488010000323232323232323232323222533300532323232323001003300100122533300f00114a226464a6660180042660080080022940c04c008cdc3a4004601a6ea8c044004cc034ccc01cc8c8c8c8c8c8c94ccc04cc0580084c8c8cdc78018009bae3016001300932533300f3370e900018091baa0011001153301149012a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e00163300830090034800854cc0412401364c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2069742065787065637465640016375c602800264646464600c00200200264640026644660100040020029110000137566600c600e6600c600e00290002401000e600200244a666020002297ae01323232323301537520026600c00c0066eb8c04400cdd59808801180a0011809000980080091129998078010a5eb7bdb1804c8c8c8c94ccc038cdc7802800880189980a19bb037520026e98008ccc01c01c00c014dd718080019bab3010002301300330110024c103d87a80004c0103d87980003300d333007323232323322323232323253330123370e00290010b0991919b87001483c850dd6980d0009806801180a1baa001332233008002001001488103666f6f0033223233223253330153370e00290010801099190009bab301d00130100033017375400400297adef6c6033223300b002001002001375666012601400690040009bae30150013008533300d3370e900018081baa0021002153300f49012a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e001633005300600748008cc014c01801d20003001001222533301100213374a900125eb804c8c8c8c94ccc040cdc7802800899ba548000cc058dd400125eb804ccc01c01c00c014dd718090019bad3012002301500330130023001001222533300f00213374a900125eb804c8c8c8c94ccc038cdc7802800899ba548000cc050dd300125eb804ccc01c01c00c014dd718080019bab3010002301300330110024c103d87a80004c0103d87980003300d3330073232323233223232533300f3375e006002266e1cc8c018004dd5998049805198049805002240009009240042940c054004c020c94ccc038cdc3a400060226ea8004400454cc0412412a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e001633223300700200137566600e60106600e60100049000240246600e6010004900100380418008009129998080008a400026466e0120023300300300130130013001001222533300f00213374a900125eb804c8c8c8c94ccc038cdd7802800899ba548000cc0500092f5c0266600e00e00600a6020006602000460260066022004980103d87a80004c0103d87980004bd701119199800800a4000006444666601666e1c0100080488ccc010010cdc0001a40046028002002460146ea8004526163001001222533300900214984cc014c004c02c008ccc00c00cc0300080055cd2b9b5738aae7555cf2ab9f5740ae855d11", + "hash": "4621310698e9ee7830df98d79551eb5672cdee04247e28c8e1c1d494" }, { "title": "withdrawals.spend", "datum": { - "title": "Unit", - "description": "The nullary constructor.", + "title": "_datum", "schema": { - "anyOf": [ - { - "dataType": "constructor", - "index": 0, - "fields": [] - } - ] + "$ref": "#/definitions/Void" } }, "redeemer": { - "title": "Unit", - "description": "The nullary constructor.", + "title": "_redeemer", "schema": { - "anyOf": [ - { - "dataType": "constructor", - "index": 0, - "fields": [] - } - ] + "$ref": "#/definitions/Void" } }, "compiledCode": "59029101000032323232323232323232222533300632323232323001003300100122533300f00114a226464a66601a0042660080080022940c04c008cdc3a4004601a6ea8c044004c8c8c8cc040ccc02cc8c94ccc034cdc3800a40042c2646466e1c0052054375a6028002600e004601c6ea8004cc004dd5998021802998021802803240009006260126d8799fd8799f581c22222222222222222222222222222222222222222222222222222222ffff004c0103d87a80004c0103d87980003301033300b3232533300d3370e00290010b0991919b8700148070dd6980a000980380118071baa00133001375666008600a66008600a00c90002401898126d8799fd87a9f581cafddc16c18e7d8de379fb9aad39b3d1b5afd27603e5ebac818432a72ffff004c0103d87a80004c0103d87980003301033300b3375e6e9cc8c8c8c008004dd599803180399803180380424000900618008009129998088008a5eb804c8c8c8c8cc058004cc01801800cc04800cdd69809001180a80118098009ba7330104c0126d8799fd8799f581c22222222222222222222222222222222222222222222222222222222ffff00330104c126d8799fd87a9f581cafddc16c18e7d8de379fb9aad39b3d1b5afd27603e5ebac818432a72ffff004bd7026103d87a80004c0103d87980004bd70111980180100098008009112999807801099ba5480092f5c0264646464a66601e66ebc0140044cdd2a4000660286ea00092f5c0266600e00e00600a60200066eb4c040008c04c00cc04400888c8ccc0040052000003222333300c3370e008004024466600800866e0000d200230140010012300a37540022930b180080091129998040010a4c26600a600260140046660060066016004002ae695cdaab9d5573caae7d5d02ba15745", "hash": "6917ce2313801b854e38507b68d2c61afd70fca721804235e4760056" }, { - "title": "mint.mint", - "redeemer": { - "title": "Data", - "description": "Any Plutus data.", - "schema": {} - }, - "compiledCode": "590488010000323232323232323232323222533300532323232323001003300100122533300f00114a226464a6660180042660080080022940c04c008cdc3a4004601a6ea8c044004cc034ccc01cc8c8c8c8c8c8c94ccc04cc0580084c8c8cdc78018009bae3016001300932533300f3370e900018091baa0011001153301149012a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e00163300830090034800854cc0412401364c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2069742065787065637465640016375c602800264646464600c00200200264640026644660100040020029110000137566600c600e6600c600e00290002401000e600200244a666020002297ae01323232323301537520026600c00c0066eb8c04400cdd59808801180a0011809000980080091129998078010a5eb7bdb1804c8c8c8c94ccc038cdc7802800880189980a19bb037520026e98008ccc01c01c00c014dd718080019bab3010002301300330110024c103d87a80004c0103d87980003300d333007323232323322323232323253330123370e00290010b0991919b87001483c850dd6980d0009806801180a1baa001332233008002001001488103666f6f0033223233223253330153370e00290010801099190009bab301d00130100033017375400400297adef6c6033223300b002001002001375666012601400690040009bae30150013008533300d3370e900018081baa0021002153300f49012a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e001633005300600748008cc014c01801d20003001001222533301100213374a900125eb804c8c8c8c94ccc040cdc7802800899ba548000cc058dd400125eb804ccc01c01c00c014dd718090019bad3012002301500330130023001001222533300f00213374a900125eb804c8c8c8c94ccc038cdc7802800899ba548000cc050dd300125eb804ccc01c01c00c014dd718080019bab3010002301300330110024c103d87a80004c0103d87980003300d3330073232323233223232533300f3375e006002266e1cc8c018004dd5998049805198049805002240009009240042940c054004c020c94ccc038cdc3a400060226ea8004400454cc0412412a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e001633223300700200137566600e60106600e60100049000240246600e6010004900100380418008009129998080008a400026466e0120023300300300130130013001001222533300f00213374a900125eb804c8c8c8c94ccc038cdd7802800899ba548000cc0500092f5c0266600e00e00600a6020006602000460260066022004980103d87a80004c0103d87980004bd701119199800800a4000006444666601666e1c0100080488ccc010010cdc0001a40046028002002460146ea8004526163001001222533300900214984cc014c004c02c008ccc00c00cc0300080055cd2b9b5738aae7555cf2ab9f5740ae855d11", - "hash": "4621310698e9ee7830df98d79551eb5672cdee04247e28c8e1c1d494" - }, - { - "title": "basic.spend", + "title": "deploy.spend", "datum": { - "title": "Unit", - "description": "The nullary constructor.", + "title": "datum", "schema": { - "anyOf": [ - { - "dataType": "constructor", - "index": 0, - "fields": [] - } - ] + "$ref": "#/definitions/Data" } }, "redeemer": { - "title": "Unit", - "description": "The nullary constructor.", + "title": "_redeemer", "schema": { - "anyOf": [ - { - "dataType": "constructor", - "index": 0, - "fields": [] - } - ] + "$ref": "#/definitions/Data" } }, - "compiledCode": "59046f0100003232323232323232323222253330063232323232300200132323232323233015333010323330113375e660146016002900b26126d8799f58200000000000000000000000000000000000000000000000000000000000000000ff004a0944cc024c02802d20004c0103d87a80004c0103d879800033015333010323253330123370e002900109919299980a19baf3300d300e00148001300126d8799f58200000000000000000000000000000000000000000000000000000000000000000ff0013370e6eb4cc034c038005200248000528180c80098060010b18099baa00133009300a00b48009300103d87a80004c0103d8798000330153330103232533301600116132533301700113232300c001330193330143375e6e98dd5998069807000a40046e98c0152080a8d6b9074c0103d87a80004c0103d8798000330193330143375e6601a601c6601a601c002900024000980122d8799f581c11111111111111111111111111111111111111111111111111111111ff004c0103d87a80004c0103d879800033019333014323253330163370e0029000099250301000214a2602e6ea8004cc034c038cc034c038005200048009300103d87a80004c0103d8798000330193330143375e6601a601c002900219ba5480012f5c098103d87a80004c0103d8798000330193330143375e6601a601c002900319ba5480092f5c098103d87a80004c0103d87980004bd70180c8010b180c8009bac3300a300b00148010cc024c02802d20004c0103d87a80004c0103d879800033015333010323375e6e98dd5998051805800a400c6e98c009205433009300a00b4800130103d87a80004c0103d87980004bd701199911299980999b870014800052f5bded8c02646400266664444666601400800600400297adef6c6000400100833332222333300c0040030020014bd6f7b630001000803a45004881000013001001222225333016004133017337606ea400cdd300125eb7bdb1804c8c8c8c94ccc058cdd79980280380099ba5480012f5c026603666ec0dd48039ba6006008153330163371e00e00226603666ec0dd48039ba600600313301b337606ea4004dd3001199998048048018038030029bae30170033756602e004603400a603000844a66601c66e400080044cdd2a400097ae01533300e3371e004002266e9520024bd70099ba5480112f5c0600200244444a66602600826602866ec0dd48019ba80024bd6f7b630099191919299980999baf330050070013374a900025eb804cc060cdd81ba9007375000c0102a66602666e3c01c0044cc060cdd81ba9007375000c00626603066ec0dd48009ba800233333009009003007006005375c60280066eb4c050008c05c014c054010c004004894ccc0380045288991929998060010998020020008a5030120023370e900118061baa301000122323330010014800000c888cccc030cdc3802001009119980200219b8000348008c0500040048c028dd50008a4c2c6002002444a666010004293099802980098050011998018019805801000ab9a5736aae7555cf2ab9f5740ae855d101", - "hash": "911eb2bc725e7a51670338f2a12102785984d2569fa75b6ba4054c49" + "compiledCode": "5903e101000032323232323232323232322225333006323232323230020013301033300a32323375e0040026601893260103d87980000074c103d87a80004c0103d87980003301033300a32323232323232330123253330123370e002900009919299980c980e0010a4c2a6602c921364c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2069742065787065637465640016375a603400260180042a660289212b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e74001630143754002a66602266ebd30106d8799f182aff0000113370e64600a00200690020a50301700130093253330103370e900018099baa0011001153301249012a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e00163322330060020010013237280026ecd30106d8799f182aff0037566600e60106600e6010012900024028600200244a6660260022900009919b8048008cc00c00c004c058004c0040048894ccc0480084cdd2a400497ae013232323253330113371e00a002266e952000330170024bd7009998038038018029bae30130033013002301600330140024c0103d87a80004c0103d87980003301033300a32533301000116132533301100116132323232533301032323009001330173330113375e660146016002900226126d87a9f5820fcaa61fb85676101d9e3398a484674e71c45c3fd41b492682f3b0054f4cf3273ff004c0103d87a80004c0103d8798000330173330113375e6601460160029003260122d8799f581ce37db487fbd58c45d059bcbf5cd6b1604d3bec16cf888f1395a4ebc4ff004c0103d87a80004c0103d87980004bd700010991918048009980b99980899baf3300a300b3300a300b0014800120024c012ad8799fd8799fd8799f581c66666666666666666666666666666666666666666666666666666666ffffff004c0103d87a80004c0103d879800033017333011323253330133370e0029002099251300d00216301537540026601460160029002260103d87a80004c0103d87980004bd700008a50301600430150041630140013013001375866006600866006600800a900024008980103d87a80004c0103d87980004bd7018008009129998078008a5113232533300c00213300400400114a0602600466e1d2002300d3754602200244646660020029000001911199980619b870040020132333004004337000069001180a800800918059baa001149858c0040048894ccc0240085261330053001300b002333003003300c0020015734ae6d5ce2ab9d5573caae7d5d02ba15745", + "hash": "3326c651fce284b443e23da9cc6f5864a1e496f2fc7774799fb897f9" } - ] + ], + "definitions": { + "Data": { + "title": "Data", + "description": "Any Plutus data." + }, + "Void": { + "title": "Unit", + "description": "The nullary constructor.", + "anyOf": [ + { + "dataType": "constructor", + "index": 0, + "fields": [] + } + ] + } + } } \ No newline at end of file diff --git a/examples/acceptance_tests/script_context/validators/basic.ak b/examples/acceptance_tests/script_context/validators/basic.ak index 57b3f37a..ae79cfbc 100644 --- a/examples/acceptance_tests/script_context/validators/basic.ak +++ b/examples/acceptance_tests/script_context/validators/basic.ak @@ -4,8 +4,8 @@ use aiken/transaction.{NoDatum, ScriptContext, Spend, TransactionId} use aiken/transaction/credential.{VerificationKeyCredential} use aiken/transaction/value -validator spend { - fn(_datum: Void, _redeemer: Void, ctx: ScriptContext) { +validator { + fn spend(_datum: Void, _redeemer: Void, ctx: ScriptContext) { [ assert_id(ctx.transaction), assert_purpose(ctx.purpose), diff --git a/examples/acceptance_tests/script_context/validators/deploy.ak b/examples/acceptance_tests/script_context/validators/deploy.ak index dd7f9ae5..d9e2a20e 100644 --- a/examples/acceptance_tests/script_context/validators/deploy.ak +++ b/examples/acceptance_tests/script_context/validators/deploy.ak @@ -5,8 +5,8 @@ use aiken/list use aiken/transaction.{DatumHash, InlineDatum, ScriptContext} use aiken/transaction/credential.{Inline, VerificationKeyCredential} -validator spend { - fn(datum: Data, _redeemer: Data, ctx: ScriptContext) { +validator { + fn spend(datum: Data, _redeemer: Data, ctx: ScriptContext) { [ assert_datum(datum), assert_datums(ctx.transaction.datums), diff --git a/examples/acceptance_tests/script_context/validators/mint.ak b/examples/acceptance_tests/script_context/validators/mint.ak index d55a5fb5..2cf739cd 100644 --- a/examples/acceptance_tests/script_context/validators/mint.ak +++ b/examples/acceptance_tests/script_context/validators/mint.ak @@ -3,8 +3,8 @@ use aiken/list use aiken/transaction.{Mint, ScriptContext} use aiken/transaction/value -validator mint { - fn(redeemer: Data, ctx: ScriptContext) { +validator { + fn mint(redeemer: Data, ctx: ScriptContext) { [ assert_purpose(ctx), assert_mint(ctx.purpose, ctx.transaction), diff --git a/examples/acceptance_tests/script_context/validators/withdrawals.ak b/examples/acceptance_tests/script_context/validators/withdrawals.ak index 8c19e01e..954eda12 100644 --- a/examples/acceptance_tests/script_context/validators/withdrawals.ak +++ b/examples/acceptance_tests/script_context/validators/withdrawals.ak @@ -5,8 +5,8 @@ use aiken/transaction/credential.{ Inline, ScriptCredential, VerificationKeyCredential, } -validator spend { - fn(_datum: Void, _redeemer: Void, ctx: ScriptContext) { +validator { + fn spend(_datum: Void, _redeemer: Void, ctx: ScriptContext) { let alice = Inline( VerificationKeyCredential( From bf34324ac0b1a9fe2743ec434ad883097759b0ae Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 17 Mar 2023 09:38:58 -0400 Subject: [PATCH 05/17] Update crates/aiken-lang/src/format.rs Co-authored-by: Matthias Benkort <5680256+KtorZ@users.noreply.github.com> --- crates/aiken-lang/src/format.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index cf20497f..77113e5c 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -522,7 +522,7 @@ impl<'comments> Formatter<'comments> { .nest(INDENT) .group() .append(if other_fun.is_some() { - docvec![line()] + line() } else { nil() }) From 3d92772d2383fca23412b8c4b5c6417478c98afc Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 17 Mar 2023 09:42:21 -0400 Subject: [PATCH 06/17] Update crates/aiken-lang/src/tipo/infer.rs Co-authored-by: Matthias Benkort <5680256+KtorZ@users.noreply.github.com> --- crates/aiken-lang/src/tipo/infer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/aiken-lang/src/tipo/infer.rs b/crates/aiken-lang/src/tipo/infer.rs index 25bf7458..7c029681 100644 --- a/crates/aiken-lang/src/tipo/infer.rs +++ b/crates/aiken-lang/src/tipo/infer.rs @@ -312,8 +312,8 @@ fn infer_definition( || other_typed_fun.arguments.len() > 3 { return Err(Error::IncorrectValidatorArity { - count: typed_fun.arguments.len() as u32, - location: typed_fun.location, + count: other_typed_fun.arguments.len() as u32, + location: other_typed_fun.location, }); } From 950598b534131e347187d0a43586c0455f9e2d9f Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 17 Mar 2023 09:45:25 -0400 Subject: [PATCH 07/17] Update crates/aiken-lang/src/parser.rs Co-authored-by: Matthias Benkort <5680256+KtorZ@users.noreply.github.com> --- crates/aiken-lang/src/parser.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 672765ae..48da0f45 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -299,8 +299,7 @@ pub fn validator_parser() -> impl Parser Date: Fri, 17 Mar 2023 09:45:49 -0400 Subject: [PATCH 08/17] Update crates/aiken-lang/src/parser.rs Co-authored-by: Matthias Benkort <5680256+KtorZ@users.noreply.github.com> --- crates/aiken-lang/src/parser.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 48da0f45..cd7cbd12 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -305,9 +305,9 @@ pub fn validator_parser() -> impl Parser Date: Fri, 17 Mar 2023 15:22:35 +0100 Subject: [PATCH 09/17] distinguish between multi vs single validator case in blueprint schema generation. --- crates/aiken-project/src/blueprint/validator.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/aiken-project/src/blueprint/validator.rs b/crates/aiken-project/src/blueprint/validator.rs index d04ab0b2..67f54ea1 100644 --- a/crates/aiken-project/src/blueprint/validator.rs +++ b/crates/aiken-project/src/blueprint/validator.rs @@ -107,9 +107,12 @@ impl Validator> { module.code.clone(), ), }) - .map(|schema| Argument { - title: Some(redeemer.arg_name.get_label()), - schema, + .map(|schema| match datum { + Some(..) if def.other_fun.is_some() => todo!(), + _ => Argument { + title: Some(redeemer.arg_name.get_label()), + schema, + }, })?, program: generator.generate(def).try_into().unwrap(), definitions, From fe92b27d501badce59c0fecd5ad7efd4d604a7ca Mon Sep 17 00:00:00 2001 From: Kasey White Date: Fri, 17 Mar 2023 10:39:26 -0400 Subject: [PATCH 10/17] start on refactoring validator to support multi-validators in blueprint --- crates/aiken-project/src/blueprint/mod.rs | 2 +- .../aiken-project/src/blueprint/validator.rs | 45 ++++++++++++++----- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/crates/aiken-project/src/blueprint/mod.rs b/crates/aiken-project/src/blueprint/mod.rs index 2863d5f1..9930169e 100644 --- a/crates/aiken-project/src/blueprint/mod.rs +++ b/crates/aiken-project/src/blueprint/mod.rs @@ -60,7 +60,7 @@ impl Blueprint> { let validators: Result, Error> = modules .validators() - .map(|(validator, def)| { + .flat_map(|(validator, def)| { Validator::from_checked_module(modules, generator, validator, def).map( |mut schema| { definitions.merge(&mut schema.definitions); diff --git a/crates/aiken-project/src/blueprint/validator.rs b/crates/aiken-project/src/blueprint/validator.rs index 67f54ea1..af6ee792 100644 --- a/crates/aiken-project/src/blueprint/validator.rs +++ b/crates/aiken-project/src/blueprint/validator.rs @@ -4,10 +4,13 @@ use super::{ schema::{Annotated, Schema}, }; use crate::module::{CheckedModule, CheckedModules}; -use aiken_lang::{ast::TypedValidator, uplc::CodeGenerator}; +use aiken_lang::{ + ast::{TypedArg, TypedFunction, TypedValidator}, + uplc::CodeGenerator, +}; use miette::NamedSource; use serde; -use uplc::ast::{DeBruijn, Program, Term}; +use uplc::ast::{DeBruijn, Name, Program, Term}; #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)] pub struct Validator { @@ -47,19 +50,39 @@ impl Validator> { generator: &mut CodeGenerator, module: &CheckedModule, def: &TypedValidator, - ) -> Result>, Error> { - let mut args = def.fun.arguments.iter().rev(); - let (_, redeemer, datum) = (args.next(), args.next().unwrap(), args.next()); - - let mut arguments = Vec::with_capacity(def.params.len() + def.fun.arguments.len()); - - arguments.extend(def.params.clone()); - arguments.extend(def.fun.arguments.clone()); + ) -> Vec>, Error>> { + let program: Program = generator.generate(def).try_into().unwrap(); let mut definitions = Definitions::new(); + let mut validators = vec![Validator::create_validator_blueprint( + modules, def.params, def.fun, + )]; + + if let Some(other_func) = def.other_fun { + todo!() + } else { + todo!() + } + + validators + } + + fn create_validator_blueprint( + modules: &CheckedModules, + params: Vec, + func: TypedFunction, + ) -> Result>, Error> { + let mut args = func.arguments.iter().rev(); + let (_, redeemer, datum) = (args.next(), args.next().unwrap(), args.next()); + + let mut arguments = Vec::with_capacity(params.len() + func.arguments.len()); + + arguments.extend(params.clone()); + arguments.extend(func.arguments.clone()); + Ok(Validator { - title: format!("{}.{}", &module.name, &def.fun.name), + title: format!("{}.{}", &module.name, &func.name), description: None, parameters: def .params From 7e1403a3b29eb86a5e6f1fb458f2ce1b65d5f9f1 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Fri, 17 Mar 2023 15:56:01 +0100 Subject: [PATCH 11/17] Complete generation of blueprint multi-validators. --- crates/aiken-project/src/blueprint/mod.rs | 17 +++--- .../aiken-project/src/blueprint/validator.rs | 55 +++++++++++++------ 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/crates/aiken-project/src/blueprint/mod.rs b/crates/aiken-project/src/blueprint/mod.rs index 9930169e..2749d74b 100644 --- a/crates/aiken-project/src/blueprint/mod.rs +++ b/crates/aiken-project/src/blueprint/mod.rs @@ -61,13 +61,16 @@ impl Blueprint> { let validators: Result, Error> = modules .validators() .flat_map(|(validator, def)| { - Validator::from_checked_module(modules, generator, validator, def).map( - |mut schema| { - definitions.merge(&mut schema.definitions); - schema.definitions = Definitions::new(); - schema - }, - ) + Validator::from_checked_module(modules, generator, validator, def) + .into_iter() + .map(|result| { + result.map(|mut schema| { + definitions.merge(&mut schema.definitions); + schema.definitions = Definitions::new(); + schema + }) + }) + .collect::>() }) .collect(); diff --git a/crates/aiken-project/src/blueprint/validator.rs b/crates/aiken-project/src/blueprint/validator.rs index af6ee792..c0b10ecd 100644 --- a/crates/aiken-project/src/blueprint/validator.rs +++ b/crates/aiken-project/src/blueprint/validator.rs @@ -10,7 +10,7 @@ use aiken_lang::{ }; use miette::NamedSource; use serde; -use uplc::ast::{DeBruijn, Name, Program, Term}; +use uplc::ast::{DeBruijn, Program, Term}; #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)] pub struct Validator { @@ -51,18 +51,28 @@ impl Validator> { module: &CheckedModule, def: &TypedValidator, ) -> Vec>, Error>> { - let program: Program = generator.generate(def).try_into().unwrap(); + let program = generator.generate(def).try_into().unwrap(); - let mut definitions = Definitions::new(); + let is_multi_validator = def.other_fun.is_some(); let mut validators = vec![Validator::create_validator_blueprint( - modules, def.params, def.fun, + modules, + module, + &program, + &def.params, + &def.fun, + is_multi_validator, )]; - if let Some(other_func) = def.other_fun { - todo!() - } else { - todo!() + if let Some(ref other_func) = def.other_fun { + validators.push(Validator::create_validator_blueprint( + modules, + module, + &program, + &def.params, + other_func, + is_multi_validator, + )); } validators @@ -70,22 +80,25 @@ impl Validator> { fn create_validator_blueprint( modules: &CheckedModules, - params: Vec, - func: TypedFunction, + module: &CheckedModule, + program: &Program, + params: &[TypedArg], + func: &TypedFunction, + is_multi_validator: bool, ) -> Result>, Error> { let mut args = func.arguments.iter().rev(); let (_, redeemer, datum) = (args.next(), args.next().unwrap(), args.next()); let mut arguments = Vec::with_capacity(params.len() + func.arguments.len()); - - arguments.extend(params.clone()); + arguments.extend(params.to_vec()); arguments.extend(func.arguments.clone()); + let mut definitions = Definitions::new(); + Ok(Validator { title: format!("{}.{}", &module.name, &func.name), description: None, - parameters: def - .params + parameters: params .iter() .map(|param| { Annotated::from_type(modules.into(), ¶m.tipo, &mut definitions) @@ -131,13 +144,13 @@ impl Validator> { ), }) .map(|schema| match datum { - Some(..) if def.other_fun.is_some() => todo!(), + Some(..) if is_multi_validator => todo!(), _ => Argument { title: Some(redeemer.arg_name.get_label()), schema, }, })?, - program: generator.generate(def).try_into().unwrap(), + program: program.clone(), definitions, }) } @@ -283,7 +296,15 @@ mod test { .next() .expect("source code did no yield any validator"); - let validator = Validator::from_checked_module(&modules, &mut generator, validator, def) + let validators = Validator::from_checked_module(&modules, &mut generator, validator, def); + + if validators.len() > 1 { + panic!("Multi-validator given to test bench. Don't do that.") + } + + let validator = validators + .get(0) + .unwrap() .expect("Failed to create validator blueprint"); println!("{}", serde_json::to_string_pretty(&validator).unwrap()); From bb6fc7697127053dbf19db6b9f3cc593d8f39e3e Mon Sep 17 00:00:00 2001 From: Kasey White Date: Fri, 17 Mar 2023 11:11:37 -0400 Subject: [PATCH 12/17] start on registering redeemer wrapper type in definitions --- crates/aiken-lang/src/builtins.rs | 10 ++++++++++ crates/aiken-project/src/blueprint/validator.rs | 14 +++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/crates/aiken-lang/src/builtins.rs b/crates/aiken-lang/src/builtins.rs index e3099568..e9825ced 100644 --- a/crates/aiken-lang/src/builtins.rs +++ b/crates/aiken-lang/src/builtins.rs @@ -23,6 +23,7 @@ pub const RESULT: &str = "Result"; pub const STRING: &str = "String"; pub const OPTION: &str = "Option"; pub const ORDERING: &str = "Ordering"; +pub const REDEEMER_WRAPPER: &str = "RedeemerWrapper"; /// Build a prelude that can be injected /// into a compiler pipeline @@ -1057,3 +1058,12 @@ pub fn unbound_var(id: u64) -> Arc { Arc::new(Type::Var { tipo }) } + +pub fn wrapped_redeemer(redeemer: Arc) -> Arc { + Arc::new(Type::App { + public: true, + module: "".to_string(), + name: REDEEMER_WRAPPER.to_string(), + args: vec![redeemer], + }) +} diff --git a/crates/aiken-project/src/blueprint/validator.rs b/crates/aiken-project/src/blueprint/validator.rs index c0b10ecd..73958b0c 100644 --- a/crates/aiken-project/src/blueprint/validator.rs +++ b/crates/aiken-project/src/blueprint/validator.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use super::{ definitions::{Definitions, Reference}, error::Error, @@ -6,6 +8,7 @@ use super::{ use crate::module::{CheckedModule, CheckedModules}; use aiken_lang::{ ast::{TypedArg, TypedFunction, TypedValidator}, + builtins::wrapped_redeemer, uplc::CodeGenerator, }; use miette::NamedSource; @@ -144,7 +147,11 @@ impl Validator> { ), }) .map(|schema| match datum { - Some(..) if is_multi_validator => todo!(), + Some(..) if is_multi_validator => { + let wrap_redeemer_type = wrapped_redeemer(redeemer.tipo); + + definitions.register(&wrap_redeemer_type, &HashMap::new(), todo!()); + } _ => Argument { title: Some(redeemer.arg_name.get_label()), schema, @@ -305,11 +312,12 @@ mod test { let validator = validators .get(0) .unwrap() + .as_ref() .expect("Failed to create validator blueprint"); - println!("{}", serde_json::to_string_pretty(&validator).unwrap()); + println!("{}", serde_json::to_string_pretty(validator).unwrap()); - assert_json_eq!(serde_json::to_value(&validator).unwrap(), expected); + assert_json_eq!(serde_json::to_value(validator).unwrap(), expected); } #[test] From bc690c541039b84c9f20c2468a362a7234e9ec9e Mon Sep 17 00:00:00 2001 From: KtorZ Date: Fri, 17 Mar 2023 16:30:30 +0100 Subject: [PATCH 13/17] Generated wrapped schemas for multi-validators' redeemers --- crates/aiken-lang/src/format.rs | 6 +--- crates/aiken-lang/src/parser.rs | 4 +-- crates/aiken-project/src/blueprint/schema.rs | 28 +++++++++++++++++++ .../aiken-project/src/blueprint/validator.rs | 21 ++++++-------- 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index 77113e5c..9881ff62 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -521,11 +521,7 @@ impl<'comments> Formatter<'comments> { )) .nest(INDENT) .group() - .append(if other_fun.is_some() { - line() - } else { - nil() - }) + .append(if other_fun.is_some() { line() } else { nil() }) .append( other_fun .as_ref() diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index cd7cbd12..3a107ebc 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -306,8 +306,8 @@ pub fn validator_parser() -> impl Parser { #[serde(skip_serializing_if = "Option::is_none")] @@ -71,6 +75,30 @@ impl From for Annotated { } impl Annotated { + pub fn as_wrapped_redeemer( + definitions: &mut Definitions>, + schema: Reference, + type_info: Arc, + ) -> Reference { + definitions + .register( + &wrapped_redeemer(type_info), + &HashMap::new(), + |_| { + Ok::<_, Error>(Annotated { + title: Some("Wrapped Redeemer".to_string()), + description: Some("A redeemer wrapped in an extra constructor to make multi-validator detection possible on-chain.".to_string()), + annotated: Schema::Data(Data::AnyOf(vec![Constructor { + index: REDEEMER_DISCRIMINANT, + fields: vec![schema.into()], + } + .into()])), + }) + }, + ) + .expect("cannot fail because Ok") + } + pub fn from_type( modules: &HashMap, type_info: &Type, diff --git a/crates/aiken-project/src/blueprint/validator.rs b/crates/aiken-project/src/blueprint/validator.rs index 73958b0c..1170e5a4 100644 --- a/crates/aiken-project/src/blueprint/validator.rs +++ b/crates/aiken-project/src/blueprint/validator.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use super::{ definitions::{Definitions, Reference}, error::Error, @@ -8,7 +6,6 @@ use super::{ use crate::module::{CheckedModule, CheckedModules}; use aiken_lang::{ ast::{TypedArg, TypedFunction, TypedValidator}, - builtins::wrapped_redeemer, uplc::CodeGenerator, }; use miette::NamedSource; @@ -146,15 +143,15 @@ impl Validator> { module.code.clone(), ), }) - .map(|schema| match datum { - Some(..) if is_multi_validator => { - let wrap_redeemer_type = wrapped_redeemer(redeemer.tipo); - - definitions.register(&wrap_redeemer_type, &HashMap::new(), todo!()); - } - _ => Argument { - title: Some(redeemer.arg_name.get_label()), - schema, + .map(|schema| Argument { + title: Some(redeemer.arg_name.get_label()), + schema: match datum { + Some(..) if is_multi_validator => Annotated::as_wrapped_redeemer( + &mut definitions, + schema, + redeemer.tipo.clone(), + ), + _ => schema, }, })?, program: program.clone(), From a4c7337df29cbfb942fdf312bbe599976762361e Mon Sep 17 00:00:00 2001 From: KtorZ Date: Fri, 17 Mar 2023 16:59:35 +0100 Subject: [PATCH 14/17] Add new acceptance scenario 079 (double validators) --- examples/acceptance_tests/079/aiken.lock | 5 ++ examples/acceptance_tests/079/aiken.toml | 2 + examples/acceptance_tests/079/plutus.json | 57 +++++++++++++++++++ .../acceptance_tests/079/validators/foo.ak | 9 +++ 4 files changed, 73 insertions(+) create mode 100644 examples/acceptance_tests/079/aiken.lock create mode 100644 examples/acceptance_tests/079/aiken.toml create mode 100644 examples/acceptance_tests/079/plutus.json create mode 100644 examples/acceptance_tests/079/validators/foo.ak diff --git a/examples/acceptance_tests/079/aiken.lock b/examples/acceptance_tests/079/aiken.lock new file mode 100644 index 00000000..3a78b1e7 --- /dev/null +++ b/examples/acceptance_tests/079/aiken.lock @@ -0,0 +1,5 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] diff --git a/examples/acceptance_tests/079/aiken.toml b/examples/acceptance_tests/079/aiken.toml new file mode 100644 index 00000000..ba588880 --- /dev/null +++ b/examples/acceptance_tests/079/aiken.toml @@ -0,0 +1,2 @@ +name = "aiken-lang/acceptance_test_079" +version = "0.0.0" diff --git a/examples/acceptance_tests/079/plutus.json b/examples/acceptance_tests/079/plutus.json new file mode 100644 index 00000000..17aa6d48 --- /dev/null +++ b/examples/acceptance_tests/079/plutus.json @@ -0,0 +1,57 @@ +{ + "preamble": { + "title": "aiken-lang/acceptance_test_079", + "version": "0.0.0", + "plutusVersion": "v2" + }, + "validators": [ + { + "title": "foo.spend", + "datum": { + "title": "datum", + "schema": { + "$ref": "#/definitions/Int" + } + }, + "redeemer": { + "title": "redeemer", + "schema": { + "$ref": "#/definitions/RedeemerWrapper$Int" + } + }, + "compiledCode": "58a7010000323232323232323232322253330063370e900018041baa0011332253330083370e004902a0a4c2c6eb40080044cc88c894ccc028cdc399b800040024815052616375a0026eb4008c02cc8c028dd50008009119199800800a4000006444666601066e1c0100080348ccc010010cdc0001a4004601e0020026002002444a666010004293099802980098048011998018019805001000ab9a5736aae7555cf2ab9f5742ae89", + "hash": "d5e5d02c9a5b71045eb8a0cfabd036d3a89bc3a403491bbff65a9621" + }, + { + "title": "foo.mint", + "redeemer": { + "title": "redeemer", + "schema": { + "$ref": "#/definitions/Int" + } + }, + "compiledCode": "58a7010000323232323232323232322253330063370e900018041baa0011332253330083370e004902a0a4c2c6eb40080044cc88c894ccc028cdc399b800040024815052616375a0026eb4008c02cc8c028dd50008009119199800800a4000006444666601066e1c0100080348ccc010010cdc0001a4004601e0020026002002444a666010004293099802980098048011998018019805001000ab9a5736aae7555cf2ab9f5742ae89", + "hash": "d5e5d02c9a5b71045eb8a0cfabd036d3a89bc3a403491bbff65a9621" + } + ], + "definitions": { + "Int": { + "dataType": "integer" + }, + "RedeemerWrapper$Int": { + "title": "Wrapped Redeemer", + "description": "A redeemer wrapped in an extra constructor to make multi-validator detection possible on-chain.", + "anyOf": [ + { + "dataType": "constructor", + "index": 1, + "fields": [ + { + "$ref": "#/definitions/Int" + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/examples/acceptance_tests/079/validators/foo.ak b/examples/acceptance_tests/079/validators/foo.ak new file mode 100644 index 00000000..0889e994 --- /dev/null +++ b/examples/acceptance_tests/079/validators/foo.ak @@ -0,0 +1,9 @@ +validator { + fn spend(datum: Int, redeemer: Int, _context: Data) { + datum + redeemer == 42 + } + + fn mint(redeemer: Int, _context: Data) { + redeemer == 42 + } +} From d753b57c1be474ff92f7136af9658f9255e68b37 Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 17 Mar 2023 17:50:41 -0400 Subject: [PATCH 15/17] =?UTF-8?q?chore:=20use=20let-else=20=F0=9F=A4=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/aiken-lang/src/tipo/infer.rs | 132 ++++++++++++++-------------- 1 file changed, 65 insertions(+), 67 deletions(-) diff --git a/crates/aiken-lang/src/tipo/infer.rs b/crates/aiken-lang/src/tipo/infer.rs index 7c029681..07e8db42 100644 --- a/crates/aiken-lang/src/tipo/infer.rs +++ b/crates/aiken-lang/src/tipo/infer.rs @@ -262,81 +262,79 @@ fn infer_definition( let temp_params = params.iter().cloned().chain(fun.arguments); fun.arguments = temp_params.collect(); - if let Definition::Fn(mut typed_fun) = infer_definition( + let Definition::Fn(mut typed_fun) = infer_definition( Definition::Fn(fun), module_name, hydrators, environment, tracing, kind, - )? { - if !typed_fun.return_type.is_bool() { - return Err(Error::ValidatorMustReturnBool { - return_type: typed_fun.return_type.clone(), - location: typed_fun.location, - }); - } - - let typed_params = typed_fun.arguments.drain(0..params_length).collect(); - - if typed_fun.arguments.len() < 2 || typed_fun.arguments.len() > 3 { - return Err(Error::IncorrectValidatorArity { - count: typed_fun.arguments.len() as u32, - location: typed_fun.location, - }); - } - - let typed_other_fun = other_fun - .map(|mut other| -> Result { - let params = params.into_iter().chain(other.arguments); - other.arguments = params.collect(); - - if let Definition::Fn(mut other_typed_fun) = infer_definition( - Definition::Fn(other), - module_name, - hydrators, - environment, - tracing, - kind, - )? { - if !other_typed_fun.return_type.is_bool() { - return Err(Error::ValidatorMustReturnBool { - return_type: other_typed_fun.return_type.clone(), - location: other_typed_fun.location, - }); - } - - other_typed_fun.arguments.drain(0..params_length); - - if other_typed_fun.arguments.len() < 2 - || other_typed_fun.arguments.len() > 3 - { - return Err(Error::IncorrectValidatorArity { - count: other_typed_fun.arguments.len() as u32, - location: other_typed_fun.location, - }); - } - - Ok(other_typed_fun) - } else { - unreachable!( - "validator definition inferred as something other than a function?" - ) - } - }) - .transpose(); - - Ok(Definition::Validator(Validator { - doc, - end_position, - fun: typed_fun, - other_fun: typed_other_fun?, - location, - params: typed_params, - })) - } else { + )? else { unreachable!("validator definition inferred as something other than a function?") + }; + + if !typed_fun.return_type.is_bool() { + return Err(Error::ValidatorMustReturnBool { + return_type: typed_fun.return_type.clone(), + location: typed_fun.location, + }); } + + let typed_params = typed_fun.arguments.drain(0..params_length).collect(); + + if typed_fun.arguments.len() < 2 || typed_fun.arguments.len() > 3 { + return Err(Error::IncorrectValidatorArity { + count: typed_fun.arguments.len() as u32, + location: typed_fun.location, + }); + } + + let typed_other_fun = other_fun + .map(|mut other| -> Result { + let params = params.into_iter().chain(other.arguments); + other.arguments = params.collect(); + + let Definition::Fn(mut other_typed_fun) = infer_definition( + Definition::Fn(other), + module_name, + hydrators, + environment, + tracing, + kind, + )? else { + unreachable!( + "validator definition inferred as something other than a function?" + ) + }; + + if !other_typed_fun.return_type.is_bool() { + return Err(Error::ValidatorMustReturnBool { + return_type: other_typed_fun.return_type.clone(), + location: other_typed_fun.location, + }); + } + + other_typed_fun.arguments.drain(0..params_length); + + if other_typed_fun.arguments.len() < 2 || other_typed_fun.arguments.len() > 3 { + return Err(Error::IncorrectValidatorArity { + count: other_typed_fun.arguments.len() as u32, + location: other_typed_fun.location, + }); + } + + Ok(other_typed_fun) + }) + .transpose(); + + Ok(Definition::Validator(Validator { + doc, + end_position, + fun: typed_fun, + other_fun: typed_other_fun?, + location, + params: typed_params, + })) } Definition::Test(f) => { From 74c61358abcbd101ac7b878e4196aa750199b76f Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 17 Mar 2023 18:18:37 -0400 Subject: [PATCH 16/17] feat: new error for when multi-validators have the same arg count --- crates/aiken-lang/src/tipo/error.rs | 11 +++++++++++ crates/aiken-lang/src/tipo/infer.rs | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index 786db0ac..160ebdc6 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -421,6 +421,17 @@ If you really meant to return that last expression, try to replace it with the f name: String, }, + #[error("I found a multi-validator where both take the same number of arguments.\n")] + #[diagnostic(code("illegal::multi_validator"))] + #[diagnostic(help("Multi-validators cannot take the same number of arguments. One must take 3 arguments\nand the other must take 2 arguments. Both of these take {} arguments.", count.to_string().purple()))] + MultiValidatorEqualArgs { + #[label("{} here", count)] + location: Span, + #[label("and {} here", count)] + other_location: Span, + count: usize, + }, + #[error( "I stumbled upon an invalid (non-local) clause guard '{}'.\n", name.if_supports_color(Stdout, |s| s.purple()) diff --git a/crates/aiken-lang/src/tipo/infer.rs b/crates/aiken-lang/src/tipo/infer.rs index 07e8db42..a8e030c0 100644 --- a/crates/aiken-lang/src/tipo/infer.rs +++ b/crates/aiken-lang/src/tipo/infer.rs @@ -323,6 +323,14 @@ fn infer_definition( }); } + if typed_fun.arguments.len() == other_typed_fun.arguments.len() { + return Err(Error::MultiValidatorEqualArgs { + location: typed_fun.location, + other_location: other_typed_fun.location, + count: other_typed_fun.arguments.len(), + }); + } + Ok(other_typed_fun) }) .transpose(); From 2dbc50f098a97f9c9b36e3f7014e340be97378af Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 17 Mar 2023 18:37:05 -0400 Subject: [PATCH 17/17] feat: generate_test doesn't need arguments --- crates/aiken-lang/src/uplc.rs | 12 +++--------- crates/aiken-project/src/lib.rs | 24 ++++++++---------------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 1a6db0a6..490593f2 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -20,8 +20,8 @@ use uplc::{ use crate::{ air::Air, ast::{ - ArgName, AssignmentKind, BinOp, Pattern, Span, TypedArg, TypedClause, TypedDataType, - TypedFunction, TypedValidator, UnOp, + ArgName, AssignmentKind, BinOp, Pattern, Span, TypedClause, TypedDataType, TypedFunction, + TypedValidator, UnOp, }, builder::{ check_replaceable_opaque_type, check_when_pattern_needs, constants_ir, @@ -145,11 +145,7 @@ impl<'a> CodeGenerator<'a> { self.finalize(term, true) } - pub fn generate_test( - &mut self, - test_body: &TypedExpr, - arguments: &[TypedArg], - ) -> Program { + pub fn generate_test(&mut self, test_body: &TypedExpr) -> Program { let mut ir_stack = vec![]; let scope = vec![self.id_gen.next()]; @@ -167,8 +163,6 @@ impl<'a> CodeGenerator<'a> { term = builder::constr_fields_exposer(term); } - term = wrap_validator_args(term, arguments); - self.finalize(term, false) } diff --git a/crates/aiken-project/src/lib.rs b/crates/aiken-project/src/lib.rs index 498a7d07..59bc1a9e 100644 --- a/crates/aiken-project/src/lib.rs +++ b/crates/aiken-project/src/lib.rs @@ -675,12 +675,7 @@ where let mut programs = Vec::new(); for (input_path, module_name, func_def) in scripts { - let Function { - arguments, - name, - body, - .. - } = func_def; + let Function { name, body, .. } = func_def; if verbose { self.event_listener.handle_event(Event::GeneratingUPLCFor { @@ -695,30 +690,27 @@ where &self.module_types, ); - let evaluation_hint = if let Some((bin_op, left_src, right_src)) = func_def.test_hint() - { + let evaluation_hint = func_def.test_hint().map(|(bin_op, left_src, right_src)| { let left = generator .clone() - .generate_test(&left_src, &[]) + .generate_test(&left_src) .try_into() .unwrap(); let right = generator .clone() - .generate_test(&right_src, &[]) + .generate_test(&right_src) .try_into() .unwrap(); - Some(EvalHint { + EvalHint { bin_op, left, right, - }) - } else { - None - }; + } + }); - let program = generator.generate_test(body, arguments); + let program = generator.generate_test(body); let script = Script::new( input_path,