From 713659edcdde4005a7e8ee2f32d0ad0b93c6a988 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Tue, 16 Sep 2025 13:36:10 -0300 Subject: [PATCH 01/18] feat: add metadata size validation and error handling --- crates/tx3-lang/src/analyzing.rs | 218 ++++++++++++++++++++++++++++++- 1 file changed, 217 insertions(+), 1 deletion(-) diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index d96221dc..ddac2254 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -9,6 +9,8 @@ use miette::Diagnostic; use crate::ast::*; +const METADATA_MAX_SIZE_BYTES: usize = 64; + #[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq)] #[error("not in scope: {name}")] #[diagnostic(code(tx3::not_in_scope))] @@ -50,6 +52,19 @@ pub struct InvalidTargetTypeError { span: Span, } +#[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq)] +#[error("metadata value exceeds 64 bytes: {size} bytes found")] +#[diagnostic(code(tx3::metadata_size_limit_exceeded))] +pub struct MetadataSizeLimitError { + pub size: usize, + + #[source_code] + src: Option, + + #[label("this value is too large")] + span: Span, +} + #[derive(thiserror::Error, Debug, miette::Diagnostic, PartialEq, Eq)] pub enum Error { #[error("duplicate definition: {0}")] @@ -72,6 +87,10 @@ pub enum Error { #[error(transparent)] #[diagnostic(transparent)] InvalidTargetType(#[from] InvalidTargetTypeError), + + #[error(transparent)] + #[diagnostic(transparent)] + MetadataSizeLimitExceeded(#[from] MetadataSizeLimitError), } impl Error { @@ -80,6 +99,7 @@ impl Error { Self::NotInScope(x) => &x.span, Self::InvalidSymbol(x) => &x.span, Self::InvalidTargetType(x) => &x.span, + Self::MetadataSizeLimitExceeded(x) => &x.span, _ => &Span::DUMMY, } } @@ -87,6 +107,7 @@ impl Error { pub fn src(&self) -> Option<&str> { match self { Self::NotInScope(x) => x.src.as_deref(), + Self::MetadataSizeLimitExceeded(x) => x.src.as_deref(), _ => None, } } @@ -760,10 +781,53 @@ impl Analyzable for InputBlock { } } +fn validate_metadata_value_size(expr: &DataExpr) -> Result<(), MetadataSizeLimitError> { + match expr { + DataExpr::String(string_literal) => { + let utf8_bytes = string_literal.value.as_bytes(); + if utf8_bytes.len() > METADATA_MAX_SIZE_BYTES { + return Err(MetadataSizeLimitError { + size: utf8_bytes.len(), + src: None, + span: string_literal.span.clone(), + }); + } + } + DataExpr::HexString(hex_literal) => { + let hex_str = &hex_literal.value; + let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str); + let byte_length = hex_str.len() / 2; + + if byte_length > METADATA_MAX_SIZE_BYTES { + return Err(MetadataSizeLimitError { + size: byte_length, + src: None, + span: hex_literal.span.clone(), + }); + } + } + // For other expression types, we can't validate at analysis time + // since they might be computed at runtime + _ => {} + } + Ok(()) +} + impl Analyzable for MetadataBlockField { fn analyze(&mut self, parent: Option>) -> AnalyzeReport { // TODO: check keys are actually numbers - self.key.analyze(parent.clone()) + self.value.analyze(parent.clone()) + let mut report = self.key.analyze(parent.clone()) + self.value.analyze(parent.clone()); + + match validate_metadata_value_size(&self.value) { + Ok(()) => {} + Err(size_error) => { + report + .errors + .push(Error::MetadataSizeLimitExceeded(size_error)); + } + } + + report } fn is_resolved(&self) -> bool { @@ -1255,4 +1319,156 @@ mod tests { let result = analyze(&mut ast); assert!(!result.errors.is_empty()); } + + #[test] + fn test_metadata_value_size_validation_string_within_limit() { + let mut ast = crate::parsing::parse_string( + r#" + tx test() { + metadata { + 123: "This is a short string that is within the 64-byte limit", + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + assert!( + result.errors.is_empty(), + "Expected no errors for string within limit, but got: {:?}", + result.errors + ); + } + + #[test] + fn test_metadata_value_size_validation_string_exceeds_limit() { + let mut ast = crate::parsing::parse_string( + r#" + tx test() { + metadata { + 123: "This is a very long string that definitely exceeds the 64-byte limit here", + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + assert_eq!(result.errors.len(), 1); + + match &result.errors[0] { + Error::MetadataSizeLimitExceeded(error) => { + assert_eq!(error.size, 73); + } + _ => panic!( + "Expected MetadataSizeLimitExceeded error, got: {:?}", + result.errors[0] + ), + } + } + + #[test] + fn test_metadata_value_size_validation_hex_string_within_limit() { + let mut ast = crate::parsing::parse_string( + r#" + tx test() { + metadata { + 123: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef, + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + assert!( + result.errors.is_empty(), + "Expected no errors for hex string within limit, but got: {:?}", + result.errors + ); + } + + #[test] + fn test_metadata_value_size_validation_hex_string_exceeds_limit() { + let mut ast = crate::parsing::parse_string( + r#" + tx test() { + metadata { + 123: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12, + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + assert_eq!(result.errors.len(), 1); + + match &result.errors[0] { + Error::MetadataSizeLimitExceeded(error) => { + assert_eq!(error.size, 65); + } + _ => panic!( + "Expected MetadataSizeLimitExceeded error, got: {:?}", + result.errors[0] + ), + } + } + + #[test] + fn test_metadata_value_size_validation_multiple_fields() { + let mut ast = crate::parsing::parse_string( + r#" + tx test() { + metadata { + 123: "Short string", + 456: "This is a very long string that definitely exceeds the 64-byte limit here", + 789: "Another short one", + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + assert_eq!(result.errors.len(), 1); + + match &result.errors[0] { + Error::MetadataSizeLimitExceeded(error) => { + assert_eq!(error.size, 73); + } + _ => panic!( + "Expected MetadataSizeLimitExceeded error, got: {:?}", + result.errors[0] + ), + } + } + + #[test] + fn test_metadata_value_size_validation_non_literal_expression() { + let mut ast = crate::parsing::parse_string( + r#" + party Alice; + + tx test(my_param: Bytes) { + metadata { + 123: my_param, + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + let metadata_errors: Vec<_> = result + .errors + .iter() + .filter(|e| matches!(e, Error::MetadataSizeLimitExceeded(_))) + .collect(); + assert!( + metadata_errors.is_empty(), + "Expected no metadata size errors for non-literal expressions" + ); + } } From b8044f3c6214c8df23b8610d6578c79aab4093b1 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Tue, 16 Sep 2025 15:07:22 -0300 Subject: [PATCH 02/18] feat: add validation for metadata key types and improve error handling --- crates/tx3-lang/src/analyzing.rs | 156 ++++++++++++++++++++++++++++--- 1 file changed, 144 insertions(+), 12 deletions(-) diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index ddac2254..e92df948 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -8,6 +8,7 @@ use std::{collections::HashMap, rc::Rc}; use miette::Diagnostic; use crate::ast::*; +use crate::parsing::AstNode; const METADATA_MAX_SIZE_BYTES: usize = 64; @@ -61,7 +62,20 @@ pub struct MetadataSizeLimitError { #[source_code] src: Option, - #[label("this value is too large")] + #[label("value too large")] + span: Span, +} + +#[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq)] +#[error("metadata key must be an integer, got: {key_type}")] +#[diagnostic(code(tx3::metadata_invalid_key_type))] +pub struct MetadataInvalidKeyTypeError { + pub key_type: String, + + #[source_code] + src: Option, + + #[label("expected integer key")] span: Span, } @@ -91,6 +105,10 @@ pub enum Error { #[error(transparent)] #[diagnostic(transparent)] MetadataSizeLimitExceeded(#[from] MetadataSizeLimitError), + + #[error(transparent)] + #[diagnostic(transparent)] + MetadataInvalidKeyType(#[from] MetadataInvalidKeyTypeError), } impl Error { @@ -100,6 +118,7 @@ impl Error { Self::InvalidSymbol(x) => &x.span, Self::InvalidTargetType(x) => &x.span, Self::MetadataSizeLimitExceeded(x) => &x.span, + Self::MetadataInvalidKeyType(x) => &x.span, _ => &Span::DUMMY, } } @@ -108,6 +127,7 @@ impl Error { match self { Self::NotInScope(x) => x.src.as_deref(), Self::MetadataSizeLimitExceeded(x) => x.src.as_deref(), + Self::MetadataInvalidKeyType(x) => x.src.as_deref(), _ => None, } } @@ -806,26 +826,59 @@ fn validate_metadata_value_size(expr: &DataExpr) -> Result<(), MetadataSizeLimit }); } } - // For other expression types, we can't validate at analysis time - // since they might be computed at runtime _ => {} } Ok(()) } +fn validate_metadata_key_type(expr: &DataExpr) -> Result<(), MetadataInvalidKeyTypeError> { + match expr { + DataExpr::Number(_) => Ok(()), + DataExpr::Identifier(id) => match id.target_type() { + Some(Type::Int) => Ok(()), + Some(other_type) => Err(MetadataInvalidKeyTypeError { + key_type: format!("identifier of type {}", other_type), + src: None, + span: id.span().clone(), + }), + None => Err(MetadataInvalidKeyTypeError { + key_type: "unresolved identifier".to_string(), + src: None, + span: id.span().clone(), + }), + }, + _ => { + let key_type = match expr { + DataExpr::String(_) => "string", + DataExpr::HexString(_) => "hex string", + DataExpr::ListConstructor(_) => "list", + DataExpr::MapConstructor(_) => "map", + DataExpr::StructConstructor(_) => "struct", + _ => "unknown", + }; + + Err(MetadataInvalidKeyTypeError { + key_type: key_type.to_string(), + src: None, + span: expr.span().clone(), + }) + } + } +} + impl Analyzable for MetadataBlockField { fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - // TODO: check keys are actually numbers let mut report = self.key.analyze(parent.clone()) + self.value.analyze(parent.clone()); - match validate_metadata_value_size(&self.value) { - Ok(()) => {} - Err(size_error) => { - report - .errors - .push(Error::MetadataSizeLimitExceeded(size_error)); - } - } + validate_metadata_key_type(&self.key) + .map_err(Error::MetadataInvalidKeyType) + .err() + .map(|e| report.errors.push(e)); + + validate_metadata_value_size(&self.value) + .map_err(Error::MetadataSizeLimitExceeded) + .err() + .map(|e| report.errors.push(e)); report } @@ -1471,4 +1524,83 @@ mod tests { "Expected no metadata size errors for non-literal expressions" ); } + + #[test] + fn test_metadata_key_type_validation_string_key() { + let mut ast = crate::parsing::parse_string( + r#" + tx test() { + metadata { + "invalid_key": "some value", + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + assert_eq!(result.errors.len(), 1); + + match &result.errors[0] { + Error::MetadataInvalidKeyType(error) => { + assert_eq!(error.key_type, "string"); + } + _ => panic!( + "Expected MetadataInvalidKeyType error, got: {:?}", + result.errors[0] + ), + } + } + + #[test] + fn test_metadata_key_type_validation_identifier_with_int_type() { + let mut ast = crate::parsing::parse_string( + r#" + tx test(my_key: Int) { + metadata { + my_key: "valid value", + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + let key_type_errors: Vec<_> = result + .errors + .iter() + .filter(|e| matches!(e, Error::MetadataInvalidKeyType(_))) + .collect(); + assert!( + key_type_errors.is_empty(), + "Expected no key type errors for Int parameter used as key" + ); + } + + #[test] + fn test_metadata_key_type_validation_identifier_with_wrong_type() { + let mut ast = crate::parsing::parse_string( + r#" + tx test(my_key: Bytes) { + metadata { + my_key: "some value", + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + assert_eq!(result.errors.len(), 1); + + match &result.errors[0] { + Error::MetadataInvalidKeyType(error) => { + assert!(error.key_type.contains("identifier of type Bytes")); + } + _ => panic!( + "Expected MetadataInvalidKeyType error, got: {:?}", + result.errors[0] + ), + } + } } From 11f6a9a5f73f57c3e1c3c572bc30cb15a3879540 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Fri, 3 Oct 2025 14:28:52 -0300 Subject: [PATCH 03/18] feat: add optional field to OutputBlock and related parsing logic --- crates/tx3-lang/src/applying.rs | 1 + crates/tx3-lang/src/ast.rs | 1 + crates/tx3-lang/src/ir.rs | 2 ++ crates/tx3-lang/src/lowering.rs | 1 + crates/tx3-lang/src/parsing.rs | 16 +++++++++++++++- crates/tx3-lang/src/tx3.pest | 3 ++- 6 files changed, 22 insertions(+), 2 deletions(-) diff --git a/crates/tx3-lang/src/applying.rs b/crates/tx3-lang/src/applying.rs index 0936f26d..b1f461e7 100644 --- a/crates/tx3-lang/src/applying.rs +++ b/crates/tx3-lang/src/applying.rs @@ -1154,6 +1154,7 @@ impl Composite for ir::Output { address: f(self.address)?, datum: f(self.datum)?, amount: f(self.amount)?, + optional: self.optional, }) } } diff --git a/crates/tx3-lang/src/ast.rs b/crates/tx3-lang/src/ast.rs index 11bc0ecf..a3e97bfe 100644 --- a/crates/tx3-lang/src/ast.rs +++ b/crates/tx3-lang/src/ast.rs @@ -393,6 +393,7 @@ impl OutputBlockField { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct OutputBlock { pub name: Option, + pub optional: bool, pub fields: Vec, pub span: Span, } diff --git a/crates/tx3-lang/src/ir.rs b/crates/tx3-lang/src/ir.rs index a87c190d..8103cb5e 100644 --- a/crates/tx3-lang/src/ir.rs +++ b/crates/tx3-lang/src/ir.rs @@ -320,6 +320,7 @@ pub struct Output { pub address: Expression, pub datum: Expression, pub amount: Expression, + pub optional: bool, } #[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)] @@ -538,6 +539,7 @@ impl Node for Output { address: self.address.apply(visitor)?, datum: self.datum.apply(visitor)?, amount: self.amount.apply(visitor)?, + optional: self.optional, }; Ok(visited) diff --git a/crates/tx3-lang/src/lowering.rs b/crates/tx3-lang/src/lowering.rs index c4d53b0f..bbd517b3 100644 --- a/crates/tx3-lang/src/lowering.rs +++ b/crates/tx3-lang/src/lowering.rs @@ -610,6 +610,7 @@ impl IntoLower for ast::OutputBlock { address, datum, amount, + optional: self.optional, }) } } diff --git a/crates/tx3-lang/src/parsing.rs b/crates/tx3-lang/src/parsing.rs index f48cd1ae..03506538 100644 --- a/crates/tx3-lang/src/parsing.rs +++ b/crates/tx3-lang/src/parsing.rs @@ -587,6 +587,14 @@ impl AstNode for OutputBlock { let span = pair.as_span().into(); let mut inner = pair.into_inner(); + let optional = inner + .peek() + .map_or(false, |first| first.as_rule() == Rule::output_optional); + + if optional { + inner.next(); + } + let has_name = inner .peek() .map(|x| x.as_rule() == Rule::identifier) @@ -602,7 +610,12 @@ impl AstNode for OutputBlock { .map(|x| OutputBlockField::parse(x)) .collect::, _>>()?; - Ok(OutputBlock { name, fields, span }) + Ok(OutputBlock { + name, + optional, + fields, + span, + }) } fn span(&self) -> &Span { @@ -2297,6 +2310,7 @@ mod tests { }"#, OutputBlock { name: None, + optional: false, fields: vec![ OutputBlockField::To(Box::new(DataExpr::Identifier(Identifier::new( "my_party".to_string(), diff --git a/crates/tx3-lang/src/tx3.pest b/crates/tx3-lang/src/tx3.pest index 448e0442..a89268ce 100644 --- a/crates/tx3-lang/src/tx3.pest +++ b/crates/tx3-lang/src/tx3.pest @@ -206,6 +206,7 @@ map_constructor = { // input block input_many = { "*" } +output_optional = { "?" } input_block_from = { "from" ~ ":" ~ data_expr } input_block_datum_is = { "datum_is" ~ ":" ~ type } input_block_min_amount = { "min_amount" ~ ":" ~ data_expr } @@ -255,7 +256,7 @@ output_block_field = _{ } output_block = { - "output" ~ identifier? ~ "{" ~ + "output" ~ output_optional? ~ identifier? ~ "{" ~ (output_block_field ~ ",")* ~ "}" } From 6d8ca5d78b00138074756ad9ddc866496d908e86 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Fri, 3 Oct 2025 16:24:22 -0300 Subject: [PATCH 04/18] feat(lang): add error handling for optional outputs with datum --- crates/tx3-cardano/src/compile/mod.rs | 45 ++++++++++----- crates/tx3-lang/src/analyzing.rs | 82 ++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 16 deletions(-) diff --git a/crates/tx3-cardano/src/compile/mod.rs b/crates/tx3-cardano/src/compile/mod.rs index 72edb08a..f2d90397 100644 --- a/crates/tx3-cardano/src/compile/mod.rs +++ b/crates/tx3-cardano/src/compile/mod.rs @@ -1,7 +1,10 @@ use std::collections::{BTreeMap, HashSet}; use pallas::{ - codec::{minicbor, utils::{KeepRaw, MaybeIndefArray, NonEmptySet}}, + codec::{ + minicbor, + utils::{KeepRaw, MaybeIndefArray, NonEmptySet}, + }, ledger::{ primitives::{ conway::{self as primitives, Redeemers}, @@ -153,13 +156,14 @@ fn compile_adhoc_script( .transpose()? .map(|v| v as PlutusVersion) .unwrap_or(3); - let script_bytes= script.unwrap().to_vec(); + let script_bytes = script.unwrap().to_vec(); let script_ref = match version { 0 => { - let decoded: pallas::codec::utils::KeepRaw<'_, primitives::NativeScript> = minicbor::decode(&script_bytes).unwrap(); + let decoded: pallas::codec::utils::KeepRaw<'_, primitives::NativeScript> = + minicbor::decode(&script_bytes).unwrap(); let owned_script = decoded.to_owned(); primitives::ScriptRef::NativeScript(owned_script) - }, + } 1 => { let script = primitives::PlutusScript::<1>(script_bytes.into()); primitives::ScriptRef::PlutusV1Script(script) @@ -272,11 +276,25 @@ fn compile_outputs( tx: &ir::Tx, network: Network, ) -> Result>, Error> { - let mut resolved = tx - .outputs - .iter() - .map(|x| compile_output_block(x, network)) - .collect::, _>>()?; + let mut resolved = Vec::new(); + + for out in &tx.outputs { + let asset_list = coercion::expr_into_assets(&out.amount)?; + + let total: i128 = asset_list + .iter() + .map(|asset| coercion::expr_into_number(&asset.amount)) + .collect::, _>>()? + .into_iter() + .fold(0i128, |acc, n| acc.saturating_add(n)); + + // TODO - what happens when the value is between 0 and min_utxo? + if out.optional && total == 0 { + continue; + } + + resolved.push(compile_output_block(out, network)?); + } let cardano_outputs = tx .adhoc @@ -312,14 +330,11 @@ pub fn compile_cardano_publish_directive( .collect::, _>>()?; let value = asset_math::aggregate_values(values); - let datum_option = adhoc - .data - .get("datum") - .map(compile_data_expr) - .transpose()?; + let datum_option = adhoc.data.get("datum").map(compile_data_expr).transpose()?; let script_ref = if let (Some(version_expr), Some(script_expr)) = - (adhoc.data.get("version"), adhoc.data.get("script")) { + (adhoc.data.get("version"), adhoc.data.get("script")) + { // Create a synthetic adhoc directive that compile_adhoc_script can handle let mut script_data = std::collections::HashMap::new(); script_data.insert("version".to_string(), version_expr.clone()); diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index 03bc326f..968f0a3a 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -50,6 +50,19 @@ pub struct InvalidTargetTypeError { span: Span, } +#[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq)] +#[error("optional output ({name}) cannot have a datum")] +#[diagnostic(code(tx3::optional_output_datum))] +pub struct OptionalOutputError { + pub name: String, + + #[source_code] + src: Option, + + #[label] + span: Span, +} + #[derive(thiserror::Error, Debug, miette::Diagnostic, PartialEq, Eq)] pub enum Error { #[error("duplicate definition: {0}")] @@ -72,6 +85,10 @@ pub enum Error { #[error(transparent)] #[diagnostic(transparent)] InvalidTargetType(#[from] InvalidTargetTypeError), + + #[error(transparent)] + #[diagnostic(transparent)] + InvalidOutputDatum(#[from] OptionalOutputError), } impl Error { @@ -80,6 +97,7 @@ impl Error { Self::NotInScope(x) => &x.span, Self::InvalidSymbol(x) => &x.span, Self::InvalidTargetType(x) => &x.span, + Self::InvalidOutputDatum(x) => &x.span, _ => &Span::DUMMY, } } @@ -165,7 +183,7 @@ impl std::fmt::Display for AnalyzeReport { } else { write!(f, "Failed with {} errors:", self.errors.len())?; for error in &self.errors { - write!(f, "\n{:?}", error)?; + write!(f, "\n{} ({:?})", error, error)?; } Ok(()) } @@ -839,6 +857,20 @@ impl Analyzable for OutputBlockField { impl Analyzable for OutputBlock { fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + if self.optional { + if let Some(_field) = self.find("datum") { + return AnalyzeReport::from(Error::InvalidOutputDatum(OptionalOutputError { + name: self + .name + .as_ref() + .map(|i| i.value.clone()) + .unwrap_or_else(|| "".to_string()), + src: None, + span: self.span.clone(), + })); + } + } + self.fields.analyze(parent) } @@ -1268,4 +1300,52 @@ mod tests { let result = analyze(&mut ast); assert!(!result.errors.is_empty()); } + + #[test] + fn test_optional_output_with_datum_error() { + let mut ast = crate::parsing::parse_string( + r#" + party Alice; + type MyDatum { + field1: Int, + } + tx test() { + output ? my_output { + to: Alice, + amount: Ada(1), + datum: MyDatum { field1: 1, }, + } + } + "#, + ) + .unwrap(); + + let report = analyze(&mut ast); + + assert!(!report.errors.is_empty()); + assert!(report + .errors + .iter() + .any(|e| matches!(e, Error::InvalidOutputDatum(_)))); + } + + #[test] + fn test_optional_output_ok() { + let mut ast = crate::parsing::parse_string( + r#" + party Alice; + + tx test() { + output ? my_output { + to: Alice, + amount: Ada(0), + } + } + "#, + ) + .unwrap(); + + let report = analyze(&mut ast); + assert!(report.errors.is_empty()); + } } From 15ceff83c403157120940c7d825a23dbbaee5402 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Fri, 3 Oct 2025 16:24:56 -0300 Subject: [PATCH 05/18] feat: add 'optional' field to various AST and TIR examples --- examples/asteria.ast | 2 + examples/asteria.move_ship.tir | 6 +- examples/burn.ast | 1 + examples/burn.burn_stuff.tir | 3 +- examples/cardano_witness.ast | 2 + ...ardano_witness.mint_from_native_script.tir | 3 +- examples/cardano_witness.mint_from_plutus.tir | 3 +- examples/disordered.ast | 2 + examples/donation.ast | 1 + examples/donation.mint_from_plutus.tir | 3 +- examples/env_vars.ast | 1 + examples/env_vars.mint_from_env.tir | 3 +- examples/faucet.ast | 1 + examples/faucet.claim_with_password.tir | 3 +- examples/input_datum.ast | 1 + examples/input_datum.increase_counter.tir | 3 +- examples/lang_tour.ast | 1 + examples/lang_tour.my_tx.tir | 3 +- examples/list_concat.ast | 1 + examples/list_concat.concat_list.tir | 3 +- examples/local_vars.ast | 1 + examples/local_vars.mint_from_local.tir | 3 +- examples/map.ast | 2 + examples/map.transfer.tir | 6 +- examples/min_utxo.transfer_min.tir | 6 +- examples/reference_script.ast | 2 + examples/reference_script.publish_native.tir | 25 ++++---- examples/reference_script.publish_plutus.tir | 57 ++++++++++--------- examples/swap.ast | 2 + examples/swap.swap.tir | 6 +- examples/transfer.ast | 2 + examples/transfer.transfer.tir | 6 +- examples/vesting.ast | 3 + examples/vesting.lock.tir | 6 +- examples/vesting.unlock.tir | 3 +- examples/withdrawal.ast | 2 + examples/withdrawal.transfer.tir | 6 +- 37 files changed, 119 insertions(+), 65 deletions(-) diff --git a/examples/asteria.ast b/examples/asteria.ast index c695a4ed..0b8015d8 100644 --- a/examples/asteria.ast +++ b/examples/asteria.ast @@ -344,6 +344,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { @@ -586,6 +587,7 @@ }, { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/asteria.move_ship.tir b/examples/asteria.move_ship.tir index 7bdfb4d6..11f7b99e 100644 --- a/examples/asteria.move_ship.tir +++ b/examples/asteria.move_ship.tir @@ -1167,7 +1167,8 @@ } ] } - } + }, + "optional": false }, { "address": { @@ -1284,7 +1285,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/burn.ast b/examples/burn.ast index 3c6d42f8..22bc9bd6 100644 --- a/examples/burn.ast +++ b/examples/burn.ast @@ -153,6 +153,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/burn.burn_stuff.tir b/examples/burn.burn_stuff.tir index 4f2544c3..3a8a4e89 100644 --- a/examples/burn.burn_stuff.tir +++ b/examples/burn.burn_stuff.tir @@ -243,7 +243,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/cardano_witness.ast b/examples/cardano_witness.ast index dd73361a..529d1b56 100644 --- a/examples/cardano_witness.ast +++ b/examples/cardano_witness.ast @@ -134,6 +134,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { @@ -455,6 +456,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/cardano_witness.mint_from_native_script.tir b/examples/cardano_witness.mint_from_native_script.tir index 0c8e2d99..95e1c83e 100644 --- a/examples/cardano_witness.mint_from_native_script.tir +++ b/examples/cardano_witness.mint_from_native_script.tir @@ -133,7 +133,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/cardano_witness.mint_from_plutus.tir b/examples/cardano_witness.mint_from_plutus.tir index 7f6c09cc..c0f4530f 100644 --- a/examples/cardano_witness.mint_from_plutus.tir +++ b/examples/cardano_witness.mint_from_plutus.tir @@ -133,7 +133,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/disordered.ast b/examples/disordered.ast index 28f28f79..d0fd23ac 100644 --- a/examples/disordered.ast +++ b/examples/disordered.ast @@ -108,6 +108,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { @@ -159,6 +160,7 @@ }, { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/donation.ast b/examples/donation.ast index 30fa67f5..10eea5bd 100644 --- a/examples/donation.ast +++ b/examples/donation.ast @@ -146,6 +146,7 @@ "end": 200 } }, + "optional": false, "fields": [ { "To": { diff --git a/examples/donation.mint_from_plutus.tir b/examples/donation.mint_from_plutus.tir index 715bc569..4aacfdf6 100644 --- a/examples/donation.mint_from_plutus.tir +++ b/examples/donation.mint_from_plutus.tir @@ -197,7 +197,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/env_vars.ast b/examples/env_vars.ast index 442382b0..114a70cb 100644 --- a/examples/env_vars.ast +++ b/examples/env_vars.ast @@ -117,6 +117,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/env_vars.mint_from_env.tir b/examples/env_vars.mint_from_env.tir index 26cf4e02..b2b72adb 100644 --- a/examples/env_vars.mint_from_env.tir +++ b/examples/env_vars.mint_from_env.tir @@ -71,7 +71,8 @@ } } ] - } + }, + "optional": false } ], "validity": null, diff --git a/examples/faucet.ast b/examples/faucet.ast index 91b135cb..7c69303b 100644 --- a/examples/faucet.ast +++ b/examples/faucet.ast @@ -83,6 +83,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/faucet.claim_with_password.tir b/examples/faucet.claim_with_password.tir index e6f64c06..d1f3874b 100644 --- a/examples/faucet.claim_with_password.tir +++ b/examples/faucet.claim_with_password.tir @@ -133,7 +133,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/input_datum.ast b/examples/input_datum.ast index 485c5476..c4711f58 100644 --- a/examples/input_datum.ast +++ b/examples/input_datum.ast @@ -72,6 +72,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/input_datum.increase_counter.tir b/examples/input_datum.increase_counter.tir index 16ec73a3..f46c0176 100644 --- a/examples/input_datum.increase_counter.tir +++ b/examples/input_datum.increase_counter.tir @@ -165,7 +165,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/lang_tour.ast b/examples/lang_tour.ast index 9ad96014..4f4f55d8 100644 --- a/examples/lang_tour.ast +++ b/examples/lang_tour.ast @@ -287,6 +287,7 @@ "end": 1008 } }, + "optional": false, "fields": [ { "To": { diff --git a/examples/lang_tour.my_tx.tir b/examples/lang_tour.my_tx.tir index c1ed8b7e..f53d623c 100644 --- a/examples/lang_tour.my_tx.tir +++ b/examples/lang_tour.my_tx.tir @@ -500,7 +500,8 @@ } ] } - } + }, + "optional": false } ], "validity": { diff --git a/examples/list_concat.ast b/examples/list_concat.ast index 97e1cf85..e9b85187 100644 --- a/examples/list_concat.ast +++ b/examples/list_concat.ast @@ -60,6 +60,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/list_concat.concat_list.tir b/examples/list_concat.concat_list.tir index 8c4e3548..2c78d058 100644 --- a/examples/list_concat.concat_list.tir +++ b/examples/list_concat.concat_list.tir @@ -127,7 +127,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/local_vars.ast b/examples/local_vars.ast index 318a91d9..555a3a80 100644 --- a/examples/local_vars.ast +++ b/examples/local_vars.ast @@ -145,6 +145,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/local_vars.mint_from_local.tir b/examples/local_vars.mint_from_local.tir index 1ba1dbb2..03853938 100644 --- a/examples/local_vars.mint_from_local.tir +++ b/examples/local_vars.mint_from_local.tir @@ -76,7 +76,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/map.ast b/examples/map.ast index c3a6da46..c265fb57 100644 --- a/examples/map.ast +++ b/examples/map.ast @@ -89,6 +89,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { @@ -140,6 +141,7 @@ }, { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/map.transfer.tir b/examples/map.transfer.tir index 75c2a5ed..99a17871 100644 --- a/examples/map.transfer.tir +++ b/examples/map.transfer.tir @@ -71,7 +71,8 @@ } } ] - } + }, + "optional": false }, { "address": { @@ -189,7 +190,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/min_utxo.transfer_min.tir b/examples/min_utxo.transfer_min.tir index 3bd9cca0..8e5d4184 100644 --- a/examples/min_utxo.transfer_min.tir +++ b/examples/min_utxo.transfer_min.tir @@ -75,7 +75,8 @@ "Number": 0 } } - } + }, + "optional": false }, { "address": { @@ -161,7 +162,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/reference_script.ast b/examples/reference_script.ast index b9793b0d..18edde78 100644 --- a/examples/reference_script.ast +++ b/examples/reference_script.ast @@ -89,6 +89,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { @@ -350,6 +351,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/reference_script.publish_native.tir b/examples/reference_script.publish_native.tir index c90fbf85..b5976489 100644 --- a/examples/reference_script.publish_native.tir +++ b/examples/reference_script.publish_native.tir @@ -126,7 +126,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, @@ -136,14 +137,6 @@ { "name": "cardano_publish", "data": { - "to": { - "EvalParam": { - "ExpectValue": [ - "receiver", - "Address" - ] - } - }, "amount": { "Assets": [ { @@ -160,6 +153,17 @@ } ] }, + "to": { + "EvalParam": { + "ExpectValue": [ + "receiver", + "Address" + ] + } + }, + "version": { + "Number": 0 + }, "script": { "Bytes": [ 130, @@ -169,9 +173,6 @@ 4, 0 ] - }, - "version": { - "Number": 0 } } } diff --git a/examples/reference_script.publish_plutus.tir b/examples/reference_script.publish_plutus.tir index fab2edf1..e98d6a11 100644 --- a/examples/reference_script.publish_plutus.tir +++ b/examples/reference_script.publish_plutus.tir @@ -126,7 +126,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, @@ -136,33 +137,6 @@ { "name": "cardano_publish", "data": { - "version": { - "Number": 3 - }, - "to": { - "EvalParam": { - "ExpectValue": [ - "receiver", - "Address" - ] - } - }, - "amount": { - "Assets": [ - { - "policy": "None", - "asset_name": "None", - "amount": { - "EvalParam": { - "ExpectValue": [ - "quantity", - "Int" - ] - } - } - } - ] - }, "script": { "Bytes": [ 81, @@ -184,6 +158,33 @@ 174, 105 ] + }, + "version": { + "Number": 3 + }, + "amount": { + "Assets": [ + { + "policy": "None", + "asset_name": "None", + "amount": { + "EvalParam": { + "ExpectValue": [ + "quantity", + "Int" + ] + } + } + } + ] + }, + "to": { + "EvalParam": { + "ExpectValue": [ + "receiver", + "Address" + ] + } } } } diff --git a/examples/swap.ast b/examples/swap.ast index 188ea2ed..9aca2381 100644 --- a/examples/swap.ast +++ b/examples/swap.ast @@ -224,6 +224,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { @@ -468,6 +469,7 @@ }, { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/swap.swap.tir b/examples/swap.swap.tir index e89e2c6a..c9a20a39 100644 --- a/examples/swap.swap.tir +++ b/examples/swap.swap.tir @@ -248,7 +248,8 @@ } } } - } + }, + "optional": false }, { "address": { @@ -337,7 +338,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/transfer.ast b/examples/transfer.ast index 6f5a7b5d..2e47681a 100644 --- a/examples/transfer.ast +++ b/examples/transfer.ast @@ -89,6 +89,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { @@ -140,6 +141,7 @@ }, { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/transfer.transfer.tir b/examples/transfer.transfer.tir index ed2fc722..d6750767 100644 --- a/examples/transfer.transfer.tir +++ b/examples/transfer.transfer.tir @@ -71,7 +71,8 @@ } } ] - } + }, + "optional": false }, { "address": { @@ -153,7 +154,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/vesting.ast b/examples/vesting.ast index 4c1ccf87..b211e86a 100644 --- a/examples/vesting.ast +++ b/examples/vesting.ast @@ -107,6 +107,7 @@ "end": 336 } }, + "optional": false, "fields": [ { "To": { @@ -270,6 +271,7 @@ }, { "name": null, + "optional": false, "fields": [ { "To": { @@ -487,6 +489,7 @@ "end": 924 } }, + "optional": false, "fields": [ { "To": { diff --git a/examples/vesting.lock.tir b/examples/vesting.lock.tir index fcec6190..5d9609db 100644 --- a/examples/vesting.lock.tir +++ b/examples/vesting.lock.tir @@ -129,7 +129,8 @@ } } ] - } + }, + "optional": false }, { "address": { @@ -211,7 +212,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/vesting.unlock.tir b/examples/vesting.unlock.tir index e24dfec2..23f07fd2 100644 --- a/examples/vesting.unlock.tir +++ b/examples/vesting.unlock.tir @@ -209,7 +209,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, diff --git a/examples/withdrawal.ast b/examples/withdrawal.ast index 62f2d34f..4a1896c9 100644 --- a/examples/withdrawal.ast +++ b/examples/withdrawal.ast @@ -89,6 +89,7 @@ "outputs": [ { "name": null, + "optional": false, "fields": [ { "To": { @@ -140,6 +141,7 @@ }, { "name": null, + "optional": false, "fields": [ { "To": { diff --git a/examples/withdrawal.transfer.tir b/examples/withdrawal.transfer.tir index be726c97..ec19f1e1 100644 --- a/examples/withdrawal.transfer.tir +++ b/examples/withdrawal.transfer.tir @@ -71,7 +71,8 @@ } } ] - } + }, + "optional": false }, { "address": { @@ -153,7 +154,8 @@ } ] } - } + }, + "optional": false } ], "validity": null, From 2648c8a2c4f48f3d4f16110113130612711915dc Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Tue, 7 Oct 2025 13:26:17 -0300 Subject: [PATCH 06/18] feat(analyzing): enforce AnyAsset type for OutputBlockField amount and add tests --- crates/tx3-lang/src/analyzing.rs | 58 +++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index 03bc326f..39abd592 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -823,7 +823,11 @@ impl Analyzable for OutputBlockField { fn analyze(&mut self, parent: Option>) -> AnalyzeReport { match self { OutputBlockField::To(x) => x.analyze(parent), - OutputBlockField::Amount(x) => x.analyze(parent), + OutputBlockField::Amount(x) => { + let expr_report = x.analyze(parent); + let type_report = AnalyzeReport::expect_data_expr_type(x, &Type::AnyAsset); + expr_report + type_report + } OutputBlockField::Datum(x) => x.analyze(parent), } } @@ -1268,4 +1272,56 @@ mod tests { let result = analyze(&mut ast); assert!(!result.errors.is_empty()); } + + #[test] + fn test_output_amount_must_be_any_asset_type() { + let mut ast = crate::parsing::parse_string( + r#" + party Alice; + tx test() { + output my_output { + to: Alice, + amount: 123, + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + assert!(!result.errors.is_empty()); + + assert_eq!( + result.errors[0], + Error::InvalidTargetType(InvalidTargetTypeError { + expected: "AnyAsset".to_string(), + got: "Int".to_string(), + src: None, + span: Span::DUMMY, + }) + ); + } + + #[test] + fn test_output_amount_accepts_any_asset_expressions() { + let mut ast = crate::parsing::parse_string( + r#" + party Alice; + tx test(quantity: Int) { + output { + to: Alice, + amount: AnyAsset(0x123, 0x456, 100), + } + output { + to: Alice, + amount: Ada(quantity), + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + assert!(result.errors.is_empty()); + } } From 724c43344ff85ed3554a90f94d629715fbcf948f Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Tue, 14 Oct 2025 15:31:10 -0300 Subject: [PATCH 07/18] update tests --- crates/tx3-cardano/src/compile/mod.rs | 1 - examples/cardano_witness.mint_from_plutus.tir | 6 +- examples/lang_tour.ast | 898 +++++++++++++----- examples/lang_tour.my_tx.tir | 191 +++- examples/reference_script.publish_native.tir | 37 +- examples/reference_script.publish_plutus.tir | 44 +- 6 files changed, 889 insertions(+), 288 deletions(-) diff --git a/crates/tx3-cardano/src/compile/mod.rs b/crates/tx3-cardano/src/compile/mod.rs index f2d90397..932c1bfb 100644 --- a/crates/tx3-cardano/src/compile/mod.rs +++ b/crates/tx3-cardano/src/compile/mod.rs @@ -288,7 +288,6 @@ fn compile_outputs( .into_iter() .fold(0i128, |acc, n| acc.saturating_add(n)); - // TODO - what happens when the value is between 0 and min_utxo? if out.optional && total == 0 { continue; } 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/lang_tour.ast b/examples/lang_tour.ast index 2d295f16..882ba085 100644 --- a/examples/lang_tour.ast +++ b/examples/lang_tour.ast @@ -18,12 +18,32 @@ "start": 28, "end": 42 } + }, + { + "name": "field_c", + "type": "Bool", + "span": { + "dummy": false, + "start": 48, + "end": 61 + } + }, + { + "name": "field_d", + "type": { + "List": "Bytes" + }, + "span": { + "dummy": false, + "start": 67, + "end": 87 + } } ], "span": { "dummy": false, "start": 0, - "end": 45 + "end": 90 } }, "txs": [ @@ -32,8 +52,8 @@ "value": "my_tx", "span": { "dummy": false, - "start": 466, - "end": 471 + "start": 540, + "end": 545 } }, "parameters": { @@ -43,8 +63,8 @@ "value": "quantity", "span": { "dummy": false, - "start": 477, - "end": 485 + "start": 551, + "end": 559 } }, "type": "Int" @@ -54,8 +74,8 @@ "value": "validUntil", "span": { "dummy": false, - "start": 496, - "end": 506 + "start": 570, + "end": 580 } }, "type": "Int" @@ -65,8 +85,8 @@ "value": "metadata", "span": { "dummy": false, - "start": 517, - "end": 525 + "start": 591, + "end": 599 } }, "type": "Bytes" @@ -74,12 +94,88 @@ ], "span": { "dummy": false, - "start": 471, - "end": 535 + "start": 545, + "end": 609 + } + }, + "locals": { + "assigns": [ + { + "name": { + "value": "local_var", + "span": { + "dummy": false, + "start": 1914, + "end": 1923 + } + }, + "value": { + "ConcatOp": { + "lhs": { + "String": { + "value": "Lang", + "span": { + "dummy": false, + "start": 1932, + "end": 1938 + } + } + }, + "rhs": { + "String": { + "value": "Tour", + "span": { + "dummy": false, + "start": 1940, + "end": 1946 + } + } + }, + "span": { + "dummy": false, + "start": 1925, + "end": 1947 + } + } + }, + "span": { + "dummy": false, + "start": 1914, + "end": 1947 + } + } + ], + "span": { + "dummy": false, + "start": 1897, + "end": 1954 } }, - "locals": null, - "references": [], + "references": [ + { + "name": "ref_block", + "ref": { + "UtxoRef": { + "txid": [ + 171, + 205, + 239 + ], + "index": 2, + "span": { + "dummy": false, + "start": 1220, + "end": 1230 + } + } + }, + "span": { + "dummy": false, + "start": 1185, + "end": 1237 + } + } + ], "inputs": [ { "name": "source", @@ -91,8 +187,8 @@ "value": "MyParty", "span": { "dummy": false, - "start": 571, - "end": 578 + "start": 645, + "end": 652 } } } @@ -118,8 +214,8 @@ "value": "Ada", "span": { "dummy": false, - "start": 628, - "end": 631 + "start": 702, + "end": 705 } }, "amount": { @@ -127,15 +223,15 @@ "value": "quantity", "span": { "dummy": false, - "start": 632, - "end": 640 + "start": 706, + "end": 714 } } }, "span": { "dummy": false, - "start": 628, - "end": 641 + "start": 702, + "end": 715 } } }, @@ -144,15 +240,15 @@ "value": "named_output", "span": { "dummy": false, - "start": 653, - "end": 665 + "start": 727, + "end": 739 } } }, "span": { "dummy": false, - "start": 642, - "end": 643 + "start": 716, + "end": 717 } } } @@ -164,8 +260,8 @@ "value": "MyVariant", "span": { "dummy": false, - "start": 686, - "end": 695 + "start": 760, + "end": 769 } }, "case": { @@ -173,8 +269,8 @@ "value": "Case1", "span": { "dummy": false, - "start": 697, - "end": 702 + "start": 771, + "end": 776 } }, "fields": [ @@ -183,8 +279,8 @@ "value": "field1", "span": { "dummy": false, - "start": 717, - "end": 723 + "start": 791, + "end": 797 } }, "value": { @@ -192,15 +288,15 @@ "value": "field_a", "span": { "dummy": false, - "start": 725, - "end": 732 + "start": 799, + "end": 806 } } }, "span": { "dummy": false, - "start": 717, - "end": 732 + "start": 791, + "end": 806 } }, { @@ -208,8 +304,8 @@ "value": "field2", "span": { "dummy": false, - "start": 746, - "end": 752 + "start": 820, + "end": 826 } }, "value": { @@ -217,15 +313,15 @@ "value": "AFAFAF", "span": { "dummy": false, - "start": 754, - "end": 762 + "start": 828, + "end": 836 } } }, "span": { "dummy": false, - "start": 746, - "end": 762 + "start": 820, + "end": 836 } }, { @@ -233,8 +329,8 @@ "value": "field3", "span": { "dummy": false, - "start": 776, - "end": 782 + "start": 850, + "end": 856 } }, "value": { @@ -242,29 +338,29 @@ "value": "quantity", "span": { "dummy": false, - "start": 784, - "end": 792 + "start": 858, + "end": 866 } } }, "span": { "dummy": false, - "start": 776, - "end": 792 + "start": 850, + "end": 866 } } ], "spread": null, "span": { "dummy": false, - "start": 695, - "end": 803 + "start": 769, + "end": 877 } }, "span": { "dummy": false, - "start": 686, - "end": 803 + "start": 760, + "end": 877 } } } @@ -272,8 +368,8 @@ ], "span": { "dummy": false, - "start": 542, - "end": 810 + "start": 616, + "end": 884 } } ], @@ -283,8 +379,8 @@ "value": "named_output", "span": { "dummy": false, - "start": 996, - "end": 1008 + "start": 1250, + "end": 1262 } }, "optional": false, @@ -295,8 +391,8 @@ "value": "MyParty", "span": { "dummy": false, - "start": 1023, - "end": 1030 + "start": 1277, + "end": 1284 } } } @@ -308,8 +404,8 @@ "value": "MyRecord", "span": { "dummy": false, - "start": 1047, - "end": 1055 + "start": 1301, + "end": 1309 } }, "case": { @@ -327,8 +423,8 @@ "value": "field1", "span": { "dummy": false, - "start": 1070, - "end": 1076 + "start": 1324, + "end": 1330 } }, "value": { @@ -336,15 +432,15 @@ "value": "quantity", "span": { "dummy": false, - "start": 1078, - "end": 1086 + "start": 1332, + "end": 1340 } } }, "span": { "dummy": false, - "start": 1070, - "end": 1086 + "start": 1324, + "end": 1340 } }, { @@ -352,8 +448,8 @@ "value": "field2", "span": { "dummy": false, - "start": 1100, - "end": 1106 + "start": 1354, + "end": 1360 } }, "value": { @@ -368,8 +464,8 @@ }, "span": { "dummy": false, - "start": 1112, - "end": 1113 + "start": 1366, + "end": 1367 } } }, @@ -383,22 +479,22 @@ }, "span": { "dummy": false, - "start": 1123, - "end": 1124 + "start": 1377, + "end": 1378 } } }, "span": { "dummy": false, - "start": 1118, - "end": 1119 + "start": 1372, + "end": 1373 } } }, "span": { "dummy": false, - "start": 1100, - "end": 1127 + "start": 1354, + "end": 1381 } }, { @@ -406,8 +502,8 @@ "value": "field4", "span": { "dummy": false, - "start": 1141, - "end": 1147 + "start": 1395, + "end": 1401 } }, "value": { @@ -429,8 +525,8 @@ "value": "source", "span": { "dummy": false, - "start": 1159, - "end": 1165 + "start": 1413, + "end": 1419 } } }, @@ -439,30 +535,96 @@ "value": "field1", "span": { "dummy": false, - "start": 1166, - "end": 1172 + "start": 1420, + "end": 1426 } } }, "span": { "dummy": false, - "start": 1165, - "end": 1172 + "start": 1419, + "end": 1426 + } + } + } + ], + "span": { + "dummy": false, + "start": 1403, + "end": 1427 + } + } + }, + "span": { + "dummy": false, + "start": 1395, + "end": 1427 + } + }, + { + "name": { + "value": "field5", + "span": { + "dummy": false, + "start": 1441, + "end": 1447 + } + }, + "value": { + "MapConstructor": { + "fields": [ + { + "key": { + "Number": 1 + }, + "value": { + "String": { + "value": "Value1", + "span": { + "dummy": false, + "start": 1453, + "end": 1461 + } } + }, + "span": { + "dummy": false, + "start": 1450, + "end": 1461 + } + }, + { + "key": { + "Number": 2 + }, + "value": { + "String": { + "value": "Value2", + "span": { + "dummy": false, + "start": 1466, + "end": 1474 + } + } + }, + "span": { + "dummy": false, + "start": 1463, + "end": 1474 } } ], "span": { "dummy": false, - "start": 1149, - "end": 1173 + "start": 1449, + "end": 1476 } } }, "span": { "dummy": false, - "start": 1141, - "end": 1173 + "start": 1441, + "end": 1476 } } ], @@ -471,21 +633,21 @@ "value": "source", "span": { "dummy": false, - "start": 1190, - "end": 1196 + "start": 1493, + "end": 1499 } } }, "span": { "dummy": false, - "start": 1056, - "end": 1206 + "start": 1310, + "end": 1509 } }, "span": { "dummy": false, - "start": 1047, - "end": 1206 + "start": 1301, + "end": 1509 } } } @@ -504,8 +666,8 @@ "value": "source", "span": { "dummy": false, - "start": 1233, - "end": 1239 + "start": 1536, + "end": 1542 } } }, @@ -514,15 +676,15 @@ "value": "field3", "span": { "dummy": false, - "start": 1240, - "end": 1246 + "start": 1543, + "end": 1549 } } }, "span": { "dummy": false, - "start": 1239, - "end": 1246 + "start": 1542, + "end": 1549 } } }, @@ -533,8 +695,8 @@ "value": "source", "span": { "dummy": false, - "start": 1248, - "end": 1254 + "start": 1551, + "end": 1557 } } }, @@ -543,15 +705,15 @@ "value": "field2", "span": { "dummy": false, - "start": 1255, - "end": 1261 + "start": 1558, + "end": 1564 } } }, "span": { "dummy": false, - "start": 1254, - "end": 1261 + "start": 1557, + "end": 1564 } } }, @@ -562,8 +724,8 @@ "value": "source", "span": { "dummy": false, - "start": 1263, - "end": 1269 + "start": 1566, + "end": 1572 } } }, @@ -572,22 +734,22 @@ "value": "field1", "span": { "dummy": false, - "start": 1270, - "end": 1276 + "start": 1573, + "end": 1579 } } }, "span": { "dummy": false, - "start": 1269, - "end": 1276 + "start": 1572, + "end": 1579 } } }, "span": { "dummy": false, - "start": 1224, - "end": 1277 + "start": 1527, + "end": 1580 } } }, @@ -597,8 +759,8 @@ "value": "Ada", "span": { "dummy": false, - "start": 1280, - "end": 1283 + "start": 1583, + "end": 1586 } }, "amount": { @@ -606,15 +768,15 @@ }, "span": { "dummy": false, - "start": 1280, - "end": 1287 + "start": 1583, + "end": 1590 } } }, "span": { "dummy": false, - "start": 1278, - "end": 1279 + "start": 1581, + "end": 1582 } } }, @@ -623,15 +785,15 @@ "value": "named_output", "span": { "dummy": false, - "start": 1299, - "end": 1311 + "start": 1602, + "end": 1614 } } }, "span": { "dummy": false, - "start": 1288, - "end": 1289 + "start": 1591, + "end": 1592 } } } @@ -639,17 +801,15 @@ ], "span": { "dummy": false, - "start": 989, - "end": 1319 + "start": 1243, + "end": 1622 } } ], "validity": { "fields": [ { - "SinceSlot": { - "Number": 1735700400000 - } + "SinceSlot": "ComputeTipSlot" }, { "UntilSlot": { @@ -657,8 +817,8 @@ "value": "validUntil", "span": { "dummy": false, - "start": 1497, - "end": 1507 + "start": 1797, + "end": 1807 } } } @@ -666,8 +826,8 @@ ], "span": { "dummy": false, - "start": 1431, - "end": 1514 + "start": 1734, + "end": 1814 } }, "mints": [ @@ -680,8 +840,8 @@ "value": "StaticAsset", "span": { "dummy": false, - "start": 839, - "end": 850 + "start": 913, + "end": 924 } }, "amount": { @@ -689,8 +849,8 @@ }, "span": { "dummy": false, - "start": 839, - "end": 855 + "start": 913, + "end": 929 } } } @@ -701,8 +861,8 @@ ], "span": { "dummy": false, - "start": 816, - "end": 884 + "start": 890, + "end": 958 } }, { @@ -715,8 +875,8 @@ "value": "AB11223344", "span": { "dummy": false, - "start": 922, - "end": 934 + "start": 996, + "end": 1008 } } }, @@ -725,8 +885,8 @@ "value": "OTHER_TOKEN", "span": { "dummy": false, - "start": 936, - "end": 949 + "start": 1010, + "end": 1023 } } }, @@ -735,8 +895,8 @@ }, "span": { "dummy": false, - "start": 913, - "end": 954 + "start": 987, + "end": 1028 } } } @@ -747,12 +907,47 @@ ], "span": { "dummy": false, - "start": 890, - "end": 983 + "start": 964, + "end": 1057 + } + } + ], + "burns": [ + { + "fields": [ + { + "Amount": { + "StaticAssetConstructor": { + "type": { + "value": "StaticAsset", + "span": { + "dummy": false, + "start": 1086, + "end": 1097 + } + }, + "amount": { + "Number": 50 + }, + "span": { + "dummy": false, + "start": 1086, + "end": 1101 + } + } + } + }, + { + "Redeemer": "Unit" + } + ], + "span": { + "dummy": false, + "start": 1063, + "end": 1130 } } ], - "burns": [], "signers": { "signers": [ { @@ -760,8 +955,8 @@ "value": "MyParty", "span": { "dummy": false, - "start": 1343, - "end": 1350 + "start": 1646, + "end": 1653 } } }, @@ -770,25 +965,207 @@ "value": "0F5B22E57FEEB5B4FD1D501B007A427C56A76884D4978FAFEF979D9C", "span": { "dummy": false, - "start": 1360, - "end": 1418 + "start": 1663, + "end": 1721 } } } ], "span": { "dummy": false, - "start": 1325, - "end": 1425 + "start": 1628, + "end": 1728 } }, - "adhoc": [], + "adhoc": [ + { + "Cardano": { + "VoteDelegationCertificate": { + "drep": { + "HexString": { + "value": "12345678", + "span": { + "dummy": false, + "start": 2013, + "end": 2023 + } + } + }, + "stake": { + "HexString": { + "value": "87654321", + "span": { + "dummy": false, + "start": 2040, + "end": 2050 + } + } + }, + "span": { + "dummy": false, + "start": 1969, + "end": 2057 + } + } + } + }, + { + "Cardano": { + "Withdrawal": { + "fields": [ + { + "From": { + "Identifier": { + "value": "MyParty", + "span": { + "dummy": false, + "start": 2099, + "end": 2106 + } + } + } + }, + { + "Amount": { + "Number": 100 + } + }, + { + "Redeemer": "Unit" + } + ], + "span": { + "dummy": false, + "start": 2072, + "end": 2156 + } + } + } + }, + { + "Cardano": { + "PlutusWitness": { + "fields": [ + { + "Version": [ + { + "Number": 2 + }, + { + "dummy": false, + "start": 2196, + "end": 2206 + } + ] + }, + { + "Script": [ + { + "HexString": { + "value": "ABCDEF1234", + "span": { + "dummy": false, + "start": 2224, + "end": 2236 + } + } + }, + { + "dummy": false, + "start": 2216, + "end": 2236 + } + ] + } + ], + "span": { + "dummy": false, + "start": 2171, + "end": 2243 + } + } + } + }, + { + "Cardano": { + "NativeWitness": { + "fields": [ + { + "Script": [ + { + "HexString": { + "value": "12345678", + "span": { + "dummy": false, + "start": 2291, + "end": 2301 + } + } + }, + { + "dummy": false, + "start": 2283, + "end": 2301 + } + ] + } + ], + "span": { + "dummy": false, + "start": 2258, + "end": 2308 + } + } + } + }, + { + "Cardano": { + "TreasuryDonation": { + "coin": { + "Number": 500 + }, + "span": { + "dummy": false, + "start": 2323, + "end": 2367 + } + } + } + } + ], "span": { "dummy": false, - "start": 463, - "end": 1559 + "start": 537, + "end": 2369 }, - "collateral": [], + "collateral": [ + { + "fields": [ + { + "Ref": { + "UtxoRef": { + "txid": [ + 171, + 205, + 239 + ], + "index": 1, + "span": { + "dummy": false, + "start": 1162, + "end": 1172 + } + } + } + } + ], + "span": { + "dummy": false, + "start": 1136, + "end": 1179 + } + } + ], "metadata": { "fields": [ { @@ -800,22 +1177,42 @@ "value": "metadata", "span": { "dummy": false, - "start": 1542, - "end": 1550 + "start": 1842, + "end": 1850 } } }, "span": { "dummy": false, - "start": 1539, - "end": 1550 + "start": 1839, + "end": 1850 + } + }, + { + "key": { + "Number": 2 + }, + "value": { + "String": { + "value": "Additional Metadata", + "span": { + "dummy": false, + "start": 1863, + "end": 1884 + } + } + }, + "span": { + "dummy": false, + "start": 1860, + "end": 1884 } } ], "span": { "dummy": false, - "start": 1520, - "end": 1557 + "start": 1820, + "end": 1891 } } } @@ -826,8 +1223,8 @@ "value": "MyRecord", "span": { "dummy": false, - "start": 68, - "end": 76 + "start": 113, + "end": 121 } }, "cases": [ @@ -846,15 +1243,15 @@ "value": "field1", "span": { "dummy": false, - "start": 83, - "end": 89 + "start": 128, + "end": 134 } }, "type": "Int", "span": { "dummy": false, - "start": 83, - "end": 94 + "start": 128, + "end": 139 } }, { @@ -862,15 +1259,15 @@ "value": "field2", "span": { "dummy": false, - "start": 100, - "end": 106 + "start": 145, + "end": 151 } }, "type": "Bytes", "span": { "dummy": false, - "start": 100, - "end": 113 + "start": 145, + "end": 158 } }, { @@ -878,15 +1275,15 @@ "value": "field3", "span": { "dummy": false, - "start": 119, - "end": 125 + "start": 164, + "end": 170 } }, "type": "Bytes", "span": { "dummy": false, - "start": 119, - "end": 132 + "start": 164, + "end": 177 } }, { @@ -894,8 +1291,8 @@ "value": "field4", "span": { "dummy": false, - "start": 138, - "end": 144 + "start": 183, + "end": 189 } }, "type": { @@ -903,22 +1300,43 @@ }, "span": { "dummy": false, - "start": 138, - "end": 155 + "start": 183, + "end": 200 + } + }, + { + "name": { + "value": "field5", + "span": { + "dummy": false, + "start": 206, + "end": 212 + } + }, + "type": { + "Map": [ + "Int", + "Bytes" + ] + }, + "span": { + "dummy": false, + "start": 206, + "end": 229 } } ], "span": { "dummy": false, - "start": 63, - "end": 158 + "start": 108, + "end": 232 } } ], "span": { "dummy": false, - "start": 63, - "end": 158 + "start": 108, + "end": 232 } }, { @@ -926,8 +1344,8 @@ "value": "MyVariant", "span": { "dummy": false, - "start": 165, - "end": 174 + "start": 239, + "end": 248 } }, "cases": [ @@ -936,8 +1354,8 @@ "value": "Case1", "span": { "dummy": false, - "start": 181, - "end": 186 + "start": 255, + "end": 260 } }, "fields": [ @@ -946,15 +1364,15 @@ "value": "field1", "span": { "dummy": false, - "start": 197, - "end": 203 + "start": 271, + "end": 277 } }, "type": "Int", "span": { "dummy": false, - "start": 197, - "end": 208 + "start": 271, + "end": 282 } }, { @@ -962,15 +1380,15 @@ "value": "field2", "span": { "dummy": false, - "start": 218, - "end": 224 + "start": 292, + "end": 298 } }, "type": "Bytes", "span": { "dummy": false, - "start": 218, - "end": 231 + "start": 292, + "end": 305 } }, { @@ -978,22 +1396,22 @@ "value": "field3", "span": { "dummy": false, - "start": 241, - "end": 247 + "start": 315, + "end": 321 } }, "type": "Int", "span": { "dummy": false, - "start": 241, - "end": 252 + "start": 315, + "end": 326 } } ], "span": { "dummy": false, - "start": 181, - "end": 259 + "start": 255, + "end": 333 } }, { @@ -1001,22 +1419,22 @@ "value": "Case2", "span": { "dummy": false, - "start": 265, - "end": 270 + "start": 339, + "end": 344 } }, "fields": [], "span": { "dummy": false, - "start": 265, - "end": 270 + "start": 339, + "end": 344 } } ], "span": { "dummy": false, - "start": 160, - "end": 273 + "start": 234, + "end": 347 } } ], @@ -1027,8 +1445,8 @@ "value": "StaticAsset", "span": { "dummy": false, - "start": 320, - "end": 331 + "start": 394, + "end": 405 } }, "policy": { @@ -1036,8 +1454,8 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 334, - "end": 346 + "start": 408, + "end": 420 } } }, @@ -1046,15 +1464,15 @@ "value": "MYTOKEN", "span": { "dummy": false, - "start": 347, - "end": 356 + "start": 421, + "end": 430 } } }, "span": { "dummy": false, - "start": 314, - "end": 357 + "start": 388, + "end": 431 } } ], @@ -1064,14 +1482,14 @@ "value": "MyParty", "span": { "dummy": false, - "start": 53, - "end": 60 + "start": 98, + "end": 105 } }, "span": { "dummy": false, - "start": 47, - "end": 61 + "start": 92, + "end": 106 } } ], @@ -1081,8 +1499,8 @@ "value": "OnlyHashPolicy", "span": { "dummy": false, - "start": 282, - "end": 296 + "start": 356, + "end": 370 } }, "value": { @@ -1090,15 +1508,15 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 299, - "end": 311 + "start": 373, + "end": 385 } } }, "span": { "dummy": false, - "start": 275, - "end": 312 + "start": 349, + "end": 386 } }, { @@ -1106,8 +1524,8 @@ "value": "FullyDefinedPolicy", "span": { "dummy": false, - "start": 366, - "end": 384 + "start": 440, + "end": 458 } }, "value": { @@ -1119,8 +1537,8 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 397, - "end": 409 + "start": 471, + "end": 483 } } } @@ -1131,8 +1549,8 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 423, - "end": 435 + "start": 497, + "end": 509 } } } @@ -1143,8 +1561,8 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 446, - "end": 458 + "start": 520, + "end": 532 } } } @@ -1152,21 +1570,21 @@ ], "span": { "dummy": false, - "start": 385, - "end": 461 + "start": 459, + "end": 535 } } }, "span": { "dummy": false, - "start": 359, - "end": 461 + "start": 433, + "end": 535 } } ], "span": { "dummy": false, "start": 0, - "end": 1560 + "end": 2370 } } \ No newline at end of file diff --git a/examples/lang_tour.my_tx.tir b/examples/lang_tour.my_tx.tir index f53d623c..11dfa54e 100644 --- a/examples/lang_tour.my_tx.tir +++ b/examples/lang_tour.my_tx.tir @@ -2,7 +2,20 @@ "fees": { "EvalParam": "ExpectFees" }, - "references": [], + "references": [ + { + "UtxoRefs": [ + { + "txid": [ + 171, + 205, + 239 + ], + "index": 2 + } + ] + } + ], "inputs": [ { "name": "source", @@ -275,6 +288,26 @@ } } ] + }, + { + "Map": [ + [ + { + "Number": 1 + }, + { + "String": "Value1" + } + ], + [ + { + "Number": 2 + }, + { + "String": "Value2" + } + ] + ] } ] } @@ -506,7 +539,7 @@ ], "validity": { "since": { - "Number": 1735700400000 + "EvalCompiler": "ComputeTipSlot" }, "until": { "EvalParam": { @@ -577,9 +610,149 @@ } } ], - "burns": [], - "adhoc": [], - "collateral": [], + "burns": [ + { + "amount": { + "Assets": [ + { + "policy": { + "Bytes": [ + 171, + 205, + 239, + 18, + 52 + ] + }, + "asset_name": { + "String": "MYTOKEN" + }, + "amount": { + "Number": 50 + } + } + ] + }, + "redeemer": { + "Struct": { + "constructor": 0, + "fields": [] + } + } + } + ], + "adhoc": [ + { + "name": "vote_delegation_certificate", + "data": { + "stake": { + "Bytes": [ + 135, + 101, + 67, + 33 + ] + }, + "drep": { + "Bytes": [ + 18, + 52, + 86, + 120 + ] + } + } + }, + { + "name": "withdrawal", + "data": { + "redeemer": { + "Struct": { + "constructor": 0, + "fields": [] + } + }, + "amount": { + "Number": 100 + }, + "credential": { + "EvalParam": { + "ExpectValue": [ + "myparty", + "Address" + ] + } + } + } + }, + { + "name": "plutus_witness", + "data": { + "version": { + "Number": 2 + }, + "script": { + "Bytes": [ + 171, + 205, + 239, + 18, + 52 + ] + } + } + }, + { + "name": "native_witness", + "data": { + "script": { + "Bytes": [ + 18, + 52, + 86, + 120 + ] + } + } + }, + { + "name": "treasury_donation", + "data": { + "coin": { + "Number": 500 + } + } + } + ], + "collateral": [ + { + "utxos": { + "EvalParam": { + "ExpectInput": [ + "collateral", + { + "address": "None", + "min_amount": "None", + "ref": { + "UtxoRefs": [ + { + "txid": [ + 171, + 205, + 239 + ], + "index": 1 + } + ] + }, + "many": false, + "collateral": true + } + ] + } + } + } + ], "signers": { "signers": [ { @@ -637,6 +810,14 @@ ] } } + }, + { + "key": { + "Number": 2 + }, + "value": { + "String": "Additional Metadata" + } } ] } \ No newline at end of file diff --git a/examples/reference_script.publish_native.tir b/examples/reference_script.publish_native.tir index a5a0bcfd..c6518063 100644 --- a/examples/reference_script.publish_native.tir +++ b/examples/reference_script.publish_native.tir @@ -137,22 +137,6 @@ { "name": "cardano_publish", "data": { - "amount": { - "Assets": [ - { - "policy": "None", - "asset_name": "None", - "amount": { - "EvalParam": { - "ExpectValue": [ - "quantity", - "Int" - ] - } - } - } - ] - }, "to": { "EvalParam": { "ExpectValue": [ @@ -170,6 +154,25 @@ 4, 0 ] + }, + "amount": { + "Assets": [ + { + "policy": "None", + "asset_name": "None", + "amount": { + "EvalParam": { + "ExpectValue": [ + "quantity", + "Int" + ] + } + } + } + ] + }, + "version": { + "Number": 0 } } } @@ -177,4 +180,4 @@ "collateral": [], "signers": null, "metadata": [] -} +} \ No newline at end of file diff --git a/examples/reference_script.publish_plutus.tir b/examples/reference_script.publish_plutus.tir index e98d6a11..f1291c1f 100644 --- a/examples/reference_script.publish_plutus.tir +++ b/examples/reference_script.publish_plutus.tir @@ -137,28 +137,6 @@ { "name": "cardano_publish", "data": { - "script": { - "Bytes": [ - 81, - 1, - 1, - 0, - 35, - 37, - 152, - 0, - 165, - 24, - 164, - 209, - 54, - 86, - 64, - 4, - 174, - 105 - ] - }, "version": { "Number": 3 }, @@ -185,6 +163,28 @@ "Address" ] } + }, + "script": { + "Bytes": [ + 81, + 1, + 1, + 0, + 35, + 37, + 152, + 0, + 165, + 24, + 164, + 209, + 54, + 86, + 64, + 4, + 174, + 105 + ] } } } From ef5b7a23ddd8a404f362a36d7ffb0d498d9152a5 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Fri, 17 Oct 2025 16:01:18 -0300 Subject: [PATCH 08/18] fix tests --- examples/lang_tour.ast | 898 ++++++++++++++----- examples/lang_tour.my_tx.tir | 191 +++- examples/reference_script.publish_native.tir | 34 +- examples/reference_script.publish_plutus.tir | 20 +- examples/withdrawal.transfer.tir | 16 +- 5 files changed, 879 insertions(+), 280 deletions(-) diff --git a/examples/lang_tour.ast b/examples/lang_tour.ast index 812c24be..79807a27 100644 --- a/examples/lang_tour.ast +++ b/examples/lang_tour.ast @@ -18,12 +18,32 @@ "start": 28, "end": 42 } + }, + { + "name": "field_c", + "type": "Bool", + "span": { + "dummy": false, + "start": 48, + "end": 61 + } + }, + { + "name": "field_d", + "type": { + "List": "Bytes" + }, + "span": { + "dummy": false, + "start": 67, + "end": 87 + } } ], "span": { "dummy": false, "start": 0, - "end": 45 + "end": 90 } }, "txs": [ @@ -32,8 +52,8 @@ "value": "my_tx", "span": { "dummy": false, - "start": 466, - "end": 471 + "start": 540, + "end": 545 } }, "parameters": { @@ -43,8 +63,8 @@ "value": "quantity", "span": { "dummy": false, - "start": 477, - "end": 485 + "start": 551, + "end": 559 } }, "type": "Int" @@ -54,8 +74,8 @@ "value": "validUntil", "span": { "dummy": false, - "start": 496, - "end": 506 + "start": 570, + "end": 580 } }, "type": "Int" @@ -65,8 +85,8 @@ "value": "metadata", "span": { "dummy": false, - "start": 517, - "end": 525 + "start": 591, + "end": 599 } }, "type": "Bytes" @@ -74,12 +94,88 @@ ], "span": { "dummy": false, - "start": 471, - "end": 535 + "start": 545, + "end": 609 + } + }, + "locals": { + "assigns": [ + { + "name": { + "value": "local_var", + "span": { + "dummy": false, + "start": 1914, + "end": 1923 + } + }, + "value": { + "ConcatOp": { + "lhs": { + "String": { + "value": "Lang", + "span": { + "dummy": false, + "start": 1932, + "end": 1938 + } + } + }, + "rhs": { + "String": { + "value": "Tour", + "span": { + "dummy": false, + "start": 1940, + "end": 1946 + } + } + }, + "span": { + "dummy": false, + "start": 1925, + "end": 1947 + } + } + }, + "span": { + "dummy": false, + "start": 1914, + "end": 1947 + } + } + ], + "span": { + "dummy": false, + "start": 1897, + "end": 1954 } }, - "locals": null, - "references": [], + "references": [ + { + "name": "ref_block", + "ref": { + "UtxoRef": { + "txid": [ + 171, + 205, + 239 + ], + "index": 2, + "span": { + "dummy": false, + "start": 1220, + "end": 1230 + } + } + }, + "span": { + "dummy": false, + "start": 1185, + "end": 1237 + } + } + ], "inputs": [ { "name": "source", @@ -91,8 +187,8 @@ "value": "MyParty", "span": { "dummy": false, - "start": 571, - "end": 578 + "start": 645, + "end": 652 } } } @@ -118,8 +214,8 @@ "value": "Ada", "span": { "dummy": false, - "start": 628, - "end": 631 + "start": 702, + "end": 705 } }, "amount": { @@ -127,15 +223,15 @@ "value": "quantity", "span": { "dummy": false, - "start": 632, - "end": 640 + "start": 706, + "end": 714 } } }, "span": { "dummy": false, - "start": 628, - "end": 641 + "start": 702, + "end": 715 } } }, @@ -144,15 +240,15 @@ "value": "named_output", "span": { "dummy": false, - "start": 653, - "end": 665 + "start": 727, + "end": 739 } } }, "span": { "dummy": false, - "start": 642, - "end": 643 + "start": 716, + "end": 717 } } } @@ -164,8 +260,8 @@ "value": "MyVariant", "span": { "dummy": false, - "start": 686, - "end": 695 + "start": 760, + "end": 769 } }, "case": { @@ -173,8 +269,8 @@ "value": "Case1", "span": { "dummy": false, - "start": 697, - "end": 702 + "start": 771, + "end": 776 } }, "fields": [ @@ -183,8 +279,8 @@ "value": "field1", "span": { "dummy": false, - "start": 717, - "end": 723 + "start": 791, + "end": 797 } }, "value": { @@ -192,15 +288,15 @@ "value": "field_a", "span": { "dummy": false, - "start": 725, - "end": 732 + "start": 799, + "end": 806 } } }, "span": { "dummy": false, - "start": 717, - "end": 732 + "start": 791, + "end": 806 } }, { @@ -208,8 +304,8 @@ "value": "field2", "span": { "dummy": false, - "start": 746, - "end": 752 + "start": 820, + "end": 826 } }, "value": { @@ -217,15 +313,15 @@ "value": "AFAFAF", "span": { "dummy": false, - "start": 754, - "end": 762 + "start": 828, + "end": 836 } } }, "span": { "dummy": false, - "start": 746, - "end": 762 + "start": 820, + "end": 836 } }, { @@ -233,8 +329,8 @@ "value": "field3", "span": { "dummy": false, - "start": 776, - "end": 782 + "start": 850, + "end": 856 } }, "value": { @@ -242,29 +338,29 @@ "value": "quantity", "span": { "dummy": false, - "start": 784, - "end": 792 + "start": 858, + "end": 866 } } }, "span": { "dummy": false, - "start": 776, - "end": 792 + "start": 850, + "end": 866 } } ], "spread": null, "span": { "dummy": false, - "start": 695, - "end": 803 + "start": 769, + "end": 877 } }, "span": { "dummy": false, - "start": 686, - "end": 803 + "start": 760, + "end": 877 } } } @@ -272,8 +368,8 @@ ], "span": { "dummy": false, - "start": 542, - "end": 810 + "start": 616, + "end": 884 } } ], @@ -283,8 +379,8 @@ "value": "named_output", "span": { "dummy": false, - "start": 996, - "end": 1008 + "start": 1250, + "end": 1262 } }, "fields": [ @@ -294,8 +390,8 @@ "value": "MyParty", "span": { "dummy": false, - "start": 1023, - "end": 1030 + "start": 1277, + "end": 1284 } } } @@ -307,8 +403,8 @@ "value": "MyRecord", "span": { "dummy": false, - "start": 1047, - "end": 1055 + "start": 1301, + "end": 1309 } }, "case": { @@ -326,8 +422,8 @@ "value": "field1", "span": { "dummy": false, - "start": 1070, - "end": 1076 + "start": 1324, + "end": 1330 } }, "value": { @@ -335,15 +431,15 @@ "value": "quantity", "span": { "dummy": false, - "start": 1078, - "end": 1086 + "start": 1332, + "end": 1340 } } }, "span": { "dummy": false, - "start": 1070, - "end": 1086 + "start": 1324, + "end": 1340 } }, { @@ -351,8 +447,8 @@ "value": "field2", "span": { "dummy": false, - "start": 1100, - "end": 1106 + "start": 1354, + "end": 1360 } }, "value": { @@ -367,8 +463,8 @@ }, "span": { "dummy": false, - "start": 1112, - "end": 1113 + "start": 1366, + "end": 1367 } } }, @@ -382,22 +478,22 @@ }, "span": { "dummy": false, - "start": 1123, - "end": 1124 + "start": 1377, + "end": 1378 } } }, "span": { "dummy": false, - "start": 1118, - "end": 1119 + "start": 1372, + "end": 1373 } } }, "span": { "dummy": false, - "start": 1100, - "end": 1127 + "start": 1354, + "end": 1381 } }, { @@ -405,8 +501,8 @@ "value": "field4", "span": { "dummy": false, - "start": 1141, - "end": 1147 + "start": 1395, + "end": 1401 } }, "value": { @@ -428,8 +524,8 @@ "value": "source", "span": { "dummy": false, - "start": 1159, - "end": 1165 + "start": 1413, + "end": 1419 } } }, @@ -438,30 +534,96 @@ "value": "field1", "span": { "dummy": false, - "start": 1166, - "end": 1172 + "start": 1420, + "end": 1426 } } }, "span": { "dummy": false, - "start": 1165, - "end": 1172 + "start": 1419, + "end": 1426 + } + } + } + ], + "span": { + "dummy": false, + "start": 1403, + "end": 1427 + } + } + }, + "span": { + "dummy": false, + "start": 1395, + "end": 1427 + } + }, + { + "name": { + "value": "field5", + "span": { + "dummy": false, + "start": 1441, + "end": 1447 + } + }, + "value": { + "MapConstructor": { + "fields": [ + { + "key": { + "Number": 1 + }, + "value": { + "String": { + "value": "Value1", + "span": { + "dummy": false, + "start": 1453, + "end": 1461 + } } + }, + "span": { + "dummy": false, + "start": 1450, + "end": 1461 + } + }, + { + "key": { + "Number": 2 + }, + "value": { + "String": { + "value": "Value2", + "span": { + "dummy": false, + "start": 1466, + "end": 1474 + } + } + }, + "span": { + "dummy": false, + "start": 1463, + "end": 1474 } } ], "span": { "dummy": false, - "start": 1149, - "end": 1173 + "start": 1449, + "end": 1476 } } }, "span": { "dummy": false, - "start": 1141, - "end": 1173 + "start": 1441, + "end": 1476 } } ], @@ -470,21 +632,21 @@ "value": "source", "span": { "dummy": false, - "start": 1190, - "end": 1196 + "start": 1493, + "end": 1499 } } }, "span": { "dummy": false, - "start": 1056, - "end": 1206 + "start": 1310, + "end": 1509 } }, "span": { "dummy": false, - "start": 1047, - "end": 1206 + "start": 1301, + "end": 1509 } } } @@ -503,8 +665,8 @@ "value": "source", "span": { "dummy": false, - "start": 1233, - "end": 1239 + "start": 1536, + "end": 1542 } } }, @@ -513,15 +675,15 @@ "value": "field3", "span": { "dummy": false, - "start": 1240, - "end": 1246 + "start": 1543, + "end": 1549 } } }, "span": { "dummy": false, - "start": 1239, - "end": 1246 + "start": 1542, + "end": 1549 } } }, @@ -532,8 +694,8 @@ "value": "source", "span": { "dummy": false, - "start": 1248, - "end": 1254 + "start": 1551, + "end": 1557 } } }, @@ -542,15 +704,15 @@ "value": "field2", "span": { "dummy": false, - "start": 1255, - "end": 1261 + "start": 1558, + "end": 1564 } } }, "span": { "dummy": false, - "start": 1254, - "end": 1261 + "start": 1557, + "end": 1564 } } }, @@ -561,8 +723,8 @@ "value": "source", "span": { "dummy": false, - "start": 1263, - "end": 1269 + "start": 1566, + "end": 1572 } } }, @@ -571,22 +733,22 @@ "value": "field1", "span": { "dummy": false, - "start": 1270, - "end": 1276 + "start": 1573, + "end": 1579 } } }, "span": { "dummy": false, - "start": 1269, - "end": 1276 + "start": 1572, + "end": 1579 } } }, "span": { "dummy": false, - "start": 1224, - "end": 1277 + "start": 1527, + "end": 1580 } } }, @@ -596,8 +758,8 @@ "value": "Ada", "span": { "dummy": false, - "start": 1280, - "end": 1283 + "start": 1583, + "end": 1586 } }, "amount": { @@ -605,15 +767,15 @@ }, "span": { "dummy": false, - "start": 1280, - "end": 1287 + "start": 1583, + "end": 1590 } } }, "span": { "dummy": false, - "start": 1278, - "end": 1279 + "start": 1581, + "end": 1582 } } }, @@ -622,15 +784,15 @@ "value": "named_output", "span": { "dummy": false, - "start": 1299, - "end": 1311 + "start": 1602, + "end": 1614 } } }, "span": { "dummy": false, - "start": 1288, - "end": 1289 + "start": 1591, + "end": 1592 } } } @@ -638,17 +800,15 @@ ], "span": { "dummy": false, - "start": 989, - "end": 1319 + "start": 1243, + "end": 1622 } } ], "validity": { "fields": [ { - "SinceSlot": { - "Number": 1735700400000 - } + "SinceSlot": "ComputeTipSlot" }, { "UntilSlot": { @@ -656,8 +816,8 @@ "value": "validUntil", "span": { "dummy": false, - "start": 1497, - "end": 1507 + "start": 1797, + "end": 1807 } } } @@ -665,8 +825,8 @@ ], "span": { "dummy": false, - "start": 1431, - "end": 1514 + "start": 1734, + "end": 1814 } }, "mints": [ @@ -679,8 +839,8 @@ "value": "StaticAsset", "span": { "dummy": false, - "start": 839, - "end": 850 + "start": 913, + "end": 924 } }, "amount": { @@ -688,8 +848,8 @@ }, "span": { "dummy": false, - "start": 839, - "end": 855 + "start": 913, + "end": 929 } } } @@ -700,8 +860,8 @@ ], "span": { "dummy": false, - "start": 816, - "end": 884 + "start": 890, + "end": 958 } }, { @@ -714,8 +874,8 @@ "value": "AB11223344", "span": { "dummy": false, - "start": 922, - "end": 934 + "start": 996, + "end": 1008 } } }, @@ -724,8 +884,8 @@ "value": "OTHER_TOKEN", "span": { "dummy": false, - "start": 936, - "end": 949 + "start": 1010, + "end": 1023 } } }, @@ -734,8 +894,8 @@ }, "span": { "dummy": false, - "start": 913, - "end": 954 + "start": 987, + "end": 1028 } } } @@ -746,12 +906,47 @@ ], "span": { "dummy": false, - "start": 890, - "end": 983 + "start": 964, + "end": 1057 + } + } + ], + "burns": [ + { + "fields": [ + { + "Amount": { + "StaticAssetConstructor": { + "type": { + "value": "StaticAsset", + "span": { + "dummy": false, + "start": 1086, + "end": 1097 + } + }, + "amount": { + "Number": 50 + }, + "span": { + "dummy": false, + "start": 1086, + "end": 1101 + } + } + } + }, + { + "Redeemer": "Unit" + } + ], + "span": { + "dummy": false, + "start": 1063, + "end": 1130 } } ], - "burns": [], "signers": { "signers": [ { @@ -759,8 +954,8 @@ "value": "MyParty", "span": { "dummy": false, - "start": 1343, - "end": 1350 + "start": 1646, + "end": 1653 } } }, @@ -769,25 +964,207 @@ "value": "0F5B22E57FEEB5B4FD1D501B007A427C56A76884D4978FAFEF979D9C", "span": { "dummy": false, - "start": 1360, - "end": 1418 + "start": 1663, + "end": 1721 } } } ], "span": { "dummy": false, - "start": 1325, - "end": 1425 + "start": 1628, + "end": 1728 } }, - "adhoc": [], + "adhoc": [ + { + "Cardano": { + "VoteDelegationCertificate": { + "drep": { + "HexString": { + "value": "12345678", + "span": { + "dummy": false, + "start": 2013, + "end": 2023 + } + } + }, + "stake": { + "HexString": { + "value": "87654321", + "span": { + "dummy": false, + "start": 2040, + "end": 2050 + } + } + }, + "span": { + "dummy": false, + "start": 1969, + "end": 2057 + } + } + } + }, + { + "Cardano": { + "Withdrawal": { + "fields": [ + { + "From": { + "Identifier": { + "value": "MyParty", + "span": { + "dummy": false, + "start": 2099, + "end": 2106 + } + } + } + }, + { + "Amount": { + "Number": 100 + } + }, + { + "Redeemer": "Unit" + } + ], + "span": { + "dummy": false, + "start": 2072, + "end": 2156 + } + } + } + }, + { + "Cardano": { + "PlutusWitness": { + "fields": [ + { + "Version": [ + { + "Number": 2 + }, + { + "dummy": false, + "start": 2196, + "end": 2206 + } + ] + }, + { + "Script": [ + { + "HexString": { + "value": "ABCDEF1234", + "span": { + "dummy": false, + "start": 2224, + "end": 2236 + } + } + }, + { + "dummy": false, + "start": 2216, + "end": 2236 + } + ] + } + ], + "span": { + "dummy": false, + "start": 2171, + "end": 2243 + } + } + } + }, + { + "Cardano": { + "NativeWitness": { + "fields": [ + { + "Script": [ + { + "HexString": { + "value": "12345678", + "span": { + "dummy": false, + "start": 2291, + "end": 2301 + } + } + }, + { + "dummy": false, + "start": 2283, + "end": 2301 + } + ] + } + ], + "span": { + "dummy": false, + "start": 2258, + "end": 2308 + } + } + } + }, + { + "Cardano": { + "TreasuryDonation": { + "coin": { + "Number": 500 + }, + "span": { + "dummy": false, + "start": 2323, + "end": 2367 + } + } + } + } + ], "span": { "dummy": false, - "start": 463, - "end": 1559 + "start": 537, + "end": 2369 }, - "collateral": [], + "collateral": [ + { + "fields": [ + { + "Ref": { + "UtxoRef": { + "txid": [ + 171, + 205, + 239 + ], + "index": 1, + "span": { + "dummy": false, + "start": 1162, + "end": 1172 + } + } + } + } + ], + "span": { + "dummy": false, + "start": 1136, + "end": 1179 + } + } + ], "metadata": { "fields": [ { @@ -799,22 +1176,42 @@ "value": "metadata", "span": { "dummy": false, - "start": 1542, - "end": 1550 + "start": 1842, + "end": 1850 } } }, "span": { "dummy": false, - "start": 1539, - "end": 1550 + "start": 1839, + "end": 1850 + } + }, + { + "key": { + "Number": 2 + }, + "value": { + "String": { + "value": "Additional Metadata", + "span": { + "dummy": false, + "start": 1863, + "end": 1884 + } + } + }, + "span": { + "dummy": false, + "start": 1860, + "end": 1884 } } ], "span": { "dummy": false, - "start": 1520, - "end": 1557 + "start": 1820, + "end": 1891 } } } @@ -825,8 +1222,8 @@ "value": "MyRecord", "span": { "dummy": false, - "start": 68, - "end": 76 + "start": 113, + "end": 121 } }, "cases": [ @@ -845,15 +1242,15 @@ "value": "field1", "span": { "dummy": false, - "start": 83, - "end": 89 + "start": 128, + "end": 134 } }, "type": "Int", "span": { "dummy": false, - "start": 83, - "end": 94 + "start": 128, + "end": 139 } }, { @@ -861,15 +1258,15 @@ "value": "field2", "span": { "dummy": false, - "start": 100, - "end": 106 + "start": 145, + "end": 151 } }, "type": "Bytes", "span": { "dummy": false, - "start": 100, - "end": 113 + "start": 145, + "end": 158 } }, { @@ -877,15 +1274,15 @@ "value": "field3", "span": { "dummy": false, - "start": 119, - "end": 125 + "start": 164, + "end": 170 } }, "type": "Bytes", "span": { "dummy": false, - "start": 119, - "end": 132 + "start": 164, + "end": 177 } }, { @@ -893,8 +1290,8 @@ "value": "field4", "span": { "dummy": false, - "start": 138, - "end": 144 + "start": 183, + "end": 189 } }, "type": { @@ -902,22 +1299,43 @@ }, "span": { "dummy": false, - "start": 138, - "end": 155 + "start": 183, + "end": 200 + } + }, + { + "name": { + "value": "field5", + "span": { + "dummy": false, + "start": 206, + "end": 212 + } + }, + "type": { + "Map": [ + "Int", + "Bytes" + ] + }, + "span": { + "dummy": false, + "start": 206, + "end": 229 } } ], "span": { "dummy": false, - "start": 63, - "end": 158 + "start": 108, + "end": 232 } } ], "span": { "dummy": false, - "start": 63, - "end": 158 + "start": 108, + "end": 232 } }, { @@ -925,8 +1343,8 @@ "value": "MyVariant", "span": { "dummy": false, - "start": 165, - "end": 174 + "start": 239, + "end": 248 } }, "cases": [ @@ -935,8 +1353,8 @@ "value": "Case1", "span": { "dummy": false, - "start": 181, - "end": 186 + "start": 255, + "end": 260 } }, "fields": [ @@ -945,15 +1363,15 @@ "value": "field1", "span": { "dummy": false, - "start": 197, - "end": 203 + "start": 271, + "end": 277 } }, "type": "Int", "span": { "dummy": false, - "start": 197, - "end": 208 + "start": 271, + "end": 282 } }, { @@ -961,15 +1379,15 @@ "value": "field2", "span": { "dummy": false, - "start": 218, - "end": 224 + "start": 292, + "end": 298 } }, "type": "Bytes", "span": { "dummy": false, - "start": 218, - "end": 231 + "start": 292, + "end": 305 } }, { @@ -977,22 +1395,22 @@ "value": "field3", "span": { "dummy": false, - "start": 241, - "end": 247 + "start": 315, + "end": 321 } }, "type": "Int", "span": { "dummy": false, - "start": 241, - "end": 252 + "start": 315, + "end": 326 } } ], "span": { "dummy": false, - "start": 181, - "end": 259 + "start": 255, + "end": 333 } }, { @@ -1000,22 +1418,22 @@ "value": "Case2", "span": { "dummy": false, - "start": 265, - "end": 270 + "start": 339, + "end": 344 } }, "fields": [], "span": { "dummy": false, - "start": 265, - "end": 270 + "start": 339, + "end": 344 } } ], "span": { "dummy": false, - "start": 160, - "end": 273 + "start": 234, + "end": 347 } } ], @@ -1026,8 +1444,8 @@ "value": "StaticAsset", "span": { "dummy": false, - "start": 320, - "end": 331 + "start": 394, + "end": 405 } }, "policy": { @@ -1035,8 +1453,8 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 334, - "end": 346 + "start": 408, + "end": 420 } } }, @@ -1045,15 +1463,15 @@ "value": "MYTOKEN", "span": { "dummy": false, - "start": 347, - "end": 356 + "start": 421, + "end": 430 } } }, "span": { "dummy": false, - "start": 314, - "end": 357 + "start": 388, + "end": 431 } } ], @@ -1063,14 +1481,14 @@ "value": "MyParty", "span": { "dummy": false, - "start": 53, - "end": 60 + "start": 98, + "end": 105 } }, "span": { "dummy": false, - "start": 47, - "end": 61 + "start": 92, + "end": 106 } } ], @@ -1080,8 +1498,8 @@ "value": "OnlyHashPolicy", "span": { "dummy": false, - "start": 282, - "end": 296 + "start": 356, + "end": 370 } }, "value": { @@ -1089,15 +1507,15 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 299, - "end": 311 + "start": 373, + "end": 385 } } }, "span": { "dummy": false, - "start": 275, - "end": 312 + "start": 349, + "end": 386 } }, { @@ -1105,8 +1523,8 @@ "value": "FullyDefinedPolicy", "span": { "dummy": false, - "start": 366, - "end": 384 + "start": 440, + "end": 458 } }, "value": { @@ -1118,8 +1536,8 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 397, - "end": 409 + "start": 471, + "end": 483 } } } @@ -1130,8 +1548,8 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 423, - "end": 435 + "start": 497, + "end": 509 } } } @@ -1142,8 +1560,8 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 446, - "end": 458 + "start": 520, + "end": 532 } } } @@ -1151,21 +1569,21 @@ ], "span": { "dummy": false, - "start": 385, - "end": 461 + "start": 459, + "end": 535 } } }, "span": { "dummy": false, - "start": 359, - "end": 461 + "start": 433, + "end": 535 } } ], "span": { "dummy": false, "start": 0, - "end": 1560 + "end": 2370 } } \ No newline at end of file diff --git a/examples/lang_tour.my_tx.tir b/examples/lang_tour.my_tx.tir index c1ed8b7e..b8b6d70c 100644 --- a/examples/lang_tour.my_tx.tir +++ b/examples/lang_tour.my_tx.tir @@ -2,7 +2,20 @@ "fees": { "EvalParam": "ExpectFees" }, - "references": [], + "references": [ + { + "UtxoRefs": [ + { + "txid": [ + 171, + 205, + 239 + ], + "index": 2 + } + ] + } + ], "inputs": [ { "name": "source", @@ -275,6 +288,26 @@ } } ] + }, + { + "Map": [ + [ + { + "Number": 1 + }, + { + "String": "Value1" + } + ], + [ + { + "Number": 2 + }, + { + "String": "Value2" + } + ] + ] } ] } @@ -505,7 +538,7 @@ ], "validity": { "since": { - "Number": 1735700400000 + "EvalCompiler": "ComputeTipSlot" }, "until": { "EvalParam": { @@ -576,9 +609,149 @@ } } ], - "burns": [], - "adhoc": [], - "collateral": [], + "burns": [ + { + "amount": { + "Assets": [ + { + "policy": { + "Bytes": [ + 171, + 205, + 239, + 18, + 52 + ] + }, + "asset_name": { + "String": "MYTOKEN" + }, + "amount": { + "Number": 50 + } + } + ] + }, + "redeemer": { + "Struct": { + "constructor": 0, + "fields": [] + } + } + } + ], + "adhoc": [ + { + "name": "vote_delegation_certificate", + "data": { + "drep": { + "Bytes": [ + 18, + 52, + 86, + 120 + ] + }, + "stake": { + "Bytes": [ + 135, + 101, + 67, + 33 + ] + } + } + }, + { + "name": "withdrawal", + "data": { + "credential": { + "EvalParam": { + "ExpectValue": [ + "myparty", + "Address" + ] + } + }, + "amount": { + "Number": 100 + }, + "redeemer": { + "Struct": { + "constructor": 0, + "fields": [] + } + } + } + }, + { + "name": "plutus_witness", + "data": { + "script": { + "Bytes": [ + 171, + 205, + 239, + 18, + 52 + ] + }, + "version": { + "Number": 2 + } + } + }, + { + "name": "native_witness", + "data": { + "script": { + "Bytes": [ + 18, + 52, + 86, + 120 + ] + } + } + }, + { + "name": "treasury_donation", + "data": { + "coin": { + "Number": 500 + } + } + } + ], + "collateral": [ + { + "utxos": { + "EvalParam": { + "ExpectInput": [ + "collateral", + { + "address": "None", + "min_amount": "None", + "ref": { + "UtxoRefs": [ + { + "txid": [ + 171, + 205, + 239 + ], + "index": 1 + } + ] + }, + "many": false, + "collateral": true + } + ] + } + } + } + ], "signers": { "signers": [ { @@ -636,6 +809,14 @@ ] } } + }, + { + "key": { + "Number": 2 + }, + "value": { + "String": "Additional Metadata" + } } ] } \ No newline at end of file diff --git a/examples/reference_script.publish_native.tir b/examples/reference_script.publish_native.tir index 3beadd57..2813897a 100644 --- a/examples/reference_script.publish_native.tir +++ b/examples/reference_script.publish_native.tir @@ -136,21 +136,8 @@ { "name": "cardano_publish", "data": { - "amount": { - "Assets": [ - { - "policy": "None", - "asset_name": "None", - "amount": { - "EvalParam": { - "ExpectValue": [ - "quantity", - "Int" - ] - } - } - } - ] + "version": { + "Number": 0 }, "to": { "EvalParam": { @@ -170,8 +157,21 @@ 0 ] }, - "version": { - "Number": 0 + "amount": { + "Assets": [ + { + "policy": "None", + "asset_name": "None", + "amount": { + "EvalParam": { + "ExpectValue": [ + "quantity", + "Int" + ] + } + } + } + ] } } } diff --git a/examples/reference_script.publish_plutus.tir b/examples/reference_script.publish_plutus.tir index b2562f35..90a5af0e 100644 --- a/examples/reference_script.publish_plutus.tir +++ b/examples/reference_script.publish_plutus.tir @@ -136,6 +136,14 @@ { "name": "cardano_publish", "data": { + "to": { + "EvalParam": { + "ExpectValue": [ + "receiver", + "Address" + ] + } + }, "script": { "Bytes": [ 81, @@ -158,9 +166,6 @@ 105 ] }, - "version": { - "Number": 3 - }, "amount": { "Assets": [ { @@ -177,13 +182,8 @@ } ] }, - "to": { - "EvalParam": { - "ExpectValue": [ - "receiver", - "Address" - ] - } + "version": { + "Number": 3 } } } diff --git a/examples/withdrawal.transfer.tir b/examples/withdrawal.transfer.tir index 5130bab6..d31757c7 100644 --- a/examples/withdrawal.transfer.tir +++ b/examples/withdrawal.transfer.tir @@ -163,14 +163,6 @@ { "name": "withdrawal", "data": { - "credential": { - "EvalParam": { - "ExpectValue": [ - "sender", - "Address" - ] - } - }, "amount": { "Number": 0 }, @@ -179,6 +171,14 @@ "constructor": 0, "fields": [] } + }, + "credential": { + "EvalParam": { + "ExpectValue": [ + "sender", + "Address" + ] + } } } } From 974752db25f51e49dbc07abdeb1615cf57dac851 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Fri, 17 Oct 2025 16:03:48 -0300 Subject: [PATCH 09/18] test: add test for mixed amount expressions in output --- crates/tx3-lang/src/analyzing.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index 8a3451e8..d7cd6ff8 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -1453,4 +1453,36 @@ mod tests { let result = analyze(&mut ast); assert!(result.errors.is_empty()); } + #[test] + fn test_output_mixed_amount_expressions() { + let mut ast = crate::parsing::parse_string( + r#" + party Alice; + tx test() { + input source { + from: Alice, + min_amount: Ada(100), + } + output { + to: Alice, + amount: source - 50, + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + assert!(!result.errors.is_empty()); + + assert_eq!( + result.errors[0], + Error::InvalidTargetType(InvalidTargetTypeError { + expected: "AnyAsset".to_string(), + got: "Int".to_string(), + src: None, + span: Span::DUMMY, + }) + ); + } } From 9b26bda2615d0e094ed7c04c76c2ec2650b39795 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Tue, 21 Oct 2025 14:32:07 -0300 Subject: [PATCH 10/18] Refactor target_type and analyze methods to include Context parameter --- crates/tx3-lang/src/analyzing.rs | 346 +++++++++++++++++-------------- crates/tx3-lang/src/ast.rs | 72 +++---- crates/tx3-lang/src/cardano.rs | 138 ++++++++---- 3 files changed, 324 insertions(+), 232 deletions(-) diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index d7cd6ff8..2a5d6517 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -9,6 +9,19 @@ use miette::Diagnostic; use crate::ast::*; +#[derive(Debug, Clone)] +pub struct Context { + pub target_type: Vec, +} + +impl Default for Context { + fn default() -> Self { + Self { + target_type: vec![], + } + } +} + #[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq)] #[error("not in scope: {name}")] #[diagnostic(code(tx3::not_in_scope))] @@ -163,11 +176,13 @@ impl AnalyzeReport { } } - pub fn expect_data_expr_type(expr: &DataExpr, expected: &Type) -> Self { - if expr.target_type().as_ref() != Some(expected) { + pub fn expect_data_expr_type(expr: &DataExpr, expected: &Type, ctx: &mut Context) -> Self { + if expr.target_type(Some(ctx)).as_ref() != Some(expected) { Self::from(Error::invalid_target_type( expected, - expr.target_type().as_ref().unwrap_or(&Type::Undefined), + expr.target_type(Some(ctx)) + .as_ref() + .unwrap_or(&Type::Undefined), expr, )) } else { @@ -366,16 +381,16 @@ pub trait Analyzable { /// /// # Returns /// * `AnalyzeReport` of the analysis. Empty if no errors are found. - fn analyze(&mut self, parent: Option>) -> AnalyzeReport; + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport; /// Returns true if all of the symbols have been resolved . fn is_resolved(&self) -> bool; } impl Analyzable for Option { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { if let Some(item) = self { - item.analyze(parent) + item.analyze(parent, ctx) } else { AnalyzeReport::default() } @@ -387,8 +402,8 @@ impl Analyzable for Option { } impl Analyzable for Box { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.as_mut().analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.as_mut().analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -397,9 +412,9 @@ impl Analyzable for Box { } impl Analyzable for Vec { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { self.iter_mut() - .map(|item| item.analyze(parent.clone())) + .map(|item| item.analyze(parent.clone(), ctx)) .collect() } @@ -409,7 +424,7 @@ impl Analyzable for Vec { } impl Analyzable for PartyDef { - fn analyze(&mut self, _parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, _parent: Option>, _ctx: &mut Context) -> AnalyzeReport { AnalyzeReport::default() } @@ -419,11 +434,11 @@ impl Analyzable for PartyDef { } impl Analyzable for PolicyField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - PolicyField::Hash(x) => x.analyze(parent), - PolicyField::Script(x) => x.analyze(parent), - PolicyField::Ref(x) => x.analyze(parent), + PolicyField::Hash(x) => x.analyze(parent, ctx), + PolicyField::Script(x) => x.analyze(parent, ctx), + PolicyField::Ref(x) => x.analyze(parent, ctx), } } @@ -436,8 +451,8 @@ impl Analyzable for PolicyField { } } impl Analyzable for PolicyConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -446,9 +461,9 @@ impl Analyzable for PolicyConstructor { } impl Analyzable for PolicyDef { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match &mut self.value { - PolicyValue::Constructor(x) => x.analyze(parent), + PolicyValue::Constructor(x) => x.analyze(parent, ctx), PolicyValue::Assign(_) => AnalyzeReport::default(), } } @@ -462,11 +477,21 @@ impl Analyzable for PolicyDef { } impl Analyzable for AddOp { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let left = self.lhs.analyze(parent.clone()); - let right = self.rhs.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let left = self.lhs.analyze(parent.clone(), ctx); + let right = self.rhs.analyze(parent.clone(), ctx); - left + right + let left_type = self.lhs.target_type(Some(ctx)); + let right_type = self.rhs.target_type(Some(ctx)); + + let type_check = match (left_type.as_ref(), right_type.as_ref()) { + (Some(l), Some(r)) if l != r => { + AnalyzeReport::from(Error::invalid_target_type(l, r, self.rhs.as_ref())) + } + _ => AnalyzeReport::default(), + }; + + left + right + type_check } fn is_resolved(&self) -> bool { @@ -475,9 +500,9 @@ impl Analyzable for AddOp { } impl Analyzable for ConcatOp { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let left = self.lhs.analyze(parent.clone()); - let right = self.rhs.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let left = self.lhs.analyze(parent.clone(), ctx); + let right = self.rhs.analyze(parent.clone(), ctx); left + right } @@ -488,11 +513,21 @@ impl Analyzable for ConcatOp { } impl Analyzable for SubOp { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let left = self.lhs.analyze(parent.clone()); - let right = self.rhs.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let left = self.lhs.analyze(parent.clone(), ctx); + let right = self.rhs.analyze(parent.clone(), ctx); - left + right + let left_type = self.lhs.target_type(Some(ctx)); + let right_type = self.rhs.target_type(Some(ctx)); + + let type_check = match (left_type.as_ref(), right_type.as_ref()) { + (Some(l), Some(r)) if l != r => { + AnalyzeReport::from(Error::invalid_target_type(l, r, self.rhs.as_ref())) + } + _ => AnalyzeReport::default(), + }; + + left + right + type_check } fn is_resolved(&self) -> bool { @@ -501,8 +536,8 @@ impl Analyzable for SubOp { } impl Analyzable for NegateOp { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.operand.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.operand.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -511,9 +546,9 @@ impl Analyzable for NegateOp { } impl Analyzable for RecordConstructorField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let name = self.name.analyze(parent.clone()); - let value = self.value.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let name = self.name.analyze(parent.clone(), ctx); + let value = self.value.analyze(parent.clone(), ctx); name + value } @@ -524,11 +559,11 @@ impl Analyzable for RecordConstructorField { } impl Analyzable for VariantCaseConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { let name = if self.name.symbol.is_some() { AnalyzeReport::default() } else { - self.name.analyze(parent.clone()) + self.name.analyze(parent.clone(), ctx) }; let mut scope = Scope::new(parent); @@ -545,9 +580,9 @@ impl Analyzable for VariantCaseConstructor { self.scope = Some(Rc::new(scope)); - let fields = self.fields.analyze(self.scope.clone()); + let fields = self.fields.analyze(self.scope.clone(), ctx); - let spread = self.spread.analyze(self.scope.clone()); + let spread = self.spread.analyze(self.scope.clone(), ctx); name + fields + spread } @@ -558,8 +593,8 @@ impl Analyzable for VariantCaseConstructor { } impl Analyzable for StructConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let r#type = self.r#type.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let r#type = self.r#type.analyze(parent.clone(), ctx); let mut scope = Scope::new(parent); @@ -587,7 +622,7 @@ impl Analyzable for StructConstructor { self.scope = Some(Rc::new(scope)); - let case = self.case.analyze(self.scope.clone()); + let case = self.case.analyze(self.scope.clone(), ctx); r#type + case } @@ -598,8 +633,8 @@ impl Analyzable for StructConstructor { } impl Analyzable for ListConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.elements.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.elements.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -608,8 +643,8 @@ impl Analyzable for ListConstructor { } impl Analyzable for MapField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.key.analyze(parent.clone()) + self.value.analyze(parent.clone()) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.key.analyze(parent.clone(), ctx) + self.value.analyze(parent.clone(), ctx) } fn is_resolved(&self) -> bool { @@ -618,8 +653,8 @@ impl Analyzable for MapField { } impl Analyzable for MapConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -628,20 +663,20 @@ impl Analyzable for MapConstructor { } impl Analyzable for DataExpr { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - DataExpr::StructConstructor(x) => x.analyze(parent), - DataExpr::ListConstructor(x) => x.analyze(parent), - DataExpr::MapConstructor(x) => x.analyze(parent), - DataExpr::Identifier(x) => x.analyze(parent), - DataExpr::AddOp(x) => x.analyze(parent), - DataExpr::SubOp(x) => x.analyze(parent), - DataExpr::NegateOp(x) => x.analyze(parent), - DataExpr::PropertyOp(x) => x.analyze(parent), - DataExpr::StaticAssetConstructor(x) => x.analyze(parent), - DataExpr::AnyAssetConstructor(x) => x.analyze(parent), - DataExpr::MinUtxo(x) => x.analyze(parent), - DataExpr::ConcatOp(x) => x.analyze(parent), + DataExpr::StructConstructor(x) => x.analyze(parent, ctx), + DataExpr::ListConstructor(x) => x.analyze(parent, ctx), + DataExpr::MapConstructor(x) => x.analyze(parent, ctx), + DataExpr::Identifier(x) => x.analyze(parent, ctx), + DataExpr::AddOp(x) => x.analyze(parent, ctx), + DataExpr::SubOp(x) => x.analyze(parent, ctx), + DataExpr::NegateOp(x) => x.analyze(parent, ctx), + DataExpr::PropertyOp(x) => x.analyze(parent, ctx), + DataExpr::StaticAssetConstructor(x) => x.analyze(parent, ctx), + DataExpr::AnyAssetConstructor(x) => x.analyze(parent, ctx), + DataExpr::MinUtxo(x) => x.analyze(parent, ctx), + DataExpr::ConcatOp(x) => x.analyze(parent, ctx), _ => AnalyzeReport::default(), } } @@ -666,9 +701,9 @@ impl Analyzable for DataExpr { } impl Analyzable for StaticAssetConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let amount = self.amount.analyze(parent.clone()); - let r#type = self.r#type.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let amount = self.amount.analyze(parent.clone(), ctx); + let r#type = self.r#type.analyze(parent.clone(), ctx); amount + r#type } @@ -679,10 +714,10 @@ impl Analyzable for StaticAssetConstructor { } impl Analyzable for AnyAssetConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let policy = self.policy.analyze(parent.clone()); - let asset_name = self.asset_name.analyze(parent.clone()); - let amount = self.amount.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let policy = self.policy.analyze(parent.clone(), ctx); + let asset_name = self.asset_name.analyze(parent.clone(), ctx); + let amount = self.amount.analyze(parent.clone(), ctx); policy + asset_name + amount } @@ -715,9 +750,9 @@ impl Analyzable for PropertyOp { } impl Analyzable for AddressExpr { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - AddressExpr::Identifier(x) => x.analyze(parent), + AddressExpr::Identifier(x) => x.analyze(parent, ctx), _ => AnalyzeReport::default(), } } @@ -731,12 +766,13 @@ impl Analyzable for AddressExpr { } impl Analyzable for AssetDef { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let policy = self.policy.analyze(parent.clone()); - let asset_name = self.asset_name.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let policy = self.policy.analyze(parent.clone(), ctx); + let asset_name = self.asset_name.analyze(parent.clone(), ctx); - let policy_type = AnalyzeReport::expect_data_expr_type(&self.policy, &Type::Bytes); - let asset_name_type = AnalyzeReport::expect_data_expr_type(&self.asset_name, &Type::Bytes); + let policy_type = AnalyzeReport::expect_data_expr_type(&self.policy, &Type::Bytes, ctx); + let asset_name_type = + AnalyzeReport::expect_data_expr_type(&self.asset_name, &Type::Bytes, ctx); policy + asset_name + policy_type + asset_name_type } @@ -747,7 +783,7 @@ impl Analyzable for AssetDef { } impl Analyzable for Identifier { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, _ctx: &mut Context) -> AnalyzeReport { let symbol = parent.and_then(|p| p.resolve(&self.value)); if symbol.is_none() { @@ -765,12 +801,12 @@ impl Analyzable for Identifier { } impl Analyzable for Type { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - Type::Custom(x) => x.analyze(parent), - Type::List(x) => x.analyze(parent), + Type::Custom(x) => x.analyze(parent, ctx), + Type::List(x) => x.analyze(parent, ctx), Type::Map(key_type, value_type) => { - key_type.analyze(parent.clone()) + value_type.analyze(parent) + key_type.analyze(parent.clone(), ctx) + value_type.analyze(parent, ctx) } _ => AnalyzeReport::default(), } @@ -787,13 +823,13 @@ impl Analyzable for Type { } impl Analyzable for InputBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - InputBlockField::From(x) => x.analyze(parent), - InputBlockField::DatumIs(x) => x.analyze(parent), - InputBlockField::MinAmount(x) => x.analyze(parent), - InputBlockField::Redeemer(x) => x.analyze(parent), - InputBlockField::Ref(x) => x.analyze(parent), + InputBlockField::From(x) => x.analyze(parent, ctx), + InputBlockField::DatumIs(x) => x.analyze(parent, ctx), + InputBlockField::MinAmount(x) => x.analyze(parent, ctx), + InputBlockField::Redeemer(x) => x.analyze(parent, ctx), + InputBlockField::Ref(x) => x.analyze(parent, ctx), } } @@ -809,8 +845,8 @@ impl Analyzable for InputBlockField { } impl Analyzable for InputBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -819,9 +855,9 @@ impl Analyzable for InputBlock { } impl Analyzable for MetadataBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { // TODO: check keys are actually numbers - self.key.analyze(parent.clone()) + self.value.analyze(parent.clone()) + self.key.analyze(parent.clone(), ctx) + self.value.analyze(parent.clone(), ctx) } fn is_resolved(&self) -> bool { @@ -830,8 +866,8 @@ impl Analyzable for MetadataBlockField { } impl Analyzable for MetadataBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -840,10 +876,10 @@ impl Analyzable for MetadataBlock { } impl Analyzable for ValidityBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - ValidityBlockField::SinceSlot(x) => x.analyze(parent), - ValidityBlockField::UntilSlot(x) => x.analyze(parent), + ValidityBlockField::SinceSlot(x) => x.analyze(parent, ctx), + ValidityBlockField::UntilSlot(x) => x.analyze(parent, ctx), } } fn is_resolved(&self) -> bool { @@ -855,8 +891,8 @@ impl Analyzable for ValidityBlockField { } impl Analyzable for ValidityBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -887,8 +923,8 @@ impl Analyzable for OutputBlockField { } impl Analyzable for OutputBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -897,8 +933,8 @@ impl Analyzable for OutputBlock { } impl Analyzable for RecordField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.r#type.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.r#type.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -907,8 +943,8 @@ impl Analyzable for RecordField { } impl Analyzable for VariantCase { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -917,8 +953,8 @@ impl Analyzable for VariantCase { } impl Analyzable for AliasDef { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.alias_type.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.alias_type.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -927,8 +963,8 @@ impl Analyzable for AliasDef { } impl Analyzable for TypeDef { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.cases.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.cases.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -937,10 +973,10 @@ impl Analyzable for TypeDef { } impl Analyzable for MintBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - MintBlockField::Amount(x) => x.analyze(parent), - MintBlockField::Redeemer(x) => x.analyze(parent), + MintBlockField::Amount(x) => x.analyze(parent, ctx), + MintBlockField::Redeemer(x) => x.analyze(parent, ctx), } } @@ -953,8 +989,8 @@ impl Analyzable for MintBlockField { } impl Analyzable for MintBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -963,8 +999,8 @@ impl Analyzable for MintBlock { } impl Analyzable for SignersBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.signers.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.signers.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -973,8 +1009,8 @@ impl Analyzable for SignersBlock { } impl Analyzable for ReferenceBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.r#ref.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.r#ref.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -983,11 +1019,11 @@ impl Analyzable for ReferenceBlock { } impl Analyzable for CollateralBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - CollateralBlockField::From(x) => x.analyze(parent), - CollateralBlockField::MinAmount(x) => x.analyze(parent), - CollateralBlockField::Ref(x) => x.analyze(parent), + CollateralBlockField::From(x) => x.analyze(parent, ctx), + CollateralBlockField::MinAmount(x) => x.analyze(parent, ctx), + CollateralBlockField::Ref(x) => x.analyze(parent, ctx), } } @@ -1001,8 +1037,8 @@ impl Analyzable for CollateralBlockField { } impl Analyzable for CollateralBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1011,9 +1047,9 @@ impl Analyzable for CollateralBlock { } impl Analyzable for ChainSpecificBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - ChainSpecificBlock::Cardano(x) => x.analyze(parent), + ChainSpecificBlock::Cardano(x) => x.analyze(parent, ctx), } } @@ -1025,8 +1061,8 @@ impl Analyzable for ChainSpecificBlock { } impl Analyzable for LocalsAssign { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.value.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.value.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1035,8 +1071,8 @@ impl Analyzable for LocalsAssign { } impl Analyzable for LocalsBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.assigns.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.assigns.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1045,8 +1081,8 @@ impl Analyzable for LocalsBlock { } impl Analyzable for ParamDef { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.r#type.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.r#type.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1055,8 +1091,8 @@ impl Analyzable for ParamDef { } impl Analyzable for ParameterList { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.parameters.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.parameters.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1065,10 +1101,10 @@ impl Analyzable for ParameterList { } impl Analyzable for TxDef { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { // analyze static types before anything else - let params = self.parameters.analyze(parent.clone()); + let params = self.parameters.analyze(parent.clone(), ctx); // create the new scope and populate its symbols @@ -1085,7 +1121,7 @@ impl Analyzable for TxDef { let mut locals = self.locals.take().unwrap_or_default(); - let locals_report = locals.analyze(Some(parent.clone())); + let locals_report = locals.analyze(Some(parent.clone()), ctx); let parent = { let mut current = Scope::new(Some(parent.clone())); @@ -1101,7 +1137,7 @@ impl Analyzable for TxDef { Rc::new(current) }; - let inputs = self.inputs.analyze(Some(parent.clone())); + let inputs = self.inputs.analyze(Some(parent.clone()), ctx); let parent = { let mut current = Scope::new(Some(parent.clone())); @@ -1117,23 +1153,23 @@ impl Analyzable for TxDef { Rc::new(current) }; - let outputs = self.outputs.analyze(Some(parent.clone())); + let outputs = self.outputs.analyze(Some(parent.clone()), ctx); - let mints = self.mints.analyze(Some(parent.clone())); + let mints = self.mints.analyze(Some(parent.clone()), ctx); - let burns = self.burns.analyze(Some(parent.clone())); + let burns = self.burns.analyze(Some(parent.clone()), ctx); - let adhoc = self.adhoc.analyze(Some(parent.clone())); + let adhoc = self.adhoc.analyze(Some(parent.clone()), ctx); - let validity = self.validity.analyze(Some(parent.clone())); + let validity = self.validity.analyze(Some(parent.clone()), ctx); - let metadata = self.metadata.analyze(Some(parent.clone())); + let metadata = self.metadata.analyze(Some(parent.clone()), ctx); - let signers = self.signers.analyze(Some(parent.clone())); + let signers = self.signers.analyze(Some(parent.clone()), ctx); - let references = self.references.analyze(Some(parent.clone())); + let references = self.references.analyze(Some(parent.clone()), ctx); - let collateral = self.collateral.analyze(Some(parent.clone())); + let collateral = self.collateral.analyze(Some(parent.clone()), ctx); self.scope = Some(parent); @@ -1182,6 +1218,7 @@ fn resolve_types_and_aliases( scope_rc: &mut Rc, types: &mut Vec, aliases: &mut Vec, + ctx: &mut Context, ) -> (AnalyzeReport, AnalyzeReport) { let mut types_report = AnalyzeReport::default(); let mut aliases_report = AnalyzeReport::default(); @@ -1201,15 +1238,15 @@ fn resolve_types_and_aliases( scope.track_alias_def(alias_def); } - types_report = types.analyze(Some(scope_rc.clone())); - aliases_report = aliases.analyze(Some(scope_rc.clone())); + types_report = types.analyze(Some(scope_rc.clone()), ctx); + aliases_report = aliases.analyze(Some(scope_rc.clone()), ctx); } (types_report, aliases_report) } impl Analyzable for Program { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { let mut scope = Scope::new(parent); if let Some(env) = self.env.take() { @@ -1242,20 +1279,20 @@ impl Analyzable for Program { self.scope = Some(Rc::new(scope)); - let parties = self.parties.analyze(self.scope.clone()); + let parties = self.parties.analyze(self.scope.clone(), ctx); - let policies = self.policies.analyze(self.scope.clone()); + let policies = self.policies.analyze(self.scope.clone(), ctx); - let assets = self.assets.analyze(self.scope.clone()); + let assets = self.assets.analyze(self.scope.clone(), ctx); let mut types = self.types.clone(); let mut aliases = self.aliases.clone(); let scope_rc = self.scope.as_mut().unwrap(); - let (types, aliases) = resolve_types_and_aliases(scope_rc, &mut types, &mut aliases); + let (types, aliases) = resolve_types_and_aliases(scope_rc, &mut types, &mut aliases, ctx); - let txs = self.txs.analyze(self.scope.clone()); + let txs = self.txs.analyze(self.scope.clone(), ctx); parties + policies + types + aliases + txs + assets } @@ -1283,7 +1320,8 @@ 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) + let mut ctx = Context::default(); + ast.analyze(None, &mut ctx) } #[cfg(test)] diff --git a/crates/tx3-lang/src/ast.rs b/crates/tx3-lang/src/ast.rs index ad428137..eca894c3 100644 --- a/crates/tx3-lang/src/ast.rs +++ b/crates/tx3-lang/src/ast.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use std::{collections::HashMap, rc::Rc}; -use crate::cardano::PlutusWitnessBlock; +use crate::analyzing::Context; #[derive(Debug, PartialEq, Eq)] pub struct Scope { @@ -161,8 +161,8 @@ impl Identifier { } } - pub fn target_type(&self) -> Option { - self.symbol.as_ref().and_then(|x| x.target_type()) + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.symbol.as_ref().and_then(|x| x.target_type(ctx)) } } @@ -545,7 +545,7 @@ pub struct StaticAssetConstructor { } impl StaticAssetConstructor { - pub fn target_type(&self) -> Option { + pub fn target_type(&self, _ctx: Option<&Context>) -> Option { Some(Type::AnyAsset) } } @@ -559,7 +559,7 @@ pub struct AnyAssetConstructor { } impl AnyAssetConstructor { - pub fn target_type(&self) -> Option { + pub fn target_type(&self, _ctx: Option<&Context>) -> Option { Some(Type::AnyAsset) } } @@ -583,8 +583,8 @@ pub struct StructConstructor { } impl StructConstructor { - pub fn target_type(&self) -> Option { - self.r#type.symbol.as_ref().and_then(|x| x.target_type()) + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.r#type.symbol.as_ref().and_then(|x| x.target_type(ctx)) } } @@ -616,8 +616,8 @@ pub struct ListConstructor { } impl ListConstructor { - pub fn target_type(&self) -> Option { - self.elements.first().and_then(|x| x.target_type()) + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.elements.first().and_then(|x| x.target_type(ctx)) } } @@ -629,8 +629,8 @@ pub struct MapField { } impl MapField { - pub fn target_type(&self) -> Option { - self.key.target_type() + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.key.target_type(ctx) } } @@ -641,10 +641,10 @@ pub struct MapConstructor { } impl MapConstructor { - pub fn target_type(&self) -> Option { + pub fn target_type(&self, ctx: Option<&Context>) -> Option { if let Some(first_field) = self.fields.first() { - let key_type = first_field.key.target_type()?; - let value_type = first_field.value.target_type()?; + let key_type = first_field.key.target_type(ctx)?; + let value_type = first_field.value.target_type(ctx)?; Some(Type::Map(Box::new(key_type), Box::new(value_type))) } else { None @@ -666,8 +666,8 @@ pub struct NegateOp { } impl NegateOp { - pub fn target_type(&self) -> Option { - self.operand.target_type() + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.operand.target_type(ctx) } } @@ -683,8 +683,8 @@ pub struct PropertyOp { } impl PropertyOp { - pub fn target_type(&self) -> Option { - self.property.target_type() + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.property.target_type(ctx) } } @@ -696,8 +696,8 @@ pub struct AddOp { } impl AddOp { - pub fn target_type(&self) -> Option { - self.lhs.target_type() + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.lhs.target_type(ctx) } } @@ -709,8 +709,8 @@ pub struct SubOp { } impl SubOp { - pub fn target_type(&self) -> Option { - self.lhs.target_type() + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.lhs.target_type(ctx) } } @@ -722,8 +722,8 @@ pub struct ConcatOp { } impl ConcatOp { - pub fn target_type(&self) -> Option { - self.lhs.target_type() + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.lhs.target_type(ctx) } } @@ -759,7 +759,9 @@ impl DataExpr { } } - pub fn target_type(&self) -> Option { + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + let default_ctx = Context::default(); + let ctx = ctx.unwrap_or(&default_ctx); match self { DataExpr::Identifier(x) => x.target_type(), DataExpr::None => Some(Type::Undefined), @@ -768,19 +770,19 @@ impl DataExpr { DataExpr::Bool(_) => Some(Type::Bool), DataExpr::String(_) => Some(Type::Bytes), DataExpr::HexString(_) => Some(Type::Bytes), - DataExpr::StructConstructor(x) => x.target_type(), - DataExpr::MapConstructor(x) => x.target_type(), - DataExpr::ListConstructor(x) => match x.target_type() { + DataExpr::StructConstructor(x) => x.target_type(Some(ctx)), + DataExpr::MapConstructor(x) => x.target_type(Some(ctx)), + DataExpr::ListConstructor(x) => match x.target_type(Some(ctx)) { Some(inner) => Some(Type::List(Box::new(inner))), None => None, }, - DataExpr::AddOp(x) => x.target_type(), - DataExpr::SubOp(x) => x.target_type(), - DataExpr::ConcatOp(x) => x.target_type(), - DataExpr::NegateOp(x) => x.target_type(), - DataExpr::PropertyOp(x) => x.target_type(), - DataExpr::StaticAssetConstructor(x) => x.target_type(), - DataExpr::AnyAssetConstructor(x) => x.target_type(), + DataExpr::AddOp(x) => x.target_type(Some(ctx)), + DataExpr::SubOp(x) => x.target_type(Some(ctx)), + DataExpr::ConcatOp(x) => x.target_type(Some(ctx)), + DataExpr::NegateOp(x) => x.target_type(Some(ctx)), + DataExpr::PropertyOp(x) => x.target_type(Some(ctx)), + DataExpr::StaticAssetConstructor(x) => x.target_type(Some(ctx)), + DataExpr::AnyAssetConstructor(x) => x.target_type(Some(ctx)), DataExpr::UtxoRef(_) => Some(Type::UtxoRef), DataExpr::MinUtxo(_) => Some(Type::AnyAsset), DataExpr::ComputeTipSlot => Some(Type::Int), diff --git a/crates/tx3-lang/src/cardano.rs b/crates/tx3-lang/src/cardano.rs index 77cd5861..039b8dab 100644 --- a/crates/tx3-lang/src/cardano.rs +++ b/crates/tx3-lang/src/cardano.rs @@ -90,15 +90,19 @@ impl AstNode for WithdrawalBlock { } impl Analyzable for WithdrawalField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { match self { - WithdrawalField::From(x) => x.analyze(parent), + WithdrawalField::From(x) => x.analyze(parent, ctx), WithdrawalField::Amount(x) => { - let amount = x.analyze(parent.clone()); - let amount_type = AnalyzeReport::expect_data_expr_type(x, &Type::Int); + let amount = x.analyze(parent.clone(), ctx); + let amount_type = AnalyzeReport::expect_data_expr_type(x, &Type::Int, ctx); amount + amount_type } - WithdrawalField::Redeemer(x) => x.analyze(parent), + WithdrawalField::Redeemer(x) => x.analyze(parent, ctx), } } @@ -112,8 +116,12 @@ impl Analyzable for WithdrawalField { } impl Analyzable for WithdrawalBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -204,9 +212,13 @@ impl AstNode for VoteDelegationCertificate { } impl Analyzable for VoteDelegationCertificate { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let drep = self.drep.analyze(parent.clone()); - let stake = self.stake.analyze(parent.clone()); + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + let drep = self.drep.analyze(parent.clone(), ctx); + let stake = self.stake.analyze(parent.clone(), ctx); drep + stake } @@ -260,9 +272,13 @@ impl AstNode for StakeDelegationCertificate { } impl Analyzable for StakeDelegationCertificate { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let pool = self.pool.analyze(parent.clone()); - let stake = self.stake.analyze(parent.clone()); + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + let pool = self.pool.analyze(parent.clone(), ctx); + let stake = self.stake.analyze(parent.clone(), ctx); pool + stake } @@ -329,10 +345,14 @@ impl AstNode for PlutusWitnessField { } impl Analyzable for PlutusWitnessField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { match self { - PlutusWitnessField::Version(x, _) => x.analyze(parent), - PlutusWitnessField::Script(x, _) => x.analyze(parent), + PlutusWitnessField::Version(x, _) => x.analyze(parent, ctx), + PlutusWitnessField::Script(x, _) => x.analyze(parent, ctx), } } @@ -370,8 +390,12 @@ impl AstNode for PlutusWitnessBlock { } impl Analyzable for PlutusWitnessBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -439,9 +463,13 @@ impl AstNode for NativeWitnessField { } impl Analyzable for NativeWitnessField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { match self { - NativeWitnessField::Script(x, _) => x.analyze(parent), + NativeWitnessField::Script(x, _) => x.analyze(parent, ctx), } } @@ -478,8 +506,12 @@ impl AstNode for NativeWitnessBlock { } impl Analyzable for NativeWitnessBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -531,9 +563,13 @@ impl AstNode for TreasuryDonationBlock { } impl Analyzable for TreasuryDonationBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let coin = self.coin.analyze(parent); - let coin_type = AnalyzeReport::expect_data_expr_type(&self.coin, &Type::Int); + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + let coin = self.coin.analyze(parent, ctx); + let coin_type = AnalyzeReport::expect_data_expr_type(&self.coin, &Type::Int, ctx); coin + coin_type } @@ -615,11 +651,15 @@ impl AstNode for CardanoPublishBlockField { } Rule::cardano_publish_block_version => { let pair = pair.into_inner().next().unwrap(); - Ok(CardanoPublishBlockField::Version(DataExpr::parse(pair)?.into())) + Ok(CardanoPublishBlockField::Version( + DataExpr::parse(pair)?.into(), + )) } Rule::cardano_publish_block_script => { let pair = pair.into_inner().next().unwrap(); - Ok(CardanoPublishBlockField::Script(DataExpr::parse(pair)?.into())) + Ok(CardanoPublishBlockField::Script( + DataExpr::parse(pair)?.into(), + )) } x => unreachable!("Unexpected rule in cardano_publish_block_field: {:?}", x), } @@ -656,13 +696,17 @@ impl AstNode for CardanoPublishBlock { } impl Analyzable for CardanoPublishBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { match self { - CardanoPublishBlockField::To(x) => x.analyze(parent), - CardanoPublishBlockField::Amount(x) => x.analyze(parent), - CardanoPublishBlockField::Datum(x) => x.analyze(parent), - CardanoPublishBlockField::Version(x) => x.analyze(parent), - CardanoPublishBlockField::Script(x) => x.analyze(parent), + CardanoPublishBlockField::To(x) => x.analyze(parent, ctx), + CardanoPublishBlockField::Amount(x) => x.analyze(parent, ctx), + CardanoPublishBlockField::Datum(x) => x.analyze(parent, ctx), + CardanoPublishBlockField::Version(x) => x.analyze(parent, ctx), + CardanoPublishBlockField::Script(x) => x.analyze(parent, ctx), } } @@ -678,8 +722,12 @@ impl Analyzable for CardanoPublishBlockField { } impl Analyzable for CardanoPublishBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -782,15 +830,19 @@ impl AstNode for CardanoBlock { } impl Analyzable for CardanoBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { match self { - CardanoBlock::VoteDelegationCertificate(x) => x.analyze(parent), - CardanoBlock::StakeDelegationCertificate(x) => x.analyze(parent), - CardanoBlock::Withdrawal(x) => x.analyze(parent), - CardanoBlock::PlutusWitness(x) => x.analyze(parent), - CardanoBlock::NativeWitness(x) => x.analyze(parent), - CardanoBlock::TreasuryDonation(x) => x.analyze(parent), - CardanoBlock::Publish(x) => x.analyze(parent), + CardanoBlock::VoteDelegationCertificate(x) => x.analyze(parent, ctx), + CardanoBlock::StakeDelegationCertificate(x) => x.analyze(parent, ctx), + CardanoBlock::Withdrawal(x) => x.analyze(parent, ctx), + CardanoBlock::PlutusWitness(x) => x.analyze(parent, ctx), + CardanoBlock::NativeWitness(x) => x.analyze(parent, ctx), + CardanoBlock::TreasuryDonation(x) => x.analyze(parent, ctx), + CardanoBlock::Publish(x) => x.analyze(parent, ctx), } } From 072d35bde93c2c83e13802bc6f9258a65f9bca11 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Tue, 21 Oct 2025 15:55:37 -0300 Subject: [PATCH 11/18] update tests --- examples/lang_tour.my_tx.tir | 6 +- examples/reference_script.publish_native.tir | 36 ++-- examples/reference_script.publish_plutus.tir | 44 ++--- examples/swap.ast | 190 ++++++++----------- examples/swap.swap.tir | 34 +--- examples/swap.tx3 | 12 +- examples/withdrawal.transfer.tir | 6 +- 7 files changed, 136 insertions(+), 192 deletions(-) diff --git a/examples/lang_tour.my_tx.tir b/examples/lang_tour.my_tx.tir index b8b6d70c..e7d90185 100644 --- a/examples/lang_tour.my_tx.tir +++ b/examples/lang_tour.my_tx.tir @@ -665,6 +665,9 @@ { "name": "withdrawal", "data": { + "amount": { + "Number": 100 + }, "credential": { "EvalParam": { "ExpectValue": [ @@ -673,9 +676,6 @@ ] } }, - "amount": { - "Number": 100 - }, "redeemer": { "Struct": { "constructor": 0, diff --git a/examples/reference_script.publish_native.tir b/examples/reference_script.publish_native.tir index 2813897a..9cf434b9 100644 --- a/examples/reference_script.publish_native.tir +++ b/examples/reference_script.publish_native.tir @@ -136,8 +136,21 @@ { "name": "cardano_publish", "data": { - "version": { - "Number": 0 + "amount": { + "Assets": [ + { + "policy": "None", + "asset_name": "None", + "amount": { + "EvalParam": { + "ExpectValue": [ + "quantity", + "Int" + ] + } + } + } + ] }, "to": { "EvalParam": { @@ -147,6 +160,9 @@ ] } }, + "version": { + "Number": 0 + }, "script": { "Bytes": [ 130, @@ -156,22 +172,6 @@ 4, 0 ] - }, - "amount": { - "Assets": [ - { - "policy": "None", - "asset_name": "None", - "amount": { - "EvalParam": { - "ExpectValue": [ - "quantity", - "Int" - ] - } - } - } - ] } } } diff --git a/examples/reference_script.publish_plutus.tir b/examples/reference_script.publish_plutus.tir index 90a5af0e..987e9dad 100644 --- a/examples/reference_script.publish_plutus.tir +++ b/examples/reference_script.publish_plutus.tir @@ -136,13 +136,21 @@ { "name": "cardano_publish", "data": { - "to": { - "EvalParam": { - "ExpectValue": [ - "receiver", - "Address" - ] - } + "amount": { + "Assets": [ + { + "policy": "None", + "asset_name": "None", + "amount": { + "EvalParam": { + "ExpectValue": [ + "quantity", + "Int" + ] + } + } + } + ] }, "script": { "Bytes": [ @@ -166,21 +174,13 @@ 105 ] }, - "amount": { - "Assets": [ - { - "policy": "None", - "asset_name": "None", - "amount": { - "EvalParam": { - "ExpectValue": [ - "quantity", - "Int" - ] - } - } - } - ] + "to": { + "EvalParam": { + "ExpectValue": [ + "receiver", + "Address" + ] + } }, "version": { "Number": 3 diff --git a/examples/swap.ast b/examples/swap.ast index df749c60..7e6c04ab 100644 --- a/examples/swap.ast +++ b/examples/swap.ast @@ -176,8 +176,8 @@ "value": "Buyer", "span": { "dummy": false, - "start": 421, - "end": 426 + "start": 417, + "end": 422 } } } @@ -190,8 +190,8 @@ "value": "fees", "span": { "dummy": false, - "start": 452, - "end": 456 + "start": 444, + "end": 448 } } }, @@ -200,15 +200,15 @@ "value": "bid", "span": { "dummy": false, - "start": 459, - "end": 462 + "start": 451, + "end": 454 } } }, "span": { "dummy": false, - "start": 457, - "end": 458 + "start": 449, + "end": 450 } } } @@ -216,8 +216,8 @@ ], "span": { "dummy": false, - "start": 391, - "end": 469 + "start": 387, + "end": 461 } } ], @@ -231,8 +231,8 @@ "value": "Dex", "span": { "dummy": false, - "start": 500, - "end": 503 + "start": 488, + "end": 491 } } } @@ -244,8 +244,8 @@ "value": "PoolState", "span": { "dummy": false, - "start": 520, - "end": 529 + "start": 508, + "end": 517 } }, "case": { @@ -263,8 +263,8 @@ "value": "pair_a", "span": { "dummy": false, - "start": 544, - "end": 550 + "start": 532, + "end": 538 } }, "value": { @@ -276,8 +276,8 @@ "value": "pool", "span": { "dummy": false, - "start": 552, - "end": 556 + "start": 540, + "end": 544 } } }, @@ -286,58 +286,39 @@ "value": "pair_a", "span": { "dummy": false, - "start": 557, - "end": 563 + "start": 545, + "end": 551 } } }, "span": { "dummy": false, - "start": 556, - "end": 563 + "start": 544, + "end": 551 } } }, "rhs": { - "PropertyOp": { - "operand": { - "Identifier": { - "value": "bid", - "span": { - "dummy": false, - "start": 566, - "end": 569 - } - } - }, - "property": { - "Identifier": { - "value": "amount", - "span": { - "dummy": false, - "start": 570, - "end": 576 - } - } - }, + "Identifier": { + "value": "bid", "span": { "dummy": false, - "start": 569, - "end": 576 + "start": 554, + "end": 557 } } }, "span": { "dummy": false, - "start": 564, - "end": 565 + "start": 552, + "end": 553 } } }, "span": { "dummy": false, - "start": 544, - "end": 576 + "start": 532, + "end": 557 } }, { @@ -345,8 +326,8 @@ "value": "pair_b", "span": { "dummy": false, - "start": 590, - "end": 596 + "start": 571, + "end": 577 } }, "value": { @@ -358,8 +339,8 @@ "value": "pool", "span": { "dummy": false, - "start": 598, - "end": 602 + "start": 579, + "end": 583 } } }, @@ -368,58 +349,39 @@ "value": "pair_b", "span": { "dummy": false, - "start": 603, - "end": 609 + "start": 584, + "end": 590 } } }, "span": { "dummy": false, - "start": 602, - "end": 609 + "start": 583, + "end": 590 } } }, "rhs": { - "PropertyOp": { - "operand": { - "Identifier": { - "value": "ask", - "span": { - "dummy": false, - "start": 612, - "end": 615 - } - } - }, - "property": { - "Identifier": { - "value": "amount", - "span": { - "dummy": false, - "start": 616, - "end": 622 - } - } - }, + "Identifier": { + "value": "ask", "span": { "dummy": false, - "start": 615, - "end": 622 + "start": 593, + "end": 596 } } }, "span": { "dummy": false, - "start": 610, - "end": 611 + "start": 591, + "end": 592 } } }, "span": { "dummy": false, - "start": 590, - "end": 622 + "start": 571, + "end": 596 } } ], @@ -428,21 +390,21 @@ "value": "pool", "span": { "dummy": false, - "start": 639, - "end": 643 + "start": 613, + "end": 617 } } }, "span": { "dummy": false, - "start": 530, - "end": 653 + "start": 518, + "end": 627 } }, "span": { "dummy": false, - "start": 520, - "end": 653 + "start": 508, + "end": 627 } } } @@ -453,8 +415,8 @@ "value": "pool", "span": { "dummy": false, - "start": 671, - "end": 675 + "start": 645, + "end": 649 } } } @@ -462,8 +424,8 @@ ], "span": { "dummy": false, - "start": 479, - "end": 682 + "start": 467, + "end": 656 } }, { @@ -475,8 +437,8 @@ "value": "Buyer", "span": { "dummy": false, - "start": 709, - "end": 714 + "start": 683, + "end": 688 } } } @@ -493,8 +455,8 @@ "value": "payment", "span": { "dummy": false, - "start": 732, - "end": 739 + "start": 706, + "end": 713 } } }, @@ -503,15 +465,15 @@ "value": "ask", "span": { "dummy": false, - "start": 742, - "end": 745 + "start": 716, + "end": 719 } } }, "span": { "dummy": false, - "start": 740, - "end": 741 + "start": 714, + "end": 715 } } }, @@ -520,15 +482,15 @@ "value": "bid", "span": { "dummy": false, - "start": 748, - "end": 751 + "start": 722, + "end": 725 } } }, "span": { "dummy": false, - "start": 746, - "end": 747 + "start": 720, + "end": 721 } } }, @@ -537,15 +499,15 @@ "value": "fees", "span": { "dummy": false, - "start": 754, - "end": 758 + "start": 728, + "end": 732 } } }, "span": { "dummy": false, - "start": 752, - "end": 753 + "start": 726, + "end": 727 } } } @@ -553,8 +515,8 @@ ], "span": { "dummy": false, - "start": 688, - "end": 765 + "start": 662, + "end": 739 } } ], @@ -566,7 +528,7 @@ "span": { "dummy": false, "start": 161, - "end": 767 + "end": 741 }, "collateral": [], "metadata": null @@ -744,6 +706,6 @@ "span": { "dummy": false, "start": 0, - "end": 767 + "end": 742 } } \ No newline at end of file diff --git a/examples/swap.swap.tir b/examples/swap.swap.tir index e89e2c6a..ff102142 100644 --- a/examples/swap.swap.tir +++ b/examples/swap.swap.tir @@ -145,19 +145,10 @@ } }, { - "EvalBuiltIn": { - "Property": [ - { - "EvalParam": { - "ExpectValue": [ - "bid", - "AnyAsset" - ] - } - }, - { - "Number": 0 - } + "EvalParam": { + "ExpectValue": [ + "bid", + "AnyAsset" ] } } @@ -202,19 +193,10 @@ } }, { - "EvalBuiltIn": { - "Property": [ - { - "EvalParam": { - "ExpectValue": [ - "ask", - "AnyAsset" - ] - } - }, - { - "Number": 0 - } + "EvalParam": { + "ExpectValue": [ + "ask", + "AnyAsset" ] } } diff --git a/examples/swap.tx3 b/examples/swap.tx3 index e13e4262..70abff27 100644 --- a/examples/swap.tx3 +++ b/examples/swap.tx3 @@ -25,17 +25,17 @@ tx swap( bid_value: bid, }, } - + input payment { - from: Buyer, + from: Buyer, min_amount: fees + bid, } - + output { to: Dex, datum: PoolState { - pair_a: pool.pair_a - bid.amount, - pair_b: pool.pair_b + ask.amount, + pair_a: pool.pair_a - bid, + pair_b: pool.pair_b + ask, ...pool }, amount: pool, @@ -45,4 +45,4 @@ tx swap( to: Buyer, amount: payment + ask - bid - fees, } -} \ No newline at end of file +} diff --git a/examples/withdrawal.transfer.tir b/examples/withdrawal.transfer.tir index d31757c7..fb2ac831 100644 --- a/examples/withdrawal.transfer.tir +++ b/examples/withdrawal.transfer.tir @@ -163,15 +163,15 @@ { "name": "withdrawal", "data": { - "amount": { - "Number": 0 - }, "redeemer": { "Struct": { "constructor": 0, "fields": [] } }, + "amount": { + "Number": 0 + }, "credential": { "EvalParam": { "ExpectValue": [ From 1dc3613ab4e15d09c365a591a8604dc81f3c5749 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Tue, 21 Oct 2025 16:04:03 -0300 Subject: [PATCH 12/18] update tests --- examples/cardano_witness.mint_from_plutus.tir | 6 +- examples/lang_tour.ast | 898 +++++++++++++----- examples/lang_tour.my_tx.tir | 191 +++- examples/reference_script.publish_native.tir | 42 +- examples/reference_script.publish_plutus.tir | 44 +- 5 files changed, 890 insertions(+), 291 deletions(-) diff --git a/examples/cardano_witness.mint_from_plutus.tir b/examples/cardano_witness.mint_from_plutus.tir index 7f6c09cc..85165ace 100644 --- a/examples/cardano_witness.mint_from_plutus.tir +++ b/examples/cardano_witness.mint_from_plutus.tir @@ -201,9 +201,6 @@ { "name": "plutus_witness", "data": { - "version": { - "Number": 3 - }, "script": { "Bytes": [ 81, @@ -225,6 +222,9 @@ 174, 105 ] + }, + "version": { + "Number": 3 } } } diff --git a/examples/lang_tour.ast b/examples/lang_tour.ast index 812c24be..79807a27 100644 --- a/examples/lang_tour.ast +++ b/examples/lang_tour.ast @@ -18,12 +18,32 @@ "start": 28, "end": 42 } + }, + { + "name": "field_c", + "type": "Bool", + "span": { + "dummy": false, + "start": 48, + "end": 61 + } + }, + { + "name": "field_d", + "type": { + "List": "Bytes" + }, + "span": { + "dummy": false, + "start": 67, + "end": 87 + } } ], "span": { "dummy": false, "start": 0, - "end": 45 + "end": 90 } }, "txs": [ @@ -32,8 +52,8 @@ "value": "my_tx", "span": { "dummy": false, - "start": 466, - "end": 471 + "start": 540, + "end": 545 } }, "parameters": { @@ -43,8 +63,8 @@ "value": "quantity", "span": { "dummy": false, - "start": 477, - "end": 485 + "start": 551, + "end": 559 } }, "type": "Int" @@ -54,8 +74,8 @@ "value": "validUntil", "span": { "dummy": false, - "start": 496, - "end": 506 + "start": 570, + "end": 580 } }, "type": "Int" @@ -65,8 +85,8 @@ "value": "metadata", "span": { "dummy": false, - "start": 517, - "end": 525 + "start": 591, + "end": 599 } }, "type": "Bytes" @@ -74,12 +94,88 @@ ], "span": { "dummy": false, - "start": 471, - "end": 535 + "start": 545, + "end": 609 + } + }, + "locals": { + "assigns": [ + { + "name": { + "value": "local_var", + "span": { + "dummy": false, + "start": 1914, + "end": 1923 + } + }, + "value": { + "ConcatOp": { + "lhs": { + "String": { + "value": "Lang", + "span": { + "dummy": false, + "start": 1932, + "end": 1938 + } + } + }, + "rhs": { + "String": { + "value": "Tour", + "span": { + "dummy": false, + "start": 1940, + "end": 1946 + } + } + }, + "span": { + "dummy": false, + "start": 1925, + "end": 1947 + } + } + }, + "span": { + "dummy": false, + "start": 1914, + "end": 1947 + } + } + ], + "span": { + "dummy": false, + "start": 1897, + "end": 1954 } }, - "locals": null, - "references": [], + "references": [ + { + "name": "ref_block", + "ref": { + "UtxoRef": { + "txid": [ + 171, + 205, + 239 + ], + "index": 2, + "span": { + "dummy": false, + "start": 1220, + "end": 1230 + } + } + }, + "span": { + "dummy": false, + "start": 1185, + "end": 1237 + } + } + ], "inputs": [ { "name": "source", @@ -91,8 +187,8 @@ "value": "MyParty", "span": { "dummy": false, - "start": 571, - "end": 578 + "start": 645, + "end": 652 } } } @@ -118,8 +214,8 @@ "value": "Ada", "span": { "dummy": false, - "start": 628, - "end": 631 + "start": 702, + "end": 705 } }, "amount": { @@ -127,15 +223,15 @@ "value": "quantity", "span": { "dummy": false, - "start": 632, - "end": 640 + "start": 706, + "end": 714 } } }, "span": { "dummy": false, - "start": 628, - "end": 641 + "start": 702, + "end": 715 } } }, @@ -144,15 +240,15 @@ "value": "named_output", "span": { "dummy": false, - "start": 653, - "end": 665 + "start": 727, + "end": 739 } } }, "span": { "dummy": false, - "start": 642, - "end": 643 + "start": 716, + "end": 717 } } } @@ -164,8 +260,8 @@ "value": "MyVariant", "span": { "dummy": false, - "start": 686, - "end": 695 + "start": 760, + "end": 769 } }, "case": { @@ -173,8 +269,8 @@ "value": "Case1", "span": { "dummy": false, - "start": 697, - "end": 702 + "start": 771, + "end": 776 } }, "fields": [ @@ -183,8 +279,8 @@ "value": "field1", "span": { "dummy": false, - "start": 717, - "end": 723 + "start": 791, + "end": 797 } }, "value": { @@ -192,15 +288,15 @@ "value": "field_a", "span": { "dummy": false, - "start": 725, - "end": 732 + "start": 799, + "end": 806 } } }, "span": { "dummy": false, - "start": 717, - "end": 732 + "start": 791, + "end": 806 } }, { @@ -208,8 +304,8 @@ "value": "field2", "span": { "dummy": false, - "start": 746, - "end": 752 + "start": 820, + "end": 826 } }, "value": { @@ -217,15 +313,15 @@ "value": "AFAFAF", "span": { "dummy": false, - "start": 754, - "end": 762 + "start": 828, + "end": 836 } } }, "span": { "dummy": false, - "start": 746, - "end": 762 + "start": 820, + "end": 836 } }, { @@ -233,8 +329,8 @@ "value": "field3", "span": { "dummy": false, - "start": 776, - "end": 782 + "start": 850, + "end": 856 } }, "value": { @@ -242,29 +338,29 @@ "value": "quantity", "span": { "dummy": false, - "start": 784, - "end": 792 + "start": 858, + "end": 866 } } }, "span": { "dummy": false, - "start": 776, - "end": 792 + "start": 850, + "end": 866 } } ], "spread": null, "span": { "dummy": false, - "start": 695, - "end": 803 + "start": 769, + "end": 877 } }, "span": { "dummy": false, - "start": 686, - "end": 803 + "start": 760, + "end": 877 } } } @@ -272,8 +368,8 @@ ], "span": { "dummy": false, - "start": 542, - "end": 810 + "start": 616, + "end": 884 } } ], @@ -283,8 +379,8 @@ "value": "named_output", "span": { "dummy": false, - "start": 996, - "end": 1008 + "start": 1250, + "end": 1262 } }, "fields": [ @@ -294,8 +390,8 @@ "value": "MyParty", "span": { "dummy": false, - "start": 1023, - "end": 1030 + "start": 1277, + "end": 1284 } } } @@ -307,8 +403,8 @@ "value": "MyRecord", "span": { "dummy": false, - "start": 1047, - "end": 1055 + "start": 1301, + "end": 1309 } }, "case": { @@ -326,8 +422,8 @@ "value": "field1", "span": { "dummy": false, - "start": 1070, - "end": 1076 + "start": 1324, + "end": 1330 } }, "value": { @@ -335,15 +431,15 @@ "value": "quantity", "span": { "dummy": false, - "start": 1078, - "end": 1086 + "start": 1332, + "end": 1340 } } }, "span": { "dummy": false, - "start": 1070, - "end": 1086 + "start": 1324, + "end": 1340 } }, { @@ -351,8 +447,8 @@ "value": "field2", "span": { "dummy": false, - "start": 1100, - "end": 1106 + "start": 1354, + "end": 1360 } }, "value": { @@ -367,8 +463,8 @@ }, "span": { "dummy": false, - "start": 1112, - "end": 1113 + "start": 1366, + "end": 1367 } } }, @@ -382,22 +478,22 @@ }, "span": { "dummy": false, - "start": 1123, - "end": 1124 + "start": 1377, + "end": 1378 } } }, "span": { "dummy": false, - "start": 1118, - "end": 1119 + "start": 1372, + "end": 1373 } } }, "span": { "dummy": false, - "start": 1100, - "end": 1127 + "start": 1354, + "end": 1381 } }, { @@ -405,8 +501,8 @@ "value": "field4", "span": { "dummy": false, - "start": 1141, - "end": 1147 + "start": 1395, + "end": 1401 } }, "value": { @@ -428,8 +524,8 @@ "value": "source", "span": { "dummy": false, - "start": 1159, - "end": 1165 + "start": 1413, + "end": 1419 } } }, @@ -438,30 +534,96 @@ "value": "field1", "span": { "dummy": false, - "start": 1166, - "end": 1172 + "start": 1420, + "end": 1426 } } }, "span": { "dummy": false, - "start": 1165, - "end": 1172 + "start": 1419, + "end": 1426 + } + } + } + ], + "span": { + "dummy": false, + "start": 1403, + "end": 1427 + } + } + }, + "span": { + "dummy": false, + "start": 1395, + "end": 1427 + } + }, + { + "name": { + "value": "field5", + "span": { + "dummy": false, + "start": 1441, + "end": 1447 + } + }, + "value": { + "MapConstructor": { + "fields": [ + { + "key": { + "Number": 1 + }, + "value": { + "String": { + "value": "Value1", + "span": { + "dummy": false, + "start": 1453, + "end": 1461 + } } + }, + "span": { + "dummy": false, + "start": 1450, + "end": 1461 + } + }, + { + "key": { + "Number": 2 + }, + "value": { + "String": { + "value": "Value2", + "span": { + "dummy": false, + "start": 1466, + "end": 1474 + } + } + }, + "span": { + "dummy": false, + "start": 1463, + "end": 1474 } } ], "span": { "dummy": false, - "start": 1149, - "end": 1173 + "start": 1449, + "end": 1476 } } }, "span": { "dummy": false, - "start": 1141, - "end": 1173 + "start": 1441, + "end": 1476 } } ], @@ -470,21 +632,21 @@ "value": "source", "span": { "dummy": false, - "start": 1190, - "end": 1196 + "start": 1493, + "end": 1499 } } }, "span": { "dummy": false, - "start": 1056, - "end": 1206 + "start": 1310, + "end": 1509 } }, "span": { "dummy": false, - "start": 1047, - "end": 1206 + "start": 1301, + "end": 1509 } } } @@ -503,8 +665,8 @@ "value": "source", "span": { "dummy": false, - "start": 1233, - "end": 1239 + "start": 1536, + "end": 1542 } } }, @@ -513,15 +675,15 @@ "value": "field3", "span": { "dummy": false, - "start": 1240, - "end": 1246 + "start": 1543, + "end": 1549 } } }, "span": { "dummy": false, - "start": 1239, - "end": 1246 + "start": 1542, + "end": 1549 } } }, @@ -532,8 +694,8 @@ "value": "source", "span": { "dummy": false, - "start": 1248, - "end": 1254 + "start": 1551, + "end": 1557 } } }, @@ -542,15 +704,15 @@ "value": "field2", "span": { "dummy": false, - "start": 1255, - "end": 1261 + "start": 1558, + "end": 1564 } } }, "span": { "dummy": false, - "start": 1254, - "end": 1261 + "start": 1557, + "end": 1564 } } }, @@ -561,8 +723,8 @@ "value": "source", "span": { "dummy": false, - "start": 1263, - "end": 1269 + "start": 1566, + "end": 1572 } } }, @@ -571,22 +733,22 @@ "value": "field1", "span": { "dummy": false, - "start": 1270, - "end": 1276 + "start": 1573, + "end": 1579 } } }, "span": { "dummy": false, - "start": 1269, - "end": 1276 + "start": 1572, + "end": 1579 } } }, "span": { "dummy": false, - "start": 1224, - "end": 1277 + "start": 1527, + "end": 1580 } } }, @@ -596,8 +758,8 @@ "value": "Ada", "span": { "dummy": false, - "start": 1280, - "end": 1283 + "start": 1583, + "end": 1586 } }, "amount": { @@ -605,15 +767,15 @@ }, "span": { "dummy": false, - "start": 1280, - "end": 1287 + "start": 1583, + "end": 1590 } } }, "span": { "dummy": false, - "start": 1278, - "end": 1279 + "start": 1581, + "end": 1582 } } }, @@ -622,15 +784,15 @@ "value": "named_output", "span": { "dummy": false, - "start": 1299, - "end": 1311 + "start": 1602, + "end": 1614 } } }, "span": { "dummy": false, - "start": 1288, - "end": 1289 + "start": 1591, + "end": 1592 } } } @@ -638,17 +800,15 @@ ], "span": { "dummy": false, - "start": 989, - "end": 1319 + "start": 1243, + "end": 1622 } } ], "validity": { "fields": [ { - "SinceSlot": { - "Number": 1735700400000 - } + "SinceSlot": "ComputeTipSlot" }, { "UntilSlot": { @@ -656,8 +816,8 @@ "value": "validUntil", "span": { "dummy": false, - "start": 1497, - "end": 1507 + "start": 1797, + "end": 1807 } } } @@ -665,8 +825,8 @@ ], "span": { "dummy": false, - "start": 1431, - "end": 1514 + "start": 1734, + "end": 1814 } }, "mints": [ @@ -679,8 +839,8 @@ "value": "StaticAsset", "span": { "dummy": false, - "start": 839, - "end": 850 + "start": 913, + "end": 924 } }, "amount": { @@ -688,8 +848,8 @@ }, "span": { "dummy": false, - "start": 839, - "end": 855 + "start": 913, + "end": 929 } } } @@ -700,8 +860,8 @@ ], "span": { "dummy": false, - "start": 816, - "end": 884 + "start": 890, + "end": 958 } }, { @@ -714,8 +874,8 @@ "value": "AB11223344", "span": { "dummy": false, - "start": 922, - "end": 934 + "start": 996, + "end": 1008 } } }, @@ -724,8 +884,8 @@ "value": "OTHER_TOKEN", "span": { "dummy": false, - "start": 936, - "end": 949 + "start": 1010, + "end": 1023 } } }, @@ -734,8 +894,8 @@ }, "span": { "dummy": false, - "start": 913, - "end": 954 + "start": 987, + "end": 1028 } } } @@ -746,12 +906,47 @@ ], "span": { "dummy": false, - "start": 890, - "end": 983 + "start": 964, + "end": 1057 + } + } + ], + "burns": [ + { + "fields": [ + { + "Amount": { + "StaticAssetConstructor": { + "type": { + "value": "StaticAsset", + "span": { + "dummy": false, + "start": 1086, + "end": 1097 + } + }, + "amount": { + "Number": 50 + }, + "span": { + "dummy": false, + "start": 1086, + "end": 1101 + } + } + } + }, + { + "Redeemer": "Unit" + } + ], + "span": { + "dummy": false, + "start": 1063, + "end": 1130 } } ], - "burns": [], "signers": { "signers": [ { @@ -759,8 +954,8 @@ "value": "MyParty", "span": { "dummy": false, - "start": 1343, - "end": 1350 + "start": 1646, + "end": 1653 } } }, @@ -769,25 +964,207 @@ "value": "0F5B22E57FEEB5B4FD1D501B007A427C56A76884D4978FAFEF979D9C", "span": { "dummy": false, - "start": 1360, - "end": 1418 + "start": 1663, + "end": 1721 } } } ], "span": { "dummy": false, - "start": 1325, - "end": 1425 + "start": 1628, + "end": 1728 } }, - "adhoc": [], + "adhoc": [ + { + "Cardano": { + "VoteDelegationCertificate": { + "drep": { + "HexString": { + "value": "12345678", + "span": { + "dummy": false, + "start": 2013, + "end": 2023 + } + } + }, + "stake": { + "HexString": { + "value": "87654321", + "span": { + "dummy": false, + "start": 2040, + "end": 2050 + } + } + }, + "span": { + "dummy": false, + "start": 1969, + "end": 2057 + } + } + } + }, + { + "Cardano": { + "Withdrawal": { + "fields": [ + { + "From": { + "Identifier": { + "value": "MyParty", + "span": { + "dummy": false, + "start": 2099, + "end": 2106 + } + } + } + }, + { + "Amount": { + "Number": 100 + } + }, + { + "Redeemer": "Unit" + } + ], + "span": { + "dummy": false, + "start": 2072, + "end": 2156 + } + } + } + }, + { + "Cardano": { + "PlutusWitness": { + "fields": [ + { + "Version": [ + { + "Number": 2 + }, + { + "dummy": false, + "start": 2196, + "end": 2206 + } + ] + }, + { + "Script": [ + { + "HexString": { + "value": "ABCDEF1234", + "span": { + "dummy": false, + "start": 2224, + "end": 2236 + } + } + }, + { + "dummy": false, + "start": 2216, + "end": 2236 + } + ] + } + ], + "span": { + "dummy": false, + "start": 2171, + "end": 2243 + } + } + } + }, + { + "Cardano": { + "NativeWitness": { + "fields": [ + { + "Script": [ + { + "HexString": { + "value": "12345678", + "span": { + "dummy": false, + "start": 2291, + "end": 2301 + } + } + }, + { + "dummy": false, + "start": 2283, + "end": 2301 + } + ] + } + ], + "span": { + "dummy": false, + "start": 2258, + "end": 2308 + } + } + } + }, + { + "Cardano": { + "TreasuryDonation": { + "coin": { + "Number": 500 + }, + "span": { + "dummy": false, + "start": 2323, + "end": 2367 + } + } + } + } + ], "span": { "dummy": false, - "start": 463, - "end": 1559 + "start": 537, + "end": 2369 }, - "collateral": [], + "collateral": [ + { + "fields": [ + { + "Ref": { + "UtxoRef": { + "txid": [ + 171, + 205, + 239 + ], + "index": 1, + "span": { + "dummy": false, + "start": 1162, + "end": 1172 + } + } + } + } + ], + "span": { + "dummy": false, + "start": 1136, + "end": 1179 + } + } + ], "metadata": { "fields": [ { @@ -799,22 +1176,42 @@ "value": "metadata", "span": { "dummy": false, - "start": 1542, - "end": 1550 + "start": 1842, + "end": 1850 } } }, "span": { "dummy": false, - "start": 1539, - "end": 1550 + "start": 1839, + "end": 1850 + } + }, + { + "key": { + "Number": 2 + }, + "value": { + "String": { + "value": "Additional Metadata", + "span": { + "dummy": false, + "start": 1863, + "end": 1884 + } + } + }, + "span": { + "dummy": false, + "start": 1860, + "end": 1884 } } ], "span": { "dummy": false, - "start": 1520, - "end": 1557 + "start": 1820, + "end": 1891 } } } @@ -825,8 +1222,8 @@ "value": "MyRecord", "span": { "dummy": false, - "start": 68, - "end": 76 + "start": 113, + "end": 121 } }, "cases": [ @@ -845,15 +1242,15 @@ "value": "field1", "span": { "dummy": false, - "start": 83, - "end": 89 + "start": 128, + "end": 134 } }, "type": "Int", "span": { "dummy": false, - "start": 83, - "end": 94 + "start": 128, + "end": 139 } }, { @@ -861,15 +1258,15 @@ "value": "field2", "span": { "dummy": false, - "start": 100, - "end": 106 + "start": 145, + "end": 151 } }, "type": "Bytes", "span": { "dummy": false, - "start": 100, - "end": 113 + "start": 145, + "end": 158 } }, { @@ -877,15 +1274,15 @@ "value": "field3", "span": { "dummy": false, - "start": 119, - "end": 125 + "start": 164, + "end": 170 } }, "type": "Bytes", "span": { "dummy": false, - "start": 119, - "end": 132 + "start": 164, + "end": 177 } }, { @@ -893,8 +1290,8 @@ "value": "field4", "span": { "dummy": false, - "start": 138, - "end": 144 + "start": 183, + "end": 189 } }, "type": { @@ -902,22 +1299,43 @@ }, "span": { "dummy": false, - "start": 138, - "end": 155 + "start": 183, + "end": 200 + } + }, + { + "name": { + "value": "field5", + "span": { + "dummy": false, + "start": 206, + "end": 212 + } + }, + "type": { + "Map": [ + "Int", + "Bytes" + ] + }, + "span": { + "dummy": false, + "start": 206, + "end": 229 } } ], "span": { "dummy": false, - "start": 63, - "end": 158 + "start": 108, + "end": 232 } } ], "span": { "dummy": false, - "start": 63, - "end": 158 + "start": 108, + "end": 232 } }, { @@ -925,8 +1343,8 @@ "value": "MyVariant", "span": { "dummy": false, - "start": 165, - "end": 174 + "start": 239, + "end": 248 } }, "cases": [ @@ -935,8 +1353,8 @@ "value": "Case1", "span": { "dummy": false, - "start": 181, - "end": 186 + "start": 255, + "end": 260 } }, "fields": [ @@ -945,15 +1363,15 @@ "value": "field1", "span": { "dummy": false, - "start": 197, - "end": 203 + "start": 271, + "end": 277 } }, "type": "Int", "span": { "dummy": false, - "start": 197, - "end": 208 + "start": 271, + "end": 282 } }, { @@ -961,15 +1379,15 @@ "value": "field2", "span": { "dummy": false, - "start": 218, - "end": 224 + "start": 292, + "end": 298 } }, "type": "Bytes", "span": { "dummy": false, - "start": 218, - "end": 231 + "start": 292, + "end": 305 } }, { @@ -977,22 +1395,22 @@ "value": "field3", "span": { "dummy": false, - "start": 241, - "end": 247 + "start": 315, + "end": 321 } }, "type": "Int", "span": { "dummy": false, - "start": 241, - "end": 252 + "start": 315, + "end": 326 } } ], "span": { "dummy": false, - "start": 181, - "end": 259 + "start": 255, + "end": 333 } }, { @@ -1000,22 +1418,22 @@ "value": "Case2", "span": { "dummy": false, - "start": 265, - "end": 270 + "start": 339, + "end": 344 } }, "fields": [], "span": { "dummy": false, - "start": 265, - "end": 270 + "start": 339, + "end": 344 } } ], "span": { "dummy": false, - "start": 160, - "end": 273 + "start": 234, + "end": 347 } } ], @@ -1026,8 +1444,8 @@ "value": "StaticAsset", "span": { "dummy": false, - "start": 320, - "end": 331 + "start": 394, + "end": 405 } }, "policy": { @@ -1035,8 +1453,8 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 334, - "end": 346 + "start": 408, + "end": 420 } } }, @@ -1045,15 +1463,15 @@ "value": "MYTOKEN", "span": { "dummy": false, - "start": 347, - "end": 356 + "start": 421, + "end": 430 } } }, "span": { "dummy": false, - "start": 314, - "end": 357 + "start": 388, + "end": 431 } } ], @@ -1063,14 +1481,14 @@ "value": "MyParty", "span": { "dummy": false, - "start": 53, - "end": 60 + "start": 98, + "end": 105 } }, "span": { "dummy": false, - "start": 47, - "end": 61 + "start": 92, + "end": 106 } } ], @@ -1080,8 +1498,8 @@ "value": "OnlyHashPolicy", "span": { "dummy": false, - "start": 282, - "end": 296 + "start": 356, + "end": 370 } }, "value": { @@ -1089,15 +1507,15 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 299, - "end": 311 + "start": 373, + "end": 385 } } }, "span": { "dummy": false, - "start": 275, - "end": 312 + "start": 349, + "end": 386 } }, { @@ -1105,8 +1523,8 @@ "value": "FullyDefinedPolicy", "span": { "dummy": false, - "start": 366, - "end": 384 + "start": 440, + "end": 458 } }, "value": { @@ -1118,8 +1536,8 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 397, - "end": 409 + "start": 471, + "end": 483 } } } @@ -1130,8 +1548,8 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 423, - "end": 435 + "start": 497, + "end": 509 } } } @@ -1142,8 +1560,8 @@ "value": "ABCDEF1234", "span": { "dummy": false, - "start": 446, - "end": 458 + "start": 520, + "end": 532 } } } @@ -1151,21 +1569,21 @@ ], "span": { "dummy": false, - "start": 385, - "end": 461 + "start": 459, + "end": 535 } } }, "span": { "dummy": false, - "start": 359, - "end": 461 + "start": 433, + "end": 535 } } ], "span": { "dummy": false, "start": 0, - "end": 1560 + "end": 2370 } } \ No newline at end of file diff --git a/examples/lang_tour.my_tx.tir b/examples/lang_tour.my_tx.tir index c1ed8b7e..8efb3640 100644 --- a/examples/lang_tour.my_tx.tir +++ b/examples/lang_tour.my_tx.tir @@ -2,7 +2,20 @@ "fees": { "EvalParam": "ExpectFees" }, - "references": [], + "references": [ + { + "UtxoRefs": [ + { + "txid": [ + 171, + 205, + 239 + ], + "index": 2 + } + ] + } + ], "inputs": [ { "name": "source", @@ -275,6 +288,26 @@ } } ] + }, + { + "Map": [ + [ + { + "Number": 1 + }, + { + "String": "Value1" + } + ], + [ + { + "Number": 2 + }, + { + "String": "Value2" + } + ] + ] } ] } @@ -505,7 +538,7 @@ ], "validity": { "since": { - "Number": 1735700400000 + "EvalCompiler": "ComputeTipSlot" }, "until": { "EvalParam": { @@ -576,9 +609,149 @@ } } ], - "burns": [], - "adhoc": [], - "collateral": [], + "burns": [ + { + "amount": { + "Assets": [ + { + "policy": { + "Bytes": [ + 171, + 205, + 239, + 18, + 52 + ] + }, + "asset_name": { + "String": "MYTOKEN" + }, + "amount": { + "Number": 50 + } + } + ] + }, + "redeemer": { + "Struct": { + "constructor": 0, + "fields": [] + } + } + } + ], + "adhoc": [ + { + "name": "vote_delegation_certificate", + "data": { + "stake": { + "Bytes": [ + 135, + 101, + 67, + 33 + ] + }, + "drep": { + "Bytes": [ + 18, + 52, + 86, + 120 + ] + } + } + }, + { + "name": "withdrawal", + "data": { + "credential": { + "EvalParam": { + "ExpectValue": [ + "myparty", + "Address" + ] + } + }, + "amount": { + "Number": 100 + }, + "redeemer": { + "Struct": { + "constructor": 0, + "fields": [] + } + } + } + }, + { + "name": "plutus_witness", + "data": { + "version": { + "Number": 2 + }, + "script": { + "Bytes": [ + 171, + 205, + 239, + 18, + 52 + ] + } + } + }, + { + "name": "native_witness", + "data": { + "script": { + "Bytes": [ + 18, + 52, + 86, + 120 + ] + } + } + }, + { + "name": "treasury_donation", + "data": { + "coin": { + "Number": 500 + } + } + } + ], + "collateral": [ + { + "utxos": { + "EvalParam": { + "ExpectInput": [ + "collateral", + { + "address": "None", + "min_amount": "None", + "ref": { + "UtxoRefs": [ + { + "txid": [ + 171, + 205, + 239 + ], + "index": 1 + } + ] + }, + "many": false, + "collateral": true + } + ] + } + } + } + ], "signers": { "signers": [ { @@ -636,6 +809,14 @@ ] } } + }, + { + "key": { + "Number": 2 + }, + "value": { + "String": "Additional Metadata" + } } ] } \ No newline at end of file diff --git a/examples/reference_script.publish_native.tir b/examples/reference_script.publish_native.tir index 3beadd57..6c2349db 100644 --- a/examples/reference_script.publish_native.tir +++ b/examples/reference_script.publish_native.tir @@ -136,6 +136,27 @@ { "name": "cardano_publish", "data": { + "version": { + "Number": 0 + }, + "script": { + "Bytes": [ + 130, + 1, + 129, + 130, + 4, + 0 + ] + }, + "to": { + "EvalParam": { + "ExpectValue": [ + "receiver", + "Address" + ] + } + }, "amount": { "Assets": [ { @@ -151,27 +172,6 @@ } } ] - }, - "to": { - "EvalParam": { - "ExpectValue": [ - "receiver", - "Address" - ] - } - }, - "script": { - "Bytes": [ - 130, - 1, - 129, - 130, - 4, - 0 - ] - }, - "version": { - "Number": 0 } } } diff --git a/examples/reference_script.publish_plutus.tir b/examples/reference_script.publish_plutus.tir index b2562f35..f67d9fc1 100644 --- a/examples/reference_script.publish_plutus.tir +++ b/examples/reference_script.publish_plutus.tir @@ -136,28 +136,6 @@ { "name": "cardano_publish", "data": { - "script": { - "Bytes": [ - 81, - 1, - 1, - 0, - 35, - 37, - 152, - 0, - 165, - 24, - 164, - 209, - 54, - 86, - 64, - 4, - 174, - 105 - ] - }, "version": { "Number": 3 }, @@ -184,6 +162,28 @@ "Address" ] } + }, + "script": { + "Bytes": [ + 81, + 1, + 1, + 0, + 35, + 37, + 152, + 0, + 165, + 24, + 164, + 209, + 54, + 86, + 64, + 4, + 174, + 105 + ] } } } From f37820d84bc92e5173db76ad66e1f99afc9cf7c2 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Wed, 22 Oct 2025 13:19:38 -0300 Subject: [PATCH 13/18] update analyze methods to include Context --- crates/tx3-lang/src/analyzing.rs | 31 ++++++++++++++++++++++--------- crates/tx3-lang/src/ast.rs | 28 ++++++++++++++++++++++++---- crates/tx3-lang/src/lowering.rs | 2 +- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index 2a5d6517..d9369072 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -728,18 +728,18 @@ impl Analyzable for AnyAssetConstructor { } impl Analyzable for PropertyOp { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let object = self.operand.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let object = self.operand.analyze(parent.clone(), ctx); let mut scope = Scope::new(parent); - if let Some(ty) = self.operand.target_type() { + if let Some(ty) = self.operand.target_type(None) { scope.track_record_fields_for_type(&ty); } self.scope = Some(Rc::new(scope)); - let path = self.property.analyze(self.scope.clone()); + let path = self.property.analyze(self.scope.clone(), ctx); object + path } @@ -901,15 +901,28 @@ impl Analyzable for ValidityBlock { } impl Analyzable for OutputBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - OutputBlockField::To(x) => x.analyze(parent), + OutputBlockField::To(x) => x.analyze(parent, ctx), OutputBlockField::Amount(x) => { - let expr_report = x.analyze(parent); - let type_report = AnalyzeReport::expect_data_expr_type(x, &Type::AnyAsset); + ctx.target_type.push(Type::AnyAsset); + + let expr_report = x.analyze(parent, ctx); + let ty = x.target_type(Some(ctx)); + + let type_report = if !matches!(ty.as_ref(), Some(&Type::AnyAsset)) { + AnalyzeReport::from(Error::invalid_target_type( + &Type::AnyAsset, + ty.as_ref().unwrap_or(&Type::Undefined), + x.as_ref(), + )) + } else { + AnalyzeReport::default() + }; + expr_report + type_report } - OutputBlockField::Datum(x) => x.analyze(parent), + OutputBlockField::Datum(x) => x.analyze(parent, ctx), } } diff --git a/crates/tx3-lang/src/ast.rs b/crates/tx3-lang/src/ast.rs index eca894c3..3cb95db1 100644 --- a/crates/tx3-lang/src/ast.rs +++ b/crates/tx3-lang/src/ast.rs @@ -120,11 +120,22 @@ impl Symbol { } } - pub fn target_type(&self) -> Option { + pub fn target_type(&self, ctx: Option<&Context>) -> Option { match self { Symbol::ParamVar(_, ty) => Some(ty.as_ref().clone()), Symbol::RecordField(x) => Some(x.r#type.clone()), - Symbol::Input(x) => x.datum_is().cloned(), + Symbol::Input(x) => { + let datum_type = x.datum_is().cloned(); + + match ctx { + Some(ctx) if ctx.target_type.last() == Some(&Type::AnyAsset) => { + Some(Type::AnyAsset) + } + _ => datum_type, + } + } + Symbol::LocalExpr(expr) => expr.target_type(ctx), + Symbol::Fees => Some(Type::AnyAsset), x => { dbg!(x); None @@ -763,7 +774,16 @@ impl DataExpr { let default_ctx = Context::default(); let ctx = ctx.unwrap_or(&default_ctx); match self { - DataExpr::Identifier(x) => x.target_type(), + DataExpr::Identifier(x) => match &x.symbol { + Some(Symbol::Input(def)) => { + if ctx.target_type.last() == Some(&Type::AnyAsset) { + Some(Type::AnyAsset) + } else { + def.datum_is().cloned() + } + } + _ => x.target_type(Some(ctx)), + }, DataExpr::None => Some(Type::Undefined), DataExpr::Unit => Some(Type::Unit), DataExpr::Number(_) => Some(Type::Int), @@ -884,7 +904,7 @@ impl Type { .map(|index| DataExpr::Number(index as i64)) } Type::List(_) => property - .target_type() + .target_type(None) .filter(|ty| *ty == Type::Int) .map(|_| property), _ => None, diff --git a/crates/tx3-lang/src/lowering.rs b/crates/tx3-lang/src/lowering.rs index 1f0b189b..c7a5cb02 100644 --- a/crates/tx3-lang/src/lowering.rs +++ b/crates/tx3-lang/src/lowering.rs @@ -414,7 +414,7 @@ impl IntoLower for ast::PropertyOp { let ty = self .operand - .target_type() + .target_type(None) .ok_or(Error::MissingAnalyzePhase(format!("{0:?}", self.operand)))?; let prop_index = From f5012f4a7a73e4a45a4c2736b5b42a16f7f280cb Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Wed, 22 Oct 2025 13:56:16 -0300 Subject: [PATCH 14/18] refactor: change target_type from Vec to Type in Context --- crates/tx3-lang/src/analyzing.rs | 6 +++--- crates/tx3-lang/src/ast.rs | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index d9369072..e686b45d 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -11,13 +11,13 @@ use crate::ast::*; #[derive(Debug, Clone)] pub struct Context { - pub target_type: Vec, + pub target_type: Type, } impl Default for Context { fn default() -> Self { Self { - target_type: vec![], + target_type: Type::Undefined, } } } @@ -905,7 +905,7 @@ impl Analyzable for OutputBlockField { match self { OutputBlockField::To(x) => x.analyze(parent, ctx), OutputBlockField::Amount(x) => { - ctx.target_type.push(Type::AnyAsset); + ctx.target_type = Type::AnyAsset; let expr_report = x.analyze(parent, ctx); let ty = x.target_type(Some(ctx)); diff --git a/crates/tx3-lang/src/ast.rs b/crates/tx3-lang/src/ast.rs index 3cb95db1..f0dd3757 100644 --- a/crates/tx3-lang/src/ast.rs +++ b/crates/tx3-lang/src/ast.rs @@ -128,9 +128,7 @@ impl Symbol { let datum_type = x.datum_is().cloned(); match ctx { - Some(ctx) if ctx.target_type.last() == Some(&Type::AnyAsset) => { - Some(Type::AnyAsset) - } + Some(ctx) if ctx.target_type == Type::AnyAsset => Some(Type::AnyAsset), _ => datum_type, } } @@ -776,7 +774,7 @@ impl DataExpr { match self { DataExpr::Identifier(x) => match &x.symbol { Some(Symbol::Input(def)) => { - if ctx.target_type.last() == Some(&Type::AnyAsset) { + if ctx.target_type == Type::AnyAsset { Some(Type::AnyAsset) } else { def.datum_is().cloned() From 9347b279d22765ed9c63935d932c08b92017be6f Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Wed, 22 Oct 2025 14:06:57 -0300 Subject: [PATCH 15/18] refactor: rename InvalidOutputDatum to InvalidOptionalOutput for clarity --- crates/tx3-lang/src/analyzing.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index ef95f50c..e352398f 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -88,7 +88,7 @@ pub enum Error { #[error(transparent)] #[diagnostic(transparent)] - InvalidOutputDatum(#[from] OptionalOutputError), + InvalidOptionalOutput(#[from] OptionalOutputError), } impl Error { @@ -97,7 +97,7 @@ impl Error { Self::NotInScope(x) => &x.span, Self::InvalidSymbol(x) => &x.span, Self::InvalidTargetType(x) => &x.span, - Self::InvalidOutputDatum(x) => &x.span, + Self::InvalidOptionalOutput(x) => &x.span, _ => &Span::DUMMY, } } @@ -908,7 +908,7 @@ impl Analyzable for OutputBlock { fn analyze(&mut self, parent: Option>) -> AnalyzeReport { if self.optional { if let Some(_field) = self.find("datum") { - return AnalyzeReport::from(Error::InvalidOutputDatum(OptionalOutputError { + return AnalyzeReport::from(Error::InvalidOptionalOutput(OptionalOutputError { name: self .name .as_ref() @@ -1459,7 +1459,7 @@ mod tests { assert!(report .errors .iter() - .any(|e| matches!(e, Error::InvalidOutputDatum(_)))); + .any(|e| matches!(e, Error::InvalidOptionalOutput(_)))); } #[test] From e156d092ba1a58a3a82af8258b06d334cef7a108 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Wed, 22 Oct 2025 15:09:14 -0300 Subject: [PATCH 16/18] refactor: streamline output validation for optional outputs --- crates/tx3-cardano/src/compile/mod.rs | 35 ++++++++++++------------- crates/tx3-lang/src/analyzing.rs | 37 ++++++++++++++++----------- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/crates/tx3-cardano/src/compile/mod.rs b/crates/tx3-cardano/src/compile/mod.rs index 932c1bfb..fcf441c2 100644 --- a/crates/tx3-cardano/src/compile/mod.rs +++ b/crates/tx3-cardano/src/compile/mod.rs @@ -272,28 +272,27 @@ fn compile_inputs(tx: &ir::Tx) -> Result, Erro Ok(refs) } +fn output_has_assets(output: &Result, Error>) -> bool { + match output { + Ok(primitives::TransactionOutput::PostAlonzo(post_alonzo)) => match &post_alonzo.value { + primitives::Value::Coin(amount) => *amount > 0, + primitives::Value::Multiasset(coin, multiasset) => *coin > 0 || !multiasset.is_empty(), + }, + _ => true, + } +} + fn compile_outputs( tx: &ir::Tx, network: Network, ) -> Result>, Error> { - let mut resolved = Vec::new(); - - for out in &tx.outputs { - let asset_list = coercion::expr_into_assets(&out.amount)?; - - let total: i128 = asset_list - .iter() - .map(|asset| coercion::expr_into_number(&asset.amount)) - .collect::, _>>()? - .into_iter() - .fold(0i128, |acc, n| acc.saturating_add(n)); - - if out.optional && total == 0 { - continue; - } - - resolved.push(compile_output_block(out, network)?); - } + let mut resolved: Vec<_> = tx + .outputs + .iter() + .map(|out| (out.optional, compile_output_block(out, network))) + .filter(|(optional, output)| !optional || output_has_assets(output)) + .map(|(_, output)| output) + .collect::, _>>()?; let cardano_outputs = tx .adhoc diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index e352398f..04dc27b0 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -906,21 +906,10 @@ impl Analyzable for OutputBlockField { impl Analyzable for OutputBlock { fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - if self.optional { - if let Some(_field) = self.find("datum") { - return AnalyzeReport::from(Error::InvalidOptionalOutput(OptionalOutputError { - name: self - .name - .as_ref() - .map(|i| i.value.clone()) - .unwrap_or_else(|| "".to_string()), - src: None, - span: self.span.clone(), - })); - } - } - - self.fields.analyze(parent) + validate_optional_output(self) + .map(AnalyzeReport::from) + .unwrap_or_else(|| AnalyzeReport::default()) + + self.fields.analyze(parent) } fn is_resolved(&self) -> bool { @@ -928,6 +917,24 @@ impl Analyzable for OutputBlock { } } +fn validate_optional_output(output: &OutputBlock) -> Option { + if output.optional { + if let Some(_field) = output.find("datum") { + return Some(Error::InvalidOptionalOutput(OptionalOutputError { + name: output + .name + .as_ref() + .map(|i| i.value.clone()) + .unwrap_or_else(|| "".to_string()), + src: None, + span: output.span.clone(), + })); + } + } + + None +} + impl Analyzable for RecordField { fn analyze(&mut self, parent: Option>) -> AnalyzeReport { self.r#type.analyze(parent) From 8b396bb806130a5b6d674e96b47af3bc78e44147 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Fri, 24 Oct 2025 15:39:49 -0300 Subject: [PATCH 17/18] feat: add validation rules for semantic analysis --- crates/tx3-lang/src/analyzing.rs | 23 +++-------------------- crates/tx3-lang/src/lib.rs | 1 + crates/tx3-lang/src/rules.rs | 24 ++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 20 deletions(-) create mode 100644 crates/tx3-lang/src/rules.rs diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index db6b0c00..06234c79 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -8,6 +8,7 @@ use std::{collections::HashMap, rc::Rc}; use miette::Diagnostic; use crate::ast::*; +use crate::rules::validate_optional_output; #[derive(Debug, Clone)] pub struct Context { @@ -70,10 +71,10 @@ pub struct OptionalOutputError { pub name: String, #[source_code] - src: Option, + pub src: Option, #[label] - span: Span, + pub span: Span, } #[derive(thiserror::Error, Debug, miette::Diagnostic, PartialEq, Eq)] @@ -968,24 +969,6 @@ impl Analyzable for OutputBlock { } } -fn validate_optional_output(output: &OutputBlock) -> Option { - if output.optional { - if let Some(_field) = output.find("datum") { - return Some(Error::InvalidOptionalOutput(OptionalOutputError { - name: output - .name - .as_ref() - .map(|i| i.value.clone()) - .unwrap_or_else(|| "".to_string()), - src: None, - span: output.span.clone(), - })); - } - } - - None -} - impl Analyzable for RecordField { fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { self.r#type.analyze(parent, ctx) diff --git a/crates/tx3-lang/src/lib.rs b/crates/tx3-lang/src/lib.rs index c991b075..4c715676 100644 --- a/crates/tx3-lang/src/lib.rs +++ b/crates/tx3-lang/src/lib.rs @@ -33,6 +33,7 @@ pub mod ir; pub mod loading; pub mod lowering; pub mod parsing; +pub mod rules; // chain specific pub mod cardano; diff --git a/crates/tx3-lang/src/rules.rs b/crates/tx3-lang/src/rules.rs new file mode 100644 index 00000000..a68c5f70 --- /dev/null +++ b/crates/tx3-lang/src/rules.rs @@ -0,0 +1,24 @@ +//! Validation rules for semantic analysis. +//! +//! This module contains helper functions that implement specific validation +//! rules for the Tx3 language. These rules are used by the semantic analyzer +//! to check constraints and produce appropriate errors. + +use crate::analyzing::{Error, OptionalOutputError}; +use crate::ast::OutputBlock; + +pub fn validate_optional_output(output: &OutputBlock) -> Option { + if output.optional && output.find("datum").is_some() { + return Some(Error::InvalidOptionalOutput(OptionalOutputError { + name: output + .name + .as_ref() + .map(|i| i.value.clone()) + .unwrap_or_else(|| "".to_string()), + src: None, + span: output.span.clone(), + })); + } + + None +} From 48f718c3269e5b12ed168d1e311bda98b2f03942 Mon Sep 17 00:00:00 2001 From: sofia-bobbiesi Date: Fri, 24 Oct 2025 16:42:52 -0300 Subject: [PATCH 18/18] feat: add metadata validation rules for key type and value size --- crates/tx3-lang/src/analyzing.rs | 85 ++++---------------------------- crates/tx3-lang/src/rules.rs | 77 ++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 78 deletions(-) diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index 1f311884..7de360cd 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -8,7 +8,9 @@ use std::{collections::HashMap, rc::Rc}; use miette::Diagnostic; use crate::ast::*; -use crate::rules::validate_optional_output; +use crate::rules::{ + validate_metadata_key_type, validate_metadata_value_size, validate_optional_output, +}; #[derive(Debug, Clone)] pub struct Context { @@ -24,8 +26,6 @@ impl Default for Context { } use crate::parsing::AstNode; -const METADATA_MAX_SIZE_BYTES: usize = 64; - #[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq)] #[error("not in scope: {name}")] #[diagnostic(code(tx3::not_in_scope))] @@ -86,10 +86,10 @@ pub struct MetadataSizeLimitError { pub size: usize, #[source_code] - src: Option, + pub src: Option, #[label("value too large")] - span: Span, + pub span: Span, } #[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq)] @@ -99,10 +99,10 @@ pub struct MetadataInvalidKeyTypeError { pub key_type: String, #[source_code] - src: Option, + pub src: Option, #[label("expected integer key")] - span: Span, + pub span: Span, } #[derive(thiserror::Error, Debug, miette::Diagnostic, PartialEq, Eq)] @@ -915,80 +915,13 @@ impl Analyzable for InputBlock { } } -fn validate_metadata_value_size(expr: &DataExpr) -> Result<(), MetadataSizeLimitError> { - match expr { - DataExpr::String(string_literal) => { - let utf8_bytes = string_literal.value.as_bytes(); - if utf8_bytes.len() > METADATA_MAX_SIZE_BYTES { - return Err(MetadataSizeLimitError { - size: utf8_bytes.len(), - src: None, - span: string_literal.span.clone(), - }); - } - } - DataExpr::HexString(hex_literal) => { - let hex_str = &hex_literal.value; - let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str); - let byte_length = hex_str.len() / 2; - - if byte_length > METADATA_MAX_SIZE_BYTES { - return Err(MetadataSizeLimitError { - size: byte_length, - src: None, - span: hex_literal.span.clone(), - }); - } - } - _ => {} - } - Ok(()) -} - -fn validate_metadata_key_type( - expr: &DataExpr, - ctx: &mut Context, -) -> Result<(), MetadataInvalidKeyTypeError> { - match expr { - DataExpr::Number(_) => Ok(()), - DataExpr::Identifier(id) => match id.target_type(Some(ctx)) { - Some(Type::Int) => Ok(()), - Some(other_type) => Err(MetadataInvalidKeyTypeError { - key_type: format!("identifier of type {}", other_type), - src: None, - span: id.span().clone(), - }), - None => Err(MetadataInvalidKeyTypeError { - key_type: "unresolved identifier".to_string(), - src: None, - span: id.span().clone(), - }), - }, - _ => { - let key_type = match expr { - DataExpr::String(_) => "string", - DataExpr::HexString(_) => "hex string", - DataExpr::ListConstructor(_) => "list", - DataExpr::MapConstructor(_) => "map", - DataExpr::StructConstructor(_) => "struct", - _ => "unknown", - }; - - Err(MetadataInvalidKeyTypeError { - key_type: key_type.to_string(), - src: None, - span: expr.span().clone(), - }) - } - } -} - impl Analyzable for MetadataBlockField { fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { let mut report = self.key.analyze(parent.clone(), ctx) + self.value.analyze(parent.clone(), ctx); - validate_metadata_key_type(&self.key, ctx) + let key_type = self.key.target_type(Some(ctx)); + validate_metadata_key_type(&self.key, key_type.as_ref()) .map_err(Error::MetadataInvalidKeyType) .err() .map(|e| report.errors.push(e)); diff --git a/crates/tx3-lang/src/rules.rs b/crates/tx3-lang/src/rules.rs index a68c5f70..fc35a086 100644 --- a/crates/tx3-lang/src/rules.rs +++ b/crates/tx3-lang/src/rules.rs @@ -4,8 +4,13 @@ //! rules for the Tx3 language. These rules are used by the semantic analyzer //! to check constraints and produce appropriate errors. -use crate::analyzing::{Error, OptionalOutputError}; -use crate::ast::OutputBlock; +use crate::analyzing::{ + Error, MetadataInvalidKeyTypeError, MetadataSizeLimitError, OptionalOutputError, +}; +use crate::ast::{DataExpr, OutputBlock, Type}; +use crate::parsing::AstNode; + +const METADATA_MAX_SIZE_BYTES: usize = 64; pub fn validate_optional_output(output: &OutputBlock) -> Option { if output.optional && output.find("datum").is_some() { @@ -22,3 +27,71 @@ pub fn validate_optional_output(output: &OutputBlock) -> Option { None } + +pub fn validate_metadata_value_size(expr: &DataExpr) -> Result<(), MetadataSizeLimitError> { + match expr { + DataExpr::String(string_literal) => { + let utf8_bytes = string_literal.value.as_bytes(); + if utf8_bytes.len() > METADATA_MAX_SIZE_BYTES { + return Err(MetadataSizeLimitError { + size: utf8_bytes.len(), + src: None, + span: string_literal.span.clone(), + }); + } + } + DataExpr::HexString(hex_literal) => { + let hex_str = &hex_literal.value; + let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str); + let byte_length = hex_str.len() / 2; + + if byte_length > METADATA_MAX_SIZE_BYTES { + return Err(MetadataSizeLimitError { + size: byte_length, + src: None, + span: hex_literal.span.clone(), + }); + } + } + _ => {} + } + Ok(()) +} + +pub fn validate_metadata_key_type( + expr: &DataExpr, + target_type: Option<&Type>, +) -> Result<(), MetadataInvalidKeyTypeError> { + match expr { + DataExpr::Number(_) => Ok(()), + DataExpr::Identifier(id) => match target_type { + Some(Type::Int) => Ok(()), + Some(other_type) => Err(MetadataInvalidKeyTypeError { + key_type: format!("identifier of type {}", other_type), + src: None, + span: id.span().clone(), + }), + None => Err(MetadataInvalidKeyTypeError { + key_type: "unresolved identifier".to_string(), + src: None, + span: id.span().clone(), + }), + }, + _ => { + let key_type = match expr { + DataExpr::String(_) => "string", + DataExpr::HexString(_) => "hex string", + DataExpr::ListConstructor(_) => "list", + DataExpr::MapConstructor(_) => "map", + DataExpr::StructConstructor(_) => "struct", + _ => "unknown", + }; + + Err(MetadataInvalidKeyTypeError { + key_type: key_type.to_string(), + src: None, + span: expr.span().clone(), + }) + } + } +}