Update blueprint generation to work with Pairs.

Closes #938.
This commit is contained in:
KtorZ 2024-05-23 18:15:46 +02:00
parent c48f15a957
commit e2bc3a9fc4
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
5 changed files with 174 additions and 29 deletions

View File

@ -147,7 +147,7 @@ impl Reference {
}, },
Type::Pair { fst, snd, .. } => Self { Type::Pair { fst, snd, .. } => Self {
inner: format!( inner: format!(
"Pair{fst}{snd}", "Pair${fst}_{snd}",
fst = Self::from_type(fst, type_parameters), fst = Self::from_type(fst, type_parameters),
snd = Self::from_type(snd, type_parameters) snd = Self::from_type(snd, type_parameters)
), ),

View File

@ -36,6 +36,18 @@ pub enum Declaration<T> {
Inline(Box<T>), Inline(Box<T>),
} }
impl<A> Declaration<A> {
pub fn map<F, B>(self, transform: F) -> Declaration<B>
where
F: FnOnce(A) -> B,
{
match self {
Declaration::Referenced(reference) => Declaration::Referenced(reference),
Declaration::Inline(inner) => Declaration::Inline(transform(*inner).into()),
}
}
}
impl<'a, T> Declaration<T> { impl<'a, T> Declaration<T> {
pub fn reference(&'a self) -> Option<&'a Reference> { pub fn reference(&'a self) -> Option<&'a Reference> {
match self { match self {
@ -297,18 +309,22 @@ impl Annotated<Schema> {
// make all types abide by this convention. // make all types abide by this convention.
let data = match definitions.try_lookup(&generic).cloned() { let data = match definitions.try_lookup(&generic).cloned() {
Some(Annotated { Some(Annotated {
annotated: Schema::Data(Data::List(Items::Many(xs))), annotated: Schema::Pair(left, right),
.. ..
}) if xs.len() == 2 => { }) => {
definitions.remove(&generic); definitions.remove(&generic);
Data::Map(
xs.first() let left = left.map(|inner| match inner {
.expect("length (== 2) checked in pattern clause") Schema::Data(data) => data,
.to_owned(), _ => panic!("impossible: left inhabitant of pair isn't Data but: {inner:#?}"),
xs.last() });
.expect("length (== 2) checked in pattern clause")
.to_owned(), let right = right.map(|inner| match inner {
) Schema::Data(data) => data,
_ => panic!("impossible: right inhabitant of pair isn't Data but: {inner:#?}"),
});
Data::Map(left, right)
} }
_ => Data::List(Items::One(Declaration::Referenced(generic))), _ => Data::List(Items::One(Declaration::Referenced(generic))),
@ -350,6 +366,25 @@ impl Annotated<Schema> {
annotated, annotated,
}) })
}), }),
Type::Pair { fst, snd, .. } => {
definitions.register(type_info, &type_parameters.clone(), |definitions| {
let left = Annotated::do_from_type(fst, modules, type_parameters, definitions)
.map(Declaration::Referenced)
.map_err(|e| e.backtrack(type_info))?;
let right = Annotated::do_from_type(snd, modules, type_parameters, definitions)
.map(Declaration::Referenced)
.map_err(|e| e.backtrack(type_info))?;
Ok(Annotated {
title: Some("Pair".to_owned()),
description: None,
annotated: Schema::Pair(left, right),
})
})
}
Type::Tuple { elems, .. } => { Type::Tuple { elems, .. } => {
definitions.register(type_info, &type_parameters.clone(), |definitions| { definitions.register(type_info, &type_parameters.clone(), |definitions| {
let elems = elems let elems = elems
@ -368,6 +403,7 @@ impl Annotated<Schema> {
}) })
}) })
} }
Type::Var { tipo, .. } => match tipo.borrow().deref() { Type::Var { tipo, .. } => match tipo.borrow().deref() {
TypeVar::Link { tipo } => { TypeVar::Link { tipo } => {
Annotated::do_from_type(tipo, modules, type_parameters, definitions) Annotated::do_from_type(tipo, modules, type_parameters, definitions)
@ -383,8 +419,8 @@ impl Annotated<Schema> {
Err(Error::new(ErrorContext::UnboundTypeVariable, type_info)) Err(Error::new(ErrorContext::UnboundTypeVariable, type_info))
} }
}, },
Type::Fn { .. } => unreachable!(), Type::Fn { .. } => unreachable!(),
Type::Pair { .. } => unreachable!(),
} }
} }
} }
@ -922,9 +958,6 @@ pub enum ErrorContext {
#[error("I caught a free variable in the contract's interface boundary.")] #[error("I caught a free variable in the contract's interface boundary.")]
FreeTypeVariable, FreeTypeVariable,
#[error("I had the misfortune to find an invalid type in an interface boundary.")]
ExpectedData,
#[error("I figured you tried to export a function in your contract's binary interface.")] #[error("I figured you tried to export a function in your contract's binary interface.")]
UnexpectedFunction, UnexpectedFunction,
@ -1002,18 +1035,6 @@ If your contract doesn't need datum or redeemer, you can always give them the ty
.if_supports_color(Stdout, |s| s.bold()) .if_supports_color(Stdout, |s| s.bold())
), ),
ErrorContext::ExpectedData => format!(
r#"While figuring out the outward-facing specification for your contract, I found a type that cannot actually be represented as valid Untyped Plutus Core (the low-level language Cardano uses to execute smart-contracts. For example, it isn't possible to have a list or a tuple of {type_String} because the underlying execution engine doesn't allow it.
There are few restrictions like this one. In this instance, here's the types I followed and that led me to this problem:
{breadcrumbs}"#,
type_String = "String"
.if_supports_color(Stdout, |s| s.bright_blue())
.if_supports_color(Stdout, |s| s.bold()),
breadcrumbs = Error::fmt_breadcrumbs(&self.breadcrumbs)
),
ErrorContext::UnexpectedFunction => format!( ErrorContext::UnexpectedFunction => format!(
r#"I can't allow that. Functions aren't serializable as data on-chain and thus cannot be used within your datum and/or redeemer types. r#"I can't allow that. Functions aren't serializable as data on-chain and thus cannot be used within your datum and/or redeemer types.

View File

@ -0,0 +1,57 @@
---
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"
---
{
"title": "test_module.list_2_tuples_as_list",
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/test_module~1Dict$test_module~1UUID_Int"
}
},
"compiledCode": "59019c010000323232323232323232232253330054a22930a9980324811856616c696461746f722072657475726e65642066616c736500136563253330043370e900018031baa0011325333009001153300600416132533300a300c002132498c8cc004004008894ccc03000452613233003003300f0023232533300e001153300b00916132325333010001153300d00b1613253330113013002149854cc03803058c94cccccc05000454cc0380305854cc0380305854cc038030584dd68008a998070060b180880098088011929999998090008a998060050b0a998060050b0a998060050b0a998060050b09bae001300f0015333333010001153300a00816153300a00816137580022a660140102c2a660140102c601a0022a6600e00a2c64a66666601a0022a6600e00a2c2a6600e00a2c26eb000454cc01c0145854cc01c01458c028004c01cdd50008a998028018b299999980500088008a998020010b0a998020010b0a998020010b0a998020010b2491972656465656d65723a20446963743c555549442c20496e743e005734ae7155ceaab9e5573eae855d12ba41",
"hash": "6027685dde99d967b45333852fe9f59531237d85fcb6b6feb2890672",
"definitions": {
"ByteArray": {
"dataType": "bytes"
},
"Int": {
"dataType": "integer"
},
"List$Tuple$ByteArray_Int": {
"dataType": "list",
"items": {
"$ref": "#/definitions/Tuple$ByteArray_Int"
}
},
"Tuple$ByteArray_Int": {
"title": "Tuple",
"dataType": "list",
"items": [
{
"$ref": "#/definitions/ByteArray"
},
{
"$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"
}
]
}
]
}
}
}

