tx3_lang/
applying.rs

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