use std::{fmt, str::FromStr};
use anyhow::anyhow;
use cardano_serialization_lib as csl;
#[cfg(feature = "lbf")]
use lbr_prelude::json::Json;
use nom::{
character::complete::char,
combinator::{all_consuming, map, map_res},
error::{context, VerboseError},
sequence::{preceded, tuple},
Finish, IResult,
};
use num_bigint::BigInt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "chrono")]
pub use crate::v1::transaction::POSIXTimeConversionError;
pub use crate::v2::transaction::{
DCert, POSIXTime, POSIXTimeRange, TransactionOutput, TransactionOutputWithExtraInfo,
WithdrawalsWithExtraInfo,
};
use crate::{
self as plutus_ledger_api,
aux::{big_int, guard_bytes},
csl::{
csl_to_pla::FromCSL,
pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL},
},
error::ConversionError,
plutus_data::{IsPlutusData, PlutusData},
v2::{
address::Credential,
assoc_map::AssocMap,
crypto::{PaymentPubKeyHash, StakePubKeyHash},
datum::{Datum, DatumHash},
redeemer::Redeemer,
script::ScriptHash,
value::{CurrencySymbol, Lovelace, Value},
},
};
use super::{
crypto::{ledger_bytes, Ed25519PubKeyHash, LedgerBytes},
ratio::Rational,
};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Newtype"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct TransactionHash(pub LedgerBytes);
impl fmt::Display for TransactionHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl TransactionHash {
pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, ConversionError> {
Ok(TransactionHash(LedgerBytes(guard_bytes(
"ScriptHash",
bytes,
32,
)?)))
}
}
impl FromCSL<csl::TransactionHash> for TransactionHash {
fn from_csl(value: &csl::TransactionHash) -> Self {
TransactionHash(LedgerBytes(value.to_bytes()))
}
}
impl TryFromPLA<TransactionHash> for csl::TransactionHash {
fn try_from_pla(val: &TransactionHash) -> Result<Self, TryFromPLAError> {
csl::TransactionHash::from_bytes(val.0 .0.to_owned())
.map_err(TryFromPLAError::CSLDeserializeError)
}
}
pub(crate) fn transaction_hash(input: &str) -> IResult<&str, TransactionHash, VerboseError<&str>> {
context(
"transaction_hash",
map_res(ledger_bytes, |LedgerBytes(bytes)| {
TransactionHash::from_bytes(bytes)
}),
)(input)
}
impl FromStr for TransactionHash {
type Err = ConversionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
all_consuming(transaction_hash)(s)
.finish()
.map_err(|err| {
ConversionError::ParseError(anyhow!(
"Error while parsing TransactionHash '{}': {}",
s,
err
))
})
.map(|(_, cs)| cs)
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct TransactionInput {
pub transaction_id: TransactionHash,
pub index: BigInt,
}
impl fmt::Display for TransactionInput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}#{}", self.transaction_id.0, self.index)
}
}
impl FromCSL<csl::TransactionInput> for TransactionInput {
fn from_csl(value: &csl::TransactionInput) -> Self {
TransactionInput {
transaction_id: TransactionHash::from_csl(&value.transaction_id()),
index: BigInt::from_csl(&value.index()),
}
}
}
impl TryFromPLA<TransactionInput> for csl::TransactionInput {
fn try_from_pla(val: &TransactionInput) -> Result<Self, TryFromPLAError> {
Ok(csl::TransactionInput::new(
&val.transaction_id.try_to_csl()?,
val.index.try_to_csl()?,
))
}
}
impl FromCSL<csl::TransactionInputs> for Vec<TransactionInput> {
fn from_csl(value: &csl::TransactionInputs) -> Self {
(0..value.len())
.map(|idx| TransactionInput::from_csl(&value.get(idx)))
.collect()
}
}
impl TryFromPLA<Vec<TransactionInput>> for csl::TransactionInputs {
fn try_from_pla(val: &Vec<TransactionInput>) -> Result<Self, TryFromPLAError> {
val.iter()
.try_fold(csl::TransactionInputs::new(), |mut acc, input| {
acc.add(&input.try_to_csl()?);
Ok(acc)
})
}
}
pub(crate) fn transaction_input(
input: &str,
) -> IResult<&str, TransactionInput, VerboseError<&str>> {
map(
tuple((transaction_hash, preceded(char('#'), big_int))),
|(transaction_id, index)| TransactionInput {
transaction_id,
index,
},
)(input)
}
impl FromStr for TransactionInput {
type Err = ConversionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
all_consuming(transaction_input)(s)
.finish()
.map_err(|err| {
ConversionError::ParseError(anyhow!(
"Error while parsing TransactionInput '{}': {}",
s,
err
))
})
.map(|(_, cs)| cs)
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Newtype"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct ColdCommitteeCredential(pub Credential);
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Newtype"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct HotCommitteeCredential(pub Credential);
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Newtype"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct DRepCredential(pub Credential);
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub enum DRep {
DRep(DRepCredential),
AlwaysAbstain,
AlwaysNoConfidence,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub enum Delegatee {
Stake(StakePubKeyHash),
Vote(DRep),
StakeVote(StakePubKeyHash, DRep),
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub enum TxCert {
RegStaking(Credential, Option<Lovelace>),
UnRegStaking(Credential, Option<Lovelace>),
DelegStaking(Credential, Delegatee),
RegDeleg(Credential, Delegatee, Lovelace),
RegDRep(DRepCredential, Lovelace),
UpdateDRep(DRepCredential),
UnRegDRep(DRepCredential, Lovelace),
PoolRegister(
Ed25519PubKeyHash,
Ed25519PubKeyHash,
),
PoolRetire(Ed25519PubKeyHash, BigInt),
AuthHotCommittee(ColdCommitteeCredential, HotCommitteeCredential),
ResignColdCommittee(ColdCommitteeCredential),
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub enum Voter {
CommitteeVoter(HotCommitteeCredential),
DRepVoter(DRepCredential),
StakePoolVoter(Ed25519PubKeyHash),
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub enum Vote {
VoteNo,
VoteYes,
Abstain,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct GovernanceActionId {
pub tx_id: TransactionHash,
pub gov_action_id: BigInt,
}
#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct Committee {
pub members: AssocMap<ColdCommitteeCredential, BigInt>,
pub quorum: Rational,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct Constitution {
pub constitution_script: Option<ScriptHash>,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct ProtocolVersion {
pub major: BigInt,
pub minor: BigInt,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Newtype"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct ChangedParameters(pub PlutusData);
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub enum GovernanceAction {
ParameterChange(
Option<GovernanceActionId>,
ChangedParameters,
Option<ScriptHash>,
),
HardForkInitiation(Option<GovernanceActionId>, ProtocolVersion),
TreasuryWithdrawals(
AssocMap<Credential, Lovelace>,
Option<ScriptHash>,
),
NoConfidence(Option<GovernanceActionId>),
UpdateCommittee(
Option<GovernanceActionId>,
Vec<ColdCommitteeCredential>,
AssocMap<ColdCommitteeCredential, BigInt>,
Rational,
),
NewConstitution(Option<GovernanceActionId>, Constitution),
InfoAction,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct ProposalProcedure {
pub deposit: Lovelace,
pub return_addr: Credential,
pub governance_action: GovernanceAction,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub enum ScriptPurpose {
Minting(CurrencySymbol),
Spending(TransactionInput),
Rewarding(Credential),
Certifying(
BigInt,
TxCert,
),
Voting(Voter),
Proposing(
BigInt,
ProposalProcedure,
),
}
#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub enum ScriptInfo {
Minting(CurrencySymbol),
Spending(TransactionInput, Option<Datum>),
Rewarding(Credential),
Certifying(BigInt, TxCert),
Voting(Voter),
Proposing(BigInt, ProposalProcedure),
}
#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct TransactionInfo {
pub inputs: Vec<TxInInfo>,
pub reference_inputs: Vec<TxInInfo>,
pub outputs: Vec<TransactionOutput>,
pub fee: Lovelace,
pub mint: Value,
pub tx_certs: Vec<TxCert>,
pub wdrl: AssocMap<Credential, Lovelace>,
pub valid_range: POSIXTimeRange,
pub signatories: Vec<PaymentPubKeyHash>,
pub redeemers: AssocMap<ScriptPurpose, Redeemer>,
pub datums: AssocMap<DatumHash, Datum>,
pub id: TransactionHash,
pub votes: AssocMap<Voter, AssocMap<GovernanceActionId, Vote>>,
pub proposal_procedures: Vec<ProposalProcedure>,
pub current_treasury_amount: Option<Lovelace>,
pub treasury_donation: Option<Lovelace>,
}
#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct TxInInfo {
pub reference: TransactionInput,
pub output: TransactionOutput,
}
impl From<(TransactionInput, TransactionOutput)> for TxInInfo {
fn from((reference, output): (TransactionInput, TransactionOutput)) -> TxInInfo {
TxInInfo { reference, output }
}
}
#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)]
#[is_plutus_data_derive_strategy = "Constr"]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "lbf", derive(Json))]
pub struct ScriptContext {
pub tx_info: TransactionInfo,
pub redeemer: Redeemer,
pub script_info: ScriptInfo,
}