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>;