Skip to main content

tx3_tir/reduce/
mod.rs

1use std::collections::{BTreeMap, HashMap};
2
3use crate::model::assets::CanonicalAssets;
4use crate::model::core::*;
5use crate::model::v1beta0::*;
6
7#[derive(Debug, thiserror::Error)]
8pub enum Error {
9    #[error("invalid built-in operation {0:?}")]
10    InvalidBuiltInOp(Box<BuiltInOp>),
11
12    #[error("invalid argument {0:?} for {1}")]
13    InvalidArgument(ArgValue, String),
14
15    #[error("property {0} not found in {1}")]
16    PropertyNotFound(String, String),
17
18    #[error("property index {0} not found in {1}")]
19    PropertyIndexNotFound(usize, String),
20
21    #[error("invalid {0} operation over {1:?} and {2:?}")]
22    InvalidBinaryOp(String, String, String),
23
24    #[error("invalid {0} operation over {1:?}")]
25    InvalidUnaryOp(String, String),
26
27    #[error("cannot coerce {0:?} into assets")]
28    CannotCoerceIntoAssets(Expression),
29
30    #[error("cannot coerce {0:?} into datum")]
31    CannotCoerceIntoDatum(Expression),
32
33    #[error("compiler op failed: {0}")]
34    CompilerOpFailed(Box<crate::compile::Error>),
35}
36
37impl From<crate::compile::Error> for Error {
38    fn from(error: crate::compile::Error) -> Self {
39        Error::CompilerOpFailed(Box::new(error))
40    }
41}
42
43pub trait Indexable: std::fmt::Debug {
44    fn index(&self, index: Expression) -> Option<Expression>;
45
46    fn index_or_err(&self, index: Expression) -> Result<Expression, Error> {
47        let index_value = index.as_number().unwrap_or(0) as usize;
48        self.index(index).ok_or(Error::PropertyIndexNotFound(
49            index_value,
50            format!("{self:?}"),
51        ))
52    }
53}
54
55impl Indexable for StructExpr {
56    fn index(&self, index: Expression) -> Option<Expression> {
57        // numeric indices represent the index of the field of the struct
58        match index {
59            Expression::Number(n) => self.fields.get(n as usize).cloned(),
60            _ => return None,
61        }
62    }
63}
64
65impl Indexable for Expression {
66    fn index(&self, index: Expression) -> Option<Expression> {
67        match self {
68            Expression::None => None,
69            Expression::Map(x) => x
70                .iter()
71                .find(|(k, _)| *k == index)
72                .map(|(k, v)| Expression::Tuple(vec![k.clone(), v.clone()])),
73            Expression::List(x) => x.get(index.as_number()? as usize).cloned(),
74            Expression::Tuple(x) => x.get(index.as_number()? as usize).cloned(),
75            Expression::Struct(x) => x.index(index.clone()),
76            _ => None,
77        }
78    }
79}
80
81pub trait Concatenable {
82    fn concat(self, other: Expression) -> Result<Expression, Error>;
83}
84
85pub trait Arithmetic {
86    fn add(self, other: Expression) -> Result<Expression, Error>;
87    fn sub(self, other: Expression) -> Result<Expression, Error>;
88    fn mul(self, other: Expression) -> Result<Expression, Error>;
89    fn div(self, other: Expression) -> Result<Expression, Error>;
90    fn neg(self) -> Result<Expression, Error>;
91}
92
93impl<T> Arithmetic for T
94where
95    T: Into<CanonicalAssets> + std::fmt::Debug,
96{
97    fn add(self, other: Expression) -> Result<Expression, Error> {
98        let y = match other {
99            Expression::Assets(x) => CanonicalAssets::from(x),
100            Expression::None => CanonicalAssets::empty(),
101            other => {
102                return Err(Error::InvalidBinaryOp(
103                    "add".to_string(),
104                    format!("{self:?}"),
105                    format!("{other:?}"),
106                ))
107            }
108        };
109
110        let x = self.into();
111        let total = x + y;
112        Ok(Expression::Assets(total.into()))
113    }
114
115    fn sub(self, other: Expression) -> Result<Expression, Error> {
116        let other_neg = other.neg()?;
117        self.add(other_neg)
118    }
119
120    fn mul(self, other: Expression) -> Result<Expression, Error> {
121        // Assets can only be scaled by a scalar; asset-by-asset multiplication
122        // is undefined.
123        match other {
124            Expression::Number(factor) => {
125                let scaled = self.into() * factor;
126                Ok(Expression::Assets(scaled.into()))
127            }
128            Expression::None => Ok(Expression::None),
129            other => Err(Error::InvalidBinaryOp(
130                "mul".to_string(),
131                format!("{self:?}"),
132                format!("{other:?}"),
133            )),
134        }
135    }
136
137    fn div(self, other: Expression) -> Result<Expression, Error> {
138        // Assets can only be scaled down by a scalar divisor; asset-by-asset
139        // division is undefined. Division does not commute, so there is no
140        // `Int / AnyAsset` counterpart.
141        match other {
142            Expression::Number(0) => Err(Error::InvalidBinaryOp(
143                "div".to_string(),
144                format!("{self:?}"),
145                "zero".to_string(),
146            )),
147            Expression::Number(divisor) => {
148                let scaled = self.into() / divisor;
149                Ok(Expression::Assets(scaled.into()))
150            }
151            Expression::None => Ok(Expression::None),
152            other => Err(Error::InvalidBinaryOp(
153                "div".to_string(),
154                format!("{self:?}"),
155                format!("{other:?}"),
156            )),
157        }
158    }
159
160    fn neg(self) -> Result<Expression, Error> {
161        let negated = std::ops::Neg::neg(self.into());
162        Ok(Expression::Assets(negated.into()))
163    }
164}
165
166impl Arithmetic for i128 {
167    fn add(self, other: Expression) -> Result<Expression, Error> {
168        match other {
169            Expression::Number(y) => Ok(Expression::Number(self + y)),
170            Expression::None => Ok(Expression::Number(self)),
171            _ => Err(Error::InvalidBinaryOp(
172                "add".to_string(),
173                format!("{self:?}"),
174                format!("{other:?}"),
175            )),
176        }
177    }
178
179    fn sub(self, other: Expression) -> Result<Expression, Error> {
180        let other_neg = other.neg()?;
181        self.add(other_neg)
182    }
183
184    fn mul(self, other: Expression) -> Result<Expression, Error> {
185        match other {
186            Expression::Number(y) => Ok(Expression::Number(self * y)),
187            // `Int * AnyAsset` scales the asset quantities (commutes with the
188            // `AnyAsset * Int` case handled by the asset impl above).
189            Expression::Assets(y) => {
190                let scaled = CanonicalAssets::from(y) * self;
191                Ok(Expression::Assets(scaled.into()))
192            }
193            Expression::None => Ok(Expression::None),
194            _ => Err(Error::InvalidBinaryOp(
195                "mul".to_string(),
196                format!("{self:?}"),
197                format!("{other:?}"),
198            )),
199        }
200    }
201
202    fn div(self, other: Expression) -> Result<Expression, Error> {
203        match other {
204            Expression::Number(0) => Err(Error::InvalidBinaryOp(
205                "div".to_string(),
206                format!("{self:?}"),
207                "zero".to_string(),
208            )),
209            Expression::Number(y) => Ok(Expression::Number(self / y)),
210            // `Int / AnyAsset` is undefined (division does not commute), so no
211            // `Assets` arm here.
212            Expression::None => Ok(Expression::None),
213            _ => Err(Error::InvalidBinaryOp(
214                "div".to_string(),
215                format!("{self:?}"),
216                format!("{other:?}"),
217            )),
218        }
219    }
220
221    fn neg(self) -> Result<Expression, Error> {
222        Ok(Expression::Number(-self))
223    }
224}
225
226impl Arithmetic for Expression {
227    fn add(self, other: Expression) -> Result<Expression, Error> {
228        match self {
229            Expression::None => Ok(other),
230            Expression::Number(x) => Arithmetic::add(x, other),
231            Expression::Assets(x) => Arithmetic::add(x, other),
232            x => Err(Error::InvalidBinaryOp(
233                "add".to_string(),
234                format!("{x:?}"),
235                format!("{other:?}"),
236            )),
237        }
238    }
239
240    fn sub(self, other: Expression) -> Result<Expression, Error> {
241        match self {
242            Expression::None => Ok(other),
243            Expression::Number(x) => Arithmetic::sub(x, other),
244            Expression::Assets(x) => Arithmetic::sub(x, other),
245            x => Err(Error::InvalidBinaryOp(
246                "sub".to_string(),
247                format!("{x:?}"),
248                format!("{other:?}"),
249            )),
250        }
251    }
252
253    fn mul(self, other: Expression) -> Result<Expression, Error> {
254        match self {
255            // `None` is absorbing for `*` (unlike `+`/`-`, where it is the 0
256            // identity) so partial reductions never fabricate a product.
257            Expression::None => Ok(Expression::None),
258            Expression::Number(x) => Arithmetic::mul(x, other),
259            Expression::Assets(x) => Arithmetic::mul(x, other),
260            x => Err(Error::InvalidBinaryOp(
261                "mul".to_string(),
262                format!("{x:?}"),
263                format!("{other:?}"),
264            )),
265        }
266    }
267
268    fn div(self, other: Expression) -> Result<Expression, Error> {
269        match self {
270            // `None` is absorbing for `/` (same as `*`) so partial reductions
271            // never fabricate a quotient.
272            Expression::None => Ok(Expression::None),
273            Expression::Number(x) => Arithmetic::div(x, other),
274            Expression::Assets(x) => Arithmetic::div(x, other),
275            x => Err(Error::InvalidBinaryOp(
276                "div".to_string(),
277                format!("{x:?}"),
278                format!("{other:?}"),
279            )),
280        }
281    }
282
283    fn neg(self) -> Result<Expression, Error> {
284        match self {
285            Expression::None => Ok(Expression::None),
286            Expression::Number(x) => Arithmetic::neg(x),
287            Expression::Assets(x) => Arithmetic::neg(x),
288            x => Err(Error::InvalidUnaryOp("neg".to_string(), format!("{x:?}"))),
289        }
290    }
291}
292
293impl Concatenable for String {
294    fn concat(self, other: Expression) -> Result<Expression, Error> {
295        match other {
296            Expression::String(y) => Ok(Expression::String(self + &y)),
297            Expression::Number(y) => Ok(Expression::String(self + &y.to_string())),
298            Expression::None => Ok(Expression::String(self)),
299            _ => Err(Error::InvalidBinaryOp(
300                "concat".to_string(),
301                format!("String({self:?})"),
302                format!("{other:?}"),
303            )),
304        }
305    }
306}
307
308impl Concatenable for Vec<Expression> {
309    fn concat(self, other: Expression) -> Result<Expression, Error> {
310        match other {
311            Expression::List(expressions) => {
312                Ok(Expression::List([&self[..], &expressions[..]].concat()))
313            }
314            _ => Err(Error::InvalidBinaryOp(
315                "concat".to_string(),
316                format!("List({:?})", self),
317                format!("{:?}", other),
318            )),
319        }
320    }
321}
322
323impl Concatenable for Vec<u8> {
324    fn concat(self, other: Expression) -> Result<Expression, Error> {
325        match other {
326            Expression::Bytes(y) => {
327                let mut result = self;
328                result.extend(y);
329                Ok(Expression::Bytes(result))
330            }
331            Expression::None => Ok(Expression::Bytes(self)),
332            _ => Err(Error::InvalidBinaryOp(
333                "concat".to_string(),
334                format!("Bytes({self:?})"),
335                format!("{other:?}"),
336            )),
337        }
338    }
339}
340
341impl Concatenable for Expression {
342    fn concat(self, other: Expression) -> Result<Expression, Error> {
343        match self {
344            Expression::None => Ok(other),
345            Expression::String(x) => Concatenable::concat(x, other),
346            Expression::Bytes(x) => Concatenable::concat(x, other),
347            Expression::List(x) => Concatenable::concat(x, other),
348            x => Err(Error::InvalidBinaryOp(
349                "concat".to_string(),
350                format!("{x:?}"),
351                format!("{other:?}"),
352            )),
353        }
354    }
355}
356
357pub trait Coerceable {
358    fn into_assets(self) -> Result<Expression, Error>;
359    fn into_datum(self) -> Result<Expression, Error>;
360}
361
362impl Coerceable for Expression {
363    fn into_assets(self) -> Result<Expression, Error> {
364        match self {
365            Expression::None => Ok(Expression::None),
366            Expression::Assets(x) => Ok(Expression::Assets(x)),
367            Expression::UtxoSet(x) => Ok(Expression::Assets(x.total_assets().into())),
368            _ => Err(Error::CannotCoerceIntoAssets(self)),
369        }
370    }
371
372    fn into_datum(self) -> Result<Expression, Error> {
373        match self {
374            Expression::None => Ok(Expression::None),
375            Expression::UtxoSet(x) => Ok(x
376                .first_by_ref()
377                .and_then(|utxo| utxo.datum.clone())
378                .unwrap_or(Expression::None)),
379            Expression::List(x) => Ok(Expression::List(x)),
380            Expression::Map(x) => Ok(Expression::Map(x)),
381            Expression::Tuple(x) => Ok(Expression::Tuple(x)),
382            Expression::Struct(x) => Ok(Expression::Struct(x)),
383            Expression::Bytes(x) => Ok(Expression::Bytes(x)),
384            Expression::Number(x) => Ok(Expression::Number(x)),
385            Expression::String(x) => Ok(Expression::String(x)),
386            Expression::Address(x) => Ok(Expression::Bytes(x)),
387            Expression::Hash(x) => Ok(Expression::Bytes(x)),
388            _ => Err(Error::CannotCoerceIntoDatum(self)),
389        }
390    }
391}
392
393fn arg_value_into_expr(arg: ArgValue) -> Expression {
394    match arg {
395        ArgValue::Address(x) => Expression::Address(x),
396        ArgValue::Int(x) => Expression::Number(x),
397        ArgValue::Bool(x) => Expression::Bool(x),
398        ArgValue::String(x) => Expression::String(x),
399        ArgValue::Bytes(x) => Expression::Bytes(x),
400        ArgValue::UtxoSet(x) => Expression::UtxoSet(x),
401        ArgValue::UtxoRef(x) => Expression::UtxoRefs(vec![x]),
402        ArgValue::List(xs) => {
403            Expression::List(xs.into_iter().map(arg_value_into_expr).collect())
404        }
405        ArgValue::Tuple(xs) => {
406            Expression::Tuple(xs.into_iter().map(arg_value_into_expr).collect())
407        }
408        ArgValue::Map(pairs) => Expression::Map(
409            pairs
410                .into_iter()
411                .map(|(k, v)| (arg_value_into_expr(k), arg_value_into_expr(v)))
412                .collect(),
413        ),
414        ArgValue::Struct {
415            constructor,
416            fields,
417        } => Expression::Struct(StructExpr {
418            constructor,
419            fields: fields.into_iter().map(arg_value_into_expr).collect(),
420        }),
421    }
422}
423
424pub trait Apply: Sized + std::fmt::Debug {
425    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error>;
426    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error>;
427    fn apply_fees(self, fees: u64) -> Result<Self, Error>;
428
429    fn is_constant(&self) -> bool;
430
431    fn params(&self) -> BTreeMap<String, Type>;
432    fn queries(&self) -> BTreeMap<String, InputQuery>;
433
434    fn reduce(self) -> Result<Self, Error>;
435}
436
437pub trait Composite: Sized {
438    fn reduce_self(self) -> Result<Self, Error> {
439        Ok(self)
440    }
441
442    fn components(&self) -> Vec<&Expression>;
443
444    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
445    where
446        F: Fn(Expression) -> Result<Expression, Error> + Clone;
447
448    fn reduce_nested(self) -> Result<Self, Error> {
449        self.try_map_components(|x| x.reduce())
450    }
451}
452
453impl<T> Apply for T
454where
455    T: Composite + std::fmt::Debug,
456{
457    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
458        self.try_map_components(|x| x.apply_args(args))
459    }
460
461    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
462        self.try_map_components(|x| x.apply_inputs(args))
463    }
464
465    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
466        self.try_map_components(|x| x.apply_fees(fees))
467    }
468
469    fn is_constant(&self) -> bool {
470        self.components().iter().all(|x| x.is_constant())
471    }
472
473    fn params(&self) -> BTreeMap<String, Type> {
474        self.components().iter().flat_map(|x| x.params()).collect()
475    }
476
477    fn queries(&self) -> BTreeMap<String, InputQuery> {
478        self.components().iter().flat_map(|x| x.queries()).collect()
479    }
480
481    fn reduce(self) -> Result<Self, Error> {
482        let x = self.reduce_nested()?;
483
484        if x.is_constant() {
485            x.reduce_self()
486        } else {
487            Ok(x)
488        }
489    }
490}
491
492impl<T> Apply for Option<T>
493where
494    T: Apply,
495{
496    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
497        self.map(|x| x.apply_args(args)).transpose()
498    }
499
500    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
501        self.map(|x| x.apply_inputs(args)).transpose()
502    }
503
504    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
505        self.map(|x| x.apply_fees(fees)).transpose()
506    }
507
508    fn is_constant(&self) -> bool {
509        match self {
510            Some(x) => x.is_constant(),
511            None => true,
512        }
513    }
514
515    fn params(&self) -> BTreeMap<String, Type> {
516        match self {
517            Some(x) => x.params(),
518            None => BTreeMap::new(),
519        }
520    }
521
522    fn queries(&self) -> BTreeMap<String, InputQuery> {
523        match self {
524            Some(x) => x.queries(),
525            None => BTreeMap::new(),
526        }
527    }
528
529    fn reduce(self) -> Result<Self, Error> {
530        self.map(|x| x.reduce()).transpose()
531    }
532}
533
534impl<T> Apply for Vec<T>
535where
536    T: Apply,
537{
538    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
539        self.into_iter().map(|x| x.apply_args(args)).collect()
540    }
541
542    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
543        self.into_iter().map(|x| x.apply_inputs(args)).collect()
544    }
545
546    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
547        self.into_iter().map(|x| x.apply_fees(fees)).collect()
548    }
549
550    fn is_constant(&self) -> bool {
551        self.iter().all(|x| x.is_constant())
552    }
553
554    fn params(&self) -> BTreeMap<String, Type> {
555        self.iter().flat_map(|x| x.params()).collect()
556    }
557
558    fn queries(&self) -> BTreeMap<String, InputQuery> {
559        self.iter().flat_map(|x| x.queries()).collect()
560    }
561
562    fn reduce(self) -> Result<Self, Error> {
563        self.into_iter().map(|x| x.reduce()).collect()
564    }
565}
566
567impl<T> Apply for HashMap<String, T>
568where
569    T: Apply,
570{
571    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
572        self.into_iter()
573            .map(|(k, v)| v.apply_args(args).map(|v| (k, v)))
574            .collect()
575    }
576
577    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
578        self.into_iter()
579            .map(|(k, v)| v.apply_inputs(args).map(|v| (k, v)))
580            .collect()
581    }
582
583    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
584        self.into_iter()
585            .map(|(k, v)| v.apply_fees(fees).map(|v| (k, v)))
586            .collect()
587    }
588
589    fn is_constant(&self) -> bool {
590        self.values().all(|x| x.is_constant())
591    }
592
593    fn params(&self) -> BTreeMap<String, Type> {
594        self.values().flat_map(|x| x.params()).collect()
595    }
596
597    fn queries(&self) -> BTreeMap<String, InputQuery> {
598        self.values().flat_map(|x| x.queries()).collect()
599    }
600
601    fn reduce(self) -> Result<Self, Error> {
602        self.into_iter()
603            .map(|(k, v)| v.reduce().map(|v| (k, v)))
604            .collect()
605    }
606}
607
608impl Composite for ScriptSource {
609    fn reduce_self(self) -> Result<Self, Error> {
610        Ok(self)
611    }
612
613    fn components(&self) -> Vec<&Expression> {
614        match self {
615            ScriptSource::Embedded(x) => vec![x],
616            ScriptSource::UtxoRef { r#ref, source } => {
617                std::iter::once(r#ref).chain(source.as_ref()).collect()
618            }
619        }
620    }
621
622    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
623    where
624        F: Fn(Expression) -> Result<Expression, Error> + Clone,
625    {
626        match self {
627            ScriptSource::Embedded(x) => Ok(ScriptSource::Embedded(f(x)?)),
628            ScriptSource::UtxoRef { r#ref, source } => Ok(ScriptSource::UtxoRef {
629                r#ref: f(r#ref)?,
630                source: source.map(&f).transpose()?,
631            }),
632        }
633    }
634}
635
636impl TryFrom<&ArgValue> for ScriptSource {
637    type Error = Error;
638
639    fn try_from(value: &ArgValue) -> Result<Self, Self::Error> {
640        match value {
641            ArgValue::Bytes(x) => Ok(ScriptSource::Embedded(Expression::Bytes(x.clone()))),
642            ArgValue::UtxoRef(x) => Ok(ScriptSource::UtxoRef {
643                r#ref: Expression::UtxoRefs(vec![x.clone()]),
644                source: None,
645            }),
646            _ => Err(Error::InvalidArgument(value.clone(), "script".to_string())),
647        }
648    }
649}
650
651impl Composite for PolicyExpr {
652    fn components(&self) -> Vec<&Expression> {
653        let script = self.script.components();
654        std::iter::once(&self.hash).chain(script).collect()
655    }
656
657    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
658    where
659        F: Fn(Expression) -> Result<Expression, Error> + Clone,
660    {
661        Ok(Self {
662            name: self.name,
663            hash: f(self.hash)?,
664            script: self.script.try_map_components(f)?,
665        })
666    }
667}
668
669impl Composite for StructExpr {
670    fn components(&self) -> Vec<&Expression> {
671        self.fields.iter().collect()
672    }
673
674    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
675    where
676        F: Fn(Expression) -> Result<Expression, Error> + Clone,
677    {
678        Ok(Self {
679            constructor: self.constructor,
680            fields: self
681                .fields
682                .into_iter()
683                .map(&f)
684                .collect::<Result<Vec<_>, _>>()?,
685        })
686    }
687}
688
689impl Composite for AssetExpr {
690    fn components(&self) -> Vec<&Expression> {
691        vec![&self.policy, &self.asset_name, &self.amount]
692    }
693
694    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
695    where
696        F: Fn(Expression) -> Result<Expression, Error> + Clone,
697    {
698        Ok(Self {
699            policy: f(self.policy)?,
700            asset_name: f(self.asset_name)?,
701            amount: f(self.amount)?,
702        })
703    }
704}
705
706impl Composite for Coerce {
707    fn components(&self) -> Vec<&Expression> {
708        match self {
709            Self::IntoAssets(x) => vec![x],
710            Self::IntoDatum(x) => vec![x],
711            Self::IntoScript(x) => vec![x],
712            Self::NoOp(x) => vec![x],
713        }
714    }
715
716    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
717    where
718        F: Fn(Expression) -> Result<Expression, Error> + Clone,
719    {
720        match self {
721            Self::IntoAssets(x) => Ok(Self::IntoAssets(f(x)?)),
722            Self::IntoDatum(x) => Ok(Self::IntoDatum(f(x)?)),
723            Self::IntoScript(x) => Ok(Self::IntoScript(f(x)?)),
724            Self::NoOp(x) => Ok(Self::NoOp(f(x)?)),
725        }
726    }
727
728    fn reduce_self(self) -> Result<Self, Error> {
729        match self {
730            Self::NoOp(x) => Ok(Self::NoOp(x)),
731            Self::IntoAssets(x) => Ok(Self::NoOp(x.into_assets()?)),
732            Self::IntoDatum(x) => Ok(Self::NoOp(x.into_datum()?)),
733            Self::IntoScript(x) => todo!(),
734        }
735    }
736}
737
738impl Composite for BuiltInOp {
739    fn components(&self) -> Vec<&Expression> {
740        match self {
741            Self::NoOp(x) => vec![x],
742            Self::Add(x, y) => vec![x, y],
743            Self::Sub(x, y) => vec![x, y],
744            Self::Mul(x, y) => vec![x, y],
745            Self::Div(x, y) => vec![x, y],
746            Self::Concat(x, y) => vec![x, y],
747            Self::Negate(x) => vec![x],
748            Self::Property(x, _) => vec![x],
749        }
750    }
751
752    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
753    where
754        F: Fn(Expression) -> Result<Expression, Error> + Clone,
755    {
756        match self {
757            Self::NoOp(x) => Ok(Self::NoOp(f(x)?)),
758            Self::Add(x, y) => Ok(Self::Add(f(x)?, f(y)?)),
759            Self::Sub(x, y) => Ok(Self::Sub(f(x)?, f(y)?)),
760            Self::Mul(x, y) => Ok(Self::Mul(f(x)?, f(y)?)),
761            Self::Div(x, y) => Ok(Self::Div(f(x)?, f(y)?)),
762            Self::Concat(x, y) => Ok(Self::Concat(f(x)?, f(y)?)),
763            Self::Negate(x) => Ok(Self::Negate(f(x)?)),
764            Self::Property(x, prop) => Ok(Self::Property(f(x)?, prop)),
765        }
766    }
767
768    fn reduce_self(self) -> Result<Self, Error> {
769        match self {
770            Self::Add(x, y) => Ok(Self::NoOp(x.add(y)?)),
771            Self::Sub(x, y) => Ok(Self::NoOp(x.sub(y)?)),
772            Self::Mul(x, y) => Ok(Self::NoOp(x.mul(y)?)),
773            Self::Div(x, y) => Ok(Self::NoOp(x.div(y)?)),
774            Self::Concat(x, y) => Ok(Self::NoOp(x.concat(y)?)),
775            Self::Negate(x) => Ok(Self::NoOp(x.neg()?)),
776            Self::Property(x, prop) => Ok(Self::NoOp(x.index_or_err(prop)?)),
777            Self::NoOp(x) => Ok(Self::NoOp(x)),
778        }
779    }
780
781    fn reduce_nested(self) -> Result<Self, Error> {
782        match self {
783            Self::Add(x, y) => Ok(Self::Add(x.reduce()?, y.reduce()?)),
784            Self::Sub(x, y) => Ok(Self::Sub(x.reduce()?, y.reduce()?)),
785            Self::Mul(x, y) => Ok(Self::Mul(x.reduce()?, y.reduce()?)),
786            Self::Div(x, y) => Ok(Self::Div(x.reduce()?, y.reduce()?)),
787            Self::Concat(x, y) => Ok(Self::Concat(x.reduce()?, y.reduce()?)),
788            Self::Negate(x) => Ok(Self::Negate(x.reduce()?)),
789            Self::Property(x, y) => Ok(Self::Property(x.reduce()?, y.reduce()?)),
790            Self::NoOp(x) => Ok(Self::NoOp(x.reduce()?)),
791        }
792    }
793}
794
795impl From<AssetExpr> for CanonicalAssets {
796    fn from(asset: AssetExpr) -> Self {
797        let policy = asset.expect_constant_policy();
798        let name = asset.expect_constant_name();
799        let amount = asset.expect_constant_amount();
800
801        Self::from_asset(policy, name, amount)
802    }
803}
804
805impl From<Vec<AssetExpr>> for CanonicalAssets {
806    fn from(assets: Vec<AssetExpr>) -> Self {
807        let mut result = CanonicalAssets::empty();
808
809        for asset in assets {
810            let asset = asset.into();
811            result = result + asset;
812        }
813
814        result
815    }
816}
817
818impl From<CanonicalAssets> for Vec<AssetExpr> {
819    fn from(assets: CanonicalAssets) -> Self {
820        let mut result = Vec::new();
821
822        for (class, amount) in assets.into_iter() {
823            result.push(AssetExpr {
824                policy: class
825                    .policy()
826                    .map(|x| Expression::Bytes(x.to_vec()))
827                    .unwrap_or(Expression::None),
828                asset_name: class
829                    .name()
830                    .map(|x| Expression::Bytes(x.to_vec()))
831                    .unwrap_or(Expression::None),
832                amount: Expression::Number(amount),
833            });
834        }
835
836        result
837    }
838}
839
840impl AssetExpr {
841    fn expect_constant_policy(&self) -> Option<&[u8]> {
842        match &self.policy {
843            Expression::None => None,
844            Expression::Bytes(x) => Some(x.as_slice()),
845            _ => None,
846        }
847    }
848
849    fn expect_constant_name(&self) -> Option<&[u8]> {
850        match &self.asset_name {
851            Expression::None => None,
852            Expression::Bytes(x) => Some(x.as_slice()),
853            Expression::String(x) => Some(x.as_bytes()),
854            _ => None,
855        }
856    }
857
858    fn expect_constant_amount(&self) -> i128 {
859        match &self.amount {
860            Expression::Number(x) => *x,
861            _ => unreachable!("amount expected to be Number"),
862        }
863    }
864}
865
866impl Composite for Input {
867    fn components(&self) -> Vec<&Expression> {
868        vec![&self.utxos, &self.redeemer]
869    }
870
871    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
872    where
873        F: Fn(Expression) -> Result<Expression, Error> + Clone,
874    {
875        Ok(Self {
876            name: self.name,
877            utxos: f(self.utxos)?,
878            redeemer: f(self.redeemer)?,
879        })
880    }
881}
882
883impl Composite for InputQuery {
884    fn components(&self) -> Vec<&Expression> {
885        vec![&self.address, &self.min_amount, &self.r#ref]
886    }
887
888    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
889    where
890        F: Fn(Expression) -> Result<Expression, Error> + Clone,
891    {
892        Ok(Self {
893            address: f(self.address)?,
894            min_amount: f(self.min_amount)?,
895            r#ref: f(self.r#ref)?,
896            ..self
897        })
898    }
899}
900
901impl Apply for Param {
902    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
903        match self {
904            Param::ExpectValue(name, ty) => {
905                let defined = args.get(&name).cloned();
906
907                match defined {
908                    Some(x) => Ok(Param::Set(arg_value_into_expr(x))),
909                    None => Ok(Self::ExpectValue(name, ty)),
910                }
911            }
912            // queries can have nested params
913            Param::ExpectInput(name, query) => {
914                Ok(Param::ExpectInput(name, query.apply_args(args)?))
915            }
916            x => Ok(x),
917        }
918    }
919
920    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
921        match self {
922            Param::ExpectInput(name, query) => {
923                let defined = args.get(&name).cloned();
924
925                match defined {
926                    Some(x) => Ok(Param::Set(Expression::UtxoSet(x))),
927                    None => Ok(Self::ExpectInput(name, query)),
928                }
929            }
930            x => Ok(x),
931        }
932    }
933
934    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
935        match self {
936            Param::ExpectFees => Ok(Param::Set(Expression::Assets(vec![AssetExpr {
937                policy: Expression::None,
938                asset_name: Expression::None,
939                amount: Expression::Number(fees as i128),
940            }]))),
941            // queries can have nested params
942            Param::ExpectInput(name, query) => {
943                Ok(Param::ExpectInput(name, query.apply_fees(fees)?))
944            }
945            x => Ok(x),
946        }
947    }
948
949    fn is_constant(&self) -> bool {
950        match self {
951            Param::Set(x) => x.is_constant(),
952            _ => false,
953        }
954    }
955
956    fn params(&self) -> BTreeMap<String, Type> {
957        match self {
958            Param::ExpectValue(name, ty) => BTreeMap::from([(name.clone(), ty.clone())]),
959            // queries can have nested params
960            Param::ExpectInput(_, x) => x.params(),
961            _ => BTreeMap::new(),
962        }
963    }
964
965    fn queries(&self) -> BTreeMap<String, InputQuery> {
966        match self {
967            Param::ExpectInput(name, query) => BTreeMap::from([(name.clone(), query.clone())]),
968            _ => BTreeMap::new(),
969        }
970    }
971
972    fn reduce(self) -> Result<Self, Error> {
973        match self {
974            // queries can have nested expressions that need to be reduced
975            Param::ExpectInput(name, query) => Ok(Param::ExpectInput(name, query.reduce()?)),
976            x => Ok(x),
977        }
978    }
979}
980
981impl Apply for Expression {
982    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
983        match self {
984            Self::List(x) => Ok(Self::List(
985                x.into_iter()
986                    .map(|x| x.apply_args(args))
987                    .collect::<Result<_, _>>()?,
988            )),
989            Self::Tuple(x) => Ok(Self::Tuple(
990                x.into_iter()
991                    .map(|x| x.apply_args(args))
992                    .collect::<Result<_, _>>()?,
993            )),
994            Self::Map(x) => Ok(Self::Map(
995                x.into_iter()
996                    .map(|(k, v)| {
997                        Ok::<(Expression, Expression), Error>((
998                            k.apply_args(args)?,
999                            v.apply_args(args)?,
1000                        ))
1001                    })
1002                    .collect::<Result<_, _>>()?,
1003            )),
1004            Self::Struct(x) => Ok(Self::Struct(x.apply_args(args)?)),
1005            Self::Assets(x) => Ok(Self::Assets(
1006                x.into_iter()
1007                    .map(|x| x.apply_args(args))
1008                    .collect::<Result<_, _>>()?,
1009            )),
1010            Self::EvalParam(x) => Ok(Self::EvalParam(Box::new(x.apply_args(args)?))),
1011            Self::EvalBuiltIn(x) => Ok(Self::EvalBuiltIn(Box::new(x.apply_args(args)?))),
1012            Self::EvalCoerce(x) => Ok(Self::EvalCoerce(Box::new(x.apply_args(args)?))),
1013            Self::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.apply_args(args)?))),
1014            Self::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.apply_args(args)?))),
1015
1016            // Don't fall into the temptation of simplifying the following cases under a single
1017            // wildcard with a default implementation, it makes it really hard to detect missing
1018            // implementation when adding new `Expression` variants.
1019            Self::None => Ok(self),
1020            Self::Bytes(_) => Ok(self),
1021            Self::Number(_) => Ok(self),
1022            Self::Bool(_) => Ok(self),
1023            Self::String(_) => Ok(self),
1024            Self::Address(_) => Ok(self),
1025            Self::Hash(_) => Ok(self),
1026            Self::UtxoRefs(_) => Ok(self),
1027            Self::UtxoSet(_) => Ok(self),
1028        }
1029    }
1030
1031    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
1032        match self {
1033            Self::List(x) => Ok(Self::List(
1034                x.into_iter()
1035                    .map(|x| x.apply_inputs(args))
1036                    .collect::<Result<_, _>>()?,
1037            )),
1038            Self::Map(x) => Ok(Self::Map(
1039                x.into_iter()
1040                    .map(|(k, v)| {
1041                        Ok::<(Expression, Expression), Error>((
1042                            k.apply_inputs(args)?,
1043                            v.apply_inputs(args)?,
1044                        ))
1045                    })
1046                    .collect::<Result<_, _>>()?,
1047            )),
1048            Self::Tuple(x) => Ok(Self::Tuple(
1049                x.into_iter()
1050                    .map(|x| x.apply_inputs(args))
1051                    .collect::<Result<_, _>>()?,
1052            )),
1053            Self::Struct(x) => Ok(Self::Struct(x.apply_inputs(args)?)),
1054            Self::Assets(x) => Ok(Self::Assets(
1055                x.into_iter()
1056                    .map(|x| x.apply_inputs(args))
1057                    .collect::<Result<_, _>>()?,
1058            )),
1059            Self::EvalParam(x) => Ok(Self::EvalParam(Box::new(x.apply_inputs(args)?))),
1060            Self::EvalBuiltIn(x) => Ok(Self::EvalBuiltIn(Box::new(x.apply_inputs(args)?))),
1061            Self::EvalCoerce(x) => Ok(Self::EvalCoerce(Box::new(x.apply_inputs(args)?))),
1062            Self::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.apply_inputs(args)?))),
1063            Self::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.apply_inputs(args)?))),
1064
1065            // Don't fall into the temptation of simplifying the following cases under a single
1066            // wildcard with a default implementation, it makes it really hard to detect missing
1067            // implementation when adding new `Expression` variants.
1068            Self::None => Ok(self),
1069            Self::Bytes(_) => Ok(self),
1070            Self::Number(_) => Ok(self),
1071            Self::Bool(_) => Ok(self),
1072            Self::String(_) => Ok(self),
1073            Self::Address(_) => Ok(self),
1074            Self::Hash(_) => Ok(self),
1075            Self::UtxoRefs(_) => Ok(self),
1076            Self::UtxoSet(_) => Ok(self),
1077        }
1078    }
1079
1080    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
1081        match self {
1082            Self::List(x) => Ok(Self::List(
1083                x.into_iter()
1084                    .map(|x| x.apply_fees(fees))
1085                    .collect::<Result<_, _>>()?,
1086            )),
1087            Self::Map(x) => Ok(Self::Map(
1088                x.into_iter()
1089                    .map(|(k, v)| {
1090                        Ok::<(Expression, Expression), Error>((
1091                            k.apply_fees(fees)?,
1092                            v.apply_fees(fees)?,
1093                        ))
1094                    })
1095                    .collect::<Result<_, _>>()?,
1096            )),
1097            Self::Tuple(x) => Ok(Self::Tuple(
1098                x.into_iter()
1099                    .map(|x| x.apply_fees(fees))
1100                    .collect::<Result<_, _>>()?,
1101            )),
1102            Self::Struct(x) => Ok(Self::Struct(x.apply_fees(fees)?)),
1103            Self::Assets(x) => Ok(Self::Assets(
1104                x.into_iter()
1105                    .map(|x| x.apply_fees(fees))
1106                    .collect::<Result<_, _>>()?,
1107            )),
1108            Self::EvalParam(x) => Ok(Self::EvalParam(Box::new(x.apply_fees(fees)?))),
1109            Self::EvalBuiltIn(x) => Ok(Self::EvalBuiltIn(Box::new(x.apply_fees(fees)?))),
1110            Self::EvalCoerce(x) => Ok(Self::EvalCoerce(Box::new(x.apply_fees(fees)?))),
1111            Self::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.apply_fees(fees)?))),
1112            Self::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.apply_fees(fees)?))),
1113
1114            // Don't fall into the temptation of simplifying the following cases under a single
1115            // wildcard with a default implementation, it makes it really hard to detect missing
1116            // implementation when adding new `Expression` variants.
1117            Self::None => Ok(self),
1118            Self::Bytes(_) => Ok(self),
1119            Self::Number(_) => Ok(self),
1120            Self::Bool(_) => Ok(self),
1121            Self::String(_) => Ok(self),
1122            Self::Address(_) => Ok(self),
1123            Self::Hash(_) => Ok(self),
1124            Self::UtxoRefs(_) => Ok(self),
1125            Self::UtxoSet(_) => Ok(self),
1126        }
1127    }
1128
1129    fn is_constant(&self) -> bool {
1130        match self {
1131            Self::List(x) => x.iter().all(|x| x.is_constant()),
1132            Self::Map(x) => x.iter().all(|(k, v)| k.is_constant() && v.is_constant()),
1133            Self::Tuple(x) => x.iter().all(|x| x.is_constant()),
1134            Self::Struct(x) => x.is_constant(),
1135            Self::Assets(x) => x.iter().all(|x| x.is_constant()),
1136            Self::EvalParam(x) => x.is_constant(),
1137            Self::EvalBuiltIn(x) => x.is_constant(),
1138            Self::EvalCoerce(x) => x.is_constant(),
1139            Self::EvalCompiler(_) => false,
1140            Self::AdHocDirective(x) => x.is_constant(),
1141
1142            // Don't fall into the temptation of simplifying the following cases under a single
1143            // wildcard with a default implementation, it makes it really hard to detect missing
1144            // implementation when adding new `Expression` variants.
1145            Self::None => true,
1146            Self::Bytes(_) => true,
1147            Self::Number(_) => true,
1148            Self::Bool(_) => true,
1149            Self::String(_) => true,
1150            Self::Address(_) => true,
1151            Self::Hash(_) => true,
1152            Self::UtxoRefs(_) => true,
1153            Self::UtxoSet(_) => true,
1154        }
1155    }
1156
1157    fn params(&self) -> BTreeMap<String, Type> {
1158        match self {
1159            Self::List(x) => x.iter().flat_map(|x| x.params()).collect(),
1160            Self::Map(x) => x
1161                .iter()
1162                .flat_map(|(k, v)| [k.params(), v.params()].into_iter().flatten())
1163                .collect(),
1164            Self::Tuple(x) => x.iter().flat_map(|x| x.params()).collect(),
1165            Self::Struct(x) => x.params(),
1166            Self::Assets(x) => x.iter().flat_map(|x| x.params()).collect(),
1167            Self::EvalParam(x) => x.params(),
1168            Self::EvalBuiltIn(x) => x.params(),
1169            Self::EvalCoerce(x) => x.params(),
1170            Self::EvalCompiler(x) => x.params(),
1171            Self::AdHocDirective(x) => x.params(),
1172
1173            // Don't fall into the temptation of simplifying the following cases under a single
1174            // wildcard with a default implementation, it makes it really hard to detect missing
1175            // implementation when adding new `Expression` variants.
1176            Self::None => BTreeMap::new(),
1177            Self::Bytes(_) => BTreeMap::new(),
1178            Self::Number(_) => BTreeMap::new(),
1179            Self::Bool(_) => BTreeMap::new(),
1180            Self::String(_) => BTreeMap::new(),
1181            Self::Address(_) => BTreeMap::new(),
1182            Self::Hash(_) => BTreeMap::new(),
1183            Self::UtxoRefs(_) => BTreeMap::new(),
1184            Self::UtxoSet(_) => BTreeMap::new(),
1185        }
1186    }
1187
1188    fn queries(&self) -> BTreeMap<String, InputQuery> {
1189        match self {
1190            Self::List(x) => x.iter().flat_map(|x| x.queries()).collect(),
1191            Self::Map(x) => x
1192                .iter()
1193                .flat_map(|(k, v)| [k.queries(), v.queries()].into_iter().flatten())
1194                .collect(),
1195            Self::Tuple(x) => x.iter().flat_map(|x| x.queries()).collect(),
1196            Self::Struct(x) => x.queries(),
1197            Self::Assets(x) => x.iter().flat_map(|x| x.queries()).collect(),
1198            Self::EvalParam(x) => x.queries(),
1199            Self::EvalBuiltIn(x) => x.queries(),
1200            Self::EvalCoerce(x) => x.queries(),
1201            Self::EvalCompiler(x) => x.queries(),
1202            Self::AdHocDirective(x) => x.queries(),
1203
1204            // Don't fall into the temptation of simplifying the following cases under a single
1205            // wildcard with a default implementation, it makes it really hard to detect missing
1206            // implementation when adding new `Expression` variants.
1207            Self::None => BTreeMap::new(),
1208            Self::Bytes(_) => BTreeMap::new(),
1209            Self::Number(_) => BTreeMap::new(),
1210            Self::Bool(_) => BTreeMap::new(),
1211            Self::String(_) => BTreeMap::new(),
1212            Self::Address(_) => BTreeMap::new(),
1213            Self::Hash(_) => BTreeMap::new(),
1214            Self::UtxoRefs(_) => BTreeMap::new(),
1215            Self::UtxoSet(_) => BTreeMap::new(),
1216        }
1217    }
1218
1219    fn reduce(self) -> Result<Self, Error> {
1220        match self {
1221            // the following expressions can only be reduced internally
1222            Expression::List(x) => Ok(Self::List(
1223                x.into_iter()
1224                    .map(|x| x.reduce())
1225                    .collect::<Result<_, _>>()?,
1226            )),
1227            Expression::Map(x) => Ok(Self::Map(
1228                x.into_iter()
1229                    .map(|(k, v)| Ok::<(Expression, Expression), Error>((k.reduce()?, v.reduce()?)))
1230                    .collect::<Result<_, _>>()?,
1231            )),
1232            Expression::Tuple(x) => Ok(Self::Tuple(
1233                x.into_iter()
1234                    .map(|x| x.reduce())
1235                    .collect::<Result<_, _>>()?,
1236            )),
1237            Expression::Struct(x) => Ok(Self::Struct(x.reduce()?)),
1238            Expression::Assets(x) => Ok(Self::Assets(
1239                x.into_iter()
1240                    .map(|x| x.reduce())
1241                    .collect::<Result<_, _>>()?,
1242            )),
1243            Expression::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.reduce()?))),
1244            Expression::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.reduce()?))),
1245
1246            // the following ones can be turned into simpler expressions
1247            Expression::EvalBuiltIn(x) => match x.reduce()? {
1248                BuiltInOp::NoOp(x) => Ok(x),
1249                x => Ok(Expression::EvalBuiltIn(Box::new(x.reduce()?))),
1250            },
1251            Expression::EvalCoerce(x) => match x.reduce()? {
1252                Coerce::NoOp(x) => Ok(x),
1253                x => Ok(Expression::EvalCoerce(Box::new(x.reduce()?))),
1254            },
1255            Expression::EvalParam(x) => match x.reduce()? {
1256                Param::Set(x) => Ok(x),
1257                x => Ok(Expression::EvalParam(Box::new(x.reduce()?))),
1258            },
1259
1260            // Don't fall into the temptation of simplifying the following cases under a single
1261            // wildcard with a default implementation, it makes it really hard to detect missing
1262            // implementation when adding new `Expression` variants.
1263            Self::None => Ok(self),
1264            Self::Bytes(_) => Ok(self),
1265            Self::Number(_) => Ok(self),
1266            Self::Bool(_) => Ok(self),
1267            Self::String(_) => Ok(self),
1268            Self::Address(_) => Ok(self),
1269            Self::Hash(_) => Ok(self),
1270            Self::UtxoRefs(_) => Ok(self),
1271            Self::UtxoSet(_) => Ok(self),
1272        }
1273    }
1274}
1275
1276impl Composite for Output {
1277    fn components(&self) -> Vec<&Expression> {
1278        vec![&self.address, &self.datum, &self.amount]
1279    }
1280
1281    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1282    where
1283        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1284    {
1285        Ok(Self {
1286            address: f(self.address)?,
1287            datum: f(self.datum)?,
1288            amount: f(self.amount)?,
1289            optional: self.optional,
1290        })
1291    }
1292}
1293
1294impl Composite for Mint {
1295    fn components(&self) -> Vec<&Expression> {
1296        vec![&self.amount, &self.redeemer]
1297    }
1298
1299    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1300    where
1301        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1302    {
1303        Ok(Self {
1304            amount: f(self.amount)?,
1305            redeemer: f(self.redeemer)?,
1306        })
1307    }
1308}
1309
1310impl Composite for AdHocDirective {
1311    fn components(&self) -> Vec<&Expression> {
1312        self.data.values().collect()
1313    }
1314
1315    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1316    where
1317        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1318    {
1319        Ok(Self {
1320            name: self.name,
1321            data: self
1322                .data
1323                .into_iter()
1324                .map(|(k, v)| f(v).map(|v| (k, v)))
1325                .collect::<Result<_, _>>()?,
1326        })
1327    }
1328}
1329
1330impl Composite for CompilerOp {
1331    fn components(&self) -> Vec<&Expression> {
1332        match self {
1333            CompilerOp::BuildScriptAddress(x) => vec![x],
1334            CompilerOp::ComputeMinUtxo(x) => vec![x],
1335            CompilerOp::ComputeTipSlot => vec![],
1336            CompilerOp::ComputeSlotToTime(x) => vec![x],
1337            CompilerOp::ComputeTimeToSlot(x) => vec![x],
1338        }
1339    }
1340
1341    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1342    where
1343        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1344    {
1345        match self {
1346            CompilerOp::BuildScriptAddress(x) => Ok(CompilerOp::BuildScriptAddress(f(x)?)),
1347            CompilerOp::ComputeMinUtxo(x) => Ok(CompilerOp::ComputeMinUtxo(f(x)?)),
1348            CompilerOp::ComputeTipSlot => Ok(CompilerOp::ComputeTipSlot),
1349            CompilerOp::ComputeSlotToTime(x) => Ok(CompilerOp::ComputeSlotToTime(f(x)?)),
1350            CompilerOp::ComputeTimeToSlot(x) => Ok(CompilerOp::ComputeTimeToSlot(f(x)?)),
1351        }
1352    }
1353}
1354
1355impl Composite for Signers {
1356    fn components(&self) -> Vec<&Expression> {
1357        self.signers.iter().collect()
1358    }
1359
1360    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1361    where
1362        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1363    {
1364        Ok(Self {
1365            signers: self.signers.into_iter().map(f).collect::<Result<_, _>>()?,
1366        })
1367    }
1368}
1369
1370impl Composite for Collateral {
1371    fn components(&self) -> Vec<&Expression> {
1372        vec![&self.utxos]
1373    }
1374
1375    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1376    where
1377        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1378    {
1379        Ok(Self {
1380            utxos: f(self.utxos)?,
1381        })
1382    }
1383}
1384
1385impl Composite for Validity {
1386    fn components(&self) -> Vec<&Expression> {
1387        vec![&self.since, &self.until]
1388    }
1389
1390    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1391    where
1392        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1393    {
1394        Ok(Self {
1395            since: f(self.since)?,
1396            until: f(self.until)?,
1397        })
1398    }
1399}
1400
1401impl Composite for Metadata {
1402    fn components(&self) -> Vec<&Expression> {
1403        vec![&self.key, &self.value]
1404    }
1405
1406    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1407    where
1408        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1409    {
1410        Ok(Self {
1411            key: f(self.key)?,
1412            value: f(self.value)?,
1413        })
1414    }
1415}
1416
1417impl Apply for Tx {
1418    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
1419        let tx = Tx {
1420            references: self.references.apply_args(args)?,
1421            inputs: self.inputs.apply_args(args)?,
1422            outputs: self.outputs.apply_args(args)?,
1423            validity: self.validity.apply_args(args)?,
1424            mints: self.mints.apply_args(args)?,
1425            burns: self.burns.apply_args(args)?,
1426            fees: self.fees.apply_args(args)?,
1427            adhoc: self.adhoc.apply_args(args)?,
1428            collateral: self.collateral.apply_args(args)?,
1429            signers: self.signers.apply_args(args)?,
1430            metadata: self.metadata.apply_args(args)?,
1431        };
1432
1433        Ok(tx)
1434    }
1435
1436    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
1437        Ok(Self {
1438            references: self.references.apply_inputs(args)?,
1439            inputs: self.inputs.apply_inputs(args)?,
1440            outputs: self.outputs.apply_inputs(args)?,
1441            validity: self.validity.apply_inputs(args)?,
1442            mints: self.mints.apply_inputs(args)?,
1443            burns: self.burns.apply_inputs(args)?,
1444            fees: self.fees.apply_inputs(args)?,
1445            adhoc: self.adhoc.apply_inputs(args)?,
1446            collateral: self.collateral.apply_inputs(args)?,
1447            signers: self.signers.apply_inputs(args)?,
1448            metadata: self.metadata.apply_inputs(args)?,
1449        })
1450    }
1451
1452    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
1453        Ok(Self {
1454            references: self.references.apply_fees(fees)?,
1455            inputs: self.inputs.apply_fees(fees)?,
1456            outputs: self.outputs.apply_fees(fees)?,
1457            validity: self.validity.apply_fees(fees)?,
1458            mints: self.mints.apply_fees(fees)?,
1459            burns: self.burns.apply_fees(fees)?,
1460            fees: self.fees.apply_fees(fees)?,
1461            adhoc: self.adhoc.apply_fees(fees)?,
1462            collateral: self.collateral.apply_fees(fees)?,
1463            signers: self.signers.apply_fees(fees)?,
1464            metadata: self.metadata.apply_fees(fees)?,
1465        })
1466    }
1467
1468    fn is_constant(&self) -> bool {
1469        self.inputs.iter().all(|x| x.is_constant())
1470            && self.outputs.iter().all(|x| x.is_constant())
1471            && self.mints.iter().all(|x| x.is_constant())
1472            && self.burns.iter().all(|x| x.is_constant())
1473            && self.fees.is_constant()
1474            && self.metadata.is_constant()
1475            && self.validity.is_constant()
1476            && self.references.is_constant()
1477            && self.collateral.is_constant()
1478            && self.adhoc.iter().all(|x| x.is_constant())
1479            && self.signers.is_constant()
1480    }
1481
1482    fn params(&self) -> BTreeMap<String, Type> {
1483        // TODO: analyze if necessary to add ref_inputs
1484        let mut params = BTreeMap::new();
1485        params.extend(self.inputs.params());
1486        params.extend(self.outputs.params());
1487        params.extend(self.mints.params());
1488        params.extend(self.burns.params());
1489        params.extend(self.fees.params());
1490        params.extend(self.adhoc.params());
1491        params.extend(self.signers.params());
1492        params.extend(self.validity.params());
1493        params.extend(self.metadata.params());
1494        params.extend(self.references.params());
1495        params.extend(self.collateral.params());
1496        params
1497    }
1498
1499    fn queries(&self) -> BTreeMap<String, InputQuery> {
1500        let mut queries = BTreeMap::new();
1501        queries.extend(self.inputs.queries());
1502        queries.extend(self.outputs.queries());
1503        queries.extend(self.mints.queries());
1504        queries.extend(self.burns.queries());
1505        queries.extend(self.fees.queries());
1506        queries.extend(self.adhoc.queries());
1507        queries.extend(self.signers.queries());
1508        queries.extend(self.validity.queries());
1509        queries.extend(self.metadata.queries());
1510        queries.extend(self.collateral.queries());
1511        queries.extend(self.references.queries());
1512        queries
1513    }
1514
1515    fn reduce(self) -> Result<Self, Error> {
1516        Ok(Self {
1517            references: self.references.reduce()?,
1518            inputs: self.inputs.reduce()?,
1519            outputs: self.outputs.reduce()?,
1520            validity: self.validity.reduce()?,
1521            mints: self.mints.reduce()?,
1522            burns: self.burns.reduce()?,
1523            fees: self.fees.reduce()?,
1524            adhoc: self.adhoc.reduce()?,
1525            collateral: self.collateral.reduce()?,
1526            signers: self.signers.reduce()?,
1527            metadata: self.metadata.reduce()?,
1528        })
1529    }
1530}
1531
1532#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1533pub enum ArgValue {
1534    Int(i128),
1535    Bool(bool),
1536    String(String),
1537    Bytes(Vec<u8>),
1538    Address(Vec<u8>),
1539    UtxoSet(UtxoSet),
1540    UtxoRef(UtxoRef),
1541
1542    // `Struct` fields are positional with their constructor index; names and
1543    // order are resolved upstream, so this layer never sees a by-name record.
1544    List(Vec<ArgValue>),
1545    Tuple(Vec<ArgValue>),
1546    Map(Vec<(ArgValue, ArgValue)>),
1547    Struct {
1548        constructor: usize,
1549        fields: Vec<ArgValue>,
1550    },
1551}
1552
1553impl From<Vec<u8>> for ArgValue {
1554    fn from(value: Vec<u8>) -> Self {
1555        Self::Bytes(value)
1556    }
1557}
1558
1559impl From<String> for ArgValue {
1560    fn from(value: String) -> Self {
1561        Self::String(value)
1562    }
1563}
1564
1565impl From<&str> for ArgValue {
1566    fn from(value: &str) -> Self {
1567        Self::String(value.to_string())
1568    }
1569}
1570
1571impl From<bool> for ArgValue {
1572    fn from(value: bool) -> Self {
1573        Self::Bool(value)
1574    }
1575}
1576
1577macro_rules! impl_from_int_for_arg_value {
1578    ($($t:ty),*) => {
1579        $(
1580            impl From<$t> for ArgValue {
1581                fn from(value: $t) -> Self {
1582                    Self::Int(value as i128)
1583                }
1584            }
1585        )*
1586    };
1587}
1588
1589impl_from_int_for_arg_value!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
1590
1591pub type ArgMap = BTreeMap<String, ArgValue>;
1592
1593pub fn apply_args<T: Apply>(template: T, args: &ArgMap) -> Result<T, Error> {
1594    template.apply_args(args)
1595}
1596
1597pub fn apply_inputs<T: Apply>(template: T, args: &BTreeMap<String, UtxoSet>) -> Result<T, Error> {
1598    template.apply_inputs(args)
1599}
1600
1601pub fn apply_fees<T: Apply>(template: T, fees: u64) -> Result<T, Error> {
1602    template.apply_fees(fees)
1603}
1604
1605pub fn reduce<T: Apply>(template: T) -> Result<T, Error> {
1606    template.reduce()
1607}
1608
1609pub fn find_params<T: Apply>(template: &T) -> BTreeMap<String, Type> {
1610    template.params()
1611}
1612
1613pub fn find_queries<T: Apply>(template: &T) -> BTreeMap<String, InputQuery> {
1614    template.queries()
1615}
1616
1617#[cfg(test)]
1618mod tests {
1619    use super::*;
1620
1621    const SUBJECT_TIR: &str = include_str!("test_subject.tir");
1622
1623    fn get_subject_tx() -> Tx {
1624        serde_json::from_str::<Tx>(SUBJECT_TIR).unwrap()
1625    }
1626
1627    #[test]
1628    fn param_expression_is_applied() {
1629        let ir = Expression::EvalParam(Box::new(Param::ExpectValue("a".to_string(), Type::Int)));
1630
1631        let params = ir.params();
1632        assert_eq!(params.len(), 1);
1633        assert_eq!(params.get("a"), Some(&Type::Int));
1634
1635        let args = BTreeMap::from([("a".to_string(), ArgValue::Int(100))]);
1636
1637        let after = ir.apply_args(&args).unwrap();
1638
1639        assert_eq!(
1640            after,
1641            Expression::EvalParam(Box::new(Param::Set(Expression::Number(100),)))
1642        );
1643    }
1644
1645    #[test]
1646    fn nested_param_expression_is_applied() {
1647        let ir = Expression::EvalParam(Box::new(Param::ExpectInput(
1648            "out".to_string(),
1649            InputQuery {
1650                address: Expression::None,
1651                min_amount: Expression::None,
1652                r#ref: Expression::EvalParam(Box::new(Param::ExpectValue(
1653                    "in".to_string(),
1654                    Type::Int,
1655                ))),
1656                many: false,
1657                collateral: false,
1658            },
1659        )));
1660
1661        let params = ir.params();
1662        assert_eq!(params.len(), 1);
1663        assert_eq!(params.get("in"), Some(&Type::Int));
1664
1665        let args = BTreeMap::from([("in".to_string(), ArgValue::Int(100))]);
1666        let after = ir.apply_args(&args).unwrap();
1667
1668        let after = after.reduce().unwrap();
1669
1670        let queries = after.queries();
1671        assert_eq!(queries.len(), 1);
1672        assert_eq!(
1673            queries.get("out"),
1674            Some(&InputQuery {
1675                address: Expression::None,
1676                min_amount: Expression::None,
1677                r#ref: Expression::Number(100),
1678                many: false,
1679                collateral: false,
1680            })
1681        );
1682    }
1683
1684    #[test]
1685    fn param_expression_is_reduced() {
1686        let ir = Expression::EvalParam(Box::new(Param::Set(Expression::Number(3))));
1687
1688        let after = ir.reduce().unwrap();
1689
1690        assert_eq!(after, Expression::Number(3));
1691    }
1692
1693    #[test]
1694    fn test_apply_args() {
1695        let before = get_subject_tx();
1696
1697        let params = find_params(&before);
1698        assert_eq!(params.len(), 3);
1699        assert_eq!(params.get("sender"), Some(&Type::Address));
1700        assert_eq!(params.get("a"), Some(&Type::Int));
1701        assert_eq!(params.get("b"), Some(&Type::Int));
1702
1703        let args = BTreeMap::from([
1704            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1705            ("a".to_string(), ArgValue::Int(100)),
1706            ("b".to_string(), ArgValue::Int(200)),
1707        ]);
1708
1709        let after = apply_args(before, &args).unwrap();
1710
1711        let params = find_params(&after);
1712        assert_eq!(params.len(), 0);
1713    }
1714
1715    #[test]
1716    fn test_apply_inputs() {
1717        let before = get_subject_tx();
1718
1719        let args = BTreeMap::from([
1720            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1721            ("a".to_string(), ArgValue::Int(100)),
1722            ("b".to_string(), ArgValue::Int(200)),
1723        ]);
1724
1725        let before = apply_args(before, &args).unwrap();
1726
1727        let before = before.reduce().unwrap();
1728
1729        let queries = find_queries(&before);
1730        dbg!(&queries);
1731
1732        assert_eq!(queries.len(), 1);
1733        assert!(queries.contains_key("source"));
1734
1735        let inputs = BTreeMap::from([(
1736            "source".to_string(),
1737            UtxoSet::from([Utxo {
1738                r#ref: UtxoRef::new(b"abc", 0),
1739                address: b"abc".to_vec(),
1740                datum: None,
1741                assets: CanonicalAssets::from_naked_amount(300),
1742                script: None,
1743            }]),
1744        )]);
1745
1746        let after = apply_inputs(before, &inputs).unwrap();
1747
1748        let queries = find_queries(&after);
1749        dbg!(&queries);
1750
1751        assert_eq!(queries.len(), 0);
1752    }
1753
1754    #[test]
1755    fn test_apply_fees() {
1756        let before = get_subject_tx();
1757
1758        let args = BTreeMap::from([
1759            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1760            ("a".to_string(), ArgValue::Int(100)),
1761            ("b".to_string(), ArgValue::Int(200)),
1762        ]);
1763
1764        let before = apply_args(before, &args).unwrap().reduce().unwrap();
1765
1766        let after = before.apply_fees(100).unwrap().reduce().unwrap();
1767
1768        let queries = find_queries(&after);
1769
1770        let query = queries.get("source").unwrap();
1771
1772        assert_eq!(
1773            query,
1774            &InputQuery {
1775                address: Expression::Address(b"abc".to_vec()),
1776                min_amount: Expression::Assets(vec![AssetExpr {
1777                    policy: Expression::None,
1778                    asset_name: Expression::None,
1779                    amount: Expression::Number(400),
1780                }]),
1781                r#ref: Expression::None,
1782                many: false,
1783                collateral: false,
1784            }
1785        );
1786    }
1787
1788    #[test]
1789    fn built_in_expression_is_reduced() {
1790        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::NoOp(Expression::Number(5))));
1791
1792        let after = op.reduce().unwrap();
1793
1794        assert_eq!(after, Expression::Number(5))
1795    }
1796
1797    #[test]
1798    fn numeric_add_is_reduced() {
1799        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Add(
1800            Expression::Number(1),
1801            Expression::Number(5),
1802        )));
1803
1804        let after = op.reduce().unwrap();
1805
1806        assert_eq!(after, Expression::Number(6));
1807    }
1808
1809    #[test]
1810    fn numeric_sub_is_reduced() {
1811        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Sub(
1812            Expression::Number(8),
1813            Expression::Number(5),
1814        )));
1815
1816        let after = op.reduce().unwrap();
1817
1818        assert_eq!(after, Expression::Number(3));
1819    }
1820
1821    #[test]
1822    fn nested_numeric_binary_op_is_reduced() {
1823        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Add(
1824            Expression::Number(1),
1825            Expression::EvalBuiltIn(Box::new(BuiltInOp::Sub(
1826                Expression::Number(5),
1827                Expression::Number(3),
1828            ))),
1829        )));
1830
1831        let after = op.reduce().unwrap();
1832
1833        assert_eq!(after, Expression::Number(3));
1834    }
1835
1836    #[test]
1837    fn numeric_mul_is_reduced() {
1838        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Mul(
1839            Expression::Number(6),
1840            Expression::Number(7),
1841        )));
1842
1843        let after = op.reduce().unwrap();
1844
1845        assert_eq!(after, Expression::Number(42));
1846    }
1847
1848    #[test]
1849    fn mul_binds_tighter_than_add_is_reduced() {
1850        // 2 + 3 * 4 == 14 (mul reduces before the surrounding add)
1851        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Add(
1852            Expression::Number(2),
1853            Expression::EvalBuiltIn(Box::new(BuiltInOp::Mul(
1854                Expression::Number(3),
1855                Expression::Number(4),
1856            ))),
1857        )));
1858
1859        let after = op.reduce().unwrap();
1860
1861        assert_eq!(after, Expression::Number(14));
1862    }
1863
1864    #[test]
1865    fn asset_scaled_is_reduced() {
1866        // AnyAsset * Int scales every quantity.
1867        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Mul(
1868            Expression::Assets(vec![AssetExpr {
1869                policy: Expression::Bytes(b"abc".to_vec()),
1870                asset_name: Expression::Bytes(b"111".to_vec()),
1871                amount: Expression::Number(100),
1872            }]),
1873            Expression::Number(3),
1874        )));
1875
1876        let reduced = op.reduce().unwrap();
1877
1878        match reduced {
1879            Expression::Assets(assets) => {
1880                assert_eq!(assets.len(), 1);
1881                assert_eq!(assets[0].policy, Expression::Bytes(b"abc".to_vec()));
1882                assert_eq!(assets[0].asset_name, Expression::Bytes(b"111".to_vec()));
1883                assert_eq!(assets[0].amount, Expression::Number(300));
1884            }
1885            _ => panic!("Expected assets"),
1886        };
1887    }
1888
1889    #[test]
1890    fn asset_scaled_commutes_is_reduced() {
1891        // Int * AnyAsset scales identically to AnyAsset * Int.
1892        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Mul(
1893            Expression::Number(3),
1894            Expression::Assets(vec![AssetExpr {
1895                policy: Expression::Bytes(b"abc".to_vec()),
1896                asset_name: Expression::Bytes(b"111".to_vec()),
1897                amount: Expression::Number(100),
1898            }]),
1899        )));
1900
1901        let reduced = op.reduce().unwrap();
1902
1903        match reduced {
1904            Expression::Assets(assets) => {
1905                assert_eq!(assets.len(), 1);
1906                assert_eq!(assets[0].amount, Expression::Number(300));
1907            }
1908            _ => panic!("Expected assets"),
1909        };
1910    }
1911
1912    #[test]
1913    fn numeric_div_is_reduced() {
1914        // Integer division truncates toward zero: 9 / 2 == 4.
1915        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Div(
1916            Expression::Number(9),
1917            Expression::Number(2),
1918        )));
1919
1920        let after = op.reduce().unwrap();
1921
1922        assert_eq!(after, Expression::Number(4));
1923    }
1924
1925    #[test]
1926    fn div_binds_like_mul_is_reduced() {
1927        // 8 / 4 * 2 is left-associative at the multiplicative level == (8/4)*2 == 4.
1928        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Mul(
1929            Expression::EvalBuiltIn(Box::new(BuiltInOp::Div(
1930                Expression::Number(8),
1931                Expression::Number(4),
1932            ))),
1933            Expression::Number(2),
1934        )));
1935
1936        let after = op.reduce().unwrap();
1937
1938        assert_eq!(after, Expression::Number(4));
1939    }
1940
1941    #[test]
1942    fn asset_divided_is_reduced() {
1943        // AnyAsset / Int divides every quantity (truncating).
1944        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Div(
1945            Expression::Assets(vec![AssetExpr {
1946                policy: Expression::Bytes(b"abc".to_vec()),
1947                asset_name: Expression::Bytes(b"111".to_vec()),
1948                amount: Expression::Number(300),
1949            }]),
1950            Expression::Number(2),
1951        )));
1952
1953        let reduced = op.reduce().unwrap();
1954
1955        match reduced {
1956            Expression::Assets(assets) => {
1957                assert_eq!(assets.len(), 1);
1958                assert_eq!(assets[0].amount, Expression::Number(150));
1959            }
1960            _ => panic!("Expected assets"),
1961        };
1962    }
1963
1964    #[test]
1965    fn div_by_zero_errors() {
1966        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Div(
1967            Expression::Number(5),
1968            Expression::Number(0),
1969        )));
1970
1971        assert!(op.reduce().is_err());
1972    }
1973
1974    #[test]
1975    fn test_reduce_single_custom_asset_binary_op() {
1976        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Add(
1977            Expression::Assets(vec![AssetExpr {
1978                policy: Expression::Bytes(b"abc".to_vec()),
1979                asset_name: Expression::Bytes(b"111".to_vec()),
1980                amount: Expression::Number(100),
1981            }]),
1982            Expression::Assets(vec![AssetExpr {
1983                policy: Expression::Bytes(b"abc".to_vec()),
1984                asset_name: Expression::Bytes(b"111".to_vec()),
1985                amount: Expression::Number(200),
1986            }]),
1987        )));
1988
1989        let reduced = op.reduce().unwrap();
1990
1991        match reduced {
1992            Expression::Assets(assets) => {
1993                assert_eq!(assets.len(), 1);
1994                assert_eq!(assets[0].policy, Expression::Bytes(b"abc".to_vec()));
1995                assert_eq!(assets[0].asset_name, Expression::Bytes(b"111".to_vec()));
1996                assert_eq!(assets[0].amount, Expression::Number(300));
1997            }
1998            _ => panic!("Expected assets"),
1999        };
2000    }
2001
2002    #[test]
2003    fn test_reduce_native_asset_binary_op() {
2004        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Add(
2005            Expression::Assets(vec![AssetExpr {
2006                policy: Expression::None,
2007                asset_name: Expression::None,
2008                amount: Expression::Number(100),
2009            }]),
2010            Expression::Assets(vec![AssetExpr {
2011                policy: Expression::None,
2012                asset_name: Expression::None,
2013                amount: Expression::Number(200),
2014            }]),
2015        )));
2016
2017        let reduced = op.reduce().unwrap();
2018
2019        match reduced {
2020            Expression::Assets(assets) => {
2021                assert_eq!(assets.len(), 1);
2022                assert_eq!(assets[0].policy, Expression::None);
2023                assert_eq!(assets[0].asset_name, Expression::None);
2024                assert_eq!(assets[0].amount, Expression::Number(300));
2025            }
2026            _ => panic!("Expected assets"),
2027        };
2028    }
2029
2030    #[test]
2031    fn test_reduce_mixed_asset_binary_op() {
2032        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Add(
2033            Expression::Assets(vec![AssetExpr {
2034                policy: Expression::None,
2035                asset_name: Expression::None,
2036                amount: Expression::Number(100),
2037            }]),
2038            Expression::Assets(vec![AssetExpr {
2039                policy: Expression::Bytes(b"abc".to_vec()),
2040                asset_name: Expression::Bytes(b"111".to_vec()),
2041                amount: Expression::Number(200),
2042            }]),
2043        )));
2044
2045        let reduced = op.reduce().unwrap();
2046
2047        match reduced {
2048            Expression::Assets(assets) => {
2049                assert_eq!(assets.len(), 2);
2050
2051                for asset in assets {
2052                    if asset.policy == Expression::None {
2053                        assert_eq!(asset.asset_name, Expression::None);
2054                        assert_eq!(asset.amount, Expression::Number(100));
2055                    } else {
2056                        assert_eq!(asset.policy, Expression::Bytes(b"abc".to_vec()));
2057                        assert_eq!(asset.asset_name, Expression::Bytes(b"111".to_vec()));
2058                        assert_eq!(asset.amount, Expression::Number(200));
2059                    }
2060                }
2061            }
2062            _ => panic!("Expected assets"),
2063        };
2064    }
2065
2066    #[test]
2067    fn test_reduce_coerce_noop() {
2068        let op = Expression::EvalCoerce(Box::new(Coerce::NoOp(Expression::Number(5))));
2069
2070        let reduced = op.reduce().unwrap();
2071
2072        match reduced {
2073            Expression::Number(5) => (),
2074            _ => panic!("Expected number 5"),
2075        };
2076    }
2077
2078    #[test]
2079    fn test_coerce_utxo_set_into_assets() {
2080        let utxos = vec![Utxo {
2081            r#ref: UtxoRef::new(b"abc", 1),
2082            address: b"abc".into(),
2083            datum: Some(Expression::Number(1)),
2084            assets: CanonicalAssets::from_defined_asset(b"abc", b"111", 1),
2085            script: None,
2086        }];
2087
2088        let op = Coerce::IntoAssets(Expression::UtxoSet(
2089            std::collections::HashSet::from_iter(utxos.clone().into_iter()).into(),
2090        ));
2091
2092        let reduced = op.reduce().unwrap();
2093
2094        assert_eq!(
2095            reduced,
2096            Coerce::NoOp(Expression::Assets(utxos[0].assets.clone().into()))
2097        );
2098    }
2099
2100    #[test]
2101    fn test_coerce_utxo_set_into_datum() {
2102        let utxos = vec![Utxo {
2103            r#ref: UtxoRef::new(b"abc", 1),
2104            address: b"abc".into(),
2105            datum: Some(Expression::Number(1)),
2106            assets: CanonicalAssets::from_naked_amount(1),
2107            script: None,
2108        }];
2109
2110        let op = Coerce::IntoDatum(Expression::UtxoSet(
2111            std::collections::HashSet::from_iter(utxos.clone().into_iter()).into(),
2112        ));
2113
2114        let reduced = op.reduce().unwrap();
2115
2116        assert_eq!(reduced, Coerce::NoOp(utxos[0].datum.clone().unwrap()));
2117    }
2118
2119    #[test]
2120    fn test_reduce_struct_property_access() {
2121        let object = Expression::Struct(StructExpr {
2122            constructor: 0,
2123            fields: vec![
2124                Expression::Number(1),
2125                Expression::Number(2),
2126                Expression::Number(3),
2127            ],
2128        });
2129
2130        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
2131            object.clone(),
2132            Expression::Number(1),
2133        )));
2134
2135        let reduced = op.reduce().unwrap();
2136
2137        assert_eq!(reduced, Expression::Number(2));
2138
2139        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
2140            object.clone(),
2141            Expression::Number(100),
2142        )));
2143
2144        let reduced = op.reduce();
2145
2146        match reduced {
2147            Err(Error::PropertyIndexNotFound(100, _)) => (),
2148            _ => panic!("Expected property index not found"),
2149        };
2150    }
2151
2152    #[test]
2153    fn test_reduce_list_property_access() {
2154        let object = Expression::List(vec![
2155            Expression::Number(1),
2156            Expression::Number(2),
2157            Expression::Number(3),
2158        ]);
2159
2160        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
2161            object.clone(),
2162            Expression::Number(1),
2163        )));
2164
2165        let reduced = op.reduce();
2166
2167        match reduced {
2168            Ok(Expression::Number(2)) => (),
2169            _ => panic!("Expected number 2"),
2170        };
2171
2172        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
2173            object.clone(),
2174            Expression::Number(100),
2175        )));
2176
2177        let reduced = op.reduce();
2178
2179        match reduced {
2180            Err(Error::PropertyIndexNotFound(100, _)) => (),
2181            _ => panic!("Expected property index not found"),
2182        };
2183    }
2184
2185    #[test]
2186    fn test_reduce_tuple_property_access() {
2187        let object = Expression::Tuple(vec![
2188            Expression::Number(1),
2189            Expression::Number(2),
2190            Expression::Number(3),
2191        ]);
2192
2193        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
2194            object.clone(),
2195            Expression::Number(1),
2196        )));
2197
2198        let reduced = op.reduce();
2199
2200        match reduced {
2201            Ok(Expression::Number(2)) => (),
2202            _ => panic!("Expected number 2"),
2203        };
2204
2205        // last element of an N-tuple is reachable
2206        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
2207            object.clone(),
2208            Expression::Number(2),
2209        )));
2210
2211        match op.reduce() {
2212            Ok(Expression::Number(3)) => (),
2213            _ => panic!("Expected number 3"),
2214        };
2215
2216        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
2217            object.clone(),
2218            Expression::Number(100),
2219        )));
2220
2221        let reduced = op.reduce();
2222
2223        match reduced {
2224            Err(Error::PropertyIndexNotFound(100, _)) => (),
2225            _ => panic!("Expected property index not found"),
2226        };
2227    }
2228
2229    #[test]
2230    fn test_string_concat_is_reduced() {
2231        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Concat(
2232            Expression::String("hello".to_string()),
2233            Expression::String("world".to_string()),
2234        )));
2235
2236        let reduced = op.reduce().unwrap();
2237
2238        match reduced {
2239            Expression::String(s) => assert_eq!(s, "helloworld"),
2240            _ => panic!("Expected string 'helloworld'"),
2241        }
2242    }
2243
2244    #[test]
2245    fn test_string_number_concat_is_reduced() {
2246        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Concat(
2247            Expression::String("hello".to_string()),
2248            Expression::Number(123),
2249        )));
2250
2251        let reduced = op.reduce().unwrap();
2252
2253        match reduced {
2254            Expression::String(s) => assert_eq!(s, "hello123"),
2255            _ => panic!("Expected string 'hello123'"),
2256        }
2257    }
2258
2259    #[test]
2260    fn test_bytes_concat_is_reduced() {
2261        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Concat(
2262            Expression::Bytes(vec![1, 2, 3]),
2263            Expression::Bytes(vec![4, 5, 6]),
2264        )));
2265
2266        let reduced = op.reduce().unwrap();
2267
2268        match reduced {
2269            Expression::Bytes(b) => assert_eq!(b, vec![1, 2, 3, 4, 5, 6]),
2270            _ => panic!("Expected bytes [1, 2, 3, 4, 5, 6]"),
2271        }
2272    }
2273
2274    #[test]
2275    fn test_list_concat_is_reduced() {
2276        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Concat(
2277            Expression::List(vec![Expression::Number(1)]),
2278            Expression::List(vec![Expression::Number(2)]),
2279        )));
2280
2281        let reduced = op.reduce().unwrap();
2282
2283        match reduced {
2284            Expression::List(b) => {
2285                assert_eq!(b, vec![Expression::Number(1), Expression::Number(2)])
2286            }
2287            _ => panic!("Expected List [Number(1), Number(2)"),
2288        }
2289    }
2290
2291    #[test]
2292    fn test_concat_type_mismatch_error() {
2293        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Concat(
2294            Expression::String("hello".to_string()),
2295            Expression::Bytes(vec![1, 2, 3]),
2296        )));
2297
2298        let reduced = op.reduce();
2299
2300        match reduced {
2301            Err(Error::InvalidBinaryOp(op, _, _)) => assert_eq!(op, "concat"),
2302            _ => panic!("Expected InvalidBinaryOp error"),
2303        }
2304    }
2305
2306    #[test]
2307    fn test_concat_with_none() {
2308        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Concat(
2309            Expression::String("hello".to_string()),
2310            Expression::None,
2311        )));
2312
2313        let reduced = op.reduce().unwrap();
2314
2315        match reduced {
2316            Expression::String(s) => assert_eq!(s, "hello"),
2317            _ => panic!("Expected string 'hello'"),
2318        }
2319    }
2320
2321    #[test]
2322    fn test_min_utxo_add_non_reduction() {
2323        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Add(
2324            Expression::Assets(vec![AssetExpr {
2325                policy: Expression::None,
2326                asset_name: Expression::None,
2327                amount: Expression::Number(29),
2328            }]),
2329            Expression::EvalCompiler(Box::new(CompilerOp::ComputeMinUtxo(Expression::Number(20)))),
2330        )));
2331
2332        let reduced = op.clone().reduce().unwrap();
2333
2334        assert!(op == reduced)
2335    }
2336
2337    #[test]
2338    fn test_min_utxo_sub_non_reduction() {
2339        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Sub(
2340            Expression::Assets(vec![AssetExpr {
2341                policy: Expression::None,
2342                asset_name: Expression::None,
2343                amount: Expression::Number(29),
2344            }]),
2345            Expression::EvalCompiler(Box::new(CompilerOp::ComputeMinUtxo(Expression::Number(20)))),
2346        )));
2347
2348        let reduced = op.clone().reduce().unwrap();
2349
2350        assert!(op == reduced)
2351    }
2352
2353    #[test]
2354    fn test_index_list_with_expression() {
2355        let list = Expression::List(vec![
2356            Expression::String("first".to_string()),
2357            Expression::String("second".to_string()),
2358            Expression::String("third".to_string()),
2359        ]);
2360
2361        let index_expr = Expression::Number(1);
2362        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(list.clone(), index_expr)));
2363
2364        let reduced = op.reduce().unwrap();
2365
2366        match reduced {
2367            Expression::String(s) => assert_eq!(s, "second"),
2368            _ => panic!("Expected string 'second'"),
2369        }
2370    }
2371
2372    #[test]
2373    fn test_index_list_out_of_bounds() {
2374        let list = Expression::List(vec![Expression::Number(1), Expression::Number(2)]);
2375
2376        let index_expr = Expression::Number(5);
2377        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(list.clone(), index_expr)));
2378
2379        let reduced = op.reduce();
2380
2381        match reduced {
2382            Err(Error::PropertyIndexNotFound(5, _)) => (),
2383            _ => panic!("Expected PropertyIndexNotFound error"),
2384        }
2385    }
2386
2387    #[test]
2388    fn test_index_tuple_with_expression() {
2389        let tuple = Expression::Tuple(vec![
2390            Expression::String("left".to_string()),
2391            Expression::String("right".to_string()),
2392        ]);
2393
2394        let index_expr = Expression::Number(0);
2395        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(tuple.clone(), index_expr)));
2396
2397        let reduced = op.reduce().unwrap();
2398
2399        match reduced {
2400            Expression::String(s) => assert_eq!(s, "left"),
2401            _ => panic!("Expected string 'left'"),
2402        }
2403
2404        let index_expr = Expression::Number(1);
2405        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(tuple.clone(), index_expr)));
2406
2407        let reduced = op.reduce().unwrap();
2408
2409        match reduced {
2410            Expression::String(s) => assert_eq!(s, "right"),
2411            _ => panic!("Expected string 'right'"),
2412        }
2413    }
2414
2415    #[test]
2416    fn test_n_tuple_params_and_apply() {
2417        // a 3-tuple mixing a constant and two params
2418        let tuple = Expression::Tuple(vec![
2419            Expression::Number(1),
2420            Expression::EvalParam(Box::new(Param::ExpectValue("a".to_string(), Type::Int))),
2421            Expression::EvalParam(Box::new(Param::ExpectValue("b".to_string(), Type::Bytes))),
2422        ]);
2423
2424        // params are collected across every element
2425        let params = tuple.params();
2426        assert_eq!(params.len(), 2);
2427        assert_eq!(params.get("a"), Some(&Type::Int));
2428        assert_eq!(params.get("b"), Some(&Type::Bytes));
2429
2430        // a parameterized tuple is not constant
2431        assert!(!tuple.is_constant());
2432
2433        let args = BTreeMap::from([
2434            ("a".to_string(), ArgValue::Int(42)),
2435            ("b".to_string(), ArgValue::Bytes(vec![0xff])),
2436        ]);
2437
2438        let reduced = tuple.apply_args(&args).unwrap().reduce().unwrap();
2439
2440        assert_eq!(
2441            reduced,
2442            Expression::Tuple(vec![
2443                Expression::Number(1),
2444                Expression::Number(42),
2445                Expression::Bytes(vec![0xff]),
2446            ])
2447        );
2448        assert!(reduced.is_constant());
2449    }
2450
2451    #[test]
2452    fn test_index_struct_with_expression() {
2453        let struct_expr = Expression::Struct(StructExpr {
2454            constructor: 0,
2455            fields: vec![
2456                Expression::String("field0".to_string()),
2457                Expression::String("field1".to_string()),
2458                Expression::String("field2".to_string()),
2459            ],
2460        });
2461
2462        let index_expr = Expression::Number(1);
2463        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
2464            struct_expr.clone(),
2465            index_expr,
2466        )));
2467
2468        let reduced = op.reduce().unwrap();
2469
2470        match reduced {
2471            Expression::String(s) => assert_eq!(s, "field1"),
2472            _ => panic!("Expected string 'field1'"),
2473        }
2474    }
2475
2476    #[test]
2477    fn test_index_none_expression() {
2478        let none_expr = Expression::None;
2479        let index_expr = Expression::Number(0);
2480
2481        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(none_expr, index_expr)));
2482
2483        let reduced = op.reduce();
2484
2485        match reduced {
2486            Err(Error::PropertyIndexNotFound(0, _)) => (),
2487            _ => panic!("Expected PropertyIndexNotFound error for None expression"),
2488        }
2489    }
2490
2491    #[test]
2492    fn test_indexable_trait_on_expression() {
2493        let list_expr = Expression::List(vec![Expression::Number(10), Expression::Number(20)]);
2494
2495        let result = list_expr.index(Expression::Number(0));
2496        assert_eq!(result, Some(Expression::Number(10)));
2497
2498        let result = list_expr.index(Expression::Number(1));
2499        assert_eq!(result, Some(Expression::Number(20)));
2500
2501        let result = list_expr.index(Expression::Number(2));
2502        assert_eq!(result, None);
2503
2504        let tuple_expr = Expression::Tuple(vec![
2505            Expression::Number(100),
2506            Expression::Number(200),
2507            Expression::Number(300),
2508        ]);
2509
2510        let result = tuple_expr.index(Expression::Number(0));
2511        assert_eq!(result, Some(Expression::Number(100)));
2512
2513        let result = tuple_expr.index(Expression::Number(1));
2514        assert_eq!(result, Some(Expression::Number(200)));
2515
2516        let result = tuple_expr.index(Expression::Number(2));
2517        assert_eq!(result, Some(Expression::Number(300)));
2518
2519        let result = tuple_expr.index(Expression::Number(3));
2520        assert_eq!(result, None);
2521    }
2522
2523    #[test]
2524    fn test_nested_property_access() {
2525        let nested_expr = Expression::List(vec![
2526            Expression::Tuple(vec![Expression::Number(1), Expression::Number(2)]),
2527            Expression::Tuple(vec![Expression::Number(3), Expression::Number(4)]),
2528        ]);
2529
2530        // Access nested_expr[1][0] (should be 3)
2531        let first_access = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
2532            nested_expr,
2533            Expression::Number(1),
2534        )));
2535
2536        let second_access = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
2537            first_access,
2538            Expression::Number(0),
2539        )));
2540
2541        let reduced = second_access.reduce().unwrap();
2542
2543        match reduced {
2544            Expression::Number(n) => assert_eq!(n, 3),
2545            _ => panic!("Expected number 3"),
2546        }
2547    }
2548
2549    #[test]
2550    fn test_arg_value_list_into_expr() {
2551        let arg = ArgValue::List(vec![ArgValue::Int(1), ArgValue::Int(2)]);
2552        assert_eq!(
2553            arg_value_into_expr(arg),
2554            Expression::List(vec![Expression::Number(1), Expression::Number(2)])
2555        );
2556    }
2557
2558    #[test]
2559    fn test_arg_value_tuple_into_expr() {
2560        let arg = ArgValue::Tuple(vec![ArgValue::Int(7), ArgValue::Bytes(vec![0xff])]);
2561        assert_eq!(
2562            arg_value_into_expr(arg),
2563            Expression::Tuple(vec![Expression::Number(7), Expression::Bytes(vec![0xff])])
2564        );
2565    }
2566
2567    #[test]
2568    fn test_arg_value_map_into_expr() {
2569        let arg = ArgValue::Map(vec![(
2570            ArgValue::String("k".to_string()),
2571            ArgValue::Int(100),
2572        )]);
2573        assert_eq!(
2574            arg_value_into_expr(arg),
2575            Expression::Map(vec![(
2576                Expression::String("k".to_string()),
2577                Expression::Number(100)
2578            )])
2579        );
2580    }
2581
2582    #[test]
2583    fn test_arg_value_struct_into_expr() {
2584        let arg = ArgValue::Struct {
2585            constructor: 1,
2586            fields: vec![ArgValue::Int(5)],
2587        };
2588        assert_eq!(
2589            arg_value_into_expr(arg),
2590            Expression::Struct(StructExpr {
2591                constructor: 1,
2592                fields: vec![Expression::Number(5)],
2593            })
2594        );
2595    }
2596
2597    #[test]
2598    fn test_apply_args_nested_struct() {
2599        // A record param nesting a parametric `List<Int>`: a positional
2600        // `ArgValue::Struct` substitutes into the param and reduces to a constant.
2601        let template = Expression::EvalParam(Box::new(Param::ExpectValue(
2602            "meta".to_string(),
2603            Type::Custom("Meta".to_string()),
2604        )));
2605
2606        assert!(!template.is_constant());
2607
2608        let args = BTreeMap::from([(
2609            "meta".to_string(),
2610            ArgValue::Struct {
2611                constructor: 0,
2612                fields: vec![
2613                    ArgValue::List(vec![ArgValue::Int(1), ArgValue::Int(2), ArgValue::Int(3)]),
2614                    ArgValue::Int(7),
2615                ],
2616            },
2617        )]);
2618
2619        let reduced = template.apply_args(&args).unwrap().reduce().unwrap();
2620
2621        assert_eq!(
2622            reduced,
2623            Expression::Struct(StructExpr {
2624                constructor: 0,
2625                fields: vec![
2626                    Expression::List(vec![
2627                        Expression::Number(1),
2628                        Expression::Number(2),
2629                        Expression::Number(3),
2630                    ]),
2631                    Expression::Number(7),
2632                ],
2633            })
2634        );
2635        assert!(reduced.is_constant());
2636    }
2637}