Files
aiken/crates/aiken-project/src/blueprint/validator.rs
KtorZ 961e323c36 Enable iterating over validator's parameters with a callback
This is how we'll construct parameters interactively. We need to lookup the definition, and provide a data representation for it.
2023-08-19 13:39:39 -04:00

1377 lines
47 KiB
Rust

use super::{
definitions::Definitions,
error::Error,
parameter::Parameter,
schema::{Annotated, Schema},
};
use crate::module::{CheckedModule, CheckedModules};
use std::rc::Rc;
use aiken_lang::{
ast::{TypedArg, TypedFunction, TypedValidator},
gen_uplc::CodeGenerator,
};
use miette::NamedSource;
use serde;
use uplc::{
ast::{Constant, DeBruijn, Program, Term},
PlutusData,
};
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
pub struct Validator {
pub title: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub datum: Option<Parameter>,
pub redeemer: Parameter,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub parameters: Vec<Parameter>,
#[serde(flatten)]
pub program: Program<DeBruijn>,
#[serde(skip_serializing_if = "Definitions::is_empty")]
#[serde(default)]
pub definitions: Definitions<Annotated<Schema>>,
}
impl Validator {
pub fn from_checked_module(
modules: &CheckedModules,
generator: &mut CodeGenerator,
module: &CheckedModule,
def: &TypedValidator,
) -> Vec<Result<Validator, Error>> {
let program = generator.generate(def).try_into().unwrap();
let is_multi_validator = def.other_fun.is_some();
let mut validators = vec![Validator::create_validator_blueprint(
modules,
module,
&program,
&def.params,
&def.fun,
is_multi_validator,
)];
if let Some(ref other_func) = def.other_fun {
validators.push(Validator::create_validator_blueprint(
modules,
module,
&program,
&def.params,
other_func,
is_multi_validator,
));
}
validators
}
fn create_validator_blueprint(
modules: &CheckedModules,
module: &CheckedModule,
program: &Program<DeBruijn>,
params: &[TypedArg],
func: &TypedFunction,
is_multi_validator: bool,
) -> Result<Validator, Error> {
let mut args = func.arguments.iter().rev();
let (_, redeemer, datum) = (args.next(), args.next().unwrap(), args.next());
let mut arguments = Vec::with_capacity(params.len() + func.arguments.len());
arguments.extend(params.to_vec());
arguments.extend(func.arguments.clone());
let mut definitions = Definitions::new();
let parameters = params
.iter()
.map(|param| {
Annotated::from_type(modules.into(), &param.tipo, &mut definitions)
.map(|schema| Parameter {
title: Some(param.arg_name.get_label()),
schema,
})
.map_err(|error| Error::Schema {
error,
location: param.location,
source_code: NamedSource::new(
module.input_path.display().to_string(),
module.code.clone(),
),
})
})
.collect::<Result<_, _>>()?;
Ok(Validator {
title: format!("{}.{}", &module.name, &func.name),
description: func.doc.clone(),
parameters,
datum: datum
.map(|datum| {
Annotated::from_type(modules.into(), &datum.tipo, &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,
}),
redeemer: Annotated::from_type(modules.into(), &redeemer.tipo, &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,
},
})?,
program: program.clone(),
definitions,
})
}
}
impl Validator {
pub fn apply(
self,
definitions: &Definitions<Annotated<Schema>>,
arg: &Term<DeBruijn>,
) -> Result<Self, Error> {
match self.parameters.split_first() {
None => Err(Error::NoParametersToApply),
Some((head, tail)) => {
head.validate(definitions, arg)?;
Ok(Self {
program: self.program.apply_term(arg),
parameters: tail.to_vec(),
..self
})
}
}
}
pub fn ask_next_parameter<F>(
&self,
definitions: &Definitions<Annotated<Schema>>,
ask: F,
) -> Result<Term<DeBruijn>, Error>
where
F: Fn(&Annotated<Schema>, &Definitions<Annotated<Schema>>) -> Result<PlutusData, Error>,
{
match self.parameters.split_first() {
None => Err(Error::NoParametersToApply),
Some((head, _)) => {
let schema = definitions
.lookup(&head.schema)
.map(|s| {
Ok(Annotated {
title: s.title.clone().or_else(|| head.title.clone()),
description: s.description.clone(),
annotated: s.annotated.clone(),
})
})
.unwrap_or_else(|| {
Err(Error::UnresolvedSchemaReference {
reference: head.schema.clone(),
})
})?;
let data = ask(&schema, definitions)?;
Ok(Term::Constant(Rc::new(Constant::Data(data.clone()))))
}
}
}
}
#[cfg(test)]
mod tests {
use assert_json_diff::assert_json_eq;
use serde_json::{self, json};
use std::collections::HashMap;
use aiken_lang::{self, builtins};
use uplc::ast as uplc_ast;
use crate::tests::TestProject;
use super::{
super::{
definitions::{Definitions, Reference},
error::Error,
schema::{Annotated, Constructor, Data, Declaration, Items, Schema},
},
*,
};
fn assert_validator(source_code: &str, expected: serde_json::Value) {
let mut project = TestProject::new();
let modules = CheckedModules::singleton(project.check(project.parse(source_code)));
let mut generator = modules.new_generator(
&project.functions,
&project.data_types,
&project.module_types,
true,
);
let (validator, def) = modules
.validators()
.next()
.expect("source code did no yield any validator");
let validators = Validator::from_checked_module(&modules, &mut generator, validator, def);
if validators.len() > 1 {
panic!("Multi-validator given to test bench. Don't do that.")
}
let validator = validators
.get(0)
.unwrap()
.as_ref()
.expect("Failed to create validator blueprint");
println!("{}", serde_json::to_string_pretty(validator).unwrap());
assert_json_eq!(serde_json::to_value(validator).unwrap(), expected);
}
fn fixture_definitions() -> Definitions<Annotated<Schema>> {
let mut definitions = Definitions::new();
// #/definitions/Int
//
// {
// "dataType": "integer"
// }
definitions
.register::<_, Error>(&builtins::int(), &HashMap::new(), |_| {
Ok(Schema::Data(Data::Integer).into())
})
.unwrap();
// #/definitions/ByteArray
//
// {
// "dataType": "bytes"
// }
definitions
.register::<_, Error>(&builtins::byte_array(), &HashMap::new(), |_| {
Ok(Schema::Data(Data::Bytes).into())
})
.unwrap();
// #/definitions/Bool
//
// {
// "anyOf": [
// {
// "dataType": "constructor",
// "index": 0,
// "fields": []
// },
// {
// "dataType": "constructor",
// "index": 1,
// "fields": []
// },
// ]
// }
definitions.insert(
&Reference::new("Bool"),
Schema::Data(Data::AnyOf(vec![
// False
Constructor {
index: 0,
fields: vec![],
}
.into(),
// True
Constructor {
index: 1,
fields: vec![],
}
.into(),
]))
.into(),
);
definitions
}
#[test]
fn mint_basic() {
assert_validator(
r#"
validator {
fn mint(redeemer: Data, ctx: Data) {
True
}
}
"#,
json!({
"title": "test_module.mint",
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/Data"
}
},
"compiledCode": "4f010000322253330034a22930b2b9a1",
"hash": "69eb6e27b7098c51cef74d8929553456e0ff6748c50a08c0daae7986",
"definitions": {
"Data": {
"title": "Data",
"description": "Any Plutus data."
}
}
}),
);
}
#[test]
fn mint_parameterized() {
assert_validator(
r#"
validator(utxo_ref: Int) {
fn mint(redeemer: Data, ctx: Data) {
True
}
}
"#,
json!({
"title": "test_module.mint",
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/Data"
}
},
"parameters": [
{
"title": "utxo_ref",
"schema": {
"$ref": "#/definitions/Int"
}
}
],
"compiledCode": "54010000322322253330054a22930b1bad00157341",
"hash": "0e31a2048fe4751926c4a1e5fd93c9c2ecc8035777884c15db157d11",
"definitions": {
"Data": {
"title": "Data",
"description": "Any Plutus data."
},
"Int": {
"dataType": "integer"
}
}
}),
);
}
#[test]
fn simplified_hydra() {
assert_validator(
r#"
/// On-chain state
type State {
/// The contestation period as a number of seconds
contestationPeriod: ContestationPeriod,
/// List of public key hashes of all participants
parties: List<Party>,
utxoHash: Hash<Blake2b_256>,
}
/// A Hash digest for a given algorithm.
type Hash<alg> = ByteArray
type Blake2b_256 { Blake2b_256 }
/// Whatever
type ContestationPeriod {
/// A positive, non-zero number of seconds.
ContestationPeriod(Int)
}
type Party =
ByteArray
type Input {
CollectCom
Close
/// Abort a transaction
Abort
}
validator {
fn simplified_hydra(datum: State, redeemer: Input, ctx: Data) {
True
}
}
"#,
json!({
"title": "test_module.simplified_hydra",
"datum": {
"title": "datum",
"schema": {
"$ref": "#/definitions/test_module~1State"
}
},
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/test_module~1Input"
}
},
"compiledCode": "59029101000032323232323232323232322223232533300a4a22930b19299980519b874800000454ccc038c020010526153300b4911d4578706563746564206e6f206669656c647320666f7220436f6e73747200161533300a3370e90010008a99980718040020a4c2a6601692011d4578706563746564206e6f206669656c647320666f7220436f6e73747200161533300a3370e90020008a99980718040020a4c2a6601692011d4578706563746564206e6f206669656c647320666f7220436f6e7374720016153300b4912b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e74001630080033253330093370e900000089919191919192999809980a8010991924c646600200200a44a66602c00229309919801801980c801191bae00130170013253330103370e900000089919299980b180c0010a4c2a660269201334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2065787065637465640016375a602c002601c00c2a660229212b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e740016300e00515330104901334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2065787065637465640016375c602600260260046eb0c044004c044008c03c004c01c01054cc0292412b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e740016300700333001001480008888cccc01ccdc38008018061199980280299b8000448008c0380040080088c018dd5000918021baa0015734ae7155ceaab9e5573eae855d11",
"hash": "401a6c4bac4f3554a9bbe260aa12d2eec8c97bf903d23cd6ad426d1e",
"definitions": {
"ByteArray": {
"dataType": "bytes"
},
"Int": {
"dataType": "integer"
},
"List$ByteArray": {
"dataType": "list",
"items": {
"$ref": "#/definitions/ByteArray"
}
},
"test_module/ContestationPeriod": {
"title": "ContestationPeriod",
"description": "Whatever",
"anyOf": [
{
"title": "ContestationPeriod",
"description": "A positive, non-zero number of seconds.",
"dataType": "constructor",
"index": 0,
"fields": [
{
"$ref": "#/definitions/Int"
}
]
}
]
},
"test_module/Input": {
"title": "Input",
"anyOf": [
{
"title": "CollectCom",
"dataType": "constructor",
"index": 0,
"fields": []
},
{
"title": "Close",
"dataType": "constructor",
"index": 1,
"fields": []
},
{
"title": "Abort",
"description": "Abort a transaction",
"dataType": "constructor",
"index": 2,
"fields": []
}
]
},
"test_module/State": {
"title": "State",
"description": "On-chain state",
"anyOf": [
{
"title": "State",
"dataType": "constructor",
"index": 0,
"fields": [
{
"title": "contestationPeriod",
"description": "The contestation period as a number of seconds",
"$ref": "#/definitions/test_module~1ContestationPeriod"
},
{
"title": "parties",
"description": "List of public key hashes of all participants",
"$ref": "#/definitions/List$ByteArray"
},
{
"title": "utxoHash",
"$ref": "#/definitions/ByteArray"
}
]
}
]
}
}
}),
);
}
#[test]
fn tuples() {
assert_validator(
r#"
validator {
fn tuples(datum: (Int, ByteArray), redeemer: (Int, Int, Int), ctx: Void) {
True
}
}
"#,
json!({
"title": "test_module.tuples",
"datum": {
"title": "datum",
"schema": {
"$ref": "#/definitions/Tuple$Int_ByteArray"
}
},
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/Tuple$Int_Int_Int"
}
},
"compiledCode": "585f01000032323232322223232323253330084a22930b191919191924c6eb4c038004c038008dd6980600098060011bad300a0013758008646eb8c020008dd69803000991919bb03009002300900130090013758006ae6955ceaab9e5742ae881",
"hash": "91b63a27e3a0523a1ccef050cb77537ea0b5e3da2e00a05f7db07db2",
"definitions": {
"ByteArray": {
"dataType": "bytes"
},
"Int": {
"dataType": "integer"
},
"Tuple$Int_ByteArray": {
"title": "Tuple",
"dataType": "list",
"items": [
{
"$ref": "#/definitions/Int"
},
{
"$ref": "#/definitions/ByteArray"
}
]
},
"Tuple$Int_Int_Int": {
"title": "Tuple",
"dataType": "list",
"items": [
{
"$ref": "#/definitions/Int"
},
{
"$ref": "#/definitions/Int"
},
{
"$ref": "#/definitions/Int"
}
]
}
}
}),
)
}
#[test]
fn generics() {
assert_validator(
r#"
type Either<left, right> {
Left(left)
Right(right)
}
type Interval<a> {
Finite(a)
Infinite
}
validator {
fn generics(redeemer: Either<ByteArray, Interval<Int>>, ctx: Void) {
True
}
}
"#,
json!({
"title": "test_module.generics",
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/test_module~1Either$ByteArray_test_module~1Interval$Int"
}
},
"compiledCode": "59020a0100003232323232323232323232223253330084a22930b19299980419b87480000044c8c94ccc038c040008526153300b4901334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2065787065637465640016375c601c002600c0062a66601066e1d200200113232533300e3010002132498c94ccc02ccdc3a400000226464a66602260260042930a99807249334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2065787065637465640016375a602200260120042a66601666e1d20020011533300f3009002149854cc03124011d4578706563746564206e6f206669656c647320666f7220436f6e7374720016153300c4912b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e7400163009001153300b4901334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2065787065637465640016300e001300600315330094912b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e740016300600233001001480008888cccc01ccdc38008018061199980280299b8000448008c0380040080088c018dd5000918021baa0015734ae7155ceaab9e5573eae855d11",
"hash": "8439b07179746c195c7631777b49e48c2931887547e3258f5f4a59f0",
"definitions": {
"ByteArray": {
"dataType": "bytes"
},
"Int": {
"dataType": "integer"
},
"test_module/Either$ByteArray_test_module/Interval$Int": {
"title": "Either",
"anyOf": [
{
"title": "Left",
"dataType": "constructor",
"index": 0,
"fields": [
{
"$ref": "#/definitions/ByteArray"
}
]
},
{
"title": "Right",
"dataType": "constructor",
"index": 1,
"fields": [
{
"$ref": "#/definitions/test_module~1Interval$Int"
}
]
}
]
},
"test_module/Interval$Int": {
"title": "Interval",
"anyOf": [
{
"title": "Finite",
"dataType": "constructor",
"index": 0,
"fields": [
{
"$ref": "#/definitions/Int"
}
]
},
{
"title": "Infinite",
"dataType": "constructor",
"index": 1,
"fields": []
}
]
}
}
}),
)
}
#[test]
fn list_2_tuples_as_map() {
assert_validator(
r#"
type Dict<key, value> {
inner: List<(ByteArray, value)>
}
type UUID { UUID }
validator {
fn list_2_tuples_as_map(redeemer: Dict<UUID, Int>, ctx: Void) {
True
}
}
"#,
json!({
"title": "test_module.list_2_tuples_as_map",
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/test_module~1Dict$test_module~1UUID_Int"
}
},
"compiledCode": "590106010000323232323232323232223253330064a22930b19299980319b87480000044c8c94ccc030c0380084c926323300100100222533300e00114984c8cc00c00cc044008c8c8dd698078011bae300d001300f0011533009491334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e20657870656374656400163756601800260126ea800c54cc01d2412b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e740016300737540046600200290001111199980299b8700100300a2333300500533700008900118060008010012b9a5738aae7555cf2ab9f5742ae89",
"hash": "683885e262c8857f80788a1626c1a327267d85cb49e08382288933b2",
"definitions": {
"ByteArray": {
"dataType": "bytes"
},
"Int": {
"dataType": "integer"
},
"List$Tuple$ByteArray_Int": {
"dataType": "map",
"keys": {
"$ref": "#/definitions/ByteArray"
},
"values": {
"$ref": "#/definitions/Int"
}
},
"test_module/Dict$test_module/UUID_Int": {
"title": "Dict",
"anyOf": [
{
"title": "Dict",
"dataType": "constructor",
"index": 0,
"fields": [
{
"title": "inner",
"$ref": "#/definitions/List$Tuple$ByteArray_Int"
}
]
}
]
}
}
}),
);
}
#[test]
fn opaque_singleton_variants() {
assert_validator(
r#"
pub opaque type Dict<key, value> {
inner: List<(ByteArray, value)>
}
type UUID { UUID }
validator {
fn opaque_singleton_variants(redeemer: Dict<UUID, Int>, ctx: Void) {
True
}
}
"#,
json!({
"title": "test_module.opaque_singleton_variants",
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/test_module~1Dict$test_module~1UUID_Int"
}
},
"compiledCode": "584c01000032323232323222323253330054a22930b19198008008011129998048008a4c26466006006601800464646eb4c028008dd7180400098050009bab0025734aae7555cf2ab9f5742ae881",
"hash": "6ab85c61be6a417c860621155f9c9c91cbaff382efbe7d532173b7ea",
"definitions": {
"ByteArray": {
"dataType": "bytes"
},
"Int": {
"dataType": "integer"
},
"test_module/Dict$test_module/UUID_Int": {
"title": "Dict",
"dataType": "map",
"keys": {
"$ref": "#/definitions/ByteArray"
},
"values": {
"$ref": "#/definitions/Int"
}
}
}
}),
);
}
#[test]
fn nested_data() {
assert_validator(
r#"
pub type Foo {
foo: Data
}
validator {
fn nested_data(datum: Foo, redeemer: Int, ctx: Void) {
True
}
}
"#,
json!({
"title": "test_module.nested_data",
"datum": {
"title": "datum",
"schema": {
"$ref": "#/definitions/test_module~1Foo"
}
},
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/Int"
}
},
"compiledCode": "58e1010000323232323232323232222323253330084a22930b1bad0033253330073370e900000089919299980698078010a4c2a660149201334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2065787065637465640016300d001300a37540082a660109212b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e740016300837540066600200290001111199980299b8700100300a2333300500533700008900118060008010012b9a5738aae7555cf2ab9f5742ae89",
"hash": "4adc0e010fd62343583ca163c1b82e2085fcb221fafd68955685bb2e",
"definitions": {
"Data": {
"title": "Data",
"description": "Any Plutus data."
},
"Int": {
"dataType": "integer"
},
"test_module/Foo": {
"title": "Foo",
"anyOf": [
{
"title": "Foo",
"dataType": "constructor",
"index": 0,
"fields": [
{
"title": "foo",
"$ref": "#/definitions/Data"
}
]
}
]
}
}
}),
);
}
#[test]
fn recursive_types() {
assert_validator(
r#"
pub type Expr {
Val(Int)
Sum(Expr, Expr)
Mul(Expr, Expr)
}
validator {
fn recursive_types(redeemer: Expr, ctx: Void) {
True
}
}
"#,
json!({
"title": "test_module.recursive_types",
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/test_module~1Expr"
}
},
"compiledCode": "5901c701000032323232323232323232223253330074a22930b19918008009119299980499b87480000044c8c94ccc03cc044008526153300c4901334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2065787065637465640016375a601e00260100042a66601266e1d20020011323232325333011301300213232498cc020020008cc01c01c00c54cc0392401334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e206578706563746564001630110013011002300f0013008002153330093370e9002000899191919299980898098010991924c660100100046600e00e0062a6601c9201334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e206578706563746564001630110013011002300f0013008002153300a4912b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e740016300a37540020046600200290001111199980319b8700100300b233330050053370000890011806800801001118029baa0015734ae7155ceaab9e5573eae855d101",
"hash": "e3d30c1599b2c29686f1053f6596f85116ee65556d1c2bcd4e354fcc",
"definitions": {
"Int": {
"dataType": "integer"
},
"test_module/Expr": {
"title": "Expr",
"anyOf": [
{
"title": "Val",
"dataType": "constructor",
"index": 0,
"fields": [
{
"$ref": "#/definitions/Int"
}
]
},
{
"title": "Sum",
"dataType": "constructor",
"index": 1,
"fields": [
{
"$ref": "#/definitions/test_module~1Expr"
},
{
"$ref": "#/definitions/test_module~1Expr"
}
]
},
{
"title": "Mul",
"dataType": "constructor",
"index": 2,
"fields": [
{
"$ref": "#/definitions/test_module~1Expr"
},
{
"$ref": "#/definitions/test_module~1Expr"
}
]
}
]
}
}
}),
)
}
#[test]
fn recursive_generic_types() {
assert_validator(
r#"
pub type LinkedList<a> {
Cons(a, LinkedList<a>)
Nil
}
pub type Foo {
Foo {
foo: LinkedList<Bool>,
}
Bar {
bar: Int,
baz: (ByteArray, List<LinkedList<Int>>)
}
}
validator {
fn recursive_generic_types(datum: Foo, redeemer: LinkedList<Int>, ctx: Void) {
True
}
}
"#,
json!({
"title": "test_module.recursive_generic_types",
"datum": {
"title": "datum",
"schema": {
"$ref": "#/definitions/test_module~1Foo"
}
},
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/test_module~1LinkedList$Int"
}
},
"compiledCode": "590358010000323232323232323232323222232323232533300c4a22930b180100299919119299980719b87480000044c8c94ccc050c0580084c92630050011533011491334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e20657870656374656400163014001300c0021533300e3370e9001000899191919299980b180c00109924c6464646600200200444a66603400229309919801801980e801191807000980d8009bac3016002375c60280022a660269201334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e20657870656374656400163232337606030004603000260300026eb0c058004c058008dd6980a00098060010a99807a4812b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e740016300c00130010012232533300d3370e9000000899191919299980a980b80109924c6600e00e0022a660249201334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2065787065637465640016301500130150023370e900118081baa3013001300b0021533300d3370e90010008a99980898058010a4c2a6601c9211d4578706563746564206e6f206669656c647320666f7220436f6e7374720016153300e4912b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e740016300b00100530010012232533300b3370e90000008991919192999809980a80109924c6600e00e0022a66020921334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e206578706563746564001630130013013002375a602200260120042a66601666e1d20020011533300f3009002149854cc03124011d4578706563746564206e6f206669656c647320666f7220436f6e7374720016153300c4912b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e740016300900133001001480008888cccc01ccdc38008018061199980280299b8000448008c0380040080088c018dd5000918021baa0015734ae7155ceaab9e5573eae855d11",
"hash": "2250642962915ebe2fc08ad9cd0377f2a4b8c281d94f8bae6782fd63",
"definitions": {
"Bool": {
"title": "Bool",
"anyOf": [
{
"title": "False",
"dataType": "constructor",
"index": 0,
"fields": []
},
{
"title": "True",
"dataType": "constructor",
"index": 1,
"fields": []
}
]
},
"ByteArray": {
"dataType": "bytes"
},
"Int": {
"dataType": "integer"
},
"List$test_module/LinkedList$Int": {
"dataType": "list",
"items": {
"$ref": "#/definitions/test_module~1LinkedList$Int"
}
},
"Tuple$ByteArray_List$test_module/LinkedList$Int": {
"title": "Tuple",
"dataType": "list",
"items": [
{
"$ref": "#/definitions/ByteArray"
},
{
"$ref": "#/definitions/List$test_module~1LinkedList$Int"
}
]
},
"test_module/Foo": {
"title": "Foo",
"anyOf": [
{
"title": "Foo",
"dataType": "constructor",
"index": 0,
"fields": [
{
"title": "foo",
"$ref": "#/definitions/test_module~1LinkedList$Bool"
}
]
},
{
"title": "Bar",
"dataType": "constructor",
"index": 1,
"fields": [
{
"title": "bar",
"$ref": "#/definitions/Int"
},
{
"title": "baz",
"$ref": "#/definitions/Tuple$ByteArray_List$test_module~1LinkedList$Int"
}
]
}
]
},
"test_module/LinkedList$Bool": {
"title": "LinkedList",
"anyOf": [
{
"title": "Cons",
"dataType": "constructor",
"index": 0,
"fields": [
{
"$ref": "#/definitions/Bool"
},
{
"$ref": "#/definitions/test_module~1LinkedList$Bool"
}
]
},
{
"title": "Nil",
"dataType": "constructor",
"index": 1,
"fields": []
}
]
},
"test_module/LinkedList$Int": {
"title": "LinkedList",
"anyOf": [
{
"title": "Cons",
"dataType": "constructor",
"index": 0,
"fields": [
{
"$ref": "#/definitions/Int"
},
{
"$ref": "#/definitions/test_module~1LinkedList$Int"
}
]
},
{
"title": "Nil",
"dataType": "constructor",
"index": 1,
"fields": []
}
]
}
}
}),
)
}
#[test]
fn validate_arguments_integer() {
let definitions = fixture_definitions();
let term = Term::data(uplc_ast::Data::integer(42.into()));
let param = Parameter {
title: None,
schema: Reference::new("Int"),
};
assert!(matches!(param.validate(&definitions, &term), Ok { .. }))
}
#[test]
fn validate_arguments_bytestring() {
let definitions = fixture_definitions();
let term = Term::data(uplc_ast::Data::bytestring(vec![102, 111, 111]));
let param = Parameter {
title: None,
schema: Reference::new("ByteArray"),
};
assert!(matches!(param.validate(&definitions, &term), Ok { .. }))
}
#[test]
fn validate_arguments_list_inline() {
let schema = Reference::new("List$Int");
// #/definitions/List$Int
//
// {
// "dataType": "list",
// "items": { "dataType": "integer" }
// }
let mut definitions = fixture_definitions();
definitions.insert(
&schema,
Schema::Data(Data::List(Items::One(Declaration::Inline(Box::new(
Data::Integer,
)))))
.into(),
);
let term = Term::data(uplc_ast::Data::list(vec![
uplc_ast::Data::integer(42.into()),
uplc_ast::Data::integer(14.into()),
]));
let param: Parameter = schema.into();
assert!(matches!(param.validate(&definitions, &term), Ok { .. }))
}
#[test]
fn validate_arguments_list_ref() {
let schema = Reference::new("List$ByteArray");
// #/definitions/List$ByteArray
//
// {
// "dataType": "list",
// "items": { "$ref": "#/definitions/ByteArray" }
// }
let mut definitions = fixture_definitions();
definitions.insert(
&schema,
Schema::Data(Data::List(Items::One(Declaration::Referenced(
Reference::new("ByteArray"),
))))
.into(),
);
let term = Term::data(uplc_ast::Data::list(vec![uplc_ast::Data::bytestring(
vec![102, 111, 111],
)]));
let param: Parameter = schema.into();
assert!(matches!(param.validate(&definitions, &term), Ok { .. }))
}
#[test]
fn validate_arguments_tuple() {
let schema = Reference::new("Tuple$Int_ByteArray");
// #/definitions/Tuple$Int_ByteArray
//
// {
// "dataType": "list",
// "items": [
// { "$ref": "#/definitions/Int" }
// { "$ref": "#/definitions/ByteArray" }
// ]
// }
let mut definitions = fixture_definitions();
definitions.insert(
&schema,
Schema::Data(Data::List(Items::Many(vec![
Declaration::Referenced(Reference::new("Int")),
Declaration::Referenced(Reference::new("ByteArray")),
])))
.into(),
);
let term = Term::data(uplc_ast::Data::list(vec![
uplc_ast::Data::integer(42.into()),
uplc_ast::Data::bytestring(vec![102, 111, 111]),
]));
let param: Parameter = schema.into();
assert!(matches!(param.validate(&definitions, &term), Ok { .. }))
}
#[test]
fn validate_arguments_dict() {
let schema = Reference::new("Dict$ByteArray_Int");
// #/definitions/Dict$Int_ByteArray
//
// {
// "dataType": "map",
// "keys": { "dataType": "bytes" },
// "values": { "dataType": "integer" }
// }
let mut definitions = fixture_definitions();
definitions.insert(
&Reference::new("Dict$ByteArray_Int"),
Schema::Data(Data::Map(
Declaration::Inline(Box::new(Data::Bytes)),
Declaration::Inline(Box::new(Data::Integer)),
))
.into(),
);
let term = Term::data(uplc_ast::Data::map(vec![(
uplc_ast::Data::bytestring(vec![102, 111, 111]),
uplc_ast::Data::integer(42.into()),
)]));
let param: Parameter = schema.into();
assert!(matches!(param.validate(&definitions, &term), Ok { .. }))
}
#[test]
fn validate_arguments_constr_nullary() {
let schema = Reference::new("Bool");
let definitions = fixture_definitions();
let term = Term::data(uplc_ast::Data::constr(1, vec![]));
let param: Parameter = schema.into();
assert!(matches!(param.validate(&definitions, &term), Ok { .. }))
}
#[test]
fn validate_arguments_constr_n_ary() {
let schema = Reference::new("Foo");
// #/definitions/Foo
//
// {
// "anyOf": [
// {
// "dataType": "constructor",
// "index": 0,
// "fields": [{
// "$ref": "#/definitions/Bool
// }]
// },
// ]
// }
let mut definitions = fixture_definitions();
definitions.insert(
&schema,
Schema::Data(Data::AnyOf(vec![Constructor {
index: 0,
fields: vec![Declaration::Referenced(Reference::new("Bool")).into()],
}
.into()]))
.into(),
);
let term = Term::data(uplc_ast::Data::constr(
0,
vec![uplc_ast::Data::constr(0, vec![])],
));
let param: Parameter = schema.into();
assert!(matches!(param.validate(&definitions, &term), Ok { .. }))
}
#[test]
fn validate_arguments_constr_recursive() {
let schema = Reference::new("LinkedList$Int");
// #/definitions/LinkedList$Int
//
// {
// "anyOf": [
// {
// "dataType": "constructor",
// "index": 0,
// "fields": []
// },
// {
// "dataType": "constructor",
// "index": 1,
// "fields": [{
// "$ref": "#/definitions/Int
// "$ref": "#/definitions/LinkedList$Int
// }]
// },
// ]
// }
let mut definitions = fixture_definitions();
definitions.insert(
&schema,
Schema::Data(Data::AnyOf(vec![
// Empty
Constructor {
index: 0,
fields: vec![],
}
.into(),
// Node
Constructor {
index: 1,
fields: vec![
Declaration::Referenced(Reference::new("Int")).into(),
Declaration::Referenced(Reference::new("LinkedList$Int")).into(),
],
}
.into(),
]))
.into(),
);
let term = Term::data(uplc_ast::Data::constr(
1,
vec![
uplc_ast::Data::integer(14.into()),
uplc_ast::Data::constr(
1,
vec![
uplc_ast::Data::integer(42.into()),
uplc_ast::Data::constr(0, vec![]),
],
),
],
));
let param: Parameter = schema.into();
assert!(matches!(param.validate(&definitions, &term), Ok { .. }))
}
}