tx3_lang/
ir.rs

1//! The Tx3 language intermediate representation (IR).
2//!
3//! This module defines the intermediate representation (IR) for the Tx3
4//! language. It provides the structure for representing Tx3 programs in a more
5//! abstract form, suitable for further processing or execution.
6//!
7//! This module is not intended to be used directly by end-users. See
8//! [`lower`](crate::lower) for lowering an AST to the intermediate
9//! representation.
10
11use std::collections::{HashMap, HashSet};
12
13use bincode::{Decode, Encode};
14use serde::{Deserialize, Serialize};
15
16use crate::{Utxo, UtxoRef};
17
18pub const IR_VERSION: &str = "v1alpha8";
19
20#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
21pub struct StructExpr {
22    pub constructor: usize,
23    pub fields: Vec<Expression>,
24}
25
26impl StructExpr {
27    pub fn unit() -> Self {
28        Self {
29            constructor: 0,
30            fields: vec![],
31        }
32    }
33}
34
35#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
36pub enum Coerce {
37    NoOp(Expression),
38    IntoAssets(Expression),
39    IntoDatum(Expression),
40    IntoScript(Expression),
41}
42
43pub type PropertyIndex = usize;
44
45/// Operations that are executed during the "apply" phase.
46///
47/// These are operations that are executed during the "apply" phase, as opposed
48/// to the compiler operations that are executed during the "compile" phase.
49///
50/// These ops can be executed (aka "reduced") very early in the process. As long
51/// as they underlying expressions are "constant" (aka: don't rely on external
52/// data), the will be simplified directly during the "apply" phase.
53#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
54pub enum BuiltInOp {
55    NoOp(Expression),
56    Add(Expression, Expression),
57    Sub(Expression, Expression),
58    Concat(Expression, Expression),
59    Negate(Expression),
60    Property(Expression, PropertyIndex),
61}
62
63/// Operations that are performed by the compiler.
64///
65/// These are operations that are performed by the compiler, as opposed to the
66/// built-in operations that are executed (aka "reduced") during the "apply"
67/// phase.
68///
69/// These ops can't be executed earlier because they are either: chain-specific
70/// or rely on data that is only available to the compiler.
71#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
72pub enum CompilerOp {
73    BuildScriptAddress(Expression),
74    ComputeMinUtxo(Expression),
75}
76
77#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
78pub struct AssetExpr {
79    pub policy: Expression,
80    pub asset_name: Expression,
81    pub amount: Expression,
82}
83
84impl AssetExpr {
85    pub fn class_matches(&self, other: &Self) -> bool {
86        self.policy.as_bytes() == other.policy.as_bytes()
87            && self.asset_name.as_bytes() == other.asset_name.as_bytes()
88    }
89}
90
91/// An ad-hoc compile directive.
92///
93/// It's a generic, pass-through structure that the final chain-specific
94/// compiler can use to compile custom structures. Tx3 won't attempt to process
95/// this IR structure for anything other than trying to apply / reduce its
96/// expressions.
97#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
98pub struct AdHocDirective {
99    pub name: String,
100    pub data: HashMap<String, Expression>,
101}
102
103#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
104pub enum ScriptSource {
105    Embedded(Expression),
106    UtxoRef {
107        r#ref: Expression,
108        source: Option<Expression>,
109    },
110}
111
112impl ScriptSource {
113    pub fn new_ref(r#ref: Expression, source: Expression) -> Self {
114        Self::UtxoRef {
115            r#ref,
116            source: Some(source),
117        }
118    }
119
120    pub fn new_embedded(source: Expression) -> Self {
121        Self::Embedded(source)
122    }
123
124    pub fn expect_parameter(policy_name: String) -> Self {
125        Self::Embedded(
126            Param::ExpectValue(
127                format!("{}_script", policy_name.to_lowercase()),
128                Type::Bytes,
129            )
130            .into(),
131        )
132    }
133
134    pub fn expect_ref_input(policy_name: String, r#ref: Expression) -> Self {
135        Self::UtxoRef {
136            r#ref: r#ref.clone(),
137            source: Some(
138                Coerce::IntoScript(
139                    Param::ExpectInput(
140                        format!("{}_script", policy_name.to_lowercase()),
141                        InputQuery {
142                            address: Expression::None,
143                            min_amount: Expression::None,
144                            many: false,
145                            r#ref,
146                            collateral: false,
147                        },
148                    )
149                    .into(),
150                )
151                .into(),
152            ),
153        }
154    }
155
156    pub fn as_utxo_ref(&self) -> Option<Expression> {
157        match self {
158            Self::UtxoRef { r#ref, .. } => Some(r#ref.clone()),
159            Self::Embedded(Expression::UtxoRefs(x)) => Some(Expression::UtxoRefs(x.clone())),
160            _ => None,
161        }
162    }
163}
164
165#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
166pub struct PolicyExpr {
167    pub name: String,
168    pub hash: Expression,
169    pub script: ScriptSource,
170}
171
172#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
173pub enum Type {
174    Undefined,
175    Unit,
176    Int,
177    Bool,
178    Bytes,
179    Address,
180    Utxo,
181    UtxoRef,
182    AnyAsset,
183    List,
184    Custom(String),
185}
186
187#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
188pub enum Param {
189    Set(Expression),
190    ExpectValue(String, Type),
191    ExpectInput(String, InputQuery),
192    ExpectFees,
193}
194
195#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
196pub enum Expression {
197    None,
198    List(Vec<Expression>),
199    Tuple(Box<(Expression, Expression)>),
200    Struct(StructExpr),
201    Bytes(Vec<u8>),
202    Number(i128),
203    Bool(bool),
204    String(String),
205    Address(Vec<u8>),
206    Hash(Vec<u8>),
207    UtxoRefs(Vec<UtxoRef>),
208    UtxoSet(HashSet<Utxo>),
209    Assets(Vec<AssetExpr>),
210
211    EvalParam(Box<Param>),
212    EvalBuiltIn(Box<BuiltInOp>),
213    EvalCompiler(Box<CompilerOp>),
214    EvalCoerce(Box<Coerce>),
215
216    // pass-through
217    AdHocDirective(Box<AdHocDirective>),
218}
219
220impl Default for Expression {
221    fn default() -> Self {
222        Self::None
223    }
224}
225
226impl Expression {
227    pub fn is_none(&self) -> bool {
228        matches!(self, Self::None)
229    }
230
231    pub fn as_option(&self) -> Option<&Self> {
232        match self {
233            Self::None => None,
234            _ => Some(self),
235        }
236    }
237
238    pub fn into_option(self) -> Option<Self> {
239        match self {
240            Self::None => None,
241            _ => Some(self),
242        }
243    }
244
245    pub fn as_bytes(&self) -> Option<&[u8]> {
246        match self {
247            Self::Bytes(bytes) => Some(bytes),
248            Self::String(s) => Some(s.as_bytes()),
249            Self::Address(x) => Some(x),
250            Self::Hash(x) => Some(x),
251            _ => None,
252        }
253    }
254
255    pub fn as_number(&self) -> Option<i128> {
256        match self {
257            Self::Number(x) => Some(*x),
258            _ => None,
259        }
260    }
261
262    pub fn as_assets(&self) -> Option<&[AssetExpr]> {
263        match self {
264            Self::Assets(assets) => Some(assets),
265            _ => None,
266        }
267    }
268
269    pub fn as_utxo_refs(&self) -> Option<&[UtxoRef]> {
270        match self {
271            Self::UtxoRefs(refs) => Some(refs),
272            _ => None,
273        }
274    }
275}
276
277impl From<BuiltInOp> for Expression {
278    fn from(op: BuiltInOp) -> Self {
279        Self::EvalBuiltIn(Box::new(op))
280    }
281}
282
283impl From<CompilerOp> for Expression {
284    fn from(op: CompilerOp) -> Self {
285        Self::EvalCompiler(Box::new(op))
286    }
287}
288
289impl From<Coerce> for Expression {
290    fn from(coerce: Coerce) -> Self {
291        Self::EvalCoerce(Box::new(coerce))
292    }
293}
294
295impl From<Param> for Expression {
296    fn from(param: Param) -> Self {
297        Self::EvalParam(Box::new(param))
298    }
299}
300
301#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
302pub struct InputQuery {
303    pub address: Expression,
304    pub min_amount: Expression,
305    pub r#ref: Expression,
306    pub many: bool,
307    pub collateral: bool,
308}
309
310#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
311pub struct Input {
312    pub name: String,
313    pub utxos: Expression,
314    pub redeemer: Expression,
315}
316
317#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
318pub struct Output {
319    pub address: Expression,
320    pub datum: Expression,
321    pub amount: Expression,
322}
323
324#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
325pub struct Validity {
326    pub since: Expression,
327    pub until: Expression,
328}
329
330#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
331pub struct Mint {
332    pub amount: Expression,
333    pub redeemer: Expression,
334}
335
336#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
337pub struct Collateral {
338    pub utxos: Expression,
339}
340
341#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
342pub struct Metadata {
343    pub key: Expression,
344    pub value: Expression,
345}
346
347#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
348pub struct Signers {
349    pub signers: Vec<Expression>,
350}
351
352#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
353pub struct Tx {
354    pub fees: Expression,
355    pub references: Vec<Expression>,
356    pub inputs: Vec<Input>,
357    pub outputs: Vec<Output>,
358    pub validity: Option<Validity>,
359    pub mints: Vec<Mint>,
360    pub burns: Vec<Mint>,
361    pub adhoc: Vec<AdHocDirective>,
362    pub collateral: Vec<Collateral>,
363    pub signers: Option<Signers>,
364    pub metadata: Vec<Metadata>,
365}
366
367pub trait Visitor {
368    fn reduce(&mut self, op: Expression) -> Result<Expression, crate::applying::Error>;
369}
370
371pub trait Node: Sized {
372    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error>;
373}
374
375impl<T: Node> Node for Option<T> {
376    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
377        self.map(|x| x.apply(visitor)).transpose()
378    }
379}
380
381impl<T: Node> Node for Box<T> {
382    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
383        let visited = (*self).apply(visitor)?;
384        Ok(Box::new(visited))
385    }
386}
387
388impl Node for (Expression, Expression) {
389    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
390        let (a, b) = self;
391        Ok((a.apply(visitor)?, b.apply(visitor)?))
392    }
393}
394
395impl<T: Node> Node for Vec<T> {
396    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
397        self.into_iter().map(|x| x.apply(visitor)).collect()
398    }
399}
400
401impl Node for StructExpr {
402    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
403        let visited = Self {
404            constructor: self.constructor,
405            fields: self.fields.apply(visitor)?,
406        };
407
408        Ok(visited)
409    }
410}
411
412impl Node for AssetExpr {
413    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
414        let visited = Self {
415            policy: self.policy.apply(visitor)?,
416            asset_name: self.asset_name.apply(visitor)?,
417            amount: self.amount.apply(visitor)?,
418        };
419
420        Ok(visited)
421    }
422}
423
424impl Node for InputQuery {
425    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
426        let visited = Self {
427            address: self.address.apply(visitor)?,
428            min_amount: self.min_amount.apply(visitor)?,
429            r#ref: self.r#ref.apply(visitor)?,
430            ..self
431        };
432
433        Ok(visited)
434    }
435}
436
437impl Node for Param {
438    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
439        let visited = match self {
440            Param::Set(x) => Param::Set(x.apply(visitor)?),
441            Param::ExpectValue(name, ty) => Param::ExpectValue(name, ty),
442            Param::ExpectInput(name, query) => Param::ExpectInput(name, query.apply(visitor)?),
443            Param::ExpectFees => Param::ExpectFees,
444        };
445
446        Ok(visited)
447    }
448}
449
450impl Node for BuiltInOp {
451    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
452        let visited = match self {
453            BuiltInOp::NoOp(x) => BuiltInOp::NoOp(x.apply(visitor)?),
454            BuiltInOp::Add(a, b) => BuiltInOp::Add(a.apply(visitor)?, b.apply(visitor)?),
455            BuiltInOp::Sub(a, b) => BuiltInOp::Sub(a.apply(visitor)?, b.apply(visitor)?),
456            BuiltInOp::Concat(a, b) => BuiltInOp::Concat(a.apply(visitor)?, b.apply(visitor)?),
457            BuiltInOp::Negate(x) => BuiltInOp::Negate(x.apply(visitor)?),
458            BuiltInOp::Property(x, i) => BuiltInOp::Property(x.apply(visitor)?, i),
459        };
460
461        Ok(visited)
462    }
463}
464
465impl Node for CompilerOp {
466    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
467        let visited = match self {
468            CompilerOp::BuildScriptAddress(x) => CompilerOp::BuildScriptAddress(x.apply(visitor)?),
469            CompilerOp::ComputeMinUtxo(x) => CompilerOp::ComputeMinUtxo(x.apply(visitor)?),
470        };
471
472        Ok(visited)
473    }
474}
475
476impl Node for Coerce {
477    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
478        let visited = match self {
479            Coerce::NoOp(x) => Coerce::NoOp(x.apply(visitor)?),
480            Coerce::IntoAssets(x) => Coerce::IntoAssets(x.apply(visitor)?),
481            Coerce::IntoDatum(x) => Coerce::IntoDatum(x.apply(visitor)?),
482            Coerce::IntoScript(x) => Coerce::IntoScript(x.apply(visitor)?),
483        };
484
485        Ok(visited)
486    }
487}
488
489impl Node for Expression {
490    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
491        // first we visit the nested expressions
492        let visited = match self {
493            Expression::List(x) => Expression::List(x.apply(visitor)?),
494            Expression::Tuple(x) => Expression::Tuple(x.apply(visitor)?),
495            Expression::Struct(x) => Expression::Struct(x.apply(visitor)?),
496            Expression::Assets(x) => Expression::Assets(x.apply(visitor)?),
497            Expression::EvalParam(x) => Expression::EvalParam(x.apply(visitor)?),
498            Expression::AdHocDirective(x) => Expression::AdHocDirective(x.apply(visitor)?),
499            Expression::EvalBuiltIn(x) => Expression::EvalBuiltIn(x.apply(visitor)?),
500            Expression::EvalCompiler(x) => Expression::EvalCompiler(x.apply(visitor)?),
501            Expression::EvalCoerce(x) => Expression::EvalCoerce(x.apply(visitor)?),
502
503            // leaf expressions don't need to be visited
504            Expression::Bytes(x) => Expression::Bytes(x),
505            Expression::None => Expression::None,
506            Expression::Number(x) => Expression::Number(x),
507            Expression::Bool(x) => Expression::Bool(x),
508            Expression::String(x) => Expression::String(x),
509            Expression::Address(x) => Expression::Address(x),
510            Expression::Hash(x) => Expression::Hash(x),
511            Expression::UtxoRefs(x) => Expression::UtxoRefs(x),
512            Expression::UtxoSet(x) => Expression::UtxoSet(x),
513        };
514
515        // then we reduce the visited expression
516        visitor.reduce(visited)
517    }
518}
519
520impl Node for Input {
521    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
522        let visited = Self {
523            utxos: self.utxos.apply(visitor)?,
524            redeemer: self.redeemer.apply(visitor)?,
525            ..self
526        };
527
528        Ok(visited)
529    }
530}
531
532impl Node for Output {
533    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
534        let visited = Self {
535            address: self.address.apply(visitor)?,
536            datum: self.datum.apply(visitor)?,
537            amount: self.amount.apply(visitor)?,
538        };
539
540        Ok(visited)
541    }
542}
543
544impl Node for Validity {
545    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
546        let visited = Self {
547            since: self.since.apply(visitor)?,
548            until: self.until.apply(visitor)?,
549        };
550
551        Ok(visited)
552    }
553}
554
555impl Node for Mint {
556    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
557        let visited = Self {
558            amount: self.amount.apply(visitor)?,
559            redeemer: self.redeemer.apply(visitor)?,
560        };
561
562        Ok(visited)
563    }
564}
565
566impl Node for Collateral {
567    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
568        let visited = Self {
569            utxos: self.utxos.apply(visitor)?,
570        };
571
572        Ok(visited)
573    }
574}
575
576impl Node for Metadata {
577    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
578        let visited = Self {
579            key: self.key.apply(visitor)?,
580            value: self.value.apply(visitor)?,
581        };
582
583        Ok(visited)
584    }
585}
586
587impl Node for Signers {
588    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
589        let visited = Self {
590            signers: self.signers.apply(visitor)?,
591        };
592
593        Ok(visited)
594    }
595}
596
597impl Node for HashMap<String, Expression> {
598    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
599        let visited: Vec<_> = self
600            .into_iter()
601            .map(|(k, v)| visitor.reduce(v).map(|v| (k, v)))
602            .collect::<Result<_, _>>()?;
603
604        Ok(visited.into_iter().collect())
605    }
606}
607
608impl Node for AdHocDirective {
609    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
610        let visited = Self {
611            name: self.name,
612            data: self.data.apply(visitor)?,
613        };
614
615        Ok(visited)
616    }
617}
618
619impl Node for Tx {
620    fn apply<V: Visitor>(self, visitor: &mut V) -> Result<Self, crate::applying::Error> {
621        let visited = Self {
622            fees: self.fees.apply(visitor)?,
623            references: self.references.apply(visitor)?,
624            inputs: self.inputs.apply(visitor)?,
625            outputs: self.outputs.apply(visitor)?,
626            validity: self.validity.apply(visitor)?,
627            mints: self.mints.apply(visitor)?,
628            burns: self.burns.apply(visitor)?,
629            adhoc: self.adhoc.apply(visitor)?,
630            collateral: self.collateral.apply(visitor)?,
631            signers: self.signers.apply(visitor)?,
632            metadata: self.metadata.apply(visitor)?,
633        };
634
635        Ok(visited)
636    }
637}