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, #[serde(skip_serializing_if = "Option::is_none")] pub datum: Option, pub redeemer: Parameter, #[serde(skip_serializing_if = "Vec::is_empty")] #[serde(default)] pub parameters: Vec, #[serde(flatten)] pub program: Program, #[serde(skip_serializing_if = "Definitions::is_empty")] #[serde(default)] pub definitions: Definitions>, } impl Validator { pub fn from_checked_module( modules: &CheckedModules, generator: &mut CodeGenerator, module: &CheckedModule, def: &TypedValidator, ) -> Vec> { 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, params: &[TypedArg], func: &TypedFunction, is_multi_validator: bool, ) -> Result { 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(), ¶m.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::>()?; 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>, arg: &Term, ) -> Result { 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( &self, definitions: &Definitions>, ask: F, ) -> Result, Error> where F: Fn(&Annotated, &Definitions>) -> Result, { 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> { 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, utxoHash: Hash, } /// A Hash digest for a given algorithm. type Hash = 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(left) Right(right) } type Interval { Finite(a) Infinite } validator { fn generics(redeemer: Either>, 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 { inner: List<(ByteArray, value)> } type UUID { UUID } validator { fn list_2_tuples_as_map(redeemer: Dict, 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 { inner: List<(ByteArray, value)> } type UUID { UUID } validator { fn opaque_singleton_variants(redeemer: Dict, 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 { Cons(a, LinkedList) Nil } pub type Foo { Foo { foo: LinkedList, } Bar { bar: Int, baz: (ByteArray, List>) } } validator { fn recursive_generic_types(datum: Foo, redeemer: LinkedList, 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 { .. })) } }