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.0.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(x) => x.is_constant(),
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        }
1129    }
1130
1131    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1132    where
1133        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1134    {
1135        match self {
1136            ir::CompilerOp::BuildScriptAddress(x) => Ok(ir::CompilerOp::BuildScriptAddress(f(x)?)),
1137        }
1138    }
1139}
1140
1141impl Composite for ir::Signers {
1142    fn components(&self) -> Vec<&ir::Expression> {
1143        self.signers.iter().collect()
1144    }
1145
1146    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1147    where
1148        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1149    {
1150        Ok(Self {
1151            signers: self.signers.into_iter().map(f).collect::<Result<_, _>>()?,
1152        })
1153    }
1154}
1155
1156impl Composite for ir::Collateral {
1157    fn components(&self) -> Vec<&ir::Expression> {
1158        vec![&self.utxos]
1159    }
1160
1161    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1162    where
1163        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1164    {
1165        Ok(Self {
1166            utxos: f(self.utxos)?,
1167        })
1168    }
1169}
1170
1171impl Composite for ir::Validity {
1172    fn components(&self) -> Vec<&ir::Expression> {
1173        vec![&self.since, &self.until]
1174    }
1175
1176    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1177    where
1178        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1179    {
1180        Ok(Self {
1181            since: f(self.since)?,
1182            until: f(self.until)?,
1183        })
1184    }
1185}
1186
1187impl Composite for ir::Metadata {
1188    fn components(&self) -> Vec<&ir::Expression> {
1189        vec![&self.key, &self.value]
1190    }
1191
1192    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1193    where
1194        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1195    {
1196        Ok(Self {
1197            key: f(self.key)?,
1198            value: f(self.value)?,
1199        })
1200    }
1201}
1202
1203impl Apply for ir::Tx {
1204    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
1205        let tx = ir::Tx {
1206            references: self.references.apply_args(args)?,
1207            inputs: self.inputs.apply_args(args)?,
1208            outputs: self.outputs.apply_args(args)?,
1209            validity: self.validity.apply_args(args)?,
1210            mints: self.mints.apply_args(args)?,
1211            burns: self.burns.apply_args(args)?,
1212            fees: self.fees.apply_args(args)?,
1213            adhoc: self.adhoc.apply_args(args)?,
1214            collateral: self.collateral.apply_args(args)?,
1215            signers: self.signers.apply_args(args)?,
1216            metadata: self.metadata.apply_args(args)?,
1217        };
1218
1219        Ok(tx)
1220    }
1221
1222    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
1223        Ok(Self {
1224            references: self.references.apply_inputs(args)?,
1225            inputs: self.inputs.apply_inputs(args)?,
1226            outputs: self.outputs.apply_inputs(args)?,
1227            validity: self.validity.apply_inputs(args)?,
1228            mints: self.mints.apply_inputs(args)?,
1229            burns: self.burns.apply_inputs(args)?,
1230            fees: self.fees.apply_inputs(args)?,
1231            adhoc: self.adhoc.apply_inputs(args)?,
1232            collateral: self.collateral.apply_inputs(args)?,
1233            signers: self.signers.apply_inputs(args)?,
1234            metadata: self.metadata.apply_inputs(args)?,
1235        })
1236    }
1237
1238    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
1239        Ok(Self {
1240            references: self.references.apply_fees(fees)?,
1241            inputs: self.inputs.apply_fees(fees)?,
1242            outputs: self.outputs.apply_fees(fees)?,
1243            validity: self.validity.apply_fees(fees)?,
1244            mints: self.mints.apply_fees(fees)?,
1245            burns: self.burns.apply_fees(fees)?,
1246            fees: self.fees.apply_fees(fees)?,
1247            adhoc: self.adhoc.apply_fees(fees)?,
1248            collateral: self.collateral.apply_fees(fees)?,
1249            signers: self.signers.apply_fees(fees)?,
1250            metadata: self.metadata.apply_fees(fees)?,
1251        })
1252    }
1253
1254    fn is_constant(&self) -> bool {
1255        self.inputs.iter().all(|x| x.is_constant())
1256            && self.outputs.iter().all(|x| x.is_constant())
1257            && self.mints.iter().all(|x| x.is_constant())
1258            && self.burns.iter().all(|x| x.is_constant())
1259            && self.fees.is_constant()
1260            && self.metadata.is_constant()
1261            && self.validity.is_constant()
1262            && self.references.is_constant()
1263            && self.collateral.is_constant()
1264            && self.adhoc.iter().all(|x| x.is_constant())
1265            && self.signers.is_constant()
1266    }
1267
1268    fn params(&self) -> BTreeMap<String, ir::Type> {
1269        // TODO: analyze if necessary to add ref_inputs
1270        let mut params = BTreeMap::new();
1271        params.extend(self.inputs.params());
1272        params.extend(self.outputs.params());
1273        params.extend(self.mints.params());
1274        params.extend(self.burns.params());
1275        params.extend(self.fees.params());
1276        params.extend(self.adhoc.params());
1277        params.extend(self.signers.params());
1278        params.extend(self.validity.params());
1279        params.extend(self.metadata.params());
1280        params.extend(self.references.params());
1281        params.extend(self.collateral.params());
1282        params
1283    }
1284
1285    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
1286        let mut queries = BTreeMap::new();
1287        queries.extend(self.inputs.queries());
1288        queries.extend(self.outputs.queries());
1289        queries.extend(self.mints.queries());
1290        queries.extend(self.burns.queries());
1291        queries.extend(self.fees.queries());
1292        queries.extend(self.adhoc.queries());
1293        queries.extend(self.signers.queries());
1294        queries.extend(self.validity.queries());
1295        queries.extend(self.metadata.queries());
1296        queries.extend(self.collateral.queries());
1297        queries.extend(self.references.queries());
1298        queries
1299    }
1300
1301    fn reduce(self) -> Result<Self, Error> {
1302        Ok(Self {
1303            references: self.references.reduce()?,
1304            inputs: self.inputs.reduce()?,
1305            outputs: self.outputs.reduce()?,
1306            validity: self.validity.reduce()?,
1307            mints: self.mints.reduce()?,
1308            burns: self.burns.reduce()?,
1309            fees: self.fees.reduce()?,
1310            adhoc: self.adhoc.reduce()?,
1311            collateral: self.collateral.reduce()?,
1312            signers: self.signers.reduce()?,
1313            metadata: self.metadata.reduce()?,
1314        })
1315    }
1316}
1317
1318pub fn apply_args(template: ir::Tx, args: &BTreeMap<String, ArgValue>) -> Result<ir::Tx, Error> {
1319    template.apply_args(args)
1320}
1321
1322pub fn apply_inputs(
1323    template: ir::Tx,
1324    args: &BTreeMap<String, HashSet<Utxo>>,
1325) -> Result<ir::Tx, Error> {
1326    template.apply_inputs(args)
1327}
1328
1329pub fn apply_fees(template: ir::Tx, fees: u64) -> Result<ir::Tx, Error> {
1330    template.apply_fees(fees)
1331}
1332
1333pub fn reduce(template: ir::Tx) -> Result<ir::Tx, Error> {
1334    template.reduce()
1335}
1336
1337pub fn find_params(template: &ir::Tx) -> BTreeMap<String, ir::Type> {
1338    template.params()
1339}
1340
1341pub fn find_queries(template: &ir::Tx) -> BTreeMap<String, ir::InputQuery> {
1342    template.queries()
1343}
1344
1345#[cfg(test)]
1346mod tests {
1347
1348    use crate::UtxoRef;
1349
1350    use super::*;
1351
1352    const SUBJECT_PROTOCOL: &str = r#"
1353        party Sender;
1354
1355        tx swap(a: Int, b: Int) {
1356            input source {
1357                from: Sender,
1358                min_amount: Ada(a) + Ada(b) + fees,
1359            }
1360        }
1361    "#;
1362
1363    #[test]
1364    fn param_expression_is_applied() {
1365        let ir = ir::Expression::EvalParam(Box::new(ir::Param::ExpectValue(
1366            "a".to_string(),
1367            ir::Type::Int,
1368        )));
1369
1370        let params = ir.params();
1371        assert_eq!(params.len(), 1);
1372        assert_eq!(params.get("a"), Some(&ir::Type::Int));
1373
1374        let args = BTreeMap::from([("a".to_string(), ArgValue::Int(100))]);
1375
1376        let after = ir.apply_args(&args).unwrap();
1377
1378        assert_eq!(
1379            after,
1380            ir::Expression::EvalParam(Box::new(ir::Param::Set(ir::Expression::Number(100),)))
1381        );
1382    }
1383
1384    #[test]
1385    fn nested_param_expression_is_applied() {
1386        let ir = ir::Expression::EvalParam(Box::new(ir::Param::ExpectInput(
1387            "out".to_string(),
1388            ir::InputQuery {
1389                address: ir::Expression::None,
1390                min_amount: ir::Expression::None,
1391                r#ref: ir::Expression::EvalParam(Box::new(ir::Param::ExpectValue(
1392                    "in".to_string(),
1393                    ir::Type::Int,
1394                ))),
1395                many: false,
1396                collateral: false,
1397            },
1398        )));
1399
1400        let params = ir.params();
1401        assert_eq!(params.len(), 1);
1402        assert_eq!(params.get("in"), Some(&ir::Type::Int));
1403
1404        let args = BTreeMap::from([("in".to_string(), ArgValue::Int(100))]);
1405        let after = ir.apply_args(&args).unwrap();
1406
1407        let after = after.reduce().unwrap();
1408
1409        let queries = after.queries();
1410        assert_eq!(queries.len(), 1);
1411        assert_eq!(
1412            queries.get("out"),
1413            Some(&ir::InputQuery {
1414                address: ir::Expression::None,
1415                min_amount: ir::Expression::None,
1416                r#ref: ir::Expression::Number(100),
1417                many: false,
1418                collateral: false,
1419            })
1420        );
1421    }
1422
1423    #[test]
1424    fn param_expression_is_reduced() {
1425        let ir = ir::Expression::EvalParam(Box::new(ir::Param::Set(ir::Expression::Number(3))));
1426
1427        let after = ir.reduce().unwrap();
1428
1429        assert_eq!(after, ir::Expression::Number(3));
1430    }
1431
1432    #[test]
1433    fn test_apply_args() {
1434        let mut ast = crate::parsing::parse_string(SUBJECT_PROTOCOL).unwrap();
1435        crate::analyzing::analyze(&mut ast).ok().unwrap();
1436
1437        let before = crate::lowering::lower(&ast, "swap").unwrap();
1438
1439        dbg!(&before);
1440
1441        let params = find_params(&before);
1442        assert_eq!(params.len(), 3);
1443        assert_eq!(params.get("sender"), Some(&ir::Type::Address));
1444        assert_eq!(params.get("a"), Some(&ir::Type::Int));
1445        assert_eq!(params.get("b"), Some(&ir::Type::Int));
1446
1447        let args = BTreeMap::from([
1448            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1449            ("a".to_string(), ArgValue::Int(100)),
1450            ("b".to_string(), ArgValue::Int(200)),
1451        ]);
1452
1453        let after = apply_args(before, &args).unwrap();
1454
1455        let params = find_params(&after);
1456        assert_eq!(params.len(), 0);
1457    }
1458
1459    #[test]
1460    fn test_apply_inputs() {
1461        let mut ast = crate::parsing::parse_string(SUBJECT_PROTOCOL).unwrap();
1462        crate::analyzing::analyze(&mut ast).ok().unwrap();
1463
1464        let before = crate::lowering::lower(&ast, "swap").unwrap();
1465
1466        let args = BTreeMap::from([
1467            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1468            ("a".to_string(), ArgValue::Int(100)),
1469            ("b".to_string(), ArgValue::Int(200)),
1470        ]);
1471
1472        let before = apply_args(before, &args).unwrap();
1473
1474        let before = before.reduce().unwrap();
1475
1476        let queries = find_queries(&before);
1477        dbg!(&queries);
1478
1479        assert_eq!(queries.len(), 1);
1480        assert!(queries.contains_key("source"));
1481
1482        let inputs = BTreeMap::from([(
1483            "source".to_string(),
1484            HashSet::from([Utxo {
1485                r#ref: UtxoRef::new(b"abc", 0),
1486                address: b"abc".to_vec(),
1487                datum: None,
1488                assets: CanonicalAssets::from_naked_amount(300),
1489                script: None,
1490            }]),
1491        )]);
1492
1493        let after = apply_inputs(before, &inputs).unwrap();
1494
1495        let queries = find_queries(&after);
1496        dbg!(&queries);
1497
1498        assert_eq!(queries.len(), 0);
1499    }
1500
1501    #[test]
1502    fn test_apply_fees() {
1503        let mut ast = crate::parsing::parse_string(SUBJECT_PROTOCOL).unwrap();
1504        crate::analyzing::analyze(&mut ast).ok().unwrap();
1505
1506        let before = crate::lowering::lower(&ast, "swap").unwrap();
1507
1508        let args = BTreeMap::from([
1509            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1510            ("a".to_string(), ArgValue::Int(100)),
1511            ("b".to_string(), ArgValue::Int(200)),
1512        ]);
1513
1514        let before = apply_args(before, &args).unwrap().reduce().unwrap();
1515
1516        let after = before.apply_fees(100).unwrap().reduce().unwrap();
1517
1518        let queries = find_queries(&after);
1519
1520        let query = queries.get("source").unwrap();
1521
1522        assert_eq!(
1523            query,
1524            &ir::InputQuery {
1525                address: ir::Expression::Address(b"abc".to_vec()),
1526                min_amount: ir::Expression::Assets(vec![ir::AssetExpr {
1527                    policy: ir::Expression::None,
1528                    asset_name: ir::Expression::None,
1529                    amount: ir::Expression::Number(400),
1530                }]),
1531                r#ref: ir::Expression::None,
1532                many: false,
1533                collateral: false,
1534            }
1535        );
1536    }
1537
1538    #[test]
1539    fn built_in_expression_is_reduced() {
1540        let op =
1541            ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::NoOp(ir::Expression::Number(5))));
1542
1543        let after = op.reduce().unwrap();
1544
1545        assert_eq!(after, ir::Expression::Number(5))
1546    }
1547
1548    #[test]
1549    fn numeric_add_is_reduced() {
1550        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1551            ir::Expression::Number(1),
1552            ir::Expression::Number(5),
1553        )));
1554
1555        let after = op.reduce().unwrap();
1556
1557        assert_eq!(after, ir::Expression::Number(6));
1558    }
1559
1560    #[test]
1561    fn numeric_sub_is_reduced() {
1562        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Sub(
1563            ir::Expression::Number(8),
1564            ir::Expression::Number(5),
1565        )));
1566
1567        let after = op.reduce().unwrap();
1568
1569        assert_eq!(after, ir::Expression::Number(3));
1570    }
1571
1572    #[test]
1573    fn nested_numeric_binary_op_is_reduced() {
1574        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1575            ir::Expression::Number(1),
1576            ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Sub(
1577                ir::Expression::Number(5),
1578                ir::Expression::Number(3),
1579            ))),
1580        )));
1581
1582        let after = op.reduce().unwrap();
1583
1584        assert_eq!(after, ir::Expression::Number(3));
1585    }
1586
1587    #[test]
1588    fn test_reduce_single_custom_asset_binary_op() {
1589        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1590            ir::Expression::Assets(vec![ir::AssetExpr {
1591                policy: ir::Expression::Bytes(b"abc".to_vec()),
1592                asset_name: ir::Expression::Bytes(b"111".to_vec()),
1593                amount: ir::Expression::Number(100),
1594            }]),
1595            ir::Expression::Assets(vec![ir::AssetExpr {
1596                policy: ir::Expression::Bytes(b"abc".to_vec()),
1597                asset_name: ir::Expression::Bytes(b"111".to_vec()),
1598                amount: ir::Expression::Number(200),
1599            }]),
1600        )));
1601
1602        let reduced = op.reduce().unwrap();
1603
1604        match reduced {
1605            ir::Expression::Assets(assets) => {
1606                assert_eq!(assets.len(), 1);
1607                assert_eq!(assets[0].policy, ir::Expression::Bytes(b"abc".to_vec()));
1608                assert_eq!(assets[0].asset_name, ir::Expression::Bytes(b"111".to_vec()));
1609                assert_eq!(assets[0].amount, ir::Expression::Number(300));
1610            }
1611            _ => panic!("Expected assets"),
1612        };
1613    }
1614
1615    #[test]
1616    fn test_reduce_native_asset_binary_op() {
1617        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1618            ir::Expression::Assets(vec![ir::AssetExpr {
1619                policy: ir::Expression::None,
1620                asset_name: ir::Expression::None,
1621                amount: ir::Expression::Number(100),
1622            }]),
1623            ir::Expression::Assets(vec![ir::AssetExpr {
1624                policy: ir::Expression::None,
1625                asset_name: ir::Expression::None,
1626                amount: ir::Expression::Number(200),
1627            }]),
1628        )));
1629
1630        let reduced = op.reduce().unwrap();
1631
1632        match reduced {
1633            ir::Expression::Assets(assets) => {
1634                assert_eq!(assets.len(), 1);
1635                assert_eq!(assets[0].policy, ir::Expression::None);
1636                assert_eq!(assets[0].asset_name, ir::Expression::None);
1637                assert_eq!(assets[0].amount, ir::Expression::Number(300));
1638            }
1639            _ => panic!("Expected assets"),
1640        };
1641    }
1642
1643    #[test]
1644    fn test_reduce_mixed_asset_binary_op() {
1645        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1646            ir::Expression::Assets(vec![ir::AssetExpr {
1647                policy: ir::Expression::None,
1648                asset_name: ir::Expression::None,
1649                amount: ir::Expression::Number(100),
1650            }]),
1651            ir::Expression::Assets(vec![ir::AssetExpr {
1652                policy: ir::Expression::Bytes(b"abc".to_vec()),
1653                asset_name: ir::Expression::Bytes(b"111".to_vec()),
1654                amount: ir::Expression::Number(200),
1655            }]),
1656        )));
1657
1658        let reduced = op.reduce().unwrap();
1659
1660        match reduced {
1661            ir::Expression::Assets(assets) => {
1662                assert_eq!(assets.len(), 2);
1663
1664                for asset in assets {
1665                    if asset.policy == ir::Expression::None {
1666                        assert_eq!(asset.asset_name, ir::Expression::None);
1667                        assert_eq!(asset.amount, ir::Expression::Number(100));
1668                    } else {
1669                        assert_eq!(asset.policy, ir::Expression::Bytes(b"abc".to_vec()));
1670                        assert_eq!(asset.asset_name, ir::Expression::Bytes(b"111".to_vec()));
1671                        assert_eq!(asset.amount, ir::Expression::Number(200));
1672                    }
1673                }
1674            }
1675            _ => panic!("Expected assets"),
1676        };
1677    }
1678
1679    #[test]
1680    fn test_reduce_coerce_noop() {
1681        let op = ir::Expression::EvalCoerce(Box::new(ir::Coerce::NoOp(ir::Expression::Number(5))));
1682
1683        let reduced = op.reduce().unwrap();
1684
1685        match reduced {
1686            ir::Expression::Number(5) => (),
1687            _ => panic!("Expected number 5"),
1688        };
1689    }
1690
1691    #[test]
1692    fn test_coerce_utxo_set_into_assets() {
1693        let utxos = vec![Utxo {
1694            r#ref: UtxoRef::new(b"abc", 1),
1695            address: b"abc".into(),
1696            datum: Some(ir::Expression::Number(1)),
1697            assets: CanonicalAssets::from_defined_asset(b"abc", b"111", 1),
1698            script: None,
1699        }];
1700
1701        let op = ir::Coerce::IntoAssets(ir::Expression::UtxoSet(HashSet::from_iter(
1702            utxos.clone().into_iter(),
1703        )));
1704
1705        let reduced = op.reduce().unwrap();
1706
1707        assert_eq!(
1708            reduced,
1709            ir::Coerce::NoOp(ir::Expression::Assets(utxos[0].assets.clone().into()))
1710        );
1711    }
1712
1713    #[test]
1714    fn test_coerce_utxo_set_into_datum() {
1715        let utxos = vec![Utxo {
1716            r#ref: UtxoRef::new(b"abc", 1),
1717            address: b"abc".into(),
1718            datum: Some(ir::Expression::Number(1)),
1719            assets: CanonicalAssets::from_naked_amount(1),
1720            script: None,
1721        }];
1722
1723        let op = ir::Coerce::IntoDatum(ir::Expression::UtxoSet(HashSet::from_iter(
1724            utxos.clone().into_iter(),
1725        )));
1726
1727        let reduced = op.reduce().unwrap();
1728
1729        assert_eq!(reduced, ir::Coerce::NoOp(utxos[0].datum.clone().unwrap()));
1730    }
1731
1732    #[test]
1733    fn test_reduce_struct_property_access() {
1734        let object = ir::Expression::Struct(ir::StructExpr {
1735            constructor: 0,
1736            fields: vec![
1737                ir::Expression::Number(1),
1738                ir::Expression::Number(2),
1739                ir::Expression::Number(3),
1740            ],
1741        });
1742
1743        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(object.clone(), 1)));
1744
1745        let reduced = op.reduce();
1746
1747        match reduced {
1748            Ok(ir::Expression::Number(2)) => (),
1749            _ => panic!("Expected number 2"),
1750        };
1751
1752        let op =
1753            ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(object.clone(), 100)));
1754
1755        let reduced = op.reduce();
1756
1757        match reduced {
1758            Err(Error::PropertyIndexNotFound(100, _)) => (),
1759            _ => panic!("Expected property index not found"),
1760        };
1761    }
1762
1763    #[test]
1764    fn test_reduce_list_property_access() {
1765        let object = ir::Expression::List(vec![
1766            ir::Expression::Number(1),
1767            ir::Expression::Number(2),
1768            ir::Expression::Number(3),
1769        ]);
1770
1771        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(object.clone(), 1)));
1772
1773        let reduced = op.reduce();
1774
1775        match reduced {
1776            Ok(ir::Expression::Number(2)) => (),
1777            _ => panic!("Expected number 2"),
1778        };
1779
1780        let op =
1781            ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(object.clone(), 100)));
1782
1783        let reduced = op.reduce();
1784
1785        match reduced {
1786            Err(Error::PropertyIndexNotFound(100, _)) => (),
1787            _ => panic!("Expected property index not found"),
1788        };
1789    }
1790
1791    #[test]
1792    fn test_reduce_tuple_property_access() {
1793        let object = ir::Expression::Tuple(Box::new((
1794            ir::Expression::Number(1),
1795            ir::Expression::Number(2),
1796        )));
1797
1798        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(object.clone(), 1)));
1799
1800        let reduced = op.reduce();
1801
1802        match reduced {
1803            Ok(ir::Expression::Number(2)) => (),
1804            _ => panic!("Expected number 2"),
1805        };
1806
1807        let op =
1808            ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(object.clone(), 100)));
1809
1810        let reduced = op.reduce();
1811
1812        match reduced {
1813            Err(Error::PropertyIndexNotFound(100, _)) => (),
1814            _ => panic!("Expected property index not found"),
1815        };
1816    }
1817
1818    #[test]
1819    fn test_string_concat_is_reduced() {
1820        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Concat(
1821            ir::Expression::String("hello".to_string()),
1822            ir::Expression::String("world".to_string()),
1823        )));
1824
1825        let reduced = op.reduce().unwrap();
1826
1827        match reduced {
1828            ir::Expression::String(s) => assert_eq!(s, "helloworld"),
1829            _ => panic!("Expected string 'helloworld'"),
1830        }
1831    }
1832
1833    #[test]
1834    fn test_bytes_concat_is_reduced() {
1835        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Concat(
1836            ir::Expression::Bytes(vec![1, 2, 3]),
1837            ir::Expression::Bytes(vec![4, 5, 6]),
1838        )));
1839
1840        let reduced = op.reduce().unwrap();
1841
1842        match reduced {
1843            ir::Expression::Bytes(b) => assert_eq!(b, vec![1, 2, 3, 4, 5, 6]),
1844            _ => panic!("Expected bytes [1, 2, 3, 4, 5, 6]"),
1845        }
1846    }
1847
1848    #[test]
1849    fn test_concat_type_mismatch_error() {
1850        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Concat(
1851            ir::Expression::String("hello".to_string()),
1852            ir::Expression::Bytes(vec![1, 2, 3]),
1853        )));
1854
1855        let reduced = op.reduce();
1856
1857        match reduced {
1858            Err(Error::InvalidBinaryOp(op, _, _)) => assert_eq!(op, "concat"),
1859            _ => panic!("Expected InvalidBinaryOp error"),
1860        }
1861    }
1862
1863    #[test]
1864    fn test_concat_with_none() {
1865        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Concat(
1866            ir::Expression::String("hello".to_string()),
1867            ir::Expression::None,
1868        )));
1869
1870        let reduced = op.reduce().unwrap();
1871
1872        match reduced {
1873            ir::Expression::String(s) => assert_eq!(s, "hello"),
1874            _ => panic!("Expected string 'hello'"),
1875        }
1876    }
1877}