pallas_primitives/alonzo/
model.rs

1//! Ledger primitives and cbor codec for the Alonzo era
2//!
3//! Handcrafted, idiomatic rust artifacts based on based on the [Alonzo CDDL](https://github.com/input-output-hk/cardano-ledger/blob/master/eras/alonzo/test-suite/cddl-files/alonzo.cddl) file in IOHK repo.
4
5use serde::{Deserialize, Serialize};
6
7use pallas_codec::minicbor::{self, data::Tag, Decode, Encode};
8
9pub use crate::{
10    plutus_data::*, AddrKeyhash, AssetName, Bytes, Coin, CostModel, DatumHash, DnsName, Epoch,
11    ExUnitPrices, ExUnits, GenesisDelegateHash, Genesishash, Hash, IPv4, IPv6, Int, KeepRaw,
12    KeyValuePairs, MaybeIndefArray, Metadata, Metadatum, MetadatumLabel, NetworkId, Nonce,
13    NonceVariant, Nullable, PlutusScript, PolicyId, PoolKeyhash, PoolMetadata, PoolMetadataHash,
14    Port, PositiveInterval, ProtocolVersion, RationalNumber, Relay, RewardAccount, ScriptHash,
15    StakeCredential, TransactionIndex, TransactionInput, UnitInterval, VrfCert, VrfKeyhash,
16};
17
18#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
19pub struct HeaderBody {
20    #[n(0)]
21    pub block_number: u64,
22
23    #[n(1)]
24    pub slot: u64,
25
26    #[n(2)]
27    pub prev_hash: Option<Hash<32>>,
28
29    #[n(3)]
30    pub issuer_vkey: Bytes,
31
32    #[n(4)]
33    pub vrf_vkey: Bytes,
34
35    #[n(5)]
36    pub nonce_vrf: VrfCert,
37
38    #[n(6)]
39    pub leader_vrf: VrfCert,
40
41    #[n(7)]
42    pub block_body_size: u64,
43
44    #[n(8)]
45    pub block_body_hash: Hash<32>,
46
47    #[n(9)]
48    pub operational_cert_hot_vkey: Bytes,
49
50    #[n(10)]
51    pub operational_cert_sequence_number: u64,
52
53    #[n(11)]
54    pub operational_cert_kes_period: u64,
55
56    #[n(12)]
57    pub operational_cert_sigma: Bytes,
58
59    #[n(13)]
60    pub protocol_major: u64,
61
62    #[n(14)]
63    pub protocol_minor: u64,
64}
65
66pub type MintedHeaderBody<'a> = KeepRaw<'a, HeaderBody>;
67
68#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
69pub struct PseudoHeader<T1> {
70    #[n(0)]
71    pub header_body: T1,
72
73    #[n(1)]
74    pub body_signature: Bytes,
75}
76
77pub type Header = PseudoHeader<HeaderBody>;
78
79pub type MintedHeader<'a> = KeepRaw<'a, PseudoHeader<MintedHeaderBody<'a>>>;
80
81impl<'a> From<MintedHeader<'a>> for Header {
82    fn from(x: MintedHeader<'a>) -> Self {
83        let x = x.unwrap();
84        Self {
85            header_body: x.header_body.into(),
86            body_signature: x.body_signature,
87        }
88    }
89}
90
91impl<'a> From<MintedHeaderBody<'a>> for HeaderBody {
92    fn from(x: MintedHeaderBody<'a>) -> Self {
93        x.unwrap()
94    }
95}
96
97pub type Multiasset<A> = KeyValuePairs<PolicyId, KeyValuePairs<AssetName, A>>;
98
99pub type Mint = Multiasset<i64>;
100
101#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
102pub enum Value {
103    Coin(Coin),
104    Multiasset(Coin, Multiasset<Coin>),
105}
106
107impl<'b, C> minicbor::decode::Decode<'b, C> for Value {
108    fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
109        match d.datatype()? {
110            minicbor::data::Type::U8 => Ok(Value::Coin(d.decode_with(ctx)?)),
111            minicbor::data::Type::U16 => Ok(Value::Coin(d.decode_with(ctx)?)),
112            minicbor::data::Type::U32 => Ok(Value::Coin(d.decode_with(ctx)?)),
113            minicbor::data::Type::U64 => Ok(Value::Coin(d.decode_with(ctx)?)),
114            minicbor::data::Type::Array => {
115                d.array()?;
116                let coin = d.decode_with(ctx)?;
117                let multiasset = d.decode_with(ctx)?;
118                Ok(Value::Multiasset(coin, multiasset))
119            }
120            _ => Err(minicbor::decode::Error::message(
121                "unknown cbor data type for Alonzo Value enum",
122            )),
123        }
124    }
125}
126
127impl<C> minicbor::encode::Encode<C> for Value {
128    fn encode<W: minicbor::encode::Write>(
129        &self,
130        e: &mut minicbor::Encoder<W>,
131        ctx: &mut C,
132    ) -> Result<(), minicbor::encode::Error<W::Error>> {
133        // TODO: check how to deal with uint variants (u32 vs u64)
134        match self {
135            Value::Coin(coin) => {
136                e.encode_with(coin, ctx)?;
137            }
138            Value::Multiasset(coin, other) => {
139                e.array(2)?;
140                e.encode_with(coin, ctx)?;
141                e.encode_with(other, ctx)?;
142            }
143        };
144
145        Ok(())
146    }
147}
148
149#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
150pub struct TransactionOutput {
151    #[n(0)]
152    pub address: Bytes,
153
154    #[n(1)]
155    pub amount: Value,
156
157    #[n(2)]
158    pub datum_hash: Option<DatumHash>,
159}
160
161/* move_instantaneous_reward = [ 0 / 1, { * stake_credential => delta_coin } / coin ]
162; The first field determines where the funds are drawn from.
163; 0 denotes the reserves, 1 denotes the treasury.
164; If the second field is a map, funds are moved to stake credentials,
165; otherwise the funds are given to the other accounting pot.
166 */
167
168#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
169pub enum InstantaneousRewardSource {
170    Reserves,
171    Treasury,
172}
173
174impl<'b, C> minicbor::decode::Decode<'b, C> for InstantaneousRewardSource {
175    fn decode(
176        d: &mut minicbor::Decoder<'b>,
177        _ctx: &mut C,
178    ) -> Result<Self, minicbor::decode::Error> {
179        let variant = d.u32()?;
180
181        match variant {
182            0 => Ok(Self::Reserves),
183            1 => Ok(Self::Treasury),
184            _ => Err(minicbor::decode::Error::message("invalid funds variant")),
185        }
186    }
187}
188
189impl<C> minicbor::encode::Encode<C> for InstantaneousRewardSource {
190    fn encode<W: minicbor::encode::Write>(
191        &self,
192        e: &mut minicbor::Encoder<W>,
193        _ctx: &mut C,
194    ) -> Result<(), minicbor::encode::Error<W::Error>> {
195        let variant = match self {
196            Self::Reserves => 0,
197            Self::Treasury => 1,
198        };
199
200        e.u32(variant)?;
201
202        Ok(())
203    }
204}
205
206#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
207pub enum InstantaneousRewardTarget {
208    StakeCredentials(KeyValuePairs<StakeCredential, i64>),
209    OtherAccountingPot(Coin),
210}
211
212impl<'b, C> minicbor::decode::Decode<'b, C> for InstantaneousRewardTarget {
213    fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
214        let datatype = d.datatype()?;
215
216        match datatype {
217            minicbor::data::Type::Map | minicbor::data::Type::MapIndef => {
218                let a = d.decode_with(ctx)?;
219                Ok(Self::StakeCredentials(a))
220            }
221            _ => {
222                let a = d.decode_with(ctx)?;
223                Ok(Self::OtherAccountingPot(a))
224            }
225        }
226    }
227}
228
229impl<C> minicbor::encode::Encode<C> for InstantaneousRewardTarget {
230    fn encode<W: minicbor::encode::Write>(
231        &self,
232        e: &mut minicbor::Encoder<W>,
233        ctx: &mut C,
234    ) -> Result<(), minicbor::encode::Error<W::Error>> {
235        match self {
236            InstantaneousRewardTarget::StakeCredentials(a) => {
237                e.encode_with(a, ctx)?;
238                Ok(())
239            }
240            InstantaneousRewardTarget::OtherAccountingPot(a) => {
241                e.encode_with(a, ctx)?;
242                Ok(())
243            }
244        }
245    }
246}
247
248#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
249#[cbor()]
250pub struct MoveInstantaneousReward {
251    #[n(0)]
252    pub source: InstantaneousRewardSource,
253
254    #[n(1)]
255    pub target: InstantaneousRewardTarget,
256}
257
258pub type Withdrawals = KeyValuePairs<RewardAccount, Coin>;
259
260pub type RequiredSigners = Vec<AddrKeyhash>;
261
262#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
263pub enum Certificate {
264    StakeRegistration(StakeCredential),
265    StakeDeregistration(StakeCredential),
266    StakeDelegation(StakeCredential, PoolKeyhash),
267    PoolRegistration {
268        operator: PoolKeyhash,
269        vrf_keyhash: VrfKeyhash,
270        pledge: Coin,
271        cost: Coin,
272        margin: UnitInterval,
273        reward_account: RewardAccount,
274        pool_owners: Vec<AddrKeyhash>,
275        relays: Vec<Relay>,
276        pool_metadata: Nullable<PoolMetadata>,
277    },
278    PoolRetirement(PoolKeyhash, Epoch),
279    GenesisKeyDelegation(Genesishash, GenesisDelegateHash, VrfKeyhash),
280    MoveInstantaneousRewardsCert(MoveInstantaneousReward),
281}
282
283impl<'b, C> minicbor::decode::Decode<'b, C> for Certificate {
284    fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
285        d.array()?;
286        let variant = d.u16()?;
287
288        match variant {
289            0 => {
290                let a = d.decode_with(ctx)?;
291                Ok(Certificate::StakeRegistration(a))
292            }
293            1 => {
294                let a = d.decode_with(ctx)?;
295                Ok(Certificate::StakeDeregistration(a))
296            }
297            2 => {
298                let a = d.decode_with(ctx)?;
299                let b = d.decode_with(ctx)?;
300                Ok(Certificate::StakeDelegation(a, b))
301            }
302            3 => {
303                let operator = d.decode_with(ctx)?;
304                let vrf_keyhash = d.decode_with(ctx)?;
305                let pledge = d.decode_with(ctx)?;
306                let cost = d.decode_with(ctx)?;
307                let margin = d.decode_with(ctx)?;
308                let reward_account = d.decode_with(ctx)?;
309                let pool_owners = d.decode_with(ctx)?;
310                let relays = d.decode_with(ctx)?;
311                let pool_metadata = d.decode_with(ctx)?;
312
313                Ok(Certificate::PoolRegistration {
314                    operator,
315                    vrf_keyhash,
316                    pledge,
317                    cost,
318                    margin,
319                    reward_account,
320                    pool_owners,
321                    relays,
322                    pool_metadata,
323                })
324            }
325            4 => {
326                let a = d.decode_with(ctx)?;
327                let b = d.decode_with(ctx)?;
328                Ok(Certificate::PoolRetirement(a, b))
329            }
330            5 => {
331                let a = d.decode_with(ctx)?;
332                let b = d.decode_with(ctx)?;
333                let c = d.decode_with(ctx)?;
334                Ok(Certificate::GenesisKeyDelegation(a, b, c))
335            }
336            6 => {
337                let a = d.decode_with(ctx)?;
338                Ok(Certificate::MoveInstantaneousRewardsCert(a))
339            }
340            _ => Err(minicbor::decode::Error::message(
341                "unknown variant id for certificate",
342            )),
343        }
344    }
345}
346
347impl<C> minicbor::encode::Encode<C> for Certificate {
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            Certificate::StakeRegistration(a) => {
355                e.array(2)?;
356                e.u16(0)?;
357                e.encode_with(a, ctx)?;
358
359                Ok(())
360            }
361            Certificate::StakeDeregistration(a) => {
362                e.array(2)?;
363                e.u16(1)?;
364                e.encode_with(a, ctx)?;
365
366                Ok(())
367            }
368            Certificate::StakeDelegation(a, b) => {
369                e.array(3)?;
370                e.u16(2)?;
371                e.encode_with(a, ctx)?;
372                e.encode_with(b, ctx)?;
373
374                Ok(())
375            }
376            Certificate::PoolRegistration {
377                operator,
378                vrf_keyhash,
379                pledge,
380                cost,
381                margin,
382                reward_account,
383                pool_owners,
384                relays,
385                pool_metadata,
386            } => {
387                e.array(10)?;
388                e.u16(3)?;
389
390                e.encode_with(operator, ctx)?;
391                e.encode_with(vrf_keyhash, ctx)?;
392                e.encode_with(pledge, ctx)?;
393                e.encode_with(cost, ctx)?;
394                e.encode_with(margin, ctx)?;
395                e.encode_with(reward_account, ctx)?;
396                e.encode_with(pool_owners, ctx)?;
397                e.encode_with(relays, ctx)?;
398                e.encode_with(pool_metadata, ctx)?;
399
400                Ok(())
401            }
402            Certificate::PoolRetirement(a, b) => {
403                e.array(3)?;
404                e.u16(4)?;
405                e.encode_with(a, ctx)?;
406                e.encode_with(b, ctx)?;
407
408                Ok(())
409            }
410            Certificate::GenesisKeyDelegation(a, b, c) => {
411                e.array(4)?;
412                e.u16(5)?;
413                e.encode_with(a, ctx)?;
414                e.encode_with(b, ctx)?;
415                e.encode_with(c, ctx)?;
416
417                Ok(())
418            }
419            Certificate::MoveInstantaneousRewardsCert(a) => {
420                e.array(2)?;
421                e.u16(6)?;
422                e.encode_with(a, ctx)?;
423
424                Ok(())
425            }
426        }
427    }
428}
429
430#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
431#[cbor(index_only)]
432pub enum Language {
433    #[n(0)]
434    PlutusV1,
435}
436
437#[deprecated(since = "0.31.0", note = "use `CostModels` instead")]
438pub type CostMdls = CostModels;
439
440pub type CostModels = KeyValuePairs<Language, CostModel>;
441
442#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
443#[cbor(map)]
444pub struct ProtocolParamUpdate {
445    #[n(0)]
446    pub minfee_a: Option<u32>,
447    #[n(1)]
448    pub minfee_b: Option<u32>,
449    #[n(2)]
450    pub max_block_body_size: Option<u32>,
451    #[n(3)]
452    pub max_transaction_size: Option<u32>,
453    #[n(4)]
454    pub max_block_header_size: Option<u32>,
455    #[n(5)]
456    pub key_deposit: Option<Coin>,
457    #[n(6)]
458    pub pool_deposit: Option<Coin>,
459    #[n(7)]
460    pub maximum_epoch: Option<Epoch>,
461    #[n(8)]
462    pub desired_number_of_stake_pools: Option<u32>,
463    #[n(9)]
464    pub pool_pledge_influence: Option<RationalNumber>,
465    #[n(10)]
466    pub expansion_rate: Option<UnitInterval>,
467    #[n(11)]
468    pub treasury_growth_rate: Option<UnitInterval>,
469    #[n(12)]
470    pub decentralization_constant: Option<UnitInterval>,
471    #[n(13)]
472    pub extra_entropy: Option<Nonce>,
473    #[n(14)]
474    pub protocol_version: Option<ProtocolVersion>,
475    #[n(16)]
476    pub min_pool_cost: Option<Coin>,
477    #[n(17)]
478    pub ada_per_utxo_byte: Option<Coin>,
479    #[n(18)]
480    pub cost_models_for_script_languages: Option<CostModels>,
481    #[n(19)]
482    pub execution_costs: Option<ExUnitPrices>,
483    #[n(20)]
484    pub max_tx_ex_units: Option<ExUnits>,
485    #[n(21)]
486    pub max_block_ex_units: Option<ExUnits>,
487    #[n(22)]
488    pub max_value_size: Option<u32>,
489    #[n(23)]
490    pub collateral_percentage: Option<u32>,
491    #[n(24)]
492    pub max_collateral_inputs: Option<u32>,
493}
494
495#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
496pub struct Update {
497    #[n(0)]
498    pub proposed_protocol_parameter_updates: KeyValuePairs<Genesishash, ProtocolParamUpdate>,
499
500    #[n(1)]
501    pub epoch: Epoch,
502}
503
504// Can't derive encode for TransactionBody because it seems to require a very
505// particular order for each key in the map
506#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
507#[cbor(map)]
508pub struct TransactionBody {
509    #[n(0)]
510    pub inputs: Vec<TransactionInput>,
511
512    #[n(1)]
513    pub outputs: Vec<TransactionOutput>,
514
515    #[n(2)]
516    pub fee: u64,
517
518    #[n(3)]
519    pub ttl: Option<u64>,
520
521    #[n(4)]
522    pub certificates: Option<Vec<Certificate>>,
523
524    #[n(5)]
525    pub withdrawals: Option<Withdrawals>,
526
527    #[n(6)]
528    pub update: Option<Update>,
529
530    #[n(7)]
531    pub auxiliary_data_hash: Option<Bytes>,
532
533    #[n(8)]
534    pub validity_interval_start: Option<u64>,
535
536    #[n(9)]
537    pub mint: Option<Multiasset<i64>>,
538
539    #[n(11)]
540    pub script_data_hash: Option<Hash<32>>,
541
542    #[n(13)]
543    pub collateral: Option<Vec<TransactionInput>>,
544
545    #[n(14)]
546    pub required_signers: Option<RequiredSigners>,
547
548    #[n(15)]
549    pub network_id: Option<NetworkId>,
550}
551
552#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
553pub struct VKeyWitness {
554    #[n(0)]
555    pub vkey: Bytes,
556
557    #[n(1)]
558    pub signature: Bytes,
559}
560
561#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
562pub enum NativeScript {
563    ScriptPubkey(AddrKeyhash),
564    ScriptAll(Vec<NativeScript>),
565    ScriptAny(Vec<NativeScript>),
566    ScriptNOfK(u32, Vec<NativeScript>),
567    InvalidBefore(u64),
568    InvalidHereafter(u64),
569}
570
571impl<'b, C> minicbor::decode::Decode<'b, C> for NativeScript {
572    fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
573        let size = d.array()?;
574
575        let assert_size = |expected| {
576            // NOTE: unwrap_or allows for indefinite arrays.
577            if expected != size.unwrap_or(expected) {
578                return Err(minicbor::decode::Error::message(
579                    "unexpected array size in NativeScript",
580                ));
581            }
582            Ok(())
583        };
584
585        let variant = d.u32()?;
586
587        let script = match variant {
588            0 => {
589                assert_size(2)?;
590                Ok(NativeScript::ScriptPubkey(d.decode_with(ctx)?))
591            }
592            1 => {
593                assert_size(2)?;
594                Ok(NativeScript::ScriptAll(d.decode_with(ctx)?))
595            }
596            2 => {
597                assert_size(2)?;
598                Ok(NativeScript::ScriptAny(d.decode_with(ctx)?))
599            }
600            3 => {
601                assert_size(3)?;
602                Ok(NativeScript::ScriptNOfK(
603                    d.decode_with(ctx)?,
604                    d.decode_with(ctx)?,
605                ))
606            }
607            4 => {
608                assert_size(2)?;
609                Ok(NativeScript::InvalidBefore(d.decode_with(ctx)?))
610            }
611            5 => {
612                assert_size(2)?;
613                Ok(NativeScript::InvalidHereafter(d.decode_with(ctx)?))
614            }
615            _ => Err(minicbor::decode::Error::message(
616                "unknown variant id for native script",
617            )),
618        }?;
619
620        if size.is_none() {
621            let next = d.datatype()?;
622            if next != minicbor::data::Type::Break {
623                return Err(minicbor::decode::Error::type_mismatch(next));
624            }
625        }
626
627        Ok(script)
628    }
629}
630
631impl<C> minicbor::encode::Encode<C> for NativeScript {
632    fn encode<W: minicbor::encode::Write>(
633        &self,
634        e: &mut minicbor::Encoder<W>,
635        ctx: &mut C,
636    ) -> Result<(), minicbor::encode::Error<W::Error>> {
637        match self {
638            NativeScript::ScriptPubkey(v) => {
639                e.array(2)?;
640                e.encode_with(0, ctx)?;
641                e.encode_with(v, ctx)?;
642            }
643            NativeScript::ScriptAll(v) => {
644                e.array(2)?;
645                e.encode_with(1, ctx)?;
646                e.encode_with(v, ctx)?;
647            }
648            NativeScript::ScriptAny(v) => {
649                e.array(2)?;
650                e.encode_with(2, ctx)?;
651                e.encode_with(v, ctx)?;
652            }
653            NativeScript::ScriptNOfK(a, b) => {
654                e.array(3)?;
655                e.encode_with(3, ctx)?;
656                e.encode_with(a, ctx)?;
657                e.encode_with(b, ctx)?;
658            }
659            NativeScript::InvalidBefore(v) => {
660                e.array(2)?;
661                e.encode_with(4, ctx)?;
662                e.encode_with(v, ctx)?;
663            }
664            NativeScript::InvalidHereafter(v) => {
665                e.array(2)?;
666                e.encode_with(5, ctx)?;
667                e.encode_with(v, ctx)?;
668            }
669        }
670
671        Ok(())
672    }
673}
674
675#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, Copy)]
676#[cbor(index_only)]
677pub enum RedeemerTag {
678    #[n(0)]
679    Spend,
680    #[n(1)]
681    Mint,
682    #[n(2)]
683    Cert,
684    #[n(3)]
685    Reward,
686}
687
688#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
689pub struct Redeemer {
690    #[n(0)]
691    pub tag: RedeemerTag,
692
693    #[n(1)]
694    pub index: u32,
695
696    #[n(2)]
697    pub data: PlutusData,
698
699    #[n(3)]
700    pub ex_units: ExUnits,
701}
702
703#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, Copy)]
704pub struct RedeemerPointer {
705    #[n(0)]
706    pub tag: RedeemerTag,
707
708    #[n(1)]
709    pub index: u32,
710}
711
712/* bootstrap_witness =
713[ public_key : $vkey
714, signature  : $signature
715, chain_code : bytes .size 32
716, attributes : bytes
717] */
718
719#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
720pub struct BootstrapWitness {
721    #[n(0)]
722    pub public_key: Bytes,
723
724    #[n(1)]
725    pub signature: Bytes,
726
727    #[n(2)]
728    pub chain_code: Bytes,
729
730    #[n(3)]
731    pub attributes: Bytes,
732}
733
734#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Clone)]
735#[cbor(map)]
736pub struct WitnessSet {
737    #[n(0)]
738    pub vkeywitness: Option<Vec<VKeyWitness>>,
739
740    #[n(1)]
741    pub native_script: Option<Vec<NativeScript>>,
742
743    #[n(2)]
744    pub bootstrap_witness: Option<Vec<BootstrapWitness>>,
745
746    #[n(3)]
747    pub plutus_script: Option<Vec<PlutusScript<1>>>,
748
749    #[n(4)]
750    pub plutus_data: Option<Vec<PlutusData>>,
751
752    #[n(5)]
753    pub redeemer: Option<Vec<Redeemer>>,
754}
755
756#[derive(Encode, Decode, Debug, PartialEq, Clone)]
757#[cbor(map)]
758pub struct MintedWitnessSet<'b> {
759    #[n(0)]
760    pub vkeywitness: Option<Vec<VKeyWitness>>,
761
762    #[n(1)]
763    pub native_script: Option<Vec<KeepRaw<'b, NativeScript>>>,
764
765    #[n(2)]
766    pub bootstrap_witness: Option<Vec<BootstrapWitness>>,
767
768    #[n(3)]
769    pub plutus_script: Option<Vec<PlutusScript<1>>>,
770
771    #[b(4)]
772    pub plutus_data: Option<Vec<KeepRaw<'b, PlutusData>>>,
773
774    #[n(5)]
775    pub redeemer: Option<Vec<Redeemer>>,
776}
777
778impl<'b> From<MintedWitnessSet<'b>> for WitnessSet {
779    #[allow(deprecated)]
780    fn from(x: MintedWitnessSet<'b>) -> Self {
781        WitnessSet {
782            vkeywitness: x.vkeywitness,
783            native_script: x
784                .native_script
785                .map(|x| x.into_iter().map(|x| x.unwrap()).collect()),
786            bootstrap_witness: x.bootstrap_witness,
787            plutus_script: x.plutus_script,
788            plutus_data: x
789                .plutus_data
790                .map(|x| x.into_iter().map(|x| x.unwrap()).collect()),
791            redeemer: x.redeemer,
792        }
793    }
794}
795
796#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
797#[cbor(map)]
798pub struct PostAlonzoAuxiliaryData {
799    #[n(0)]
800    pub metadata: Option<Metadata>,
801
802    #[n(1)]
803    pub native_scripts: Option<Vec<NativeScript>>,
804
805    #[n(2)]
806    pub plutus_scripts: Option<Vec<PlutusScript<1>>>,
807}
808
809#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
810pub struct ShelleyMaAuxiliaryData {
811    #[n(0)]
812    pub transaction_metadata: Metadata,
813
814    #[n(1)]
815    pub auxiliary_scripts: Option<Vec<NativeScript>>,
816}
817
818#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
819pub enum AuxiliaryData {
820    Shelley(Metadata),
821    ShelleyMa(ShelleyMaAuxiliaryData),
822    PostAlonzo(PostAlonzoAuxiliaryData),
823}
824
825impl<'b, C> minicbor::Decode<'b, C> for AuxiliaryData {
826    fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
827        match d.datatype()? {
828            minicbor::data::Type::Map | minicbor::data::Type::MapIndef => {
829                Ok(AuxiliaryData::Shelley(d.decode_with(ctx)?))
830            }
831            minicbor::data::Type::Array => Ok(AuxiliaryData::ShelleyMa(d.decode_with(ctx)?)),
832            minicbor::data::Type::Tag => {
833                d.tag()?;
834                Ok(AuxiliaryData::PostAlonzo(d.decode_with(ctx)?))
835            }
836            _ => Err(minicbor::decode::Error::message(
837                "Can't infer variant from data type for AuxiliaryData",
838            )),
839        }
840    }
841}
842
843impl<C> minicbor::Encode<C> for AuxiliaryData {
844    fn encode<W: minicbor::encode::Write>(
845        &self,
846        e: &mut minicbor::Encoder<W>,
847        ctx: &mut C,
848    ) -> Result<(), minicbor::encode::Error<W::Error>> {
849        match self {
850            AuxiliaryData::Shelley(m) => {
851                e.encode_with(m, ctx)?;
852            }
853            AuxiliaryData::ShelleyMa(m) => {
854                e.encode_with(m, ctx)?;
855            }
856            AuxiliaryData::PostAlonzo(v) => {
857                // TODO: check if this is the correct tag
858                e.tag(Tag::new(259))?;
859                e.encode_with(v, ctx)?;
860            }
861        };
862
863        Ok(())
864    }
865}
866
867#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Clone)]
868pub struct Block {
869    #[n(0)]
870    pub header: Header,
871
872    #[b(1)]
873    pub transaction_bodies: Vec<TransactionBody>,
874
875    #[n(2)]
876    pub transaction_witness_sets: Vec<WitnessSet>,
877
878    #[n(3)]
879    pub auxiliary_data_set: KeyValuePairs<TransactionIndex, AuxiliaryData>,
880
881    #[n(4)]
882    pub invalid_transactions: Option<Vec<TransactionIndex>>,
883}
884
885/// A memory representation of an already minted block
886///
887/// This structure is analogous to [Block], but it allows to retrieve the
888/// original CBOR bytes for each structure that might require hashing. In this
889/// way, we make sure that the resulting hash matches what exists on-chain.
890#[derive(Encode, Decode, Debug, PartialEq, Clone)]
891pub struct MintedBlock<'b> {
892    #[n(0)]
893    pub header: KeepRaw<'b, MintedHeader<'b>>,
894
895    #[b(1)]
896    pub transaction_bodies: MaybeIndefArray<KeepRaw<'b, TransactionBody>>,
897
898    #[n(2)]
899    pub transaction_witness_sets: MaybeIndefArray<KeepRaw<'b, MintedWitnessSet<'b>>>,
900
901    #[n(3)]
902    pub auxiliary_data_set: KeyValuePairs<TransactionIndex, KeepRaw<'b, AuxiliaryData>>,
903
904    #[n(4)]
905    pub invalid_transactions: Option<MaybeIndefArray<TransactionIndex>>,
906}
907
908impl<'b> From<MintedBlock<'b>> for Block {
909    fn from(x: MintedBlock<'b>) -> Self {
910        Block {
911            header: x.header.unwrap().into(),
912            transaction_bodies: x
913                .transaction_bodies
914                .to_vec()
915                .into_iter()
916                .map(|x| x.unwrap())
917                .collect(),
918            transaction_witness_sets: x
919                .transaction_witness_sets
920                .to_vec()
921                .into_iter()
922                .map(|x| x.unwrap())
923                .map(WitnessSet::from)
924                .collect(),
925            auxiliary_data_set: x
926                .auxiliary_data_set
927                .to_vec()
928                .into_iter()
929                .map(|(k, v)| (k, v.unwrap()))
930                .collect::<Vec<_>>()
931                .into(),
932            invalid_transactions: x.invalid_transactions.map(|x| x.into()),
933        }
934    }
935}
936
937#[derive(Serialize, Deserialize, Encode, Decode, Debug)]
938pub struct Tx {
939    #[n(0)]
940    pub transaction_body: TransactionBody,
941
942    #[n(1)]
943    pub transaction_witness_set: WitnessSet,
944
945    #[n(2)]
946    pub success: bool,
947
948    #[n(3)]
949    pub auxiliary_data: Nullable<AuxiliaryData>,
950}
951
952#[derive(Encode, Decode, Debug, Clone)]
953pub struct MintedTx<'b> {
954    #[b(0)]
955    pub transaction_body: KeepRaw<'b, TransactionBody>,
956
957    #[n(1)]
958    pub transaction_witness_set: KeepRaw<'b, MintedWitnessSet<'b>>,
959
960    #[n(2)]
961    pub success: bool,
962
963    #[n(3)]
964    pub auxiliary_data: Nullable<KeepRaw<'b, AuxiliaryData>>,
965}
966
967#[cfg(test)]
968mod tests {
969    use pallas_codec::minicbor::{self, to_vec};
970
971    use crate::{alonzo::PlutusData, Fragment};
972
973    use super::{Header, MintedBlock, NativeScript};
974
975    type BlockWrapper<'b> = (u16, MintedBlock<'b>);
976
977    #[test]
978    fn native_script_encode_n_of_k() {
979        let script = NativeScript::ScriptNOfK(1, vec![]);
980        let bytes = minicbor::to_vec(script).unwrap();
981        assert_eq!(&hex::encode(&bytes), "83030180");
982    }
983
984    #[test]
985    fn native_script_decode_indef() {
986        let mut bytes = vec![0x9f, 0x02, 0x80]; // truncated
987
988        let result: Result<NativeScript, _> = minicbor::decode(&bytes);
989
990        assert!(
991            result.is_err(),
992            "should have failed but yielded: {result:#?}"
993        );
994
995        bytes.push(0xFF);
996
997        assert_eq!(
998            minicbor::decode(&bytes).map_err(|e| e.to_string()),
999            Ok(NativeScript::ScriptAny(vec![]))
1000        );
1001    }
1002
1003    #[test]
1004    fn block_isomorphic_decoding_encoding() {
1005        let test_blocks = vec![
1006            include_str!("../../../test_data/alonzo1.block"),
1007            include_str!("../../../test_data/alonzo2.block"),
1008            include_str!("../../../test_data/alonzo3.block"),
1009            include_str!("../../../test_data/alonzo4.block"),
1010            include_str!("../../../test_data/alonzo5.block"),
1011            include_str!("../../../test_data/alonzo6.block"),
1012            include_str!("../../../test_data/alonzo7.block"),
1013            include_str!("../../../test_data/alonzo8.block"),
1014            include_str!("../../../test_data/alonzo9.block"),
1015            // old block without invalid_transactions fields
1016            include_str!("../../../test_data/alonzo10.block"),
1017            // peculiar block with protocol update params
1018            include_str!("../../../test_data/alonzo11.block"),
1019            // peculiar block with decoding issue
1020            // https://github.com/txpipe/oura/issues/37
1021            include_str!("../../../test_data/alonzo12.block"),
1022            // peculiar block with protocol update params, including nonce
1023            include_str!("../../../test_data/alonzo13.block"),
1024            // peculiar block with overflow crash
1025            // https://github.com/txpipe/oura/issues/113
1026            include_str!("../../../test_data/alonzo14.block"),
1027            // peculiar block with many move-instantaneous-rewards certs
1028            include_str!("../../../test_data/alonzo15.block"),
1029            // peculiar block with protocol update values
1030            include_str!("../../../test_data/alonzo16.block"),
1031            // peculiar block with missing nonce hash
1032            include_str!("../../../test_data/alonzo17.block"),
1033            // peculiar block with strange AuxiliaryData variant
1034            include_str!("../../../test_data/alonzo18.block"),
1035            // peculiar block with strange AuxiliaryData variant
1036            include_str!("../../../test_data/alonzo18.block"),
1037            // peculiar block with nevative i64 overflow
1038            include_str!("../../../test_data/alonzo19.block"),
1039            // peculiar block with very BigInt in plutus code
1040            include_str!("../../../test_data/alonzo20.block"),
1041            // peculiar block with bad tx hash
1042            include_str!("../../../test_data/alonzo21.block"),
1043            // peculiar block with bad tx hash
1044            include_str!("../../../test_data/alonzo22.block"),
1045            // peculiar block with indef byte array in plutus data
1046            include_str!("../../../test_data/alonzo23.block"),
1047            // peculiar block with invalid address (pointer overflow)
1048            include_str!("../../../test_data/alonzo27.block"),
1049        ];
1050
1051        for (idx, block_str) in test_blocks.iter().enumerate() {
1052            println!("decoding test block {}", idx + 1);
1053            let bytes = hex::decode(block_str).unwrap_or_else(|_| panic!("bad block file {idx}"));
1054
1055            let block: BlockWrapper = minicbor::decode(&bytes[..])
1056                .unwrap_or_else(|_| panic!("error decoding cbor for file {idx}"));
1057
1058            let bytes2 = to_vec(block)
1059                .unwrap_or_else(|_| panic!("error encoding block cbor for file {idx}"));
1060
1061            assert!(bytes.eq(&bytes2), "re-encoded bytes didn't match original");
1062        }
1063    }
1064
1065    #[test]
1066    fn header_isomorphic_decoding_encoding() {
1067        let test_headers = [
1068            // peculiar alonzo header used as origin for a vasil devnet
1069            include_str!("../../../test_data/alonzo26.header"),
1070        ];
1071
1072        for (idx, header_str) in test_headers.iter().enumerate() {
1073            println!("decoding test header {}", idx + 1);
1074            let bytes = hex::decode(header_str).unwrap_or_else(|_| panic!("bad header file {idx}"));
1075
1076            let header: Header = minicbor::decode(&bytes[..])
1077                .unwrap_or_else(|_| panic!("error decoding cbor for file {idx}"));
1078
1079            let bytes2 = to_vec(header)
1080                .unwrap_or_else(|_| panic!("error encoding header cbor for file {idx}"));
1081
1082            assert!(bytes.eq(&bytes2), "re-encoded bytes didn't match original");
1083        }
1084    }
1085
1086    #[test]
1087    fn plutus_data_isomorphic_decoding_encoding() {
1088        let datas = [
1089            // unit = Constr 0 []
1090            "d87980",
1091            // pltmap = Map [(I 1, unit), (I 2, pltlist)]
1092            "a201d87980029f000102ff",
1093            // pltlist = List [I 0, I 1, I 2]
1094            "9f000102ff",
1095            // Constr 5 [pltmap, Constr 5 [Map [(pltmap, toData True), (pltlist, pltmap), (List [], List [I 1])], unit, toData (0, 1)]]
1096            "d87e9fa201d87980029f000102ffd87e9fa3a201d87980029f000102ffd87a809f000102ffa201d87980029f000102ff809f01ffd87980d8799f0001ffffff",
1097            // Constr 5 [List [], List [I 1], Map [], Map [(I 1, unit), (I 2, Constr 2 [I 2])]]
1098            "d87e9f809f01ffa0a201d8798002d87b9f02ffff",
1099            // B (B.replicate 32 105)
1100            "58206969696969696969696969696969696969696969696969696969696969696969",
1101            // B (B.replicate 67 105)
1102            "5f58406969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696943696969ff",
1103            // B B.empty
1104            "40"
1105        ];
1106        for data_hex in datas {
1107            let data_bytes = hex::decode(data_hex).unwrap();
1108            let data = PlutusData::decode_fragment(&data_bytes).unwrap();
1109            assert_eq!(data.encode_fragment().unwrap(), data_bytes);
1110        }
1111    }
1112}