fix blueprint schema definitions for pairs.

This commit is contained in:
KtorZ 2025-02-08 14:56:43 +01:00
parent d3885ac67a
commit 9162df7c6a
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
8 changed files with 234 additions and 61 deletions

View File

@ -1,11 +1,11 @@
use crate::{ use crate::{
Annotated, Schema,
blueprint::{ blueprint::{
parameter::Parameter, parameter::Parameter,
schema::{Data, Declaration, Items}, schema::{Data, Declaration, Items},
}, },
Annotated, Schema,
}; };
use aiken_lang::tipo::{pretty::resolve_alias, Type, TypeAliasAnnotation, TypeVar}; use aiken_lang::tipo::{Type, TypeAliasAnnotation, TypeVar, pretty::resolve_alias};
use itertools::Itertools; use itertools::Itertools;
use serde::{ use serde::{
self, self,
@ -103,7 +103,7 @@ impl Definitions<Annotated<Schema>> {
/// Initially, we would clean those Pair definitions right-away, but this would cause /// Initially, we would clean those Pair definitions right-away, but this would cause
/// Pair definitions to be missing in some legit cases when the Pair is also used as a /// Pair definitions to be missing in some legit cases when the Pair is also used as a
/// standalone type. /// standalone type.
pub fn prune_orphan_pairs(&mut self, parameters: Vec<&Parameter>) { pub fn prune_orphan_pairs(&mut self, parameters: Vec<&Parameter>) -> &mut Self {
fn traverse_schema( fn traverse_schema(
src: Reference, src: Reference,
schema: &Schema, schema: &Schema,
@ -238,6 +238,72 @@ impl Definitions<Annotated<Schema>> {
last_len = usage.len(); last_len = usage.len();
} }
} }
self
}
pub fn replace_pairs_with_data_lists(&mut self) {
fn swap_declaration(declaration: &mut Declaration<Schema>) -> Declaration<Data> {
match std::mem::replace(declaration, Declaration::Inline(Schema::Unit.into())) {
Declaration::Referenced(reference) => Declaration::Referenced(reference),
Declaration::Inline(mut inline) => {
schema_to_data(&mut inline);
if let Schema::Data(data) = *inline {
Declaration::Inline(data.into())
} else {
unreachable!()
}
}
}
}
fn schema_to_data(schema: &mut Schema) {
let items = match schema {
Schema::Data(_) => None,
Schema::Pair(ref mut left, ref mut right) => {
let left = swap_declaration(left);
let right = swap_declaration(right);
Some(Items::Many(vec![left, right]))
}
Schema::List(Items::One(ref mut item)) => {
let item = swap_declaration(item);
Some(Items::One(item))
}
Schema::List(Items::Many(ref mut items)) => Some(Items::Many(
items.iter_mut().map(swap_declaration).collect(),
)),
Schema::Integer => {
*schema = Schema::Data(Data::Integer);
None
}
Schema::Bytes => {
*schema = Schema::Data(Data::Bytes);
None
}
Schema::String => {
*schema = Schema::Data(Data::Bytes);
None
}
Schema::Unit => {
*schema = Schema::void();
None
}
Schema::Boolean => {
*schema = Schema::bool();
None
}
};
if let Some(items) = items {
*schema = Schema::Data(Data::List(items));
}
}
for (_, entry) in self.inner.iter_mut() {
if let Some(ref mut annotated) = entry {
schema_to_data(&mut annotated.annotated);
}
}
} }
} }

View File

@ -5,8 +5,8 @@ use super::{
}; };
use std::{iter, ops::Deref}; use std::{iter, ops::Deref};
use uplc::{ use uplc::{
ast::{Constant, Data as UplcData},
PlutusData, PlutusData,
ast::{Constant, Data as UplcData},
}; };
#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)] #[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)]

View File

