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