diff --git a/crates/tx3-cardano/src/lib.rs b/crates/tx3-cardano/src/lib.rs index d8128b9b..322b1848 100644 --- a/crates/tx3-cardano/src/lib.rs +++ b/crates/tx3-cardano/src/lib.rs @@ -42,18 +42,26 @@ pub struct Config { pub type TxBody = pallas::codec::utils::KeepRaw<'static, primitives::conway::TransactionBody<'static>>; +#[derive(Debug, Clone)] +pub struct ChainPoint { + pub slot: u64, + pub hash: Vec, +} + pub struct Compiler { pub pparams: PParams, pub config: Config, pub latest_tx_body: Option, + pub cursor: ChainPoint, } impl Compiler { - pub fn new(pparams: PParams, config: Config) -> Self { + pub fn new(pparams: PParams, config: Config, cursor: ChainPoint) -> Self { Self { pparams, config, latest_tx_body: None, + cursor, } } } @@ -100,6 +108,7 @@ impl tx3_lang::backend::Compiler for Compiler { amount: ir::Expression::Number(lovelace), }])) } + ir::CompilerOp::ComputeTipSlot => Ok(ir::Expression::Number(self.cursor.slot as i128)), } } } diff --git a/crates/tx3-cardano/src/tests.rs b/crates/tx3-cardano/src/tests.rs index fc07e628..8aaf2f44 100644 --- a/crates/tx3-cardano/src/tests.rs +++ b/crates/tx3-cardano/src/tests.rs @@ -48,7 +48,14 @@ fn test_compiler(config: Option) -> Compiler { let config = config.unwrap_or(Config { extra_fees: None }); - Compiler::new(pparams, config) + Compiler::new( + pparams, + config, + ChainPoint { + slot: 101674141, + hash: vec![], + }, + ) } fn load_protocol(example_name: &str) -> Protocol { diff --git a/crates/tx3-lang/src/applying.rs b/crates/tx3-lang/src/applying.rs index f7ed4dcf..cd67e8b8 100644 --- a/crates/tx3-lang/src/applying.rs +++ b/crates/tx3-lang/src/applying.rs @@ -1150,6 +1150,7 @@ impl Composite for ir::CompilerOp { match self { ir::CompilerOp::BuildScriptAddress(x) => vec![x], ir::CompilerOp::ComputeMinUtxo(x) => vec![x], + ir::CompilerOp::ComputeTipSlot => vec![], } } @@ -1160,6 +1161,7 @@ impl Composite for ir::CompilerOp { match self { ir::CompilerOp::BuildScriptAddress(x) => Ok(ir::CompilerOp::BuildScriptAddress(f(x)?)), ir::CompilerOp::ComputeMinUtxo(x) => Ok(ir::CompilerOp::ComputeMinUtxo(f(x)?)), + ir::CompilerOp::ComputeTipSlot => Ok(ir::CompilerOp::ComputeTipSlot), } } } diff --git a/crates/tx3-lang/src/ast.rs b/crates/tx3-lang/src/ast.rs index 62eb40d9..9843446d 100644 --- a/crates/tx3-lang/src/ast.rs +++ b/crates/tx3-lang/src/ast.rs @@ -699,6 +699,7 @@ pub enum DataExpr { AnyAssetConstructor(AnyAssetConstructor), Identifier(Identifier), MinUtxo(Identifier), + ComputeTipSlot, AddOp(AddOp), SubOp(SubOp), ConcatOp(ConcatOp), @@ -738,6 +739,7 @@ impl DataExpr { DataExpr::AnyAssetConstructor(x) => x.target_type(), DataExpr::UtxoRef(_) => Some(Type::UtxoRef), DataExpr::MinUtxo(_) => Some(Type::AnyAsset), + DataExpr::ComputeTipSlot => Some(Type::Int), } } } diff --git a/crates/tx3-lang/src/ir.rs b/crates/tx3-lang/src/ir.rs index df5db8b1..86103376 100644 --- a/crates/tx3-lang/src/ir.rs +++ b/crates/tx3-lang/src/ir.rs @@ -70,6 +70,7 @@ pub enum BuiltInOp { pub enum CompilerOp { BuildScriptAddress(Expression), ComputeMinUtxo(Expression), + ComputeTipSlot, } #[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] @@ -465,6 +466,7 @@ impl Node for CompilerOp { let visited = match self { CompilerOp::BuildScriptAddress(x) => CompilerOp::BuildScriptAddress(x.apply(visitor)?), CompilerOp::ComputeMinUtxo(x) => CompilerOp::ComputeMinUtxo(x.apply(visitor)?), + CompilerOp::ComputeTipSlot => CompilerOp::ComputeTipSlot, }; Ok(visited) diff --git a/crates/tx3-lang/src/lowering.rs b/crates/tx3-lang/src/lowering.rs index e727a388..528a4372 100644 --- a/crates/tx3-lang/src/lowering.rs +++ b/crates/tx3-lang/src/lowering.rs @@ -449,6 +449,9 @@ impl IntoLower for ast::DataExpr { ast::DataExpr::MinUtxo(x) => ir::Expression::EvalCompiler(Box::new( ir::CompilerOp::ComputeMinUtxo(x.into_lower(ctx)?), )), + ast::DataExpr::ComputeTipSlot => { + ir::Expression::EvalCompiler(Box::new(ir::CompilerOp::ComputeTipSlot)) + } }; Ok(out) diff --git a/crates/tx3-lang/src/parsing.rs b/crates/tx3-lang/src/parsing.rs index 2317f39e..867f52ac 100644 --- a/crates/tx3-lang/src/parsing.rs +++ b/crates/tx3-lang/src/parsing.rs @@ -1094,6 +1094,10 @@ impl DataExpr { Ok(DataExpr::MinUtxo(Identifier::parse(inner)?)) } + fn tip_slot_parse(_pair: Pair) -> Result { + Ok(DataExpr::ComputeTipSlot) + } + fn concat_constructor_parse(pair: Pair) -> Result { Ok(DataExpr::ConcatOp(ConcatOp::parse(pair)?)) } @@ -1180,6 +1184,7 @@ impl AstNode for DataExpr { Rule::any_asset_constructor => DataExpr::any_asset_constructor_parse(x), Rule::concat_constructor => DataExpr::concat_constructor_parse(x), Rule::min_utxo => DataExpr::min_utxo_parse(x), + Rule::tip_slot => DataExpr::tip_slot_parse(x), Rule::data_expr => DataExpr::parse(x), x => unreachable!("unexpected rule as data primary: {:?}", x), }) @@ -1220,6 +1225,7 @@ impl AstNode for DataExpr { DataExpr::PropertyOp(x) => &x.span, DataExpr::UtxoRef(x) => x.span(), DataExpr::MinUtxo(x) => x.span(), + DataExpr::ComputeTipSlot => &Span::DUMMY, // TODO } } } @@ -1986,6 +1992,24 @@ mod tests { }) ); + input_to_ast_check!( + DataExpr, + "tip_slot_basic", + "tip_slot()", + DataExpr::ComputeTipSlot + ); + + input_to_ast_check!( + DataExpr, + "tip_slot_in_expression", + "1000 + tip_slot()", + DataExpr::AddOp(AddOp { + lhs: Box::new(DataExpr::Number(1000)), + rhs: Box::new(DataExpr::ComputeTipSlot), + span: Span::DUMMY, + }) + ); + input_to_ast_check!( StructConstructor, "struct_constructor_record", diff --git a/crates/tx3-lang/src/tx3.pest b/crates/tx3-lang/src/tx3.pest index 35e24a81..b401d68a 100644 --- a/crates/tx3-lang/src/tx3.pest +++ b/crates/tx3-lang/src/tx3.pest @@ -126,6 +126,8 @@ concat_constructor = { min_utxo = { "min_utxo" ~ "(" ~ identifier ~ ")" } +tip_slot = { "tip_slot" ~ "(" ~ ")" } + data_expr = { data_prefix* ~ data_primary ~ data_postfix* ~ (data_infix ~ data_prefix* ~ data_primary ~ data_postfix* )* } data_infix = _{ data_add | data_sub } @@ -147,6 +149,7 @@ data_expr = { data_prefix* ~ data_primary ~ data_postfix* ~ (data_infix ~ data_p bool | string | min_utxo | + tip_slot | struct_constructor | list_constructor | concat_constructor | diff --git a/examples/tip_slot.tx3 b/examples/tip_slot.tx3 new file mode 100644 index 00000000..fd186a02 --- /dev/null +++ b/examples/tip_slot.tx3 @@ -0,0 +1,22 @@ +party Sender; + +type TimestampDatum { + current_slot: Int, + expiry_slot: Int, +} + +tx create_timestamp_tx(validity_period: Int) { + input source { + from: Sender, + min_amount: Ada(2000000), + } + + output timestamp_output { + to: Sender, + amount: source - fees, + datum: TimestampDatum { + current_slot: tip_slot(), + expiry_slot: tip_slot() + validity_period, + }, + } +}