1mod certificates;
2mod collaterals;
3mod context;
4mod inputs;
5mod metadata;
6mod mints;
7pub mod outputs;
8mod parsable;
9mod reference_inputs;
10mod required_signers;
11mod validity_range;
12mod votes;
13mod withdrawals;
14
15use crate::{
16 tx_parser::{
17 certificates::extract_certificates, collaterals::extract_collaterals,
18 context::ParserContext, inputs::extract_inputs, metadata::extract_metadata,
19 mints::extract_mints, outputs::extract_outputs, reference_inputs::extract_reference_inputs,
20 required_signers::extract_required_signers, validity_range::extract_validity_range,
21 votes::extract_votes, withdrawals::extract_withdrawals,
22 },
23 wrapper::transaction_body::{ScriptRef, ScriptRefKind, Transaction},
24};
25
26use pallas::ledger::traverse::ComputeHash;
27use pallas_crypto::key::ed25519::{PublicKey, Signature};
28use whisky_common::{TxBuilderBody, UTxO, UtxoInput, UtxoOutput, WError};
29
30pub fn parse(tx_hex: &str, resolved_utxos: &[UTxO]) -> Result<TxBuilderBody, WError> {
31 let bytes = hex::decode(tx_hex).map_err(|e| {
32 WError::new(
33 "WhiskyPallas - parse tx hex:",
34 &format!("Hex decode error: {}", e),
35 )
36 })?;
37 let pallas_tx = Transaction::decode_bytes(&bytes)?;
38 let mut parser_context = ParserContext::new();
39 parser_context.fill_resolved_utxos(&pallas_tx.inner.transaction_body, resolved_utxos)?;
40 parser_context
41 .collect_script_witnesses_from_tx_witnesses_set(&pallas_tx.inner.transaction_witness_set)?;
42 parser_context.collect_script_witnesses_from_tx_body(&pallas_tx.inner.transaction_body)?;
43
44 let inputs = extract_inputs(&pallas_tx.inner, &parser_context)?;
45 let outputs = extract_outputs(&pallas_tx.inner)?;
46 let collaterals = extract_collaterals(&pallas_tx.inner, &parser_context)?;
47 let required_signers = extract_required_signers(&pallas_tx.inner)?;
48 let reference_inputs = extract_reference_inputs(&pallas_tx.inner, &parser_context)?;
49 let withdrawals = extract_withdrawals(&pallas_tx.inner, &parser_context)?;
50 let mints = extract_mints(&pallas_tx.inner, &parser_context)?;
51 let certificates = extract_certificates(&pallas_tx.inner, &parser_context)?;
52 let validity_range = extract_validity_range(&pallas_tx.inner)?;
53 let metadata = extract_metadata(&pallas_tx.inner)?;
54 let votes = extract_votes(&pallas_tx.inner, &parser_context)?;
55
56 let change_output = outputs.last().unwrap();
57 Ok(TxBuilderBody {
58 inputs,
59 outputs: outputs.clone(),
60 collaterals,
61 required_signatures: required_signers,
62 reference_inputs,
63 withdrawals,
64 mints,
65 change_address: change_output.address.clone(),
66 change_datum: change_output.datum.clone(),
67 metadata,
68 validity_range,
69 certificates,
70 votes,
71 signing_key: vec![],
72 fee: None, network: None,
74 total_collateral: None, collateral_return_address: None, })
77}
78
79pub fn check_tx_required_signers(tx_hex: &str) -> Result<bool, WError> {
80 let bytes = hex::decode(tx_hex).map_err(|e| {
81 WError::new(
82 "WhiskyPallas - check tx required signers:",
83 &format!("Hex decode error: {}", e),
84 )
85 })?;
86 let pallas_tx = Transaction::decode_bytes(&bytes)?;
87 let required_signers = extract_required_signers(&pallas_tx.inner)?;
88
89 if let Some(signatures) = &pallas_tx.inner.transaction_witness_set.vkeywitness {
90 Ok(required_signers.iter().all(|signer| {
91 signatures.iter().any(|vkey_witness| {
92 let vkey_hex = vkey_witness.vkey.to_string();
93 let public_key =
94 PublicKey::from(<[u8; 32]>::try_from(vkey_witness.vkey.to_vec()).unwrap());
95 public_key.verify(
96 pallas_tx.inner.transaction_body.compute_hash(),
97 &Signature::from(
98 <[u8; 64]>::try_from(vkey_witness.signature.to_vec()).unwrap(),
99 ),
100 ) && (vkey_hex == *signer)
101 })
102 }))
103 } else {
104 Ok(required_signers.is_empty())
105 }
106}
107
108pub fn extract_tx_utxos(tx_hex: &str) -> Result<Vec<whisky_common::UTxO>, WError> {
109 let bytes = hex::decode(tx_hex).map_err(|e| {
110 WError::new(
111 "WhiskyPallas - extract tx outputs:",
112 &format!("Hex decode error: {}", e),
113 )
114 })?;
115 let pallas_tx = Transaction::decode_bytes(&bytes)?;
116 extract_outputs(&pallas_tx.inner).and_then(|outputs| {
117 outputs
118 .into_iter()
119 .enumerate()
120 .map(|(index, output)| -> Result<UTxO, WError> {
121 let datum_cbor = match output.datum.clone() {
122 Some(datum) => match datum {
123 whisky_common::Datum::Inline(s) => Some(s),
124 whisky_common::Datum::Hash(_) => None,
125 whisky_common::Datum::Embedded(_) => None,
126 },
127 None => None,
128 };
129 let datum_hash = match output.datum {
130 Some(datum) => match datum {
131 whisky_common::Datum::Inline(_) => None,
132 whisky_common::Datum::Hash(s) => Some(s),
133 whisky_common::Datum::Embedded(s) => Some(s),
134 },
135 None => None,
136 };
137 let script_cbor: Option<String> = match output.reference_script {
138 Some(script) => match script {
139 whisky_common::OutputScriptSource::ProvidedSimpleScriptSource(
140 provided_simple_script_source,
141 ) => Some(
142 ScriptRef::new(ScriptRefKind::NativeScript {
143 native_script_hex: provided_simple_script_source.script_cbor,
144 })
145 .unwrap()
146 .encode()?,
147 ),
148
149 whisky_common::OutputScriptSource::ProvidedScriptSource(
150 provided_script_source,
151 ) => match provided_script_source.language_version {
152 whisky_common::LanguageVersion::V1 => Some(
153 ScriptRef::new(ScriptRefKind::PlutusV1Script {
154 plutus_v1_script_hex: provided_script_source.script_cbor,
155 })
156 .unwrap()
157 .encode()?,
158 ),
159 whisky_common::LanguageVersion::V2 => Some(
160 ScriptRef::new(ScriptRefKind::PlutusV2Script {
161 plutus_v2_script_hex: provided_script_source.script_cbor,
162 })
163 .unwrap()
164 .encode()?,
165 ),
166 whisky_common::LanguageVersion::V3 => Some(
167 ScriptRef::new(ScriptRefKind::PlutusV3Script {
168 plutus_v3_script_hex: provided_script_source.script_cbor,
169 })
170 .unwrap()
171 .encode()?,
172 ),
173 },
174 },
175 None => None,
176 };
177 Ok(UTxO {
178 input: UtxoInput {
179 tx_hash: pallas_tx.inner.transaction_body.compute_hash().to_string(),
180 output_index: index as u32,
181 },
182 output: UtxoOutput {
183 address: output.address,
184 amount: output.amount,
185 plutus_data: datum_cbor,
186 data_hash: datum_hash,
187 script_ref: script_cbor,
188 script_hash: None,
189 },
190 })
191 })
192 .collect()
193 })
194}