1use std::{fmt, str::FromStr};
4
5use anyhow::anyhow;
6use cardano_serialization_lib as csl;
7#[cfg(feature = "lbf")]
8use lbr_prelude::json::Json;
9use nom::{
10 character::complete::char,
11 combinator::{all_consuming, map, map_res},
12 error::{context, VerboseError},
13 sequence::{preceded, tuple},
14 Finish, IResult,
15};
16use num_bigint::BigInt;
17#[cfg(feature = "serde")]
18use serde::{Deserialize, Serialize};
19#[cfg(feature = "serde")]
20use serde_with::{DeserializeFromStr, SerializeDisplay};
21
22#[cfg(feature = "chrono")]
23pub use crate::v1::transaction::POSIXTimeConversionError;
24pub use crate::v2::transaction::{
25 DCert, POSIXTime, POSIXTimeRange, TransactionOutput, TransactionOutputWithExtraInfo,
26 WithdrawalsWithExtraInfo,
27};
28use crate::{
29 self as plutus_ledger_api,
30 aux::{big_int, guard_bytes},
31 csl::{
32 csl_to_pla::FromCSL,
33 pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL},
34 },
35 error::ConversionError,
36 plutus_data::{IsPlutusData, PlutusData},
37 v2::{
38 address::Credential,
39 assoc_map::AssocMap,
40 crypto::{PaymentPubKeyHash, StakePubKeyHash},
41 datum::{Datum, DatumHash},
42 redeemer::Redeemer,
43 script::ScriptHash,
44 value::{CurrencySymbol, Lovelace, Value},
45 },
46};
47
48use super::{
49 crypto::{ledger_bytes, Ed25519PubKeyHash, LedgerBytes},
50 ratio::Rational,
51};
52
53#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
63#[is_plutus_data_derive_strategy = "Newtype"]
64#[cfg_attr(feature = "serde", derive(SerializeDisplay, DeserializeFromStr))]
65#[cfg_attr(feature = "lbf", derive(Json))]
66pub struct TransactionHash(pub LedgerBytes);
67
68impl fmt::Display for TransactionHash {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 write!(f, "{}", self.0)
71 }
72}
73
74impl TransactionHash {
75 pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, ConversionError> {
76 Ok(TransactionHash(LedgerBytes(guard_bytes(
77 "ScriptHash",
78 bytes,
79 32,
80 )?)))
81 }
82}
83
84impl FromCSL<csl::TransactionHash> for TransactionHash {
85 fn from_csl(value: &csl::TransactionHash) -> Self {
86 TransactionHash(LedgerBytes(value.to_bytes()))
87 }
88}
89
90impl TryFromPLA<TransactionHash> for csl::TransactionHash {
91 fn try_from_pla(val: &TransactionHash) -> Result<Self, TryFromPLAError> {
92 csl::TransactionHash::from_bytes(val.0 .0.to_owned())
93 .map_err(TryFromPLAError::CSLDeserializeError)
94 }
95}
96
97pub(crate) fn transaction_hash(input: &str) -> IResult<&str, TransactionHash, VerboseError<&str>> {
101 context(
102 "transaction_hash",
103 map_res(ledger_bytes, |LedgerBytes(bytes)| {
104 TransactionHash::from_bytes(bytes)
105 }),
106 )(input)
107}
108
109impl FromStr for TransactionHash {
110 type Err = ConversionError;
111
112 fn from_str(s: &str) -> Result<Self, Self::Err> {
113 all_consuming(transaction_hash)(s)
114 .finish()
115 .map_err(|err| {
116 ConversionError::ParseError(anyhow!(
117 "Error while parsing TransactionHash '{}': {}",
118 s,
119 err
120 ))
121 })
122 .map(|(_, cs)| cs)
123 }
124}
125
126#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
135#[is_plutus_data_derive_strategy = "Constr"]
136#[cfg_attr(feature = "serde", derive(SerializeDisplay, DeserializeFromStr))]
137#[cfg_attr(feature = "lbf", derive(Json))]
138pub struct TransactionInput {
139 pub transaction_id: TransactionHash,
140 pub index: BigInt,
141}
142
143impl fmt::Display for TransactionInput {
145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 write!(f, "{}#{}", self.transaction_id.0, self.index)
147 }
148}
149
150impl FromCSL<csl::TransactionInput> for TransactionInput {
151 fn from_csl(value: &csl::TransactionInput) -> Self {
152 TransactionInput {
153 transaction_id: TransactionHash::from_csl(&value.transaction_id()),
154 index: BigInt::from_csl(&value.index()),
155 }
156 }
157}
158
159impl TryFromPLA<TransactionInput> for csl::TransactionInput {
160 fn try_from_pla(val: &TransactionInput) -> Result<Self, TryFromPLAError> {
161 Ok(csl::TransactionInput::new(
162 &val.transaction_id.try_to_csl()?,
163 val.index.try_to_csl()?,
164 ))
165 }
166}
167
168impl FromCSL<csl::TransactionInputs> for Vec<TransactionInput> {
169 fn from_csl(value: &csl::TransactionInputs) -> Self {
170 (0..value.len())
171 .map(|idx| TransactionInput::from_csl(&value.get(idx)))
172 .collect()
173 }
174}
175
176impl TryFromPLA<Vec<TransactionInput>> for csl::TransactionInputs {
177 fn try_from_pla(val: &Vec<TransactionInput>) -> Result<Self, TryFromPLAError> {
178 val.iter()
179 .try_fold(csl::TransactionInputs::new(), |mut acc, input| {
180 acc.add(&input.try_to_csl()?);
181 Ok(acc)
182 })
183 }
184}
185
186pub(crate) fn transaction_input(
190 input: &str,
191) -> IResult<&str, TransactionInput, VerboseError<&str>> {
192 map(
193 tuple((transaction_hash, preceded(char('#'), big_int))),
194 |(transaction_id, index)| TransactionInput {
195 transaction_id,
196 index,
197 },
198 )(input)
199}
200
201impl FromStr for TransactionInput {
202 type Err = ConversionError;
203
204 fn from_str(s: &str) -> Result<Self, Self::Err> {
205 all_consuming(transaction_input)(s)
206 .finish()
207 .map_err(|err| {
208 ConversionError::ParseError(anyhow!(
209 "Error while parsing TransactionInput '{}': {}",
210 s,
211 err
212 ))
213 })
214 .map(|(_, cs)| cs)
215 }
216}
217
218#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
223#[is_plutus_data_derive_strategy = "Newtype"]
224#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
225#[cfg_attr(feature = "lbf", derive(Json))]
226pub struct ColdCommitteeCredential(pub Credential);
227
228#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
233#[is_plutus_data_derive_strategy = "Newtype"]
234#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
235#[cfg_attr(feature = "lbf", derive(Json))]
236pub struct HotCommitteeCredential(pub Credential);
237
238#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
243#[is_plutus_data_derive_strategy = "Newtype"]
244#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
245#[cfg_attr(feature = "lbf", derive(Json))]
246pub struct DRepCredential(pub Credential);
247
248#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
253#[is_plutus_data_derive_strategy = "Constr"]
254#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
255#[cfg_attr(feature = "lbf", derive(Json))]
256pub enum DRep {
257 DRep(DRepCredential),
258 AlwaysAbstain,
259 AlwaysNoConfidence,
260}
261
262#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
267#[is_plutus_data_derive_strategy = "Constr"]
268#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
269#[cfg_attr(feature = "lbf", derive(Json))]
270pub enum Delegatee {
271 Stake(StakePubKeyHash),
272 Vote(DRep),
273 StakeVote(StakePubKeyHash, DRep),
274}
275
276#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
281#[is_plutus_data_derive_strategy = "Constr"]
282#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
283#[cfg_attr(feature = "lbf", derive(Json))]
284pub enum TxCert {
285 RegStaking(Credential, Option<Lovelace>),
287 UnRegStaking(Credential, Option<Lovelace>),
289 DelegStaking(Credential, Delegatee),
291 RegDeleg(Credential, Delegatee, Lovelace),
293 RegDRep(DRepCredential, Lovelace),
295 UpdateDRep(DRepCredential),
297 UnRegDRep(DRepCredential, Lovelace),
299 PoolRegister(
301 Ed25519PubKeyHash,
303 Ed25519PubKeyHash,
305 ),
306 PoolRetire(Ed25519PubKeyHash, BigInt),
308 AuthHotCommittee(ColdCommitteeCredential, HotCommitteeCredential),
310 ResignColdCommittee(ColdCommitteeCredential),
311}
312
313#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
318#[is_plutus_data_derive_strategy = "Constr"]
319#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
320#[cfg_attr(feature = "lbf", derive(Json))]
321pub enum Voter {
322 CommitteeVoter(HotCommitteeCredential),
323 DRepVoter(DRepCredential),
324 StakePoolVoter(Ed25519PubKeyHash),
325}
326
327#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
332#[is_plutus_data_derive_strategy = "Constr"]
333#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
334#[cfg_attr(feature = "lbf", derive(Json))]
335pub enum Vote {
336 VoteNo,
337 VoteYes,
338 Abstain,
339}
340
341#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
347#[is_plutus_data_derive_strategy = "Constr"]
348#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
349#[cfg_attr(feature = "lbf", derive(Json))]
350pub struct GovernanceActionId {
351 pub tx_id: TransactionHash,
352 pub gov_action_id: BigInt,
353}
354
355#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
360#[is_plutus_data_derive_strategy = "Constr"]
361#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
362#[cfg_attr(feature = "lbf", derive(Json))]
363pub struct Committee {
364 pub members: AssocMap<ColdCommitteeCredential, BigInt>,
366 pub quorum: Rational,
368}
369
370#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
375#[is_plutus_data_derive_strategy = "Constr"]
376#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
377#[cfg_attr(feature = "lbf", derive(Json))]
378pub struct Constitution {
379 pub constitution_script: Option<ScriptHash>,
381}
382
383#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
388#[is_plutus_data_derive_strategy = "Constr"]
389#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
390#[cfg_attr(feature = "lbf", derive(Json))]
391pub struct ProtocolVersion {
392 pub major: BigInt,
393 pub minor: BigInt,
394}
395
396#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
403#[is_plutus_data_derive_strategy = "Newtype"]
404#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
405#[cfg_attr(feature = "lbf", derive(Json))]
406pub struct ChangedParameters(pub PlutusData);
407
408#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
413#[is_plutus_data_derive_strategy = "Constr"]
414#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
415#[cfg_attr(feature = "lbf", derive(Json))]
416pub enum GovernanceAction {
417 ParameterChange(
419 Option<GovernanceActionId>,
420 ChangedParameters,
421 Option<ScriptHash>,
423 ),
424 HardForkInitiation(Option<GovernanceActionId>, ProtocolVersion),
426 TreasuryWithdrawals(
428 AssocMap<Credential, Lovelace>,
429 Option<ScriptHash>,
431 ),
432 NoConfidence(Option<GovernanceActionId>),
434 UpdateCommittee(
436 Option<GovernanceActionId>,
437 Vec<ColdCommitteeCredential>,
439 AssocMap<ColdCommitteeCredential, BigInt>,
441 Rational,
443 ),
444 NewConstitution(Option<GovernanceActionId>, Constitution),
446 InfoAction,
447}
448
449#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, IsPlutusData)]
454#[is_plutus_data_derive_strategy = "Constr"]
455#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
456#[cfg_attr(feature = "lbf", derive(Json))]
457pub struct ProposalProcedure {
458 pub deposit: Lovelace,
459 pub return_addr: Credential,
460 pub governance_action: GovernanceAction,
461}
462
463#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, IsPlutusData)]
468#[is_plutus_data_derive_strategy = "Constr"]
469#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
470#[cfg_attr(feature = "lbf", derive(Json))]
471pub enum ScriptPurpose {
472 Minting(CurrencySymbol),
473 Spending(TransactionInput),
474 Rewarding(Credential),
475 Certifying(
476 BigInt,
478 TxCert,
479 ),
480 Voting(Voter),
481 Proposing(
482 BigInt,
484 ProposalProcedure,
485 ),
486}
487
488#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
493#[is_plutus_data_derive_strategy = "Constr"]
494#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
495#[cfg_attr(feature = "lbf", derive(Json))]
496pub enum ScriptInfo {
497 Minting(CurrencySymbol),
498 Spending(TransactionInput, Option<Datum>),
499 Rewarding(Credential),
500 Certifying(BigInt, TxCert),
501 Voting(Voter),
502 Proposing(BigInt, ProposalProcedure),
503}
504
505#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
510#[is_plutus_data_derive_strategy = "Constr"]
511#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
512#[cfg_attr(feature = "lbf", derive(Json))]
513pub struct TransactionInfo {
514 pub inputs: Vec<TxInInfo>,
515 pub reference_inputs: Vec<TxInInfo>,
516 pub outputs: Vec<TransactionOutput>,
517 pub fee: Lovelace,
518 pub mint: Value,
519 pub tx_certs: Vec<TxCert>,
520 pub wdrl: AssocMap<Credential, Lovelace>,
521 pub valid_range: POSIXTimeRange,
522 pub signatories: Vec<PaymentPubKeyHash>,
523 pub redeemers: AssocMap<ScriptPurpose, Redeemer>,
524 pub datums: AssocMap<DatumHash, Datum>,
525 pub id: TransactionHash,
526 pub votes: AssocMap<Voter, AssocMap<GovernanceActionId, Vote>>,
527 pub proposal_procedures: Vec<ProposalProcedure>,
528 pub current_treasury_amount: Option<Lovelace>,
529 pub treasury_donation: Option<Lovelace>,
530}
531
532#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
538#[is_plutus_data_derive_strategy = "Constr"]
539#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
540#[cfg_attr(feature = "lbf", derive(Json))]
541pub struct TxInInfo {
542 pub reference: TransactionInput,
543 pub output: TransactionOutput,
544}
545
546impl From<(TransactionInput, TransactionOutput)> for TxInInfo {
547 fn from((reference, output): (TransactionInput, TransactionOutput)) -> TxInInfo {
548 TxInInfo { reference, output }
549 }
550}
551
552#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
557#[is_plutus_data_derive_strategy = "Constr"]
558#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
559#[cfg_attr(feature = "lbf", derive(Json))]
560pub struct ScriptContext {
561 pub tx_info: TransactionInfo,
562 pub redeemer: Redeemer,
563 pub script_info: ScriptInfo,
564}