pallas_primitives/babbage/
model.rs

1//! Ledger primitives and cbor codec for the Babbage era
2//!
3//! Handcrafted, idiomatic rust artifacts based on based on the [Babbage CDDL](https://github.com/input-output-hk/cardano-ledger/blob/master/eras/babbage/test-suite/cddl-files/babbage.cddl) file in IOHK repo.
4
5use serde::{Deserialize, Serialize};
6
7use pallas_codec::{
8    minicbor::{self, Decode, Encode},
9    utils::{Bytes, CborWrap, KeepRaw, KeyValuePairs, MaybeIndefArray, Nullable},
10};
11use pallas_crypto::hash::{Hash, Hasher};
12
13pub use crate::{
14    plutus_data::*, AddrKeyhash, AssetName, DatumHash, DnsName, Epoch, ExUnitPrices, ExUnits,
15    GenesisDelegateHash, Genesishash, IPv4, IPv6, Metadata, Metadatum, MetadatumLabel, NetworkId,
16    Nonce, NonceVariant, PlutusScript, PolicyId, PoolKeyhash, PoolMetadata, PoolMetadataHash, Port,
17    PositiveInterval, ProtocolVersion, RationalNumber, Relay, ScriptHash, StakeCredential,
18    TransactionIndex, TransactionInput, UnitInterval, VrfCert, VrfKeyhash,
19};
20
21#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
22pub struct HeaderBody {
23    #[n(0)]
24    pub block_number: u64,
25
26    #[n(1)]
27    pub slot: u64,
28
29    #[n(2)]
30    pub prev_hash: Option<Hash<32>>,
31
32    #[n(3)]
33    pub issuer_vkey: Bytes,
34
35    #[n(4)]
36    pub vrf_vkey: Bytes,
37
38    #[n(5)]
39    pub vrf_result: VrfCert,
40
41    #[n(6)]
42    pub block_body_size: u64,
43
44    #[n(7)]
45    pub block_body_hash: Hash<32>,
46
47    #[n(8)]
48    pub operational_cert: OperationalCert,
49
50    #[n(9)]
51    pub protocol_version: ProtocolVersion,
52}
53
54#[derive(Serialize, Deserialize, Encode, Decode, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
55pub struct OperationalCert {
56    #[n(0)]
57    pub operational_cert_hot_vkey: Bytes,
58
59    #[n(1)]
60    pub operational_cert_sequence_number: u64,
61
62    #[n(2)]
63    pub operational_cert_kes_period: u64,
64
65    #[n(3)]
66    pub operational_cert_sigma: Bytes,
67}
68
69pub type MintedHeaderBody<'a> = KeepRaw<'a, HeaderBody>;
70
71#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
72pub struct PseudoHeader<T1> {
73    #[n(0)]
74    pub header_body: T1,
75
76    #[n(1)]
77    pub body_signature: Bytes,
78}
79
80pub type Header = PseudoHeader<HeaderBody>;
81
82pub type MintedHeader<'a> = KeepRaw<'a, PseudoHeader<MintedHeaderBody<'a>>>;
83
84impl<'a> From<MintedHeader<'a>> for Header {
85    fn from(x: MintedHeader<'a>) -> Self {
86        let x = x.unwrap();
87        Self {
88            header_body: x.header_body.into(),
89            body_signature: x.body_signature,
90        }
91    }
92}
93
94impl<'a> From<MintedHeaderBody<'a>> for HeaderBody {
95    fn from(x: MintedHeaderBody<'a>) -> Self {
96        x.unwrap()
97    }
98}
99
100pub use crate::alonzo::Multiasset;
101
102pub use crate::alonzo::Mint;
103
104pub use crate::alonzo::Coin;
105
106pub use crate::alonzo::Value;
107
108pub use crate::alonzo::TransactionOutput as LegacyTransactionOutput;
109
110pub use crate::alonzo::InstantaneousRewardSource;
111
112pub use crate::alonzo::InstantaneousRewardTarget;
113
114pub use crate::alonzo::MoveInstantaneousReward;
115
116pub use crate::alonzo::RewardAccount;
117
118pub use crate::alonzo::Withdrawals;
119
120pub use crate::alonzo::RequiredSigners;
121
122pub use crate::alonzo::Certificate;
123
124#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
125#[cbor(index_only)]
126pub enum Language {
127    #[n(0)]
128    PlutusV1,
129
130    #[n(1)]
131    PlutusV2,
132}
133
134#[deprecated(since = "0.31.0", note = "use `CostModels` instead")]
135pub type CostMdls = CostModels;
136
137pub use crate::alonzo::CostModel;
138
139#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
140#[cbor(map)]
141pub struct CostModels {
142    #[n(0)]
143    pub plutus_v1: Option<CostModel>,
144
145    #[n(1)]
146    pub plutus_v2: Option<CostModel>,
147}
148
149#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
150#[cbor(map)]
151pub struct ProtocolParamUpdate {
152    #[n(0)]
153    pub minfee_a: Option<u32>,
154    #[n(1)]
155    pub minfee_b: Option<u32>,
156    #[n(2)]
157    pub max_block_body_size: Option<u32>,
158    #[n(3)]
159    pub max_transaction_size: Option<u32>,
160    #[n(4)]
161    pub max_block_header_size: Option<u32>,
162    #[n(5)]
163    pub key_deposit: Option<Coin>,
164    #[n(6)]
165    pub pool_deposit: Option<Coin>,
166    #[n(7)]
167    pub maximum_epoch: Option<Epoch>,
168    #[n(8)]
169    pub desired_number_of_stake_pools: Option<u32>,
170    #[n(9)]
171    pub pool_pledge_influence: Option<RationalNumber>,
172    #[n(10)]
173    pub expansion_rate: Option<UnitInterval>,
174    #[n(11)]
175    pub treasury_growth_rate: Option<UnitInterval>,
176
177    #[n(14)]
178    pub protocol_version: Option<ProtocolVersion>,
179    #[n(16)]
180    pub min_pool_cost: Option<Coin>,
181    #[n(17)]
182    pub ada_per_utxo_byte: Option<Coin>,
183    #[n(18)]
184    pub cost_models_for_script_languages: Option<CostModels>,
185    #[n(19)]
186    pub execution_costs: Option<ExUnitPrices>,
187    #[n(20)]
188    pub max_tx_ex_units: Option<ExUnits>,
189    #[n(21)]
190    pub max_block_ex_units: Option<ExUnits>,
191    #[n(22)]
192    pub max_value_size: Option<u32>,
193    #[n(23)]
194    pub collateral_percentage: Option<u32>,
195    #[n(24)]
196    pub max_collateral_inputs: Option<u32>,
197}
198
199#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
200pub struct Update {
201    #[n(0)]
202    pub proposed_protocol_parameter_updates: KeyValuePairs<Genesishash, ProtocolParamUpdate>,
203
204    #[n(1)]
205    pub epoch: Epoch,
206}
207
208#[derive(Encode, Decode, Debug, PartialEq, Clone)]
209#[cbor(map)]
210pub struct PseudoTransactionBody<T1> {
211    #[n(0)]
212    pub inputs: Vec<TransactionInput>,
213
214    #[n(1)]
215    pub outputs: Vec<T1>,
216
217    #[n(2)]
218    pub fee: u64,
219
220    #[n(3)]
221    pub ttl: Option<u64>,
222
223    #[n(4)]
224    pub certificates: Option<Vec<Certificate>>,
225
226    #[n(5)]
227    pub withdrawals: Option<KeyValuePairs<RewardAccount, Coin>>,
228
229    #[n(6)]
230    pub update: Option<Update>,
231
232    #[n(7)]
233    pub auxiliary_data_hash: Option<Bytes>,
234
235    #[n(8)]
236    pub validity_interval_start: Option<u64>,
237
238    #[n(9)]
239    pub mint: Option<Multiasset<i64>>,
240
241    #[n(11)]
242    pub script_data_hash: Option<Hash<32>>,
243
244    #[n(13)]
245    pub collateral: Option<Vec<TransactionInput>>,
246
247    #[n(14)]
248    pub required_signers: Option<Vec<AddrKeyhash>>,
249
250    #[n(15)]
251    pub network_id: Option<NetworkId>,
252
253    #[n(16)]
254    pub collateral_return: Option<T1>,
255
256    #[n(17)]
257    pub total_collateral: Option<Coin>,
258
259    #[n(18)]
260    pub reference_inputs: Option<Vec<TransactionInput>>,
261}
262
263pub type TransactionBody = PseudoTransactionBody<TransactionOutput>;
264
265pub type MintedTransactionBody<'a> = PseudoTransactionBody<MintedTransactionOutput<'a>>;
266
267impl<'a> From<MintedTransactionBody<'a>> for TransactionBody {
268    fn from(value: MintedTransactionBody<'a>) -> Self {
269        Self {
270            inputs: value.inputs,
271            outputs: value.outputs.into_iter().map(|x| x.into()).collect(),
272            fee: value.fee,
273            ttl: value.ttl,
274            certificates: value.certificates,
275            withdrawals: value.withdrawals,
276            update: value.update,
277            auxiliary_data_hash: value.auxiliary_data_hash,
278            validity_interval_start: value.validity_interval_start,
279            mint: value.mint,
280            script_data_hash: value.script_data_hash,
281            collateral: value.collateral,
282            required_signers: value.required_signers,
283            network_id: value.network_id,
284            collateral_return: value.collateral_return.map(|x| x.into()),
285            total_collateral: value.total_collateral,
286            reference_inputs: value.reference_inputs,
287        }
288    }
289}
290
291pub enum VrfDerivation {
292    Leader,
293    Nonce,
294}
295
296pub fn derive_tagged_vrf_output(
297    block_vrf_output_bytes: &[u8],
298    derivation: VrfDerivation,
299) -> Vec<u8> {
300    let mut tagged_vrf: Vec<u8> = match derivation {
301        VrfDerivation::Leader => vec![0x4C_u8], /* "L" */
302        VrfDerivation::Nonce => vec![0x4E_u8],  /* "N" */
303    };
304
305    tagged_vrf.extend(block_vrf_output_bytes);
306    Hasher::<256>::hash(&tagged_vrf).to_vec()
307}
308
309impl HeaderBody {
310    pub fn leader_vrf_output(&self) -> Vec<u8> {
311        derive_tagged_vrf_output(&self.vrf_result.0, VrfDerivation::Leader)
312    }
313
314    pub fn nonce_vrf_output(&self) -> Vec<u8> {
315        derive_tagged_vrf_output(&self.vrf_result.0, VrfDerivation::Nonce)
316    }
317}
318
319#[derive(Debug, PartialEq, Eq, Clone)]
320pub enum PseudoTransactionOutput<T> {
321    Legacy(LegacyTransactionOutput),
322    PostAlonzo(T),
323}
324
325impl<'b, C, T> minicbor::Decode<'b, C> for PseudoTransactionOutput<T>
326where
327    T: minicbor::Decode<'b, C>,
328{
329    fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
330        match d.datatype()? {
331            minicbor::data::Type::Array | minicbor::data::Type::ArrayIndef => {
332                Ok(PseudoTransactionOutput::Legacy(d.decode_with(ctx)?))
333            }
334            minicbor::data::Type::Map | minicbor::data::Type::MapIndef => {
335                Ok(PseudoTransactionOutput::PostAlonzo(d.decode_with(ctx)?))
336            }
337            _ => Err(minicbor::decode::Error::message(
338                "invalid type for transaction output struct",
339            )),
340        }
341    }
342}
343
344impl<C, T> minicbor::Encode<C> for PseudoTransactionOutput<T>
345where
346    T: minicbor::Encode<C>,
347{
348    fn encode<W: minicbor::encode::Write>(
349        &self,
350        e: &mut minicbor::Encoder<W>,
351        ctx: &mut C,
352    ) -> Result<(), minicbor::encode::Error<W::Error>> {
353        match self {
354            PseudoTransactionOutput::Legacy(x) => x.encode(e, ctx),
355            PseudoTransactionOutput::PostAlonzo(x) => x.encode(e, ctx),
356        }
357    }
358}
359
360pub type TransactionOutput = PseudoTransactionOutput<PostAlonzoTransactionOutput>;
361
362pub type MintedTransactionOutput<'b> =
363    PseudoTransactionOutput<MintedPostAlonzoTransactionOutput<'b>>;
364
365impl<'b> From<MintedTransactionOutput<'b>> for TransactionOutput {
366    fn from(value: MintedTransactionOutput<'b>) -> Self {
367        match value {
368            PseudoTransactionOutput::Legacy(x) => Self::Legacy(x),
369            PseudoTransactionOutput::PostAlonzo(x) => Self::PostAlonzo(x.into()),
370        }
371    }
372}
373
374#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
375#[cbor(map)]
376pub struct PseudoPostAlonzoTransactionOutput<T1, T2, T3> {
377    #[n(0)]
378    pub address: Bytes,
379
380    #[n(1)]
381    pub value: T1,
382
383    #[n(2)]
384    pub datum_option: Option<T2>,
385
386    #[n(3)]
387    pub script_ref: Option<CborWrap<T3>>,
388}
389
390pub type PostAlonzoTransactionOutput =
391    PseudoPostAlonzoTransactionOutput<Value, DatumOption, ScriptRef>;
392
393pub type MintedPostAlonzoTransactionOutput<'b> =
394    PseudoPostAlonzoTransactionOutput<Value, MintedDatumOption<'b>, MintedScriptRef<'b>>;
395
396impl<'b> From<MintedPostAlonzoTransactionOutput<'b>> for PostAlonzoTransactionOutput {
397    fn from(value: MintedPostAlonzoTransactionOutput<'b>) -> Self {
398        Self {
399            address: value.address,
400            value: value.value,
401            datum_option: value.datum_option.map(|x| x.into()),
402            script_ref: value.script_ref.map(|x| CborWrap(x.unwrap().into())),
403        }
404    }
405}
406
407pub use crate::alonzo::VKeyWitness;
408
409pub use crate::alonzo::NativeScript;
410
411pub use crate::alonzo::RedeemerTag;
412
413pub use crate::alonzo::Redeemer;
414
415pub use crate::alonzo::BootstrapWitness;
416
417#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Clone)]
418#[cbor(map)]
419pub struct WitnessSet {
420    #[n(0)]
421    pub vkeywitness: Option<Vec<VKeyWitness>>,
422
423    #[n(1)]
424    pub native_script: Option<Vec<NativeScript>>,
425
426    #[n(2)]
427    pub bootstrap_witness: Option<Vec<BootstrapWitness>>,
428
429    #[n(3)]
430    pub plutus_v1_script: Option<Vec<PlutusScript<1>>>,
431
432    #[n(4)]
433    pub plutus_data: Option<Vec<PlutusData>>,
434
435    #[n(5)]
436    pub redeemer: Option<Vec<Redeemer>>,
437
438    #[n(6)]
439    pub plutus_v2_script: Option<Vec<PlutusScript<2>>>,
440}
441
442#[derive(Encode, Decode, Debug, PartialEq, Clone)]
443#[cbor(map)]
444pub struct MintedWitnessSet<'b> {
445    #[n(0)]
446    pub vkeywitness: Option<Vec<VKeyWitness>>,
447
448    #[n(1)]
449    pub native_script: Option<Vec<KeepRaw<'b, NativeScript>>>,
450
451    #[n(2)]
452    pub bootstrap_witness: Option<Vec<BootstrapWitness>>,
453
454    #[n(3)]
455    pub plutus_v1_script: Option<Vec<PlutusScript<1>>>,
456
457    #[b(4)]
458    pub plutus_data: Option<Vec<KeepRaw<'b, PlutusData>>>,
459
460    #[n(5)]
461    pub redeemer: Option<Vec<Redeemer>>,
462
463    #[n(6)]
464    pub plutus_v2_script: Option<Vec<PlutusScript<2>>>,
465}
466
467impl<'b> From<MintedWitnessSet<'b>> for WitnessSet {
468    fn from(x: MintedWitnessSet<'b>) -> Self {
469        WitnessSet {
470            vkeywitness: x.vkeywitness,
471            native_script: x
472                .native_script
473                .map(|x| x.into_iter().map(|x| x.unwrap()).collect()),
474            bootstrap_witness: x.bootstrap_witness,
475            plutus_v1_script: x.plutus_v1_script,
476            plutus_data: x
477                .plutus_data
478                .map(|x| x.into_iter().map(|x| x.unwrap()).collect()),
479            redeemer: x.redeemer,
480            plutus_v2_script: x.plutus_v2_script,
481        }
482    }
483}
484
485#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Clone)]
486#[cbor(map)]
487pub struct PostAlonzoAuxiliaryData {
488    #[n(0)]
489    pub metadata: Option<Metadata>,
490
491    #[n(1)]
492    pub native_scripts: Option<Vec<NativeScript>>,
493
494    #[n(2)]
495    pub plutus_v1_scripts: Option<Vec<PlutusScript<1>>>,
496
497    #[n(3)]
498    pub plutus_v2_scripts: Option<Vec<PlutusScript<2>>>,
499}
500
501// datum_option = [ 0, $hash32 // 1, data ]
502#[derive(Debug, PartialEq, Eq, Clone)]
503pub enum PseudoDatumOption<T1> {
504    Hash(DatumHash),
505    Data(CborWrap<T1>),
506}
507
508impl<'b, C, T> minicbor::Decode<'b, C> for PseudoDatumOption<T>
509where
510    T: minicbor::Decode<'b, C>,
511{
512    fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
513        d.array()?;
514
515        match d.u8()? {
516            0 => Ok(Self::Hash(d.decode_with(ctx)?)),
517            1 => Ok(Self::Data(d.decode_with(ctx)?)),
518            _ => Err(minicbor::decode::Error::message(
519                "invalid variant for datum option enum",
520            )),
521        }
522    }
523}
524
525impl<C, T> minicbor::Encode<C> for PseudoDatumOption<T>
526where
527    T: minicbor::Encode<C>,
528{
529    fn encode<W: minicbor::encode::Write>(
530        &self,
531        e: &mut minicbor::Encoder<W>,
532        ctx: &mut C,
533    ) -> Result<(), minicbor::encode::Error<W::Error>> {
534        match self {
535            Self::Hash(x) => e.encode_with((0, x), ctx)?,
536            Self::Data(x) => e.encode_with((1, x), ctx)?,
537        };
538
539        Ok(())
540    }
541}
542
543pub type DatumOption = PseudoDatumOption<PlutusData>;
544
545pub type MintedDatumOption<'b> = PseudoDatumOption<KeepRaw<'b, PlutusData>>;
546
547impl<'b> From<MintedDatumOption<'b>> for DatumOption {
548    fn from(value: MintedDatumOption<'b>) -> Self {
549        match value {
550            PseudoDatumOption::Hash(x) => Self::Hash(x),
551            PseudoDatumOption::Data(x) => Self::Data(CborWrap(x.unwrap().unwrap())),
552        }
553    }
554}
555
556#[deprecated(since = "0.31.0", note = "use `PlutusScript<1>` instead")]
557pub type PlutusV1Script = PlutusScript<1>;
558
559#[deprecated(since = "0.31.0", note = "use `PlutusScript<2>` instead")]
560pub type PlutusV2Script = PlutusScript<2>;
561
562// script = [ 0, native_script // 1, plutus_v1_script // 2, plutus_v2_script ]
563#[derive(Debug, PartialEq, Eq, Clone)]
564pub enum PseudoScript<T1> {
565    NativeScript(T1),
566    PlutusV1Script(PlutusScript<1>),
567    PlutusV2Script(PlutusScript<2>),
568}
569
570// script_ref = #6.24(bytes .cbor script)
571pub type ScriptRef = PseudoScript<NativeScript>;
572
573pub type MintedScriptRef<'b> = PseudoScript<KeepRaw<'b, NativeScript>>;
574
575impl<'b> From<MintedScriptRef<'b>> for ScriptRef {
576    fn from(value: MintedScriptRef<'b>) -> Self {
577        match value {
578            PseudoScript::NativeScript(x) => Self::NativeScript(x.unwrap()),
579            PseudoScript::PlutusV1Script(x) => Self::PlutusV1Script(x),
580            PseudoScript::PlutusV2Script(x) => Self::PlutusV2Script(x),
581        }
582    }
583}
584
585impl<'b, C, T> minicbor::Decode<'b, C> for PseudoScript<T>
586where
587    T: minicbor::Decode<'b, ()>,
588{
589    fn decode(
590        d: &mut minicbor::Decoder<'b>,
591        _ctx: &mut C,
592    ) -> Result<Self, minicbor::decode::Error> {
593        d.array()?;
594
595        match d.u8()? {
596            0 => Ok(Self::NativeScript(d.decode()?)),
597            1 => Ok(Self::PlutusV1Script(d.decode()?)),
598            2 => Ok(Self::PlutusV2Script(d.decode()?)),
599            _ => Err(minicbor::decode::Error::message(
600                "invalid variant for script enum",
601            )),
602        }
603    }
604}
605
606impl<C, T> minicbor::Encode<C> for PseudoScript<T>
607where
608    T: minicbor::Encode<C>,
609{
610    fn encode<W: minicbor::encode::Write>(
611        &self,
612        e: &mut minicbor::Encoder<W>,
613        ctx: &mut C,
614    ) -> Result<(), minicbor::encode::Error<W::Error>> {
615        match self {
616            Self::NativeScript(x) => e.encode_with((0, x), ctx)?,
617            Self::PlutusV1Script(x) => e.encode_with((1, x), ctx)?,
618            Self::PlutusV2Script(x) => e.encode_with((2, x), ctx)?,
619        };
620
621        Ok(())
622    }
623}
624
625pub use crate::alonzo::AuxiliaryData;
626
627#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Clone)]
628pub struct PseudoBlock<T1, T2, T3, T4>
629where
630    T4: std::clone::Clone,
631{
632    #[n(0)]
633    pub header: T1,
634
635    #[b(1)]
636    pub transaction_bodies: MaybeIndefArray<T2>,
637
638    #[n(2)]
639    pub transaction_witness_sets: MaybeIndefArray<T3>,
640
641    #[n(3)]
642    pub auxiliary_data_set: KeyValuePairs<TransactionIndex, T4>,
643
644    #[n(4)]
645    pub invalid_transactions: Option<MaybeIndefArray<TransactionIndex>>,
646}
647
648pub type Block = PseudoBlock<Header, TransactionBody, WitnessSet, AuxiliaryData>;
649
650/// A memory representation of an already minted block
651///
652/// This structure is analogous to [Block], but it allows to retrieve the
653/// original CBOR bytes for each structure that might require hashing. In this
654/// way, we make sure that the resulting hash matches what exists on-chain.
655pub type MintedBlock<'b> = PseudoBlock<
656    KeepRaw<'b, MintedHeader<'b>>,
657    KeepRaw<'b, MintedTransactionBody<'b>>,
658    KeepRaw<'b, MintedWitnessSet<'b>>,
659    KeepRaw<'b, AuxiliaryData>,
660>;
661
662impl<'b> From<MintedBlock<'b>> for Block {
663    fn from(x: MintedBlock<'b>) -> Self {
664        Block {
665            header: x.header.unwrap().into(),
666            transaction_bodies: MaybeIndefArray::Def(
667                x.transaction_bodies
668                    .iter()
669                    .cloned()
670                    .map(|x| x.unwrap())
671                    .map(TransactionBody::from)
672                    .collect(),
673            ),
674            transaction_witness_sets: MaybeIndefArray::Def(
675                x.transaction_witness_sets
676                    .iter()
677                    .cloned()
678                    .map(|x| x.unwrap())
679                    .map(WitnessSet::from)
680                    .collect(),
681            ),
682            auxiliary_data_set: x
683                .auxiliary_data_set
684                .to_vec()
685                .into_iter()
686                .map(|(k, v)| (k, v.unwrap()))
687                .collect::<Vec<_>>()
688                .into(),
689            invalid_transactions: x.invalid_transactions,
690        }
691    }
692}
693
694#[derive(Clone, Serialize, Deserialize, Encode, Decode, Debug)]
695pub struct PseudoTx<T1, T2, T3>
696where
697    T1: std::clone::Clone,
698    T2: std::clone::Clone,
699    T3: std::clone::Clone,
700{
701    #[n(0)]
702    pub transaction_body: T1,
703
704    #[n(1)]
705    pub transaction_witness_set: T2,
706
707    #[n(2)]
708    pub success: bool,
709
710    #[n(3)]
711    pub auxiliary_data: Nullable<T3>,
712}
713
714pub type Tx = PseudoTx<TransactionBody, WitnessSet, AuxiliaryData>;
715
716pub type MintedTx<'b> = PseudoTx<
717    KeepRaw<'b, MintedTransactionBody<'b>>,
718    KeepRaw<'b, MintedWitnessSet<'b>>,
719    KeepRaw<'b, AuxiliaryData>,
720>;
721
722impl<'b> From<MintedTx<'b>> for Tx {
723    fn from(x: MintedTx<'b>) -> Self {
724        Tx {
725            transaction_body: x.transaction_body.unwrap().into(),
726            transaction_witness_set: x.transaction_witness_set.unwrap().into(),
727            success: x.success,
728            auxiliary_data: x.auxiliary_data.map(|x| x.unwrap()),
729        }
730    }
731}
732
733#[cfg(test)]
734mod tests {
735    use pallas_codec::minicbor;
736
737    use super::{MintedBlock, TransactionOutput};
738    use crate::Fragment;
739
740    type BlockWrapper<'b> = (u16, MintedBlock<'b>);
741
742    #[test]
743    fn block_isomorphic_decoding_encoding() {
744        let test_blocks = [
745            include_str!("../../../test_data/babbage1.block"),
746            include_str!("../../../test_data/babbage2.block"),
747            include_str!("../../../test_data/babbage3.block"),
748            // peculiar block with single plutus cost model
749            include_str!("../../../test_data/babbage4.block"),
750            // peculiar block with i32 overlfow
751            include_str!("../../../test_data/babbage5.block"),
752            // peculiar block with map undef in plutus data
753            include_str!("../../../test_data/babbage6.block"),
754            // block with generic int in cbor
755            include_str!("../../../test_data/babbage7.block"),
756            // block with indef bytes for plutus data bignum
757            include_str!("../../../test_data/babbage8.block"),
758            // block with inline datum that fails hashes
759            include_str!("../../../test_data/babbage9.block"),
760            // block with pool margin numerator greater than i64::MAX
761            include_str!("../../../test_data/babbage10.block"),
762        ];
763
764        for (idx, block_str) in test_blocks.iter().enumerate() {
765            println!("decoding test block {}", idx + 1);
766            let bytes = hex::decode(block_str).unwrap_or_else(|_| panic!("bad block file {idx}"));
767
768            let block: BlockWrapper = minicbor::decode(&bytes[..])
769                .unwrap_or_else(|e| panic!("error decoding cbor for file {idx}: {e:?}"));
770
771            let bytes2 = minicbor::to_vec(block)
772                .unwrap_or_else(|e| panic!("error encoding block cbor for file {idx}: {e:?}"));
773
774            assert!(bytes.eq(&bytes2), "re-encoded bytes didn't match original");
775        }
776    }
777
778    #[test]
779    fn fragments_decoding() {
780        // peculiar array of outputs used in an hydra transaction
781        let hex = include_str!("../../../test_data/babbage1.fr");
782        let bytes = hex::decode(hex).unwrap();
783        let outputs = Vec::<TransactionOutput>::decode_fragment(&bytes).unwrap();
784
785        dbg!(outputs);
786
787        // add any loose fragment tests here
788    }
789}