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