tx3_lang/
applying.rs

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