@ -97,6 +97,48 @@ pub enum Schema {
Data(Data), Data(Data),
} }
impl Schema {
pub fn void() -> Self {
Schema::Data(Data::AnyOf(vec![Annotated {
title: None,
description: None,
annotated: Constructor {
index: 0,
fields: vec![],
},
}]))
}
pub fn int() -> Self {
Schema::Data(Data::Integer)
}
pub fn bytes() -> Self {
Schema::Data(Data::Bytes)
}
pub fn bool() -> Self {
Schema::Data(Data::AnyOf(vec![
Annotated {
title: Some("False".to_string()),
description: None,
annotated: Constructor {
index: 0,
fields: vec![],
},
},
Annotated {
title: Some("True".to_string()),
description: None,
annotated: Constructor {
index: 1,
fields: vec![],
},
},
]))
}
}
/// A schema for Plutus' Data. /// A schema for Plutus' Data.
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum Data { pub enum Data {
@ -205,46 +247,22 @@ impl Annotated<Schema> {
annotated: Schema::Data(Data::Opaque), annotated: Schema::Data(Data::Opaque),
}), }),
"ByteArray" => Ok(with_title(title.as_ref(), Schema::Data(Data::Bytes))), "ByteArray" => Ok(with_title(title.as_ref(), Schema::bytes())),
"Int" => Ok(with_title(title.as_ref(), Schema::Data(Data::Integer))), "Int" => Ok(with_title(title.as_ref(), Schema::int())),
"String" => Ok(with_title(title.as_ref(), Schema::String)), "String" => Ok(with_title(title.as_ref(), Schema::String)),
"Void" => Ok(Annotated { "Void" => Ok(Annotated {
title: title.or(Some("Unit".to_string())), title: title.or(Some("Unit".to_string())),
description: None, description: None,
annotated: Schema::Data(Data::AnyOf(vec![Annotated { annotated: Schema::void(),
title: None,
description: None,
annotated: Constructor {
index: 0,
fields: vec![],
},
}])),
}), }),
"Bool" => Ok(Annotated { "Bool" => Ok(Annotated {
title: title.or(Some("Bool".to_string())), title: title.or(Some("Bool".to_string())),
description: None, description: None,
annotated: Schema::Data(Data::AnyOf(vec![ annotated: Schema::bool(),
Annotated {
title: Some("False".to_string()),
description: None,
annotated: Constructor {
index: 0,
fields: vec![],
},
},
Annotated {
title: Some("True".to_string()),
description: None,
annotated: Constructor {
index: 1,
fields: vec![],
},
},
])),
}), }),
"Ordering" => Ok(Annotated { "Ordering" => Ok(Annotated {
@ -1099,7 +1117,7 @@ Here's the types I followed and that led me to this problem:
pub mod tests { pub mod tests {
use super::*; use super::*;
use proptest::prelude::*; use proptest::prelude::*;
use serde_json::{self, json, Value}; use serde_json::{self, Value, json};
pub fn assert_json(schema: &impl Serialize, expected: Value) { pub fn assert_json(schema: &impl Serialize, expected: Value) {
assert_eq!(serde_json::to_value(schema).unwrap(), expected); assert_eq!(serde_json::to_value(schema).unwrap(), expected);

View File

@ -0,0 +1,65 @@
---
source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\npub type MyPair =\n Pair<List<Int>, Bool>\n\nvalidator placeholder {\n spend(_datum: Option<Data>, _redeemer: MyPair, _utxo: Data, _self: Data,) {\n True\n }\n}\n"
---
{
"title": "test_module.placeholder.spend",
"datum": {
"title": "_datum",
"schema": {
"$ref": "#/definitions/Data"
}
},
"redeemer": {
"title": "_redeemer",
"schema": {
"$ref": "#/definitions/MyPair"
}
},
"compiledCode": "<redacted>",
"hash": "<redacted>",
"definitions": {
"Bool": {
"title": "Bool",
"anyOf": [
{
"title": "False",
"dataType": "constructor",
"index": 0,
"fields": []
},
{
"title": "True",
"dataType": "constructor",
"index": 1,
"fields": []
}
]
},
"Data": {
"title": "Data",
"description": "Any Plutus data."
},
"Int": {
"dataType": "integer"
},
"List$Int": {
"dataType": "list",
"items": {
"$ref": "#/definitions/Int"
}
},
"MyPair": {
"title": "MyPair",
"dataType": "list",
"items": [
{
"$ref": "#/definitions/List$Int"
},
{
"$ref": "#/definitions/Bool"
}
]
}
}
}

View File

@ -33,13 +33,15 @@ description: "Code:\n\npub type MyPair =\n Pair<Int, Int>\n\npub type MyDatum {
}, },
"MyPair": { "MyPair": {
"title": "MyPair", "title": "MyPair",
"dataType": "#pair", "dataType": "list",
"left": { "items": [
{
"$ref": "#/definitions/Int" "$ref": "#/definitions/Int"
}, },
"right": { {
"$ref": "#/definitions/Int" "$ref": "#/definitions/Int"
} }
]
}, },
"Void": { "Void": {
"title": "Unit", "title": "Unit",

View File

@ -33,13 +33,15 @@ description: "Code:\n\npub type MyPair =\n Pair<Int, Int>\n\npub type MyDatum {
}, },
"MyPair": { "MyPair": {
"title": "MyPair", "title": "MyPair",
"dataType": "#pair", "dataType": "list",
"left": { "items": [
{
"$ref": "#/definitions/Int" "$ref": "#/definitions/Int"
}, },
"right": { {
"$ref": "#/definitions/Int" "$ref": "#/definitions/Int"
} }
]
}, },
"Void": { "Void": {
"title": "Unit", "title": "Unit",

View File

@ -78,13 +78,15 @@ description: "Code:\n\npub type Asset = (ByteArray, Int)\n\npub type POSIXTime =
}, },
"Pair$POSIXTime_Bool": { "Pair$POSIXTime_Bool": {
"title": "Pair", "title": "Pair",
"dataType": "#pair", "dataType": "list",
"left": { "items": [
{
"$ref": "#/definitions/POSIXTime" "$ref": "#/definitions/POSIXTime"
}, },
"right": { {
"$ref": "#/definitions/Bool" "$ref": "#/definitions/Bool"
} }
]
}, },
"Pairs$Asset_POSIXTime": { "Pairs$Asset_POSIXTime": {
"title": "Pairs<a, POSIXTime>", "title": "Pairs<a, POSIXTime>",

View File

@ -202,13 +202,15 @@ impl Validator {
schema: Declaration::Inline(Box::new(Schema::Data(Data::Opaque))), schema: Declaration::Inline(Box::new(Schema::Data(Data::Opaque))),
})); }));
definitions.prune_orphan_pairs( definitions
.prune_orphan_pairs(
parameters parameters
.iter() .iter()
.chain(redeemer.as_ref().map(|x| vec![x]).unwrap_or_default()) .chain(redeemer.as_ref().map(|x| vec![x]).unwrap_or_default())
.chain(datum.as_ref().map(|x| vec![x]).unwrap_or_default()) .chain(datum.as_ref().map(|x| vec![x]).unwrap_or_default())
.collect::<Vec<&Parameter>>(), .collect::<Vec<&Parameter>>(),
); )
.replace_pairs_with_data_lists();
Ok(Validator { Ok(Validator {
title: format!("{}.{}.{}", &module.name, &def.name, &func.name,), title: format!("{}.{}.{}", &module.name, &def.name, &func.name,),
@ -805,6 +807,22 @@ mod tests {
); );
} }
#[test]
fn pair_of_lists() {
assert_validator!(
r#"
pub type MyPair =
Pair<List<Int>, Bool>
validator placeholder {
spend(_datum: Option<Data>, _redeemer: MyPair, _utxo: Data, _self: Data,) {
True
}
}
"#
);
}
#[test] #[test]
fn else_redeemer() { fn else_redeemer() {
assert_validator!( assert_validator!(