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