Merge pull request #977 from aiken-lang/rvcas/validators_v3

Implement PlutusV3 Validators
This commit is contained in:
Matthias Benkort 2024-08-27 20:31:25 +02:00 committed by GitHub
commit 9943c2cc7a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
174 changed files with 4680 additions and 3789 deletions

25
Cargo.lock generated vendored
View File

@ -1948,8 +1948,9 @@ checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f"
[[package]]
name = "pallas-addresses"
version = "0.29.0"
source = "git+https://github.com/KtorZ/pallas.git?rev=454b3bcf32cdc238aba65e4c3e4656a352480ec1#454b3bcf32cdc238aba65e4c3e4656a352480ec1"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c38fac39e0da3b0fc4c859635c72e97584f01f3a0f4f1508b0851c02d6d52f15"
dependencies = [
"base58",
"bech32",
@ -1963,8 +1964,9 @@ dependencies = [
[[package]]
name = "pallas-codec"
version = "0.29.0"
source = "git+https://github.com/KtorZ/pallas.git?rev=454b3bcf32cdc238aba65e4c3e4656a352480ec1#454b3bcf32cdc238aba65e4c3e4656a352480ec1"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea8a4b87dbc8bcb8aeb865f7cca5e1eb29744330e23b307169fc30537648b264"
dependencies = [
"hex",
"minicbor",
@ -1975,8 +1977,9 @@ dependencies = [
[[package]]
name = "pallas-crypto"
version = "0.29.0"
source = "git+https://github.com/KtorZ/pallas.git?rev=454b3bcf32cdc238aba65e4c3e4656a352480ec1#454b3bcf32cdc238aba65e4c3e4656a352480ec1"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b98c3f204299d47d9b581ab425043789caff1f491c078ee3d3f109d6556f725"
dependencies = [
"cryptoxide",
"hex",
@ -1988,8 +1991,9 @@ dependencies = [
[[package]]
name = "pallas-primitives"
version = "0.29.0"
source = "git+https://github.com/KtorZ/pallas.git?rev=454b3bcf32cdc238aba65e4c3e4656a352480ec1#454b3bcf32cdc238aba65e4c3e4656a352480ec1"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f64835dd9cbdd75a38961a190b983f02746c872340daf1a921eada8c525a4b6"
dependencies = [
"base58",
"bech32",
@ -2003,8 +2007,9 @@ dependencies = [
[[package]]
name = "pallas-traverse"
version = "0.29.0"
source = "git+https://github.com/KtorZ/pallas.git?rev=454b3bcf32cdc238aba65e4c3e4656a352480ec1#454b3bcf32cdc238aba65e4c3e4656a352480ec1"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad516b05ba7d838ee84f9998d7b2b4ff7acc178cb052bcfd5fea9edc2ef6023f"
dependencies = [
"hex",
"itertools 0.13.0",

View File

@ -49,11 +49,11 @@ x86_64-unknown-linux-gnu = "ubuntu-22.04"
walkdir = "2.3.2"
insta = { version = "1.30.0", features = ["yaml", "json", "redactions"] }
miette = { version = "7.2.0", features = ["fancy"] }
pallas-addresses = { git = "https://github.com/KtorZ/pallas.git", rev = "454b3bcf32cdc238aba65e4c3e4656a352480ec1" }
pallas-codec = { git = "https://github.com/KtorZ/pallas.git", rev = "454b3bcf32cdc238aba65e4c3e4656a352480ec1", features = ["num-bigint"] }
pallas-crypto = { git = "https://github.com/KtorZ/pallas.git", rev = "454b3bcf32cdc238aba65e4c3e4656a352480ec1" }
pallas-primitives = { git = "https://github.com/KtorZ/pallas.git", rev = "454b3bcf32cdc238aba65e4c3e4656a352480ec1" }
pallas-traverse = { git = "https://github.com/KtorZ/pallas.git", rev = "454b3bcf32cdc238aba65e4c3e4656a352480ec1" }
pallas-addresses = "0.30.1"
pallas-codec = { version = "0.30.1", features = ["num-bigint"] }
pallas-crypto = "0.30.1"
pallas-primitives = "0.30.1"
pallas-traverse = "0.30.1"
[profile.dev.package.insta]
opt-level = 3

View File

@ -1,5 +1,7 @@
pub mod well_known;
use crate::{
builtins::{self, g1_element, g2_element},
ast::well_known::VALIDATOR_ELSE,
expr::{TypedExpr, UntypedExpr},
line_numbers::LineNumbers,
parser::token::{Base, Token},
@ -25,6 +27,13 @@ pub const ENV_MODULE: &str = "env";
pub const CONFIG_MODULE: &str = "config";
pub const DEFAULT_ENV_MODULE: &str = "default";
pub const HANDLER_SPEND: &str = "spend";
pub const HANDLER_MINT: &str = "mint";
pub const HANDLER_WITHDRAW: &str = "withdraw";
pub const HANDLER_PUBLISH: &str = "publish";
pub const HANDLER_VOTE: &str = "vote";
pub const HANDLER_PROPOSE: &str = "propose";
pub type TypedModule = Module<TypeInfo, TypedDefinition>;
pub type UntypedModule = Module<(), UntypedDefinition>;
@ -189,11 +198,7 @@ impl TypedModule {
Definition::Validator(v) => {
let module_name = self.name.as_str();
if let Some((k, v)) = v.into_function_definition(module_name, |f, _| Some(f)) {
functions.insert(k, v);
}
if let Some((k, v)) = v.into_function_definition(module_name, |_, f| f) {
for (k, v) in v.into_function_definitions(module_name) {
functions.insert(k, v);
}
}
@ -237,6 +242,19 @@ fn str_to_keyword(word: &str) -> Option<Token> {
pub type TypedFunction = Function<Rc<Type>, TypedExpr, TypedArg>;
pub type UntypedFunction = Function<(), UntypedExpr, UntypedArg>;
impl UntypedFunction {
pub fn is_default_fallback(&self) -> bool {
matches!(
&self.arguments[..],
[UntypedArg {
by: ArgBy::ByName(ArgName::Discarded { .. }),
..
}]
) && matches!(&self.body, UntypedExpr::ErrorTerm { .. })
&& self.name.as_str() == well_known::VALIDATOR_ELSE
}
}
pub type TypedTest = Function<Rc<Type>, TypedExpr, TypedArgVia>;
pub type UntypedTest = Function<(), UntypedExpr, UntypedArgVia>;
@ -261,6 +279,16 @@ pub struct Function<T, Expr, Arg> {
pub on_test_failure: OnTestFailure,
}
impl<T, Expr, Arg> Function<T, Expr, Arg> {
pub fn is_spend(&self) -> bool {
self.name == HANDLER_SPEND
}
pub fn is_mint(&self) -> bool {
self.name == HANDLER_MINT
}
}
impl TypedFunction {
pub fn find_node(&self, byte_index: usize) -> Option<Located<'_>> {
self.arguments
@ -273,6 +301,33 @@ impl TypedFunction {
.and_then(|a| a.find_node(byte_index))
})
}
pub fn has_valid_purpose_name(&self) -> bool {
self.name == HANDLER_SPEND
|| self.name == HANDLER_PUBLISH
|| self.name == HANDLER_PROPOSE
|| self.name == HANDLER_MINT
|| self.name == HANDLER_WITHDRAW
|| self.name == HANDLER_VOTE
}
pub fn validator_arity(&self) -> usize {
if self.name == HANDLER_SPEND {
4
} else if self.name == HANDLER_MINT
|| self.name == HANDLER_WITHDRAW
|| self.name == HANDLER_VOTE
|| self.name == HANDLER_PUBLISH
|| self.name == HANDLER_PROPOSE
{
3
} else {
panic!(
"tried to get validator arity of a non-validator function {}",
&self.name
);
}
}
}
impl TypedTest {
@ -349,153 +404,37 @@ pub struct FunctionAccessKey {
pub function_name: String,
}
pub type UntypedDataType = DataType<()>;
pub type TypedDataType = DataType<Rc<Type>>;
impl TypedDataType {
pub fn data() -> Self {
DataType {
constructors: vec![],
doc: None,
pub fn known_enum(name: &str, constructors: &[&str]) -> Self {
Self {
name: name.to_string(),
constructors: RecordConstructor::known_enum(constructors),
location: Span::empty(),
name: "Data".to_string(),
opaque: false,
parameters: vec![],
public: true,
parameters: vec![],
typed_parameters: vec![],
doc: None,
}
}
pub fn bool() -> Self {
DataType {
constructors: vec![
RecordConstructor {
location: Span::empty(),
name: "False".to_string(),
arguments: vec![],
doc: None,
sugar: false,
},
RecordConstructor {
location: Span::empty(),
name: "True".to_string(),
arguments: vec![],
doc: None,
sugar: false,
},
],
doc: None,
location: Span::empty(),
name: "Bool".to_string(),
opaque: false,
parameters: vec![],
public: true,
typed_parameters: vec![],
}
}
pub fn prng() -> Self {
DataType {
constructors: vec![
RecordConstructor {
location: Span::empty(),
name: "Seeded".to_string(),
arguments: vec![],
doc: None,
sugar: false,
},
RecordConstructor {
location: Span::empty(),
name: "Replayed".to_string(),
arguments: vec![],
doc: None,
sugar: false,
},
],
doc: None,
location: Span::empty(),
name: "PRNG".to_string(),
opaque: false,
parameters: vec![],
public: true,
typed_parameters: vec![],
}
}
pub fn ordering() -> Self {
DataType {
constructors: vec![
RecordConstructor {
location: Span::empty(),
name: "Less".to_string(),
arguments: vec![],
doc: None,
sugar: false,
},
RecordConstructor {
location: Span::empty(),
name: "Equal".to_string(),
arguments: vec![],
doc: None,
sugar: false,
},
RecordConstructor {
location: Span::empty(),
name: "Greater".to_string(),
arguments: vec![],
doc: None,
sugar: false,
},
],
doc: None,
location: Span::empty(),
name: "Ordering".to_string(),
opaque: false,
parameters: vec![],
public: true,
typed_parameters: vec![],
}
}
pub fn option(tipo: Rc<Type>) -> Self {
DataType {
constructors: vec![
RecordConstructor {
location: Span::empty(),
name: "Some".to_string(),
arguments: vec![RecordConstructorArg {
label: None,
annotation: Annotation::Var {
location: Span::empty(),
name: "a".to_string(),
},
location: Span::empty(),
tipo: tipo.clone(),
doc: None,
}],
doc: None,
sugar: false,
},
RecordConstructor {
location: Span::empty(),
name: "None".to_string(),
arguments: vec![],
doc: None,
sugar: false,
},
],
doc: None,
location: Span::empty(),
name: "Option".to_string(),
opaque: false,
parameters: vec!["a".to_string()],
public: true,
typed_parameters: vec![tipo],
}
pub fn is_never(&self) -> bool {
self.name == well_known::NEVER
&& self.constructors.len() == well_known::NEVER_CONSTRUCTORS.len()
&& self.location == Span::empty()
&& self
.constructors
.iter()
.zip(well_known::NEVER_CONSTRUCTORS)
.all(|(constructor, name)| {
name == &constructor.name && constructor.arguments.is_empty()
})
}
}
pub type UntypedDataType = DataType<()>;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct DataType<T> {
pub constructors: Vec<RecordConstructor<T>>,
@ -537,58 +476,260 @@ pub struct ModuleConstant<T> {
pub type TypedValidator = Validator<Rc<Type>, TypedArg, TypedExpr>;
pub type UntypedValidator = Validator<(), UntypedArg, UntypedExpr>;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Purpose {
Spend,
Mint,
Withdraw,
Publish,
Propose,
Vote,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Validator<T, Arg, Expr> {
pub doc: Option<String>,
pub end_position: usize,
pub fun: Function<T, Expr, Arg>,
pub other_fun: Option<Function<T, Expr, Arg>>,
pub handlers: Vec<Function<T, Expr, Arg>>,
pub location: Span,
pub name: String,
pub params: Vec<Arg>,
pub fallback: Function<T, Expr, Arg>,
}
impl<T, Arg, Expr> Validator<T, Arg, Expr> {
pub fn handler_name(validator: &str, handler: &str) -> String {
format!("{}.{}", validator, handler)
}
}
impl UntypedValidator {
pub fn default_fallback(location: Span) -> UntypedFunction {
Function {
arguments: vec![UntypedArg {
by: ArgBy::ByName(ArgName::Discarded {
name: "_".to_string(),
label: "_".to_string(),
location,
}),
location,
annotation: None,
doc: None,
is_validator_param: false,
}],
body: UntypedExpr::fail(None, location),
doc: None,
location,
end_position: location.end - 1,
name: well_known::VALIDATOR_ELSE.to_string(),
public: true,
return_annotation: Some(Annotation::boolean(location)),
return_type: (),
on_test_failure: OnTestFailure::FailImmediately,
}
}
}
impl TypedValidator {
pub fn available_handler_names() -> Vec<String> {
vec![
HANDLER_SPEND.to_string(),
HANDLER_MINT.to_string(),
HANDLER_WITHDRAW.to_string(),
HANDLER_PUBLISH.to_string(),
HANDLER_VOTE.to_string(),
HANDLER_PROPOSE.to_string(),
VALIDATOR_ELSE.to_string(),
]
}
// Define a validator wrapper extracting and matching on script purpose for
// users.
pub fn into_script_context_handler(&self) -> TypedExpr {
let var_context = "__context__";
let var_transaction = "__transaction__";
let var_redeemer = "__redeemer__";
let var_purpose = "__purpose__";
let var_purpose_arg = "__purpose_arg__";
let var_datum = "__datum__";
TypedExpr::sequence(&[
TypedExpr::let_(
TypedExpr::local_var(var_context, Type::script_context()),
TypedPattern::Constructor {
is_record: false,
location: Span::empty(),
name: well_known::SCRIPT_CONTEXT_CONSTRUCTORS[0].to_string(),
arguments: vec![
CallArg::var(var_transaction),
CallArg::var(var_redeemer),
CallArg::var(var_purpose),
],
module: None,
constructor: PatternConstructor::Record {
name: well_known::SCRIPT_CONTEXT_CONSTRUCTORS[0].to_string(),
field_map: None,
},
spread_location: None,
tipo: Type::function(
vec![Type::data(), Type::data(), Type::script_purpose()],
Type::data(),
),
},
Type::script_context(),
),
TypedExpr::When {
location: Span::empty(),
tipo: Type::bool(),
subject: TypedExpr::local_var(var_purpose, Type::script_purpose()).into(),
clauses: self
.handlers
.iter()
.map(|handler| {
let datum = if handler.name.as_str() == "spend" {
handler.arguments.first()
} else {
None
};
let redeemer = handler
.arguments
.get(if datum.is_some() { 1 } else { 0 })
.unwrap();
let purpose_arg = handler.arguments.iter().nth_back(1).unwrap();
let transaction = handler.arguments.last().unwrap();
let pattern = match handler.name.as_str() {
"spend" => TypedPattern::spend_purpose(var_purpose_arg, var_datum),
"mint" => TypedPattern::mint_purpose(var_purpose_arg),
"withdraw" => TypedPattern::withdraw_purpose(var_purpose_arg),
"publish" => TypedPattern::publish_purpose(var_purpose_arg),
"propose" => TypedPattern::propose_purpose(var_purpose_arg),
"vote" => TypedPattern::vote_purpose(var_purpose_arg),
purpose => {
unreachable!("unexpected/unknown purpose: {:?}", purpose)
}
};
let mut then = vec![];
// expect redeemer: tipo = __redeemer__
then.push(TypedExpr::flexible_expect(
TypedExpr::local_var(var_redeemer, Type::data()),
TypedPattern::var(redeemer.get_variable_name().unwrap_or("_")),
redeemer.tipo.clone(),
));
// Cast the datum, if any
if let Some(datum) = datum {
// expect datum: tipo = __datum__
then.push(TypedExpr::flexible_expect(
TypedExpr::local_var(var_datum, Type::option(Type::data())),
TypedPattern::var(datum.get_variable_name().unwrap_or("_")),
datum.tipo.clone(),
))
}
// let purpose_arg = __purpose_arg__
if let Some(arg_name) = purpose_arg.get_variable_name() {
then.push(TypedExpr::let_(
TypedExpr::local_var(var_purpose_arg, Type::data()),
TypedPattern::var(arg_name),
purpose_arg.tipo.clone(),
));
}
// let last_arg_name = __transaction__
if let Some(arg_name) = transaction.get_variable_name() {
then.push(TypedExpr::let_(
TypedExpr::local_var(var_transaction, Type::data()),
TypedPattern::var(arg_name),
Type::data(),
));
}
then.push(handler.body.clone());
TypedClause {
location: Span::empty(),
pattern,
then: TypedExpr::Sequence {
location: Span::empty(),
expressions: then,
},
}
})
.chain(std::iter::once(&self.fallback).map(|fallback| {
let arg = fallback.arguments.first().unwrap();
let then = &[
TypedExpr::let_(
TypedExpr::local_var(var_context, arg.tipo.clone()),
arg.get_variable_name().map(TypedPattern::var).unwrap_or(
TypedPattern::Discard {
name: var_context.to_string(),
location: Span::empty(),
},
),
arg.tipo.clone(),
),
fallback.body.clone(),
];
TypedClause {
location: Span::empty(),
pattern: TypedPattern::var(var_context),
then: TypedExpr::sequence(then),
}
}))
.collect(),
},
])
}
pub fn find_node(&self, byte_index: usize) -> Option<Located<'_>> {
self.params
.iter()
.find_map(|arg| arg.find_node(byte_index))
.or_else(|| self.fun.find_node(byte_index))
.or_else(|| {
self.other_fun
.as_ref()
.and_then(|f| f.find_node(byte_index))
self.handlers
.iter()
.find_map(|func| func.find_node(byte_index))
})
.or_else(|| self.fallback.find_node(byte_index))
}
pub fn into_function_definition<'a, F>(
&'a self,
pub fn into_function_definitions(
&self,
module_name: &str,
select: F,
) -> Option<(FunctionAccessKey, TypedFunction)>
where
F: Fn(&'a TypedFunction, Option<&'a TypedFunction>) -> Option<&'a TypedFunction> + 'a,
{
match select(&self.fun, self.other_fun.as_ref()) {
None => None,
Some(fun) => {
let mut fun = fun.clone();
) -> Vec<(FunctionAccessKey, TypedFunction)> {
self.handlers
.iter()
.chain(std::iter::once(&self.fallback))
.map(|handler| {
let mut handler = handler.clone();
fun.arguments = self
handler.arguments = self
.params
.clone()
.into_iter()
.chain(fun.arguments)
.chain(handler.arguments)
.collect();
Some((
(
FunctionAccessKey {
module_name: module_name.to_string(),
function_name: fun.name.clone(),
function_name: TypedValidator::handler_name(
self.name.as_str(),
handler.name.as_str(),
),
},
fun,
))
}
}
handler,
)
})
.collect()
}
}
@ -731,12 +872,12 @@ pub enum Constant {
impl Constant {
pub fn tipo(&self) -> Rc<Type> {
match self {
Constant::Int { .. } => builtins::int(),
Constant::String { .. } => builtins::string(),
Constant::ByteArray { .. } => builtins::byte_array(),
Constant::Int { .. } => Type::int(),
Constant::String { .. } => Type::string(),
Constant::ByteArray { .. } => Type::byte_array(),
Constant::CurvePoint { point, .. } => match point.as_ref() {
Curve::Bls12_381(Bls12_381Point::G1(_)) => builtins::g1_element(),
Curve::Bls12_381(Bls12_381Point::G2(_)) => builtins::g2_element(),
Curve::Bls12_381(Bls12_381Point::G1(_)) => Type::g1_element(),
Curve::Bls12_381(Bls12_381Point::G2(_)) => Type::g2_element(),
},
}
}
@ -776,6 +917,19 @@ impl TypedCallArg {
}
}
impl CallArg<TypedPattern> {
pub fn var(name: &str) -> Self {
CallArg {
label: None,
location: Span::empty(),
value: TypedPattern::Var {
location: Span::empty(),
name: name.to_string(),
},
}
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct RecordConstructor<T> {
pub location: Span,
@ -789,6 +943,19 @@ impl<A> RecordConstructor<A> {
pub fn put_doc(&mut self, new_doc: String) {
self.doc = Some(new_doc);
}
pub fn known_enum(names: &[&str]) -> Vec<RecordConstructor<A>> {
names
.iter()
.map(|name| RecordConstructor {
location: Span::empty(),
name: name.to_string(),
arguments: vec![],
doc: None,
sugar: false,
})
.collect()
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
@ -889,6 +1056,21 @@ pub struct TypedArg {
}
impl TypedArg {
pub fn new(name: &str, tipo: Rc<Type>) -> Self {
TypedArg {
arg_name: ArgName::Named {
name: name.to_string(),
label: name.to_string(),
location: Span::empty(),
},
location: Span::empty(),
annotation: None,
doc: None,
is_validator_param: false,
tipo: tipo.clone(),
}
}
pub fn put_doc(&mut self, new_doc: String) {
self.doc = Some(new_doc);
}
@ -897,6 +1079,10 @@ impl TypedArg {
self.arg_name.get_variable_name()
}
pub fn get_name(&self) -> String {
self.arg_name.get_name()
}
pub fn is_capture(&self) -> bool {
if let ArgName::Named {
ref name, location, ..
@ -1103,6 +1289,15 @@ impl Annotation {
}
}
pub fn option(inner: Annotation) -> Self {
Annotation::Constructor {
name: "Option".to_string(),
module: None,
location: inner.location(),
arguments: vec![inner],
}
}
pub fn is_logically_equal(&self, other: &Annotation) -> bool {
match self {
Annotation::Constructor {
@ -1279,6 +1474,88 @@ impl BinOp {
pub type UntypedPattern = Pattern<(), ()>;
pub type TypedPattern = Pattern<PatternConstructor, Rc<Type>>;
impl TypedPattern {
pub fn var(name: &str) -> Self {
TypedPattern::Var {
name: name.to_string(),
location: Span::empty(),
}
}
pub fn constructor(name: &str, arguments: &[CallArg<TypedPattern>], tipo: Rc<Type>) -> Self {
TypedPattern::Constructor {
is_record: false,
location: Span::empty(),
name: name.to_string(),
arguments: arguments.to_vec(),
module: None,
constructor: PatternConstructor::Record {
name: name.to_string(),
field_map: None,
},
spread_location: None,
tipo: tipo.clone(),
}
}
pub fn mint_purpose(var_purpose_arg: &str) -> Self {
TypedPattern::constructor(
well_known::SCRIPT_PURPOSE_MINT,
&[CallArg::var(var_purpose_arg)],
Type::function(vec![Type::byte_array()], Type::script_purpose()),
)
}
pub fn spend_purpose(var_purpose_arg: &str, var_datum: &str) -> Self {
TypedPattern::constructor(
well_known::SCRIPT_PURPOSE_SPEND,
&[CallArg::var(var_purpose_arg), CallArg::var(var_datum)],
Type::function(
vec![Type::data(), Type::option(Type::data())],
Type::script_purpose(),
),
)
}
pub fn withdraw_purpose(var_purpose_arg: &str) -> Self {
TypedPattern::constructor(
well_known::SCRIPT_PURPOSE_WITHDRAW,
&[CallArg::var(var_purpose_arg)],
Type::function(vec![Type::data()], Type::script_purpose()),
)
}
pub fn publish_purpose(var_purpose_arg: &str) -> Self {
TypedPattern::constructor(
well_known::SCRIPT_PURPOSE_PUBLISH,
&[
CallArg::var("__discarded_purpose_ix__"),
CallArg::var(var_purpose_arg),
],
Type::function(vec![Type::int(), Type::data()], Type::script_purpose()),
)
}
pub fn vote_purpose(var_purpose_arg: &str) -> Self {
TypedPattern::constructor(
well_known::SCRIPT_PURPOSE_VOTE,
&[CallArg::var(var_purpose_arg)],
Type::function(vec![Type::data()], Type::script_purpose()),
)
}
pub fn propose_purpose(var_purpose_arg: &str) -> Self {
TypedPattern::constructor(
well_known::SCRIPT_PURPOSE_PROPOSE,
&[
CallArg::var("__discarded_purpose_ix__"),
CallArg::var(var_purpose_arg),
],
Type::function(vec![Type::int(), Type::data()], Type::script_purpose()),
)
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Pattern<Constructor, Type> {
Int {
@ -1490,8 +1767,8 @@ impl TypedPattern {
// TODO: This function definition is weird, see where this is used and how.
pub fn tipo(&self, value: &TypedExpr) -> Option<Rc<Type>> {
match self {
Pattern::Int { .. } => Some(builtins::int()),
Pattern::ByteArray { .. } => Some(builtins::byte_array()),
Pattern::Int { .. } => Some(Type::int()),
Pattern::ByteArray { .. } => Some(Type::byte_array()),
Pattern::Constructor { tipo, .. } => Some(tipo.clone()),
Pattern::Var { .. } | Pattern::Assign { .. } | Pattern::Discard { .. } => {
Some(value.tipo())
@ -1689,8 +1966,8 @@ impl<'de> serde::Deserialize<'de> for Bls12_381Point {
impl Bls12_381Point {
pub fn tipo(&self) -> Rc<Type> {
match self {
Bls12_381Point::G1(_) => g1_element(),
Bls12_381Point::G2(_) => g2_element(),
Bls12_381Point::G1(_) => Type::g1_element(),
Bls12_381Point::G2(_) => Type::g2_element(),
}
}
}

View File

@ -0,0 +1,352 @@
use crate::{
ast::{Annotation, Span},
tipo::{Type, TypeAliasAnnotation, TypeVar},
};
use std::{cell::RefCell, rc::Rc};
pub const BOOL: &str = "Bool";
pub const BOOL_CONSTRUCTORS: &[&str] = &["False", "True"];
pub const BYTE_ARRAY: &str = "ByteArray";
pub const DATA: &str = "Data";
pub const FUZZER: &str = "Fuzzer";
pub const G1_ELEMENT: &str = "G1Element";
pub const G2_ELEMENT: &str = "G2Element";
pub const INT: &str = "Int";
pub const LIST: &str = "List";
pub const MILLER_LOOP_RESULT: &str = "MillerLoopResult";
pub const OPTION: &str = "Option";
pub const OPTION_CONSTRUCTORS: &[&str] = &["Some", "None"];
pub const NEVER: &str = "Never";
pub const NEVER_CONSTRUCTORS: &[&str] = &["__hole", "Never"];
pub const ORDERING: &str = "Ordering";
pub const ORDERING_CONSTRUCTORS: &[&str] = &["Less", "Equal", "Greater"];
pub const PAIR: &str = "Pair";
pub const PAIRS: &str = "Pairs";
pub const PRNG: &str = "PRNG";
pub const PRNG_CONSTRUCTORS: &[&str] = &["Seeded", "Replayed"];
pub const REDEEMER_WRAPPER: &str = "RedeemerWrapper";
pub const STRING: &str = "String";
pub const VOID: &str = "Void";
pub const VOID_CONSTRUCTORS: &[&str] = &["Void"];
pub const SCRIPT_CONTEXT: &str = "__ScriptContext";
pub const SCRIPT_CONTEXT_CONSTRUCTORS: &[&str] = &["__ScriptContext"];
pub const SCRIPT_CONTEXT_TRANSACTION: &str = "__Transaction";
pub const SCRIPT_CONTEXT_REDEEMER: &str = "__Redeemer";
pub const SCRIPT_CONTEXT_PURPOSE: &str = "__ScriptPurpose";
pub const SCRIPT_PURPOSE: &str = "__ScriptPurpose";
pub const SCRIPT_PURPOSE_MINT: &str = "__Mint";
pub const SCRIPT_PURPOSE_SPEND: &str = "__Spend";
pub const SCRIPT_PURPOSE_WITHDRAW: &str = "__Withdraw";
pub const SCRIPT_PURPOSE_PUBLISH: &str = "__Publish";
pub const SCRIPT_PURPOSE_VOTE: &str = "__Vote";
pub const SCRIPT_PURPOSE_PROPOSE: &str = "__Propose";
pub const SCRIPT_PURPOSE_CONSTRUCTORS: &[&str] = &[
SCRIPT_PURPOSE_MINT,
SCRIPT_PURPOSE_SPEND,
SCRIPT_PURPOSE_WITHDRAW,
SCRIPT_PURPOSE_PUBLISH,
SCRIPT_PURPOSE_VOTE,
SCRIPT_PURPOSE_PROPOSE,
];
pub const VALIDATOR_ELSE: &str = "else";
// ----------------------------------------------------------------------------
// Types
impl Type {
pub fn data() -> Rc<Type> {
Rc::new(Type::App {
public: true,
contains_opaque: false,
name: DATA.to_string(),
module: "".to_string(),
args: vec![],
alias: None,
})
}
pub fn int() -> Rc<Type> {
Rc::new(Type::App {
public: true,
contains_opaque: false,
name: INT.to_string(),
module: "".to_string(),
args: vec![],
alias: None,
})
}
pub fn bool() -> Rc<Self> {
Rc::new(Type::App {
args: vec![],
public: true,
contains_opaque: false,
name: BOOL.to_string(),
module: "".to_string(),
alias: None,
})
}
pub fn byte_array() -> Rc<Type> {
Rc::new(Type::App {
args: vec![],
public: true,
contains_opaque: false,
name: BYTE_ARRAY.to_string(),
module: "".to_string(),
alias: None,
})
}
pub fn g1_element() -> Rc<Type> {
Rc::new(Type::App {
public: true,
contains_opaque: false,
module: "".to_string(),
name: G1_ELEMENT.to_string(),
args: vec![],
alias: None,
})
}
pub fn g2_element() -> Rc<Type> {
Rc::new(Type::App {
public: true,
contains_opaque: false,
module: "".to_string(),
name: G2_ELEMENT.to_string(),
args: vec![],
alias: None,
})
}
pub fn miller_loop_result() -> Rc<Type> {
Rc::new(Type::App {
public: true,
contains_opaque: false,
module: "".to_string(),
name: MILLER_LOOP_RESULT.to_string(),
args: vec![],
alias: None,
})
}
pub fn tuple(elems: Vec<Rc<Type>>) -> Rc<Type> {
Rc::new(Type::Tuple { elems, alias: None })
}
pub fn pair(fst: Rc<Type>, snd: Rc<Type>) -> Rc<Type> {
Rc::new(Type::Pair {
fst,
snd,
alias: None,
})
}
pub fn script_purpose() -> Rc<Type> {
Rc::new(Type::App {
args: vec![],
public: true,
contains_opaque: false,
name: SCRIPT_PURPOSE.to_string(),
module: "".to_string(),
alias: None,
})
}
pub fn script_context() -> Rc<Type> {
Rc::new(Type::App {
args: vec![],
public: true,
contains_opaque: false,
name: SCRIPT_CONTEXT.to_string(),
module: "".to_string(),
alias: None,
})
}
pub fn prng() -> Rc<Type> {
Rc::new(Type::App {
args: vec![],
public: true,
contains_opaque: false,
name: PRNG.to_string(),
module: "".to_string(),
alias: None,
})
}
pub fn fuzzer(a: Rc<Type>) -> Rc<Type> {
let prng_annotation = Annotation::Constructor {
location: Span::empty(),
module: None,
name: PRNG.to_string(),
arguments: vec![],
};
Rc::new(Type::Fn {
args: vec![Type::prng()],
ret: Type::option(Type::tuple(vec![Type::prng(), a])),
alias: Some(
TypeAliasAnnotation {
alias: FUZZER.to_string(),
parameters: vec!["a".to_string()],
annotation: Annotation::Fn {
location: Span::empty(),
arguments: vec![prng_annotation.clone()],
ret: Annotation::Constructor {
location: Span::empty(),
module: None,
name: OPTION.to_string(),
arguments: vec![Annotation::Tuple {
location: Span::empty(),
elems: vec![
prng_annotation,
Annotation::Var {
location: Span::empty(),
name: "a".to_string(),
},
],
}],
}
.into(),
},
}
.into(),
),
})
}
pub fn map(k: Rc<Type>, v: Rc<Type>) -> Rc<Type> {
Rc::new(Type::App {
public: true,
contains_opaque: false,
module: "".to_string(),
name: LIST.to_string(),
args: vec![Type::pair(k, v)],
alias: Some(
TypeAliasAnnotation {
alias: PAIRS.to_string(),
parameters: vec!["k".to_string(), "v".to_string()],
annotation: Annotation::Constructor {
location: Span::empty(),
module: None,
name: LIST.to_string(),
arguments: vec![Annotation::Pair {
location: Span::empty(),
fst: Box::new(Annotation::Var {
location: Span::empty(),
name: "k".to_string(),
}),
snd: Box::new(Annotation::Var {
location: Span::empty(),
name: "v".to_string(),
}),
}],
},
}
.into(),
),
})
}
pub fn list(t: Rc<Type>) -> Rc<Type> {
Rc::new(Type::App {
public: true,
contains_opaque: t.contains_opaque(),
name: LIST.to_string(),
module: "".to_string(),
args: vec![t],
alias: None,
})
}
pub fn string() -> Rc<Type> {
Rc::new(Type::App {
args: vec![],
public: true,
contains_opaque: false,
name: STRING.to_string(),
module: "".to_string(),
alias: None,
})
}
pub fn void() -> Rc<Type> {
Rc::new(Type::App {
args: vec![],
public: true,
contains_opaque: false,
name: VOID.to_string(),
module: "".to_string(),
alias: None,
})
}
pub fn option(a: Rc<Type>) -> Rc<Type> {
Rc::new(Type::App {
public: true,
contains_opaque: a.contains_opaque(),
name: OPTION.to_string(),
module: "".to_string(),
args: vec![a],
alias: None,
})
}
pub fn never() -> Rc<Type> {
Rc::new(Type::App {
public: true,
contains_opaque: false,
name: NEVER.to_string(),
module: "".to_string(),
args: vec![],
alias: None,
})
}
pub fn ordering() -> Rc<Type> {
Rc::new(Type::App {
public: true,
contains_opaque: false,
name: ORDERING.to_string(),
module: "".to_string(),
args: vec![],
alias: None,
})
}
pub fn function(args: Vec<Rc<Type>>, ret: Rc<Type>) -> Rc<Type> {
Rc::new(Type::Fn {
ret,
args,
alias: None,
})
}
pub fn generic_var(id: u64) -> Rc<Type> {
let tipo = Rc::new(RefCell::new(TypeVar::Generic { id }));
Rc::new(Type::Var { tipo, alias: None })
}
pub fn unbound_var(id: u64) -> Rc<Type> {
let tipo = Rc::new(RefCell::new(TypeVar::Unbound { id }));
Rc::new(Type::Var { tipo, alias: None })
}
pub fn wrapped_redeemer(redeemer: Rc<Type>) -> Rc<Type> {
Rc::new(Type::App {
public: true,
contains_opaque: false,
module: "".to_string(),
name: REDEEMER_WRAPPER.to_string(),
args: vec![redeemer],
alias: None,
})
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,17 @@
use crate::tipo::ValueConstructorVariant;
pub(crate) use crate::{
ast::{
self, Annotation, ArgBy, ArgName, AssignmentPattern, BinOp, Bls12_381Point,
self, Annotation, ArgBy, ArgName, AssignmentKind, AssignmentPattern, BinOp, Bls12_381Point,
ByteArrayFormatPreference, CallArg, Curve, DataType, DataTypeKey, DefinitionLocation,
Located, LogicalOpChainKind, ParsedCallArg, Pattern, RecordConstructorArg,
RecordUpdateSpread, Span, TraceKind, TypedArg, TypedAssignmentKind, TypedClause,
TypedDataType, TypedIfBranch, TypedRecordUpdateArg, UnOp, UntypedArg,
TypedDataType, TypedIfBranch, TypedPattern, TypedRecordUpdateArg, UnOp, UntypedArg,
UntypedAssignmentKind, UntypedClause, UntypedIfBranch, UntypedRecordUpdateArg,
},
builtins::void,
parser::token::Base,
tipo::{
check_replaceable_opaque_type, convert_opaque_type, lookup_data_type_by_tipo,
ModuleValueConstructor, PatternConstructor, Type, TypeVar, ValueConstructor,
ValueConstructorVariant,
},
};
use indexmap::IndexMap;
@ -201,6 +200,52 @@ impl<T> From<Vec1Ref<T>> for Vec1<T> {
}
impl TypedExpr {
pub fn sequence(exprs: &[TypedExpr]) -> Self {
TypedExpr::Sequence {
location: Span::empty(),
expressions: exprs.to_vec(),
}
}
pub fn let_(value: Self, pattern: TypedPattern, tipo: Rc<Type>) -> Self {
TypedExpr::Assignment {
location: Span::empty(),
tipo: tipo.clone(),
value: value.into(),
pattern,
kind: AssignmentKind::let_(),
}
}
// Create an expect assignment, unless the target type is `Data`; then fallback to a let.
pub fn flexible_expect(value: Self, pattern: TypedPattern, tipo: Rc<Type>) -> Self {
TypedExpr::Assignment {
location: Span::empty(),
tipo: tipo.clone(),
value: value.into(),
pattern,
kind: if tipo.is_data() {
AssignmentKind::let_()
} else {
AssignmentKind::expect()
},
}
}
pub fn local_var(name: &str, tipo: Rc<Type>) -> Self {
TypedExpr::Var {
location: Span::empty(),
constructor: ValueConstructor {
public: true,
variant: ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
tipo: tipo.clone(),
},
name: name.to_string(),
}
}
pub fn tipo(&self) -> Rc<Type> {
match self {
Self::Var { constructor, .. } => constructor.tipo.clone(),
@ -224,9 +269,10 @@ impl TypedExpr {
| Self::RecordAccess { tipo, .. }
| Self::RecordUpdate { tipo, .. }
| Self::CurvePoint { tipo, .. } => tipo.clone(),
Self::Pipeline { expressions, .. } | Self::Sequence { expressions, .. } => {
expressions.last().map(TypedExpr::tipo).unwrap_or_else(void)
}
Self::Pipeline { expressions, .. } | Self::Sequence { expressions, .. } => expressions
.last()
.map(TypedExpr::tipo)
.unwrap_or_else(Type::void),
}
}
@ -241,6 +287,10 @@ impl TypedExpr {
)
}
pub fn is_error_term(&self) -> bool {
matches!(self, Self::ErrorTerm { .. })
}
/// Returns `true` if the typed expr is [`Assignment`].
pub fn is_assignment(&self) -> bool {
matches!(self, Self::Assignment { .. })
@ -487,7 +537,7 @@ impl TypedExpr {
module: String::new(),
constructors_count: 1,
},
tipo: void(),
tipo: Type::void(),
},
location,
}

View File

@ -3,10 +3,10 @@ use crate::{
Annotation, ArgBy, ArgName, ArgVia, AssignmentKind, AssignmentPattern, BinOp,
ByteArrayFormatPreference, CallArg, Constant, CurveType, DataType, Definition, Function,
LogicalOpChainKind, ModuleConstant, OnTestFailure, Pattern, RecordConstructor,
RecordConstructorArg, RecordUpdateSpread, Span, TraceKind, TypeAlias, TypedArg, UnOp,
UnqualifiedImport, UntypedArg, UntypedArgVia, UntypedAssignmentKind, UntypedClause,
UntypedDefinition, UntypedFunction, UntypedIfBranch, UntypedModule, UntypedPattern,
UntypedRecordUpdateArg, Use, Validator, CAPTURE_VARIABLE,
RecordConstructorArg, RecordUpdateSpread, Span, TraceKind, TypeAlias, TypedArg,
TypedValidator, UnOp, UnqualifiedImport, UntypedArg, UntypedArgVia, UntypedAssignmentKind,
UntypedClause, UntypedDefinition, UntypedFunction, UntypedIfBranch, UntypedModule,
UntypedPattern, UntypedRecordUpdateArg, Use, Validator, CAPTURE_VARIABLE,
},
docvec,
expr::{FnStyle, UntypedExpr, DEFAULT_ERROR_STR, DEFAULT_TODO_STR},
@ -232,15 +232,24 @@ impl<'comments> Formatter<'comments> {
return_annotation,
end_position,
..
}) => self.definition_fn(public, name, args, return_annotation, body, *end_position),
}) => self.definition_fn(
public,
name,
args,
return_annotation,
body,
*end_position,
false,
),
Definition::Validator(Validator {
end_position,
fun,
other_fun,
handlers,
fallback,
params,
name,
..
}) => self.definition_validator(params, fun, other_fun, *end_position),
}) => self.definition_validator(name, params, handlers, fallback, *end_position),
Definition::Test(Function {
name,
@ -512,16 +521,29 @@ impl<'comments> Formatter<'comments> {
return_annotation: &'a Option<Annotation>,
body: &'a UntypedExpr,
end_location: usize,
is_validator: bool,
) -> Document<'a> {
// Fn name and args
let head = pub_(*public)
.append("fn ")
.append(name)
.append(wrap_args(args.iter().map(|e| (self.fn_arg(e), false))));
let head = if !is_validator {
pub_(*public)
.append("fn ")
.append(name)
.append(wrap_args(args.iter().map(|e| (self.fn_arg(e), false))))
} else {
name.to_doc()
.append(wrap_args(args.iter().map(|e| (self.fn_arg(e), false))))
};
// Add return annotation
let head = match return_annotation {
Some(anno) => head.append(" -> ").append(self.annotation(anno)),
Some(anno) => {
let is_bool = anno.is_logically_equal(&Annotation::boolean(Span::empty()));
if is_validator && is_bool {
head
} else {
head.append(" -> ").append(self.annotation(anno))
}
}
None => head,
}
.group();
@ -581,57 +603,73 @@ impl<'comments> Formatter<'comments> {
fn definition_validator<'a>(
&mut self,
name: &'a str,
params: &'a [UntypedArg],
fun: &'a UntypedFunction,
other_fun: &'a Option<UntypedFunction>,
handlers: &'a [UntypedFunction],
fallback: &'a UntypedFunction,
end_position: usize,
) -> Document<'a> {
// 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()
});
// validator name(params)
let v_head = "validator"
.to_doc()
.append(" ")
.append(name)
.append(if !params.is_empty() {
wrap_args(params.iter().map(|e| (self.fn_arg(e), false)))
} else {
nil()
});
let fun_comments = self.pop_comments(fun.location.start);
let fun_doc_comments = self.doc_comments(fun.location.start);
let first_fn = self
.definition_fn(
&fun.public,
&fun.name,
&fun.arguments,
&fun.return_annotation,
&fun.body,
fun.end_position,
)
.group();
let first_fn = commented(fun_doc_comments.append(first_fn).group(), fun_comments);
let mut handler_docs = vec![];
let other_fn = match other_fun {
None => nil(),
Some(other) => {
let other_comments = self.pop_comments(other.location.start);
let other_doc_comments = self.doc_comments(other.location.start);
for handler in handlers.iter() {
let fun_comments = self.pop_comments(handler.location.start);
let fun_doc_comments = self.doc_comments(handler.location.start);
let other_fn = self
.definition_fn(
&other.public,
&other.name,
&other.arguments,
&other.return_annotation,
&other.body,
other.end_position,
)
.group();
let first_fn = self
.definition_fn(
&handler.public,
&handler.name,
&handler.arguments,
&handler.return_annotation,
&handler.body,
handler.end_position,
true,
)
.group();
commented(other_doc_comments.append(other_fn).group(), other_comments)
}
};
let first_fn = commented(fun_doc_comments.append(first_fn).group(), fun_comments);
let v_body = line()
.append(first_fn)
.append(if other_fun.is_some() { lines(2) } else { nil() })
.append(other_fn);
handler_docs.push(first_fn);
}
let is_exhaustive = handlers.len() >= TypedValidator::available_handler_names().len() - 1;
if !is_exhaustive || !fallback.is_default_fallback() {
let fallback_comments = self.pop_comments(fallback.location.start);
let fallback_doc_comments = self.doc_comments(fallback.location.start);
let fallback_fn = self
.definition_fn(
&fallback.public,
&fallback.name,
&fallback.arguments,
&fallback.return_annotation,
&fallback.body,
fallback.end_position,
true,
)
.group();
let fallback_fn = commented(
fallback_doc_comments.append(fallback_fn).group(),
fallback_comments,
);
handler_docs.push(fallback_fn);
}
let v_body = line().append(join(handler_docs, lines(2)));
let v_body = match printed_comments(self.pop_comments(end_position), false) {
Some(comments) => v_body.append(lines(2)).append(comments).nest(INDENT),

View File

@ -17,7 +17,7 @@ use crate::{
Span, TraceLevel, Tracing, TypedArg, TypedClause, TypedDataType, TypedFunction,
TypedPattern, TypedValidator, UnOp,
},
builtins::{bool, byte_array, data, int, list, pair, void, PRELUDE},
builtins::PRELUDE,
expr::TypedExpr,
gen_uplc::{
air::ExpectLevel,
@ -114,66 +114,15 @@ impl<'a> CodeGenerator<'a> {
}
}
pub fn generate(
&mut self,
TypedValidator {
fun,
other_fun,
params,
..
}: &TypedValidator,
module_name: &str,
) -> Program<Name> {
let mut air_tree_fun = self.build(&fun.body, module_name, &[]);
pub fn generate(&mut self, validator: &TypedValidator, module_name: &str) -> Program<Name> {
let air_tree_fun = wrap_validator_condition(
self.build(&validator.into_script_context_handler(), module_name, &[]),
self.tracing,
);
air_tree_fun = wrap_validator_condition(air_tree_fun, self.tracing);
let air_tree_fun = AirTree::anon_func(vec!["__context__".to_string()], air_tree_fun, true);
let (src_code, lines) = self.module_src.get(module_name).unwrap();
let mut validator_args_tree =
self.check_validator_args(&fun.arguments, true, air_tree_fun, src_code, lines);
if let Some(other) = other_fun {
let mut air_tree_fun_other = self.build(&other.body, module_name, &[]);
air_tree_fun_other = wrap_validator_condition(air_tree_fun_other, self.tracing);
let validator_args_tree_other = self.check_validator_args(
&other.arguments,
true,
air_tree_fun_other,
src_code,
lines,
);
let (spend, spend_name, mint, mint_name) =
if other.arguments.len() > fun.arguments.len() {
(
validator_args_tree_other,
other.name.clone(),
validator_args_tree,
fun.name.clone(),
)
} else {
(
validator_args_tree,
fun.name.clone(),
validator_args_tree_other,
other.name.clone(),
)
};
validator_args_tree = AirTree::multi_validator(mint_name, mint, spend_name, spend);
// Special Case with multi_validators
self.special_functions
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string());
self.special_functions
.use_function_uplc(CONSTR_INDEX_EXPOSER.to_string());
}
validator_args_tree = AirTree::no_op(validator_args_tree);
let validator_args_tree = AirTree::no_op(air_tree_fun);
let full_tree = self.hoist_functions_to_validator(validator_args_tree);
@ -181,9 +130,9 @@ impl<'a> CodeGenerator<'a> {
let full_vec = full_tree.to_vec();
let mut term = self.uplc_code_gen(full_vec);
let term = self.uplc_code_gen(full_vec);
term = cast_validator_args(term, params);
let term = cast_validator_args(term, &validator.params);
self.finalize(term)
}
@ -278,7 +227,7 @@ impl<'a> CodeGenerator<'a> {
self.special_functions.insert_new_function(
msg_func_name.clone(),
Term::Error.delayed_trace(Term::string(msg)).delay(),
void(),
Type::void(),
);
Some(self.special_functions.use_function_tree(msg_func_name))
}
@ -683,17 +632,21 @@ impl<'a> CodeGenerator<'a> {
let function_name = format!("__access_index_{}", *index);
if self.code_gen_functions.get(&function_name).is_none() {
let mut body = AirTree::local_var("__fields", list(data()));
let mut body = AirTree::local_var("__fields", Type::list(Type::data()));
for _ in 0..*index {
body = AirTree::builtin(
DefaultFunction::TailList,
list(data()),
Type::list(Type::data()),
vec![body],
)
}
body = AirTree::builtin(DefaultFunction::HeadList, data(), vec![body]);
body = AirTree::builtin(
DefaultFunction::HeadList,
Type::data(),
vec![body],
);
self.code_gen_functions.insert(
function_name.clone(),
@ -707,7 +660,7 @@ impl<'a> CodeGenerator<'a> {
let list_of_fields = AirTree::call(
self.special_functions
.use_function_tree(CONSTR_FIELDS_EXPOSER.to_string()),
list(data()),
Type::list(Type::data()),
vec![self.build(record, module_build_name, &[])],
);
@ -822,17 +775,21 @@ impl<'a> CodeGenerator<'a> {
let function_name = format!("__access_index_{}", *index);
if self.code_gen_functions.get(&function_name).is_none() {
let mut body = AirTree::local_var("__fields", list(data()));
let mut body = AirTree::local_var("__fields", Type::list(Type::data()));
for _ in 0..*index {
body = AirTree::builtin(
DefaultFunction::TailList,
list(data()),
Type::list(Type::data()),
vec![body],
)
}
body = AirTree::builtin(DefaultFunction::HeadList, data(), vec![body]);
body = AirTree::builtin(
DefaultFunction::HeadList,
Type::data(),
vec![body],
);
self.code_gen_functions.insert(
function_name.clone(),
@ -941,7 +898,7 @@ impl<'a> CodeGenerator<'a> {
let otherwise = match &props.otherwise {
Some(x) => x.clone(),
// (delay (error ))
None => AirTree::anon_func(vec![], AirTree::error(void(), false), true),
None => AirTree::anon_func(vec![], AirTree::error(Type::void(), false), true),
};
match pattern {
@ -957,10 +914,10 @@ impl<'a> CodeGenerator<'a> {
let expect = AirTree::binop(
BinOp::Eq,
bool(),
Type::bool(),
AirTree::int(expected_int),
AirTree::local_var(&name, int()),
int(),
AirTree::local_var(&name, Type::int()),
Type::int(),
);
assign_casted_value(
@ -979,10 +936,10 @@ impl<'a> CodeGenerator<'a> {
let expect = AirTree::binop(
BinOp::Eq,
bool(),
Type::bool(),
AirTree::byte_array(expected_bytes.clone()),
AirTree::local_var(&name, byte_array()),
byte_array(),
AirTree::local_var(&name, Type::byte_array()),
Type::byte_array(),
);
assign_casted_value(
@ -1369,7 +1326,13 @@ impl<'a> CodeGenerator<'a> {
type_map.insert(index, field_type);
}
assert!(type_map.len() >= arguments.len());
assert!(
type_map.len() >= arguments.len(),
"type map len: {}, arguments len: {}; for constructor {:?}",
type_map.len(),
arguments.len(),
name,
);
let mut fields = vec![];
@ -1471,7 +1434,9 @@ impl<'a> CodeGenerator<'a> {
.unwrap_or_else(|| unreachable!("Failed to find definition for {}", name));
let then = if props.kind.is_expect()
&& (data_type.constructors.len() > 1 || props.full_check)
&& (data_type.constructors.len() > 1
|| props.full_check
|| data_type.is_never())
{
let (index, _) = data_type
.constructors
@ -1484,7 +1449,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::when(
&subject_name,
void(),
Type::void(),
tipo.clone(),
AirTree::local_var(&constructor_name, tipo.clone()),
AirTree::assert_constr_index(
@ -1496,8 +1461,11 @@ impl<'a> CodeGenerator<'a> {
)
} else {
assert!(
data_type.constructors.len() == 1,
"data_type={data_type:#?}"
data_type.constructors.len() == 1 || data_type.is_never(),
"attempted let-assignment on a type with more or less than 1 constructor: \nis_expect? {}\nfull_check? {}\ndata_type={data_type:#?}\n{}",
props.kind.is_expect(),
props.full_check,
name,
);
then
};
@ -1609,7 +1577,11 @@ impl<'a> CodeGenerator<'a> {
otherwise: AirTree,
depth: usize,
) -> AirTree {
assert!(tipo.get_generic().is_none());
assert!(
tipo.get_generic().is_none(),
"left-hand side of expect is generic: {}",
tipo.to_pretty(0)
);
// Shouldn't be needed but still here just in case
// this function is called from anywhere else besides assignment
let tipo = &convert_opaque_type(tipo, &self.data_types, true);
@ -1650,11 +1622,14 @@ impl<'a> CodeGenerator<'a> {
defined_data_types,
location,
AirTree::call(
AirTree::local_var(format!("__curried_expect_on_list_{}", depth), void()),
void(),
AirTree::local_var(
format!("__curried_expect_on_list_{}", depth),
Type::void(),
),
Type::void(),
vec![AirTree::builtin(
DefaultFunction::TailList,
list(data()),
Type::list(Type::data()),
vec![AirTree::local_var(
format!("__list_{}", depth),
tipo.clone(),
@ -1689,7 +1664,7 @@ impl<'a> CodeGenerator<'a> {
&pair_name,
AirTree::builtin(
DefaultFunction::HeadList,
pair(data(), data()),
Type::pair(Type::data(), Type::data()),
vec![AirTree::local_var(
format!("__list_{}", depth),
tipo.clone(),
@ -1733,7 +1708,7 @@ impl<'a> CodeGenerator<'a> {
let func_call = AirTree::call(
AirTree::var(
ValueConstructor::public(
void(),
Type::void(),
ValueConstructorVariant::ModuleFn {
name: EXPECT_ON_LIST.to_string(),
field_map: None,
@ -1746,7 +1721,7 @@ impl<'a> CodeGenerator<'a> {
EXPECT_ON_LIST,
"",
),
void(),
Type::void(),
vec![AirTree::local_var(&map_name, tipo.clone()), unwrap_function],
);
@ -1826,7 +1801,7 @@ impl<'a> CodeGenerator<'a> {
&item_name,
AirTree::builtin(
DefaultFunction::HeadList,
data(),
Type::data(),
vec![AirTree::local_var(
format!("__list_{}", depth),
tipo.clone(),
@ -1835,7 +1810,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::soft_cast_assignment(
&item_name,
inner_list_type.clone(),
AirTree::local_var(&item_name, data()),
AirTree::local_var(&item_name, Type::data()),
self.expect_type_assign(
inner_list_type,
AirTree::local_var(&item_name, inner_list_type.clone()),
@ -1844,12 +1819,12 @@ impl<'a> CodeGenerator<'a> {
AirTree::call(
AirTree::local_var(
format!("__curried_expect_on_list_{}", depth),
void(),
Type::void(),
),
void(),
Type::void(),
vec![AirTree::builtin(
DefaultFunction::TailList,
list(data()),
Type::list(Type::data()),
vec![AirTree::local_var(
format!("__list_{}", depth),
tipo.clone(),
@ -1893,7 +1868,7 @@ impl<'a> CodeGenerator<'a> {
let func_call = AirTree::call(
AirTree::var(
ValueConstructor::public(
void(),
Type::void(),
ValueConstructorVariant::ModuleFn {
name: EXPECT_ON_LIST.to_string(),
field_map: None,
@ -1906,7 +1881,7 @@ impl<'a> CodeGenerator<'a> {
EXPECT_ON_LIST,
"",
),
void(),
Type::void(),
vec![
AirTree::local_var(&list_name, tipo.clone()),
unwrap_function,
@ -1997,14 +1972,28 @@ impl<'a> CodeGenerator<'a> {
let current_defined = defined_data_types.clone();
let mut diff_defined_types = vec![];
let var_then =
AirTree::call(AirTree::local_var("then_delayed", void()), void(), vec![]);
let var_then = AirTree::call(
AirTree::local_var("then_delayed", Type::void()),
Type::void(),
vec![],
);
let otherwise_delayed = AirTree::local_var("otherwise_delayed", void());
let otherwise_delayed = AirTree::local_var("otherwise_delayed", Type::void());
let is_never = data_type.is_never();
let constr_clauses = data_type.constructors.iter().enumerate().rfold(
otherwise_delayed.clone(),
|acc, (index, constr)| {
// NOTE: For the Never type, we have an placeholder first constructor
// that must be ignored. The Never type is considered to have only one
// constructor starting at index 1 so it shouldn't be possible to
// cast from Data into the first constructor. There's virtually no
// constructor at index 0.
if is_never && index == 0 {
return acc;
}
let mut constr_args = vec![];
let constr_then = constr.arguments.iter().enumerate().rfold(
@ -2081,13 +2070,13 @@ impl<'a> CodeGenerator<'a> {
let when_expr = AirTree::when(
format!("__subject_span_{}_{}", location.start, location.end),
void(),
Type::void(),
tipo.clone(),
AirTree::local_var(
format!("__constr_var_span_{}_{}", location.start, location.end),
tipo.clone(),
),
AirTree::call(constr_clauses, void(), vec![]),
AirTree::call(constr_clauses, Type::void(), vec![]),
);
let func_body = AirTree::let_assignment(
@ -2140,7 +2129,7 @@ impl<'a> CodeGenerator<'a> {
"",
);
AirTree::call(func_var, void(), args)
AirTree::call(func_var, Type::void(), args)
}
}
}
@ -2193,7 +2182,7 @@ impl<'a> CodeGenerator<'a> {
),
)
} else if let Some(data_type) = data_type {
if data_type.constructors.len() > 1 {
if data_type.constructors.len() > 1 && !data_type.is_never() {
AirTree::clause(
&props.original_subject_name,
clause_cond,
@ -2664,7 +2653,7 @@ impl<'a> CodeGenerator<'a> {
// Since check_last_item is false this will never get added to the final uplc anyway
ExpectLevel::None,
elems_then,
AirTree::error(void(), false),
AirTree::error(Type::void(), false),
)
} else {
assert!(defined_tails.len() >= elems.len());
@ -2745,7 +2734,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()),
false,
next_then,
AirTree::error(void(), false),
AirTree::error(Type::void(), false),
)
};
@ -2775,7 +2764,11 @@ impl<'a> CodeGenerator<'a> {
)
});
assert!(!data_type.constructors.is_empty());
assert!(
!data_type.constructors.is_empty(),
"{}\n{constructor:#?}\n{data_type:#?}",
subject_tipo.to_pretty(0)
);
let (constr_index, _) = data_type
.constructors
@ -2872,7 +2865,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()),
false,
next_then,
AirTree::error(void(), false),
AirTree::error(Type::void(), false),
)
};
@ -3001,7 +2994,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(&props.clause_var_name, subject_tipo.clone()),
false,
tuple_name_assigns,
AirTree::error(void(), false),
AirTree::error(Type::void(), false),
),
)
} else {
@ -3029,7 +3022,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::clause_guard(
&props.original_subject_name,
AirTree::int(value),
int(),
Type::int(),
then,
)
}
@ -3038,7 +3031,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::clause_guard(
&props.original_subject_name,
AirTree::byte_array(value.clone()),
byte_array(),
Type::byte_array(),
then,
)
}
@ -3163,14 +3156,14 @@ impl<'a> CodeGenerator<'a> {
AirTree::clause_guard(
&props.original_subject_name,
AirTree::bool(constr_name == "True"),
bool(),
Type::bool(),
then,
)
} else if subject_tipo.is_void() {
AirTree::clause_guard(
&props.original_subject_name,
AirTree::void(),
void(),
Type::void(),
then,
)
} else {
@ -3242,7 +3235,7 @@ impl<'a> CodeGenerator<'a> {
arg_names.push(arg_name.clone());
let param = AirTree::local_var(&arg_name, data());
let param = AirTree::local_var(&arg_name, Type::data());
let actual_type = convert_opaque_type(&arg.tipo, &self.data_types, true);
@ -3267,7 +3260,7 @@ impl<'a> CodeGenerator<'a> {
self.special_functions.insert_new_function(
msg_func_name.clone(),
Term::Error.delayed_trace(Term::string(msg)).delay(),
void(),
Type::void(),
);
Some(self.special_functions.use_function_tree(msg_func_name))
@ -3283,7 +3276,7 @@ impl<'a> CodeGenerator<'a> {
inner_then,
&actual_type,
AssignmentProperties {
value_type: data(),
value_type: Type::data(),
kind: AssignmentKind::expect(),
remove_unused: false,
full_check: true,
@ -4036,9 +4029,9 @@ impl<'a> CodeGenerator<'a> {
.get(&generic_function_key.function_name)
.unwrap_or_else(|| {
panic!(
"Missing function definition for {}. Known definitions: {:?}",
"Missing function definition for {}. Known functions: {:?}",
generic_function_key.function_name,
self.code_gen_functions.keys(),
self.functions.keys(),
)
});
@ -5604,8 +5597,18 @@ impl<'a> CodeGenerator<'a> {
} => {
let tail_name_prefix = "__tail_index";
let data_type = lookup_data_type_by_tipo(&self.data_types, &tipo)
.unwrap_or_else(|| panic!("HOW DID YOU DO THIS ON BOOL OR VOID"));
let data_type =
lookup_data_type_by_tipo(&self.data_types, &tipo).unwrap_or_else(|| {
panic!(
"Attempted record update on an unknown type!\ntype: {:#?}",
tipo
)
});
assert!(
!data_type.is_never(),
"Attempted record update on a Never type.",
);
let constructor_field_count = data_type.constructors[0].arguments.len();
let record = arg_stack.pop().unwrap();

View File

@ -7,7 +7,6 @@ use crate::{
Constant, DataTypeKey, FunctionAccessKey, Pattern, Span, TraceLevel, TypedArg,
TypedAssignmentKind, TypedClause, TypedDataType, TypedPattern,
},
builtins::{data, function, int, list, void},
expr::TypedExpr,
line_numbers::{LineColumn, LineNumbers},
tipo::{
@ -212,7 +211,7 @@ impl CodeGenSpecialFuncs {
Term::snd_pair()
.apply(Term::unconstr_data().apply(Term::var("__constr_var")))
.lambda("__constr_var"),
function(vec![data()], list(data())),
Type::function(vec![Type::data()], Type::list(Type::data())),
),
);
@ -222,7 +221,7 @@ impl CodeGenSpecialFuncs {
Term::fst_pair()
.apply(Term::unconstr_data().apply(Term::var("__constr_var")))
.lambda("__constr_var"),
function(vec![data()], int()),
Type::function(vec![Type::data()], Type::int()),
),
);
@ -784,7 +783,7 @@ pub fn rearrange_list_clauses(
tipo: tipo.clone(),
text: Box::new(TypedExpr::String {
location: Span::empty(),
tipo: crate::builtins::string(),
tipo: Type::string(),
value: format!("Clause hole found for {index} elements."),
}),
then: Box::new(TypedExpr::ErrorTerm {
@ -1688,15 +1687,15 @@ pub fn cast_validator_args(term: Term<Name>, arguments: &[TypedArg]) -> Term<Nam
pub fn wrap_validator_condition(air_tree: AirTree, trace: TraceLevel) -> AirTree {
let otherwise = match trace {
TraceLevel::Silent | TraceLevel::Compact => AirTree::error(void(), true),
TraceLevel::Silent | TraceLevel::Compact => AirTree::error(Type::void(), true),
TraceLevel::Verbose => AirTree::trace(
AirTree::string("Validator returned false"),
void(),
AirTree::error(void(), true),
Type::void(),
AirTree::error(Type::void(), true),
),
};
AirTree::if_branch(void(), air_tree, AirTree::void(), otherwise)
AirTree::if_branch(Type::void(), air_tree, AirTree::void(), otherwise)
}
pub fn extract_constant(term: &Term<Name>) -> Option<Rc<UplcConstant>> {

View File

@ -1,7 +1,6 @@
use super::air::{Air, ExpectLevel};
use crate::{
ast::{BinOp, Curve, Span, UnOp},
builtins::{bool, byte_array, data, int, list, string, void},
tipo::{Type, ValueConstructor, ValueConstructorVariant},
};
use indexmap::IndexSet;
@ -113,7 +112,7 @@ pub enum AirMsg {
impl AirMsg {
pub fn to_air_tree(&self) -> AirTree {
match self {
AirMsg::LocalVar(name) => AirTree::local_var(name, string()),
AirMsg::LocalVar(name) => AirTree::local_var(name, Type::string()),
AirMsg::Msg(msg) => AirTree::string(msg),
}
}
@ -862,8 +861,8 @@ impl AirTree {
AirTree::var(
ValueConstructor::public(
Type::Fn {
args: vec![list(data())],
ret: data(),
args: vec![Type::list(Type::data())],
ret: Type::data(),
alias: None,
}
.into(),
@ -879,7 +878,7 @@ impl AirTree {
function_name,
"",
),
data(),
Type::data(),
vec![list_of_fields],
),
tipo.clone(),
@ -984,7 +983,7 @@ impl AirTree {
} else {
DefaultFunction::SndPair
},
data(),
Type::data(),
vec![tuple],
),
tipo.clone(),
@ -1039,9 +1038,9 @@ impl AirTree {
}
pub fn expect_on_list2() -> AirTree {
let inner_expect_on_list = AirTree::local_var(INNER_EXPECT_ON_LIST, void());
let inner_expect_on_list = AirTree::local_var(INNER_EXPECT_ON_LIST, Type::void());
let list_var = AirTree::local_var("__list_to_check", list(data()));
let list_var = AirTree::local_var("__list_to_check", Type::list(Type::data()));
AirTree::let_assignment(
INNER_EXPECT_ON_LIST,
@ -1051,13 +1050,13 @@ impl AirTree {
"__list_to_check".to_string(),
],
AirTree::call(
AirTree::local_var("__check_with", void()),
void(),
AirTree::local_var("__check_with", Type::void()),
Type::void(),
vec![
list_var.clone(),
AirTree::call(
inner_expect_on_list.clone(),
void(),
Type::void(),
vec![inner_expect_on_list.clone()],
),
],
@ -1066,27 +1065,27 @@ impl AirTree {
),
AirTree::call(
inner_expect_on_list.clone(),
void(),
Type::void(),
vec![inner_expect_on_list, list_var],
),
)
}
pub fn expect_on_list() -> AirTree {
let list_var = AirTree::local_var("__list_to_check", list(data()));
let list_var = AirTree::local_var("__list_to_check", Type::list(Type::data()));
let head_list = AirTree::builtin(DefaultFunction::HeadList, data(), vec![list_var]);
let head_list = AirTree::builtin(DefaultFunction::HeadList, Type::data(), vec![list_var]);
let expect_on_head = AirTree::call(
AirTree::local_var("__check_with", void()),
void(),
AirTree::local_var("__check_with", Type::void()),
Type::void(),
vec![head_list],
);
let next_call = AirTree::call(
AirTree::var(
ValueConstructor::public(
void(),
Type::void(),
ValueConstructorVariant::ModuleFn {
name: EXPECT_ON_LIST.to_string(),
field_map: None,
@ -1099,14 +1098,17 @@ impl AirTree {
EXPECT_ON_LIST,
"",
),
void(),
Type::void(),
vec![
AirTree::builtin(
DefaultFunction::TailList,
list(data()),
vec![AirTree::local_var("__list_to_check", list(data()))],
Type::list(Type::data()),
vec![AirTree::local_var(
"__list_to_check",
Type::list(Type::data()),
)],
),
AirTree::local_var("__check_with", void()),
AirTree::local_var("__check_with", Type::void()),
],
);
@ -1114,7 +1116,7 @@ impl AirTree {
AirTree::list_clause(
"__list_to_check",
void(),
Type::void(),
AirTree::void(),
assign,
None,
@ -1675,10 +1677,10 @@ impl AirTree {
pub fn return_type(&self) -> Rc<Type> {
match self {
AirTree::Int { .. } => int(),
AirTree::String { .. } => string(),
AirTree::ByteArray { .. } => byte_array(),
AirTree::Bool { .. } => bool(),
AirTree::Int { .. } => Type::int(),
AirTree::String { .. } => Type::string(),
AirTree::ByteArray { .. } => Type::byte_array(),
AirTree::Bool { .. } => Type::bool(),
AirTree::CurvePoint { point } => point.tipo(),
AirTree::List { tipo, .. }
| AirTree::Tuple { tipo, .. }
@ -1693,14 +1695,14 @@ impl AirTree {
| AirTree::RecordUpdate { tipo, .. }
| AirTree::ErrorTerm { tipo, .. }
| AirTree::Trace { tipo, .. } => tipo.clone(),
AirTree::Void => void(),
AirTree::Void => Type::void(),
AirTree::Var { constructor, .. } => constructor.tipo.clone(),
AirTree::Fn { func_body, .. } => func_body.return_type(),
AirTree::UnOp { op, .. } => match op {
UnOp::Not => bool(),
UnOp::Negate => int(),
UnOp::Not => Type::bool(),
UnOp::Negate => Type::int(),
},
AirTree::CastToData { .. } => data(),
AirTree::CastToData { .. } => Type::data(),
AirTree::Clause { then, .. }
| AirTree::ListClause { then, .. }
| AirTree::WrapClause { then, .. }
@ -1725,7 +1727,7 @@ impl AirTree {
| AirTree::FieldsEmpty { then, .. }
| AirTree::ListEmpty { then, .. }
| AirTree::NoOp { then } => then.return_type(),
AirTree::MultiValidator { .. } => void(),
AirTree::MultiValidator { .. } => Type::void(),
}
}

View File

@ -1,7 +1,7 @@
use super::{error::ParseError, token::Token};
use crate::{
ast,
builtins::{PAIR, PRELUDE},
ast::{self, well_known},
builtins::PRELUDE,
};
use chumsky::prelude::*;
@ -19,7 +19,7 @@ pub fn parser() -> impl Parser<Token, ast::Annotation, Error = ParseError> {
select! {Token::Name { name } if name == PRELUDE => name}
.then_ignore(just(Token::Dot))
.or_not()
.then_ignore(select! {Token::UpName { name } if name == PAIR => name})
.then_ignore(select! {Token::UpName { name } if name == well_known::PAIR => name})
.ignore_then(
expression
.clone()

View File

@ -1,78 +1,36 @@
---
source: crates/aiken-lang/src/parser/definition/validator.rs
description: "Code:\n\nvalidator {\n fn foo(datum, rdmr, ctx) {\n True\n }\n\n fn bar(rdmr, ctx) {\n True\n }\n}\n"
description: "Code:\n\nvalidator thing {\n spend (datum, rdmr, ctx) {\n True\n }\n\n mint (rdmr, ctx) {\n True\n }\n}\n"
---
Validator(
Validator {
doc: None,
end_position: 90,
fun: Function {
arguments: [
UntypedArg {
by: ByName(
Named {
name: "datum",
label: "datum",
location: 21..26,
},
),
location: 21..26,
annotation: None,
doc: None,
is_validator_param: false,
},
UntypedArg {
by: ByName(
Named {
name: "rdmr",
label: "rdmr",
location: 28..32,
},
),
location: 28..32,
annotation: None,
doc: None,
is_validator_param: false,
},
UntypedArg {
by: ByName(
Named {
name: "ctx",
label: "ctx",
location: 34..37,
},
),
location: 34..37,
annotation: None,
doc: None,
is_validator_param: false,
},
],
body: Var {
location: 45..49,
name: "True",
},
doc: None,
location: 14..38,
name: "foo",
public: false,
return_annotation: None,
return_type: (),
end_position: 52,
on_test_failure: FailImmediately,
},
other_fun: Some(
end_position: 95,
handlers: [
Function {
arguments: [
UntypedArg {
by: ByName(
Named {
name: "rdmr",
label: "rdmr",
location: 64..68,
name: "datum",
label: "datum",
location: 27..32,
},
),
location: 64..68,
location: 27..32,
annotation: None,
doc: None,
is_validator_param: false,
},
UntypedArg {
by: ByName(
Named {
name: "rdmr",
label: "rdmr",
location: 34..38,
},
),
location: 34..38,
annotation: None,
doc: None,
is_validator_param: false,
@ -82,30 +40,122 @@ Validator(
Named {
name: "ctx",
label: "ctx",
location: 70..73,
location: 40..43,
},
),
location: 70..73,
location: 40..43,
annotation: None,
doc: None,
is_validator_param: false,
},
],
body: Var {
location: 81..85,
location: 51..55,
name: "True",
},
doc: None,
location: 57..74,
name: "bar",
public: false,
return_annotation: None,
location: 20..44,
name: "spend",
public: true,
return_annotation: Some(
Constructor {
location: 26..44,
module: None,
name: "Bool",
arguments: [],
},
),
return_type: (),
end_position: 88,
end_position: 58,
on_test_failure: FailImmediately,
},
),
Function {
arguments: [
UntypedArg {
by: ByName(
Named {
name: "rdmr",
label: "rdmr",
location: 69..73,
},
),
location: 69..73,
annotation: None,
doc: None,
is_validator_param: false,
},
UntypedArg {
by: ByName(
Named {
name: "ctx",
label: "ctx",
location: 75..78,
},
),
location: 75..78,
annotation: None,
doc: None,
is_validator_param: false,
},
],
body: Var {
location: 86..90,
name: "True",
},
doc: None,
location: 63..79,
name: "mint",
public: true,
return_annotation: Some(
Constructor {
location: 68..79,
module: None,
name: "Bool",
arguments: [],
},
),
return_type: (),
end_position: 93,
on_test_failure: FailImmediately,
},
],
location: 0..9,
name: "thing",
params: [],
fallback: Function {
arguments: [
UntypedArg {
by: ByName(
Discarded {
name: "_",
label: "_",
location: 0..9,
},
),
location: 0..9,
annotation: None,
doc: None,
is_validator_param: false,
},
],
body: ErrorTerm {
location: 0..9,
},
doc: None,
location: 0..9,
name: "else",
public: true,
return_annotation: Some(
Constructor {
location: 0..9,
module: None,
name: "Bool",
arguments: [],
},
),
return_type: (),
end_position: 8,
on_test_failure: FailImmediately,
},
},
)

View File

@ -0,0 +1,161 @@
---
source: crates/aiken-lang/src/parser/definition/validator.rs
description: "Code:\n\nvalidator thing {\n spend (datum, rdmr, ctx) {\n True\n }\n\n mint (rdmr, ctx) {\n True\n }\n\n else (_) {\n fail\n }\n}\n"
---
Validator(
Validator {
doc: None,
end_position: 122,
handlers: [
Function {
arguments: [
UntypedArg {
by: ByName(
Named {
name: "datum",
label: "datum",
location: 27..32,
},
),
location: 27..32,
annotation: None,
doc: None,
is_validator_param: false,
},
UntypedArg {
by: ByName(
Named {
name: "rdmr",
label: "rdmr",
location: 34..38,
},
),
location: 34..38,
annotation: None,
doc: None,
is_validator_param: false,
},
UntypedArg {
by: ByName(
Named {
name: "ctx",
label: "ctx",
location: 40..43,
},
),
location: 40..43,
annotation: None,
doc: None,
is_validator_param: false,
},
],
body: Var {
location: 51..55,
name: "True",
},
doc: None,
location: 20..44,
name: "spend",
public: true,
return_annotation: Some(
Constructor {
location: 26..44,
module: None,
name: "Bool",
arguments: [],
},
),
return_type: (),
end_position: 58,
on_test_failure: FailImmediately,
},
Function {
arguments: [
UntypedArg {
by: ByName(
Named {
name: "rdmr",
label: "rdmr",
location: 69..73,
},
),
location: 69..73,
annotation: None,
doc: None,
is_validator_param: false,
},
UntypedArg {
by: ByName(
Named {
name: "ctx",
label: "ctx",
location: 75..78,
},
),
location: 75..78,
annotation: None,
doc: None,
is_validator_param: false,
},
],
body: Var {
location: 86..90,
name: "True",
},
doc: None,
location: 63..79,
name: "mint",
public: true,
return_annotation: Some(
Constructor {
location: 68..79,
module: None,
name: "Bool",
arguments: [],
},
),
return_type: (),
end_position: 93,
on_test_failure: FailImmediately,
},
],
location: 0..9,
name: "thing",
params: [],
fallback: Function {
arguments: [
UntypedArg {
by: ByName(
Discarded {
name: "_",
label: "_",
location: 104..105,
},
),
location: 104..105,
annotation: None,
doc: None,
is_validator_param: false,
},
],
body: ErrorTerm {
location: 113..117,
},
doc: None,
location: 103..106,
name: "else",
public: true,
return_annotation: Some(
Constructor {
location: 103..106,
module: None,
name: "Bool",
arguments: [],
},
),
return_type: (),
end_position: 120,
on_test_failure: FailImmediately,
},
},
)

View File

@ -1,68 +1,112 @@
---
source: crates/aiken-lang/src/parser/definition/validator.rs
description: "Code:\n\nvalidator {\n fn foo(datum, rdmr, ctx) {\n True\n }\n}\n"
description: "Code:\n\nvalidator hello {\n spend (datum, rdmr, ctx) {\n True\n }\n}\n"
---
Validator(
Validator {
doc: None,
end_position: 54,
fun: Function {
end_position: 60,
handlers: [
Function {
arguments: [
UntypedArg {
by: ByName(
Named {
name: "datum",
label: "datum",
location: 27..32,
},
),
location: 27..32,
annotation: None,
doc: None,
is_validator_param: false,
},
UntypedArg {
by: ByName(
Named {
name: "rdmr",
label: "rdmr",
location: 34..38,
},
),
location: 34..38,
annotation: None,
doc: None,
is_validator_param: false,
},
UntypedArg {
by: ByName(
Named {
name: "ctx",
label: "ctx",
location: 40..43,
},
),
location: 40..43,
annotation: None,
doc: None,
is_validator_param: false,
},
],
body: Var {
location: 51..55,
name: "True",
},
doc: None,
location: 20..44,
name: "spend",
public: true,
return_annotation: Some(
Constructor {
location: 26..44,
module: None,
name: "Bool",
arguments: [],
},
),
return_type: (),
end_position: 58,
on_test_failure: FailImmediately,
},
],
location: 0..9,
name: "hello",
params: [],
fallback: Function {
arguments: [
UntypedArg {
by: ByName(
Named {
name: "datum",
label: "datum",
location: 21..26,
Discarded {
name: "_",
label: "_",
location: 0..9,
},
),
location: 21..26,
annotation: None,
doc: None,
is_validator_param: false,
},
UntypedArg {
by: ByName(
Named {
name: "rdmr",
label: "rdmr",
location: 28..32,
},
),
location: 28..32,
annotation: None,
doc: None,
is_validator_param: false,
},
UntypedArg {
by: ByName(
Named {
name: "ctx",
label: "ctx",
location: 34..37,
},
),
location: 34..37,
location: 0..9,
annotation: None,
doc: None,
is_validator_param: false,
},
],
body: Var {
location: 45..49,
name: "True",
body: ErrorTerm {
location: 0..9,
},
doc: None,
location: 14..38,
name: "foo",
public: false,
return_annotation: None,
location: 0..9,
name: "else",
public: true,
return_annotation: Some(
Constructor {
location: 0..9,
module: None,
name: "Bool",
arguments: [],
},
),
return_type: (),
end_position: 52,
end_position: 8,
on_test_failure: FailImmediately,
},
other_fun: None,
location: 0..9,
params: [],
},
)

View File

@ -1,66 +1,112 @@
use chumsky::prelude::*;
use super::function::param;
use crate::{
ast,
parser::{error::ParseError, token::Token},
ast::{self, well_known},
expr::UntypedExpr,
parser::{annotation, error::ParseError, expr, token::Token},
};
use super::function;
use chumsky::prelude::*;
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
just(Token::Validator)
.ignore_then(
function::param(true)
.ignore_then(select! {Token::Name {name} => name})
.then(
param(true)
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
.map_with_span(|arguments, span| (arguments, span))
.or_not(),
)
// so far: validator my_validator(arg1: Whatever)
.then(
function()
select! {Token::Name {name} => name}
.then(args_and_body())
.map_with_span(|(name, mut function), span| {
function.name = name;
function.location.start = span.start;
function
})
.repeated()
.at_least(1)
.at_most(2)
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace))
.map(|defs| {
defs.into_iter().map(|def| {
let ast::UntypedDefinition::Fn(fun) = def else {
unreachable!("It should be a fn definition");
};
.then(
just(Token::Else)
.ignore_then(args_and_body().map_with_span(|mut function, span| {
function.name = well_known::VALIDATOR_ELSE.to_string();
function.location.start = span.start;
fun
})
}),
function
}))
.or_not(),
)
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
)
.map_with_span(|(opt_extra_params, mut functions), span| {
let (params, params_span) = opt_extra_params.unwrap_or((
vec![],
ast::Span {
start: 0,
end: span.start + "validator".len(),
},
));
.map_with_span(
|((name, opt_extra_params), (handlers, opt_catch_all)), span| {
let (params, params_span) = opt_extra_params.unwrap_or((
vec![],
ast::Span {
start: 0,
end: span.start + "validator".len(),
},
));
let fun = functions
.next()
.expect("unwrapping safe because there's 'at_least(1)' function");
let other_fun = functions.next();
ast::UntypedDefinition::Validator(ast::Validator {
doc: None,
fun,
other_fun,
location: ast::Span {
let location = ast::Span {
start: span.start,
// capture the span from the optional params
end: params_span.end,
},
params,
end_position: span.end - 1,
})
})
};
ast::UntypedDefinition::Validator(ast::Validator {
doc: None,
name,
handlers,
location,
params,
end_position: span.end - 1,
fallback: opt_catch_all
.unwrap_or(ast::UntypedValidator::default_fallback(location)),
})
},
)
}
pub fn args_and_body() -> impl Parser<Token, ast::UntypedFunction, Error = ParseError> {
param(false)
.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(annotation()).or_not())
.then(
expr::sequence()
.or_not()
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
)
.map_with_span(
|(((arguments, args_span), return_annotation), body), span| {
let location = ast::Span {
start: span.start,
end: return_annotation
.as_ref()
.map(|l| l.location().end)
.unwrap_or_else(|| args_span.end),
};
ast::Function {
arguments,
body: body.unwrap_or_else(|| UntypedExpr::todo(None, span)),
doc: None,
location,
end_position: span.end - 1,
name: "temp".to_string(),
public: true,
return_annotation: return_annotation
.or(Some(ast::Annotation::boolean(location))),
return_type: (),
on_test_failure: ast::OnTestFailure::FailImmediately,
}
},
)
}
#[cfg(test)]
@ -71,8 +117,8 @@ mod tests {
fn validator() {
assert_definition!(
r#"
validator {
fn foo(datum, rdmr, ctx) {
validator hello {
spend (datum, rdmr, ctx) {
True
}
}
@ -84,16 +130,37 @@ mod tests {
fn double_validator() {
assert_definition!(
r#"
validator {
fn foo(datum, rdmr, ctx) {
validator thing {
spend (datum, rdmr, ctx) {
True
}
fn bar(rdmr, ctx) {
mint (rdmr, ctx) {
True
}
}
"#
);
}
#[test]
fn fallback() {
assert_definition!(
r#"
validator thing {
spend (datum, rdmr, ctx) {
True
}
mint (rdmr, ctx) {
True
}
else (_) {
fail
}
}
"#
);
}
}

View File

@ -1,5 +1,6 @@
use crate::{
builtins::{PAIR, PRELUDE},
ast::well_known,
builtins::PRELUDE,
expr::UntypedExpr,
parser::{error::ParseError, token::Token},
};
@ -11,7 +12,7 @@ pub fn parser(
select! {Token::Name { name } if name == PRELUDE => name}
.then_ignore(just(Token::Dot))
.or_not()
.then_ignore(select! {Token::UpName { name } if name == PAIR => name})
.then_ignore(select! {Token::UpName { name } if name == well_known::PAIR => name})
.ignore_then(
r.clone()
.separated_by(just(Token::Comma))

View File

@ -1,6 +1,5 @@
use crate::{
ast::UntypedPattern,
builtins::PAIR,
ast::{well_known, UntypedPattern},
parser::{error::ParseError, token::Token},
};
use chumsky::prelude::*;
@ -8,7 +7,7 @@ use chumsky::prelude::*;
pub fn parser(
pattern: Recursive<'_, Token, UntypedPattern, ParseError>,
) -> impl Parser<Token, UntypedPattern, Error = ParseError> + '_ {
select! {Token::UpName { name } if name == PAIR => name}
select! {Token::UpName { name } if name == well_known::PAIR => name}
.ignore_then(choice((
just(Token::LeftParen),
just(Token::NewLineLeftParen),

View File

@ -175,6 +175,6 @@ macro_rules! assert_format {
let (module2, extra2) = $crate::parser::module(&out, $crate::ast::ModuleKind::Lib).unwrap();
let mut out2 = String::new();
$crate::format::pretty(&mut out2, module2, extra2, &out);
assert_eq!(out, out2, "formatting isn't idempotent");
pretty_assertions::assert_eq!(out, out2, "formatting isn't idempotent");
};
}

View File

@ -111,8 +111,8 @@ fn bls12_381_ml_result_in_data_type() {
#[test]
fn validator_illegal_return_type() {
let source_code = r#"
validator {
fn foo(d, r, c) {
validator foo {
spend(d, r, c) -> Int {
1
}
}
@ -140,8 +140,8 @@ fn implicitly_discard_void() {
#[test]
fn validator_illegal_arity() {
let source_code = r#"
validator {
fn foo(c) {
validator foo {
mint(c) {
True
}
}
@ -318,19 +318,17 @@ fn mark_constructors_as_used_via_field_access() {
bar: Int,
}
validator {
fn foo(d: Datum, _r, _c) {
when d is {
D0(params) -> params.foo == 1
D1(_params) -> False
}
fn spend(d: Datum, _r, _c) {
when d is {
D0(params) -> params.foo == 1
D1(_params) -> False
}
}
"#;
let (warnings, _) = check_validator(parse(source_code)).unwrap();
let (warnings, _) = check(parse(source_code)).unwrap();
assert_eq!(warnings.len(), 1)
assert_eq!(warnings.len(), 2)
}
#[test]
@ -359,8 +357,8 @@ fn expect_multi_patterns() {
#[test]
fn validator_correct_form() {
let source_code = r#"
validator {
fn foo(d, r, c) {
validator foo {
spend(d: Option<Data>, r, oref, c) {
True
}
}
@ -372,8 +370,8 @@ fn validator_correct_form() {
#[test]
fn validator_in_lib_warning() {
let source_code = r#"
validator {
fn foo(c) {
validator foo {
spend(c) {
True
}
}
@ -390,12 +388,12 @@ fn validator_in_lib_warning() {
#[test]
fn multi_validator() {
let source_code = r#"
validator(foo: ByteArray, bar: Int) {
fn spend(_d, _r, _c) {
validator foo(foo: ByteArray, bar: Int) {
spend(_d: Option<Data>, _r, _oref, _c) {
foo == #"aabb"
}
fn mint(_r, _c) {
mint(_r, _p, _c) {
bar == 0
}
}
@ -409,12 +407,12 @@ fn multi_validator() {
#[test]
fn multi_validator_warning() {
let source_code = r#"
validator(foo: ByteArray, bar: Int) {
fn spend(_d, _r, _c) {
validator foo(foo: ByteArray, bar: Int) {
spend(_d: Option<Data>, _r, _oref, _c) {
foo == #"aabb"
}
fn mint(_r, _c) {
mint(_r, _p, _c) {
True
}
}
@ -459,8 +457,8 @@ fn exhaustiveness_simple() {
#[test]
fn validator_args_no_annotation() {
let source_code = r#"
validator(d) {
fn foo(a, b, c) {
validator hello(d) {
spend(a: Option<Data>, b, oref, c) {
True
}
}
@ -477,9 +475,13 @@ fn validator_args_no_annotation() {
assert!(param.tipo.is_data());
});
validator.fun.arguments.iter().for_each(|arg| {
assert!(arg.tipo.is_data());
})
validator.handlers[0]
.arguments
.iter()
.skip(1)
.for_each(|arg| {
assert!(arg.tipo.is_data());
})
})
}
@ -2472,9 +2474,11 @@ fn validator_private_type_leak() {
bar: Int,
}
validator {
pub fn bar(datum: Datum, redeemer: Redeemer, _ctx) {
datum.foo == redeemer.bar
validator bar {
spend(datum: Option<Datum>, redeemer: Redeemer, _oref, _ctx) {
expect Some(d) = datum
d.foo == redeemer.bar
}
}
"#;
@ -2496,30 +2500,11 @@ fn validator_public() {
bar: Int,
}
validator {
pub fn bar(datum: Datum, redeemer: Redeemer, _ctx) {
datum.foo == redeemer.bar
}
}
"#;
validator bar {
spend(datum: Option<Datum>, redeemer: Redeemer, _oref, _ctx) {
expect Some(d) = datum
assert!(check_validator(parse(source_code)).is_ok())
}
#[test]
fn validator_private_everything() {
let source_code = r#"
type Datum {
foo: Int,
}
type Redeemer {
bar: Int,
}
validator {
fn bar(datum: Datum, redeemer: Redeemer, _ctx) {
datum.foo == redeemer.bar
d.foo == redeemer.bar
}
}
"#;
@ -3065,3 +3050,173 @@ fn test_return_illegal() {
Err((_, Error::IllegalTestType { .. }))
))
}
#[test]
fn validator_by_name() {
let source_code = r#"
validator foo {
mint(_redeemer: Data, policy_id: ByteArray, _self: Data) {
policy_id == "foo"
}
}
test test_1() {
foo.mint(Void, "foo", Void)
}
"#;
assert!(check_validator(parse(source_code)).is_ok())
}
#[test]
fn validator_by_name_unknown_handler() {
let source_code = r#"
validator foo {
mint(_redeemer: Data, policy_id: ByteArray, _self: Data) {
policy_id == "foo"
}
}
test foo() {
foo.bar(Void, "foo", Void)
}
"#;
assert!(matches!(
check_validator(parse(source_code)),
Err((_, Error::UnknownValidatorHandler { .. }))
))
}
#[test]
fn validator_by_name_module_duplicate() {
let source_code = r#"
use aiken/builtin
validator builtin {
mint(_redeemer: Data, _policy_id: ByteArray, _self: Data) {
True
}
}
"#;
assert!(matches!(
check_validator(parse(source_code)),
Err((_, Error::DuplicateName { .. }))
))
}
#[test]
fn validator_by_name_validator_duplicate_1() {
let source_code = r#"
validator foo {
mint(_redeemer: Data, _policy_id: ByteArray, _self: Data) {
True
}
}
validator foo {
mint(_redeemer: Data, _policy_id: ByteArray, _self: Data) {
True
}
}
"#;
assert!(matches!(
check_validator(parse(source_code)),
Err((_, Error::DuplicateName { .. }))
))
}
#[test]
fn validator_by_name_validator_duplicate_2() {
let source_code = r#"
validator foo {
mint(_redeemer: Data, _policy_id: ByteArray, _self: Data) {
True
}
mint(_redeemer: Data, _policy_id: ByteArray, _self: Data) {
True
}
}
"#;
assert!(matches!(
check_validator(parse(source_code)),
Err((_, Error::DuplicateName { .. }))
))
}
#[test]
fn exhaustive_handlers() {
let source_code = r#"
validator foo {
mint(_redeemer, _policy_id, _self) {
True
}
spend(_datum, _redeemer, _policy_id, _self) {
True
}
withdraw(_redeemer, _account, _self) {
True
}
publish(_redeemer, _certificate, _self) {
True
}
vote(_redeemer, _voter, _self) {
True
}
propose(_redeemer, _proposal, _self) {
True
}
}
"#;
assert!(check_validator(parse(source_code)).is_ok())
}
#[test]
fn extraneous_fallback_on_exhaustive_handlers() {
let source_code = r#"
validator foo {
mint(_redeemer, _policy_id, _self) {
True
}
spend(_datum, _redeemer, _policy_id, _self) {
True
}
withdraw(_redeemer, _account, _self) {
True
}
publish(_redeemer, _certificate, _self) {
True
}
vote(_redeemer, _voter, _self) {
True
}
propose(_redeemer, _proposal, _self) {
True
}
else (_) -> Bool {
fail
}
}
"#;
assert!(matches!(
check_validator(parse(source_code)),
Err((_, Error::UnexpectedValidatorFallback { .. }))
))
}

View File

@ -198,18 +198,18 @@ fn format_preserve_newline_after_bool_expect() {
fn format_validator() {
assert_format!(
r#"
validator ( ) {
validator thing ( ) {
// What is the purpose of life
fn foo (d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {
spend(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {
True
}
}
// What?
validator {
validator foo {
/// Some documentation for foo
fn foo() {
foo() {
Void
}
@ -223,12 +223,12 @@ fn format_validator() {
fn format_double_validator() {
assert_format!(
r#"
validator ( param1 : ByteArray ) {
fn foo (d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {
validator foo( param1 : ByteArray ) {
spend(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {
True
}
/// This is bar
fn bar(r: Redeemer, ctx : ScriptContext ) -> Bool { True }
mint(r: Redeemer, ctx : ScriptContext ) -> Bool { True }
}
"#
);
@ -238,12 +238,12 @@ fn format_double_validator() {
fn format_double_validator_public() {
assert_format!(
r#"
validator ( param1 : ByteArray ) {
pub fn foo (d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {
validator foo ( param1 : ByteArray ) {
spend(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {
True
}
/// This is bar
pub fn bar(r: Redeemer, ctx : ScriptContext ) -> Bool { True }
mint(r: Redeemer, ctx : ScriptContext ) -> Bool { True }
}
"#
);
@ -967,20 +967,20 @@ fn format_anon_fn_pattern() {
fn format_validator_pattern() {
assert_format!(
r#"
validator(Foo { a, b, .. }) {
fn foo() { todo }
validator foo(Foo { a, b, .. }) {
spend() { todo }
}
validator([Bar] : List<Bar>) {
fn bar() { todo }
validator foo([Bar] : List<Bar>) {
spend() { todo }
}
validator((Baz, Baz) as x) {
fn baz() { todo }
validator foo((Baz, Baz) as x) {
mint() { todo }
}
validator((fst, snd) as x: Pair<Int, Int>) {
fn fiz() { todo }
validator fiz((fst, snd) as x: Pair<Int, Int>) {
spend() { todo }
}
"#
);
@ -1140,3 +1140,110 @@ fn format_long_pair() {
"#
);
}
#[test]
fn format_validator_exhaustive_handlers() {
assert_format!(
r#"
validator foo {
mint(_redeemer, _policy_id, _self) {
True
}
spend(_datum, _redeemer, _policy_id, _self) {
True
}
withdraw(_redeemer, _account, _self) {
True
}
publish(_redeemer, _certificate, _self) {
True
}
vote(_redeemer, _voter, _self) {
True
}
propose(_redeemer, _proposal, _self) {
True
}
}
"#
);
}
#[test]
fn format_validator_exhaustive_handlers_extra_default_fallback() {
assert_format!(
r#"
validator foo {
mint(_redeemer, _policy_id, _self) {
True
}
spend(_datum, _redeemer, _policy_id, _self) {
True
}
withdraw(_redeemer, _account, _self) {
True
}
publish(_redeemer, _certificate, _self) {
True
}
vote(_redeemer, _voter, _self) {
True
}
propose(_redeemer, _proposal, _self) {
True
}
else(_) {
fail
}
}
"#
);
}
#[test]
fn format_validator_exhaustive_handlers_extra_non_default_fallback() {
assert_format!(
r#"
validator foo {
mint(_redeemer, _policy_id, _self) {
True
}
spend(_datum, _redeemer, _policy_id, _self) {
True
}
withdraw(_redeemer, _account, _self) {
True
}
publish(_redeemer, _certificate, _self) {
True
}
vote(_redeemer, _voter, _self) {
True
}
propose(_redeemer, _proposal, _self) {
True
}
else(_) {
True
}
}
"#
);
}

View File

@ -1,14 +1,18 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\n validator ( param1 : ByteArray ) {\n fn foo (d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {\n True\n }\n /// This is bar\nfn bar(r: Redeemer, ctx : ScriptContext ) -> Bool { True }\n }\n"
description: "Code:\n\n validator foo( param1 : ByteArray ) {\n spend(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {\n True\n }\n /// This is bar\nmint(r: Redeemer, ctx : ScriptContext ) -> Bool { True }\n }\n"
---
validator(param1: ByteArray) {
fn foo(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {
validator foo(param1: ByteArray) {
spend(d: Datum, r: Redeemer, ctx: ScriptContext) {
True
}
/// This is bar
fn bar(r: Redeemer, ctx: ScriptContext) -> Bool {
mint(r: Redeemer, ctx: ScriptContext) {
True
}
else(_) {
fail
}
}

View File

@ -1,14 +1,18 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\n validator ( param1 : ByteArray ) {\n pub fn foo (d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {\n True\n }\n /// This is bar\npub fn bar(r: Redeemer, ctx : ScriptContext ) -> Bool { True }\n }\n"
description: "Code:\n\n validator foo ( param1 : ByteArray ) {\n spend(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {\n True\n }\n /// This is bar\nmint(r: Redeemer, ctx : ScriptContext ) -> Bool { True }\n }\n"
---
validator(param1: ByteArray) {
pub fn foo(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {
validator foo(param1: ByteArray) {
spend(d: Datum, r: Redeemer, ctx: ScriptContext) {
True
}
/// This is bar
pub fn bar(r: Redeemer, ctx: ScriptContext) -> Bool {
mint(r: Redeemer, ctx: ScriptContext) {
True
}
else(_) {
fail
}
}

View File

@ -1,21 +1,29 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\nvalidator ( ) {\n// What is the purpose of life\n\nfn foo (d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {\nTrue\n}\n}\n\n// What?\nvalidator {\n /// Some documentation for foo\n fn foo() {\n Void\n }\n\n // I am lost\n}\n"
description: "Code:\n\nvalidator thing ( ) {\n// What is the purpose of life\n\nspend(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {\nTrue\n}\n}\n\n// What?\nvalidator foo {\n /// Some documentation for foo\n foo() {\n Void\n }\n\n // I am lost\n}\n"
---
validator {
validator thing {
// What is the purpose of life
fn foo(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool {
spend(d: Datum, r: Redeemer, ctx: ScriptContext) {
True
}
else(_) {
fail
}
}
// What?
validator {
validator foo {
/// Some documentation for foo
fn foo() {
foo() {
Void
}
else(_) {
fail
}
// I am lost
}

View File

@ -0,0 +1,29 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\nvalidator foo {\n mint(_redeemer, _policy_id, _self) {\n True\n }\n\n spend(_datum, _redeemer, _policy_id, _self) {\n True\n }\n\n withdraw(_redeemer, _account, _self) {\n True\n }\n\n publish(_redeemer, _certificate, _self) {\n True\n }\n\n vote(_redeemer, _voter, _self) {\n True\n }\n\n propose(_redeemer, _proposal, _self) {\n True\n }\n}\n"
---
validator foo {
mint(_redeemer, _policy_id, _self) {
True
}
spend(_datum, _redeemer, _policy_id, _self) {
True
}
withdraw(_redeemer, _account, _self) {
True
}
publish(_redeemer, _certificate, _self) {
True
}
vote(_redeemer, _voter, _self) {
True
}
propose(_redeemer, _proposal, _self) {
True
}
}

View File

@ -0,0 +1,29 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\nvalidator foo {\n mint(_redeemer, _policy_id, _self) {\n True\n }\n\n spend(_datum, _redeemer, _policy_id, _self) {\n True\n }\n\n withdraw(_redeemer, _account, _self) {\n True\n }\n\n publish(_redeemer, _certificate, _self) {\n True\n }\n\n vote(_redeemer, _voter, _self) {\n True\n }\n\n propose(_redeemer, _proposal, _self) {\n True\n }\n\n else(_) {\n fail\n }\n}\n"
---
validator foo {
mint(_redeemer, _policy_id, _self) {
True
}
spend(_datum, _redeemer, _policy_id, _self) {
True
}
withdraw(_redeemer, _account, _self) {
True
}
publish(_redeemer, _certificate, _self) {
True
}
vote(_redeemer, _voter, _self) {
True
}
propose(_redeemer, _proposal, _self) {
True
}
}

View File

@ -0,0 +1,33 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\nvalidator foo {\n mint(_redeemer, _policy_id, _self) {\n True\n }\n\n spend(_datum, _redeemer, _policy_id, _self) {\n True\n }\n\n withdraw(_redeemer, _account, _self) {\n True\n }\n\n publish(_redeemer, _certificate, _self) {\n True\n }\n\n vote(_redeemer, _voter, _self) {\n True\n }\n\n propose(_redeemer, _proposal, _self) {\n True\n }\n\n else(_) {\n True\n }\n}\n"
---
validator foo {
mint(_redeemer, _policy_id, _self) {
True
}
spend(_datum, _redeemer, _policy_id, _self) {
True
}
withdraw(_redeemer, _account, _self) {
True
}
publish(_redeemer, _certificate, _self) {
True
}
vote(_redeemer, _voter, _self) {
True
}
propose(_redeemer, _proposal, _self) {
True
}
else(_) {
True
}
}

View File

@ -1,27 +1,43 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\nvalidator(Foo { a, b, .. }) {\n fn foo() { todo }\n}\n\nvalidator([Bar] : List<Bar>) {\n fn bar() { todo }\n}\n\nvalidator((Baz, Baz) as x) {\n fn baz() { todo }\n}\n\nvalidator((fst, snd) as x: Pair<Int, Int>) {\n fn fiz() { todo }\n}\n"
description: "Code:\n\nvalidator foo(Foo { a, b, .. }) {\n spend() { todo }\n}\n\nvalidator foo([Bar] : List<Bar>) {\n spend() { todo }\n}\n\nvalidator foo((Baz, Baz) as x) {\n mint() { todo }\n}\n\nvalidator fiz((fst, snd) as x: Pair<Int, Int>) {\n spend() { todo }\n}\n"
---
validator(Foo { a, b, .. }) {
fn foo() {
validator foo(Foo { a, b, .. }) {
spend() {
todo
}
else(_) {
fail
}
}
validator([Bar]: List<Bar>) {
fn bar() {
validator foo([Bar]: List<Bar>) {
spend() {
todo
}
else(_) {
fail
}
}
validator((Baz, Baz) as x) {
fn baz() {
validator foo((Baz, Baz) as x) {
mint() {
todo
}
else(_) {
fail
}
}
validator((fst, snd) as x: Pair<Int, Int>) {
fn fiz() {
validator fiz((fst, snd) as x: Pair<Int, Int>) {
spend() {
todo
}
else(_) {
fail
}
}

View File

@ -1,10 +1,9 @@
use self::{environment::Environment, pretty::Printer};
use crate::{
ast::{
Annotation, Constant, DataType, DataTypeKey, DefinitionLocation, ModuleKind, Span,
TypedDataType,
well_known, Annotation, Constant, DataType, DataTypeKey, DefinitionLocation, ModuleKind,
Span, TypedDataType,
},
builtins::{G1_ELEMENT, G2_ELEMENT, MILLER_LOOP_RESULT},
tipo::fields::FieldMap,
};
use indexmap::IndexMap;
@ -23,6 +22,8 @@ mod pattern;
mod pipe;
pub mod pretty;
pub use environment::collapse_links;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TypeAliasAnnotation {
pub alias: String,
@ -304,7 +305,7 @@ impl Type {
pub fn is_int(&self) -> bool {
match self {
Self::App { module, name, .. } if "Int" == name && module.is_empty() => true,
Self::App { module, name, .. } if well_known::INT == name && module.is_empty() => true,
Self::Var { tipo, .. } => tipo.borrow().is_int(),
_ => false,
}
@ -312,7 +313,11 @@ impl Type {
pub fn is_bytearray(&self) -> bool {
match self {
Self::App { module, name, .. } if "ByteArray" == name && module.is_empty() => true,
Self::App { module, name, .. }
if well_known::BYTE_ARRAY == name && module.is_empty() =>
{
true
}
Self::Var { tipo, .. } => tipo.borrow().is_bytearray(),
_ => false,
}
@ -320,7 +325,7 @@ impl Type {
pub fn is_bls381_12_g1(&self) -> bool {
match self {
Self::App { module, name, .. } => G1_ELEMENT == name && module.is_empty(),
Self::App { module, name, .. } => well_known::G1_ELEMENT == name && module.is_empty(),
Self::Var { tipo, .. } => tipo.borrow().is_bls381_12_g1(),
_ => false,
@ -329,7 +334,7 @@ impl Type {
pub fn is_bls381_12_g2(&self) -> bool {
match self {
Self::App { module, name, .. } => G2_ELEMENT == name && module.is_empty(),
Self::App { module, name, .. } => well_known::G2_ELEMENT == name && module.is_empty(),
Self::Var { tipo, .. } => tipo.borrow().is_bls381_12_g2(),
_ => false,
@ -338,7 +343,9 @@ impl Type {
pub fn is_ml_result(&self) -> bool {
match self {
Self::App { module, name, .. } => MILLER_LOOP_RESULT == name && module.is_empty(),
Self::App { module, name, .. } => {
well_known::MILLER_LOOP_RESULT == name && module.is_empty()
}
Self::Var { tipo, .. } => tipo.borrow().is_ml_result(),
_ => false,
@ -422,31 +429,33 @@ impl Type {
}
pub fn is_generic(&self) -> bool {
match self {
Self::App { args, .. } => {
let mut is_a_generic = false;
for arg in args {
is_a_generic = is_a_generic || arg.is_generic();
}
is_a_generic
}
!self.collect_generics().is_empty()
}
Self::Var { tipo, .. } => tipo.borrow().is_generic(),
Self::Tuple { elems, .. } => {
let mut is_a_generic = false;
for elem in elems {
is_a_generic = is_a_generic || elem.is_generic();
pub fn collect_generics(&self) -> Vec<Rc<Type>> {
match self {
Self::App { args, .. } => args.iter().flat_map(|arg| arg.collect_generics()).collect(),
Self::Var { tipo, .. } => {
if tipo.borrow().is_generic() {
vec![self.clone().into()]
} else {
Vec::new()
}
is_a_generic
}
Self::Fn { args, ret, .. } => {
let mut is_a_generic = false;
for arg in args {
is_a_generic = is_a_generic || arg.is_generic();
}
is_a_generic || ret.is_generic()
Self::Tuple { elems, .. } => elems
.iter()
.flat_map(|arg| arg.collect_generics())
.collect(),
Self::Fn { args, ret, .. } => args
.iter()
.chain(std::iter::once(ret))
.flat_map(|arg| arg.collect_generics())
.collect(),
Self::Pair { fst, snd, .. } => {
let mut generics = fst.collect_generics();
generics.extend(snd.collect_generics());
generics
}
Self::Pair { fst, snd, .. } => fst.is_generic() || snd.is_generic(),
}
}
@ -1068,7 +1077,7 @@ impl TypeVar {
match self {
TypeVar::Generic { .. } => true,
TypeVar::Link { tipo } => tipo.is_generic(),
_ => false,
TypeVar::Unbound { .. } => false,
}
}
@ -1118,6 +1127,51 @@ impl ValueConstructor {
}
}
pub fn known_enum(
values: &mut HashMap<String, Self>,
tipo: Rc<Type>,
constructors: &[&str],
) -> Vec<String> {
for constructor in constructors {
values.insert(
constructor.to_string(),
ValueConstructor::public(
tipo.clone(),
ValueConstructorVariant::known_enum_variant(constructor, constructors.len(), 0),
),
);
}
constructors
.iter()
.map(|constructor| constructor.to_string())
.collect()
}
pub fn known_adt(
values: &mut HashMap<String, Self>,
constructors: &[(&str, Rc<Type>)],
) -> Vec<String> {
for (constructor, tipo) in constructors {
values.insert(
constructor.to_string(),
ValueConstructor::public(
tipo.clone(),
ValueConstructorVariant::known_enum_variant(
constructor,
constructors.len(),
tipo.fn_arity().unwrap_or(0),
),
),
);
}
constructors
.iter()
.map(|(constructor, _)| constructor.to_string())
.collect()
}
fn field_map(&self) -> Option<&FieldMap> {
match &self.variant {
ValueConstructorVariant::ModuleFn { field_map, .. }
@ -1248,6 +1302,17 @@ impl ValueConstructorVariant {
pub fn is_local_variable(&self) -> bool {
matches!(self, Self::LocalVariable { .. })
}
pub fn known_enum_variant(name: &str, constructors_count: usize, arity: usize) -> Self {
ValueConstructorVariant::Record {
module: "".into(),
name: name.to_string(),
field_map: None::<FieldMap>,
arity,
location: Span::empty(),
constructors_count: constructors_count as u16,
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
@ -1271,6 +1336,18 @@ pub struct TypeConstructor {
pub tipo: Rc<Type>,
}
impl TypeConstructor {
pub fn primitive(tipo: Rc<Type>) -> Self {
TypeConstructor {
location: Span::empty(),
parameters: tipo.collect_generics(),
tipo,
module: "".to_string(),
public: true,
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct AccessorsMap {
pub public: bool,

View File

@ -9,10 +9,9 @@ use crate::{
ast::{
self, Annotation, CallArg, DataType, Definition, Function, ModuleConstant, ModuleKind,
RecordConstructor, RecordConstructorArg, Span, TypeAlias, TypedDefinition, TypedFunction,
TypedPattern, UnqualifiedImport, UntypedArg, UntypedDefinition, UntypedFunction, Use,
Validator, PIPE_VARIABLE,
TypedPattern, TypedValidator, UnqualifiedImport, UntypedArg, UntypedDefinition,
UntypedFunction, Use, Validator, PIPE_VARIABLE,
},
builtins::{function, generic_var, pair, tuple, unbound_var},
tipo::{fields::FieldMap, TypeAliasAnnotation},
IdGenerator,
};
@ -58,6 +57,9 @@ pub struct Environment<'a> {
/// Top-level function definitions from the module
pub module_functions: HashMap<String, &'a UntypedFunction>,
/// Top-level validator definitions from the module
pub module_validators: HashMap<String, (Span, Vec<String>)>,
/// Top-level functions that have been inferred
pub inferred_functions: HashMap<String, TypedFunction>,
@ -183,7 +185,7 @@ impl<'a> Environment<'a> {
if let Some((args, ret)) = new_value {
*tipo.borrow_mut() = TypeVar::Link {
tipo: function(args.clone(), ret.clone()),
tipo: Type::function(args.clone(), ret.clone()),
};
return Ok((args, Type::with_alias(ret, alias.clone())));
@ -307,32 +309,51 @@ impl<'a> Environment<'a> {
Definition::Validator(Validator {
doc,
end_position,
fun,
other_fun,
handlers,
name,
mut fallback,
location,
params,
}) => {
let Definition::Fn(fun) =
self.generalise_definition(Definition::Fn(fun), module_name)
let handlers = handlers
.into_iter()
.map(|mut fun| {
let handler_name = TypedValidator::handler_name(&name, &fun.name);
let old_name = fun.name;
fun.name = handler_name;
let Definition::Fn(mut fun) =
self.generalise_definition(Definition::Fn(fun), module_name)
else {
unreachable!()
};
fun.name = old_name;
fun
})
.collect();
let fallback_name = TypedValidator::handler_name(&name, &fallback.name);
let old_name = fallback.name;
fallback.name = fallback_name;
let Definition::Fn(mut fallback) =
self.generalise_definition(Definition::Fn(fallback), module_name)
else {
unreachable!()
};
let other_fun = other_fun.map(|other_fun| {
let Definition::Fn(other_fun) =
self.generalise_definition(Definition::Fn(other_fun), module_name)
else {
unreachable!()
};
other_fun
});
fallback.name = old_name;
Definition::Validator(Validator {
doc,
name,
end_position,
fun,
other_fun,
handlers,
fallback,
location,
params,
})
@ -671,7 +692,7 @@ impl<'a> Environment<'a> {
}
Type::Fn { args, ret, alias } => Type::with_alias(
function(
Type::function(
args.iter()
.map(|t| self.instantiate(t.clone(), ids, hydrator))
.collect(),
@ -681,7 +702,7 @@ impl<'a> Environment<'a> {
),
Type::Tuple { elems, alias } => Type::with_alias(
tuple(
Type::tuple(
elems
.iter()
.map(|t| self.instantiate(t.clone(), ids, hydrator))
@ -690,7 +711,7 @@ impl<'a> Environment<'a> {
alias.clone(),
),
Type::Pair { fst, snd, alias } => Type::with_alias(
pair(
Type::pair(
self.instantiate(fst.clone(), ids, hydrator),
self.instantiate(snd.clone(), ids, hydrator),
),
@ -758,6 +779,7 @@ impl<'a> Environment<'a> {
module_types_constructors: prelude.types_constructors.clone(),
module_values: HashMap::new(),
module_functions: HashMap::new(),
module_validators: HashMap::new(),
imported_modules: HashMap::new(),
unused_modules: HashMap::new(),
unqualified_imported_names: HashMap::new(),
@ -776,13 +798,13 @@ impl<'a> Environment<'a> {
/// Create a new generic type that can stand in for any type.
pub fn new_generic_var(&mut self) -> Rc<Type> {
generic_var(self.next_uid())
Type::generic_var(self.next_uid())
}
/// Create a new unbound type that is a specific type, we just don't
/// know which one yet.
pub fn new_unbound_var(&mut self) -> Rc<Type> {
unbound_var(self.next_uid())
Type::unbound_var(self.next_uid())
}
pub fn next_uid(&mut self) -> u64 {
@ -815,9 +837,7 @@ impl<'a> Environment<'a> {
let module_info = self.find_module(module, *location)?;
if module_info.kind.is_validator()
&& (self.current_kind.is_lib()
|| self.current_kind.is_env()
|| !self.current_module.starts_with("tests"))
&& (self.current_kind.is_lib() || self.current_kind.is_env())
{
return Err(Error::ValidatorImported {
location: *location,
@ -1162,12 +1182,12 @@ impl<'a> Environment<'a> {
#[allow(clippy::too_many_arguments)]
fn register_function(
&mut self,
name: &'a str,
name: &str,
arguments: &[UntypedArg],
return_annotation: &Option<Annotation>,
module_name: &String,
hydrators: &mut HashMap<String, Hydrator>,
names: &mut HashMap<&'a str, &'a Span>,
names: &mut HashMap<String, &'a Span>,
location: &'a Span,
) -> Result<(), Error> {
assert_unique_value_name(names, name, location)?;
@ -1195,7 +1215,7 @@ impl<'a> Environment<'a> {
let return_type = hydrator.type_from_option_annotation(return_annotation, self)?;
let tipo = function(arg_types, return_type);
let tipo = Type::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
@ -1224,7 +1244,7 @@ impl<'a> Environment<'a> {
def: &'a UntypedDefinition,
module_name: &String,
hydrators: &mut HashMap<String, Hydrator>,
names: &mut HashMap<&'a str, &'a Span>,
names: &mut HashMap<String, &'a Span>,
kind: ModuleKind,
) -> Result<(), Error> {
match def {
@ -1247,58 +1267,104 @@ impl<'a> Environment<'a> {
}
Definition::Validator(Validator {
fun,
other_fun,
handlers,
fallback,
params,
name,
doc: _,
location: _,
location,
end_position: _,
}) if kind.is_validator() => {
let default_annotation = |mut arg: UntypedArg| {
let default_annotation = |mut arg: UntypedArg, ann: Annotation| {
if arg.annotation.is_none() {
arg.annotation = Some(Annotation::data(arg.location));
arg.annotation = Some(ann);
arg
} else {
arg
}
};
let temp_params: Vec<UntypedArg> = params
.iter()
.cloned()
.chain(fun.arguments.clone())
.map(default_annotation)
.collect();
let mut handler_names = vec![];
self.register_function(
&fun.name,
&temp_params,
&fun.return_annotation,
module_name,
hydrators,
names,
&fun.location,
)?;
let params_len = params.len();
if let Some(other) = other_fun {
for handler in handlers {
let temp_params: Vec<UntypedArg> = params
.iter()
.cloned()
.chain(other.arguments.clone())
.map(default_annotation)
.chain(handler.arguments.clone())
.enumerate()
.map(|(ix, arg)| {
let is_datum = handler.is_spend() && ix == params_len;
let is_mint_policy = handler.is_mint() && ix == params_len + 1;
let location = arg.location;
default_annotation(
arg,
if is_datum {
Annotation::option(Annotation::data(location))
} else if is_mint_policy {
Annotation::bytearray(location)
} else {
Annotation::data(location)
},
)
})
.collect();
handler_names.push(handler.name.clone());
self.register_function(
&other.name,
&TypedValidator::handler_name(name.as_str(), handler.name.as_str()),
&temp_params,
&other.return_annotation,
&handler.return_annotation,
module_name,
hydrators,
names,
&other.location,
&handler.location,
)?;
}
let temp_params: Vec<UntypedArg> = params
.iter()
.cloned()
.chain(fallback.arguments.clone())
.map(|arg| {
let location = arg.location;
default_annotation(arg, Annotation::data(location))
})
.collect();
self.register_function(
&TypedValidator::handler_name(name.as_str(), fallback.name.as_str()),
&temp_params,
&fallback.return_annotation,
module_name,
hydrators,
names,
&fallback.location,
)?;
handler_names.push(fallback.name.clone());
let err_duplicate_name = |previous_location: Span| {
Err(Error::DuplicateName {
name: name.to_string(),
previous_location,
location: location.map_end(|end| end + 1 + name.len()),
})
};
if let Some((previous_location, _)) = self.imported_modules.get(name) {
return err_duplicate_name(*previous_location);
}
match self
.module_validators
.insert(name.to_string(), (*location, handler_names))
{
Some((previous_location, _)) => err_duplicate_name(previous_location),
None => Ok(()),
}?
}
Definition::Validator(Validator { location, .. }) => {
@ -1395,7 +1461,7 @@ impl<'a> Environment<'a> {
// Insert constructor function into module scope
let typ = match constructor.arguments.len() {
0 => typ.clone(),
_ => function(args_types, typ.clone()),
_ => Type::function(args_types, typ.clone()),
};
let constructor_info = ValueConstructorVariant::Record {
@ -1910,11 +1976,11 @@ fn assert_unique_type_name<'a>(
}
fn assert_unique_value_name<'a>(
names: &mut HashMap<&'a str, &'a Span>,
name: &'a str,
names: &mut HashMap<String, &'a Span>,
name: &str,
location: &'a Span,
) -> Result<(), Error> {
match names.insert(name, location) {
match names.insert(name.to_string(), location) {
Some(previous_location) => Err(Error::DuplicateName {
name: name.to_string(),
previous_location: *previous_location,
@ -1925,11 +1991,11 @@ fn assert_unique_value_name<'a>(
}
fn assert_unique_const_name<'a>(
names: &mut HashMap<&'a str, &'a Span>,
name: &'a str,
names: &mut HashMap<String, &'a Span>,
name: &str,
location: &'a Span,
) -> Result<(), Error> {
match names.insert(name, location) {
match names.insert(name.to_string(), location) {
Some(previous_location) => Err(Error::DuplicateConstName {
name: name.to_string(),
previous_location: *previous_location,
@ -1948,7 +2014,7 @@ pub(super) fn assert_no_labeled_arguments<A>(args: &[CallArg<A>]) -> Option<(Spa
None
}
pub(super) fn collapse_links(t: Rc<Type>) -> Rc<Type> {
pub fn collapse_links(t: Rc<Type>) -> Rc<Type> {
if let Type::Var { tipo, alias } = t.deref() {
if let TypeVar::Link { tipo } = tipo.borrow().deref() {
return Type::with_alias(tipo.clone(), alias.clone());
@ -1993,7 +2059,7 @@ pub(crate) fn generalise(t: Rc<Type>, ctx_level: usize) -> Rc<Type> {
match t.deref() {
Type::Var { tipo, alias } => Type::with_alias(
match tipo.borrow().deref() {
TypeVar::Unbound { id } => generic_var(*id),
TypeVar::Unbound { id } => Type::generic_var(*id),
TypeVar::Link { tipo } => generalise(tipo.clone(), ctx_level),
TypeVar::Generic { .. } => Rc::new(Type::Var {
tipo: tipo.clone(),
@ -2027,7 +2093,7 @@ pub(crate) fn generalise(t: Rc<Type>, ctx_level: usize) -> Rc<Type> {
}
Type::Fn { args, ret, alias } => Type::with_alias(
function(
Type::function(
args.iter()
.map(|t| generalise(t.clone(), ctx_level))
.collect(),
@ -2037,7 +2103,7 @@ pub(crate) fn generalise(t: Rc<Type>, ctx_level: usize) -> Rc<Type> {
),
Type::Tuple { elems, alias } => Type::with_alias(
tuple(
Type::tuple(
elems
.iter()
.map(|t| generalise(t.clone(), ctx_level))
@ -2046,7 +2112,7 @@ pub(crate) fn generalise(t: Rc<Type>, ctx_level: usize) -> Rc<Type> {
alias.clone(),
),
Type::Pair { fst, snd, alias } => Type::with_alias(
pair(
Type::pair(
generalise(fst.clone(), ctx_level),
generalise(snd.clone(), ctx_level),
),

View File

@ -8,6 +8,7 @@ use crate::{
pretty::Documentable,
};
use indoc::formatdoc;
use itertools::Itertools;
use miette::{Diagnostic, LabeledSpan};
use ordinal::Ordinal;
use owo_colors::{
@ -489,15 +490,21 @@ 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)]
#[error(
"I stumbled upon an invalid (non-local) clause guard '{}'.\n",
name.if_supports_color(Stdout, |s| s.purple())
)]
#[diagnostic(url(
"https://aiken-lang.org/language-tour/control-flow#checking-equality-and-ordering-in-patterns"
))]
#[diagnostic(code("illegal::clause_guard"))]
#[diagnostic(help(
"There are some conditions regarding what can be used in a guard. Values must be either local to the function, or defined as module constants. You can't use functions or records in there."
))]
NonLocalClauseGuardVariable {
#[label]
location: Span,
#[label("and {} here", count)]
other_location: Span,
count: usize,
name: String,
},
#[error("I tripped over an attempt to access elements on something that isn't indexable.\n")]
@ -1021,7 +1028,8 @@ The best thing to do from here is to remove it."#))]
))]
IncorrectValidatorArity {
count: u32,
#[label("{} arguments", if *count < 2 { "not enough" } else { "too many" })]
expected: u32,
#[label("{} arguments", if count < expected { "not enough" } else { "too many" })]
location: Span,
},
@ -1063,6 +1071,46 @@ The best thing to do from here is to remove it."#))]
function: UntypedFunction,
location: Span,
},
#[error("I found a validator handler referring to an unknown purpose.\n")]
#[diagnostic(code("unknown::purpose"))]
#[diagnostic(help(
"Handler must be named after a known purpose. Here is a list of available purposes:\n{}",
available_purposes
.iter()
.map(|p| format!("-> {}", p.if_supports_color(Stdout, |s| s.green())))
.join("\n")
))]
UnknownPurpose {
#[label("unknown purpose")]
location: Span,
available_purposes: Vec<String>,
},
#[error("I could not find an appropriate handler in the validator definition\n")]
#[diagnostic(code("unknown::handler"))]
#[diagnostic(help(
"When referring to a validator handler via record access, you must refer to one of the declared handlers:\n{}",
available_handlers
.iter()
.map(|p| format!("-> {}", p.if_supports_color(Stdout, |s| s.green())))
.join("\n")
))]
UnknownValidatorHandler {
#[label("unknown validator handler")]
location: Span,
available_handlers: Vec<String>,
},
#[error("I caught an extraneous fallback handler in an already exhaustive validator\n")]
#[diagnostic(code("extraneous::fallback"))]
#[diagnostic(help(
"Validator handlers must be exhaustive and either cover all purposes, or provide a fallback handler. Here, you have successfully covered all script purposes with your handler, but left an extraneous fallback branch. I cannot let that happen, but removing it for you would probably be deemed rude. So please, remove the fallback."
))]
UnexpectedValidatorFallback {
#[label("redundant fallback handler")]
fallback: Span,
},
}
impl ExtraData for Error {
@ -1093,7 +1141,7 @@ impl ExtraData for Error {
| Error::LastExpressionIsAssignment { .. }
| Error::LogicalOpChainMissingExpr { .. }
| Error::MissingVarInAlternativePattern { .. }
| Error::MultiValidatorEqualArgs { .. }
| Error::NonLocalClauseGuardVariable { .. }
| Error::NotIndexable { .. }
| Error::NotExhaustivePatternMatch { .. }
| Error::NotFn { .. }
@ -1122,6 +1170,9 @@ impl ExtraData for Error {
| Error::UnexpectedMultiPatternAssignment { .. }
| Error::ExpectOnOpaqueType { .. }
| Error::ValidatorMustReturnBool { .. }
| Error::UnknownPurpose { .. }
| Error::UnknownValidatorHandler { .. }
| Error::UnexpectedValidatorFallback { .. }
| Error::MustInferFirst { .. } => None,
Error::UnknownType { name, .. }

View File

@ -1,7 +1,6 @@
use crate::{
ast,
builtins::{self},
tipo::{self, environment::Environment, error::Error},
tipo::{self, environment::Environment, error::Error, Type},
};
use itertools::Itertools;
use std::{collections::BTreeMap, iter, ops::Deref};
@ -500,8 +499,8 @@ fn pretty_tail(tail: Pattern) -> String {
}
fn list_constructors() -> Vec<tipo::ValueConstructor> {
let list_parameter = builtins::generic_var(0);
let list_type = builtins::list(list_parameter);
let list_parameter = Type::generic_var(0);
let list_type = Type::list(list_parameter);
vec![
tipo::ValueConstructor {

View File

@ -14,13 +14,10 @@ use crate::{
ByteArrayFormatPreference, CallArg, Constant, Curve, Function, IfBranch,
LogicalOpChainKind, Pattern, RecordUpdateSpread, Span, TraceKind, TraceLevel, Tracing,
TypedArg, TypedCallArg, TypedClause, TypedIfBranch, TypedPattern, TypedRecordUpdateArg,
UnOp, UntypedArg, UntypedAssignmentKind, UntypedClause, UntypedFunction, UntypedIfBranch,
UntypedPattern, UntypedRecordUpdateArg,
},
builtins::{
bool, byte_array, data, from_default_function, function, g1_element, g2_element, int, list,
pair, string, tuple, void, BUILTIN,
TypedValidator, UnOp, UntypedArg, UntypedAssignmentKind, UntypedClause, UntypedFunction,
UntypedIfBranch, UntypedPattern, UntypedRecordUpdateArg,
},
builtins::{from_default_function, BUILTIN},
expr::{FnStyle, TypedExpr, UntypedExpr},
format,
tipo::{fields::FieldMap, DefaultFunction, PatternConstructor, TypeVar},
@ -163,7 +160,7 @@ pub(crate) fn infer_function(
let args_types = arguments.iter().map(|a| a.tipo.clone()).collect();
let tipo = function(args_types, return_type);
let tipo = Type::function(args_types, return_type);
let safe_to_generalise = !expr_typer.ungeneralised_function_used;
@ -594,15 +591,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
Ok(TypedExpr::ByteArray {
location,
bytes,
tipo: byte_array(),
tipo: Type::byte_array(),
})
}
fn infer_curve_point(&mut self, curve: Curve, location: Span) -> Result<TypedExpr, Error> {
let tipo = match curve {
Curve::Bls12_381(point) => match point {
Bls12_381Point::G1(_) => g1_element(),
Bls12_381Point::G2(_) => g2_element(),
Bls12_381Point::G1(_) => Type::g1_element(),
Bls12_381Point::G2(_) => Type::g2_element(),
},
};
@ -631,7 +628,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
module: String::new(),
constructors_count: 2,
},
tipo: bool(),
tipo: Type::bool(),
},
};
@ -648,14 +645,14 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
module: String::new(),
constructors_count: 2,
},
tipo: bool(),
tipo: Type::bool(),
},
};
let text = match self.tracing.trace_level(false) {
TraceLevel::Verbose => Some(TypedExpr::String {
location,
tipo: string(),
tipo: Type::string(),
value: format!(
"{} ? False",
format::Formatter::new()
@ -668,7 +665,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
let typed_value = self.infer(value)?;
self.unify(bool(), typed_value.tipo(), typed_value.location(), false)?;
self.unify(
Type::bool(),
typed_value.tipo(),
typed_value.location(),
false,
)?;
match text {
None => Ok(typed_value),
@ -682,11 +684,11 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
}],
final_else: Box::new(TypedExpr::Trace {
location,
tipo: bool(),
tipo: Type::bool(),
text: Box::new(text),
then: Box::new(var_false),
}),
tipo: bool(),
tipo: Type::bool(),
}),
}
}
@ -714,22 +716,22 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
return Ok(TypedExpr::BinOp {
location,
name,
tipo: bool(),
tipo: Type::bool(),
left: Box::new(left),
right: Box::new(right),
});
}
BinOp::And => (bool(), bool()),
BinOp::Or => (bool(), bool()),
BinOp::LtInt => (int(), bool()),
BinOp::LtEqInt => (int(), bool()),
BinOp::GtEqInt => (int(), bool()),
BinOp::GtInt => (int(), bool()),
BinOp::AddInt => (int(), int()),
BinOp::SubInt => (int(), int()),
BinOp::MultInt => (int(), int()),
BinOp::DivInt => (int(), int()),
BinOp::ModInt => (int(), int()),
BinOp::And => (Type::bool(), Type::bool()),
BinOp::Or => (Type::bool(), Type::bool()),
BinOp::LtInt => (Type::int(), Type::bool()),
BinOp::LtEqInt => (Type::int(), Type::bool()),
BinOp::GtEqInt => (Type::int(), Type::bool()),
BinOp::GtInt => (Type::int(), Type::bool()),
BinOp::AddInt => (Type::int(), Type::int()),
BinOp::SubInt => (Type::int(), Type::int()),
BinOp::MultInt => (Type::int(), Type::int()),
BinOp::DivInt => (Type::int(), Type::int()),
BinOp::ModInt => (Type::int(), Type::int()),
};
let left = self.infer(left)?;
@ -896,8 +898,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
let value = self.infer(value)?;
let tipo = match op {
UnOp::Not => bool(),
UnOp::Negate => int(),
UnOp::Not => Type::bool(),
UnOp::Negate => Type::int(),
};
self.unify(tipo.clone(), value.tipo(), value.location(), false)?;
@ -916,6 +918,28 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
label: String,
access_location: Span,
) -> Result<TypedExpr, Error> {
if let UntypedExpr::Var { ref name, location } = container {
if let Some((_, available_handlers)) = self
.environment
.module_validators
.get(name.as_str())
.cloned()
{
return self
.infer_var(
TypedValidator::handler_name(name.as_str(), label.as_str()),
location,
)
.map_err(|err| match err {
Error::UnknownVariable { .. } => Error::UnknownValidatorHandler {
location: access_location.map(|_start, end| (location.end, end)),
available_handlers,
},
_ => err,
});
}
}
// Attempt to infer the container as a record access. If that fails, we may be shadowing the name
// of an imported module, so attempt to infer the container as a module access.
// TODO: Remove this cloning
@ -1603,7 +1627,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
let condition = self.infer(branch.condition.clone())?;
self.unify(
bool(),
Type::bool(),
condition.tipo(),
condition.type_defining_location(),
false,
@ -1647,7 +1671,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
let args_types = args.iter().map(|a| a.tipo.clone()).collect();
let tipo = function(args_types, return_type);
let tipo = Type::function(args_types, return_type);
Ok(TypedExpr::Fn {
location,
@ -1745,7 +1769,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
TypedExpr::UInt {
location,
value,
tipo: int(),
tipo: Type::int(),
}
}
@ -1772,7 +1796,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
ensure_serialisable(false, tipo.clone(), location)?;
// Type check the ..tail, if there is one
let tipo = list(tipo);
let tipo = Type::list(tipo);
let tail = match tail {
Some(tail) => {
@ -1807,7 +1831,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
let typed_expression = self.infer(expression)?;
self.unify(
bool(),
Type::bool(),
typed_expression.tipo(),
typed_expression.location(),
false,
@ -1831,7 +1855,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
.rev()
.reduce(|acc, typed_expression| TypedExpr::BinOp {
location,
tipo: bool(),
tipo: Type::bool(),
name,
left: typed_expression.into(),
right: acc.into(),
@ -2151,7 +2175,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
TypedExpr::String {
location,
value,
tipo: string(),
tipo: Type::string(),
}
}
@ -2169,7 +2193,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
Ok(TypedExpr::Pair {
location,
tipo: pair(typed_fst.tipo(), typed_snd.tipo()),
tipo: Type::pair(typed_fst.tipo(), typed_snd.tipo()),
fst: typed_fst.into(),
snd: typed_snd.into(),
})
@ -2187,7 +2211,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
typed_elems.push(typed_elem);
}
let tipo = tuple(typed_elems.iter().map(|e| e.tipo()).collect());
let tipo = Type::tuple(typed_elems.iter().map(|e| e.tipo()).collect());
Ok(TypedExpr::Tuple {
location,
@ -2255,9 +2279,14 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
fn infer_trace_arg(&mut self, arg: UntypedExpr) -> Result<TypedExpr, Error> {
let typed_arg = self.infer(arg)?;
match self.unify(string(), typed_arg.tipo(), typed_arg.location(), false) {
match self.unify(
Type::string(),
typed_arg.tipo(),
typed_arg.location(),
false,
) {
Err(_) => {
self.unify(data(), typed_arg.tipo(), typed_arg.location(), true)?;
self.unify(Type::data(), typed_arg.tipo(), typed_arg.location(), true)?;
Ok(diagnose_expr(typed_arg))
}
Ok(()) => Ok(typed_arg),
@ -2291,7 +2320,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
TraceLevel::Silent => Ok(then),
TraceLevel::Compact => {
let text = self.infer(label)?;
self.unify(string(), text.tipo(), text.location(), false)?;
self.unify(Type::string(), text.tipo(), text.location(), false)?;
Ok(TypedExpr::Trace {
location,
tipo,
@ -2307,7 +2336,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
} else {
let delimiter = |ix| TypedExpr::String {
location: Span::empty(),
tipo: string(),
tipo: Type::string(),
value: if ix == 0 { ": " } else { ", " }.to_string(),
};
typed_arguments
@ -2594,7 +2623,7 @@ fn assert_assignment(expr: TypedExpr) -> Result<TypedExpr, Error> {
if expr.tipo().is_void() {
return Ok(TypedExpr::Assignment {
location: expr.location(),
tipo: void(),
tipo: Type::void(),
value: expr.clone().into(),
pattern: Pattern::Constructor {
is_record: false,
@ -2607,7 +2636,7 @@ fn assert_assignment(expr: TypedExpr) -> Result<TypedExpr, Error> {
arguments: vec![],
module: None,
spread_location: None,
tipo: void(),
tipo: Type::void(),
},
kind: AssignmentKind::let_(),
});
@ -2713,7 +2742,7 @@ fn diagnose_expr(expr: TypedExpr) -> TypedExpr {
name: "diagnostic".to_string(),
constructor: ValueConstructor {
public: true,
tipo: function(vec![data(), byte_array()], byte_array()),
tipo: Type::function(vec![Type::data(), Type::byte_array()], Type::byte_array()),
variant: ValueConstructorVariant::ModuleFn {
name: "diagnostic".to_string(),
field_map: None,
@ -2728,13 +2757,13 @@ fn diagnose_expr(expr: TypedExpr) -> TypedExpr {
let location = expr.location();
TypedExpr::Call {
tipo: string(),
tipo: Type::string(),
fun: Box::new(decode_utf8.clone()),
args: vec![CallArg {
label: None,
location: expr.location(),
value: TypedExpr::Call {
tipo: byte_array(),
tipo: Type::byte_array(),
fun: Box::new(diagnostic.clone()),
args: vec![
CallArg {
@ -2746,7 +2775,7 @@ fn diagnose_expr(expr: TypedExpr) -> TypedExpr {
label: None,
location,
value: TypedExpr::ByteArray {
tipo: byte_array(),
tipo: Type::byte_array(),
bytes: vec![],
location,
},
@ -2785,7 +2814,7 @@ fn append_string_expr(left: TypedExpr, right: TypedExpr) -> TypedExpr {
TypedExpr::Call {
location: Span::empty(),
tipo: string(),
tipo: Type::string(),
fun: Box::new(append_string.clone()),
args: vec![
CallArg {

View File

@ -3,11 +3,7 @@ use super::{
error::{Error, Warning},
Type, TypeConstructor,
};
use crate::{
ast::Annotation,
builtins::{function, pair, tuple},
tipo::Span,
};
use crate::{ast::Annotation, tipo::Span};
use std::{collections::HashMap, rc::Rc};
/// The Hydrator takes an AST representing a type (i.e. a type annotation
@ -201,7 +197,7 @@ impl Hydrator {
let ret = self.do_type_from_annotation(ret, environment, unbounds)?;
Ok(function(args, ret))
Ok(Type::function(args, ret))
}
Annotation::Var { name, location, .. } => match self.created_type_variables.get(name) {
@ -244,13 +240,13 @@ impl Hydrator {
typed_elems.push(typed_elem)
}
Ok(tuple(typed_elems))
Ok(Type::tuple(typed_elems))
}
Annotation::Pair { fst, snd, .. } => {
let fst = self.do_type_from_annotation(fst, environment, unbounds)?;
let snd = self.do_type_from_annotation(snd, environment, unbounds)?;
Ok(pair(fst, snd))
Ok(Type::pair(fst, snd))
}
}?;

View File

@ -9,10 +9,9 @@ use crate::{
ast::{
Annotation, ArgName, ArgVia, DataType, Definition, Function, ModuleConstant, ModuleKind,
RecordConstructor, RecordConstructorArg, Tracing, TypeAlias, TypedArg, TypedDefinition,
TypedFunction, TypedModule, UntypedDefinition, UntypedModule, Use, Validator,
TypedModule, TypedValidator, UntypedArg, UntypedDefinition, UntypedModule,
UntypedValidator, Use, Validator,
},
builtins,
builtins::{fuzzer, generic_var},
tipo::{expr::infer_function, Span, Type, TypeVar},
IdGenerator,
};
@ -172,138 +171,151 @@ fn infer_definition(
doc,
location,
end_position,
mut fun,
other_fun,
handlers,
mut fallback,
params,
name,
}) => {
let params_length = params.len();
let temp_params = params.iter().cloned().chain(fun.arguments);
fun.arguments = temp_params.collect();
environment.in_new_scope(|environment| {
let preregistered_fn = environment
.get_variable(&fun.name)
.expect("Could not find preregistered type for function");
let fallback_name = TypedValidator::handler_name(&name, &fallback.name);
let preregistered_type = preregistered_fn.tipo.clone();
put_params_in_scope(&fallback_name, environment, &params);
let (args_types, _return_type) = preregistered_type
.function_types()
.expect("Preregistered type for fn was not a fn");
let mut typed_handlers = vec![];
for (ix, (arg, t)) in params
.iter()
.zip(args_types[0..params.len()].iter())
.enumerate()
{
match &arg.arg_name(ix) {
ArgName::Named {
name,
label: _,
location: _,
} if arg.is_validator_param => {
environment.insert_variable(
name.to_string(),
ValueConstructorVariant::LocalVariable {
location: arg.location,
},
t.clone(),
);
for mut handler in handlers {
let typed_fun = environment.in_new_scope(|environment| {
let temp_params = params.iter().cloned().chain(handler.arguments);
handler.arguments = temp_params.collect();
environment.init_usage(
name.to_string(),
EntityKind::Variable,
arg.location,
);
}
ArgName::Named { .. } | ArgName::Discarded { .. } => (),
};
}
let handler_name = TypedValidator::handler_name(&name, &handler.name);
let mut typed_fun =
infer_function(&fun, module_name, hydrators, environment, tracing)?;
let old_name = handler.name;
handler.name = handler_name;
if !typed_fun.return_type.is_bool() {
return Err(Error::ValidatorMustReturnBool {
return_type: typed_fun.return_type.clone(),
location: typed_fun.location,
});
}
let mut typed_fun =
infer_function(&handler, module_name, hydrators, environment, tracing)?;
let typed_params = typed_fun
.arguments
.drain(0..params_length)
.map(|mut arg| {
if arg.tipo.is_unbound() {
arg.tipo = builtins::data();
}
typed_fun.name = old_name;
arg
})
.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,
});
}
for arg in typed_fun.arguments.iter_mut() {
if arg.tipo.is_unbound() {
arg.tipo = builtins::data();
}
}
let typed_other_fun = other_fun
.map(|mut other| -> Result<TypedFunction, Error> {
let params = params.into_iter().chain(other.arguments);
other.arguments = params.collect();
let mut other_typed_fun =
infer_function(&other, module_name, hydrators, environment, tracing)?;
if !other_typed_fun.return_type.is_bool() {
if !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,
});
}
if typed_fun.arguments.len() == other_typed_fun.arguments.len() {
return Err(Error::MultiValidatorEqualArgs {
return_type: typed_fun.return_type.clone(),
location: typed_fun.location,
other_location: other_typed_fun.location,
count: other_typed_fun.arguments.len(),
});
}
for arg in other_typed_fun.arguments.iter_mut() {
typed_fun.arguments.drain(0..params_length);
if !typed_fun.has_valid_purpose_name() {
return Err(Error::UnknownPurpose {
location: typed_fun
.location
.map(|start, _end| (start, start + typed_fun.name.len())),
available_purposes: TypedValidator::available_handler_names(),
});
}
if typed_fun.arguments.len() != typed_fun.validator_arity() {
return Err(Error::IncorrectValidatorArity {
count: typed_fun.arguments.len() as u32,
expected: typed_fun.validator_arity() as u32,
location: typed_fun.location,
});
}
if typed_fun.is_spend() && !typed_fun.arguments[0].tipo.is_option() {
return Err(Error::CouldNotUnify {
location: typed_fun.arguments[0].location,
expected: Type::option(typed_fun.arguments[0].tipo.clone()),
given: typed_fun.arguments[0].tipo.clone(),
situation: None,
rigid_type_names: Default::default(),
});
}
for arg in typed_fun.arguments.iter_mut() {
if arg.tipo.is_unbound() {
arg.tipo = builtins::data();
arg.tipo = Type::data();
}
}
Ok(other_typed_fun)
})
.transpose()?;
Ok(typed_fun)
})?;
typed_handlers.push(typed_fun);
}
// NOTE: Duplicates are handled when registering handler names. So if we have N
// typed handlers, they are different. The -1 represents takes out the fallback
// handler name.
let is_exhaustive =
typed_handlers.len() >= TypedValidator::available_handler_names().len() - 1;
if is_exhaustive
&& fallback != UntypedValidator::default_fallback(fallback.location)
{
return Err(Error::UnexpectedValidatorFallback {
fallback: fallback.location,
});
}
let (typed_params, typed_fallback) = environment.in_new_scope(|environment| {
let temp_params = params.iter().cloned().chain(fallback.arguments);
fallback.arguments = temp_params.collect();
let old_name = fallback.name;
fallback.name = fallback_name;
let mut typed_fallback =
infer_function(&fallback, module_name, hydrators, environment, tracing)?;
typed_fallback.name = old_name;
if !typed_fallback.return_type.is_bool() {
return Err(Error::ValidatorMustReturnBool {
return_type: typed_fallback.return_type.clone(),
location: typed_fallback.location,
});
}
let typed_params = typed_fallback
.arguments
.drain(0..params_length)
.map(|mut arg| {
if arg.tipo.is_unbound() {
arg.tipo = Type::data();
}
arg
})
.collect();
if typed_fallback.arguments.len() != 1 {
return Err(Error::IncorrectValidatorArity {
count: typed_fallback.arguments.len() as u32,
expected: 1,
location: typed_fallback.location,
});
}
for arg in typed_fallback.arguments.iter_mut() {
if arg.tipo.is_unbound() {
arg.tipo = Type::data();
}
}
Ok((typed_params, typed_fallback))
})?;
Ok(Definition::Validator(Validator {
doc,
end_position,
fun: typed_fun,
other_fun: typed_other_fun,
handlers: typed_handlers,
fallback: typed_fallback,
name,
location,
params: typed_params,
}))
@ -394,14 +406,14 @@ fn infer_definition(
let is_bool = environment.unify(
typed_f.return_type.clone(),
builtins::bool(),
Type::bool(),
typed_f.location,
false,
);
let is_void = environment.unify(
typed_f.return_type.clone(),
builtins::void(),
Type::void(),
typed_f.location,
false,
);
@ -653,10 +665,10 @@ fn infer_fuzzer(
) -> Result<(Annotation, Rc<Type>), Error> {
let could_not_unify = || Error::CouldNotUnify {
location: *location,
expected: fuzzer(
expected: Type::fuzzer(
expected_inner_type
.clone()
.unwrap_or_else(|| generic_var(0)),
.unwrap_or_else(|| Type::generic_var(0)),
),
given: tipo.clone(),
situation: None,
@ -692,7 +704,7 @@ fn infer_fuzzer(
// `unify` now that we have figured out the type carried by the fuzzer.
environment.unify(
tipo.clone(),
fuzzer(wrapped.clone()),
Type::fuzzer(wrapped.clone()),
*location,
false,
)?;
@ -779,3 +791,40 @@ fn annotate_fuzzer(tipo: &Type, location: &Span) -> Result<Annotation, Error> {
}
}
}
fn put_params_in_scope(name: &str, environment: &mut Environment, params: &[UntypedArg]) {
let preregistered_fn = environment
.get_variable(name)
.expect("Could not find preregistered type for function");
let preregistered_type = preregistered_fn.tipo.clone();
let (args_types, _return_type) = preregistered_type
.function_types()
.expect("Preregistered type for fn was not a fn");
for (ix, (arg, t)) in params
.iter()
.zip(args_types[0..params.len()].iter())
.enumerate()
{
match &arg.arg_name(ix) {
ArgName::Named {
name,
label: _,
location: _,
} if arg.is_validator_param => {
environment.insert_variable(
name.to_string(),
ValueConstructorVariant::LocalVariable {
location: arg.location,
},
t.clone(),
);
environment.init_usage(name.to_string(), EntityKind::Variable, arg.location);
}
ArgName::Named { .. } | ArgName::Discarded { .. } => (),
};
}
}

View File

@ -6,10 +6,7 @@ use super::{
hydrator::Hydrator,
PatternConstructor, Type, ValueConstructorVariant,
};
use crate::{
ast::{CallArg, Pattern, Span, TypedPattern, UntypedPattern},
builtins::{byte_array, int, list, pair, tuple},
};
use crate::ast::{CallArg, Pattern, Span, TypedPattern, UntypedPattern};
use itertools::Itertools;
use std::{
collections::{HashMap, HashSet},
@ -190,7 +187,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
value,
base,
} => {
self.environment.unify(tipo, int(), location, false)?;
self.environment.unify(tipo, Type::int(), location, false)?;
Ok(Pattern::Int {
location,
@ -205,7 +202,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
preferred_format,
} => {
self.environment
.unify(tipo, byte_array(), location, false)?;
.unify(tipo, Type::byte_array(), location, false)?;
Ok(Pattern::ByteArray {
location,
@ -231,7 +228,12 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
.try_collect()?;
let tail = match tail {
Some(tail) => Some(Box::new(self.unify(*tail, list(tipo), None, false)?)),
Some(tail) => Some(Box::new(self.unify(
*tail,
Type::list(tipo),
None,
false,
)?)),
None => None,
};
@ -243,7 +245,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
}
None => Err(Error::CouldNotUnify {
given: list(self.environment.new_unbound_var()),
given: Type::list(self.environment.new_unbound_var()),
expected: tipo.clone(),
situation: None,
location,
@ -267,7 +269,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
let t_snd = self.environment.new_unbound_var();
self.environment.unify(
pair(t_fst.clone(), t_snd.clone()),
Type::pair(t_fst.clone(), t_snd.clone()),
tipo,
location,
false,
@ -280,7 +282,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
}
_ => Err(Error::CouldNotUnify {
given: pair(
given: Type::pair(
self.environment.new_unbound_var(),
self.environment.new_unbound_var(),
),
@ -322,8 +324,12 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
.map(|_| self.environment.new_unbound_var())
.collect();
self.environment
.unify(tuple(elems_types.clone()), tipo, location, false)?;
self.environment.unify(
Type::tuple(elems_types.clone()),
tipo,
location,
false,
)?;
let mut patterns = vec![];
@ -345,7 +351,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
.collect();
Err(Error::CouldNotUnify {
given: tuple(elems_types),
given: Type::tuple(elems_types),
expected: tipo,
situation: None,
location,

View File

@ -5,7 +5,6 @@ use super::{
};
use crate::{
ast::{AssignmentKind, CallArg, Pattern, Span, PIPE_VARIABLE},
builtins::function,
expr::{TypedExpr, UntypedExpr},
};
use std::{ops::Deref, rc::Rc};
@ -257,7 +256,7 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> {
.environment
.unify(
func.tipo(),
function(vec![self.argument_type.clone()], return_type.clone()),
Type::function(vec![self.argument_type.clone()], return_type.clone()),
func.location(),
if let Type::Fn { args, .. } = func.tipo().deref() {
if let Some(typ) = args.first() {

View File

@ -296,10 +296,7 @@ fn resolve_alias(
#[cfg(test)]
mod tests {
use super::*;
use crate::{
builtins::{function, int},
tipo::Span,
};
use crate::tipo::{Span, Type};
use pretty_assertions::assert_eq;
use std::cell::RefCell;
@ -490,7 +487,7 @@ mod tests {
"?",
);
assert_string!(
function(
Type::function(
vec![Rc::new(Type::Var {
tipo: Rc::new(RefCell::new(TypeVar::Unbound { id: 78 })),
alias: None,
@ -503,7 +500,7 @@ mod tests {
"fn(?) -> ?",
);
assert_string!(
function(
Type::function(
vec![Rc::new(Type::Var {
tipo: Rc::new(RefCell::new(TypeVar::Generic { id: 78 })),
alias: None,
@ -692,10 +689,16 @@ mod tests {
#[test]
fn function_test() {
assert_eq!(pretty_print(function(vec![], int())), "fn() -> Int");
assert_eq!(
pretty_print(Type::function(vec![], Type::int())),
"fn() -> Int"
);
assert_eq!(
pretty_print(function(vec![int(), int(), int()], int())),
pretty_print(Type::function(
vec![Type::int(), Type::int(), Type::int()],
Type::int()
)),
"fn(Int, Int, Int) -> Int"
);
}

View File

@ -153,7 +153,7 @@ impl From<&Config> for Preamble {
#[cfg(test)]
mod tests {
use super::*;
use aiken_lang::builtins;
use aiken_lang::tipo::Type;
use schema::{Data, Declaration, Items, Schema};
use serde_json::{self, json};
use std::collections::HashMap;
@ -225,17 +225,17 @@ mod tests {
fn serialize_with_definitions() {
let mut definitions = Definitions::new();
definitions
.register::<_, Error>(&builtins::int(), &HashMap::new(), |_| {
.register::<_, Error>(&Type::int(), &HashMap::new(), |_| {
Ok(Schema::Data(Data::Integer).into())
})
.unwrap();
definitions
.register::<_, Error>(
&builtins::list(builtins::byte_array()),
&Type::list(Type::byte_array()),
&HashMap::new(),
|definitions| {
let ref_bytes = definitions.register::<_, Error>(
&builtins::byte_array(),
&Type::byte_array(),
&HashMap::new(),
|_| Ok(Schema::Data(Data::Bytes).into()),
)?;

View File

@ -4,7 +4,6 @@ use crate::{
};
use aiken_lang::{
ast::{Definition, TypedDataType, TypedDefinition},
builtins::wrapped_redeemer,
tipo::{pretty, Type, TypeVar},
};
use owo_colors::{OwoColorize, Stream::Stdout};
@ -142,7 +141,7 @@ impl Annotated<Schema> {
) -> Reference {
definitions
.register(
&wrapped_redeemer(type_info),
&Type::wrapped_redeemer(type_info),
&HashMap::new(),
|_| {
Ok::<_, Error>(Annotated {

View File

@ -1,9 +1,9 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\npub type Foo {\n foo: Int\n}\n\nvalidator {\n fn annotated_data(datum: Data<Foo>, redeemer: Data, ctx: Void) {\n True\n }\n}\n"
description: "Code:\n\npub type Foo {\n foo: Int\n}\n\nvalidator annotated_data {\n spend(datum: Option<Data<Foo>>, redeemer: Data, output_reference: Data, transpose: Data) {\n True\n }\n}\n"
---
{
"title": "test_module.annotated_data",
"title": "test_module.annotated_data.spend",
"datum": {
"title": "datum",
"schema": {

View File

@ -1,6 +1,6 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\nvalidator {\n fn generics(redeemer: a, ctx: Void) {\n True\n }\n}\n"
description: "Code:\n\nvalidator generics {\n mint(redeemer: a, policy_id: ByteArray, transaction: Data) {\n True\n }\n}\n"
---
Schema {
error: Error {
@ -16,7 +16,7 @@ Schema {
},
],
},
location: 26..37,
location: 28..39,
source_code: NamedSource {
name: "",
source: "<redacted>",

View File

@ -1,9 +1,15 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\ntype Either<left, right> {\n Left(left)\n Right(right)\n}\n\ntype Interval<a> {\n Finite(a)\n Infinite\n}\n\nvalidator {\n fn generics(redeemer: Either<ByteArray, Interval<Int>>, ctx: Void) {\n True\n }\n}\n"
description: "Code:\n\npub type Either<left, right> {\n Left(left)\n Right(right)\n}\n\npub type Interval<a> {\n Finite(a)\n Infinite\n}\n\nvalidator generics {\n spend(datum: Option<Data>, redeemer: Either<ByteArray, Interval<Int>>, output_reference: Data, transaction: Data) {\n True\n }\n}\n"
---
{
"title": "test_module.generics",
"title": "test_module.generics.spend",
"datum": {
"title": "datum",
"schema": {
"$ref": "#/definitions/Data"
}
},
"redeemer": {
"title": "redeemer",
"schema": {
@ -16,6 +22,10 @@ description: "Code:\n\ntype Either<left, right> {\n Left(left)\n Right(rig
"ByteArray": {
"dataType": "bytes"
},
"Data": {
"title": "Data",
"description": "Any Plutus data."
},
"Int": {
"dataType": "integer"
},

View File

@ -1,9 +1,9 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\ntype Dict<key, value> {\n inner: List<(ByteArray, value)>\n}\n\ntype UUID { UUID }\n\nvalidator {\n fn list_2_tuples_as_list(redeemer: Dict<UUID, Int>, ctx: Void) {\n True\n }\n}\n"
description: "Code:\n\npub type Dict<key, value> {\n inner: List<(ByteArray, value)>\n}\n\npub type UUID { UUID }\n\nvalidator list_2_tuples_as_list {\n mint(redeemer: Dict<UUID, Int>, policy_id: ByteArray, transaction: Data) {\n True\n }\n}\n"
---
{
"title": "test_module.list_2_tuples_as_list",
"title": "test_module.list_2_tuples_as_list.mint",
"redeemer": {
"title": "redeemer",
"schema": {

View File

@ -1,9 +1,15 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\ntype Dict<key, value> {\n inner: List<Pair<ByteArray, value>>\n}\n\ntype UUID { UUID }\n\nvalidator {\n fn list_pairs_as_map(redeemer: Dict<UUID, Int>, ctx: Void) {\n True\n }\n}\n"
description: "Code:\n\npub type Dict<key, value> {\n inner: List<Pair<ByteArray, value>>\n}\n\npub type UUID { UUID }\n\nvalidator list_pairs_as_map {\n spend(datum: Option<Data>, redeemer: Dict<UUID, Int>, _output_reference: Data, transaction: Data) {\n True\n }\n}\n"
---
{
"title": "test_module.list_pairs_as_map",
"title": "test_module.list_pairs_as_map.spend",
"datum": {
"title": "datum",
"schema": {
"$ref": "#/definitions/Data"
}
},
"redeemer": {
"title": "redeemer",
"schema": {
@ -16,6 +22,10 @@ description: "Code:\n\ntype Dict<key, value> {\n inner: List<Pair<ByteArray,
"ByteArray": {
"dataType": "bytes"
},
"Data": {
"title": "Data",
"description": "Any Plutus data."
},
"Int": {
"dataType": "integer"
},

View File

@ -1,9 +1,9 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\nvalidator {\n fn mint(redeemer: Data, ctx: Data) {\n True\n }\n}\n// "
description: "Code:\n\nvalidator thing {\n mint(redeemer: Data, policy_id: ByteArray, transaction: Data) {\n True\n }\n}\n// "
---
{
"title": "test_module.mint",
"title": "test_module.thing.mint",
"redeemer": {
"title": "redeemer",
"schema": {

View File

@ -1,9 +1,9 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\nvalidator(utxo_ref: Int) {\n fn mint(redeemer: Data, ctx: Data) {\n True\n }\n}\n// "
description: "Code:\n\nvalidator thing(utxo_ref: Int) {\n mint(redeemer: Data, policy_id: ByteArray, transaction: Data) {\n True\n }\n}\n// "
---
{
"title": "test_module.mint",
"title": "test_module.thing.mint",
"redeemer": {
"title": "redeemer",
"schema": {

View File

@ -1,9 +1,9 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\npub type Foo {\n foo: Data\n}\n\nvalidator {\n fn nested_data(datum: Foo, redeemer: Int, ctx: Void) {\n True\n }\n}\n"
description: "Code:\n\npub type Foo {\n foo: Data\n}\n\nvalidator nested_data {\n spend(datum: Option<Foo>, redeemer: Int, output_reference: Data, transaction: Data) {\n True\n }\n}\n"
---
{
"title": "test_module.nested_data",
"title": "test_module.nested_data.spend",
"datum": {
"title": "datum",
"schema": {

View File

@ -1,6 +1,6 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\npub opaque type Rational {\n numerator: Int,\n denominator: Int,\n}\n\nvalidator {\n fn opaque_singleton_multi_variants(redeemer: Rational, ctx: Void) {\n True\n }\n}\n"
description: "Code:\n\npub opaque type Rational {\n numerator: Int,\n denominator: Int,\n}\n\nvalidator opaque_singleton_multi_variants {\n spend(datum: Option<Data>, redeemer: Rational, oref: Data, transaction: Data) {\n True\n }\n}\n"
---
Schema {
error: Error {
@ -16,7 +16,7 @@ Schema {
},
],
},
location: 117..135,
location: 141..159,
source_code: NamedSource {
name: "",
source: "<redacted>",

View File

@ -1,6 +1,6 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\npub opaque type Dict<key, value> {\n inner: List<(ByteArray, value)>\n}\n\ntype UUID { UUID }\n\nvalidator {\n fn opaque_singleton_variants(redeemer: Dict<UUID, Int>, ctx: Void) {\n True\n }\n}\n"
description: "Code:\n\npub opaque type Dict<key, value> {\n inner: List<(ByteArray, value)>\n}\n\npub type UUID { UUID }\n\nvalidator opaque_singleton_variants {\n spend(datum: Option<Data>, redeemer: Dict<UUID, Int>, output_reference: Data, transaction: Data) {\n True\n }\n}\n"
---
Schema {
error: Error {
@ -16,7 +16,7 @@ Schema {
tipo: RefCell {
value: Link {
tipo: App {
public: false,
public: true,
contains_opaque: false,
module: "test_module",
name: "UUID",
@ -47,7 +47,7 @@ Schema {
},
],
},
location: 137..162,
location: 165..190,
source_code: NamedSource {
name: "",
source: "<redacted>",

View File

@ -1,9 +1,9 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\npub type LinkedList<a> {\n Cons(a, LinkedList<a>)\n Nil\n}\n\npub type Foo {\n Foo {\n foo: LinkedList<Bool>,\n }\n Bar {\n bar: Int,\n baz: (ByteArray, List<LinkedList<Int>>)\n }\n}\n\nvalidator {\n fn recursive_generic_types(datum: Foo, redeemer: LinkedList<Int>, ctx: Void) {\n True\n }\n}\n"
description: "Code:\n\npub type LinkedList<a> {\n Cons(a, LinkedList<a>)\n Nil\n}\n\npub type Foo {\n Foo {\n foo: LinkedList<Bool>,\n }\n Bar {\n bar: Int,\n baz: (ByteArray, List<LinkedList<Int>>)\n }\n}\n\nvalidator recursive_generic_types {\n spend(datum: Option<Foo>, redeemer: LinkedList<Int>, output_reference: Data, transaction: Data) {\n True\n }\n}\n"
---
{
"title": "test_module.recursive_generic_types",
"title": "test_module.recursive_generic_types.spend",
"datum": {
"title": "datum",
"schema": {

View File

@ -1,9 +1,15 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\npub type Expr {\n Val(Int)\n Sum(Expr, Expr)\n Mul(Expr, Expr)\n}\n\nvalidator {\n fn recursive_types(redeemer: Expr, ctx: Void) {\n True\n }\n}\n"
description: "Code:\n\npub type Expr {\n Val(Int)\n Sum(Expr, Expr)\n Mul(Expr, Expr)\n}\n\nvalidator recursive_types {\n spend(datum: Option<Data>, redeemer: Expr, output_reference: Data, transaction: Data) {\n True\n }\n}\n"
---
{
"title": "test_module.recursive_types",
"title": "test_module.recursive_types.spend",
"datum": {
"title": "datum",
"schema": {
"$ref": "#/definitions/Data"
}
},
"redeemer": {
"title": "redeemer",
"schema": {
@ -13,6 +19,10 @@ description: "Code:\n\npub type Expr {\n Val(Int)\n Sum(Expr, Expr)\n Mul(Exp
"compiledCode": "<redacted>",
"hash": "<redacted>",
"definitions": {
"Data": {
"title": "Data",
"description": "Any Plutus data."
},
"Int": {
"dataType": "integer"
},

View File

@ -1,9 +1,9 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\n/// On-chain state\ntype State {\n /// The contestation period as a number of seconds\n contestationPeriod: ContestationPeriod,\n /// List of public key hashes of all participants\n parties: List<Party>,\n utxoHash: Hash<Blake2b_256>,\n}\n\n/// A Hash digest for a given algorithm.\ntype Hash<alg> = ByteArray\n\ntype Blake2b_256 { Blake2b_256 }\n\n/// Whatever\ntype ContestationPeriod {\n /// A positive, non-zero number of seconds.\n ContestationPeriod(Int)\n}\n\ntype Party =\n ByteArray\n\ntype Input {\n CollectCom\n Close\n /// Abort a transaction\n Abort\n}\n\nvalidator {\n fn simplified_hydra(datum: State, redeemer: Input, ctx: Data) {\n True\n }\n}\n"
description: "Code:\n\n/// On-chain state\npub type State {\n /// The contestation period as a number of seconds\n contestationPeriod: ContestationPeriod,\n /// List of public key hashes of all participants\n parties: List<Party>,\n utxoHash: Hash<Blake2b_256>,\n}\n\n/// A Hash digest for a given algorithm.\npub type Hash<alg> = ByteArray\n\npub type Blake2b_256 { Blake2b_256 }\n\n/// Whatever\npub type ContestationPeriod {\n /// A positive, non-zero number of seconds.\n ContestationPeriod(Int)\n}\n\npub type Party =\n ByteArray\n\npub type Input {\n CollectCom\n Close\n /// Abort a transaction\n Abort\n}\n\nvalidator simplified_hydra {\n spend(datum: Option<State>, redeemer: Input, output_reference: Data, transaction: Data) {\n True\n }\n}\n"
---
{
"title": "test_module.simplified_hydra",
"title": "test_module.simplified_hydra.spend",
"datum": {
"title": "datum",
"schema": {

View File

@ -1,9 +1,9 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\nvalidator {\n fn tuples(datum: (Int, ByteArray), redeemer: (Int, Int, Int), ctx: Void) {\n True\n }\n}\n"
description: "Code:\n\nvalidator tuples {\n spend(datum: Option<(Int, ByteArray)>, redeemer: (Int, Int, Int), output_reference: Data, transaction: Data) {\n True\n }\n}\n"
---
{
"title": "test_module.tuples",
"title": "test_module.tuples.spend",
"datum": {
"title": "datum",
"schema": {

View File

@ -7,10 +7,10 @@ use super::{
};
use crate::module::{CheckedModule, CheckedModules};
use aiken_lang::{
ast::{Annotation, TypedArg, TypedFunction, TypedValidator},
ast::{well_known, Annotation, TypedArg, TypedFunction, TypedValidator},
gen_uplc::CodeGenerator,
plutus_version::PlutusVersion,
tipo::Type,
tipo::{collapse_links, Type},
};
use miette::NamedSource;
use serde;
@ -30,7 +30,8 @@ pub struct Validator {
#[serde(skip_serializing_if = "Option::is_none")]
pub datum: Option<Parameter>,
pub redeemer: Parameter,
#[serde(skip_serializing_if = "Option::is_none")]
pub redeemer: Option<Parameter>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
@ -52,29 +53,33 @@ impl Validator {
def: &TypedValidator,
plutus_version: &PlutusVersion,
) -> Vec<Result<Validator, Error>> {
let is_multi_validator = def.other_fun.is_some();
let mut program = MemoProgram::new();
let mut validators = vec![Validator::create_validator_blueprint(
generator,
modules,
module,
def,
&def.fun,
is_multi_validator,
&mut program,
plutus_version,
)];
let mut validators = vec![];
if let Some(ref other_func) = def.other_fun {
for handler in &def.handlers {
validators.push(Validator::create_validator_blueprint(
generator,
modules,
module,
def,
other_func,
is_multi_validator,
handler,
&mut program,
plutus_version,
));
}
// NOTE: Only push the fallback if all other validators have been successfully
// generated. Otherwise, we may fall into scenarios where we cannot generate validators
// (e.g. due to the presence of generics in datum/redeemer), which won't be caught by
// the else branch since it lacks arguments.
if validators.iter().all(|v| v.is_ok()) {
validators.push(Validator::create_validator_blueprint(
generator,
modules,
module,
def,
&def.fallback,
&mut program,
plutus_version,
));
@ -90,13 +95,9 @@ impl Validator {
module: &CheckedModule,
def: &TypedValidator,
func: &TypedFunction,
is_multi_validator: bool,
program: &mut MemoProgram,
plutus_version: &PlutusVersion,
) -> Result<Validator, Error> {
let mut args = func.arguments.iter().rev();
let (_, redeemer, datum) = (args.next(), args.next().unwrap(), args.next());
let mut definitions = Definitions::new();
let parameters = def
@ -123,53 +124,81 @@ impl Validator {
})
.collect::<Result<_, _>>()?;
let datum = datum
.map(|datum| {
Annotated::from_type(
modules.into(),
tipo_or_annotation(module, datum),
&mut definitions,
)
.map_err(|error| Error::Schema {
error,
location: datum.location,
source_code: NamedSource::new(
module.input_path.display().to_string(),
module.code.clone(),
),
})
})
.transpose()?
.map(|schema| Parameter {
title: datum.map(|datum| datum.arg_name.get_label()),
schema,
});
let (datum, redeemer) = if func.name == well_known::VALIDATOR_ELSE {
(None, None)
} else {
let mut args = func.arguments.iter().rev();
let redeemer = Annotated::from_type(
modules.into(),
tipo_or_annotation(module, redeemer),
&mut definitions,
)
.map_err(|error| Error::Schema {
error,
location: redeemer.location,
source_code: NamedSource::new(
module.input_path.display().to_string(),
module.code.clone(),
),
})
.map(|schema| Parameter {
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,
},
})?;
let (_, _, redeemer, datum) = (
args.next(),
args.next(),
args.next().expect("redeemer is always present"),
args.next(),
);
let datum = datum
.map(|datum| {
match datum.tipo.as_ref() {
Type::App { module: module_name, name, args, .. } if module_name.is_empty() && name == well_known::OPTION => {
let annotation = if let Some(Annotation::Constructor { arguments, .. }) = datum.annotation.as_ref() {
arguments.first().cloned().expect("Datum isn't an option but should be; this should have been caught by the type-checker!")
} else {
Annotation::data(datum.location)
};
Annotated::from_type(
modules.into(),
tipo_or_annotation(module, &TypedArg {
arg_name: datum.arg_name.clone(),
location: datum.location,
annotation: Some(annotation),
doc: datum.doc.clone(),
is_validator_param: datum.is_validator_param,
tipo: args.first().expect("Option always have a single type argument.").clone()
}),
&mut definitions,
)
.map_err(|error| Error::Schema {
error,
location: datum.location,
source_code: NamedSource::new(
module.input_path.display().to_string(),
module.code.clone(),
),
})
},
_ => panic!("Datum isn't an option but should be; this should have been caught by the type-checker!"),
}
})
.transpose()?
.map(|schema| Parameter {
title: datum.map(|datum| datum.arg_name.get_label()),
schema,
});
let redeemer = Annotated::from_type(
modules.into(),
tipo_or_annotation(module, redeemer),
&mut definitions,
)
.map_err(|error| Error::Schema {
error,
location: redeemer.location,
source_code: NamedSource::new(
module.input_path.display().to_string(),
module.code.clone(),
),
})
.map(|schema| Parameter {
title: Some(redeemer.arg_name.get_label()),
schema,
})?;
(datum, Some(redeemer))
};
Ok(Validator {
title: format!("{}.{}", &module.name, &func.name),
title: format!("{}.{}.{}", &module.name, &def.name, &func.name,),
description: func.doc.clone(),
parameters,
datum,
@ -185,7 +214,7 @@ impl Validator {
}
pub fn tipo_or_annotation<'a>(module: &'a CheckedModule, arg: &'a TypedArg) -> &'a Type {
match *arg.tipo.borrow() {
match collapse_links(arg.tipo.clone()).borrow() {
Type::App {
module: ref module_name,
name: ref type_name,
@ -274,7 +303,7 @@ mod tests {
use aiken_lang::{
self,
ast::{TraceLevel, Tracing},
builtins,
tipo::Type,
};
use std::collections::HashMap;
use uplc::ast as uplc_ast;
@ -296,7 +325,7 @@ mod tests {
let validators = Validator::from_checked_module(&modules, &mut generator, validator, def, &PlutusVersion::default());
if validators.len() > 1 {
if validators.len() > 2 {
panic!("Multi-validator given to test bench. Don't do that.")
}
@ -338,7 +367,7 @@ mod tests {
// "dataType": "integer"
// }
definitions
.register::<_, Error>(&builtins::int(), &HashMap::new(), |_| {
.register::<_, Error>(&Type::int(), &HashMap::new(), |_| {
Ok(Schema::Data(Data::Integer).into())
})
.unwrap();
@ -349,7 +378,7 @@ mod tests {
// "dataType": "bytes"
// }
definitions
.register::<_, Error>(&builtins::byte_array(), &HashMap::new(), |_| {
.register::<_, Error>(&Type::byte_array(), &HashMap::new(), |_| {
Ok(Schema::Data(Data::Bytes).into())
})
.unwrap();
@ -396,8 +425,8 @@ mod tests {
fn mint_basic() {
assert_validator!(
r#"
validator {
fn mint(redeemer: Data, ctx: Data) {
validator thing {
mint(redeemer: Data, policy_id: ByteArray, transaction: Data) {
True
}
}
@ -409,8 +438,8 @@ mod tests {
fn mint_parameterized() {
assert_validator!(
r#"
validator(utxo_ref: Int) {
fn mint(redeemer: Data, ctx: Data) {
validator thing(utxo_ref: Int) {
mint(redeemer: Data, policy_id: ByteArray, transaction: Data) {
True
}
}
@ -423,7 +452,7 @@ mod tests {
assert_validator!(
r#"
/// On-chain state
type State {
pub type State {
/// The contestation period as a number of seconds
contestationPeriod: ContestationPeriod,
/// List of public key hashes of all participants
@ -432,28 +461,28 @@ mod tests {
}
/// A Hash digest for a given algorithm.
type Hash<alg> = ByteArray
pub type Hash<alg> = ByteArray
type Blake2b_256 { Blake2b_256 }
pub type Blake2b_256 { Blake2b_256 }
/// Whatever
type ContestationPeriod {
pub type ContestationPeriod {
/// A positive, non-zero number of seconds.
ContestationPeriod(Int)
}
type Party =
pub type Party =
ByteArray
type Input {
pub type Input {
CollectCom
Close
/// Abort a transaction
Abort
}
validator {
fn simplified_hydra(datum: State, redeemer: Input, ctx: Data) {
validator simplified_hydra {
spend(datum: Option<State>, redeemer: Input, output_reference: Data, transaction: Data) {
True
}
}
@ -465,8 +494,8 @@ mod tests {
fn tuples() {
assert_validator!(
r#"
validator {
fn tuples(datum: (Int, ByteArray), redeemer: (Int, Int, Int), ctx: Void) {
validator tuples {
spend(datum: Option<(Int, ByteArray)>, redeemer: (Int, Int, Int), output_reference: Data, transaction: Data) {
True
}
}
@ -478,18 +507,18 @@ mod tests {
fn generics() {
assert_validator!(
r#"
type Either<left, right> {
pub type Either<left, right> {
Left(left)
Right(right)
}
type Interval<a> {
pub type Interval<a> {
Finite(a)
Infinite
}
validator {
fn generics(redeemer: Either<ByteArray, Interval<Int>>, ctx: Void) {
validator generics {
spend(datum: Option<Data>, redeemer: Either<ByteArray, Interval<Int>>, output_reference: Data, transaction: Data) {
True
}
}
@ -501,8 +530,8 @@ mod tests {
fn free_vars() {
assert_validator!(
r#"
validator {
fn generics(redeemer: a, ctx: Void) {
validator generics {
mint(redeemer: a, policy_id: ByteArray, transaction: Data) {
True
}
}
@ -514,14 +543,14 @@ mod tests {
fn list_2_tuples_as_list() {
assert_validator!(
r#"
type Dict<key, value> {
pub type Dict<key, value> {
inner: List<(ByteArray, value)>
}
type UUID { UUID }
pub type UUID { UUID }
validator {
fn list_2_tuples_as_list(redeemer: Dict<UUID, Int>, ctx: Void) {
validator list_2_tuples_as_list {
mint(redeemer: Dict<UUID, Int>, policy_id: ByteArray, transaction: Data) {
True
}
}
@ -533,14 +562,14 @@ mod tests {
fn list_pairs_as_map() {
assert_validator!(
r#"
type Dict<key, value> {
pub type Dict<key, value> {
inner: List<Pair<ByteArray, value>>
}
type UUID { UUID }
pub type UUID { UUID }
validator {
fn list_pairs_as_map(redeemer: Dict<UUID, Int>, ctx: Void) {
validator list_pairs_as_map {
spend(datum: Option<Data>, redeemer: Dict<UUID, Int>, _output_reference: Data, transaction: Data) {
True
}
}
@ -556,10 +585,10 @@ mod tests {
inner: List<(ByteArray, value)>
}
type UUID { UUID }
pub type UUID { UUID }
validator {
fn opaque_singleton_variants(redeemer: Dict<UUID, Int>, ctx: Void) {
validator opaque_singleton_variants {
spend(datum: Option<Data>, redeemer: Dict<UUID, Int>, output_reference: Data, transaction: Data) {
True
}
}
@ -576,8 +605,8 @@ mod tests {
denominator: Int,
}
validator {
fn opaque_singleton_multi_variants(redeemer: Rational, ctx: Void) {
validator opaque_singleton_multi_variants {
spend(datum: Option<Data>, redeemer: Rational, oref: Data, transaction: Data) {
True
}
}
@ -593,8 +622,8 @@ mod tests {
foo: Data
}
validator {
fn nested_data(datum: Foo, redeemer: Int, ctx: Void) {
validator nested_data {
spend(datum: Option<Foo>, redeemer: Int, output_reference: Data, transaction: Data) {
True
}
}
@ -612,8 +641,8 @@ mod tests {
Mul(Expr, Expr)
}
validator {
fn recursive_types(redeemer: Expr, ctx: Void) {
validator recursive_types {
spend(datum: Option<Data>, redeemer: Expr, output_reference: Data, transaction: Data) {
True
}
}
@ -640,8 +669,8 @@ mod tests {
}
}
validator {
fn recursive_generic_types(datum: Foo, redeemer: LinkedList<Int>, ctx: Void) {
validator recursive_generic_types {
spend(datum: Option<Foo>, redeemer: LinkedList<Int>, output_reference: Data, transaction: Data) {
True
}
}
@ -657,8 +686,8 @@ mod tests {
foo: Int
}
validator {
fn annotated_data(datum: Data<Foo>, redeemer: Data, ctx: Void) {
validator annotated_data {
spend(datum: Option<Data<Foo>>, redeemer: Data, output_reference: Data, transpose: Data) {
True
}
}

View File

@ -390,8 +390,8 @@ impl CheckedModule {
}
Definition::Validator(Validator {
params,
fun,
other_fun,
handlers,
fallback,
..
}) => {
for param in params {
@ -404,18 +404,8 @@ impl CheckedModule {
}
}
for argument in fun.arguments.iter_mut() {
let docs: Vec<&str> =
comments_before(&mut doc_comments, argument.location.start, &self.code);
if !docs.is_empty() {
let doc = docs.join("\n");
argument.put_doc(doc);
}
}
if let Some(fun) = other_fun {
for argument in fun.arguments.iter_mut() {
for handler in handlers.iter_mut() {
for argument in handler.arguments.iter_mut() {
let docs: Vec<&str> = comments_before(
&mut doc_comments,
argument.location.start,
@ -428,6 +418,16 @@ impl CheckedModule {
}
}
}
for argument in fallback.arguments.iter_mut() {
let docs: Vec<&str> =
comments_before(&mut doc_comments, argument.location.start, &self.code);
if !docs.is_empty() {
let doc = docs.join("\n");
argument.put_doc(doc);
}
}
}
_ => (),
}
@ -463,6 +463,7 @@ impl CheckedModules {
modules
}
// todo: this might need fixing
pub fn validators(&self) -> impl Iterator<Item = (&CheckedModule, &TypedValidator)> {
let mut items = vec![];
@ -478,12 +479,12 @@ impl CheckedModules {
(
left.0.package.to_string(),
left.0.name.to_string(),
left.1.fun.name.to_string(),
left.1.name.to_string(),
)
.cmp(&(
right.0.package.to_string(),
right.0.name.to_string(),
right.1.fun.name.to_string(),
right.1.name.to_string(),
))
});

View File

@ -1,7 +1,6 @@
use aiken_lang::ast::OnTestFailure;
pub(crate) use aiken_lang::{
ast::{BinOp, DataTypeKey, IfBranch, Span, TypedArg, TypedDataType, TypedTest},
builtins::bool,
expr::{TypedExpr, UntypedExpr},
format::Formatter,
gen_uplc::CodeGenerator,
@ -1056,7 +1055,7 @@ impl TryFrom<TypedExpr> for Assertion<TypedExpr> {
left,
right,
..
} if tipo == bool() => {
} if tipo == Type::bool() => {
// 'and' and 'or' are left-associative operators.
match (*right).clone().try_into() {
Ok(Assertion {
@ -1094,7 +1093,7 @@ impl TryFrom<TypedExpr> for Assertion<TypedExpr> {
let then_is_true = match body {
TypedExpr::Var {
name, constructor, ..
} => name == "True" && constructor.tipo == bool(),
} => name == "True" && constructor.tipo == Type::bool(),
_ => false,
};
@ -1102,7 +1101,7 @@ impl TryFrom<TypedExpr> for Assertion<TypedExpr> {
TypedExpr::Trace { then, .. } => match *then {
TypedExpr::Var {
name, constructor, ..
} => name == "False" && constructor.tipo == bool(),
} => name == "False" && constructor.tipo == Type::bool(),
_ => false,
},
_ => false,

File diff suppressed because it is too large Load Diff

View File

@ -5,78 +5,120 @@ use std::string::FromUtf8Error;
#[derive(Debug, Clone, PartialEq, thiserror::Error, miette::Diagnostic)]
pub enum Error {
#[error("Over budget mem: {} & cpu: {}", .0.mem, .0.cpu)]
#[error("execution went over budget\n{:>13} {}\n{:>13} {}", "Mem", .0.mem, "CPU", .0.cpu)]
OutOfExError(ExBudget),
#[error("Invalid Stepkind: {0}")]
#[error("invalid step kind: {0}")]
InvalidStepKind(u8),
#[error("Cannot evaluate an open term:\\n\\n{}", .0.to_pretty())]
#[error(
"cannot evaluate an open term:\n{:>13} {}",
"Term",
indent(redacted(.0.to_pretty(), 10)),
)]
OpenTermEvaluated(Term<NamedDeBruijn>),
#[error("The validator crashed / exited prematurely")]
#[error("the validator crashed / exited prematurely")]
EvaluationFailure,
#[error(
"Attempted to instantiate a non-polymorphic term\n{:>13} {}",
"attempted to instantiate a non-polymorphic term\n{:>13} {}",
"Term",
indent(redacted(format!("{:#?}", .0), 10)),
)]
NonPolymorphicInstantiation(Value),
#[error(
"Attempted to apply an argument to a non-function\n{:>13} {}\n{:>13} {}",
"attempted to apply an argument to a non-function\n{:>13} {}\n{:>13} {}",
"Thing",
indent(redacted(format!("{:#?}", .0), 5)),
"Argument",
indent(redacted(format!("{:#?}", .1), 5)),
)]
NonFunctionalApplication(Value, Value),
#[error("Attempted to case a non-const:\n\n{0:#?}")]
#[error(
"attempted to case a non-const\n{:>13} {}",
"Value",
indent(redacted(format!("{:#?}", .0), 10)),
)]
NonConstrScrutinized(Value),
#[error("Cases: {0:#?}\n\n are missing branch for constr:\n\n{1:#?}")]
MissingCaseBranch(Vec<Term<NamedDeBruijn>>, Value),
#[error("Type mismatch expected '{0}' got '{1}'")]
#[error("type mismatch\n{:>13} {0}\n{:>13} {1}", "Expected", "Got")]
TypeMismatch(Type, Type),
#[error("Type mismatch expected '(list a)' got '{0}'")]
#[error("type mismatch\n{:>13} (list a)\n{:>13} {0}", "Expected", "Got")]
ListTypeMismatch(Type),
#[error("Type mismatch expected '(pair a b)' got '{0}'")]
#[error("type mismatch\n{:>13}(pair a b)\n{:>13} {0}", "Expected", "Got")]
PairTypeMismatch(Type),
#[error("Empty List:\n\n{0:#?}")]
#[error(
"unexpected empty list\n{:>13} {}",
"List",
indent(redacted(format!("{:#?}", .0), 10)),
)]
EmptyList(Value),
#[error(
"A builtin received a term argument when something else was expected:\n\n{0}\n\nYou probably forgot to wrap the builtin with a force."
"a builtin received a term argument when something else was expected\n{:>13} {}\n{:>13} You probably forgot to wrap the builtin with a force.",
"Term",
indent(redacted(format!("{:#?}", .0), 10)),
"Hint"
)]
UnexpectedBuiltinTermArgument(Term<NamedDeBruijn>),
#[error(
"A builtin expected a term argument, but something else was received:\n\n{0}\n\nYou probably have an extra force wrapped around a builtin"
"a builtin expected a term argument, but something else was received:\n{:>13} {}\n{:>13} You probably have an extra force wrapped around a builtin",
"Term",
indent(redacted(format!("{:#?}", .0), 10)),
"Hint"
)]
BuiltinTermArgumentExpected(Term<NamedDeBruijn>),
#[error("Unable to unlift value because it is not a constant:\n\n{0:#?}")]
#[error(
"Unable to unlift value because it is not a constant\n{:>13} {}",
"Value",
indent(redacted(format!("{:#?}", .0), 10)),
)]
NotAConstant(Value),
#[error("The evaluation never reached a final state")]
MachineNeverReachedDone,
#[error("integerToByteString encountered negative size {0}")]
#[error("integerToByteString encountered negative size\n{:>13} {0}", "Size")]
IntegerToByteStringNegativeSize(BigInt),
#[error("integerToByteString encountered negative input {0}")]
#[error("integerToByteString encountered negative input\n{:>13} {0}", "Input")]
IntegerToByteStringNegativeInput(BigInt),
#[error("integerToByteString encountered size {0} which is bigger than the max size of {1}")]
#[error(
"bytes size beyond limit when converting from integer\n{:>13} {0}\n{:>13} {1}",
"Size",
"Maximum"
)]
IntegerToByteStringSizeTooBig(BigInt, i64),
#[error("integerToByteString encountered size {0} which is not enough space for {1} bytes")]
#[error(
"bytes size below limit when converting from integer\n{:>13} {0}\n{:>13} {1}",
"Size",
"Minimum"
)]
IntegerToByteStringSizeTooSmall(BigInt, usize),
#[error("Decoding utf8")]
Utf8(#[from] FromUtf8Error),
#[error("Out of Bounds\n\nindex: {}\nbytestring: {}\npossible: 0 - {}", .0, hex::encode(.1), .1.len() - 1)]
#[error(
"Out of Bounds\n{:>13} {}\nb{:>13} {}\n{:>13} 0 - {}",
"Index",
.0,
"ByteArray",
hex::encode(.1),
"Allowed",
.1.len() - 1
)]
ByteStringOutOfBounds(BigInt, Vec<u8>),
#[error("Attempt to consByteString something than isn't a byte between [0-255]: {0}")]
#[error(
"attempt to consByteString something than isn't a byte between [0-255]\n{:>13} {0}",
"Found"
)]
ByteStringConsNotAByte(BigInt),
#[error("Divide By Zero\n\n{0} / {1}")]
#[error("divide By Zero: {0} / {1}")]
DivideByZero(BigInt, BigInt),
#[error("Ed25519S PublicKey should be 32 bytes but it was {0}")]
UnexpectedEd25519PublicKeyLength(usize),
#[error("Ed25519S Signature should be 64 bytes but it was {0}")]
UnexpectedEd25519SignatureLength(usize),
#[error(
"Failed to deserialise PlutusData using {0}:\n\n{}",
redacted(format!("{:#?}", .1), 10),
"failed to deserialise PlutusData using {0}\n{:>13} {}",
"Value",
indent(redacted(format!("{:#?}", .1), 10)),
)]
DeserialisationError(String, Value),
#[error("Integer overflow")]
#[error("integer overflow")]
OverflowError,
#[error("blst error {0:?}")]
Blst(blst::BLST_ERROR),

View File

@ -100,7 +100,7 @@ pub fn eval_phase_two_raw(
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, tx_bytes))
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, tx_bytes))?;
let cost_mdls = cost_mdls_bytes.map(|x| CostMdls::decode_fragment(x)).transpose()?;
let cost_mdls = cost_mdls_bytes.map(CostMdls::decode_fragment).transpose()?;
let budget = ExBudget {
cpu: initial_budget.0 as i64,

View File

@ -46,15 +46,7 @@ pub fn eval_redeemer(
.apply_data(redeemer.data.clone())
.apply_data(script_context.to_plutus_data()),
// FIXME: Temporary, but needed until https://github.com/aiken-lang/aiken/pull/977
// is implemented.
ScriptContext::V3 { .. } => if let Some(datum) = datum {
program.apply_data(datum)
} else {
program
}
.apply_data(redeemer.data.clone())
.apply_data(script_context.to_plutus_data()),
ScriptContext::V3 { .. } => program.apply_data(script_context.to_plutus_data()),
};
let mut eval_result = if let Some(costs) = cost_mdl_opt {

View File

@ -570,20 +570,9 @@ Constr(
),
Constr(
Constr {
tag: 121,
tag: 122,
any_constructor: None,
fields: [
BigInt(
Int(
Int(
Int {
neg: false,
val: 3000000,
},
),
),
),
],
fields: [],
},
),
],
@ -638,20 +627,9 @@ Constr(
),
Constr(
Constr {
tag: 121,
tag: 122,
any_constructor: None,
fields: [
BigInt(
Int(
Int(
Int {
neg: false,
val: 3000000,
},
),
),
),
],
fields: [],
},
),
],

View File

@ -52,6 +52,8 @@ struct WithArrayRational<'a, T>(&'a T);
struct WithPartialCertificates<'a, T>(&'a T);
struct WithNeverRegistrationDeposit<'a, T>(&'a T);
pub trait ToPlutusData {
fn to_plutus_data(&self) -> PlutusData;
}
@ -203,6 +205,29 @@ impl<'a> ToPlutusData for WithWrappedTransactionId<'a, KeyValuePairs<ScriptPurpo
}
}
impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, Vec<Certificate>> {
fn to_plutus_data(&self) -> PlutusData {
self.0
.iter()
.map(WithNeverRegistrationDeposit)
.collect::<Vec<_>>()
.to_plutus_data()
}
}
impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, KeyValuePairs<ScriptPurpose, Redeemer>> {
fn to_plutus_data(&self) -> PlutusData {
let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![];
for (key, value) in self.0.iter() {
data_vec.push((
WithNeverRegistrationDeposit(key).to_plutus_data(),
value.to_plutus_data(),
))
}
PlutusData::Map(KeyValuePairs::Def(data_vec))
}
}
impl<A: ToPlutusData> ToPlutusData for Option<A> {
fn to_plutus_data(&self) -> PlutusData {
match self {
@ -549,14 +574,16 @@ impl<'a> ToPlutusData for WithPartialCertificates<'a, Certificate> {
vec![pool_keyhash.to_plutus_data(), epoch.to_plutus_data()],
),
certificate => unreachable!("unexpected in V1/V2 script context: {certificate:?}"),
certificate => {
unreachable!("unexpected certificate type in V1/V2 script context: {certificate:?}")
}
}
}
}
impl ToPlutusData for Certificate {
impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, Certificate> {
fn to_plutus_data(&self) -> PlutusData {
match self {
match self.0 {
Certificate::StakeRegistration(stake_credential) => wrap_multiple_with_constr(
0,
vec![
@ -565,11 +592,11 @@ impl ToPlutusData for Certificate {
],
),
Certificate::Reg(stake_credential, deposit) => wrap_multiple_with_constr(
Certificate::Reg(stake_credential, _) => wrap_multiple_with_constr(
0,
vec![
stake_credential.to_plutus_data(),
Some(*deposit).to_plutus_data(),
None::<PlutusData>.to_plutus_data(),
],
),
@ -581,11 +608,11 @@ impl ToPlutusData for Certificate {
],
),
Certificate::UnReg(stake_credential, deposit) => wrap_multiple_with_constr(
Certificate::UnReg(stake_credential, _) => wrap_multiple_with_constr(
1,
vec![
stake_credential.to_plutus_data(),
Some(*deposit).to_plutus_data(),
None::<PlutusData>.to_plutus_data(),
],
),
@ -835,32 +862,44 @@ impl ToPlutusData for TxInInfo {
}
}
// NOTE: This is a _small_ abuse of the 'WithWrappedTransactionId'. We know the wrapped
// is needed for V1 and V2, and it also appears that for V1 and V2, the certifying
// purpose mustn't include the certificate index. So, we also short-circuit it here.
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, ScriptPurpose> {
fn to_plutus_data(&self) -> PlutusData {
match self.0 {
ScriptPurpose::Minting(policy_id) => wrap_with_constr(0, policy_id.to_plutus_data()),
ScriptPurpose::Spending(out_ref, ()) => {
wrap_with_constr(1, WithWrappedTransactionId(out_ref).to_plutus_data())
}
// NOTE: This is a _small_ abuse of the 'WithWrappedTransactionId'. We know the wrapped
// is needed for V1 and V2, and it also appears that for V1 and V2, the certifying
// purpose mustn't include the certificate index. So, we also short-circuit it here.
ScriptPurpose::Certifying(_, dcert) => wrap_with_constr(3, dcert.to_plutus_data()),
otherwise => otherwise.to_plutus_data(),
ScriptPurpose::Rewarding(stake_credential) => {
wrap_with_constr(2, stake_credential.to_plutus_data())
}
ScriptPurpose::Certifying(_, dcert) => {
wrap_with_constr(3, WithPartialCertificates(dcert).to_plutus_data())
}
purpose => {
unreachable!("unsupported purpose for V1 or V2 script context: {purpose:?}")
}
}
}
}
impl ToPlutusData for ScriptPurpose {
impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, ScriptPurpose> {
fn to_plutus_data(&self) -> PlutusData {
match self {
match self.0 {
ScriptPurpose::Minting(policy_id) => wrap_with_constr(0, policy_id.to_plutus_data()),
ScriptPurpose::Spending(out_ref, ()) => wrap_with_constr(1, out_ref.to_plutus_data()),
ScriptPurpose::Rewarding(stake_credential) => {
wrap_with_constr(2, stake_credential.to_plutus_data())
}
ScriptPurpose::Certifying(ix, dcert) => {
wrap_multiple_with_constr(3, vec![ix.to_plutus_data(), dcert.to_plutus_data()])
}
ScriptPurpose::Certifying(ix, dcert) => wrap_multiple_with_constr(
3,
vec![
ix.to_plutus_data(),
WithNeverRegistrationDeposit(dcert).to_plutus_data(),
],
),
ScriptPurpose::Voting(voter) => {
wrap_multiple_with_constr(4, vec![voter.to_plutus_data()])
}
@ -1240,12 +1279,12 @@ impl ToPlutusData for Vote {
}
}
impl<T> ToPlutusData for ScriptInfo<T>
impl<'a, T> ToPlutusData for WithNeverRegistrationDeposit<'a, ScriptInfo<T>>
where
T: ToPlutusData,
{
fn to_plutus_data(&self) -> PlutusData {
match self {
match self.0 {
ScriptInfo::Minting(policy_id) => wrap_with_constr(0, policy_id.to_plutus_data()),
ScriptInfo::Spending(out_ref, datum) => {
wrap_multiple_with_constr(1, vec![out_ref.to_plutus_data(), datum.to_plutus_data()])
@ -1253,9 +1292,13 @@ where
ScriptInfo::Rewarding(stake_credential) => {
wrap_with_constr(2, stake_credential.to_plutus_data())
}
ScriptInfo::Certifying(ix, dcert) => {
wrap_multiple_with_constr(3, vec![ix.to_plutus_data(), dcert.to_plutus_data()])
}
ScriptInfo::Certifying(ix, dcert) => wrap_multiple_with_constr(
3,
vec![
ix.to_plutus_data(),
WithNeverRegistrationDeposit(dcert).to_plutus_data(),
],
),
ScriptInfo::Voting(voter) => wrap_multiple_with_constr(4, vec![voter.to_plutus_data()]),
ScriptInfo::Proposing(ix, procedure) => {
wrap_multiple_with_constr(5, vec![ix.to_plutus_data(), procedure.to_plutus_data()])
@ -1311,11 +1354,11 @@ impl ToPlutusData for TxInfo {
tx_info.outputs.to_plutus_data(),
tx_info.fee.to_plutus_data(),
tx_info.mint.to_plutus_data(),
tx_info.certificates.to_plutus_data(),
WithNeverRegistrationDeposit(&tx_info.certificates).to_plutus_data(),
tx_info.withdrawals.to_plutus_data(),
tx_info.valid_range.to_plutus_data(),
tx_info.signatories.to_plutus_data(),
tx_info.redeemers.to_plutus_data(),
WithNeverRegistrationDeposit(&tx_info.redeemers).to_plutus_data(),
tx_info.data.to_plutus_data(),
tx_info.id.to_plutus_data(),
tx_info.votes.to_plutus_data(),
@ -1347,7 +1390,7 @@ impl ToPlutusData for ScriptContext {
vec![
tx_info.to_plutus_data(),
redeemer.to_plutus_data(),
purpose.to_plutus_data(),
WithNeverRegistrationDeposit(purpose).to_plutus_data(),
],
),
}

View File

@ -14,6 +14,7 @@ pub fn filter(xs: List<a>, f: fn(a) -> Bool) -> List<a> {
}
test filter_1() {
filter([1,
2, 3, 4, 5, 6], fn(x) { builtin.mod_integer(x, 2) == 0 }) == [2, 4, 6]
filter([1, 2, 3, 4, 5, 6], fn(x) { builtin.mod_integer(x, 2) == 0 }) == [
2, 4, 6,
]
}

View File

@ -3,14 +3,14 @@
[[requirements]]
name = "aiken-lang/stdlib"
version = "main"
version = "v2"
source = "github"
[[packages]]
name = "aiken-lang/stdlib"
version = "main"
version = "v2"
requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158432, nanos_since_epoch = 10066000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@v2" = [{ secs_since_epoch = 1724777134, nanos_since_epoch = 232157000 }, "cdbbce58b61deb385e7ea787a2e0fc2dc8fe94db9999e0e6275bc9c70e5796be"]

View File

@ -3,5 +3,5 @@ version = "0.0.0"
[[dependencies]]
name = 'aiken-lang/stdlib'
version = 'main'
version = 'v2'
source = 'github'

View File

@ -5,12 +5,12 @@
"plutusVersion": "v2",
"compiler": {
"name": "Aiken",
"version": "v1.0.31-alpha+6e4a16d"
"version": "v1.0.31-alpha+4003343"
}
},
"validators": [
{
"title": "spend.mint",
"title": "spend.bar.mint",
"redeemer": {
"title": "_redeemer",
"schema": {
@ -21,15 +21,28 @@
{
"title": "output_reference",
"schema": {
"$ref": "#/definitions/aiken~1transaction~1OutputReference"
"$ref": "#/definitions/cardano~1transaction~1OutputReference"
}
}
],
"compiledCode": "589e0100003232323232323222322533300553330053370e900018031baa3232330010013758600460126ea8c008c024dd50019129998058008a6103d87a800013232533300a3375e600a60186ea80080244cdd2a40006601c00497ae0133004004001300f002300d0012300b00114a229405261365653330033370e900018021baa00115333006300537540022930b0b2b9a5573aaae7955cfaba05742ae881",
"hash": "0241f73ef186e53566d1b5a81eca50049b0d86833e6b88fe26b1e4bc"
"compiledCode": "58c90100003232323232323222533300332323232325332330093001300a375400426464a666016600660186ea8c8c8cc004004dd6180118079baa00922533301100114c0103d87a80001323253330103375e600a60246ea800803c4cdd2a40006602800497ae0133004004001301500230130012301100114a229414ccc028c008c02cdd50020a99980698061baa00414985858dd7180718059baa002370e90000b1806180680118058009805801180480098029baa00114984d9595cd2ab9d5573caae7d5d02ba157441",
"hash": "2edaecec5a072cd65b12395410ce6da7cafaffdf70506cbcb40b4df8"
},
{
"title": "spend.spend",
"title": "spend.bar.else",
"parameters": [
{
"title": "output_reference",
"schema": {
"$ref": "#/definitions/cardano~1transaction~1OutputReference"
}
}
],
"compiledCode": "58c90100003232323232323222533300332323232325332330093001300a375400426464a666016600660186ea8c8c8cc004004dd6180118079baa00922533301100114c0103d87a80001323253330103375e600a60246ea800803c4cdd2a40006602800497ae0133004004001301500230130012301100114a229414ccc028c008c02cdd50020a99980698061baa00414985858dd7180718059baa002370e90000b1806180680118058009805801180480098029baa00114984d9595cd2ab9d5573caae7d5d02ba157441",
"hash": "2edaecec5a072cd65b12395410ce6da7cafaffdf70506cbcb40b4df8"
},
{
"title": "spend.foo.spend",
"datum": {
"title": "_datum",
"schema": {
@ -42,8 +55,13 @@
"$ref": "#/definitions/Data"
}
},
"compiledCode": "58ef01000032323232323232222533300432330010013758601460166016600e6ea8c028c01cdd50011129998048008a501325333007333007533300a3253330083370e900118049baa00114bd6f7b63009bab300d300a375400264660020026eacc034c038c028dd518068019129998060008a60103d87a8000132323232533300d33722911050000000000000021533300d3371e91010500000000000000213374a9000198089ba60014bd700a6103d87a80001330060060033756601c0066eb8c030008c040008c0380045288a504a094452889980180180098060008a4c26cacae6955ceaab9e5573eae815d0aba21",
"hash": "f56561e01063b11146809755d9907147e79d3166aa5c65fba4040fd1"
"compiledCode": "5901230100003232323232323225333002323232323253330073370e900118041baa001132323232330010013758602060226022601a6ea8020894ccc03c0045280992999806999806a99980819299980719b8748008c03cdd50008a5eb7bdb1804dd5980998081baa001323300100137566026602860206ea8c04c00c894ccc048004530103d87a800013232323253330133372291105000000000000002153330133371e91010500000000000000213374a90001980b9ba60014bd700a6103d87a8000133006006003375660280066eb8c048008c058008c0500045288a504a0944528899801801800980900098071807801180680098049baa00116300b300c002300a001300a00230080013004375400229309b2b2b9a5573aaae7955cfaba05742ae881",
"hash": "cf024265a1ff4ab129cef178c64b8c4cab25d62129242e01e29bb3d1"
},
{
"title": "spend.foo.else",
"compiledCode": "5901230100003232323232323225333002323232323253330073370e900118041baa001132323232330010013758602060226022601a6ea8020894ccc03c0045280992999806999806a99980819299980719b8748008c03cdd50008a5eb7bdb1804dd5980998081baa001323300100137566026602860206ea8c04c00c894ccc048004530103d87a800013232323253330133372291105000000000000002153330133371e91010500000000000000213374a90001980b9ba60014bd700a6103d87a8000133006006003375660280066eb8c048008c058008c0500045288a504a0944528899801801800980900098071807801180680098049baa00116300b300c002300a001300a00230080013004375400229309b2b2b9a5573aaae7955cfaba05742ae881",
"hash": "cf024265a1ff4ab129cef178c64b8c4cab25d62129242e01e29bb3d1"
}
],
"definitions": {
@ -68,7 +86,7 @@
}
]
},
"aiken/transaction/OutputReference": {
"cardano/transaction/OutputReference": {
"title": "OutputReference",
"description": "An `OutputReference` is a unique reference to an output on-chain. The `output_index`\n corresponds to the position in the output list of the transaction (identified by its id)\n that produced that output",
"anyOf": [
@ -79,7 +97,7 @@
"fields": [
{
"title": "transaction_id",
"$ref": "#/definitions/aiken~1transaction~1TransactionId"
"$ref": "#/definitions/ByteArray"
},
{
"title": "output_index",
@ -88,23 +106,6 @@
]
}
]
},
"aiken/transaction/TransactionId": {
"title": "TransactionId",
"description": "A unique transaction identifier, as the hash of a transaction body. Note that the transaction id\n isn't a direct hash of the `Transaction` as visible on-chain. Rather, they correspond to hash\n digests of transaction body as they are serialized on the network.",
"anyOf": [
{
"title": "TransactionId",
"dataType": "constructor",
"index": 0,
"fields": [
{
"title": "hash",
"$ref": "#/definitions/ByteArray"
}
]
}
]
}
}
}

View File

@ -1,29 +1,33 @@
use aiken/dict
use aiken/list
use aiken/transaction.{Output, OutputReference, ScriptContext}
use aiken/transaction/value.{PolicyId}
use aiken/collection/dict
use aiken/collection/list
use cardano/assets.{PolicyId}
use cardano/transaction.{Output, OutputReference, Transaction}
const my_policy_id: PolicyId = #"0000000000"
pub fn has_policy_id(self: Output, policy_id: PolicyId) -> Bool {
self.value
|> value.tokens(policy_id)
|> assets.tokens(policy_id)
|> dict.is_empty
|> not
}
validator {
fn spend(_datum: Data, _redeemer: Data, ctx: ScriptContext) -> Bool {
ctx.transaction.outputs
validator foo {
spend(_datum: Option<Data>, _redeemer: Data, _o_ref: Data, self: Transaction) {
self.outputs
|> list.any(has_policy_id(_, my_policy_id))
}
else(_) {
fail
}
}
validator(output_reference: OutputReference) {
fn mint(_redeemer: Void, ctx: ScriptContext) -> Bool {
validator bar(output_reference: OutputReference) {
mint(_redeemer: Void, _policy_id, self: Transaction) {
when
list.find(
ctx.transaction.inputs,
self.inputs,
fn(input) { input.output_reference == output_reference },
)
is {
@ -31,4 +35,8 @@ validator(output_reference: OutputReference) {
None -> False
}
}
else(_) {
fail
}
}

View File

@ -29,7 +29,7 @@ test expect_ford1() {
],
)
expect Ford { owner, wheels, truck_bed_limit, .. }: Car = initial_car
owner == #"" && ( wheels == 4 && truck_bed_limit == 10000 )
owner == #"" && wheels == 4 && truck_bed_limit == 10000
}
test expect_ford2() {
@ -42,14 +42,14 @@ test expect_ford2() {
car_doors: [],
}
expect Ford { owner, wheels, remote_connect, .. } = initial_car
owner == #"2222222222" && ( wheels == 6 && remote_connect == #"" )
owner == #"2222222222" && wheels == 6 && remote_connect == #""
}
test expect_list1() {
let initial_car =
[5, 6, 7]
expect [a, b, c] = initial_car
a == 5 && ( b == 6 && c == 7 )
a == 5 && b == 6 && c == 7
}
test expect_list2() {

View File

@ -5,12 +5,12 @@
"plutusVersion": "v2",
"compiler": {
"name": "Aiken",
"version": "v1.0.31-alpha+6e4a16d"
"version": "v1.0.31-alpha+4003343"
}
},
"validators": [
{
"title": "foo.spend",
"title": "foo.foo.spend",
"datum": {
"title": "_datum",
"schema": {
@ -23,8 +23,13 @@
"$ref": "#/definitions/Void"
}
},
"compiledCode": "585201000032323232232232253330064a229309b2b299980219b8748000c014dd50008a99980398031baa001149858594ccc008cdc3a400060066ea800454ccc014c010dd50008a4c2c2cae6955ceaab9e5573f",
"hash": "84a516ff1f146f698164b5e64ff813e4e22ba2fa35491f2dc3d70935"
"compiledCode": "587601000032323232323225333002323232323253330073370e900118041baa0011323232324a2a66601466e1d2000300b375400a2a66601a60186ea80145261616300d300e002300c001300937540022c6014601600460120026012004600e00260086ea8004526136565734aae7555cf2ab9f5742ae89",
"hash": "c613c8326fea00dff179b7108f248b60c155881bbce544d84fe573e1"
},
{
"title": "foo.foo.else",
"compiledCode": "587601000032323232323225333002323232323253330073370e900118041baa0011323232324a2a66601466e1d2000300b375400a2a66601a60186ea80145261616300d300e002300c001300937540022c6014601600460120026012004600e00260086ea8004526136565734aae7555cf2ab9f5742ae89",
"hash": "c613c8326fea00dff179b7108f248b60c155881bbce544d84fe573e1"
}
],
"definitions": {

View File

@ -1,7 +1,11 @@
validator {
fn spend(_datum: Void, _redeemer: Void, _ctx: Void) -> Bool {
validator foo {
spend(_datum: Option<Void>, _redeemer: Void, oref: Data, _tx: Void) {
True
}
else(_) {
fail
}
}
test foo() {

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158433, nanos_since_epoch = 380614000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724776969, nanos_since_epoch = 835808000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158435, nanos_since_epoch = 363713000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724776974, nanos_since_epoch = 866167000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158434, nanos_since_epoch = 837171000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724776995, nanos_since_epoch = 750600000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158435, nanos_since_epoch = 367316000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724776974, nanos_since_epoch = 455232000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158436, nanos_since_epoch = 571367000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724777001, nanos_since_epoch = 587403000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -322,7 +322,7 @@ test get_proof_4() {
let h1: ByteArray = get_proof_item_value(p1)
let h2: ByteArray = get_proof_item_value(p2)
size_match && ( h1 == hash_fn(cat) && h2 == right_node_hash )
size_match && h1 == hash_fn(cat) && h2 == right_node_hash
}
fn do_from_list(

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158436, nanos_since_epoch = 551807000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724776997, nanos_since_epoch = 900786000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158435, nanos_since_epoch = 941669000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724777000, nanos_since_epoch = 47370000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158437, nanos_since_epoch = 43333000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724776959, nanos_since_epoch = 949879000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -3,14 +3,14 @@
[[requirements]]
name = "aiken-lang/stdlib"
version = "main"
version = "v2"
source = "github"
[[packages]]
name = "aiken-lang/stdlib"
version = "main"
version = "v2"
requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158434, nanos_since_epoch = 487166000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@v2" = [{ secs_since_epoch = 1724776963, nanos_since_epoch = 265617000 }, "cdbbce58b61deb385e7ea787a2e0fc2dc8fe94db9999e0e6275bc9c70e5796be"]

View File

@ -1,7 +1,8 @@
name = "aiken-lang/acceptance_test_071"
plutusVersion = "v3"
version = "0.0.0"
[[dependencies]]
name = 'aiken-lang/stdlib'
version = 'main'
version = 'v2'
source = 'github'

View File

@ -5,12 +5,12 @@
"plutusVersion": "v2",
"compiler": {
"name": "Aiken",
"version": "v1.0.31-alpha+6e4a16d"
"version": "v1.0.31-alpha+4003343"
}
},
"validators": [
{
"title": "spend.pool_contract",
"title": "spend.foo.spend",
"datum": {
"title": "datum",
"schema": {
@ -23,8 +23,13 @@
"$ref": "#/definitions/spend~1PoolRedeemer"
}
},
"compiledCode": "5904170100003232323232323232232325333004300230053754006264a6660100022c264a66666601a002264a6660140022c264a66666601e0022c2c2c26464a66601a0022c264a6666660240022c2c2c264a66601e6024006266601200e24a66601a6016601c6ea80044c94ccc044004584c94cccccc05800454ccc048c0540084c8c8c94ccc048c0400044c94ccc058004584c94cccccc06c0045858584c94ccc060c06c00c5401458dd68008b180c000980a1baa004153330123003001132533301600116132533333301b00115333017301a00215333014301230153754002264a6660300022c264a66666603a002264a6660340022c264a66666603e0022c2c2c264a666038603e006266602c0082a0122c2c6eb400458c070004c07000858585858c068004c058dd50008b0b0b0b0b0b180c000980a1baa004153330123370e9002000899299980b0008b099299999980d8008a99980b980d0010a99980a1809180a9baa001132533301800116132533333301d001132533301a00116132533333301f001161616132533301c301f0031333016004150091616375a0022c603800260380042c2c2c2c6034002602c6ea8004585858585858c060004c050dd50020b18091baa0031253330113232325333014301200114a226464646464a666032601400c26464a666036601860386ea80044c8c8c94ccc078c070c07cdd5000899192999810180f18109baa0011324a2604a60446ea800458cc01c03c00cc08cc080dd50008b198038068009804244101ff003020301d37540022940c00cc070dd5005180f180d9baa00713232533301b300c301c37540022646464a66603c6038603e6ea80044c8c94ccc080c078c084dd50008992513025302237540022c6600e01e006604660406ea800458cc01c034004c021220101ff003020301d37540022940c00cc070dd5005180f180d9baa00722330053758601260386ea8c024c070dd5001119baf300a301d37546008603a6ea80040088c074c07800488cc00cdd6180e980f180f180d1baa3007301a3754004466ebcc020c06cdd500080111191980080080191299980e0008a6103d87a800013232533301b300500213374a90001980f80125eb804cc010010004c080008c0780048cdd2a40006603266e95200233019375200297ae0330194c103d87a80004bd70180a1baa00130013014375400c4602e00229309b2b1b87480085858585858c04c004c03cdd50008b0b0b1bad00116300f001300f003375a0022c601800260180042c2c2c2c6014002600c6ea800c588894ccc018c010c01cdd5001899299980500080109929999998078008018018018018991929998068008028992999999809000803003003003099299980798090018a8040039bae001300f001300f003375c002601800260106ea800c004dc3a4000ae6955ceaab9e5573eae815d0aba25749",
"hash": "640debfa5063d2e3fd7f23e9777aaf3a0f575a99972ccda8748a49c0"
"compiledCode": "5903a2010000323232323232323225333002323232323253323300830013009375400426464646464a66601a6004601c6ea80204c94ccc044004584c94cccccc05800454ccc048c0540084c8c94ccc044c0180044c94ccc054004584c94cccccc0680045858584c94ccc05cc06800c5401458dd68008b180b80098099baa00315333011300a001132533301500116132533333301a00115333016301900215333013300830143754002264a66602e0022c264a666666038002264a6660320022c264a66666603c0022c2c2c264a666036603c00626660180082a0122c2c6eb400458c06c004c06c00858585858c064004c054dd50008b0b0b0b0b0b180b80098099baa003153330113370e9002000899299980a8008b099299999980d0008a99980b180c8010a9998099804180a1baa001132533301700116132533333301c001132533301900116132533333301e001161616132533301b301e003133300c004150091616375a0022c603600260360042c2c2c2c6032002602a6ea8004585858585858c05c004c04cdd50018b18089baa0021533300f30043010375400a264646464a666026601000229444c8c8c8c94ccc05cc0400144c8c8c94ccc068c03cc06cdd500089919299980e1808980e9baa0011324a26042603c6ea800458cc01406000cc07cc070dd50008b1980200b0009802a44101ff00301c3019375400c2646464a666034601e60366ea80044c8c94ccc070c044c074dd50008992513021301e37540022c6600a030006603e60386ea800458cc010058004c015220101ff00301c3019375400c44660086eb0c020c068dd5001119baf3009301b3754603c603e60366ea800400888cc00cdd6180e180e980e980c9baa00223375e601060346ea800400888c8cc00400400c894ccc06c004530103d87a800013232533301a300500213374a90001980f00125eb804cc010010004c07c008c0740048cdd2a40006603066e95200233018375200297ae0330184c103d87a80004bd7018099baa0013001301337540184602c002602860226ea8014585858585858c04c004c03cdd50040b1112999807980218081baa00313253330130010021325333333018001003003003003132325333016001005132533333301b0010060060060061325333018301b00315008007375c002603000260300066eb8004c054004c044dd50018009b8748000c03cc040008c038004c028dd50011b874800858c02cc030008c028004c028008c020004c010dd50008a4c26cacae6955ceaab9e5573eae815d0aba257481",
"hash": "10e8faa163e7c7699b978c2df5f2b1fadc9796e866d67b88799def6e"
},
{
"title": "spend.foo.else",
"compiledCode": "5903a2010000323232323232323225333002323232323253323300830013009375400426464646464a66601a6004601c6ea80204c94ccc044004584c94cccccc05800454ccc048c0540084c8c94ccc044c0180044c94ccc054004584c94cccccc0680045858584c94ccc05cc06800c5401458dd68008b180b80098099baa00315333011300a001132533301500116132533333301a00115333016301900215333013300830143754002264a66602e0022c264a666666038002264a6660320022c264a66666603c0022c2c2c264a666036603c00626660180082a0122c2c6eb400458c06c004c06c00858585858c064004c054dd50008b0b0b0b0b0b180b80098099baa003153330113370e9002000899299980a8008b099299999980d0008a99980b180c8010a9998099804180a1baa001132533301700116132533333301c001132533301900116132533333301e001161616132533301b301e003133300c004150091616375a0022c603600260360042c2c2c2c6032002602a6ea8004585858585858c05c004c04cdd50018b18089baa0021533300f30043010375400a264646464a666026601000229444c8c8c8c94ccc05cc0400144c8c8c94ccc068c03cc06cdd500089919299980e1808980e9baa0011324a26042603c6ea800458cc01406000cc07cc070dd50008b1980200b0009802a44101ff00301c3019375400c2646464a666034601e60366ea80044c8c94ccc070c044c074dd50008992513021301e37540022c6600a030006603e60386ea800458cc010058004c015220101ff00301c3019375400c44660086eb0c020c068dd5001119baf3009301b3754603c603e60366ea800400888cc00cdd6180e180e980e980c9baa00223375e601060346ea800400888c8cc00400400c894ccc06c004530103d87a800013232533301a300500213374a90001980f00125eb804cc010010004c07c008c0740048cdd2a40006603066e95200233018375200297ae0330184c103d87a80004bd7018099baa0013001301337540184602c002602860226ea8014585858585858c04c004c03cdd50040b1112999807980218081baa00313253330130010021325333333018001003003003003132325333016001005132533333301b0010060060060061325333018301b00315008007375c002603000260300066eb8004c054004c044dd50018009b8748000c03cc040008c038004c028dd50011b874800858c02cc030008c028004c028008c020004c010dd50008a4c26cacae6955ceaab9e5573eae815d0aba257481",
"hash": "10e8faa163e7c7699b978c2df5f2b1fadc9796e866d67b88799def6e"
}
],
"definitions": {

View File

@ -1,17 +1,15 @@
use aiken/collection/list
use aiken/hash.{Blake2b_224, Hash}
use aiken/interval.{Finite}
use aiken/list
use aiken/option
use aiken/transaction.{
Datum, InlineDatum, Input, Mint, Output, OutputReference, ScriptContext, Spend,
Transaction, TransactionId, ValidityRange,
}
use aiken/transaction/credential.{
Address, Script, ScriptCredential, VerificationKey, VerificationKeyCredential,
}
use aiken/transaction/value.{
use cardano/assets.{
AssetName, PolicyId, Value, add, flatten, from_asset, negate, quantity_of,
}
use cardano/credential.{Address, Script, VerificationKey}
use cardano/transaction.{
Datum, InlineDatum, Input, Output, OutputReference, Transaction, TransactionId,
ValidityRange,
}
// Datum/Redeemer pool
pub type PoolDatum {
@ -20,7 +18,7 @@ pub type PoolDatum {
lent_out: Int,
}
type PoolRedeemer {
pub type PoolRedeemer {
action: PoolRedeemerType,
}
@ -45,67 +43,73 @@ pub type CurrencySymbol {
asset_name: AssetName,
}
pub fn get_output(ctx: ScriptContext, address: Address) -> Option<Output> {
list.find(ctx.transaction.outputs, fn(output) { output.address == address })
pub fn get_output(transaction: Transaction, address: Address) -> Option<Output> {
list.find(transaction.outputs, fn(output) { output.address == address })
}
pub fn get_input(ctx: ScriptContext, address: Address) -> Option<Input> {
list.find(
ctx.transaction.inputs,
fn(input) { input.output.address == address },
)
pub fn get_input(transaction: Transaction, address: Address) -> Option<Input> {
list.find(transaction.inputs, fn(input) { input.output.address == address })
}
pub fn scripthash_address(scripthash: ByteArray) {
Address {
payment_credential: ScriptCredential(scripthash),
stake_credential: None,
}
Address { payment_credential: Script(scripthash), stake_credential: None }
}
pub fn validate_pool_deposit(
ctx: ScriptContext,
transaction: Transaction,
output_reference: OutputReference,
datum: PoolDatum,
redeemer: PoolDepositRedeemer,
) -> Bool {
let validator_address = scripthash_address(#"ff")
expect Some(pool_output) = get_output(ctx, validator_address)
expect Some(pool_input) = get_input(ctx, validator_address)
expect Some(pool_output) = get_output(transaction, validator_address)
expect Some(pool_input) = get_input(transaction, validator_address)
True
}
pub fn validate_pool_borrow(
ctx: ScriptContext,
transaction: Transaction,
output_reference: OutputReference,
datum: PoolDatum,
redeemer: PoolBorrowRedeemer,
) -> Bool {
let validator_address = scripthash_address(#"ff")
expect Some(pool_output) = get_output(ctx, validator_address)
expect Some(pool_input) = get_input(ctx, validator_address)
expect Some(pool_output) = get_output(transaction, validator_address)
expect Some(pool_input) = get_input(transaction, validator_address)
True
}
validator {
fn pool_contract(datum: PoolDatum, redeemer: PoolRedeemer, ctx: ScriptContext) {
validator foo {
spend(
datum: Option<PoolDatum>,
redeemer: PoolRedeemer,
output_ref: OutputReference,
transaction: Transaction,
) {
expect Some(datum) = datum
when redeemer.action is {
PoolWithdraw(_) -> True
PoolDeposit(pool_deposit_redeemer) ->
when ctx.purpose is {
Spend(output_ref) ->
validate_pool_deposit(ctx, output_ref, datum, pool_deposit_redeemer)
_ -> False
}
validate_pool_deposit(
transaction,
output_ref,
datum,
pool_deposit_redeemer,
)
PoolBorrow(pool_borrow_redeemer) ->
when ctx.purpose is {
Spend(output_ref) ->
validate_pool_borrow(ctx, output_ref, datum, pool_borrow_redeemer)
_ -> False
}
validate_pool_borrow(
transaction,
output_ref,
datum,
pool_borrow_redeemer,
)
}
}
else(_) {
fail
}
}

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158434, nanos_since_epoch = 466737000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724776989, nanos_since_epoch = 656788000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158432, nanos_since_epoch = 54533000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724776984, nanos_since_epoch = 618944000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158437, nanos_since_epoch = 127361000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724776985, nanos_since_epoch = 375340000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -3,14 +3,14 @@
[[requirements]]
name = "aiken-lang/stdlib"
version = "main"
version = "v2"
source = "github"
[[packages]]
name = "aiken-lang/stdlib"
version = "main"
version = "v2"
requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158435, nanos_since_epoch = 59914000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@v2" = [{ secs_since_epoch = 1724776958, nanos_since_epoch = 450634000 }, "cdbbce58b61deb385e7ea787a2e0fc2dc8fe94db9999e0e6275bc9c70e5796be"]

View File

@ -1,7 +1,8 @@
name = "aiken-lang/acceptance_test_077"
plutusVersion = "v3"
version = "0.0.0"
[[dependencies]]
name = 'aiken-lang/stdlib'
version = 'main'
version = 'v2'
source = 'github'

View File

@ -5,12 +5,12 @@
"plutusVersion": "v2",
"compiler": {
"name": "Aiken",
"version": "v1.0.31-alpha+6e4a16d"
"version": "v1.0.31-alpha+4003343"
}
},
"validators": [
{
"title": "spend.gift_card",
"title": "spend.foo.mint",
"redeemer": {
"title": "rdmr",
"schema": {
@ -27,15 +27,34 @@
{
"title": "utxo_ref",
"schema": {
"$ref": "#/definitions/aiken~1transaction~1OutputReference"
"$ref": "#/definitions/cardano~1transaction~1OutputReference"
}
}
],
"compiledCode": "5901ce010000323232323232322322232323232533300930040011533300c300b375400a2a0042c2a66601260060022a66601860166ea8014540085858c024dd50020929998041919192999805980318061baa00113232323253330123015002132325333011300c3012375401a2a666022646600200200c44a66602c00229404c94ccc050cdd7980c980b1baa301900201114a226600600600260320022a6660226016002266e3c00803c5280b0b1bad3012002375c60200022c602600264a66601c6010601e6ea800452f5bded8c026eacc04cc040dd500099191980080099198008009bab3015301630163016301600522533301400114bd6f7b630099191919299980a99b9148900002153330153371e91010000210031005133019337606ea4008dd3000998030030019bab3016003375c60280046030004602c00244a666026002298103d87a800013232323253330143372200e0042a66602866e3c01c0084cdd2a4000660306e980052f5c02980103d87a80001330060060033756602a0066eb8c04c008c05c008c054004dd7180918079baa00337586022002601a6ea800858c03cc040008c038004c028dd50008a4c26cac6e1d2002370e90001bae0015734aae7555cf2ab9f5740ae855d11",
"hash": "4ef9bf69c108d6abd0a5af057a519df56d7fb333de428563810495a0"
"compiledCode": "59018501000032323232323232232225333005323232323253323300b3001300c3754004264646464a66601e600a0022a66602460226ea801c540085854ccc03cc00c00454ccc048c044dd50038a8010b0b18079baa006132323232533301430170021323253330133009301437540162a666026646600200200c44a66603000229404c94ccc058cdd7980d980c1baa301b00201314a226600600600260360022a666026600e002266e3c0080445280b0b1bad3014002375c60240022c602a00264a666020600860226ea800452f5bded8c026eacc054c048dd500099198008009bab3015301630163016301600322533301400114c103d87a80001323232325333015337220140042a66602a66e3c0280084cdd2a4000660326e980052f5c02980103d87a80001330060060033756602c0066eb8c050008c060008c058004dd6180980098079baa007370e90011bae3010300d37540046e1d200016300e300f002300d001300d002300b0013007375400229309b2b1bae0015734aae7555cf2ab9f5740ae855d11",
"hash": "784b2caa5e99b3d37361e3c3d51e7fd39f616025037eee525b73c5da"
},
{
"title": "spend2.backtrace",
"title": "spend.foo.else",
"parameters": [
{
"title": "token_name",
"schema": {
"$ref": "#/definitions/ByteArray"
}
},
{
"title": "utxo_ref",
"schema": {
"$ref": "#/definitions/cardano~1transaction~1OutputReference"
}
}
],
"compiledCode": "59018501000032323232323232232225333005323232323253323300b3001300c3754004264646464a66601e600a0022a66602460226ea801c540085854ccc03cc00c00454ccc048c044dd50038a8010b0b18079baa006132323232533301430170021323253330133009301437540162a666026646600200200c44a66603000229404c94ccc058cdd7980d980c1baa301b00201314a226600600600260360022a666026600e002266e3c0080445280b0b1bad3014002375c60240022c602a00264a666020600860226ea800452f5bded8c026eacc054c048dd500099198008009bab3015301630163016301600322533301400114c103d87a80001323232325333015337220140042a66602a66e3c0280084cdd2a4000660326e980052f5c02980103d87a80001330060060033756602c0066eb8c050008c060008c058004dd6180980098079baa007370e90011bae3010300d37540046e1d200016300e300f002300d001300d002300b0013007375400229309b2b1bae0015734aae7555cf2ab9f5740ae855d11",
"hash": "784b2caa5e99b3d37361e3c3d51e7fd39f616025037eee525b73c5da"
},
{
"title": "spend2.foo.spend",
"datum": {
"title": "_datum",
"schema": {
@ -48,8 +67,13 @@
"$ref": "#/definitions/Void"
}
},
"compiledCode": "58ac010000323232323232322323223225333007533300730053008375464660020026eb0c034c038c038c028dd5180698051baa00222533300c00114c0103d87a800013232533300b4a2266e9520003300f0024bd70099802002000980800118070008a511614984d9594ccc014c00cc018dd50008a99980418039baa001149858594ccc00cc004c010dd50010a99980318029baa00214985858dc3a4000ae6955ceaab9e5573eae815d0aba201",
"hash": "c58d31e63ad2d807c2188dfb1deafc433fe0f6a867e5cf8df68f068f"
"compiledCode": "58c70100003232323232323225333002323232323253330073370e900118041baa001132323232533300b3370e900018061baa32330010013758602260246024601c6ea8024894ccc0400045300103d87a800013232533300f4a2266e952000330130024bd70099802002000980a00118090008a5116533300a3370e900018059baa0051533300d300c375400a2930b0b18071807801180680098049baa00116300b300c002300a001300a00230080013004375400229309b2b2b9a5573aaae7955cfaba05742ae881",
"hash": "c4700bfdb383c890d2a4c4505d3fc6d4b51e1998c3f33dd83e12ba44"
},
{
"title": "spend2.foo.else",
"compiledCode": "58c70100003232323232323225333002323232323253330073370e900118041baa001132323232533300b3370e900018061baa32330010013758602260246024601c6ea8024894ccc0400045300103d87a800013232533300f4a2266e952000330130024bd70099802002000980a00118090008a5116533300a3370e900018059baa0051533300d300c375400a2930b0b18071807801180680098049baa00116300b300c002300a001300a00230080013004375400229309b2b2b9a5573aaae7955cfaba05742ae881",
"hash": "c4700bfdb383c890d2a4c4505d3fc6d4b51e1998c3f33dd83e12ba44"
}
],
"definitions": {
@ -70,7 +94,7 @@
}
]
},
"aiken/transaction/OutputReference": {
"cardano/transaction/OutputReference": {
"title": "OutputReference",
"description": "An `OutputReference` is a unique reference to an output on-chain. The `output_index`\n corresponds to the position in the output list of the transaction (identified by its id)\n that produced that output",
"anyOf": [
@ -81,7 +105,7 @@
"fields": [
{
"title": "transaction_id",
"$ref": "#/definitions/aiken~1transaction~1TransactionId"
"$ref": "#/definitions/ByteArray"
},
{
"title": "output_index",
@ -91,23 +115,6 @@
}
]
},
"aiken/transaction/TransactionId": {
"title": "TransactionId",
"description": "A unique transaction identifier, as the hash of a transaction body. Note that the transaction id\n isn't a direct hash of the `Transaction` as visible on-chain. Rather, they correspond to hash\n digests of transaction body as they are serialized on the network.",
"anyOf": [
{
"title": "TransactionId",
"dataType": "constructor",
"index": 0,
"fields": [
{
"title": "hash",
"$ref": "#/definitions/ByteArray"
}
]
}
]
},
"spend/Action": {
"title": "Action",
"anyOf": [

View File

@ -1,23 +1,19 @@
use aiken/dict
use aiken/list.{find, foldr}
use aiken/transaction.{Input,
OutputReference, ScriptContext, Spend, Transaction} as tx
use aiken/transaction/value.{add, zero}
use aiken/collection/dict
use aiken/collection/list.{find, foldr}
use cardano/assets.{PolicyId, add, zero}
use cardano/transaction.{Input, OutputReference, Transaction} as tx
type Action {
pub type Action {
Mint
Burn
}
validator(token_name: ByteArray, utxo_ref: OutputReference) {
fn gift_card(rdmr: Action, ctx: ScriptContext) -> Bool {
let ScriptContext { transaction, purpose } = ctx
expect tx.Mint(policy_id) = purpose
validator foo(token_name: ByteArray, utxo_ref: OutputReference) {
mint(rdmr: Action, policy_id: PolicyId, transaction: Transaction) {
let Transaction { inputs, mint, .. } = transaction
expect [Pair(asset_name, amount)] =
mint
|> value.from_minted_value
|> value.tokens(policy_id)
|> assets.tokens(policy_id)
|> dict.to_pairs()
when rdmr is {
Mint -> {
@ -28,4 +24,8 @@ validator(token_name: ByteArray, utxo_ref: OutputReference) {
Burn -> todo @"burn"
}
}
else(_) {
fail
}
}

View File

@ -1,15 +1,24 @@
use aiken/list
use aiken/transaction.{Output, ScriptContext}
use aiken/collection/list
use cardano/transaction.{Output, Transaction}
validator {
fn backtrace(_datum: Void, _redeemer: Void, context: ScriptContext) -> Bool {
expect Some(_) = list.find(context.transaction.outputs, fn(_) { True })
let _ = find_stuff(context)
validator foo {
spend(
_datum: Option<Void>,
_redeemer: Void,
_ref: Data,
transaction: Transaction,
) {
expect Some(_) = list.find(transaction.outputs, fn(_) { True })
let _ = find_stuff(transaction)
True
}
else(_) {
fail
}
}
fn find_stuff(context: ScriptContext) -> Output {
expect Some(stuff) = list.find(context.transaction.outputs, fn(_) { True })
fn find_stuff(transaction: Transaction) -> Output {
expect Some(stuff) = list.find(transaction.outputs, fn(_) { True })
stuff
}

View File

@ -5,12 +5,12 @@
"plutusVersion": "v2",
"compiler": {
"name": "Aiken",
"version": "v1.0.31-alpha+6e4a16d"
"version": "v1.0.31-alpha+4003343"
}
},
"validators": [
{
"title": "foo.spend",
"title": "foo.foo.spend",
"datum": {
"title": "datum",
"schema": {
@ -20,42 +20,32 @@
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/RedeemerWrapper$Int"
"$ref": "#/definitions/Int"
}
},
"compiledCode": "5850010000323232322253330033370e900018021baa001153330033370e6eb4009205414984d9584c8c894ccc018cdc399b800030024815052613656375a600e600c6ea8008dd68012b9a5573aaae795d09",
"hash": "dcac7ebcaf29721b4b48cc73775fa91939ad9015ea267b697e92b051"
"compiledCode": "589a01000032323232322533300232323232323253330083370e9001000899191919299980619b8748000c034dd5001099b87337006eb4c03cc038dd5001000a40a82c6eb4018c034c038008c030004c028dd50010a99980419b87480000044c8cdc39bad00448150dd7180598051baa00216300837540026012601400460100026010004600c00260086ea8004526136565734aae7555cf2ba15745",
"hash": "86f0253dd8dd836d6588f5794eb8f08edd4476df28b607553126846e"
},
{
"title": "foo.mint",
"title": "foo.foo.mint",
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/Int"
}
},
"compiledCode": "5850010000323232322253330033370e900018021baa001153330033370e6eb4009205414984d9584c8c894ccc018cdc399b800030024815052613656375a600e600c6ea8008dd68012b9a5573aaae795d09",
"hash": "dcac7ebcaf29721b4b48cc73775fa91939ad9015ea267b697e92b051"
"compiledCode": "589a01000032323232322533300232323232323253330083370e9001000899191919299980619b8748000c034dd5001099b87337006eb4c03cc038dd5001000a40a82c6eb4018c034c038008c030004c028dd50010a99980419b87480000044c8cdc39bad00448150dd7180598051baa00216300837540026012601400460100026010004600c00260086ea8004526136565734aae7555cf2ba15745",
"hash": "86f0253dd8dd836d6588f5794eb8f08edd4476df28b607553126846e"
},
{
"title": "foo.foo.else",
"compiledCode": "589a01000032323232322533300232323232323253330083370e9001000899191919299980619b8748000c034dd5001099b87337006eb4c03cc038dd5001000a40a82c6eb4018c034c038008c030004c028dd50010a99980419b87480000044c8cdc39bad00448150dd7180598051baa00216300837540026012601400460100026010004600c00260086ea8004526136565734aae7555cf2ba15745",
"hash": "86f0253dd8dd836d6588f5794eb8f08edd4476df28b607553126846e"
}
],
"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"
}
]
}
]
}
}
}

View File

@ -1,9 +1,14 @@
validator {
fn spend(datum: Int, redeemer: Int, _context: Data) {
validator foo {
spend(datum: Option<Int>, redeemer: Int, _ref: Data, _transaction: Data) {
expect Some(datum) = datum
datum + redeemer == 42
}
fn mint(redeemer: Int, _context: Data) {
mint(redeemer: Int, _policy: ByteArray, _transaction: Data) {
redeemer == 42
}
else(_) {
fail
}
}

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158431, nanos_since_epoch = 785683000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724776965, nanos_since_epoch = 937696000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158433, nanos_since_epoch = 377116000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724776958, nanos_since_epoch = 302886000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158433, nanos_since_epoch = 339674000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1724776958, nanos_since_epoch = 249560000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]

View File

@ -3,14 +3,14 @@
[[requirements]]
name = "aiken-lang/stdlib"
version = "main"
version = "v2"
source = "github"
[[packages]]
name = "aiken-lang/stdlib"
version = "main"
version = "v2"
requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1723158437, nanos_since_epoch = 77334000 }, "5e58899446492a704d0927a43299139856bef746e697b55895ba34206fa28452"]
"aiken-lang/stdlib@v2" = [{ secs_since_epoch = 1724776991, nanos_since_epoch = 269398000 }, "cdbbce58b61deb385e7ea787a2e0fc2dc8fe94db9999e0e6275bc9c70e5796be"]

View File

@ -4,5 +4,5 @@ description = ""
[[dependencies]]
name = 'aiken-lang/stdlib'
version = 'main'
version = 'v2'
source = 'github'

View File

@ -5,12 +5,12 @@
"plutusVersion": "v2",
"compiler": {
"name": "Aiken",
"version": "v1.0.31-alpha+6e4a16d"
"version": "v1.0.31-alpha+4003343"
}
},
"validators": [
{
"title": "other.validate",
"title": "other.validate.spend",
"datum": {
"title": "raw_datum",
"schema": {
@ -23,8 +23,13 @@
"$ref": "#/definitions/Data"
}
},
"compiledCode": "5902b1010000323232323232322225333004323232533300730033008375400c264a6660160022c264a66666601e0022a666018601c00426464a666016600e002264a66601e0022c264a6666660260022a66602060240042a66601a6012601c6ea80044c94ccc044004584c94cccccc05400454ccc048c05000854ccc03cc02cc040dd500089929998098008b099299999980b800899299980a8008b099299999980c8008a99980b180c001099980680189919299980a9808800899299980c8008b099299999980e8008a99980d180e00109919299980c980a800899299980e8008b09929999998108008a99980f1810001099980a8008a8020b0b0b0b0b0b180f000980d9baa003153330193014001132533301d001161325333333021001161616132325333020001161325333333024001161616132325333023001161325333333027001161616132533302530270031500b16375a0022c604800260480066eb400458c084004c08400cdd68008b180f000980d9baa00316301937540042a0082c2c2c2c2c6034002602e6ea800c54ccc054c04000454ccc060c05cdd50018a8010b0b180a9baa0021500a16161616161630160013016002161616163014001301137540022c2c2c2c2c2c6024002601e6ea8004585858585858c040004c034dd50018a99980598030008a99980718069baa003150021616300b37540042a666012600a60146ea8c034c02cdd50040a5114a02c2c2c2c2c601800260126ea801858888c94ccc028c0180044c94ccc03800400c4c94cccccc0480040100100100104c94ccc040c04800c54018014dd7000980780098061baa0041533300a3005001132533300e00100313253333330120010040040040041325333010301200315006005375c002601e00260186ea8010008c028dd50019b8748008dc3a400029309b2b2b9a5573aaae7955cfaba15744ae91",
"hash": "22fcdd5defc5c63e55afed5ee2e88c9c695809f8b1e99342e045438d"
"compiledCode": "5902fd010000323232323232322533300232323232325332330083001300937540042646464a664660186002601a6ea80084c8c94ccc038c00cc03cdd500109929998090008b099299999980b0008a999809980a8010991929998091803800899299980b0008b099299999980d0008a99980b980c8010a99980a1804980a9baa001132533301800116132533333301c00115333019301b00215333016300b30173754002264a6660340022c264a66666603c002264a6660380022c264a6666660400022a66603a603e004266601a00626464a6660386022002264a6660400022c264a6666660480022a666042604600426464a666040602a002264a6660480022c264a6666660500022a66604a604e004266602a0022a0082c2c2c2c2c2c604a00260446ea800c54ccc080c0640044c94ccc090004584c94cccccc0a00045858584c8c94ccc09c004584c94cccccc0ac0045858584c8c94ccc0a8004584c94cccccc0b80045858584c94ccc0b0c0b800c5402c58dd68008b181580098158019bad0011630280013028003375a0022c604a00260446ea800c58c080dd50010a8020b0b0b0b0b1810800980f1baa0031533301c30150011533301f301e37540062a0042c2c60386ea800854028585858585858c074004c07400858585858c06c004c060dd50008b0b0b0b0b0b180c800980b1baa0011616161616163017001301437540062a66602460160022a66602a60286ea800c540085858c048dd50010a999808180298089baa30143012375400829445280b0b0b0b0b180980098081baa002162223253330113006001132533301500100313253333330190010040040040041325333017301900315006005375c002602c00260266ea801054ccc044c0280044c94ccc05400400c4c94cccccc0640040100100100104c94ccc05cc06400c54018014dd7000980b00098099baa004002301137540066020601c6ea8008dc3a40002c601c601e004601a00260146ea8008dc3a40042c6014601600460120026012004600e00260086ea8004526136565734aae7555cf2ab9f5742ae895d21",
"hash": "a63866b5537d618101fccdf892f6bb58f0f1092bc483bfedca3a813e"
},
{
"title": "other.validate.else",
"compiledCode": "5902fd010000323232323232322533300232323232325332330083001300937540042646464a664660186002601a6ea80084c8c94ccc038c00cc03cdd500109929998090008b099299999980b0008a999809980a8010991929998091803800899299980b0008b099299999980d0008a99980b980c8010a99980a1804980a9baa001132533301800116132533333301c00115333019301b00215333016300b30173754002264a6660340022c264a66666603c002264a6660380022c264a6666660400022a66603a603e004266601a00626464a6660386022002264a6660400022c264a6666660480022a666042604600426464a666040602a002264a6660480022c264a6666660500022a66604a604e004266602a0022a0082c2c2c2c2c2c604a00260446ea800c54ccc080c0640044c94ccc090004584c94cccccc0a00045858584c8c94ccc09c004584c94cccccc0ac0045858584c8c94ccc0a8004584c94cccccc0b80045858584c94ccc0b0c0b800c5402c58dd68008b181580098158019bad0011630280013028003375a0022c604a00260446ea800c58c080dd50010a8020b0b0b0b0b1810800980f1baa0031533301c30150011533301f301e37540062a0042c2c60386ea800854028585858585858c074004c07400858585858c06c004c060dd50008b0b0b0b0b0b180c800980b1baa0011616161616163017001301437540062a66602460160022a66602a60286ea800c540085858c048dd50010a999808180298089baa30143012375400829445280b0b0b0b0b180980098081baa002162223253330113006001132533301500100313253333330190010040040040041325333017301900315006005375c002602c00260266ea801054ccc044c0280044c94ccc05400400c4c94cccccc0640040100100100104c94ccc05cc06400c54018014dd7000980b00098099baa004002301137540066020601c6ea8008dc3a40002c601c601e004601a00260146ea8008dc3a40042c6014601600460120026012004600e00260086ea8004526136565734aae7555cf2ab9f5742ae895d21",
"hash": "a63866b5537d618101fccdf892f6bb58f0f1092bc483bfedca3a813e"
}
],
"definitions": {

View File

@ -1,5 +1,5 @@
use aiken/transaction.{ScriptContext}
use aiken/transaction/credential.{Address}
use cardano/credential.{Address}
use cardano/transaction.{Transaction}
type TestData {
addr: Address,
@ -9,13 +9,18 @@ type TestDatum {
data: Option<TestData>,
}
validator {
fn validate(raw_datum: Data, _redeemer: Data, _context: ScriptContext) -> Bool {
expect datum: TestDatum = raw_datum
validator validate {
spend(raw_datum: Option<Data>, _redeemer: Data, oref: Data, _tx: Transaction) {
expect Some(datum): Option<Data> = raw_datum
expect datum: TestDatum = datum
let TestDatum { data } = datum
when data is {
Some(_) -> True
None -> False
}
}
else(_) {
fail
}
}

Some files were not shown because too many files have changed in this diff Show More