diff --git a/Cargo.lock b/Cargo.lock index be1edebf..61c4ccf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -313,6 +313,14 @@ dependencies = [ "half", ] +[[package]] +name = "cip-57" +version = "0.13.0" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "core-foundation" version = "0.10.0" @@ -2154,6 +2162,7 @@ version = "0.13.0" dependencies = [ "assert-json-diff", "ciborium", + "cip-57", "hex", "miette", "paste", diff --git a/Cargo.toml b/Cargo.toml index 42a07143..2e313c3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,11 @@ [workspace] resolver = "2" -members = ["crates/tx3-cardano", "crates/tx3-lang", "crates/tx3-resolver"] +members = [ + "crates/tx3-cardano", + "crates/tx3-lang", + "crates/tx3-resolver", + "crates/cip-57", +] [workspace.package] publish = true diff --git a/crates/cip-57/Cargo.toml b/crates/cip-57/Cargo.toml new file mode 100644 index 00000000..d70aa572 --- /dev/null +++ b/crates/cip-57/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "cip-57" +description = "CIP-57 compatibility (JSON parsing and serialization)" +publish.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +version.workspace = true +keywords.workspace = true +documentation.workspace = true +homepage.workspace = true +readme.workspace = true + + +[dependencies] +serde = { version = "1", features = ["derive"] } +serde_json = "1" diff --git a/crates/cip-57/examples/plutus.json b/crates/cip-57/examples/plutus.json new file mode 100644 index 00000000..97472029 --- /dev/null +++ b/crates/cip-57/examples/plutus.json @@ -0,0 +1,634 @@ +{ + "preamble": { + "title": "txpipe/contract", + "description": "Aiken contracts for project 'txpipe/contract'", + "version": "0.0.0", + "plutusVersion": "v3", + "compiler": { + "name": "Aiken", + "version": "v1.1.17+c3a7fba" + }, + "license": "Apache-2.0" + }, + "validators": [ + { + "title": "githoney_contract.badges_contract.spend", + "datum": { + "title": "_datum", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1Datum" + } + }, + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1Redeemer" + } + }, + "parameters": [ + { + "title": "settings_policy_id", + "schema": { + "$ref": "#/definitions/cardano~1assets~1PolicyId" + } + } + ], + "compiledCode": "eedaa957c60268de", + "hash": "24b9b1964ce02550db270a1d6270b505b9c0342625ee766d77fab1f9" + }, + { + "title": "githoney_contract.badges_contract.else", + "redeemer": { + "schema": {} + }, + "parameters": [ + { + "title": "settings_policy_id", + "schema": { + "$ref": "#/definitions/cardano~1assets~1PolicyId" + } + } + ], + "compiledCode": "3fe9763bc5ea0108", + "hash": "24b9b1964ce02550db270a1d6270b505b9c0342625ee766d77fab1f9" + }, + { + "title": "githoney_contract.badges_policy.mint", + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1Redeemer" + } + }, + "parameters": [ + { + "title": "utxo_ref", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1OutputReference" + } + }, + { + "title": "nonce", + "schema": { + "$ref": "#/definitions/Int" + } + } + ], + "compiledCode": "c8db1de3fbbc8921", + "hash": "87d6bd0b40f204d49803dad8e0d70611918d22354125c8f226a42670" + }, + { + "title": "githoney_contract.badges_policy.else", + "redeemer": { + "schema": {} + }, + "parameters": [ + { + "title": "utxo_ref", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1OutputReference" + } + }, + { + "title": "nonce", + "schema": { + "$ref": "#/definitions/Int" + } + } + ], + "compiledCode": "f143b98f13d5b12b", + "hash": "87d6bd0b40f204d49803dad8e0d70611918d22354125c8f226a42670" + }, + { + "title": "githoney_contract.githoney.spend", + "datum": { + "title": "datum", + "schema": { + "$ref": "#/definitions/types~1GithoneyDatum" + } + }, + "redeemer": { + "title": "redeemer", + "schema": { + "$ref": "#/definitions/types~1GithoneyContractRedeemers" + } + }, + "parameters": [ + { + "title": "settings_policy_id", + "schema": { + "$ref": "#/definitions/cardano~1assets~1PolicyId" + } + } + ], + "compiledCode": "f1a9d1de401b5004", + "hash": "7577273f99e7f3c9211d6342338d6316afc91689333c4d5099e7b2d1" + }, + { + "title": "githoney_contract.githoney.mint", + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1Redeemer" + } + }, + "parameters": [ + { + "title": "settings_policy_id", + "schema": { + "$ref": "#/definitions/cardano~1assets~1PolicyId" + } + } + ], + "compiledCode": "f1f665b7b5ec44d6", + "hash": "7577273f99e7f3c9211d6342338d6316afc91689333c4d5099e7b2d1" + }, + { + "title": "githoney_contract.githoney.else", + "redeemer": { + "schema": {} + }, + "parameters": [ + { + "title": "settings_policy_id", + "schema": { + "$ref": "#/definitions/cardano~1assets~1PolicyId" + } + } + ], + "compiledCode": "8dc98413cbd1b43f", + "hash": "7577273f99e7f3c9211d6342338d6316afc91689333c4d5099e7b2d1" + }, + { + "title": "githoney_contract.settings.spend", + "datum": { + "title": "datum", + "schema": { + "$ref": "#/definitions/types~1SettingsDatum" + } + }, + "redeemer": { + "title": "redeemer", + "schema": { + "$ref": "#/definitions/types~1SettingsRedeemers" + } + }, + "compiledCode": "5eb78784a02ee1a0", + "hash": "049f1a09fb535089fdd9df98b4b0975d1081f9afc2d6f59ad2f9c208" + }, + { + "title": "githoney_contract.settings.else", + "redeemer": { + "schema": {} + }, + "compiledCode": "9813b3c286674b27", + "hash": "049f1a09fb535089fdd9df98b4b0975d1081f9afc2d6f59ad2f9c208" + }, + { + "title": "githoney_contract.settings_minting.mint", + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1Redeemer" + } + }, + "parameters": [ + { + "title": "utxo_ref", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1OutputReference" + } + }, + { + "title": "settings_script_addr", + "schema": { + "$ref": "#/definitions/cardano~1address~1Address" + } + } + ], + "compiledCode": "6396e66bd8b6ac88", + "hash": "15721f473d73adf918aa7541871739c2e8df0a60abe023411cf9f1b0" + }, + { + "title": "githoney_contract.settings_minting.else", + "redeemer": { + "schema": {} + }, + "parameters": [ + { + "title": "utxo_ref", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1OutputReference" + } + }, + { + "title": "settings_script_addr", + "schema": { + "$ref": "#/definitions/cardano~1address~1Address" + } + } + ], + "compiledCode": "fa2dba3b69a8d00c", + "hash": "15721f473d73adf918aa7541871739c2e8df0a60abe023411cf9f1b0" + } + ], + "definitions": { + "Bool": { + "title": "Bool", + "anyOf": [ + { + "title": "False", + "dataType": "constructor", + "index": 0, + "fields": [] + }, + { + "title": "True", + "dataType": "constructor", + "index": 1, + "fields": [] + } + ] + }, + "ByteArray": { + "title": "ByteArray", + "dataType": "bytes" + }, + "Data": { + "title": "Data", + "description": "Any Plutus data." + }, + "Int": { + "dataType": "integer" + }, + "Option$cardano/address/Address": { + "title": "Option", + "anyOf": [ + { + "title": "Some", + "description": "An optional value.", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "$ref": "#/definitions/cardano~1address~1Address" + } + ] + }, + { + "title": "None", + "description": "Nothing.", + "dataType": "constructor", + "index": 1, + "fields": [] + } + ] + }, + "Option$cardano/address/StakeCredential": { + "title": "Option", + "anyOf": [ + { + "title": "Some", + "description": "An optional value.", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "$ref": "#/definitions/cardano~1address~1StakeCredential" + } + ] + }, + { + "title": "None", + "description": "Nothing.", + "dataType": "constructor", + "index": 1, + "fields": [] + } + ] + }, + "Pairs$cardano/assets/AssetName_Int": { + "title": "Pairs", + "dataType": "map", + "keys": { + "$ref": "#/definitions/cardano~1assets~1AssetName" + }, + "values": { + "$ref": "#/definitions/Int" + } + }, + "Pairs$cardano/assets/PolicyId_Pairs$cardano/assets/AssetName_Int": { + "title": "Pairs>", + "dataType": "map", + "keys": { + "$ref": "#/definitions/cardano~1assets~1PolicyId" + }, + "values": { + "$ref": "#/definitions/Pairs$cardano~1assets~1AssetName_Int" + } + }, + "aiken/crypto/DataHash": { + "title": "DataHash", + "dataType": "bytes" + }, + "aiken/crypto/ScriptHash": { + "title": "ScriptHash", + "dataType": "bytes" + }, + "aiken/crypto/VerificationKeyHash": { + "title": "VerificationKeyHash", + "dataType": "bytes" + }, + "cardano/address/Address": { + "title": "Address", + "description": "A Cardano `Address` typically holding one or two credential references.\n\n Note that legacy bootstrap addresses (a.k.a. 'Byron addresses') are\n completely excluded from Plutus contexts. Thus, from an on-chain\n perspective only exists addresses of type 00, 01, ..., 07 as detailed\n in [CIP-0019 :: Shelley Addresses](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0019/#shelley-addresses).", + "anyOf": [ + { + "title": "Address", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "title": "payment_credential", + "$ref": "#/definitions/cardano~1address~1PaymentCredential" + }, + { + "title": "stake_credential", + "$ref": "#/definitions/Option$cardano~1address~1StakeCredential" + } + ] + } + ] + }, + "cardano/address/Credential": { + "title": "Credential", + "description": "A general structure for representing an on-chain `Credential`.\n\n Credentials are always one of two kinds: a direct public/private key\n pair, or a script (native or Plutus).", + "anyOf": [ + { + "title": "VerificationKey", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "$ref": "#/definitions/aiken~1crypto~1VerificationKeyHash" + } + ] + }, + { + "title": "Script", + "dataType": "constructor", + "index": 1, + "fields": [ + { + "$ref": "#/definitions/aiken~1crypto~1ScriptHash" + } + ] + } + ] + }, + "cardano/address/PaymentCredential": { + "title": "PaymentCredential", + "description": "A general structure for representing an on-chain `Credential`.\n\n Credentials are always one of two kinds: a direct public/private key\n pair, or a script (native or Plutus).", + "anyOf": [ + { + "title": "VerificationKey", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "$ref": "#/definitions/aiken~1crypto~1VerificationKeyHash" + } + ] + }, + { + "title": "Script", + "dataType": "constructor", + "index": 1, + "fields": [ + { + "$ref": "#/definitions/aiken~1crypto~1ScriptHash" + } + ] + } + ] + }, + "cardano/address/StakeCredential": { + "title": "StakeCredential", + "description": "Represent a type of object that can be represented either inline (by hash)\n or via a reference (i.e. a pointer to an on-chain location).\n\n This is mainly use for capturing pointers to a stake credential\n registration certificate in the case of so-called pointer addresses.", + "anyOf": [ + { + "title": "Inline", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "$ref": "#/definitions/cardano~1address~1Credential" + } + ] + }, + { + "title": "Pointer", + "dataType": "constructor", + "index": 1, + "fields": [ + { + "title": "slot_number", + "$ref": "#/definitions/Int" + }, + { + "title": "transaction_index", + "$ref": "#/definitions/Int" + }, + { + "title": "certificate_index", + "$ref": "#/definitions/Int" + } + ] + } + ] + }, + "cardano/assets/AssetName": { + "title": "AssetName", + "dataType": "bytes" + }, + "cardano/assets/PolicyId": { + "title": "PolicyId", + "dataType": "bytes" + }, + "cardano/transaction/Datum": { + "title": "Datum", + "description": "An output `Datum`.", + "anyOf": [ + { + "title": "NoDatum", + "dataType": "constructor", + "index": 0, + "fields": [] + }, + { + "title": "DatumHash", + "description": "A datum referenced by its hash digest.", + "dataType": "constructor", + "index": 1, + "fields": [ + { + "$ref": "#/definitions/aiken~1crypto~1DataHash" + } + ] + }, + { + "title": "InlineDatum", + "description": "A datum completely inlined in the output.", + "dataType": "constructor", + "index": 2, + "fields": [ + { + "$ref": "#/definitions/Data" + } + ] + } + ] + }, + "cardano/transaction/OutputReference": { + "title": "OutputReference", + "description": "An `OutputReference` is a unique reference to an output on-chain. The `output_index`\n corresponds to the position in the output list of the transaction (identified by its id)\n that produced that output", + "anyOf": [ + { + "title": "OutputReference", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "title": "transaction_id", + "$ref": "#/definitions/ByteArray" + }, + { + "title": "output_index", + "$ref": "#/definitions/Int" + } + ] + } + ] + }, + "cardano/transaction/Redeemer": { + "title": "Redeemer", + "description": "Any Plutus data." + }, + "types/GithoneyContractRedeemers": { + "title": "GithoneyContractRedeemers", + "anyOf": [ + { + "title": "AddRewards", + "dataType": "constructor", + "index": 0, + "fields": [] + }, + { + "title": "Assign", + "dataType": "constructor", + "index": 1, + "fields": [] + }, + { + "title": "Merge", + "dataType": "constructor", + "index": 2, + "fields": [] + }, + { + "title": "Close", + "dataType": "constructor", + "index": 3, + "fields": [] + }, + { + "title": "Claim", + "dataType": "constructor", + "index": 4, + "fields": [] + } + ] + }, + "types/GithoneyDatum": { + "title": "GithoneyDatum", + "anyOf": [ + { + "title": "GithoneyDatum", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "title": "admin_payment_credential", + "$ref": "#/definitions/cardano~1address~1Credential" + }, + { + "title": "maintainer_address", + "$ref": "#/definitions/cardano~1address~1Address" + }, + { + "title": "contributor_address", + "$ref": "#/definitions/Option$cardano~1address~1Address" + }, + { + "title": "bounty_reward_fee", + "$ref": "#/definitions/Int" + }, + { + "title": "deadline", + "$ref": "#/definitions/Int" + }, + { + "title": "merged", + "$ref": "#/definitions/Bool" + }, + { + "title": "initial_value", + "$ref": "#/definitions/Pairs$cardano~1assets~1PolicyId_Pairs$cardano~1assets~1AssetName_Int" + } + ] + } + ] + }, + "types/SettingsDatum": { + "title": "SettingsDatum", + "anyOf": [ + { + "title": "SettingsDatum", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "title": "githoney_address", + "$ref": "#/definitions/cardano~1address~1Address" + }, + { + "title": "bounty_creation_fee", + "$ref": "#/definitions/Int" + }, + { + "title": "bounty_reward_fee", + "$ref": "#/definitions/Int" + } + ] + } + ] + }, + "types/SettingsRedeemers": { + "title": "SettingsRedeemers", + "anyOf": [ + { + "title": "UpdateSettings", + "dataType": "constructor", + "index": 0, + "fields": [] + }, + { + "title": "CloseSettings", + "dataType": "constructor", + "index": 1, + "fields": [] + } + ] + } + } +} diff --git a/crates/cip-57/src/lib.rs b/crates/cip-57/src/lib.rs new file mode 100644 index 00000000..d46fc5f8 --- /dev/null +++ b/crates/cip-57/src/lib.rs @@ -0,0 +1,186 @@ +//! This module defines the structures for a Blueprint, including its preamble, validators, and definitions. + +use serde::{Deserialize, Serialize}; +use serde_json::Number; +use std::collections::BTreeMap; +/// Represents a blueprint containing preamble, validators, and optional definitions. +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct Blueprint { + pub preamble: Preamble, + pub validators: Vec, + pub definitions: Option, +} + +/// Represents the preamble of a blueprint, including metadata such as title, description, version, and compiler information. +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Preamble { + pub title: String, + pub description: Option, + pub version: String, + pub plutus_version: String, + pub compiler: Option, + pub license: Option, +} + +/// Represents the compiler information in the preamble. +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct Compiler { + pub name: String, + pub version: Option, +} + +/// Represents a validator in the blueprint, including its title, description, compiled code, hash, datum, redeemer, and parameters. +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Validator { + pub title: String, + pub description: Option, + pub compiled_code: Option, + pub hash: Option, + pub datum: Option, + pub redeemer: Option, + pub parameters: Option>, +} + +/// Represents an argument in a validator, including its title, description, purpose, and schema reference. +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct Argument { + pub title: Option, + pub description: Option, + pub purpose: Option, + pub schema: Reference, +} + +/// Represents a purpose which can be either a single purpose or an object with oneOf. +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(untagged)] +pub enum PurposeArray { + Single(Purpose), + OneOf(PurposeOneOf), +} + +/// Represents a purpose object with a oneOf field containing an array of purposes. +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct PurposeOneOf { + pub one_of: Vec, +} + +/// Represents the purpose of an argument, which can be spend, mint, withdraw, or publish. +#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq)] +#[serde(rename_all = "camelCase")] +pub enum Purpose { + Spend, + Mint, + Withdraw, + Publish, +} + +/// Represents a reference to a schema. +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct Reference { + #[serde(rename = "$ref")] + pub reference: Option, +} + +/// Represents a parameter in a validator, including its title, description, purpose, and schema reference. +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct Parameter { + pub title: Option, + pub description: Option, + pub purpose: Option, + pub schema: Reference, +} + +/// Represents the definitions in a blueprint, which is a map of definition names to their corresponding definitions. +#[derive(Debug, Default, Deserialize, Serialize, Clone)] +pub struct Definitions { + #[serde(flatten, default)] + pub inner: BTreeMap, +} + +/// Represents a definition in the blueprint, including its title, description, data type, any_of schemas, items, keys, and values. +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Definition { + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub data_type: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub any_of: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub items: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub keys: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub values: Option, +} + +/// Represents an array of references which can be either a single reference or an array of references. +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(untagged)] +pub enum ReferencesArray { + Single(Reference), + Array(Vec), +} + +/// Represents a schema in a definition, including its title, description, data type, index, and fields. +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Schema { + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + pub data_type: DataType, + pub index: Number, + pub fields: Vec, +} + +/// Represents the data type of a schema, which can be integer, bytes, list, map, or constructor. +#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq)] +#[serde(rename_all = "camelCase")] +pub enum DataType { + Integer, + Bytes, + List, + Map, + Constructor, +} + +/// Represents a field in a schema, including its title and reference. +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct Field { + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option, + #[serde(rename = "$ref")] + pub reference: String, +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + use std::path::PathBuf; + + #[test] + fn deserialize_plutus_json_into_blueprint() { + let manifest = env!("CARGO_MANIFEST_DIR"); + let path = PathBuf::from(manifest).join("examples").join("plutus.json"); + + let failure_msg = format!("failed to read example file: {}", path.display()); + let json = fs::read_to_string(&path).expect(&failure_msg); + + let bp: Blueprint = serde_json::from_str(&json).expect("failed to deserialize blueprint"); + + assert!( + !bp.preamble.title.is_empty(), + "preamble.title should not be empty" + ); + assert!(!bp.validators.is_empty(), "expected at least one validator"); + } +} diff --git a/crates/tx3-lang/Cargo.toml b/crates/tx3-lang/Cargo.toml index 6e8735aa..60b8a95c 100644 --- a/crates/tx3-lang/Cargo.toml +++ b/crates/tx3-lang/Cargo.toml @@ -16,15 +16,16 @@ readme.workspace = true thiserror = { workspace = true } trait-variant = { workspace = true } hex = { workspace = true } - +cip-57 = { version = "0.13.0", path = "../cip-57" } +serde_json = "1.0.137" miette = { version = "7.4.0", features = ["fancy"] } pest = { version = "2.7.15", features = ["miette-error", "pretty-print"] } pest_derive = "2.7.15" serde = { version = "1.0.217", features = ["derive"] } ciborium = "0.2.2" + [dev-dependencies] assert-json-diff = "2.0.2" paste = "1.0.15" proptest = "1.7.0" -serde_json = "1.0.137" diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index 120d6366..2b1c1fec 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -1439,7 +1439,7 @@ impl Analyzable for Program { /// # Returns /// * `AnalyzeReport` of the analysis. Empty if no errors are found. pub fn analyze(ast: &mut Program) -> AnalyzeReport { - ast.analyze(None) + ast.analyze(ast.scope.clone()) } #[cfg(test)] diff --git a/crates/tx3-lang/src/ast.rs b/crates/tx3-lang/src/ast.rs index 1c89006a..d2fd9576 100644 --- a/crates/tx3-lang/src/ast.rs +++ b/crates/tx3-lang/src/ast.rs @@ -179,6 +179,7 @@ pub struct Program { pub assets: Vec, pub parties: Vec, pub policies: Vec, + pub imports: Vec, pub span: Span, // analysis @@ -907,6 +908,13 @@ pub struct AliasDef { } impl AliasDef { + pub fn new(name: &str, target: Type) -> Self { + Self { + name: Identifier::new(name), + alias_type: target, + span: Span::DUMMY, + } + } pub fn resolve_alias_chain(&self) -> Option<&TypeDef> { match &self.alias_type { Type::Custom(identifier) => match &identifier.symbol { diff --git a/crates/tx3-lang/src/cardano.rs b/crates/tx3-lang/src/cardano.rs index a91c511f..e46f42aa 100644 --- a/crates/tx3-lang/src/cardano.rs +++ b/crates/tx3-lang/src/cardano.rs @@ -1,11 +1,11 @@ -use std::{collections::HashMap, rc::Rc}; +use std::{collections::HashMap, fs, rc::Rc}; use pest::iterators::Pair; use serde::{Deserialize, Serialize}; use crate::{ analyzing::{Analyzable, AnalyzeReport}, - ast::{DataExpr, Identifier, Scope, Span, Type}, + ast::{DataExpr, Identifier, RecordField, Scope, Span, Symbol, Type, TypeDef, VariantCase}, ir, lowering::IntoLower, parsing::{AstNode, Error, Rule}, @@ -841,13 +841,162 @@ impl IntoLower for CardanoBlock { } } +/// Sanitizes a type name to be a valid tx3 identifier. +/// Replaces characters like `<`, `>`, `,`, `/`, `$`, `~1` with underscores. +fn sanitize_type_name(name: &str) -> String { + name.replace("~1", "_") // URL-encoded `/` in JSON references + .replace('/', "_") + .replace('$', "_") + .replace('<', "_") + .replace('>', "") + .replace(',', "_") + .replace(' ', "") +} + +// TODO: add policies, and script parameters as well +pub fn load_externals( + path: &str, +) -> Result, crate::parsing::Error> { + let json = fs::read_to_string(path).map_err(|e| crate::parsing::Error { + message: format!("Failed to read import file: {}", e), + src: "".to_string(), // TODO: propagate source? + span: crate::ast::Span::DUMMY, + })?; + let bp = + serde_json::from_str::(&json).map_err(|e| crate::parsing::Error { + message: format!("Failed to parse blueprint JSON: {}", e), + src: "".to_string(), // TODO: should I add path here? + span: crate::ast::Span::DUMMY, + })?; + + let ref_to_type = |r: &str| -> Type { + let sanitized = sanitize_type_name(r.strip_prefix("#/definitions/").unwrap_or(r)); + Type::Custom(Identifier::new(&sanitized)) + }; + + let mut symbols = HashMap::new(); + for (key, def) in bp + .definitions + .as_ref() + .map(|d| d.inner.iter()) + .into_iter() + .flatten() + { + let name = sanitize_type_name(key); + + let new = match def.data_type { + Some(cip_57::DataType::Integer) => Some(Symbol::AliasDef(Box::new( + crate::ast::AliasDef::new(&name, Type::Int), + ))), + Some(cip_57::DataType::Bytes) => Some(Symbol::AliasDef(Box::new( + crate::ast::AliasDef::new(&name, Type::Bytes), + ))), + Some(cip_57::DataType::Map) => { + let key_ty = def + .keys + .as_ref() + .and_then(|r| r.reference.as_ref()) + .map(|r| ref_to_type(r)); + let value = def + .values + .as_ref() + .and_then(|r| r.reference.as_ref()) + .map(|r| ref_to_type(r)); + + if let (Some(key_ty), Some(value)) = (key_ty, value) { + Some(Symbol::AliasDef(Box::new(crate::ast::AliasDef::new( + &name, + Type::Map(Box::new(key_ty), Box::new(value)), + )))) + } else { + None + } + } + Some(cip_57::DataType::List) => match &def.items { + Some(cip_57::ReferencesArray::Single(r)) => { + let name = name.clone(); + r.reference.as_ref().map(|r| { + Symbol::AliasDef(Box::new(crate::ast::AliasDef::new( + &name, + Type::List(Box::new(ref_to_type(r))), + ))) + }) + } + _ => None, + }, + Some(cip_57::DataType::Constructor) => { + let mut cases = vec![]; + if let Some(any_of) = &def.any_of { + for schema in any_of { + let case_name = schema + .title + .clone() + .unwrap_or_else(|| format!("Constructor{}", schema.index)); + let mut fields = vec![]; + for (i, field) in schema.fields.iter().enumerate() { + let field_name = field + .title + .clone() + .unwrap_or_else(|| format!("field_{}", i)); + let field_ty = ref_to_type(&field.reference); + fields.push(RecordField::new(&field_name, field_ty)); + } + cases.push(VariantCase { + name: Identifier::new(case_name), + fields, + span: Span::default(), + }); + } + } + Some(Symbol::TypeDef(Box::new(TypeDef { + name: Identifier::new(&name), + cases, + span: Span::default(), + }))) + } + None if def.any_of.is_some() => { + let mut cases = vec![]; + if let Some(any_of) = &def.any_of { + for schema in any_of { + let case_name = schema + .title + .clone() + .unwrap_or_else(|| format!("Constructor{}", schema.index)); + let mut fields = vec![]; + for (i, field) in schema.fields.iter().enumerate() { + let field_name = field + .title + .clone() + .unwrap_or_else(|| format!("field_{}", i)); + let field_ty = ref_to_type(&field.reference); + fields.push(RecordField::new(&field_name, field_ty)); + } + cases.push(VariantCase { + name: Identifier::new(case_name), + fields, + span: Span::default(), + }); + } + } + Some(Symbol::TypeDef(Box::new(TypeDef { + name: Identifier::new(&name), + cases, + span: Span::default(), + }))) + } + None => None, + }; + if let Some(symbol) = new { + symbols.insert(name, symbol); + } + } + Ok(symbols) +} + #[cfg(test)] mod tests { use super::*; - use crate::{ - analyzing::analyze, - ast::{self, *}, - }; + use crate::{analyzing::analyze, ast::*}; use pest::Parser; macro_rules! input_to_ast_check { diff --git a/crates/tx3-lang/src/loading.rs b/crates/tx3-lang/src/loading.rs index 8d95808d..9965f8f5 100644 --- a/crates/tx3-lang/src/loading.rs +++ b/crates/tx3-lang/src/loading.rs @@ -21,6 +21,8 @@ pub enum Error { InvalidEnvFile(String), } +use crate::cardano::load_externals; + /// Parses a Tx3 source file into a Program AST. /// /// # Arguments @@ -46,10 +48,41 @@ pub enum Error { /// ``` pub fn parse_file(path: &str) -> Result { let input = std::fs::read_to_string(path)?; - let program = parsing::parse_string(&input)?; + let mut program = parsing::parse_string(&input)?; + // Should it be configurable by trix.toml? A path for imports like "../onchain" and all imports + // would be really clean + let base_path = std::path::Path::new(path) + .parent() + .unwrap_or(std::path::Path::new(".")); + process_imports(&mut program, base_path)?; Ok(program) } +fn process_imports(program: &mut ast::Program, base_path: &Path) -> Result<(), Error> { + for import_path in &program.imports { + let full_path = base_path.join(import_path); + let path_str = full_path.to_str().ok_or_else(|| { + Error::Io(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Invalid path", + )) + })?; + let external_types = load_externals(path_str)?; + + if let Some(ref mut scope) = program.scope { + if let Some(scope_mut) = std::rc::Rc::get_mut(scope) { + scope_mut.symbols.extend(external_types); + } + } else { + program.scope = Some(std::rc::Rc::new(ast::Scope { + symbols: external_types, + parent: None, + })); + } + } + Ok(()) +} + pub type ArgMap = std::collections::HashMap; fn load_env_file(path: &Path) -> Result { @@ -132,14 +165,19 @@ impl ProtocolLoader { } pub fn load(self) -> Result { - let code = match (self.code_file, self.code_string) { + let code = match (&self.code_file, &self.code_string) { (Some(file), None) => std::fs::read_to_string(file)?, - (None, Some(code)) => code, + (None, Some(code)) => code.clone(), _ => unreachable!(), }; let mut ast = parsing::parse_string(&code)?; + if let Some(file) = &self.code_file { + let base_path = file.parent().unwrap_or(std::path::Path::new(".")); + process_imports(&mut ast, base_path)?; + } + if self.analyze { analyzing::analyze(&mut ast).ok()?; } @@ -173,4 +211,18 @@ pub mod tests { let manifest_dir = env!("CARGO_MANIFEST_DIR"); let _ = parse_file(&format!("{}/../..//examples/transfer.tx3", manifest_dir)).unwrap(); } + + #[test] + fn test_cardano_import() { + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let path = format!("{}/../../examples/cip_imports.tx3", manifest_dir); + let program = parse_file(&path).unwrap(); + + assert!(program.scope.is_some()); + let scope = program.scope.as_ref().unwrap(); + + assert!(scope.symbols.contains_key("Int")); + assert!(scope.symbols.contains_key("cardano_assets_AssetName")); + assert!(scope.symbols.contains_key("cardano_transaction_Datum")); + } } diff --git a/crates/tx3-lang/src/parsing.rs b/crates/tx3-lang/src/parsing.rs index 1e67ca80..2d885441 100644 --- a/crates/tx3-lang/src/parsing.rs +++ b/crates/tx3-lang/src/parsing.rs @@ -12,10 +12,7 @@ use pest::{ }; use pest_derive::Parser; -use crate::{ - ast::*, - cardano::{PlutusWitnessBlock, PlutusWitnessField}, -}; +use crate::{ast::*, cardano::load_externals}; #[derive(Parser)] #[grammar = "tx3.pest"] pub(crate) struct Tx3Grammar; @@ -94,6 +91,7 @@ impl AstNode for Program { aliases: Vec::new(), parties: Vec::new(), policies: Vec::new(), + imports: Vec::new(), scope: None, span, }; @@ -108,6 +106,10 @@ impl AstNode for Program { Rule::alias_def => program.aliases.push(AliasDef::parse(pair)?), Rule::party_def => program.parties.push(PartyDef::parse(pair)?), Rule::policy_def => program.policies.push(PolicyDef::parse(pair)?), + Rule::cardano_import => { + let import_path = pair.into_inner().next().unwrap().as_str().trim_matches('"'); + program.imports.push(import_path.to_string()); + } Rule::EOI => break, x => unreachable!("Unexpected rule in program: {:?}", x), } @@ -2627,6 +2629,7 @@ mod tests { env: None, assets: vec![], policies: vec![], + imports: vec![], span: Span::DUMMY, scope: None, } diff --git a/crates/tx3-lang/src/tx3.pest b/crates/tx3-lang/src/tx3.pest index 43ef45c1..4a43ce27 100644 --- a/crates/tx3-lang/src/tx3.pest +++ b/crates/tx3-lang/src/tx3.pest @@ -462,9 +462,14 @@ tx_def = { "tx" ~ identifier ~ parameter_list ~ "{" ~ tx_body_block* ~ "}" } +cardano_import = { + "cardano::import " ~ string ~ ";" +} + // Program program = { SOI ~ + (cardano_import)* ~ (env_def | asset_def | party_def | policy_def | type_def | tx_def)* ~ EOI } diff --git a/examples/asteria.ast b/examples/asteria.ast index 0d231790..a8555b4f 100644 --- a/examples/asteria.ast +++ b/examples/asteria.ast @@ -1081,6 +1081,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/burn.ast b/examples/burn.ast index 6eafd087..f5c16afb 100644 --- a/examples/burn.ast +++ b/examples/burn.ast @@ -285,6 +285,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/cardano_witness.ast b/examples/cardano_witness.ast index 9136d18d..93ccd6c0 100644 --- a/examples/cardano_witness.ast +++ b/examples/cardano_witness.ast @@ -652,6 +652,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/cardano_witness.mint_from_plutus.tir b/examples/cardano_witness.mint_from_plutus.tir index c0f4530f..8ff6e2e2 100644 --- a/examples/cardano_witness.mint_from_plutus.tir +++ b/examples/cardano_witness.mint_from_plutus.tir @@ -202,9 +202,6 @@ { "name": "plutus_witness", "data": { - "version": { - "Number": 3 - }, "script": { "Bytes": [ 81, @@ -226,6 +223,9 @@ 174, 105 ] + }, + "version": { + "Number": 3 } } } diff --git a/examples/cip_imports.tx3 b/examples/cip_imports.tx3 new file mode 100644 index 00000000..9a88e668 --- /dev/null +++ b/examples/cip_imports.tx3 @@ -0,0 +1,34 @@ +cardano::import "imports/plutus.json"; + +party Sender; + +party Receiver; + +tx transfer_with_imports( + quantity: Int +) { + input source { + from: Sender, + min_amount: Ada(quantity), + } + + output { + to: Receiver, + amount: Ada(quantity), + } + + output { + to: Sender, + amount: source - Ada(quantity) - fees, + datum: types_SettingsDatum::SettingsDatum { + githoney_address: cardano_address_Address::Address{ + payment_credential: cardano_address_PaymentCredential::VerificationKey { + field_0: 0x123123, + }, + stake_credential: Option_cardano_address_StakeCredential::None {}, + }, + bounty_creation_fee: 0, + bounty_reward_fee: 0, + }, + } +} diff --git a/examples/disordered.ast b/examples/disordered.ast index 5dd0f5f3..5f2e9a7f 100644 --- a/examples/disordered.ast +++ b/examples/disordered.ast @@ -299,6 +299,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/donation.ast b/examples/donation.ast index d7fd2b5b..54942850 100644 --- a/examples/donation.ast +++ b/examples/donation.ast @@ -316,6 +316,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/env_vars.ast b/examples/env_vars.ast index cef2b474..5958754d 100644 --- a/examples/env_vars.ast +++ b/examples/env_vars.ast @@ -255,6 +255,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/faucet.ast b/examples/faucet.ast index ef4e9653..51d509c8 100644 --- a/examples/faucet.ast +++ b/examples/faucet.ast @@ -320,6 +320,7 @@ } } ], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/imports/plutus.json b/examples/imports/plutus.json new file mode 100644 index 00000000..97472029 --- /dev/null +++ b/examples/imports/plutus.json @@ -0,0 +1,634 @@ +{ + "preamble": { + "title": "txpipe/contract", + "description": "Aiken contracts for project 'txpipe/contract'", + "version": "0.0.0", + "plutusVersion": "v3", + "compiler": { + "name": "Aiken", + "version": "v1.1.17+c3a7fba" + }, + "license": "Apache-2.0" + }, + "validators": [ + { + "title": "githoney_contract.badges_contract.spend", + "datum": { + "title": "_datum", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1Datum" + } + }, + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1Redeemer" + } + }, + "parameters": [ + { + "title": "settings_policy_id", + "schema": { + "$ref": "#/definitions/cardano~1assets~1PolicyId" + } + } + ], + "compiledCode": "eedaa957c60268de", + "hash": "24b9b1964ce02550db270a1d6270b505b9c0342625ee766d77fab1f9" + }, + { + "title": "githoney_contract.badges_contract.else", + "redeemer": { + "schema": {} + }, + "parameters": [ + { + "title": "settings_policy_id", + "schema": { + "$ref": "#/definitions/cardano~1assets~1PolicyId" + } + } + ], + "compiledCode": "3fe9763bc5ea0108", + "hash": "24b9b1964ce02550db270a1d6270b505b9c0342625ee766d77fab1f9" + }, + { + "title": "githoney_contract.badges_policy.mint", + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1Redeemer" + } + }, + "parameters": [ + { + "title": "utxo_ref", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1OutputReference" + } + }, + { + "title": "nonce", + "schema": { + "$ref": "#/definitions/Int" + } + } + ], + "compiledCode": "c8db1de3fbbc8921", + "hash": "87d6bd0b40f204d49803dad8e0d70611918d22354125c8f226a42670" + }, + { + "title": "githoney_contract.badges_policy.else", + "redeemer": { + "schema": {} + }, + "parameters": [ + { + "title": "utxo_ref", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1OutputReference" + } + }, + { + "title": "nonce", + "schema": { + "$ref": "#/definitions/Int" + } + } + ], + "compiledCode": "f143b98f13d5b12b", + "hash": "87d6bd0b40f204d49803dad8e0d70611918d22354125c8f226a42670" + }, + { + "title": "githoney_contract.githoney.spend", + "datum": { + "title": "datum", + "schema": { + "$ref": "#/definitions/types~1GithoneyDatum" + } + }, + "redeemer": { + "title": "redeemer", + "schema": { + "$ref": "#/definitions/types~1GithoneyContractRedeemers" + } + }, + "parameters": [ + { + "title": "settings_policy_id", + "schema": { + "$ref": "#/definitions/cardano~1assets~1PolicyId" + } + } + ], + "compiledCode": "f1a9d1de401b5004", + "hash": "7577273f99e7f3c9211d6342338d6316afc91689333c4d5099e7b2d1" + }, + { + "title": "githoney_contract.githoney.mint", + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1Redeemer" + } + }, + "parameters": [ + { + "title": "settings_policy_id", + "schema": { + "$ref": "#/definitions/cardano~1assets~1PolicyId" + } + } + ], + "compiledCode": "f1f665b7b5ec44d6", + "hash": "7577273f99e7f3c9211d6342338d6316afc91689333c4d5099e7b2d1" + }, + { + "title": "githoney_contract.githoney.else", + "redeemer": { + "schema": {} + }, + "parameters": [ + { + "title": "settings_policy_id", + "schema": { + "$ref": "#/definitions/cardano~1assets~1PolicyId" + } + } + ], + "compiledCode": "8dc98413cbd1b43f", + "hash": "7577273f99e7f3c9211d6342338d6316afc91689333c4d5099e7b2d1" + }, + { + "title": "githoney_contract.settings.spend", + "datum": { + "title": "datum", + "schema": { + "$ref": "#/definitions/types~1SettingsDatum" + } + }, + "redeemer": { + "title": "redeemer", + "schema": { + "$ref": "#/definitions/types~1SettingsRedeemers" + } + }, + "compiledCode": "5eb78784a02ee1a0", + "hash": "049f1a09fb535089fdd9df98b4b0975d1081f9afc2d6f59ad2f9c208" + }, + { + "title": "githoney_contract.settings.else", + "redeemer": { + "schema": {} + }, + "compiledCode": "9813b3c286674b27", + "hash": "049f1a09fb535089fdd9df98b4b0975d1081f9afc2d6f59ad2f9c208" + }, + { + "title": "githoney_contract.settings_minting.mint", + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1Redeemer" + } + }, + "parameters": [ + { + "title": "utxo_ref", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1OutputReference" + } + }, + { + "title": "settings_script_addr", + "schema": { + "$ref": "#/definitions/cardano~1address~1Address" + } + } + ], + "compiledCode": "6396e66bd8b6ac88", + "hash": "15721f473d73adf918aa7541871739c2e8df0a60abe023411cf9f1b0" + }, + { + "title": "githoney_contract.settings_minting.else", + "redeemer": { + "schema": {} + }, + "parameters": [ + { + "title": "utxo_ref", + "schema": { + "$ref": "#/definitions/cardano~1transaction~1OutputReference" + } + }, + { + "title": "settings_script_addr", + "schema": { + "$ref": "#/definitions/cardano~1address~1Address" + } + } + ], + "compiledCode": "fa2dba3b69a8d00c", + "hash": "15721f473d73adf918aa7541871739c2e8df0a60abe023411cf9f1b0" + } + ], + "definitions": { + "Bool": { + "title": "Bool", + "anyOf": [ + { + "title": "False", + "dataType": "constructor", + "index": 0, + "fields": [] + }, + { + "title": "True", + "dataType": "constructor", + "index": 1, + "fields": [] + } + ] + }, + "ByteArray": { + "title": "ByteArray", + "dataType": "bytes" + }, + "Data": { + "title": "Data", + "description": "Any Plutus data." + }, + "Int": { + "dataType": "integer" + }, + "Option$cardano/address/Address": { + "title": "Option", + "anyOf": [ + { + "title": "Some", + "description": "An optional value.", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "$ref": "#/definitions/cardano~1address~1Address" + } + ] + }, + { + "title": "None", + "description": "Nothing.", + "dataType": "constructor", + "index": 1, + "fields": [] + } + ] + }, + "Option$cardano/address/StakeCredential": { + "title": "Option", + "anyOf": [ + { + "title": "Some", + "description": "An optional value.", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "$ref": "#/definitions/cardano~1address~1StakeCredential" + } + ] + }, + { + "title": "None", + "description": "Nothing.", + "dataType": "constructor", + "index": 1, + "fields": [] + } + ] + }, + "Pairs$cardano/assets/AssetName_Int": { + "title": "Pairs", + "dataType": "map", + "keys": { + "$ref": "#/definitions/cardano~1assets~1AssetName" + }, + "values": { + "$ref": "#/definitions/Int" + } + }, + "Pairs$cardano/assets/PolicyId_Pairs$cardano/assets/AssetName_Int": { + "title": "Pairs>", + "dataType": "map", + "keys": { + "$ref": "#/definitions/cardano~1assets~1PolicyId" + }, + "values": { + "$ref": "#/definitions/Pairs$cardano~1assets~1AssetName_Int" + } + }, + "aiken/crypto/DataHash": { + "title": "DataHash", + "dataType": "bytes" + }, + "aiken/crypto/ScriptHash": { + "title": "ScriptHash", + "dataType": "bytes" + }, + "aiken/crypto/VerificationKeyHash": { + "title": "VerificationKeyHash", + "dataType": "bytes" + }, + "cardano/address/Address": { + "title": "Address", + "description": "A Cardano `Address` typically holding one or two credential references.\n\n Note that legacy bootstrap addresses (a.k.a. 'Byron addresses') are\n completely excluded from Plutus contexts. Thus, from an on-chain\n perspective only exists addresses of type 00, 01, ..., 07 as detailed\n in [CIP-0019 :: Shelley Addresses](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0019/#shelley-addresses).", + "anyOf": [ + { + "title": "Address", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "title": "payment_credential", + "$ref": "#/definitions/cardano~1address~1PaymentCredential" + }, + { + "title": "stake_credential", + "$ref": "#/definitions/Option$cardano~1address~1StakeCredential" + } + ] + } + ] + }, + "cardano/address/Credential": { + "title": "Credential", + "description": "A general structure for representing an on-chain `Credential`.\n\n Credentials are always one of two kinds: a direct public/private key\n pair, or a script (native or Plutus).", + "anyOf": [ + { + "title": "VerificationKey", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "$ref": "#/definitions/aiken~1crypto~1VerificationKeyHash" + } + ] + }, + { + "title": "Script", + "dataType": "constructor", + "index": 1, + "fields": [ + { + "$ref": "#/definitions/aiken~1crypto~1ScriptHash" + } + ] + } + ] + }, + "cardano/address/PaymentCredential": { + "title": "PaymentCredential", + "description": "A general structure for representing an on-chain `Credential`.\n\n Credentials are always one of two kinds: a direct public/private key\n pair, or a script (native or Plutus).", + "anyOf": [ + { + "title": "VerificationKey", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "$ref": "#/definitions/aiken~1crypto~1VerificationKeyHash" + } + ] + }, + { + "title": "Script", + "dataType": "constructor", + "index": 1, + "fields": [ + { + "$ref": "#/definitions/aiken~1crypto~1ScriptHash" + } + ] + } + ] + }, + "cardano/address/StakeCredential": { + "title": "StakeCredential", + "description": "Represent a type of object that can be represented either inline (by hash)\n or via a reference (i.e. a pointer to an on-chain location).\n\n This is mainly use for capturing pointers to a stake credential\n registration certificate in the case of so-called pointer addresses.", + "anyOf": [ + { + "title": "Inline", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "$ref": "#/definitions/cardano~1address~1Credential" + } + ] + }, + { + "title": "Pointer", + "dataType": "constructor", + "index": 1, + "fields": [ + { + "title": "slot_number", + "$ref": "#/definitions/Int" + }, + { + "title": "transaction_index", + "$ref": "#/definitions/Int" + }, + { + "title": "certificate_index", + "$ref": "#/definitions/Int" + } + ] + } + ] + }, + "cardano/assets/AssetName": { + "title": "AssetName", + "dataType": "bytes" + }, + "cardano/assets/PolicyId": { + "title": "PolicyId", + "dataType": "bytes" + }, + "cardano/transaction/Datum": { + "title": "Datum", + "description": "An output `Datum`.", + "anyOf": [ + { + "title": "NoDatum", + "dataType": "constructor", + "index": 0, + "fields": [] + }, + { + "title": "DatumHash", + "description": "A datum referenced by its hash digest.", + "dataType": "constructor", + "index": 1, + "fields": [ + { + "$ref": "#/definitions/aiken~1crypto~1DataHash" + } + ] + }, + { + "title": "InlineDatum", + "description": "A datum completely inlined in the output.", + "dataType": "constructor", + "index": 2, + "fields": [ + { + "$ref": "#/definitions/Data" + } + ] + } + ] + }, + "cardano/transaction/OutputReference": { + "title": "OutputReference", + "description": "An `OutputReference` is a unique reference to an output on-chain. The `output_index`\n corresponds to the position in the output list of the transaction (identified by its id)\n that produced that output", + "anyOf": [ + { + "title": "OutputReference", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "title": "transaction_id", + "$ref": "#/definitions/ByteArray" + }, + { + "title": "output_index", + "$ref": "#/definitions/Int" + } + ] + } + ] + }, + "cardano/transaction/Redeemer": { + "title": "Redeemer", + "description": "Any Plutus data." + }, + "types/GithoneyContractRedeemers": { + "title": "GithoneyContractRedeemers", + "anyOf": [ + { + "title": "AddRewards", + "dataType": "constructor", + "index": 0, + "fields": [] + }, + { + "title": "Assign", + "dataType": "constructor", + "index": 1, + "fields": [] + }, + { + "title": "Merge", + "dataType": "constructor", + "index": 2, + "fields": [] + }, + { + "title": "Close", + "dataType": "constructor", + "index": 3, + "fields": [] + }, + { + "title": "Claim", + "dataType": "constructor", + "index": 4, + "fields": [] + } + ] + }, + "types/GithoneyDatum": { + "title": "GithoneyDatum", + "anyOf": [ + { + "title": "GithoneyDatum", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "title": "admin_payment_credential", + "$ref": "#/definitions/cardano~1address~1Credential" + }, + { + "title": "maintainer_address", + "$ref": "#/definitions/cardano~1address~1Address" + }, + { + "title": "contributor_address", + "$ref": "#/definitions/Option$cardano~1address~1Address" + }, + { + "title": "bounty_reward_fee", + "$ref": "#/definitions/Int" + }, + { + "title": "deadline", + "$ref": "#/definitions/Int" + }, + { + "title": "merged", + "$ref": "#/definitions/Bool" + }, + { + "title": "initial_value", + "$ref": "#/definitions/Pairs$cardano~1assets~1PolicyId_Pairs$cardano~1assets~1AssetName_Int" + } + ] + } + ] + }, + "types/SettingsDatum": { + "title": "SettingsDatum", + "anyOf": [ + { + "title": "SettingsDatum", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "title": "githoney_address", + "$ref": "#/definitions/cardano~1address~1Address" + }, + { + "title": "bounty_creation_fee", + "$ref": "#/definitions/Int" + }, + { + "title": "bounty_reward_fee", + "$ref": "#/definitions/Int" + } + ] + } + ] + }, + "types/SettingsRedeemers": { + "title": "SettingsRedeemers", + "anyOf": [ + { + "title": "UpdateSettings", + "dataType": "constructor", + "index": 0, + "fields": [] + }, + { + "title": "CloseSettings", + "dataType": "constructor", + "index": 1, + "fields": [] + } + ] + } + } +} diff --git a/examples/input_datum.ast b/examples/input_datum.ast index c0cb2672..dc43f438 100644 --- a/examples/input_datum.ast +++ b/examples/input_datum.ast @@ -364,6 +364,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/lang_tour.ast b/examples/lang_tour.ast index 882ba085..71060e61 100644 --- a/examples/lang_tour.ast +++ b/examples/lang_tour.ast @@ -1582,6 +1582,7 @@ } } ], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/list_concat.ast b/examples/list_concat.ast index f8b6c17f..65d268bf 100644 --- a/examples/list_concat.ast +++ b/examples/list_concat.ast @@ -305,6 +305,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/local_vars.ast b/examples/local_vars.ast index 432bb4ff..c950a292 100644 --- a/examples/local_vars.ast +++ b/examples/local_vars.ast @@ -258,6 +258,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/map.ast b/examples/map.ast index 7f8e3eee..abcc336c 100644 --- a/examples/map.ast +++ b/examples/map.ast @@ -439,6 +439,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/reference_script.ast b/examples/reference_script.ast index 9c312a18..22ada355 100644 --- a/examples/reference_script.ast +++ b/examples/reference_script.ast @@ -564,6 +564,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/reference_script.publish_native.tir b/examples/reference_script.publish_native.tir index 90fdd3b9..73821504 100644 --- a/examples/reference_script.publish_native.tir +++ b/examples/reference_script.publish_native.tir @@ -137,16 +137,6 @@ { "name": "cardano_publish", "data": { - "script": { - "Bytes": [ - 130, - 1, - 129, - 130, - 4, - 0 - ] - }, "amount": { "Assets": [ { @@ -163,6 +153,19 @@ } ] }, + "script": { + "Bytes": [ + 130, + 1, + 129, + 130, + 4, + 0 + ] + }, + "version": { + "Number": 0 + }, "to": { "EvalParam": { "ExpectValue": [ @@ -170,9 +173,6 @@ "Address" ] } - }, - "version": { - "Number": 0 } } } diff --git a/examples/reference_script.publish_plutus.tir b/examples/reference_script.publish_plutus.tir index 7506c0f5..81087f15 100644 --- a/examples/reference_script.publish_plutus.tir +++ b/examples/reference_script.publish_plutus.tir @@ -140,22 +140,6 @@ "version": { "Number": 3 }, - "amount": { - "Assets": [ - { - "policy": "None", - "asset_name": "None", - "amount": { - "EvalParam": { - "ExpectValue": [ - "quantity", - "Int" - ] - } - } - } - ] - }, "script": { "Bytes": [ 81, @@ -185,6 +169,22 @@ "Address" ] } + }, + "amount": { + "Assets": [ + { + "policy": "None", + "asset_name": "None", + "amount": { + "EvalParam": { + "ExpectValue": [ + "quantity", + "Int" + ] + } + } + } + ] } } } diff --git a/examples/swap.ast b/examples/swap.ast index 8d915d0f..d1ff5dd8 100644 --- a/examples/swap.ast +++ b/examples/swap.ast @@ -743,6 +743,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/transfer.ast b/examples/transfer.ast index b100c14b..acc911e2 100644 --- a/examples/transfer.ast +++ b/examples/transfer.ast @@ -280,6 +280,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/vesting.ast b/examples/vesting.ast index 5c40088d..692c4570 100644 --- a/examples/vesting.ast +++ b/examples/vesting.ast @@ -754,6 +754,7 @@ } } ], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/withdrawal.ast b/examples/withdrawal.ast index ca893dd5..5c79f5a0 100644 --- a/examples/withdrawal.ast +++ b/examples/withdrawal.ast @@ -314,6 +314,7 @@ } ], "policies": [], + "imports": [], "span": { "dummy": false, "start": 0, diff --git a/examples/withdrawal.transfer.tir b/examples/withdrawal.transfer.tir index facdbb3c..ec19f1e1 100644 --- a/examples/withdrawal.transfer.tir +++ b/examples/withdrawal.transfer.tir @@ -165,6 +165,9 @@ { "name": "withdrawal", "data": { + "amount": { + "Number": 0 + }, "credential": { "EvalParam": { "ExpectValue": [ @@ -173,9 +176,6 @@ ] } }, - "amount": { - "Number": 0 - }, "redeemer": { "Struct": { "constructor": 0,