Skip to main content

pallas_primitives/
lib.rs

1//! Era-aware Cardano ledger types with their CBOR codecs.
2//!
3//! This is the data layer that the rest of the Pallas ledger crates sit on:
4//! [`pallas-traverse`] gives you a multi-era read API over these types,
5//! [`pallas-validate`] applies ledger rules to them, and [`pallas-txbuilder`]
6//! builds new ones.
7//!
8//! If you need raw, era-specific access to a `Tx`, `Block`, or `PlutusData`,
9//! you want this crate. If you'd rather work over many eras through one
10//! interface, reach for [`pallas-traverse`].
11//!
12//! [`pallas-traverse`]: https://crates.io/crates/pallas-traverse
13//! [`pallas-validate`]: https://crates.io/crates/pallas-validate
14//! [`pallas-txbuilder`]: https://crates.io/crates/pallas-txbuilder
15//!
16//! # Usage
17//!
18//! ```no_run
19//! use pallas_codec::minicbor;
20//! use pallas_primitives::conway;
21//!
22//! # let cbor_bytes: Vec<u8> = vec![];
23//! let tx: conway::Tx = minicbor::decode(&cbor_bytes)?;
24//!
25//! for input in tx.transaction_body.inputs.iter() {
26//!     println!("{:?}#{}", input.transaction_id, input.index);
27//! }
28//! # Ok::<_, Box<dyn std::error::Error>>(())
29//! ```
30//!
31//! # Overview
32//!
33//! - [`byron`], [`alonzo`], [`babbage`], [`conway`] — one module per era,
34//!   each exposing the era's `Block`, `Tx`, `TransactionInput`,
35//!   `TransactionOutput`, `Value`, `Certificate`, `Metadata`, witness sets,
36//!   and so on.
37//! - `plutus_data` — re-exported [`PlutusData`], [`BigInt`], and helpers
38//!   shared across eras.
39//! - `framework` — common type aliases and codec primitives
40//!   ([`AddrKeyhash`], [`Coin`], [`PolicyId`], [`RationalNumber`],
41//!   [`StakeCredential`], [`TransactionInput`], [`ExUnits`],
42//!   [`PlutusScript`], …).
43//! - Re-exports from [`pallas-codec`] ([`Bytes`], [`KeepRaw`],
44//!   [`KeyValuePairs`], [`NonEmptySet`], [`Set`], [`Nullable`], …) and
45//!   [`pallas-crypto`] ([`struct@Hash`]).
46//!
47//! [`pallas-codec`]: https://crates.io/crates/pallas-codec
48//! [`pallas-crypto`]: https://crates.io/crates/pallas-crypto
49//!
50//! # Feature flags
51//!
52//! - `relaxed` — relax some validation invariants applied during decoding;
53//!   useful for round-tripping non-canonical historical artifacts.
54//!
55//! # Usage as part of `pallas`
56//!
57//! When depending on the umbrella [`pallas`] crate, this crate is re-exported
58//! as `pallas::ledger::primitives`.
59//!
60//! [`pallas`]: https://crates.io/crates/pallas
61
62mod framework;
63mod plutus_data;
64
65/// Ledger primitives for the Alonzo era (smart contracts).
66pub mod alonzo;
67/// Ledger primitives for the Babbage era (reference inputs / inline datums).
68pub mod babbage;
69/// Ledger primitives for the Byron era.
70pub mod byron;
71/// Ledger primitives for the Conway era (governance).
72pub mod conway;
73pub use plutus_data::*;
74
75pub use framework::*;
76
77pub use pallas_codec::codec_by_datatype;
78
79pub use pallas_codec::utils::{
80    Bytes, Int, KeepRaw, KeyValuePairs, MaybeIndefArray, NonEmptySet, NonZeroInt, Nullable,
81    PositiveCoin, Set,
82};
83pub use pallas_crypto::hash::Hash;
84
85use pallas_codec::minicbor::{self, Decode, Encode, data::Tag};
86use serde::{Deserialize, Serialize};
87
88use std::collections::BTreeMap;
89
90// ----- Common type definitions
91
92/// Hash of a Cardano address verification key (Blake2b-224).
93pub type AddrKeyhash = Hash<28>;
94
95/// Token name within a multi-asset bundle (raw bytes, up to 32 long).
96pub type AssetName = Bytes;
97
98/// Quantity in lovelace.
99pub type Coin = u64;
100
101/// Plutus cost model: ordered list of per-primitive cost coefficients.
102pub type CostModel = Vec<i64>;
103
104/// Hash of a Plutus datum (Blake2b-256).
105pub type DatumHash = Hash<32>;
106
107/// DNS name (A or SRV record) used in relay declarations.
108pub type DnsName = String;
109
110/// Epoch number on the Cardano chain.
111pub type Epoch = u64;
112
113/// Plutus script execution budget: memory and step units.
114#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, Copy)]
115pub struct ExUnits {
116    /// Memory units consumed.
117    #[n(0)]
118    pub mem: u64,
119    /// CPU step units consumed.
120    #[n(1)]
121    pub steps: u64,
122}
123
124/// Per-unit prices used to convert [`ExUnits`] into fee lovelace.
125#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
126pub struct ExUnitPrices {
127    /// Price per memory unit.
128    #[n(0)]
129    pub mem_price: PositiveInterval,
130
131    /// Price per CPU step.
132    #[n(1)]
133    pub step_price: PositiveInterval,
134}
135
136/// Hash identifying a genesis configuration.
137pub type Genesishash = Bytes;
138
139/// Hash of a genesis delegate certificate.
140pub type GenesisDelegateHash = Bytes;
141
142/// IPv4 address bytes (4 bytes, big-endian).
143pub type IPv4 = Bytes;
144
145/// IPv6 address bytes (16 bytes, big-endian).
146pub type IPv6 = Bytes;
147
148/// Transaction metadata map, keyed by label.
149pub type Metadata = BTreeMap<MetadatumLabel, Metadatum>;
150
151/// Single metadata value of any supported CBOR shape.
152#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
153pub enum Metadatum {
154    /// Integer (signed or unsigned, up to 64 bits).
155    Int(Int),
156    /// Raw byte string.
157    Bytes(Bytes),
158    /// UTF-8 text string.
159    Text(String),
160    /// Ordered list of metadata values.
161    Array(Vec<Metadatum>),
162    /// Map of metadata values keyed by metadata values.
163    Map(KeyValuePairs<Metadatum, Metadatum>),
164}
165
166codec_by_datatype! {
167    Metadatum,
168    U8 | U16 | U32 | U64 | I8 | I16 | I32 | I64 | Int => Int,
169    Bytes => Bytes,
170    String | StringIndef => Text,
171    Array | ArrayIndef => Array,
172    Map | MapIndef => Map,
173    ()
174}
175
176/// Top-level metadata label (CIP-10 / CIP-25 / etc.).
177pub type MetadatumLabel = u64;
178
179/// The network this artifact targets (encoded as a small CBOR enum).
180#[derive(
181    Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy,
182)]
183#[cbor(index_only)]
184pub enum NetworkId {
185    /// The Cardano testnet.
186    #[n(0)]
187    Testnet,
188    /// The Cardano mainnet.
189    #[n(1)]
190    Mainnet,
191}
192
193impl From<NetworkId> for u8 {
194    fn from(network_id: NetworkId) -> u8 {
195        match network_id {
196            NetworkId::Testnet => 0,
197            NetworkId::Mainnet => 1,
198        }
199    }
200}
201
202impl TryFrom<u8> for NetworkId {
203    type Error = ();
204
205    fn try_from(i: u8) -> Result<Self, Self::Error> {
206        match i {
207            0 => Ok(Self::Testnet),
208            1 => Ok(Self::Mainnet),
209            _ => Err(()),
210        }
211    }
212}
213
214/// Praos nonce used as input to the leader-selection schedule.
215#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
216pub struct Nonce {
217    /// Discriminator selecting how the nonce was produced.
218    #[n(0)]
219    pub variant: NonceVariant,
220
221    /// Hash payload, present when `variant` is [`NonceVariant::Nonce`].
222    #[n(1)]
223    pub hash: Option<Hash<32>>,
224}
225
226/// Discriminator for [`Nonce`]: neutral (genesis) or hashed.
227#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
228#[cbor(index_only)]
229pub enum NonceVariant {
230    /// Initial neutral nonce, with no hash payload.
231    #[n(0)]
232    NeutralNonce,
233
234    /// A hashed nonce; the payload lives in [`Nonce::hash`].
235    #[n(1)]
236    Nonce,
237}
238
239/// Raw bytes of a Plutus script of language version `VERSION` (1, 2, or 3).
240#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
241#[cbor(transparent)]
242pub struct PlutusScript<const VERSION: usize>(pub Bytes);
243
244impl<const VERSION: usize> AsRef<[u8]> for PlutusScript<VERSION> {
245    fn as_ref(&self) -> &[u8] {
246        self.0.as_slice()
247    }
248}
249
250/// Hash of a minting policy (Blake2b-224).
251pub type PolicyId = Hash<28>;
252
253/// Hash of a stake pool's cold key (Blake2b-224).
254pub type PoolKeyhash = Hash<28>;
255
256/// Stake pool metadata reference: URL plus the hash of the pointed-to JSON.
257#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
258pub struct PoolMetadata {
259    /// URL serving the pool metadata JSON.
260    #[n(0)]
261    pub url: String,
262
263    /// Hash of the JSON document served at `url`.
264    #[n(1)]
265    pub hash: PoolMetadataHash,
266}
267
268/// Hash of stake pool metadata (Blake2b-256).
269pub type PoolMetadataHash = Bytes;
270
271/// TCP/UDP port number.
272pub type Port = u32;
273
274/// Rational number guaranteed to be strictly positive.
275pub type PositiveInterval = RationalNumber;
276
277/// Protocol version: `(major, minor)`.
278pub type ProtocolVersion = (u64, u64);
279
280/// On-chain rational number, encoded as a CBOR tag-30 array of `[num, den]`.
281#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
282pub struct RationalNumber {
283    /// Numerator of the rational.
284    pub numerator: u64,
285    /// Denominator of the rational.
286    pub denominator: u64,
287}
288
289impl<'b, C> minicbor::decode::Decode<'b, C> for RationalNumber {
290    fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
291        // TODO: Enforce tag == 30 & array of size 2
292        d.tag()?;
293        d.array()?;
294        Ok(RationalNumber {
295            numerator: d.decode_with(ctx)?,
296            denominator: d.decode_with(ctx)?,
297        })
298    }
299}
300
301impl<C> minicbor::encode::Encode<C> for RationalNumber {
302    fn encode<W: minicbor::encode::Write>(
303        &self,
304        e: &mut minicbor::Encoder<W>,
305        ctx: &mut C,
306    ) -> Result<(), minicbor::encode::Error<W::Error>> {
307        e.tag(Tag::new(30))?;
308        e.array(2)?;
309        e.encode_with(self.numerator, ctx)?;
310        e.encode_with(self.denominator, ctx)?;
311        Ok(())
312    }
313}
314
315/// Network endpoint declared by a stake pool's relay.
316#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
317pub enum Relay {
318    /// IP-based relay with optional port and either or both IPv4/IPv6.
319    SingleHostAddr(Option<Port>, Option<IPv4>, Option<IPv6>),
320    /// DNS A-record relay with optional port and a hostname.
321    SingleHostName(Option<Port>, DnsName),
322    /// DNS SRV-record relay (port and host both come from the SRV record).
323    MultiHostName(DnsName),
324}
325
326impl<'b, C> minicbor::decode::Decode<'b, C> for Relay {
327    fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
328        d.array()?;
329        let variant = d.u16()?;
330
331        match variant {
332            0 => Ok(Relay::SingleHostAddr(
333                d.decode_with(ctx)?,
334                d.decode_with(ctx)?,
335                d.decode_with(ctx)?,
336            )),
337            1 => Ok(Relay::SingleHostName(
338                d.decode_with(ctx)?,
339                d.decode_with(ctx)?,
340            )),
341            2 => Ok(Relay::MultiHostName(d.decode_with(ctx)?)),
342            _ => Err(minicbor::decode::Error::message(
343                "invalid variant id for Relay",
344            )),
345        }
346    }
347}
348
349impl<C> minicbor::encode::Encode<C> for Relay {
350    fn encode<W: minicbor::encode::Write>(
351        &self,
352        e: &mut minicbor::Encoder<W>,
353        ctx: &mut C,
354    ) -> Result<(), minicbor::encode::Error<W::Error>> {
355        match self {
356            Relay::SingleHostAddr(a, b, c) => {
357                e.array(4)?;
358                e.encode_with(0, ctx)?;
359                e.encode_with(a, ctx)?;
360                e.encode_with(b, ctx)?;
361                e.encode_with(c, ctx)?;
362
363                Ok(())
364            }
365            Relay::SingleHostName(a, b) => {
366                e.array(3)?;
367                e.encode_with(1, ctx)?;
368                e.encode_with(a, ctx)?;
369                e.encode_with(b, ctx)?;
370
371                Ok(())
372            }
373            Relay::MultiHostName(a) => {
374                e.array(2)?;
375                e.encode_with(2, ctx)?;
376                e.encode_with(a, ctx)?;
377
378                Ok(())
379            }
380        }
381    }
382}
383
384/// Reward-account bytes (network header byte plus stake-credential hash).
385pub type RewardAccount = Bytes;
386
387/// Hash of a script (Blake2b-224).
388pub type ScriptHash = Hash<28>;
389
390#[derive(
391    Serialize, Deserialize, Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Hash, Encode, Decode,
392)]
393// !! NOTE / IMPORTANT !!
394// It is tempting to swap the order of the two constructors so that AddrKeyHash
395// comes first. This indeed nicely maps the binary representation which
396// associates 0 to AddrKeyHash and 1 to ScriptHash.
397//
398// However, for historical reasons, the ScriptHash variant comes first in the
399// Haskell reference codebase. From this ordering is derived the `PartialOrd`
400// and `Ord` instances; which impacts how Maps/Dictionnaries indexed by
401// StakeCredential will be ordered. So, it is crucial to preserve this quirks to
402// avoid hard to troubleshoot issues down the line.
403#[cbor(flat)]
404/// On-chain credential controlling a stake address: a script or a key hash.
405pub enum StakeCredential {
406    /// Stake credential backed by a script hash.
407    #[n(1)]
408    ScriptHash(#[n(0)] ScriptHash),
409    /// Stake credential backed by a verification-key hash.
410    #[n(0)]
411    AddrKeyhash(#[n(0)] AddrKeyhash),
412}
413
414/// Index of a transaction within its containing block.
415pub type TransactionIndex = u32;
416
417/// Reference to a transaction output: `(tx_hash, output_index)`.
418#[derive(
419    Serialize,
420    Deserialize,
421    Encode,
422    Decode,
423    Debug,
424    PartialEq,
425    Eq,
426    PartialOrd,
427    Ord,
428    Clone,
429    std::hash::Hash,
430)]
431pub struct TransactionInput {
432    /// Hash of the transaction that produced the output.
433    #[n(0)]
434    pub transaction_id: Hash<32>,
435
436    /// Index of the output within that transaction.
437    #[n(1)]
438    pub index: u64,
439}
440
441/// Rational number constrained to the closed interval [0, 1].
442pub type UnitInterval = RationalNumber;
443
444/// VRF certificate: the output bytes followed by the proof bytes.
445#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
446pub struct VrfCert(
447    /// VRF output bytes.
448    #[n(0)]
449    pub Bytes,
450    /// VRF proof bytes.
451    #[n(1)]
452    pub Bytes,
453);
454
455/// Hash of a VRF verification key (Blake2b-256).
456pub type VrfKeyhash = Hash<32>;