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