whisky/builder/
tx_eval.rs1use 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}