whisky/builder/
tx_eval.rs

1use crate::*;
2use async_trait::async_trait;
3use uplc::tx::SlotConfig;
4use whisky_common::Evaluator;
5
6use super::TxBuilder;
7
8pub trait TxEvaluation {
9    fn update_redeemer(&mut self, tx_evaluation: Vec<Action>) -> &mut Self;
10}
11
12impl TxEvaluation for TxBuilder {
13    fn update_redeemer(&mut self, tx_evaluation: Vec<Action>) -> &mut Self {
14        let multiplier = self.serializer.tx_evaluation_multiplier_percentage();
15        for redeemer_evaluation in tx_evaluation {
16            match redeemer_evaluation.tag {
17                RedeemerTag::Spend => {
18                    let input =
19                        &mut self.tx_builder_body.inputs[redeemer_evaluation.index as usize];
20                    if let TxIn::ScriptTxIn(ScriptTxIn { script_tx_in, .. }) = input {
21                        let redeemer: &mut Redeemer = script_tx_in.redeemer.as_mut().unwrap();
22                        redeemer.ex_units.mem = redeemer_evaluation.budget.mem * multiplier / 100;
23                        redeemer.ex_units.steps =
24                            redeemer_evaluation.budget.steps * multiplier / 100;
25                    }
26                }
27                RedeemerTag::Mint => {
28                    let mint_item =
29                        &mut self.tx_builder_body.mints[redeemer_evaluation.index as usize];
30                    if let MintItem::ScriptMint(mint) = mint_item {
31                        let redeemer: &mut Redeemer = mint.redeemer.as_mut().unwrap();
32                        redeemer.ex_units.mem = redeemer_evaluation.budget.mem * multiplier / 100;
33                        redeemer.ex_units.steps =
34                            redeemer_evaluation.budget.steps * multiplier / 100;
35                    }
36                }
37                RedeemerTag::Cert => {
38                    let cert_item =
39                        &mut self.tx_builder_body.certificates[redeemer_evaluation.index as usize];
40                    if let Certificate::ScriptCertificate(cert) = cert_item {
41                        let redeemer: &mut Redeemer = cert.redeemer.as_mut().unwrap();
42                        redeemer.ex_units.mem = redeemer_evaluation.budget.mem * multiplier / 100;
43                        redeemer.ex_units.steps =
44                            redeemer_evaluation.budget.steps * multiplier / 100;
45                    }
46                }
47                RedeemerTag::Reward => {
48                    let withdrawal_item =
49                        &mut self.tx_builder_body.withdrawals[redeemer_evaluation.index as usize];
50                    if let Withdrawal::PlutusScriptWithdrawal(withdrawal) = withdrawal_item {
51                        let redeemer: &mut Redeemer = withdrawal.redeemer.as_mut().unwrap();
52                        redeemer.ex_units.mem = redeemer_evaluation.budget.mem * multiplier / 100;
53                        redeemer.ex_units.steps =
54                            redeemer_evaluation.budget.steps * multiplier / 100;
55                    }
56                }
57                RedeemerTag::Propose => todo!(),
58                RedeemerTag::Vote => todo!(),
59            }
60        }
61        self
62    }
63}
64
65#[derive(Clone, Debug)]
66pub struct OfflineTxEvaluator {}
67
68impl OfflineTxEvaluator {
69    pub fn new() -> Self {
70        OfflineTxEvaluator {}
71    }
72}
73
74impl Default for OfflineTxEvaluator {
75    fn default() -> Self {
76        OfflineTxEvaluator::new()
77    }
78}
79
80impl OfflineTxEvaluator {
81    fn evaluate_tx_sync(
82        &self,
83        tx_hex: &str,
84        inputs: &[UTxO],
85        additional_txs: &[String],
86        network: &Network,
87        slot_config: &SlotConfig,
88    ) -> Result<Vec<Action>, WError> {
89        consolidate_errors(evaluate_tx_scripts(
90            tx_hex,
91            inputs,
92            additional_txs,
93            network,
94            slot_config,
95        )?)
96    }
97}
98
99fn consolidate_errors(eval_results: Vec<EvalResult>) -> Result<Vec<Action>, WError> {
100    let mut actions = Vec::new();
101    let mut errors_texts = Vec::new();
102    for eval_result in eval_results {
103        match eval_result {
104            EvalResult::Success(action) => actions.push(action),
105            EvalResult::Error(error) => {
106                errors_texts.push(format!("Error at index: [ {} ] - Budget: [ {:?} ] - Tag: [ {:?} ] - Error message: [ {} ] - Logs: [ {:?} ]",
107                                          error.index, error.budget, error.tag, error.error_message, error.logs));
108            }
109        }
110    }
111    if errors_texts.is_empty() {
112        Ok(actions)
113    } else {
114        Err(WError::new(
115            "consolidate_errors",
116            &format!("Errors found during evaluation: [ {:?} ]", errors_texts),
117        ))
118    }
119}
120
121#[async_trait]
122impl Evaluator for OfflineTxEvaluator {
123    async fn evaluate_tx(
124        &self,
125        tx_hex: &str,
126        inputs: &[UTxO],
127        additional_txs: &[String],
128        network: &Network,
129        slot_config: &SlotConfig,
130    ) -> Result<Vec<Action>, WError> {
131        self.evaluate_tx_sync(tx_hex, inputs, additional_txs, network, slot_config)
132    }
133}