Skip to main content

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, Nullable},
10};
11use pallas_crypto::hash::{Hash, Hasher};
12
13pub use pallas_codec::codec_by_datatype;
14
15pub use crate::{
16    AddrKeyhash, AssetName, DatumHash, DnsName, Epoch, ExUnitPrices, ExUnits, GenesisDelegateHash,
17    Genesishash, IPv4, IPv6, Metadata, Metadatum, MetadatumLabel, NetworkId, Nonce, NonceVariant,
18    PlutusScript, PolicyId, PoolKeyhash, PoolMetadata, PoolMetadataHash, Port, PositiveInterval,
19    ProtocolVersion, RationalNumber, Relay, ScriptHash, StakeCredential, TransactionIndex,
20    TransactionInput, UnitInterval, VrfCert, VrfKeyhash, plutus_data::*,
21};
22
23use crate::BTreeMap;
24
25#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
26pub struct HeaderBody {
27    #[n(0)]
28    pub block_number: u64,
29
30    #[n(1)]
31    pub slot: u64,
32
33    #[n(2)]
34    pub prev_hash: Option<Hash<32>>,
35
36    #[n(3)]
37    pub issuer_vkey: Bytes,
38
39    #[n(4)]
40    pub vrf_vkey: Bytes,
41
42    #[n(5)]
43    pub vrf_result: VrfCert,
44
45    #[n(6)]
46    pub block_body_size: u64,
47
48    #[n(7)]
49    pub block_body_hash: Hash<32>,
50
51    #[n(8)]
52    pub operational_cert: OperationalCert,
53
54    #[n(9)]
55    pub protocol_version: ProtocolVersion,
56}
57
58#[derive(Serialize, Deserialize, Encode, Decode, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
59pub struct OperationalCert {
60    #[n(0)]
61    pub operational_cert_hot_vkey: Bytes,
62
63    #[n(1)]
64    pub operational_cert_sequence_number: u64,
65
66    #[n(2)]
67    pub operational_cert_kes_period: u64,
68
69    #[n(3)]
70    pub operational_cert_sigma: Bytes,
71}
72
73#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
74pub struct Header {
75    #[n(0)]
76    pub header_body: HeaderBody,
77
78    #[n(1)]
79    pub body_signature: Bytes,
80}
81
82#[deprecated(since = "1.0.0-alpha", note = "use `KeepRaw<'_, Header>` instead")]
83pub type MintedHeader<'a> = KeepRaw<'a, Header>;
84
85pub use crate::alonzo::Multiasset;
86
87pub use crate::alonzo::Mint;
88
89pub use crate::alonzo::Coin;
90
91pub use crate::alonzo::Value;
92
93pub use crate::alonzo::TransactionOutput as LegacyTransactionOutput;
94
95pub use crate::alonzo::InstantaneousRewardSource;
96
97pub use crate::alonzo::InstantaneousRewardTarget;
98
99pub use crate::alonzo::MoveInstantaneousReward;
100
101pub use crate::alonzo::RewardAccount;
102
103pub use crate::alonzo::Withdrawals;
104
105pub use crate::alonzo::RequiredSigners;
106
107pub use crate::alonzo::Certificate;
108
109#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
110#[cbor(index_only)]
111pub enum Language {
112    #[n(0)]
113    PlutusV1,
114
115    #[n(1)]
116    PlutusV2,
117}
118
119#[deprecated(since = "0.31.0", note = "use `CostModels` instead")]
120pub type CostMdls = CostModels;
121
122pub use crate::alonzo::CostModel;
123
124#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
125#[cbor(map)]
126pub struct CostModels {
127    #[n(0)]
128    pub plutus_v1: Option<CostModel>,
129
130    #[n(1)]
131    pub plutus_v2: Option<CostModel>,
132}
133
134#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
135#[cbor(map)]
136pub struct ProtocolParamUpdate {
137    #[n(0)]
138    pub minfee_a: Option<u32>,
139    #[n(1)]
140    pub minfee_b: Option<u32>,
141    #[n(2)]
142    pub max_block_body_size: Option<u32>,
143    #[n(3)]
144    pub max_transaction_size: Option<u32>,
145    #[n(4)]
146    pub max_block_header_size: Option<u32>,
147    #[n(5)]
148    pub key_deposit: Option<Coin>,
149    #[n(6)]
150    pub pool_deposit: Option<Coin>,
151    #[n(7)]
152    pub maximum_epoch: Option<Epoch>,
153    #[n(8)]
154    pub desired_number_of_stake_pools: Option<u32>,
155    #[n(9)]
156    pub pool_pledge_influence: Option<RationalNumber>,
157    #[n(10)]
158    pub expansion_rate: Option<UnitInterval>,
159    #[n(11)]
160    pub treasury_growth_rate: Option<UnitInterval>,
161
162    #[n(14)]
163    pub protocol_version: Option<ProtocolVersion>,
164    #[n(16)]
165    pub min_pool_cost: Option<Coin>,
166    #[n(17)]
167    pub ada_per_utxo_byte: Option<Coin>,
168    #[n(18)]
169    pub cost_models_for_script_languages: Option<CostModels>,
170    #[n(19)]
171    pub execution_costs: Option<ExUnitPrices>,
172    #[n(20)]
173    pub max_tx_ex_units: Option<ExUnits>,
174    #[n(21)]
175    pub max_block_ex_units: Option<ExUnits>,
176    #[n(22)]
177    pub max_value_size: Option<u32>,
178    #[n(23)]
179    pub collateral_percentage: Option<u32>,
180    #[n(24)]
181    pub max_collateral_inputs: Option<u32>,
182}
183
184#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
185pub struct Update {
186    #[n(0)]
187    pub proposed_protocol_parameter_updates: BTreeMap<Genesishash, ProtocolParamUpdate>,
188
189    #[n(1)]
190    pub epoch: Epoch,
191}
192
193#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Clone)]
194#[cbor(map)]
195pub struct TransactionBody<'b> {
196    #[n(0)]
197    pub inputs: Vec<TransactionInput>,
198
199    #[b(1)]
200    pub outputs: Vec<KeepRaw<'b, TransactionOutput<'b>>>,
201
202    #[n(2)]
203    pub fee: u64,
204
205    #[n(3)]
206    pub ttl: Option<u64>,
207
208    #[n(4)]
209    pub certificates: Option<Vec<Certificate>>,
210
211    #[n(5)]
212    pub withdrawals: Option<BTreeMap<RewardAccount, Coin>>,
213
214    #[n(6)]
215    pub update: Option<Update>,
216
217    #[n(7)]
218    pub auxiliary_data_hash: Option<Bytes>,
219
220    #[n(8)]
221    pub validity_interval_start: Option<u64>,
222
223    #[n(9)]
224    pub mint: Option<Multiasset<i64>>,
225
226    #[n(11)]
227    pub script_data_hash: Option<Hash<32>>,
228
229    #[n(13)]
230    pub collateral: Option<Vec<TransactionInput>>,
231
232    #[n(14)]
233    pub required_signers: Option<Vec<AddrKeyhash>>,
234
235    #[n(15)]
236    pub network_id: Option<NetworkId>,
237
238    #[n(16)]
239    pub collateral_return: Option<KeepRaw<'b, TransactionOutput<'b>>>,
240
241    #[n(17)]
242    pub total_collateral: Option<Coin>,
243
244    #[n(18)]
245    pub reference_inputs: Option<Vec<TransactionInput>>,
246}
247
248#[deprecated(since = "1.0.0-alpha", note = "use `TransactionBody` instead")]
249pub type MintedTransactionBody<'a> = TransactionBody<'a>;
250
251pub enum VrfDerivation {
252    Leader,
253    Nonce,
254}
255
256pub fn derive_tagged_vrf_output(
257    block_vrf_output_bytes: &[u8],
258    derivation: VrfDerivation,
259) -> Vec<u8> {
260    let mut tagged_vrf: Vec<u8> = match derivation {
261        VrfDerivation::Leader => vec![0x4C_u8], /* "L" */
262        VrfDerivation::Nonce => vec![0x4E_u8],  /* "N" */
263    };
264
265    tagged_vrf.extend(block_vrf_output_bytes);
266    Hasher::<256>::hash(&tagged_vrf).to_vec()
267}
268
269impl HeaderBody {
270    pub fn leader_vrf_output(&self) -> Vec<u8> {
271        derive_tagged_vrf_output(&self.vrf_result.0, VrfDerivation::Leader)
272    }
273
274    pub fn nonce_vrf_output(&self) -> Vec<u8> {
275        derive_tagged_vrf_output(&self.vrf_result.0, VrfDerivation::Nonce)
276    }
277}
278
279#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
280pub enum GenTransactionOutput<'b, T> {
281    Legacy(KeepRaw<'b, LegacyTransactionOutput>),
282    PostAlonzo(KeepRaw<'b, T>),
283}
284
285// FIXME: Repeated since macro does not handle type generics yet.
286codec_by_datatype! {
287    TransactionOutput<'b>,
288    Array | ArrayIndef => Legacy,
289    Map | MapIndef => PostAlonzo,
290    ()
291}
292
293pub type TransactionOutput<'b> = GenTransactionOutput<'b, PostAlonzoTransactionOutput<'b>>;
294
295#[deprecated(since = "1.0.0-alpha", note = "use `TransactionOutput` instead")]
296pub type MintedTransactionOutput<'b> = TransactionOutput<'b>;
297
298#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
299#[cbor(map)]
300pub struct GenPostAlonzoTransactionOutput<'b, V, S> {
301    #[n(0)]
302    pub address: Bytes,
303
304    #[n(1)]
305    pub value: V,
306
307    #[b(2)]
308    pub datum_option: Option<KeepRaw<'b, DatumOption<'b>>>,
309
310    #[n(3)]
311    pub script_ref: Option<CborWrap<S>>,
312}
313
314pub type PostAlonzoTransactionOutput<'b> = GenPostAlonzoTransactionOutput<'b, Value, ScriptRef<'b>>;
315
316#[deprecated(
317    since = "1.0.0-alpha",
318    note = "use `PostAlonzoTransactionOutput` instead"
319)]
320pub type MintedPostAlonzoTransactionOutput<'b> = PostAlonzoTransactionOutput<'b>;
321
322pub use crate::alonzo::VKeyWitness;
323
324pub use crate::alonzo::NativeScript;
325
326pub use crate::alonzo::RedeemerTag;
327
328pub use crate::alonzo::Redeemer;
329
330pub use crate::alonzo::BootstrapWitness;
331
332#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Clone)]
333#[cbor(map)]
334pub struct WitnessSet<'b> {
335    #[n(0)]
336    pub vkeywitness: Option<Vec<VKeyWitness>>,
337
338    #[n(1)]
339    pub native_script: Option<Vec<KeepRaw<'b, NativeScript>>>,
340
341    #[n(2)]
342    pub bootstrap_witness: Option<Vec<BootstrapWitness>>,
343
344    #[n(3)]
345    pub plutus_v1_script: Option<Vec<PlutusScript<1>>>,
346
347    #[b(4)]
348    pub plutus_data: Option<Vec<KeepRaw<'b, PlutusData>>>,
349
350    #[n(5)]
351    pub redeemer: Option<Vec<Redeemer>>,
352
353    #[n(6)]
354    pub plutus_v2_script: Option<Vec<PlutusScript<2>>>,
355}
356
357#[deprecated(since = "1.0.0-alpha", note = "use `WitnessSet` instead")]
358pub type MintedWitnessSet<'b> = WitnessSet<'b>;
359
360#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Clone)]
361#[cbor(map)]
362pub struct PostAlonzoAuxiliaryData {
363    #[n(0)]
364    pub metadata: Option<Metadata>,
365
366    #[n(1)]
367    pub native_scripts: Option<Vec<NativeScript>>,
368
369    #[n(2)]
370    pub plutus_v1_scripts: Option<Vec<PlutusScript<1>>>,
371
372    #[n(3)]
373    pub plutus_v2_scripts: Option<Vec<PlutusScript<2>>>,
374}
375
376// datum_option = [ 0, $hash32 // 1, data ]
377#[derive(Encode, Decode, Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
378#[cbor(flat)]
379pub enum DatumOption<'b> {
380    #[n(0)]
381    Hash(#[n(0)] DatumHash),
382    #[n(1)]
383    Data(#[b(0)] CborWrap<KeepRaw<'b, PlutusData>>),
384}
385
386#[deprecated(since = "1.0.0-alpha", note = "use `DatumOption` instead")]
387pub type MintedDatumOption<'b> = DatumOption<'b>;
388
389#[deprecated(since = "0.31.0", note = "use `PlutusScript<1>` instead")]
390pub type PlutusV1Script = PlutusScript<1>;
391
392#[deprecated(since = "0.31.0", note = "use `PlutusScript<2>` instead")]
393pub type PlutusV2Script = PlutusScript<2>;
394
395// script = [ 0, native_script // 1, plutus_v1_script // 2, plutus_v2_script ]
396#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
397#[cbor(flat)]
398pub enum ScriptRef<'b> {
399    #[n(0)]
400    NativeScript(#[b(0)] KeepRaw<'b, NativeScript>),
401    #[n(1)]
402    PlutusV1Script(#[n(0)] PlutusScript<1>),
403    #[n(2)]
404    PlutusV2Script(#[n(0)] PlutusScript<2>),
405}
406
407#[deprecated(since = "1.0.0-alpha", note = "use `ScriptRef` instead")]
408pub type MintedScriptRef<'b> = ScriptRef<'b>;
409
410pub use crate::alonzo::AuxiliaryData;
411
412/// A memory representation of an already minted block
413///
414/// This structure allows to retrieve the
415/// original CBOR bytes for each structure that might require hashing. In this
416/// way, we make sure that the resulting hash matches what exists on-chain.
417#[derive(Serialize, Encode, Decode, Debug, PartialEq, Clone)]
418pub struct Block<'b> {
419    #[n(0)]
420    pub header: KeepRaw<'b, Header>,
421
422    #[b(1)]
423    pub transaction_bodies: Vec<KeepRaw<'b, TransactionBody<'b>>>,
424
425    #[n(2)]
426    pub transaction_witness_sets: Vec<KeepRaw<'b, WitnessSet<'b>>>,
427
428    #[n(3)]
429    pub auxiliary_data_set: BTreeMap<TransactionIndex, KeepRaw<'b, AuxiliaryData>>,
430
431    #[n(4)]
432    pub invalid_transactions: Option<Vec<TransactionIndex>>,
433}
434
435#[deprecated(since = "1.0.0-alpha", note = "use `Block` instead")]
436pub type MintedBlock<'b> = Block<'b>;
437
438#[derive(Clone, Serialize, Deserialize, Encode, Decode, Debug)]
439pub struct Tx<'b> {
440    #[b(0)]
441    pub transaction_body: KeepRaw<'b, TransactionBody<'b>>,
442
443    #[n(1)]
444    pub transaction_witness_set: KeepRaw<'b, WitnessSet<'b>>,
445
446    #[n(2)]
447    pub success: bool,
448
449    #[n(3)]
450    pub auxiliary_data: Nullable<KeepRaw<'b, AuxiliaryData>>,
451}
452
453#[deprecated(since = "1.0.0-alpha", note = "use `Tx` instead")]
454pub type MintedTx<'b> = Tx<'b>;
455
456#[cfg(test)]
457mod tests {
458    use pallas_codec::minicbor;
459
460    use super::{Block, TransactionOutput};
461    use crate::Fragment;
462
463    type BlockWrapper<'b> = (u16, Block<'b>);
464
465    #[test]
466    fn block_isomorphic_decoding_encoding() {
467        let test_blocks = [
468            include_str!("../../../test_data/babbage1.block"),
469            include_str!("../../../test_data/babbage2.block"),
470            include_str!("../../../test_data/babbage3.block"),
471            // peculiar block with single plutus cost model
472            include_str!("../../../test_data/babbage4.block"),
473            // peculiar block with i32 overlfow
474            include_str!("../../../test_data/babbage5.block"),
475            // peculiar block with map undef in plutus data
476            include_str!("../../../test_data/babbage6.block"),
477            // block with generic int in cbor
478            include_str!("../../../test_data/babbage7.block"),
479            // block with indef bytes for plutus data bignum
480            include_str!("../../../test_data/babbage8.block"),
481            // // block with inline datum that fails hashes
482            // include_str!("../../../test_data/babbage9.block"),
483            // block with pool margin numerator greater than i64::MAX
484            include_str!("../../../test_data/babbage10.block"),
485        ];
486
487        for (idx, block_str) in test_blocks.iter().enumerate() {
488            println!("decoding test block {}", idx + 1);
489            let bytes = hex::decode(block_str).unwrap_or_else(|_| panic!("bad block file {idx}"));
490
491            let block: BlockWrapper = minicbor::decode(&bytes[..])
492                .unwrap_or_else(|e| panic!("error decoding cbor for file {idx}: {e:?}"));
493
494            let bytes2 = minicbor::to_vec(block)
495                .unwrap_or_else(|e| panic!("error encoding block cbor for file {idx}: {e:?}"));
496
497            assert!(bytes.eq(&bytes2), "re-encoded bytes didn't match original");
498        }
499    }
500
501    #[test]
502    fn fragments_decoding() {
503        // peculiar array of outputs used in an hydra transaction
504        let hex = include_str!("../../../test_data/babbage1.fr");
505        let bytes = hex::decode(hex).unwrap();
506        let outputs = Vec::<TransactionOutput>::decode_fragment(&bytes).unwrap();
507
508        dbg!(outputs);
509
510        // add any loose fragment tests here
511    }
512}