whisky_pallas/utils/
phase_two.rs

1use pallas::codec::minicbor;
2use pallas::ledger::primitives::conway::{CostModels as PallasCostModels, Tx};
3use pallas_primitives::conway::Redeemer as PallasRedeemer;
4use uplc::machine::cost_model::ExBudget;
5use uplc::tx::error::Error;
6use uplc::tx::{eval, iter_redeemers, DataLookupTable, ResolvedInput, SlotConfig};
7use uplc::Fragment;
8
9pub enum PhaseTwoEvalResult {
10    Success(PallasRedeemer),
11    Error(PallasRedeemer, Error),
12}
13
14pub fn eval_phase_two(
15    tx: &Tx,
16    utxos: &[ResolvedInput],
17    cost_mdls: Option<&PallasCostModels>,
18    slot_config: &SlotConfig,
19) -> Result<Vec<PhaseTwoEvalResult>, Error> {
20    // Convert pallas 1.0.0-alpha.3 Tx to pallas 0.31.0 MintedTx for uplc compatibility
21    // Encode using pallas 1.0.0-alpha.3's CBOR encoder
22    let mut tx_bytes = Vec::new();
23    minicbor::encode(tx, &mut tx_bytes)
24        .map_err(|e| Error::FragmentDecode(format!("Failed to encode tx: {:?}", e).into()))?;
25
26    // Deserialize into pallas 0.31.0 types (via uplc's pallas-primitives 0.31.0 dependency)
27    let tx_for_uplc =
28        pallas_primitives::conway::MintedTx::decode_fragment(&tx_bytes).map_err(|e| {
29            Error::FragmentDecode(format!("Failed to decode tx for uplc: {:?}", e).into())
30        })?;
31
32    let lookup_table = DataLookupTable::from_transaction(&tx_for_uplc, utxos);
33
34    // Convert cost models if provided
35    let cost_mdls_for_uplc = cost_mdls
36        .map(|cm| {
37            let mut cm_bytes = Vec::new();
38            minicbor::encode(cm, &mut cm_bytes).ok()?;
39            pallas_primitives::conway::CostModels::decode_fragment(&cm_bytes).ok()
40        })
41        .flatten();
42
43    let redeemers = tx_for_uplc.transaction_witness_set.redeemer.as_ref();
44
45    match redeemers {
46        Some(rs) => {
47            let mut results = Vec::new();
48            for (key, data, ex_units) in iter_redeemers(rs) {
49                let remaining_budget = ExBudget {
50                    cpu: i64::MAX,
51                    mem: i64::MAX,
52                };
53                let redeemer_for_uplc = pallas_primitives::conway::Redeemer {
54                    tag: key.tag,
55                    index: key.index,
56                    data: data.clone(),
57                    ex_units,
58                };
59
60                let eval_result = eval::eval_redeemer(
61                    &tx_for_uplc,
62                    utxos,
63                    slot_config,
64                    &redeemer_for_uplc,
65                    &lookup_table,
66                    cost_mdls_for_uplc.as_ref(),
67                    &remaining_budget,
68                );
69
70                // The redeemer_for_uplc is already pallas_primitives 0.31.0, so no conversion needed
71                let pallas_redeemer = redeemer_for_uplc.clone();
72
73                match eval_result {
74                    Ok(updated_redeemer) => {
75                        results.push(PhaseTwoEvalResult::Success(updated_redeemer))
76                    }
77                    Err(error) => results.push(PhaseTwoEvalResult::Error(pallas_redeemer, error)),
78                }
79            }
80
81            Ok(results)
82        }
83        None => Ok(vec![]),
84    }
85}