View File

@ -0,0 +1,48 @@
---
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"
---
{
"title": "test_module.list_pairs_as_map",
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/test_module~1Dict$test_module~1UUID_Int"
}
},
"compiledCode": "59014e010000323232323232323232232253330054a22930a9980324811856616c696461746f722072657475726e65642066616c736500136563253330043370e900018031baa0011325333009001153300600416132533300a300c002132498c8cc004004008894ccc03000452613233003003300f0023232325333333012001153300c00a16153300c00a16153300c00a161375a0022a660180142c601a00464a6666660220022a660160122c2a660160122c2a660160122c2a660160122c26eb8004c02c004c03400454cc01c01458c94cccccc03400454cc01c014584dd58008a998038028b0a998038028b0a998038028b180500098039baa001153300500316533333300a001100115330040021615330040021615330040021615330040021649011972656465656d65723a20446963743c555549442c20496e743e005734ae7155ceaab9e5573eae855d12ba41",
"hash": "de6d51e2a272ec0ab73566bbb32700ad5864fdd01290dd925e35ebb4",
"definitions": {
"ByteArray": {
"dataType": "bytes"
},
"Int": {
"dataType": "integer"
},
"List$Pair$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$Pair$ByteArray_Int"
}
]
}
]
}
}
}

View File

@ -495,7 +495,7 @@ mod tests {
} }
#[test] #[test]
fn list_2_tuples_as_map() { fn list_2_tuples_as_list() {
assert_validator!( assert_validator!(
r#" r#"
type Dict<key, value> { type Dict<key, value> {
@ -505,7 +505,26 @@ mod tests {
type UUID { UUID } type UUID { UUID }
validator { validator {
fn list_2_tuples_as_map(redeemer: Dict<UUID, Int>, ctx: Void) { fn list_2_tuples_as_list(redeemer: Dict<UUID, Int>, ctx: Void) {
True
}
}
"#
);
}
#[test]
fn list_pairs_as_map() {
assert_validator!(
r#"
type Dict<key, value> {
inner: List<Pair<ByteArray, value>>
}
type UUID { UUID }
validator {
fn list_pairs_as_map(redeemer: Dict<UUID, Int>, ctx: Void) {
True True
} }
} }