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