From 6d098a45710936bc369b95bfcb7ecbb894e06023 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Thu, 2 Mar 2023 19:52:50 +0100 Subject: [PATCH] Fix blueprint generation for nested fields with Data Having the data's schema be optional at the level of the 'Schema' did not allow to represent cases where there would be an opaque data at an arbitrary nesting. So I introduced a new variant 'Opaque' on 'Data' to fill that gap. --- crates/aiken-project/src/blueprint/schema.rs | 93 +++++++++---------- .../aiken-project/src/blueprint/validator.rs | 15 ++- 2 files changed, 55 insertions(+), 53 deletions(-) diff --git a/crates/aiken-project/src/blueprint/schema.rs b/crates/aiken-project/src/blueprint/schema.rs index 8b5b4009..aecb52fe 100644 --- a/crates/aiken-project/src/blueprint/schema.rs +++ b/crates/aiken-project/src/blueprint/schema.rs @@ -36,7 +36,7 @@ pub enum Schema { String, Pair(Data, Data), List(Vec), - Data(Option), + Data(Data), } /// A schema for Plutus' Data. @@ -47,6 +47,7 @@ pub enum Data { List(Items), Map(Box, Box), AnyOf(Vec>), + Opaque, } /// A structure that represents either one or many elements. @@ -89,32 +90,32 @@ impl Annotated { "Data" => Ok(Annotated { title: Some("Data".to_string()), description: Some("Any Plutus data.".to_string()), - annotated: Schema::Data(None), + annotated: Schema::Data(Data::Opaque), }), - "ByteArray" => Ok(Schema::Data(Some(Data::Bytes)).into()), + "ByteArray" => Ok(Schema::Data(Data::Bytes).into()), - "Int" => Ok(Schema::Data(Some(Data::Integer)).into()), + "Int" => Ok(Schema::Data(Data::Integer).into()), "String" => Ok(Schema::String.into()), "Void" => Ok(Annotated { title: Some("Unit".to_string()), description: Some("The nullary constructor.".to_string()), - annotated: Schema::Data(Some(Data::AnyOf(vec![Annotated { + annotated: Schema::Data(Data::AnyOf(vec![Annotated { title: None, description: None, annotated: Constructor { index: 0, fields: vec![], }, - }]))), + }])), }), "Bool" => Ok(Annotated { title: Some("Bool".to_string()), description: None, - annotated: Schema::Data(Some(Data::AnyOf(vec![ + annotated: Schema::Data(Data::AnyOf(vec![ Annotated { title: Some("False".to_string()), description: None, @@ -131,13 +132,13 @@ impl Annotated { fields: vec![], }, }, - ]))), + ])), }), "Ordering" => Ok(Annotated { title: Some("Ordering".to_string()), description: None, - annotated: Schema::Data(Some(Data::AnyOf(vec![ + annotated: Schema::Data(Data::AnyOf(vec![ Annotated { title: Some("Less".to_string()), description: None, @@ -162,7 +163,7 @@ impl Annotated { fields: vec![], }, }, - ]))), + ])), }), "Option" => { @@ -172,7 +173,7 @@ impl Annotated { Ok(Annotated { title: Some("Optional".to_string()), description: None, - annotated: Schema::Data(Some(Data::AnyOf(vec![ + annotated: Schema::Data(Data::AnyOf(vec![ Annotated { title: Some("Some".to_string()), description: Some("An optional value.".to_string()), @@ -189,7 +190,7 @@ impl Annotated { fields: vec![], }, }, - ]))), + ])), }) } @@ -202,19 +203,17 @@ impl Annotated { // as such. We don't have a concept of language maps in Aiken, so we simply // make all types abide by this convention. let data = match generic.annotated { - Schema::Data(Some(Data::List(Items::Many(xs)))) if xs.len() == 2 => { - Data::Map( - Box::new(xs.first().unwrap().to_owned()), - Box::new(xs.last().unwrap().to_owned()), - ) - } + Schema::Data(Data::List(Items::Many(xs))) if xs.len() == 2 => Data::Map( + Box::new(xs.first().unwrap().to_owned()), + Box::new(xs.last().unwrap().to_owned()), + ), _ => { let inner = generic.into_data(type_info)?.annotated; Data::List(Items::One(Box::new(inner))) } }; - Ok(Schema::Data(Some(data)).into()) + Ok(Schema::Data(data).into()) } _ => Err(Error::new(ErrorContext::UnsupportedType, type_info)), @@ -228,10 +227,10 @@ impl Annotated { let module = modules.get(module_name).unwrap(); let constructor = find_definition(type_name, &module.ast.definitions).unwrap(); let type_parameters = collect_type_parameters(&constructor.typed_parameters, args); - let annotated = Schema::Data(Some( + let annotated = Schema::Data( Data::from_data_type(modules, constructor, &type_parameters) .map_err(|e| e.backtrack(type_info))?, - )); + ); Ok(Annotated { title: Some(constructor.name.clone()), @@ -259,10 +258,10 @@ impl Annotated { let right = Annotated::from_type(modules, right, type_parameters)? .into_data(right) .map_err(|e| e.backtrack(type_info))?; - Ok(Schema::Data(Some(Data::List(Items::Many(vec![ + Ok(Schema::Data(Data::List(Items::Many(vec![ left.annotated, right.annotated, - ])))) + ]))) .into()) } _ => { @@ -277,7 +276,7 @@ impl Annotated { Ok(Annotated { title: Some("Tuple".to_owned()), description: None, - annotated: Schema::Data(Some(Data::List(Items::Many(elems)))), + annotated: Schema::Data(Data::List(Items::Many(elems))), }) } }, @@ -290,7 +289,7 @@ impl Annotated { Annotated { title, description, - annotated: Schema::Data(Some(data)), + annotated: Schema::Data(data), } => Ok(Annotated { title, description, @@ -403,11 +402,7 @@ impl Serialize for Schema { s.serialize_field("items", &items)?; s.end() } - Schema::Data(None) => { - let s = serializer.serialize_struct("Data", 0)?; - s.end() - } - Schema::Data(Some(data)) => data.serialize(serializer), + Schema::Data(data) => data.serialize(serializer), } } } @@ -415,6 +410,10 @@ impl Serialize for Schema { impl Serialize for Data { fn serialize(&self, serializer: S) -> Result { match self { + Data::Opaque => { + let s = serializer.serialize_struct("Opaque", 0)?; + s.end() + } Data::Integer => { let mut s = serializer.serialize_struct("Integer", 1)?; s.serialize_field("dataType", "integer")?; @@ -628,7 +627,7 @@ pub mod test { #[test] fn serialize_data_integer() { - let schema = Schema::Data(Some(Data::Integer)); + let schema = Schema::Data(Data::Integer); assert_json( &schema, json!({ @@ -639,7 +638,7 @@ pub mod test { #[test] fn serialize_data_bytes() { - let schema = Schema::Data(Some(Data::Bytes)); + let schema = Schema::Data(Data::Bytes); assert_json( &schema, json!({ @@ -650,7 +649,7 @@ pub mod test { #[test] fn serialize_data_list_1() { - let schema = Schema::Data(Some(Data::List(Items::One(Box::new(Data::Integer))))); + let schema = Schema::Data(Data::List(Items::One(Box::new(Data::Integer)))); assert_json( &schema, json!({ @@ -664,8 +663,8 @@ pub mod test { #[test] fn serialize_data_list_2() { - let schema = Schema::Data(Some(Data::List(Items::One(Box::new(Data::List( - Items::One(Box::new(Data::Integer)), + let schema = Schema::Data(Data::List(Items::One(Box::new(Data::List(Items::One( + Box::new(Data::Integer), )))))); assert_json( &schema, @@ -682,10 +681,7 @@ pub mod test { #[test] fn serialize_data_list_3() { - let schema = Schema::Data(Some(Data::List(Items::Many(vec![ - Data::Integer, - Data::Bytes, - ])))); + let schema = Schema::Data(Data::List(Items::Many(vec![Data::Integer, Data::Bytes]))); assert_json( &schema, json!({ @@ -700,10 +696,7 @@ pub mod test { #[test] fn serialize_data_map_1() { - let schema = Schema::Data(Some(Data::Map( - Box::new(Data::Integer), - Box::new(Data::Bytes), - ))); + let schema = Schema::Data(Data::Map(Box::new(Data::Integer), Box::new(Data::Bytes))); assert_json( &schema, json!({ @@ -720,10 +713,10 @@ pub mod test { #[test] fn serialize_data_map_2() { - let schema = Schema::Data(Some(Data::Map( + let schema = Schema::Data(Data::Map( Box::new(Data::Bytes), Box::new(Data::List(Items::One(Box::new(Data::Integer)))), - ))); + )); assert_json( &schema, json!({ @@ -741,11 +734,11 @@ pub mod test { #[test] fn serialize_data_constr_1() { - let schema = Schema::Data(Some(Data::AnyOf(vec![Constructor { + let schema = Schema::Data(Data::AnyOf(vec![Constructor { index: 0, fields: vec![], } - .into()]))); + .into()])); assert_json( &schema, json!({ @@ -760,7 +753,7 @@ pub mod test { #[test] fn serialize_data_constr_2() { - let schema = Schema::Data(Some(Data::AnyOf(vec![ + let schema = Schema::Data(Data::AnyOf(vec![ Constructor { index: 0, fields: vec![Data::Integer.into()], @@ -771,7 +764,7 @@ pub mod test { fields: vec![Data::Bytes.into()], } .into(), - ]))); + ])); assert_json( &schema, json!({ @@ -793,7 +786,7 @@ pub mod test { #[test] fn serialize_empty_data() { - let schema = Schema::Data(None); + let schema = Schema::Data(Data::Opaque); assert_json(&schema, json!({})) } diff --git a/crates/aiken-project/src/blueprint/validator.rs b/crates/aiken-project/src/blueprint/validator.rs index da4136b6..2c20353a 100644 --- a/crates/aiken-project/src/blueprint/validator.rs +++ b/crates/aiken-project/src/blueprint/validator.rs @@ -710,9 +710,18 @@ mod test { "title": "test_module.spend", "hash": "a3dbab684d90d19e6bab3a0b00a7290ff59fe637d14428859bf74376", "datum": { - "title": "Data", - "description": "Any Plutus data.", - "schema": {}, + "title": "Foo", + "schema": { + "anyOf": [{ + "title": "Foo", + "index": 0, + "fields": [{ + "title": "foo", + "description": "Any Plutus data.", + }], + "dataType": "constructor", + }] + }, }, "redeemer": { "schema": {