tx3_lang/
applying.rs

1use std::collections::{BTreeMap, HashMap, HashSet};
2
3use crate::{backend, ir, ArgValue, CanonicalAssets, Utxo};
4
5#[derive(Debug, thiserror::Error)]
6pub enum Error {
7    #[error("invalid built-in operation {0:?}")]
8    InvalidBuiltInOp(Box<ir::BuiltInOp>),
9
10    #[error(transparent)]
11    BackendError(#[from] backend::Error),
12
13    #[error("invalid argument {0:?} for {1}")]
14    InvalidArgument(ArgValue, String),
15
16    #[error("property {0} not found in {1}")]
17    PropertyNotFound(String, String),
18
19    #[error("property index {0} not found in {1}")]
20    PropertyIndexNotFound(usize, String),
21
22    #[error("invalid {0} operation over {1:?} and {2:?}")]
23    InvalidBinaryOp(String, String, String),
24
25    #[error("invalid {0} operation over {1:?}")]
26    InvalidUnaryOp(String, String),
27
28    #[error("cannot coerce {0:?} into assets")]
29    CannotCoerceIntoAssets(ir::Expression),
30
31    #[error("cannot coerce {0:?} into datum")]
32    CannotCoerceIntoDatum(ir::Expression),
33}
34
35pub trait Indexable: std::fmt::Debug {
36    fn index(&self, index: usize) -> Option<ir::Expression>;
37
38    fn index_or_err(&self, index: usize) -> Result<ir::Expression, Error> {
39        self.index(index)
40            .ok_or(Error::PropertyIndexNotFound(index, format!("{self:?}")))
41    }
42}
43
44impl Indexable for ir::StructExpr {
45    fn index(&self, index: usize) -> Option<ir::Expression> {
46        self.fields.get(index).cloned()
47    }
48}
49
50impl Indexable for ir::Expression {
51    fn index(&self, index: usize) -> Option<ir::Expression> {
52        match self {
53            ir::Expression::None => None,
54            ir::Expression::List(x) => x.get(index).cloned(),
55            ir::Expression::Tuple(x) => match index {
56                0 => Some(x.0.clone()),
57                1 => Some(x.1.clone()),
58                _ => None,
59            },
60            ir::Expression::Struct(x) => x.index(index),
61            _ => None,
62        }
63    }
64}
65
66pub trait Concatenable {
67    fn concat(self, other: ir::Expression) -> Result<ir::Expression, Error>;
68}
69
70pub trait Arithmetic {
71    fn add(self, other: ir::Expression) -> Result<ir::Expression, Error>;
72    fn sub(self, other: ir::Expression) -> Result<ir::Expression, Error>;
73    fn neg(self) -> Result<ir::Expression, Error>;
74}
75
76impl<T> Arithmetic for T
77where
78    T: Into<CanonicalAssets> + std::fmt::Debug,
79{
80    fn add(self, other: ir::Expression) -> Result<ir::Expression, Error> {
81        let y = match other {
82            ir::Expression::Assets(x) => CanonicalAssets::from(x),
83            ir::Expression::None => CanonicalAssets::empty(),
84            other => {
85                return Err(Error::InvalidBinaryOp(
86                    "add".to_string(),
87                    format!("{self:?}"),
88                    format!("{other:?}"),
89                ))
90            }
91        };
92
93        let x = self.into();
94        let total = x + y;
95        Ok(ir::Expression::Assets(total.into()))
96    }
97
98    fn sub(self, other: ir::Expression) -> Result<ir::Expression, Error> {
99        let other_neg = other.neg()?;
100        self.add(other_neg)
101    }
102
103    fn neg(self) -> Result<ir::Expression, Error> {
104        let negated = std::ops::Neg::neg(self.into());
105        Ok(ir::Expression::Assets(negated.into()))
106    }
107}
108
109impl Arithmetic for i128 {
110    fn add(self, other: ir::Expression) -> Result<ir::Expression, Error> {
111        match other {
112            ir::Expression::Number(y) => Ok(ir::Expression::Number(self + y)),
113            ir::Expression::None => Ok(ir::Expression::Number(self)),
114            _ => Err(Error::InvalidBinaryOp(
115                "add".to_string(),
116                format!("{self:?}"),
117                format!("{other:?}"),
118            )),
119        }
120    }
121
122    fn sub(self, other: ir::Expression) -> Result<ir::Expression, Error> {
123        let other_neg = other.neg()?;
124        self.add(other_neg)
125    }
126
127    fn neg(self) -> Result<ir::Expression, Error> {
128        Ok(ir::Expression::Number(-self))
129    }
130}
131
132impl Arithmetic for ir::Expression {
133    fn add(self, other: ir::Expression) -> Result<ir::Expression, Error> {
134        match self {
135            ir::Expression::None => Ok(other),
136            ir::Expression::Number(x) => Arithmetic::add(x, other),
137            ir::Expression::Assets(x) => Arithmetic::add(x, other),
138            x => Err(Error::InvalidBinaryOp(
139                "add".to_string(),
140                format!("{x:?}"),
141                format!("{other:?}"),
142            )),
143        }
144    }
145
146    fn sub(self, other: ir::Expression) -> Result<ir::Expression, Error> {
147        match self {
148            ir::Expression::None => Ok(other),
149            ir::Expression::Number(x) => Arithmetic::sub(x, other),
150            ir::Expression::Assets(x) => Arithmetic::sub(x, other),
151            x => Err(Error::InvalidBinaryOp(
152                "sub".to_string(),
153                format!("{x:?}"),
154                format!("{other:?}"),
155            )),
156        }
157    }
158
159    fn neg(self) -> Result<ir::Expression, Error> {
160        match self {
161            ir::Expression::None => Ok(ir::Expression::None),
162            ir::Expression::Number(x) => Arithmetic::neg(x),
163            ir::Expression::Assets(x) => Arithmetic::neg(x),
164            x => Err(Error::InvalidUnaryOp("neg".to_string(), format!("{x:?}"))),
165        }
166    }
167}
168
169impl Concatenable for String {
170    fn concat(self, other: ir::Expression) -> Result<ir::Expression, Error> {
171        match other {
172            ir::Expression::String(y) => Ok(ir::Expression::String(self + &y)),
173            ir::Expression::None => Ok(ir::Expression::String(self)),
174            _ => Err(Error::InvalidBinaryOp(
175                "concat".to_string(),
176                format!("String({self:?})"),
177                format!("{other:?}"),
178            )),
179        }
180    }
181}
182
183impl Concatenable for Vec<u8> {
184    fn concat(self, other: ir::Expression) -> Result<ir::Expression, Error> {
185        match other {
186            ir::Expression::Bytes(y) => {
187                let mut result = self;
188                result.extend(y);
189                Ok(ir::Expression::Bytes(result))
190            }
191            ir::Expression::None => Ok(ir::Expression::Bytes(self)),
192            _ => Err(Error::InvalidBinaryOp(
193                "concat".to_string(),
194                format!("Bytes({self:?})"),
195                format!("{other:?}"),
196            )),
197        }
198    }
199}
200
201impl Concatenable for ir::Expression {
202    fn concat(self, other: ir::Expression) -> Result<ir::Expression, Error> {
203        match self {
204            ir::Expression::None => Ok(other),
205            ir::Expression::String(x) => Concatenable::concat(x, other),
206            ir::Expression::Bytes(x) => Concatenable::concat(x, other),
207            x => Err(Error::InvalidBinaryOp(
208                "concat".to_string(),
209                format!("{x:?}"),
210                format!("{other:?}"),
211            )),
212        }
213    }
214}
215
216pub trait Coerceable {
217    fn into_assets(self) -> Result<ir::Expression, Error>;
218    fn into_datum(self) -> Result<ir::Expression, Error>;
219}
220
221impl Coerceable for ir::Expression {
222    fn into_assets(self) -> Result<ir::Expression, Error> {
223        match self {
224            ir::Expression::None => Ok(ir::Expression::None),
225            ir::Expression::Assets(x) => Ok(ir::Expression::Assets(x)),
226            ir::Expression::UtxoSet(x) => {
227                let all = x
228                    .into_iter()
229                    .map(|x| x.assets)
230                    .fold(CanonicalAssets::empty(), |acc, x| acc + x);
231
232                Ok(ir::Expression::Assets(all.into()))
233            }
234            _ => Err(Error::CannotCoerceIntoAssets(self)),
235        }
236    }
237
238    fn into_datum(self) -> Result<ir::Expression, Error> {
239        match self {
240            ir::Expression::None => Ok(ir::Expression::None),
241            ir::Expression::UtxoSet(x) => Ok(x
242                .into_iter()
243                .next()
244                .and_then(|x| x.datum)
245                .unwrap_or(ir::Expression::None)),
246            ir::Expression::List(x) => Ok(ir::Expression::List(x)),
247            ir::Expression::Tuple(x) => Ok(ir::Expression::Tuple(x)),
248            ir::Expression::Struct(x) => Ok(ir::Expression::Struct(x)),
249            ir::Expression::Bytes(x) => Ok(ir::Expression::Bytes(x)),
250            ir::Expression::Number(x) => Ok(ir::Expression::Number(x)),
251            ir::Expression::String(x) => Ok(ir::Expression::String(x)),
252            ir::Expression::Address(x) => Ok(ir::Expression::Bytes(x)),
253            ir::Expression::Hash(x) => Ok(ir::Expression::Bytes(x)),
254            _ => Err(Error::CannotCoerceIntoDatum(self)),
255        }
256    }
257}
258
259fn arg_value_into_expr(arg: ArgValue) -> ir::Expression {
260    match arg {
261        ArgValue::Address(x) => ir::Expression::Address(x),
262        ArgValue::Int(x) => ir::Expression::Number(x),
263        ArgValue::Bool(x) => ir::Expression::Bool(x),
264        ArgValue::String(x) => ir::Expression::String(x),
265        ArgValue::Bytes(x) => ir::Expression::Bytes(x),
266        ArgValue::UtxoSet(x) => ir::Expression::UtxoSet(x),
267        ArgValue::UtxoRef(x) => ir::Expression::UtxoRefs(vec![x]),
268    }
269}
270
271pub trait Apply: Sized + std::fmt::Debug {
272    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error>;
273    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error>;
274    fn apply_fees(self, fees: u64) -> Result<Self, Error>;
275
276    fn is_constant(&self) -> bool;
277
278    fn params(&self) -> BTreeMap<String, ir::Type>;
279    fn queries(&self) -> BTreeMap<String, ir::InputQuery>;
280
281    fn reduce(self) -> Result<Self, Error>;
282}
283
284pub trait Composite: Sized {
285    fn reduce_self(self) -> Result<Self, Error> {
286        Ok(self)
287    }
288
289    fn components(&self) -> Vec<&ir::Expression>;
290
291    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
292    where
293        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone;
294
295    fn reduce_nested(self) -> Result<Self, Error> {
296        self.try_map_components(|x| x.reduce())
297    }
298}
299
300impl<T> Apply for T
301where
302    T: Composite + std::fmt::Debug,
303{
304    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
305        self.try_map_components(|x| x.apply_args(args))
306    }
307
308    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
309        self.try_map_components(|x| x.apply_inputs(args))
310    }
311
312    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
313        self.try_map_components(|x| x.apply_fees(fees))
314    }
315
316    fn is_constant(&self) -> bool {
317        self.components().iter().all(|x| x.is_constant())
318    }
319
320    fn params(&self) -> BTreeMap<String, ir::Type> {
321        self.components().iter().flat_map(|x| x.params()).collect()
322    }
323
324    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
325        self.components().iter().flat_map(|x| x.queries()).collect()
326    }
327
328    fn reduce(self) -> Result<Self, Error> {
329        let x = self.reduce_nested()?;
330
331        if x.is_constant() {
332            x.reduce_self()
333        } else {
334            Ok(x)
335        }
336    }
337}
338
339impl<T> Apply for Option<T>
340where
341    T: Apply,
342{
343    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
344        self.map(|x| x.apply_args(args)).transpose()
345    }
346
347    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
348        self.map(|x| x.apply_inputs(args)).transpose()
349    }
350
351    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
352        self.map(|x| x.apply_fees(fees)).transpose()
353    }
354
355    fn is_constant(&self) -> bool {
356        match self {
357            Some(x) => x.is_constant(),
358            None => true,
359        }
360    }
361
362    fn params(&self) -> BTreeMap<String, ir::Type> {
363        match self {
364            Some(x) => x.params(),
365            None => BTreeMap::new(),
366        }
367    }
368
369    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
370        match self {
371            Some(x) => x.queries(),
372            None => BTreeMap::new(),
373        }
374    }
375
376    fn reduce(self) -> Result<Self, Error> {
377        self.map(|x| x.reduce()).transpose()
378    }
379}
380
381impl<T> Apply for Vec<T>
382where
383    T: Apply,
384{
385    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
386        self.into_iter().map(|x| x.apply_args(args)).collect()
387    }
388
389    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
390        self.into_iter().map(|x| x.apply_inputs(args)).collect()
391    }
392
393    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
394        self.into_iter().map(|x| x.apply_fees(fees)).collect()
395    }
396
397    fn is_constant(&self) -> bool {
398        self.iter().all(|x| x.is_constant())
399    }
400
401    fn params(&self) -> BTreeMap<String, ir::Type> {
402        self.iter().flat_map(|x| x.params()).collect()
403    }
404
405    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
406        self.iter().flat_map(|x| x.queries()).collect()
407    }
408
409    fn reduce(self) -> Result<Self, Error> {
410        self.into_iter().map(|x| x.reduce()).collect()
411    }
412}
413
414impl<T> Apply for HashMap<String, T>
415where
416    T: Apply,
417{
418    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
419        self.into_iter()
420            .map(|(k, v)| v.apply_args(args).map(|v| (k, v)))
421            .collect()
422    }
423
424    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
425        self.into_iter()
426            .map(|(k, v)| v.apply_inputs(args).map(|v| (k, v)))
427            .collect()
428    }
429
430    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
431        self.into_iter()
432            .map(|(k, v)| v.apply_fees(fees).map(|v| (k, v)))
433            .collect()
434    }
435
436    fn is_constant(&self) -> bool {
437        self.values().all(|x| x.is_constant())
438    }
439
440    fn params(&self) -> BTreeMap<String, ir::Type> {
441        self.values().flat_map(|x| x.params()).collect()
442    }
443
444    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
445        self.values().flat_map(|x| x.queries()).collect()
446    }
447
448    fn reduce(self) -> Result<Self, Error> {
449        self.into_iter()
450            .map(|(k, v)| v.reduce().map(|v| (k, v)))
451            .collect()
452    }
453}
454
455impl Composite for ir::ScriptSource {
456    fn reduce_self(self) -> Result<Self, Error> {
457        Ok(self)
458    }
459
460    fn components(&self) -> Vec<&ir::Expression> {
461        match self {
462            ir::ScriptSource::Embedded(x) => vec![x],
463            ir::ScriptSource::UtxoRef { r#ref, source } => {
464                std::iter::once(r#ref).chain(source.as_ref()).collect()
465            }
466        }
467    }
468
469    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
470    where
471        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
472    {
473        match self {
474            ir::ScriptSource::Embedded(x) => Ok(ir::ScriptSource::Embedded(f(x)?)),
475            ir::ScriptSource::UtxoRef { r#ref, source } => Ok(ir::ScriptSource::UtxoRef {
476                r#ref: f(r#ref)?,
477                source: source.map(&f).transpose()?,
478            }),
479        }
480    }
481}
482
483impl TryFrom<&ArgValue> for ir::ScriptSource {
484    type Error = Error;
485
486    fn try_from(value: &ArgValue) -> Result<Self, Self::Error> {
487        match value {
488            ArgValue::Bytes(x) => Ok(ir::ScriptSource::Embedded(ir::Expression::Bytes(x.clone()))),
489            ArgValue::UtxoRef(x) => Ok(ir::ScriptSource::UtxoRef {
490                r#ref: ir::Expression::UtxoRefs(vec![x.clone()]),
491                source: None,
492            }),
493            _ => Err(Error::InvalidArgument(value.clone(), "script".to_string())),
494        }
495    }
496}
497
498impl Composite for ir::PolicyExpr {
499    fn components(&self) -> Vec<&ir::Expression> {
500        let script = self.script.components();
501        std::iter::once(&self.hash).chain(script).collect()
502    }
503
504    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
505    where
506        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
507    {
508        Ok(Self {
509            name: self.name,
510            hash: f(self.hash)?,
511            script: self.script.try_map_components(f)?,
512        })
513    }
514}
515
516impl Composite for ir::StructExpr {
517    fn components(&self) -> Vec<&ir::Expression> {
518        self.fields.iter().collect()
519    }
520
521    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
522    where
523        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
524    {
525        Ok(Self {
526            constructor: self.constructor,
527            fields: self
528                .fields
529                .into_iter()
530                .map(&f)
531                .collect::<Result<Vec<_>, _>>()?,
532        })
533    }
534}
535
536impl Composite for ir::AssetExpr {
537    fn components(&self) -> Vec<&ir::Expression> {
538        vec![&self.policy, &self.asset_name, &self.amount]
539    }
540
541    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
542    where
543        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
544    {
545        Ok(Self {
546            policy: f(self.policy)?,
547            asset_name: f(self.asset_name)?,
548            amount: f(self.amount)?,
549        })
550    }
551}
552
553impl Composite for ir::Coerce {
554    fn components(&self) -> Vec<&ir::Expression> {
555        match self {
556            Self::IntoAssets(x) => vec![x],
557            Self::IntoDatum(x) => vec![x],
558            Self::IntoScript(x) => vec![x],
559            Self::NoOp(x) => vec![x],
560        }
561    }
562
563    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
564    where
565        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
566    {
567        match self {
568            Self::IntoAssets(x) => Ok(Self::IntoAssets(f(x)?)),
569            Self::IntoDatum(x) => Ok(Self::IntoDatum(f(x)?)),
570            Self::IntoScript(x) => Ok(Self::IntoScript(f(x)?)),
571            Self::NoOp(x) => Ok(Self::NoOp(f(x)?)),
572        }
573    }
574
575    fn reduce_self(self) -> Result<Self, Error> {
576        match self {
577            Self::NoOp(x) => Ok(Self::NoOp(x)),
578            Self::IntoAssets(x) => Ok(Self::NoOp(x.into_assets()?)),
579            Self::IntoDatum(x) => Ok(Self::NoOp(x.into_datum()?)),
580            Self::IntoScript(x) => todo!(),
581        }
582    }
583}
584
585impl Composite for ir::BuiltInOp {
586    fn components(&self) -> Vec<&ir::Expression> {
587        match self {
588            Self::NoOp(x) => vec![x],
589            Self::Add(x, y) => vec![x, y],
590            Self::Sub(x, y) => vec![x, y],
591            Self::Concat(x, y) => vec![x, y],
592            Self::Negate(x) => vec![x],
593            Self::Property(x, _) => vec![x],
594        }
595    }
596
597    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
598    where
599        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
600    {
601        match self {
602            Self::NoOp(x) => Ok(Self::NoOp(f(x)?)),
603            Self::Add(x, y) => Ok(Self::Add(f(x)?, f(y)?)),
604            Self::Sub(x, y) => Ok(Self::Sub(f(x)?, f(y)?)),
605            Self::Concat(x, y) => Ok(Self::Concat(f(x)?, f(y)?)),
606            Self::Negate(x) => Ok(Self::Negate(f(x)?)),
607            Self::Property(x, prop) => Ok(Self::Property(f(x)?, prop)),
608        }
609    }
610
611    fn reduce_self(self) -> Result<Self, Error> {
612        match self {
613            Self::Add(x, y) => Ok(Self::NoOp(x.add(y)?)),
614            Self::Sub(x, y) => Ok(Self::NoOp(x.sub(y)?)),
615            Self::Concat(x, y) => Ok(Self::NoOp(x.concat(y)?)),
616            Self::Negate(x) => Ok(Self::NoOp(x.neg()?)),
617            Self::Property(x, prop) => Ok(Self::NoOp(x.index_or_err(prop)?)),
618            Self::NoOp(x) => Ok(Self::NoOp(x)),
619        }
620    }
621
622    fn reduce_nested(self) -> Result<Self, Error> {
623        match self {
624            Self::Add(x, y) => Ok(Self::Add(x.reduce()?, y.reduce()?)),
625            Self::Sub(x, y) => Ok(Self::Sub(x.reduce()?, y.reduce()?)),
626            Self::Concat(x, y) => Ok(Self::Concat(x.reduce()?, y.reduce()?)),
627            Self::Negate(x) => Ok(Self::Negate(x.reduce()?)),
628            Self::Property(x, y) => Ok(Self::Property(x.reduce()?, y)),
629            Self::NoOp(x) => Ok(Self::NoOp(x.reduce()?)),
630        }
631    }
632}
633
634impl From<ir::AssetExpr> for CanonicalAssets {
635    fn from(asset: ir::AssetExpr) -> Self {
636        let policy = asset.expect_constant_policy();
637        let name = asset.expect_constant_name();
638        let amount = asset.expect_constant_amount();
639
640        Self::from_asset(policy, name, amount)
641    }
642}
643
644impl From<Vec<ir::AssetExpr>> for CanonicalAssets {
645    fn from(assets: Vec<ir::AssetExpr>) -> Self {
646        let mut result = CanonicalAssets::empty();
647
648        for asset in assets {
649            let asset = asset.into();
650            result = result + asset;
651        }
652
653        result
654    }
655}
656
657impl From<CanonicalAssets> for Vec<ir::AssetExpr> {
658    fn from(assets: CanonicalAssets) -> Self {
659        let mut result = Vec::new();
660
661        for (class, amount) in assets.into_iter() {
662            result.push(ir::AssetExpr {
663                policy: class
664                    .policy()
665                    .map(|x| ir::Expression::Bytes(x.to_vec()))
666                    .unwrap_or(ir::Expression::None),
667                asset_name: class
668                    .name()
669                    .map(|x| ir::Expression::Bytes(x.to_vec()))
670                    .unwrap_or(ir::Expression::None),
671                amount: ir::Expression::Number(amount),
672            });
673        }
674
675        result
676    }
677}
678
679impl ir::AssetExpr {
680    fn expect_constant_policy(&self) -> Option<&[u8]> {
681        match &self.policy {
682            ir::Expression::None => None,
683            ir::Expression::Bytes(x) => Some(x.as_slice()),
684            _ => None,
685        }
686    }
687
688    fn expect_constant_name(&self) -> Option<&[u8]> {
689        match &self.asset_name {
690            ir::Expression::None => None,
691            ir::Expression::Bytes(x) => Some(x.as_slice()),
692            ir::Expression::String(x) => Some(x.as_bytes()),
693            _ => None,
694        }
695    }
696
697    fn expect_constant_amount(&self) -> i128 {
698        match &self.amount {
699            ir::Expression::Number(x) => *x,
700            _ => unreachable!("amount expected to be Number"),
701        }
702    }
703}
704
705impl Composite for ir::Input {
706    fn components(&self) -> Vec<&ir::Expression> {
707        vec![&self.utxos, &self.redeemer]
708    }
709
710    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
711    where
712        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
713    {
714        Ok(Self {
715            name: self.name,
716            utxos: f(self.utxos)?,
717            redeemer: f(self.redeemer)?,
718        })
719    }
720}
721
722impl Composite for ir::InputQuery {
723    fn components(&self) -> Vec<&ir::Expression> {
724        vec![&self.address, &self.min_amount, &self.r#ref]
725    }
726
727    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
728    where
729        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
730    {
731        Ok(Self {
732            address: f(self.address)?,
733            min_amount: f(self.min_amount)?,
734            r#ref: f(self.r#ref)?,
735            ..self
736        })
737    }
738}
739
740impl Apply for ir::Param {
741    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
742        match self {
743            ir::Param::ExpectValue(name, ty) => {
744                let defined = args.get(&name).cloned();
745
746                match defined {
747                    Some(x) => Ok(ir::Param::Set(arg_value_into_expr(x))),
748                    None => Ok(Self::ExpectValue(name, ty)),
749                }
750            }
751            // queries can have nested params
752            ir::Param::ExpectInput(name, query) => {
753                Ok(ir::Param::ExpectInput(name, query.apply_args(args)?))
754            }
755            x => Ok(x),
756        }
757    }
758
759    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
760        match self {
761            ir::Param::ExpectInput(name, query) => {
762                let defined = args.get(&name).cloned();
763
764                match defined {
765                    Some(x) => Ok(ir::Param::Set(ir::Expression::UtxoSet(x))),
766                    None => Ok(Self::ExpectInput(name, query)),
767                }
768            }
769            x => Ok(x),
770        }
771    }
772
773    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
774        match self {
775            ir::Param::ExpectFees => Ok(ir::Param::Set(ir::Expression::Assets(vec![
776                ir::AssetExpr {
777                    policy: ir::Expression::None,
778                    asset_name: ir::Expression::None,
779                    amount: ir::Expression::Number(fees as i128),
780                },
781            ]))),
782            // queries can have nested params
783            ir::Param::ExpectInput(name, query) => {
784                Ok(ir::Param::ExpectInput(name, query.apply_fees(fees)?))
785            }
786            x => Ok(x),
787        }
788    }
789
790    fn is_constant(&self) -> bool {
791        match self {
792            ir::Param::Set(x) => x.is_constant(),
793            _ => false,
794        }
795    }
796
797    fn params(&self) -> BTreeMap<String, ir::Type> {
798        match self {
799            ir::Param::ExpectValue(name, ty) => BTreeMap::from([(name.clone(), ty.clone())]),
800            // queries can have nested params
801            ir::Param::ExpectInput(_, x) => x.params(),
802            _ => BTreeMap::new(),
803        }
804    }
805
806    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
807        match self {
808            ir::Param::ExpectInput(name, query) => BTreeMap::from([(name.clone(), query.clone())]),
809            _ => BTreeMap::new(),
810        }
811    }
812
813    fn reduce(self) -> Result<Self, Error> {
814        match self {
815            // queries can have nested expressions that need to be reduced
816            ir::Param::ExpectInput(name, query) => {
817                Ok(ir::Param::ExpectInput(name, query.reduce()?))
818            }
819            x => Ok(x),
820        }
821    }
822}
823
824impl Apply for ir::Expression {
825    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
826        match self {
827            Self::List(x) => Ok(Self::List(
828                x.into_iter()
829                    .map(|x| x.apply_args(args))
830                    .collect::<Result<_, _>>()?,
831            )),
832            Self::Tuple(x) => Ok(Self::Tuple(Box::new((
833                x.0.apply_args(args)?,
834                x.1.apply_args(args)?,
835            )))),
836            Self::Struct(x) => Ok(Self::Struct(x.apply_args(args)?)),
837            Self::Assets(x) => Ok(Self::Assets(
838                x.into_iter()
839                    .map(|x| x.apply_args(args))
840                    .collect::<Result<_, _>>()?,
841            )),
842            Self::EvalParam(x) => Ok(Self::EvalParam(Box::new(x.apply_args(args)?))),
843            Self::EvalBuiltIn(x) => Ok(Self::EvalBuiltIn(Box::new(x.apply_args(args)?))),
844            Self::EvalCoerce(x) => Ok(Self::EvalCoerce(Box::new(x.apply_args(args)?))),
845            Self::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.apply_args(args)?))),
846            Self::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.apply_args(args)?))),
847
848            // Don't fall into the temptation of simplifying the following cases under a single
849            // wildcard with a default implementation, it makes it really hard to detect missing
850            // implementation when adding new `Expression` variants.
851            Self::None => Ok(self),
852            Self::Bytes(_) => Ok(self),
853            Self::Number(_) => Ok(self),
854            Self::Bool(_) => Ok(self),
855            Self::String(_) => Ok(self),
856            Self::Address(_) => Ok(self),
857            Self::Hash(_) => Ok(self),
858            Self::UtxoRefs(_) => Ok(self),
859            Self::UtxoSet(_) => Ok(self),
860        }
861    }
862
863    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
864        match self {
865            Self::List(x) => Ok(Self::List(
866                x.into_iter()
867                    .map(|x| x.apply_inputs(args))
868                    .collect::<Result<_, _>>()?,
869            )),
870            Self::Tuple(x) => Ok(Self::Tuple(Box::new((
871                x.0.apply_inputs(args)?,
872                x.1.apply_inputs(args)?,
873            )))),
874            Self::Struct(x) => Ok(Self::Struct(x.apply_inputs(args)?)),
875            Self::Assets(x) => Ok(Self::Assets(
876                x.into_iter()
877                    .map(|x| x.apply_inputs(args))
878                    .collect::<Result<_, _>>()?,
879            )),
880            Self::EvalParam(x) => Ok(Self::EvalParam(Box::new(x.apply_inputs(args)?))),
881            Self::EvalBuiltIn(x) => Ok(Self::EvalBuiltIn(Box::new(x.apply_inputs(args)?))),
882            Self::EvalCoerce(x) => Ok(Self::EvalCoerce(Box::new(x.apply_inputs(args)?))),
883            Self::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.apply_inputs(args)?))),
884            Self::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.apply_inputs(args)?))),
885
886            // Don't fall into the temptation of simplifying the following cases under a single
887            // wildcard with a default implementation, it makes it really hard to detect missing
888            // implementation when adding new `Expression` variants.
889            Self::None => Ok(self),
890            Self::Bytes(_) => Ok(self),
891            Self::Number(_) => Ok(self),
892            Self::Bool(_) => Ok(self),
893            Self::String(_) => Ok(self),
894            Self::Address(_) => Ok(self),
895            Self::Hash(_) => Ok(self),
896            Self::UtxoRefs(_) => Ok(self),
897            Self::UtxoSet(_) => Ok(self),
898        }
899    }
900
901    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
902        match self {
903            Self::List(x) => Ok(Self::List(
904                x.into_iter()
905                    .map(|x| x.apply_fees(fees))
906                    .collect::<Result<_, _>>()?,
907            )),
908            Self::Tuple(x) => Ok(Self::Tuple(Box::new((
909                x.0.apply_fees(fees)?,
910                x.1.apply_fees(fees)?,
911            )))),
912            Self::Struct(x) => Ok(Self::Struct(x.apply_fees(fees)?)),
913            Self::Assets(x) => Ok(Self::Assets(
914                x.into_iter()
915                    .map(|x| x.apply_fees(fees))
916                    .collect::<Result<_, _>>()?,
917            )),
918            Self::EvalParam(x) => Ok(Self::EvalParam(Box::new(x.apply_fees(fees)?))),
919            Self::EvalBuiltIn(x) => Ok(Self::EvalBuiltIn(Box::new(x.apply_fees(fees)?))),
920            Self::EvalCoerce(x) => Ok(Self::EvalCoerce(Box::new(x.apply_fees(fees)?))),
921            Self::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.apply_fees(fees)?))),
922            Self::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.apply_fees(fees)?))),
923
924            // Don't fall into the temptation of simplifying the following cases under a single
925            // wildcard with a default implementation, it makes it really hard to detect missing
926            // implementation when adding new `Expression` variants.
927            Self::None => Ok(self),
928            Self::Bytes(_) => Ok(self),
929            Self::Number(_) => Ok(self),
930            Self::Bool(_) => Ok(self),
931            Self::String(_) => Ok(self),
932            Self::Address(_) => Ok(self),
933            Self::Hash(_) => Ok(self),
934            Self::UtxoRefs(_) => Ok(self),
935            Self::UtxoSet(_) => Ok(self),
936        }
937    }
938
939    fn is_constant(&self) -> bool {
940        match self {
941            Self::List(x) => x.iter().all(|x| x.is_constant()),
942            Self::Tuple(x) => x.0.is_constant() && x.1.is_constant(),
943            Self::Struct(x) => x.is_constant(),
944            Self::Assets(x) => x.iter().all(|x| x.is_constant()),
945            Self::EvalParam(x) => x.is_constant(),
946            Self::EvalBuiltIn(x) => x.is_constant(),
947            Self::EvalCoerce(x) => x.is_constant(),
948            Self::EvalCompiler(_) => false,
949            Self::AdHocDirective(x) => x.is_constant(),
950
951            // Don't fall into the temptation of simplifying the following cases under a single
952            // wildcard with a default implementation, it makes it really hard to detect missing
953            // implementation when adding new `Expression` variants.
954            Self::None => true,
955            Self::Bytes(_) => true,
956            Self::Number(_) => true,
957            Self::Bool(_) => true,
958            Self::String(_) => true,
959            Self::Address(_) => true,
960            Self::Hash(_) => true,
961            Self::UtxoRefs(_) => true,
962            Self::UtxoSet(_) => true,
963        }
964    }
965
966    fn params(&self) -> BTreeMap<String, ir::Type> {
967        match self {
968            Self::List(x) => x.iter().flat_map(|x| x.params()).collect(),
969            Self::Tuple(x) => [x.0.params(), x.1.params()].into_iter().flatten().collect(),
970            Self::Struct(x) => x.params(),
971            Self::Assets(x) => x.iter().flat_map(|x| x.params()).collect(),
972            Self::EvalParam(x) => x.params(),
973            Self::EvalBuiltIn(x) => x.params(),
974            Self::EvalCoerce(x) => x.params(),
975            Self::EvalCompiler(x) => x.params(),
976            Self::AdHocDirective(x) => x.params(),
977
978            // Don't fall into the temptation of simplifying the following cases under a single
979            // wildcard with a default implementation, it makes it really hard to detect missing
980            // implementation when adding new `Expression` variants.
981            Self::None => BTreeMap::new(),
982            Self::Bytes(_) => BTreeMap::new(),
983            Self::Number(_) => BTreeMap::new(),
984            Self::Bool(_) => BTreeMap::new(),
985            Self::String(_) => BTreeMap::new(),
986            Self::Address(_) => BTreeMap::new(),
987            Self::Hash(_) => BTreeMap::new(),
988            Self::UtxoRefs(_) => BTreeMap::new(),
989            Self::UtxoSet(_) => BTreeMap::new(),
990        }
991    }
992
993    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
994        match self {
995            Self::List(x) => x.iter().flat_map(|x| x.queries()).collect(),
996            Self::Tuple(x) => [x.0.queries(), x.1.queries()]
997                .into_iter()
998                .flatten()
999                .collect(),
1000            Self::Struct(x) => x.queries(),
1001            Self::Assets(x) => x.iter().flat_map(|x| x.queries()).collect(),
1002            Self::EvalParam(x) => x.queries(),
1003            Self::EvalBuiltIn(x) => x.queries(),
1004            Self::EvalCoerce(x) => x.queries(),
1005            Self::EvalCompiler(x) => x.queries(),
1006            Self::AdHocDirective(x) => x.queries(),
1007
1008            // Don't fall into the temptation of simplifying the following cases under a single
1009            // wildcard with a default implementation, it makes it really hard to detect missing
1010            // implementation when adding new `Expression` variants.
1011            Self::None => BTreeMap::new(),
1012            Self::Bytes(_) => BTreeMap::new(),
1013            Self::Number(_) => BTreeMap::new(),
1014            Self::Bool(_) => BTreeMap::new(),
1015            Self::String(_) => BTreeMap::new(),
1016            Self::Address(_) => BTreeMap::new(),
1017            Self::Hash(_) => BTreeMap::new(),
1018            Self::UtxoRefs(_) => BTreeMap::new(),
1019            Self::UtxoSet(_) => BTreeMap::new(),
1020        }
1021    }
1022
1023    fn reduce(self) -> Result<Self, Error> {
1024        match self {
1025            // the following expressions can only be reduced internally
1026            ir::Expression::List(x) => Ok(Self::List(
1027                x.into_iter()
1028                    .map(|x| x.reduce())
1029                    .collect::<Result<_, _>>()?,
1030            )),
1031            ir::Expression::Tuple(x) => Ok(Self::Tuple(Box::new((x.0.reduce()?, x.1.reduce()?)))),
1032            ir::Expression::Struct(x) => Ok(Self::Struct(x.reduce()?)),
1033            ir::Expression::Assets(x) => Ok(Self::Assets(
1034                x.into_iter()
1035                    .map(|x| x.reduce())
1036                    .collect::<Result<_, _>>()?,
1037            )),
1038            ir::Expression::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.reduce()?))),
1039            ir::Expression::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.reduce()?))),
1040
1041            // the following ones can be turned into simpler expressions
1042            ir::Expression::EvalBuiltIn(x) => match x.reduce()? {
1043                ir::BuiltInOp::NoOp(x) => Ok(x),
1044                x => Ok(ir::Expression::EvalBuiltIn(Box::new(x.reduce()?))),
1045            },
1046            ir::Expression::EvalCoerce(x) => match x.reduce()? {
1047                ir::Coerce::NoOp(x) => Ok(x),
1048                x => Ok(ir::Expression::EvalCoerce(Box::new(x.reduce()?))),
1049            },
1050            ir::Expression::EvalParam(x) => match x.reduce()? {
1051                ir::Param::Set(x) => Ok(x),
1052                x => Ok(ir::Expression::EvalParam(Box::new(x.reduce()?))),
1053            },
1054
1055            // Don't fall into the temptation of simplifying the following cases under a single
1056            // wildcard with a default implementation, it makes it really hard to detect missing
1057            // implementation when adding new `Expression` variants.
1058            Self::None => Ok(self),
1059            Self::Bytes(_) => Ok(self),
1060            Self::Number(_) => Ok(self),
1061            Self::Bool(_) => Ok(self),
1062            Self::String(_) => Ok(self),
1063            Self::Address(_) => Ok(self),
1064            Self::Hash(_) => Ok(self),
1065            Self::UtxoRefs(_) => Ok(self),
1066            Self::UtxoSet(_) => Ok(self),
1067        }
1068    }
1069}
1070
1071impl Composite for ir::Output {
1072    fn components(&self) -> Vec<&ir::Expression> {
1073        vec![&self.address, &self.datum, &self.amount]
1074    }
1075
1076    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1077    where
1078        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1079    {
1080        Ok(Self {
1081            address: f(self.address)?,
1082            datum: f(self.datum)?,
1083            amount: f(self.amount)?,
1084        })
1085    }
1086}
1087
1088impl Composite for ir::Mint {
1089    fn components(&self) -> Vec<&ir::Expression> {
1090        vec![&self.amount, &self.redeemer]
1091    }
1092
1093    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1094    where
1095        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1096    {
1097        Ok(Self {
1098            amount: f(self.amount)?,
1099            redeemer: f(self.redeemer)?,
1100        })
1101    }
1102}
1103
1104impl Composite for ir::AdHocDirective {
1105    fn components(&self) -> Vec<&ir::Expression> {
1106        self.data.values().collect()
1107    }
1108
1109    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1110    where
1111        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1112    {
1113        Ok(Self {
1114            name: self.name,
1115            data: self
1116                .data
1117                .into_iter()
1118                .map(|(k, v)| f(v).map(|v| (k, v)))
1119                .collect::<Result<_, _>>()?,
1120        })
1121    }
1122}
1123
1124impl Composite for ir::CompilerOp {
1125    fn components(&self) -> Vec<&ir::Expression> {
1126        match self {
1127            ir::CompilerOp::BuildScriptAddress(x) => vec![x],
1128            ir::CompilerOp::ComputeMinUtxo(x) => vec![x],
1129        }
1130    }
1131
1132    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1133    where
1134        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1135    {
1136        match self {
1137            ir::CompilerOp::BuildScriptAddress(x) => Ok(ir::CompilerOp::BuildScriptAddress(f(x)?)),
1138            ir::CompilerOp::ComputeMinUtxo(x) => Ok(ir::CompilerOp::ComputeMinUtxo(f(x)?)),
1139        }
1140    }
1141}
1142
1143impl Composite for ir::Signers {
1144    fn components(&self) -> Vec<&ir::Expression> {
1145        self.signers.iter().collect()
1146    }
1147
1148    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1149    where
1150        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1151    {
1152        Ok(Self {
1153            signers: self.signers.into_iter().map(f).collect::<Result<_, _>>()?,
1154        })
1155    }
1156}
1157
1158impl Composite for ir::Collateral {
1159    fn components(&self) -> Vec<&ir::Expression> {
1160        vec![&self.utxos]
1161    }
1162
1163    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1164    where
1165        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1166    {
1167        Ok(Self {
1168            utxos: f(self.utxos)?,
1169        })
1170    }
1171}
1172
1173impl Composite for ir::Validity {
1174    fn components(&self) -> Vec<&ir::Expression> {
1175        vec![&self.since, &self.until]
1176    }
1177
1178    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1179    where
1180        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1181    {
1182        Ok(Self {
1183            since: f(self.since)?,
1184            until: f(self.until)?,
1185        })
1186    }
1187}
1188
1189impl Composite for ir::Metadata {
1190    fn components(&self) -> Vec<&ir::Expression> {
1191        vec![&self.key, &self.value]
1192    }
1193
1194    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1195    where
1196        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1197    {
1198        Ok(Self {
1199            key: f(self.key)?,
1200            value: f(self.value)?,
1201        })
1202    }
1203}
1204
1205impl Apply for ir::Tx {
1206    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
1207        let tx = ir::Tx {
1208            references: self.references.apply_args(args)?,
1209            inputs: self.inputs.apply_args(args)?,
1210            outputs: self.outputs.apply_args(args)?,
1211            validity: self.validity.apply_args(args)?,
1212            mints: self.mints.apply_args(args)?,
1213            burns: self.burns.apply_args(args)?,
1214            fees: self.fees.apply_args(args)?,
1215            adhoc: self.adhoc.apply_args(args)?,
1216            collateral: self.collateral.apply_args(args)?,
1217            signers: self.signers.apply_args(args)?,
1218            metadata: self.metadata.apply_args(args)?,
1219        };
1220
1221        Ok(tx)
1222    }
1223
1224    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
1225        Ok(Self {
1226            references: self.references.apply_inputs(args)?,
1227            inputs: self.inputs.apply_inputs(args)?,
1228            outputs: self.outputs.apply_inputs(args)?,
1229            validity: self.validity.apply_inputs(args)?,
1230            mints: self.mints.apply_inputs(args)?,
1231            burns: self.burns.apply_inputs(args)?,
1232            fees: self.fees.apply_inputs(args)?,
1233            adhoc: self.adhoc.apply_inputs(args)?,
1234            collateral: self.collateral.apply_inputs(args)?,
1235            signers: self.signers.apply_inputs(args)?,
1236            metadata: self.metadata.apply_inputs(args)?,
1237        })
1238    }
1239
1240    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
1241        Ok(Self {
1242            references: self.references.apply_fees(fees)?,
1243            inputs: self.inputs.apply_fees(fees)?,
1244            outputs: self.outputs.apply_fees(fees)?,
1245            validity: self.validity.apply_fees(fees)?,
1246            mints: self.mints.apply_fees(fees)?,
1247            burns: self.burns.apply_fees(fees)?,
1248            fees: self.fees.apply_fees(fees)?,
1249            adhoc: self.adhoc.apply_fees(fees)?,
1250            collateral: self.collateral.apply_fees(fees)?,
1251            signers: self.signers.apply_fees(fees)?,
1252            metadata: self.metadata.apply_fees(fees)?,
1253        })
1254    }
1255
1256    fn is_constant(&self) -> bool {
1257        self.inputs.iter().all(|x| x.is_constant())
1258            && self.outputs.iter().all(|x| x.is_constant())
1259            && self.mints.iter().all(|x| x.is_constant())
1260            && self.burns.iter().all(|x| x.is_constant())
1261            && self.fees.is_constant()
1262            && self.metadata.is_constant()
1263            && self.validity.is_constant()
1264            && self.references.is_constant()
1265            && self.collateral.is_constant()
1266            && self.adhoc.iter().all(|x| x.is_constant())
1267            && self.signers.is_constant()
1268    }
1269
1270    fn params(&self) -> BTreeMap<String, ir::Type> {
1271        // TODO: analyze if necessary to add ref_inputs
1272        let mut params = BTreeMap::new();
1273        params.extend(self.inputs.params());
1274        params.extend(self.outputs.params());
1275        params.extend(self.mints.params());
1276        params.extend(self.burns.params());
1277        params.extend(self.fees.params());
1278        params.extend(self.adhoc.params());
1279        params.extend(self.signers.params());
1280        params.extend(self.validity.params());
1281        params.extend(self.metadata.params());
1282        params.extend(self.references.params());
1283        params.extend(self.collateral.params());
1284        params
1285    }
1286
1287    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
1288        let mut queries = BTreeMap::new();
1289        queries.extend(self.inputs.queries());
1290        queries.extend(self.outputs.queries());
1291        queries.extend(self.mints.queries());
1292        queries.extend(self.burns.queries());
1293        queries.extend(self.fees.queries());
1294        queries.extend(self.adhoc.queries());
1295        queries.extend(self.signers.queries());
1296        queries.extend(self.validity.queries());
1297        queries.extend(self.metadata.queries());
1298        queries.extend(self.collateral.queries());
1299        queries.extend(self.references.queries());
1300        queries
1301    }
1302
1303    fn reduce(self) -> Result<Self, Error> {
1304        Ok(Self {
1305            references: self.references.reduce()?,
1306            inputs: self.inputs.reduce()?,
1307            outputs: self.outputs.reduce()?,
1308            validity: self.validity.reduce()?,
1309            mints: self.mints.reduce()?,
1310            burns: self.burns.reduce()?,
1311            fees: self.fees.reduce()?,
1312            adhoc: self.adhoc.reduce()?,
1313            collateral: self.collateral.reduce()?,
1314            signers: self.signers.reduce()?,
1315            metadata: self.metadata.reduce()?,
1316        })
1317    }
1318}
1319
1320pub fn apply_args(template: ir::Tx, args: &BTreeMap<String, ArgValue>) -> Result<ir::Tx, Error> {
1321    template.apply_args(args)
1322}
1323
1324pub fn apply_inputs(
1325    template: ir::Tx,
1326    args: &BTreeMap<String, HashSet<Utxo>>,
1327) -> Result<ir::Tx, Error> {
1328    template.apply_inputs(args)
1329}
1330
1331pub fn apply_fees(template: ir::Tx, fees: u64) -> Result<ir::Tx, Error> {
1332    template.apply_fees(fees)
1333}
1334
1335pub fn reduce(template: ir::Tx) -> Result<ir::Tx, Error> {
1336    template.reduce()
1337}
1338
1339pub fn find_params(template: &ir::Tx) -> BTreeMap<String, ir::Type> {
1340    template.params()
1341}
1342
1343pub fn find_queries(template: &ir::Tx) -> BTreeMap<String, ir::InputQuery> {
1344    template.queries()
1345}
1346
1347#[cfg(test)]
1348mod tests {
1349
1350    use crate::UtxoRef;
1351
1352    use super::*;
1353
1354    const SUBJECT_PROTOCOL: &str = r#"
1355        party Sender;
1356
1357        tx swap(a: Int, b: Int) {
1358            input source {
1359                from: Sender,
1360                min_amount: Ada(a) + Ada(b) + fees,
1361            }
1362        }
1363    "#;
1364
1365    #[test]
1366    fn param_expression_is_applied() {
1367        let ir = ir::Expression::EvalParam(Box::new(ir::Param::ExpectValue(
1368            "a".to_string(),
1369            ir::Type::Int,
1370        )));
1371
1372        let params = ir.params();
1373        assert_eq!(params.len(), 1);
1374        assert_eq!(params.get("a"), Some(&ir::Type::Int));
1375
1376        let args = BTreeMap::from([("a".to_string(), ArgValue::Int(100))]);
1377
1378        let after = ir.apply_args(&args).unwrap();
1379
1380        assert_eq!(
1381            after,
1382            ir::Expression::EvalParam(Box::new(ir::Param::Set(ir::Expression::Number(100),)))
1383        );
1384    }
1385
1386    #[test]
1387    fn nested_param_expression_is_applied() {
1388        let ir = ir::Expression::EvalParam(Box::new(ir::Param::ExpectInput(
1389            "out".to_string(),
1390            ir::InputQuery {
1391                address: ir::Expression::None,
1392                min_amount: ir::Expression::None,
1393                r#ref: ir::Expression::EvalParam(Box::new(ir::Param::ExpectValue(
1394                    "in".to_string(),
1395                    ir::Type::Int,
1396                ))),
1397                many: false,
1398                collateral: false,
1399            },
1400        )));
1401
1402        let params = ir.params();
1403        assert_eq!(params.len(), 1);
1404        assert_eq!(params.get("in"), Some(&ir::Type::Int));
1405
1406        let args = BTreeMap::from([("in".to_string(), ArgValue::Int(100))]);
1407        let after = ir.apply_args(&args).unwrap();
1408
1409        let after = after.reduce().unwrap();
1410
1411        let queries = after.queries();
1412        assert_eq!(queries.len(), 1);
1413        assert_eq!(
1414            queries.get("out"),
1415            Some(&ir::InputQuery {
1416                address: ir::Expression::None,
1417                min_amount: ir::Expression::None,
1418                r#ref: ir::Expression::Number(100),
1419                many: false,
1420                collateral: false,
1421            })
1422        );
1423    }
1424
1425    #[test]
1426    fn param_expression_is_reduced() {
1427        let ir = ir::Expression::EvalParam(Box::new(ir::Param::Set(ir::Expression::Number(3))));
1428
1429        let after = ir.reduce().unwrap();
1430
1431        assert_eq!(after, ir::Expression::Number(3));
1432    }
1433
1434    #[test]
1435    fn test_apply_args() {
1436        let mut ast = crate::parsing::parse_string(SUBJECT_PROTOCOL).unwrap();
1437        crate::analyzing::analyze(&mut ast).ok().unwrap();
1438
1439        let before = crate::lowering::lower(&ast, "swap").unwrap();
1440
1441        dbg!(&before);
1442
1443        let params = find_params(&before);
1444        assert_eq!(params.len(), 3);
1445        assert_eq!(params.get("sender"), Some(&ir::Type::Address));
1446        assert_eq!(params.get("a"), Some(&ir::Type::Int));
1447        assert_eq!(params.get("b"), Some(&ir::Type::Int));
1448
1449        let args = BTreeMap::from([
1450            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1451            ("a".to_string(), ArgValue::Int(100)),
1452            ("b".to_string(), ArgValue::Int(200)),
1453        ]);
1454
1455        let after = apply_args(before, &args).unwrap();
1456
1457        let params = find_params(&after);
1458        assert_eq!(params.len(), 0);
1459    }
1460
1461    #[test]
1462    fn test_apply_inputs() {
1463        let mut ast = crate::parsing::parse_string(SUBJECT_PROTOCOL).unwrap();
1464        crate::analyzing::analyze(&mut ast).ok().unwrap();
1465
1466        let before = crate::lowering::lower(&ast, "swap").unwrap();
1467
1468        let args = BTreeMap::from([
1469            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1470            ("a".to_string(), ArgValue::Int(100)),
1471            ("b".to_string(), ArgValue::Int(200)),
1472        ]);
1473
1474        let before = apply_args(before, &args).unwrap();
1475
1476        let before = before.reduce().unwrap();
1477
1478        let queries = find_queries(&before);
1479        dbg!(&queries);
1480
1481        assert_eq!(queries.len(), 1);
1482        assert!(queries.contains_key("source"));
1483
1484        let inputs = BTreeMap::from([(
1485            "source".to_string(),
1486            HashSet::from([Utxo {
1487                r#ref: UtxoRef::new(b"abc", 0),
1488                address: b"abc".to_vec(),
1489                datum: None,
1490                assets: CanonicalAssets::from_naked_amount(300),
1491                script: None,
1492            }]),
1493        )]);
1494
1495        let after = apply_inputs(before, &inputs).unwrap();
1496
1497        let queries = find_queries(&after);
1498        dbg!(&queries);
1499
1500        assert_eq!(queries.len(), 0);
1501    }
1502
1503    #[test]
1504    fn test_apply_fees() {
1505        let mut ast = crate::parsing::parse_string(SUBJECT_PROTOCOL).unwrap();
1506        crate::analyzing::analyze(&mut ast).ok().unwrap();
1507
1508        let before = crate::lowering::lower(&ast, "swap").unwrap();
1509
1510        let args = BTreeMap::from([
1511            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1512            ("a".to_string(), ArgValue::Int(100)),
1513            ("b".to_string(), ArgValue::Int(200)),
1514        ]);
1515
1516        let before = apply_args(before, &args).unwrap().reduce().unwrap();
1517
1518        let after = before.apply_fees(100).unwrap().reduce().unwrap();
1519
1520        let queries = find_queries(&after);
1521
1522        let query = queries.get("source").unwrap();
1523
1524        assert_eq!(
1525            query,
1526            &ir::InputQuery {
1527                address: ir::Expression::Address(b"abc".to_vec()),
1528                min_amount: ir::Expression::Assets(vec![ir::AssetExpr {
1529                    policy: ir::Expression::None,
1530                    asset_name: ir::Expression::None,
1531                    amount: ir::Expression::Number(400),
1532                }]),
1533                r#ref: ir::Expression::None,
1534                many: false,
1535                collateral: false,
1536            }
1537        );
1538    }
1539
1540    #[test]
1541    fn built_in_expression_is_reduced() {
1542        let op =
1543            ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::NoOp(ir::Expression::Number(5))));
1544
1545        let after = op.reduce().unwrap();
1546
1547        assert_eq!(after, ir::Expression::Number(5))
1548    }
1549
1550    #[test]
1551    fn numeric_add_is_reduced() {
1552        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1553            ir::Expression::Number(1),
1554            ir::Expression::Number(5),
1555        )));
1556
1557        let after = op.reduce().unwrap();
1558
1559        assert_eq!(after, ir::Expression::Number(6));
1560    }
1561
1562    #[test]
1563    fn numeric_sub_is_reduced() {
1564        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Sub(
1565            ir::Expression::Number(8),
1566            ir::Expression::Number(5),
1567        )));
1568
1569        let after = op.reduce().unwrap();
1570
1571        assert_eq!(after, ir::Expression::Number(3));
1572    }
1573
1574    #[test]
1575    fn nested_numeric_binary_op_is_reduced() {
1576        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1577            ir::Expression::Number(1),
1578            ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Sub(
1579                ir::Expression::Number(5),
1580                ir::Expression::Number(3),
1581            ))),
1582        )));
1583
1584        let after = op.reduce().unwrap();
1585
1586        assert_eq!(after, ir::Expression::Number(3));
1587    }
1588
1589    #[test]
1590    fn test_reduce_single_custom_asset_binary_op() {
1591        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1592            ir::Expression::Assets(vec![ir::AssetExpr {
1593                policy: ir::Expression::Bytes(b"abc".to_vec()),
1594                asset_name: ir::Expression::Bytes(b"111".to_vec()),
1595                amount: ir::Expression::Number(100),
1596            }]),
1597            ir::Expression::Assets(vec![ir::AssetExpr {
1598                policy: ir::Expression::Bytes(b"abc".to_vec()),
1599                asset_name: ir::Expression::Bytes(b"111".to_vec()),
1600                amount: ir::Expression::Number(200),
1601            }]),
1602        )));
1603
1604        let reduced = op.reduce().unwrap();
1605
1606        match reduced {
1607            ir::Expression::Assets(assets) => {
1608                assert_eq!(assets.len(), 1);
1609                assert_eq!(assets[0].policy, ir::Expression::Bytes(b"abc".to_vec()));
1610                assert_eq!(assets[0].asset_name, ir::Expression::Bytes(b"111".to_vec()));
1611                assert_eq!(assets[0].amount, ir::Expression::Number(300));
1612            }
1613            _ => panic!("Expected assets"),
1614        };
1615    }
1616
1617    #[test]
1618    fn test_reduce_native_asset_binary_op() {
1619        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1620            ir::Expression::Assets(vec![ir::AssetExpr {
1621                policy: ir::Expression::None,
1622                asset_name: ir::Expression::None,
1623                amount: ir::Expression::Number(100),
1624            }]),
1625            ir::Expression::Assets(vec![ir::AssetExpr {
1626                policy: ir::Expression::None,
1627                asset_name: ir::Expression::None,
1628                amount: ir::Expression::Number(200),
1629            }]),
1630        )));
1631
1632        let reduced = op.reduce().unwrap();
1633
1634        match reduced {
1635            ir::Expression::Assets(assets) => {
1636                assert_eq!(assets.len(), 1);
1637                assert_eq!(assets[0].policy, ir::Expression::None);
1638                assert_eq!(assets[0].asset_name, ir::Expression::None);
1639                assert_eq!(assets[0].amount, ir::Expression::Number(300));
1640            }
1641            _ => panic!("Expected assets"),
1642        };
1643    }
1644
1645    #[test]
1646    fn test_reduce_mixed_asset_binary_op() {
1647        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1648            ir::Expression::Assets(vec![ir::AssetExpr {
1649                policy: ir::Expression::None,
1650                asset_name: ir::Expression::None,
1651                amount: ir::Expression::Number(100),
1652            }]),
1653            ir::Expression::Assets(vec![ir::AssetExpr {
1654                policy: ir::Expression::Bytes(b"abc".to_vec()),
1655                asset_name: ir::Expression::Bytes(b"111".to_vec()),
1656                amount: ir::Expression::Number(200),
1657            }]),
1658        )));
1659
1660        let reduced = op.reduce().unwrap();
1661
1662        match reduced {
1663            ir::Expression::Assets(assets) => {
1664                assert_eq!(assets.len(), 2);
1665
1666                for asset in assets {
1667                    if asset.policy == ir::Expression::None {
1668                        assert_eq!(asset.asset_name, ir::Expression::None);
1669                        assert_eq!(asset.amount, ir::Expression::Number(100));
1670                    } else {
1671                        assert_eq!(asset.policy, ir::Expression::Bytes(b"abc".to_vec()));
1672                        assert_eq!(asset.asset_name, ir::Expression::Bytes(b"111".to_vec()));
1673                        assert_eq!(asset.amount, ir::Expression::Number(200));
1674                    }
1675                }
1676            }
1677            _ => panic!("Expected assets"),
1678        };
1679    }
1680
1681    #[test]
1682    fn test_reduce_coerce_noop() {
1683        let op = ir::Expression::EvalCoerce(Box::new(ir::Coerce::NoOp(ir::Expression::Number(5))));
1684
1685        let reduced = op.reduce().unwrap();
1686
1687        match reduced {
1688            ir::Expression::Number(5) => (),
1689            _ => panic!("Expected number 5"),
1690        };
1691    }
1692
1693    #[test]
1694    fn test_coerce_utxo_set_into_assets() {
1695        let utxos = vec![Utxo {
1696            r#ref: UtxoRef::new(b"abc", 1),
1697            address: b"abc".into(),
1698            datum: Some(ir::Expression::Number(1)),
1699            assets: CanonicalAssets::from_defined_asset(b"abc", b"111", 1),
1700            script: None,
1701        }];
1702
1703        let op = ir::Coerce::IntoAssets(ir::Expression::UtxoSet(HashSet::from_iter(
1704            utxos.clone().into_iter(),
1705        )));
1706
1707        let reduced = op.reduce().unwrap();
1708
1709        assert_eq!(
1710            reduced,
1711            ir::Coerce::NoOp(ir::Expression::Assets(utxos[0].assets.clone().into()))
1712        );
1713    }
1714
1715    #[test]
1716    fn test_coerce_utxo_set_into_datum() {
1717        let utxos = vec![Utxo {
1718            r#ref: UtxoRef::new(b"abc", 1),
1719            address: b"abc".into(),
1720            datum: Some(ir::Expression::Number(1)),
1721            assets: CanonicalAssets::from_naked_amount(1),
1722            script: None,
1723        }];
1724
1725        let op = ir::Coerce::IntoDatum(ir::Expression::UtxoSet(HashSet::from_iter(
1726            utxos.clone().into_iter(),
1727        )));
1728
1729        let reduced = op.reduce().unwrap();
1730
1731        assert_eq!(reduced, ir::Coerce::NoOp(utxos[0].datum.clone().unwrap()));
1732    }
1733
1734    #[test]
1735    fn test_reduce_struct_property_access() {
1736        let object = ir::Expression::Struct(ir::StructExpr {
1737            constructor: 0,
1738            fields: vec![
1739                ir::Expression::Number(1),
1740                ir::Expression::Number(2),
1741                ir::Expression::Number(3),
1742            ],
1743        });
1744
1745        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(object.clone(), 1)));
1746
1747        let reduced = op.reduce();
1748
1749        match reduced {
1750            Ok(ir::Expression::Number(2)) => (),
1751            _ => panic!("Expected number 2"),
1752        };
1753
1754        let op =
1755            ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(object.clone(), 100)));
1756
1757        let reduced = op.reduce();
1758
1759        match reduced {
1760            Err(Error::PropertyIndexNotFound(100, _)) => (),
1761            _ => panic!("Expected property index not found"),
1762        };
1763    }
1764
1765    #[test]
1766    fn test_reduce_list_property_access() {
1767        let object = ir::Expression::List(vec![
1768            ir::Expression::Number(1),
1769            ir::Expression::Number(2),
1770            ir::Expression::Number(3),
1771        ]);
1772
1773        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(object.clone(), 1)));
1774
1775        let reduced = op.reduce();
1776
1777        match reduced {
1778            Ok(ir::Expression::Number(2)) => (),
1779            _ => panic!("Expected number 2"),
1780        };
1781
1782        let op =
1783            ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(object.clone(), 100)));
1784
1785        let reduced = op.reduce();
1786
1787        match reduced {
1788            Err(Error::PropertyIndexNotFound(100, _)) => (),
1789            _ => panic!("Expected property index not found"),
1790        };
1791    }
1792
1793    #[test]
1794    fn test_reduce_tuple_property_access() {
1795        let object = ir::Expression::Tuple(Box::new((
1796            ir::Expression::Number(1),
1797            ir::Expression::Number(2),
1798        )));
1799
1800        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(object.clone(), 1)));
1801
1802        let reduced = op.reduce();
1803
1804        match reduced {
1805            Ok(ir::Expression::Number(2)) => (),
1806            _ => panic!("Expected number 2"),
1807        };
1808
1809        let op =
1810            ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(object.clone(), 100)));
1811
1812        let reduced = op.reduce();
1813
1814        match reduced {
1815            Err(Error::PropertyIndexNotFound(100, _)) => (),
1816            _ => panic!("Expected property index not found"),
1817        };
1818    }
1819
1820    #[test]
1821    fn test_string_concat_is_reduced() {
1822        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Concat(
1823            ir::Expression::String("hello".to_string()),
1824            ir::Expression::String("world".to_string()),
1825        )));
1826
1827        let reduced = op.reduce().unwrap();
1828
1829        match reduced {
1830            ir::Expression::String(s) => assert_eq!(s, "helloworld"),
1831            _ => panic!("Expected string 'helloworld'"),
1832        }
1833    }
1834
1835    #[test]
1836    fn test_bytes_concat_is_reduced() {
1837        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Concat(
1838            ir::Expression::Bytes(vec![1, 2, 3]),
1839            ir::Expression::Bytes(vec![4, 5, 6]),
1840        )));
1841
1842        let reduced = op.reduce().unwrap();
1843
1844        match reduced {
1845            ir::Expression::Bytes(b) => assert_eq!(b, vec![1, 2, 3, 4, 5, 6]),
1846            _ => panic!("Expected bytes [1, 2, 3, 4, 5, 6]"),
1847        }
1848    }
1849
1850    #[test]
1851    fn test_concat_type_mismatch_error() {
1852        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Concat(
1853            ir::Expression::String("hello".to_string()),
1854            ir::Expression::Bytes(vec![1, 2, 3]),
1855        )));
1856
1857        let reduced = op.reduce();
1858
1859        match reduced {
1860            Err(Error::InvalidBinaryOp(op, _, _)) => assert_eq!(op, "concat"),
1861            _ => panic!("Expected InvalidBinaryOp error"),
1862        }
1863    }
1864
1865    #[test]
1866    fn test_concat_with_none() {
1867        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Concat(
1868            ir::Expression::String("hello".to_string()),
1869            ir::Expression::None,
1870        )));
1871
1872        let reduced = op.reduce().unwrap();
1873
1874        match reduced {
1875            ir::Expression::String(s) => assert_eq!(s, "hello"),
1876            _ => panic!("Expected string 'hello'"),
1877        }
1878    }
1879
1880    #[test]
1881    fn test_min_utxo_add_non_reduction() {
1882        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1883            ir::Expression::Assets(vec![ir::AssetExpr {
1884                policy: ir::Expression::None,
1885                asset_name: ir::Expression::None,
1886                amount: ir::Expression::Number(29),
1887            }]),
1888            ir::Expression::EvalCompiler(Box::new(ir::CompilerOp::ComputeMinUtxo(
1889                ir::Expression::Number(20),
1890            ))),
1891        )));
1892
1893        let reduced = op.clone().reduce().unwrap();
1894
1895        assert!(op == reduced)
1896    }
1897
1898    #[test]
1899    fn test_min_utxo_sub_non_reduction() {
1900        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Sub(
1901            ir::Expression::Assets(vec![ir::AssetExpr {
1902                policy: ir::Expression::None,
1903                asset_name: ir::Expression::None,
1904                amount: ir::Expression::Number(29),
1905            }]),
1906            ir::Expression::EvalCompiler(Box::new(ir::CompilerOp::ComputeMinUtxo(
1907                ir::Expression::Number(20),
1908            ))),
1909        )));
1910
1911        let reduced = op.clone().reduce().unwrap();
1912
1913        assert!(op == reduced)
1914    }
1915}