1use std::{fmt, str::FromStr};
3
4use anyhow::anyhow;
5use cardano_serialization_lib as csl;
6#[cfg(feature = "lbf")]
7use lbr_prelude::json::Json;
8use nom::{
9 character::complete::char,
10 combinator::{all_consuming, map, map_res},
11 error::{context, VerboseError},
12 sequence::{preceded, tuple},
13 Finish, IResult,
14};
15use num_bigint::BigInt;
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Serialize};
18
19use super::{
20 address::{Address, StakingCredential},
21 crypto::{ledger_bytes, LedgerBytes, PaymentPubKeyHash},
22 datum::{Datum, DatumHash},
23 interval::PlutusInterval,
24 value::{CurrencySymbol, Value},
25};
26
27use crate::{
28 self as plutus_ledger_api,
29 aux::{big_int, guard_bytes},
30};
31use crate::{
32 csl::pla_to_csl::{TryFromPLAError, TryToCSL},
33 plutus_data::IsPlutusData,
34};
35use crate::{
36 csl::{csl_to_pla::FromCSL, pla_to_csl::TryFromPLA},
37 error::ConversionError,
38};
39
40#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
49#[is_plutus_data_derive_strategy = "Constr"]
50#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
51#[cfg_attr(feature = "lbf", derive(Json))]
52pub struct TransactionInput {
53 pub transaction_id: TransactionHash,
54 pub index: BigInt,
55}
56
57impl fmt::Display for TransactionInput {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 write!(f, "{}#{}", self.transaction_id.0, self.index)
61 }
62}
63
64impl FromCSL<csl::TransactionInput> for TransactionInput {
65 fn from_csl(value: &csl::TransactionInput) -> Self {
66 TransactionInput {
67 transaction_id: TransactionHash::from_csl(&value.transaction_id()),
68 index: BigInt::from_csl(&value.index()),
69 }
70 }
71}
72
73impl TryFromPLA<TransactionInput> for csl::TransactionInput {
74 fn try_from_pla(val: &TransactionInput) -> Result<Self, TryFromPLAError> {
75 Ok(csl::TransactionInput::new(
76 &val.transaction_id.try_to_csl()?,
77 val.index.try_to_csl()?,
78 ))
79 }
80}
81
82impl FromCSL<csl::TransactionInputs> for Vec<TransactionInput> {
83 fn from_csl(value: &csl::TransactionInputs) -> Self {
84 (0..value.len())
85 .map(|idx| TransactionInput::from_csl(&value.get(idx)))
86 .collect()
87 }
88}
89
90impl TryFromPLA<Vec<TransactionInput>> for csl::TransactionInputs {
91 fn try_from_pla(val: &Vec<TransactionInput>) -> Result<Self, TryFromPLAError> {
92 val.iter()
93 .try_fold(csl::TransactionInputs::new(), |mut acc, input| {
94 acc.add(&input.try_to_csl()?);
95 Ok(acc)
96 })
97 }
98}
99
100pub(crate) fn transaction_input(
104 input: &str,
105) -> IResult<&str, TransactionInput, VerboseError<&str>> {
106 map(
107 tuple((transaction_hash, preceded(char('#'), big_int))),
108 |(transaction_id, index)| TransactionInput {
109 transaction_id,
110 index,
111 },
112 )(input)
113}
114
115impl FromStr for TransactionInput {
116 type Err = ConversionError;
117
118 fn from_str(s: &str) -> Result<Self, Self::Err> {
119 all_consuming(transaction_input)(s)
120 .finish()
121 .map_err(|err| {
122 ConversionError::ParseError(anyhow!(
123 "Error while parsing TransactionInput '{}': {}",
124 s,
125 err
126 ))
127 })
128 .map(|(_, cs)| cs)
129 }
130}
131
132#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
141#[is_plutus_data_derive_strategy = "Constr"]
142#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
143#[cfg_attr(feature = "lbf", derive(Json))]
144pub struct TransactionHash(pub LedgerBytes);
145
146impl fmt::Display for TransactionHash {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 write!(f, "{}", self.0)
149 }
150}
151
152impl TransactionHash {
153 pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, ConversionError> {
154 Ok(TransactionHash(LedgerBytes(guard_bytes(
155 "ScriptHash",
156 bytes,
157 32,
158 )?)))
159 }
160}
161
162impl FromCSL<csl::TransactionHash> for TransactionHash {
163 fn from_csl(value: &csl::TransactionHash) -> Self {
164 TransactionHash(LedgerBytes(value.to_bytes()))
165 }
166}
167
168impl TryFromPLA<TransactionHash> for csl::TransactionHash {
169 fn try_from_pla(val: &TransactionHash) -> Result<Self, TryFromPLAError> {
170 csl::TransactionHash::from_bytes(val.0 .0.to_owned())
171 .map_err(TryFromPLAError::CSLDeserializeError)
172 }
173}
174
175pub(crate) fn transaction_hash(input: &str) -> IResult<&str, TransactionHash, VerboseError<&str>> {
179 context(
180 "transaction_hash",
181 map_res(ledger_bytes, |LedgerBytes(bytes)| {
182 TransactionHash::from_bytes(bytes)
183 }),
184 )(input)
185}
186
187impl FromStr for TransactionHash {
188 type Err = ConversionError;
189
190 fn from_str(s: &str) -> Result<Self, Self::Err> {
191 all_consuming(transaction_hash)(s)
192 .finish()
193 .map_err(|err| {
194 ConversionError::ParseError(anyhow!(
195 "Error while parsing TransactionHash '{}': {}",
196 s,
197 err
198 ))
199 })
200 .map(|(_, cs)| cs)
201 }
202}
203
204#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
213#[is_plutus_data_derive_strategy = "Constr"]
214#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
215#[cfg_attr(feature = "lbf", derive(Json))]
216pub struct TransactionOutput {
217 pub address: Address,
218 pub value: Value,
219 pub datum_hash: Option<DatumHash>,
220}
221
222#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
228#[is_plutus_data_derive_strategy = "Newtype"]
229#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
230#[cfg_attr(feature = "lbf", derive(Json))]
231pub struct POSIXTime(pub BigInt);
232
233#[cfg(feature = "chrono")]
234#[derive(thiserror::Error, Debug)]
235pub enum POSIXTimeConversionError {
236 #[error(transparent)]
237 TryFromBigIntError(#[from] num_bigint::TryFromBigIntError<BigInt>),
238 #[error("POSIXTime is out of bounds.")]
239 OutOfBoundsError,
240}
241
242#[cfg(feature = "chrono")]
243impl<Tz: chrono::TimeZone> From<chrono::DateTime<Tz>> for POSIXTime {
244 fn from(datetime: chrono::DateTime<Tz>) -> POSIXTime {
245 POSIXTime(BigInt::from(datetime.timestamp_millis()))
246 }
247}
248
249#[cfg(feature = "chrono")]
250impl TryFrom<POSIXTime> for chrono::DateTime<chrono::Utc> {
251 type Error = POSIXTimeConversionError;
252
253 fn try_from(posix_time: POSIXTime) -> Result<chrono::DateTime<chrono::Utc>, Self::Error> {
254 let POSIXTime(millis) = posix_time;
255 chrono::DateTime::from_timestamp_millis(
256 <i64>::try_from(millis).map_err(POSIXTimeConversionError::TryFromBigIntError)?,
257 )
258 .ok_or(POSIXTimeConversionError::OutOfBoundsError)
259 }
260}
261
262pub type POSIXTimeRange = PlutusInterval<POSIXTime>;
267
268#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
274#[is_plutus_data_derive_strategy = "Constr"]
275#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
276#[cfg_attr(feature = "lbf", derive(Json))]
277pub struct TxInInfo {
278 pub reference: TransactionInput,
279 pub output: TransactionOutput,
280}
281
282impl From<(TransactionInput, TransactionOutput)> for TxInInfo {
283 fn from((reference, output): (TransactionInput, TransactionOutput)) -> TxInInfo {
284 TxInInfo { reference, output }
285 }
286}
287
288#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, IsPlutusData)]
294#[is_plutus_data_derive_strategy = "Constr"]
295#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
296#[cfg_attr(feature = "lbf", derive(Json))]
297pub enum DCert {
298 DelegRegKey(StakingCredential),
299 DelegDeRegKey(StakingCredential),
300 DelegDelegate(
301 StakingCredential,
303 PaymentPubKeyHash,
305 ),
306 PoolRegister(
308 PaymentPubKeyHash,
310 PaymentPubKeyHash,
312 ),
313 PoolRetire(
314 PaymentPubKeyHash,
315 BigInt,
317 ),
318 Genesis,
319 Mir,
320}
321
322#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, IsPlutusData)]
328#[is_plutus_data_derive_strategy = "Constr"]
329#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
330#[cfg_attr(feature = "lbf", derive(Json))]
331pub enum ScriptPurpose {
332 Minting(CurrencySymbol),
333 Spending(TransactionInput),
334 Rewarding(StakingCredential),
335 Certifying(DCert),
336}
337
338#[derive(Debug, PartialEq, Eq, Clone, IsPlutusData)]
344#[is_plutus_data_derive_strategy = "Constr"]
345#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
346#[cfg_attr(feature = "lbf", derive(Json))]
347pub struct TransactionInfo {
348 pub inputs: Vec<TxInInfo>,
349 pub outputs: Vec<TransactionOutput>,
350 pub fee: Value,
351 pub mint: Value,
352 pub d_cert: Vec<DCert>,
353 pub wdrl: Vec<(StakingCredential, BigInt)>,
354 pub valid_range: POSIXTimeRange,
355 pub signatories: Vec<PaymentPubKeyHash>,
356 pub datums: Vec<(DatumHash, Datum)>,
357 pub id: TransactionHash,
358}
359
360#[derive(Debug, PartialEq, Eq, Clone, IsPlutusData)]
366#[is_plutus_data_derive_strategy = "Constr"]
367#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
368#[cfg_attr(feature = "lbf", derive(Json))]
369pub struct ScriptContext {
370 pub tx_info: TransactionInfo,
371 pub purpose: ScriptPurpose,
372}