1use std::collections::BTreeMap;
4
5use cardano_serialization_lib as csl;
6#[cfg(feature = "lbf")]
7use lbr_prelude::json::Json;
8use num_bigint::BigInt;
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12use crate as plutus_ledger_api;
13use crate::csl::csl_to_pla::{FromCSL, TryFromCSL, TryFromCSLError, TryToPLA};
14use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL};
15use crate::plutus_data::IsPlutusData;
16#[cfg(feature = "chrono")]
17pub use crate::v1::transaction::POSIXTimeConversionError;
18pub use crate::v1::transaction::{
19 DCert, POSIXTime, POSIXTimeRange, ScriptPurpose, TransactionHash, TransactionInput,
20};
21
22use super::address::AddressWithExtraInfo;
23use super::{
24 address::{Address, RewardAddressWithExtraInfo, StakingCredential},
25 assoc_map::AssocMap,
26 crypto::PaymentPubKeyHash,
27 datum::{Datum, DatumHash, OutputDatum},
28 redeemer::Redeemer,
29 script::ScriptHash,
30 value::Value,
31};
32
33#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
42#[is_plutus_data_derive_strategy = "Constr"]
43#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
44#[cfg_attr(feature = "lbf", derive(Json))]
45pub struct TransactionOutput {
46 pub address: Address,
47 pub value: Value,
48 pub datum: OutputDatum,
49 pub reference_script: Option<ScriptHash>,
50}
51
52impl TryFromCSL<csl::TransactionOutput> for TransactionOutput {
53 fn try_from_csl(value: &csl::TransactionOutput) -> Result<Self, TryFromCSLError> {
54 Ok(TransactionOutput {
55 address: value.address().try_to_pla()?,
56 datum: if value.has_data_hash() {
57 OutputDatum::DatumHash(DatumHash::from_csl(&value.data_hash().unwrap()))
58 } else if value.has_plutus_data() {
59 OutputDatum::InlineDatum(Datum(value.plutus_data().unwrap().try_to_pla()?))
60 } else {
61 OutputDatum::None
62 },
63 reference_script: if value.has_script_ref() {
64 let script_ref = value.script_ref().unwrap();
65 let script_hash = if script_ref.is_native_script() {
66 script_ref.native_script().unwrap().hash()
67 } else {
68 script_ref.plutus_script().unwrap().hash()
69 };
70 Some(ScriptHash::from_csl(&script_hash))
71 } else {
72 None
73 },
74 value: Value::from_csl(&value.amount()),
75 })
76 }
77}
78
79impl TryFromCSL<csl::TransactionOutputs> for Vec<TransactionOutput> {
80 fn try_from_csl(value: &csl::TransactionOutputs) -> Result<Self, TryFromCSLError> {
81 (0..value.len())
82 .map(|idx| TransactionOutput::try_from_csl(&value.get(idx)))
83 .collect()
84 }
85}
86
87#[derive(Clone, Debug)]
88pub struct TransactionOutputWithExtraInfo<'a> {
89 pub transaction_output: &'a TransactionOutput,
90 pub scripts: &'a BTreeMap<ScriptHash, csl::PlutusScript>,
91 pub network_id: u8,
92 pub data_cost: &'a csl::DataCost,
93}
94
95impl TryFromPLA<TransactionOutputWithExtraInfo<'_>> for csl::TransactionOutput {
96 fn try_from_pla(val: &TransactionOutputWithExtraInfo<'_>) -> Result<Self, TryFromPLAError> {
97 let mut output_builder = csl::TransactionOutputBuilder::new().with_address(
98 &AddressWithExtraInfo {
99 address: &val.transaction_output.address,
100 network_tag: val.network_id,
101 }
102 .try_to_csl()?,
103 );
104
105 output_builder = match &val.transaction_output.datum {
106 OutputDatum::None => output_builder,
107 OutputDatum::InlineDatum(Datum(d)) => output_builder.with_plutus_data(&d.try_to_csl()?),
108 OutputDatum::DatumHash(dh) => output_builder.with_data_hash(&dh.try_to_csl()?),
109 };
110
111 let script_ref = val
112 .transaction_output
113 .reference_script
114 .clone()
115 .map(|script_hash| -> Result<_, TryFromPLAError> {
116 let script = val
117 .scripts
118 .get(&script_hash)
119 .ok_or(TryFromPLAError::MissingScript(script_hash))?;
120 Ok(csl::ScriptRef::new_plutus_script(script))
121 })
122 .transpose()?;
123
124 if let Some(script_ref) = &script_ref {
125 output_builder = output_builder.with_script_ref(script_ref);
126 };
127
128 let value_without_min_utxo = val.transaction_output.value.try_to_csl()?;
129
130 let mut calc = csl::MinOutputAdaCalculator::new_empty(val.data_cost)
131 .map_err(TryFromPLAError::CSLJsError)?;
132 calc.set_amount(&value_without_min_utxo);
133 match &val.transaction_output.datum {
134 OutputDatum::None => {}
135 OutputDatum::InlineDatum(Datum(d)) => {
136 calc.set_plutus_data(&d.try_to_csl()?);
137 }
138 OutputDatum::DatumHash(dh) => {
139 calc.set_data_hash(&dh.try_to_csl()?);
140 }
141 };
142 if let Some(script_ref) = script_ref {
143 calc.set_script_ref(&script_ref);
144 }
145
146 let required_coin = calc.calculate_ada().map_err(TryFromPLAError::CSLJsError)?;
147 let coin = std::cmp::max(value_without_min_utxo.coin(), required_coin);
148
149 let value = match value_without_min_utxo.multiasset() {
150 Some(multiasset) => csl::Value::new_with_assets(&coin, &multiasset),
151 None => csl::Value::new(&coin),
152 };
153
154 output_builder
155 .next()
156 .map_err(TryFromPLAError::CSLJsError)?
157 .with_value(&value)
158 .build()
159 .map_err(TryFromPLAError::CSLJsError)
160 }
161}
162
163#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
169#[is_plutus_data_derive_strategy = "Constr"]
170#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
171#[cfg_attr(feature = "lbf", derive(Json))]
172pub struct TxInInfo {
173 pub reference: TransactionInput,
174 pub output: TransactionOutput,
175}
176
177impl From<(TransactionInput, TransactionOutput)> for TxInInfo {
178 fn from((reference, output): (TransactionInput, TransactionOutput)) -> TxInInfo {
179 TxInInfo { reference, output }
180 }
181}
182
183#[derive(Debug, PartialEq, Eq, Clone, IsPlutusData)]
188#[is_plutus_data_derive_strategy = "Constr"]
189#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
190#[cfg_attr(feature = "lbf", derive(Json))]
191pub struct TransactionInfo {
192 pub inputs: Vec<TxInInfo>,
193 pub reference_inputs: Vec<TxInInfo>,
194 pub outputs: Vec<TransactionOutput>,
195 pub fee: Value,
196 pub mint: Value,
197 pub d_cert: Vec<DCert>,
198 pub wdrl: AssocMap<StakingCredential, BigInt>,
199 pub valid_range: POSIXTimeRange,
200 pub signatories: Vec<PaymentPubKeyHash>,
201 pub redeemers: AssocMap<ScriptPurpose, Redeemer>,
202 pub datums: AssocMap<DatumHash, Datum>,
203 pub id: TransactionHash,
204}
205
206#[derive(Clone, Debug)]
207pub struct WithdrawalsWithExtraInfo<'a> {
208 pub withdrawals: &'a AssocMap<StakingCredential, BigInt>,
209 pub network_tag: u8,
210}
211
212impl TryFromPLA<WithdrawalsWithExtraInfo<'_>> for csl::Withdrawals {
213 fn try_from_pla(val: &WithdrawalsWithExtraInfo<'_>) -> Result<Self, TryFromPLAError> {
214 val.withdrawals
215 .0
216 .iter()
217 .try_fold(csl::Withdrawals::new(), |mut acc, (s, q)| {
218 acc.insert(
219 &RewardAddressWithExtraInfo {
220 staking_credential: s,
221 network_tag: val.network_tag,
222 }
223 .try_to_csl()?,
224 &q.try_to_csl()?,
225 );
226 Ok(acc)
227 })
228 }
229}
230
231#[derive(Debug, PartialEq, Eq, Clone, IsPlutusData)]
237#[is_plutus_data_derive_strategy = "Constr"]
238#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
239#[cfg_attr(feature = "lbf", derive(Json))]
240pub struct ScriptContext {
241 pub tx_info: TransactionInfo,
242 pub purpose: ScriptPurpose,
243}