whisky_csl/tx_parser/
utxo_converter.rs

1use whisky_common::{
2    DatumSource, InlineDatumSource, PubKeyTxIn, RefTxIn, ScriptSource, ScriptTxIn,
3    ScriptTxInParameter, SimpleScriptTxIn, SimpleScriptTxInParameter, TxIn, TxInParameter, UTxO,
4    WError,
5};
6
7use cardano_serialization_lib as csl;
8
9use super::context::{ParserContext, RedeemerIndex, Script};
10
11pub fn utxo_to_ref_tx_in(
12    tx_input: &csl::TransactionInput,
13    context: &ParserContext,
14) -> Result<RefTxIn, WError> {
15    let utxo = context.resolved_utxos.get(tx_input).ok_or_else(|| {
16        WError::new(
17            "utxo_to_ref_tx_in",
18            &format!("Failed to find UTxO for input: {:?}", tx_input),
19        )
20    })?;
21    let tx_in_param = RefTxIn {
22        tx_hash: tx_input.transaction_id().to_hex(),
23        tx_index: tx_input.index(),
24        script_size: utxo
25            .output
26            .script_ref
27            .as_ref()
28            .map(|script_ref| script_ref.len() / 2),
29    };
30    Ok(tx_in_param)
31}
32
33pub fn utxo_to_pub_key_tx_in(
34    tx_input: &csl::TransactionInput,
35    context: &ParserContext,
36) -> Result<PubKeyTxIn, WError> {
37    let utxo = context.resolved_utxos.get(tx_input).ok_or_else(|| {
38        WError::new(
39            "utxo_to_pub_key_tx_in",
40            &format!("Failed to find UTxO for input: {:?}", tx_input),
41        )
42    })?;
43    let tx_in_param = PubKeyTxIn {
44        tx_in: TxInParameter {
45            tx_hash: tx_input.transaction_id().to_hex(),
46            tx_index: tx_input.index(),
47            amount: Some(utxo.output.amount.clone()),
48            address: Some(utxo.output.address.clone()),
49        },
50    };
51    Ok(tx_in_param)
52}
53
54pub fn utxo_to_tx_in(
55    tx_input: &csl::TransactionInput,
56    context: &ParserContext,
57    index: usize,
58) -> Result<TxIn, WError> {
59    let utxo = context.resolved_utxos.get(tx_input).ok_or_else(|| {
60        WError::new(
61            "utxo_to_tx_in",
62            &format!("Failed to find UTxO for input: {:?}", tx_input),
63        )
64    })?;
65
66    let tx_in_param = TxInParameter {
67        tx_hash: utxo.input.tx_hash.clone(),
68        tx_index: utxo.input.output_index,
69        amount: Some(utxo.output.amount.clone()),
70        address: Some(utxo.output.address.clone()),
71    };
72
73    let address = csl::Address::from_bech32(&utxo.output.address).map_err(|e| {
74        WError::new(
75            "utxo_to_tx_in",
76            &format!("Failed to parse address: {:?}", e),
77        )
78    })?;
79    let payment_cred = address
80        .payment_cred()
81        .ok_or_else(|| WError::new("utxo_to_tx_in", "Failed to get payment credential"))?;
82
83    if payment_cred.to_keyhash().is_some() {
84        return Ok(TxIn::PubKeyTxIn(PubKeyTxIn { tx_in: tx_in_param }));
85    };
86
87    if let Some(script_hash) = payment_cred.to_scripthash() {
88        if let Some(script) = context.script_witness.scripts.get(&script_hash) {
89            match script {
90                Script::ProvidedNative(native_script) => {
91                    return Ok(TxIn::SimpleScriptTxIn(SimpleScriptTxIn {
92                        tx_in: tx_in_param,
93                        simple_script_tx_in: SimpleScriptTxInParameter::ProvidedSimpleScriptSource(
94                            native_script.clone(),
95                        ),
96                    }));
97                }
98                Script::ProvidedPlutus(plutus_script) => {
99                    let datum_source = get_datum_for_output(utxo, context);
100
101                    let script_source =
102                        Some(ScriptSource::ProvidedScriptSource(plutus_script.clone()));
103
104                    let redeemer = context
105                        .script_witness
106                        .redeemers
107                        .get(&RedeemerIndex::Spend(index))
108                        .cloned();
109
110                    return Ok(TxIn::ScriptTxIn(ScriptTxIn {
111                        tx_in: tx_in_param,
112                        script_tx_in: ScriptTxInParameter {
113                            script_source,
114                            datum_source,
115                            redeemer,
116                        },
117                    }));
118                }
119                Script::ReferencedNative(inline_script_source) => {
120                    return Ok(TxIn::SimpleScriptTxIn(SimpleScriptTxIn {
121                        tx_in: tx_in_param,
122                        simple_script_tx_in: SimpleScriptTxInParameter::InlineSimpleScriptSource(
123                            inline_script_source.clone(),
124                        ),
125                    }))
126                }
127                Script::ReferencedPlutus(inline_script_source) => {
128                    let datum_source = get_datum_for_output(utxo, context);
129
130                    let script_source = Some(ScriptSource::InlineScriptSource(
131                        inline_script_source.clone(),
132                    ));
133
134                    let redeemer = context
135                        .script_witness
136                        .redeemers
137                        .get(&RedeemerIndex::Spend(index))
138                        .cloned();
139
140                    return Ok(TxIn::ScriptTxIn(ScriptTxIn {
141                        tx_in: tx_in_param,
142                        script_tx_in: ScriptTxInParameter {
143                            script_source,
144                            datum_source,
145                            redeemer,
146                        },
147                    }));
148                }
149            }
150        } else {
151            return Ok(TxIn::PubKeyTxIn(PubKeyTxIn { tx_in: tx_in_param }));
152        }
153    } else {
154        return Ok(TxIn::PubKeyTxIn(PubKeyTxIn { tx_in: tx_in_param }));
155    }
156}
157
158fn get_datum_for_output(utxo: &UTxO, context: &ParserContext) -> Option<DatumSource> {
159    if let Some(_) = &utxo.output.plutus_data {
160        Some(DatumSource::InlineDatumSource(InlineDatumSource {
161            tx_hash: utxo.input.tx_hash.clone(),
162            tx_index: utxo.input.output_index,
163        }))
164    } else if let Some(datum_hash) = &utxo.output.data_hash {
165        context.script_witness.datums.get(datum_hash).cloned()
166    } else {
167        None
168    }
169}