plutus/
lib.rs

1//! Minimal Plutus data types and encoding implementation
2//!
3//! This crate implements the `Data` type used by [Untyped Plutus Core], a low-level language
4//! used by Cardano smart contracts, along with traits for encoding Rust types as Plutus data
5//! and Plutus data CBOR bytes.
6//!
7//! This crate is developed as part of the [Partner Chains toolkit] for use in [no_std] contexts
8//! where existing alternatives ([uplc], [cardano-serialization-lib]) can not be used, and as
9//! such is not intended to be feature-complete for general use.
10//!
11//! [Untyped Plutus Core]: https://plutonomicon.github.io/plutonomicon/uplc
12//! [Partner Chains toolkit]: https://github.com/input-output-hk/partner-chains
13//! [no_std]: https://doc.rust-lang.org/reference/names/preludes.html?highlight=no_std#r-names.preludes.extern.no_std
14//! [uplc]: https://github.com/aiken-lang/aiken/tree/main/crates/uplc
15//! [cardano-serialization-lib]: https://github.com/Emurgo/cardano-serialization-lib
16#![no_std]
17#![deny(missing_docs)]
18
19mod cbor;
20
21extern crate alloc;
22
23use crate::Datum::*;
24#[doc(hidden)]
25pub use alloc::{vec, vec::Vec};
26use core::fmt::{Debug, Formatter};
27use num_bigint::BigInt;
28
29/// Plutus datum type
30#[derive(Clone, PartialEq)]
31pub enum Datum {
32	/// Integer value
33	IntegerDatum(BigInt),
34	/// Byte string value
35	ByteStringDatum(Vec<u8>),
36	/// Constructor value
37	///
38	/// A Plutus constructor datum consists of an ordered list of Plutus field values together
39	/// with numeric constructor ID that marks the constructor variant used. These can only be
40	/// interpreted against an external schema that specifies expected content of `fields` and
41	/// their interpretation.
42	ConstructorDatum {
43		/// Constructor variant number
44		constructor: u64,
45		/// List of field values
46		fields: Vec<Datum>,
47	},
48	/// List of values
49	ListDatum(Vec<Datum>),
50	/// Key-value mapping
51	MapDatum(Vec<MapDatumEntry>),
52}
53
54/// Key-value pair stored in [Datum::MapDatum]
55#[derive(Clone, Debug, PartialEq)]
56pub struct MapDatumEntry {
57	/// Key
58	key: Datum,
59	/// Value
60	value: Datum,
61}
62
63/// Trait for types that can be encoded as a Plutus [Datum].
64pub trait ToDatum {
65	/// Encodes `self` to [Datum].
66	fn to_datum(&self) -> Datum;
67}
68
69impl ToDatum for Vec<u8> {
70	fn to_datum(&self) -> Datum {
71		ByteStringDatum(self.clone())
72	}
73}
74
75impl ToDatum for [u8] {
76	fn to_datum(&self) -> Datum {
77		ByteStringDatum(Vec::from(self))
78	}
79}
80
81impl<T: ToDatum> ToDatum for Option<T> {
82	fn to_datum(&self) -> Datum {
83		match self {
84			Some(value) => ConstructorDatum { constructor: 0, fields: vec![value.to_datum()] },
85			None => ConstructorDatum { constructor: 1, fields: vec![] },
86		}
87	}
88}
89
90impl<T> ToDatum for [T]
91where
92	T: ToDatum,
93{
94	fn to_datum(&self) -> Datum {
95		ListDatum(self.iter().map(ToDatum::to_datum).collect())
96	}
97}
98
99impl<T> ToDatum for Vec<T>
100where
101	T: ToDatum,
102{
103	fn to_datum(&self) -> Datum {
104		ListDatum(self.iter().map(ToDatum::to_datum).collect())
105	}
106}
107
108impl ToDatum for Datum {
109	fn to_datum(&self) -> Datum {
110		self.clone()
111	}
112}
113
114impl Datum {
115	fn integer<T>(value: T) -> Datum
116	where
117		BigInt: From<T>,
118	{
119		IntegerDatum(BigInt::from(value))
120	}
121
122	/// Returns byte content if `self` is a [Datum::ByteStringDatum] and [None] otherwise.
123	pub fn as_bytestring(&self) -> Option<&Vec<u8>> {
124		match self {
125			ByteStringDatum(bytes) => Some(bytes),
126			_ => None,
127		}
128	}
129}
130
131macro_rules! impl_int_datum_from_uint {
132	($T:ty) => {
133		impl ToDatum for $T {
134			fn to_datum(&self) -> Datum {
135				Datum::integer(*self)
136			}
137		}
138	};
139}
140
141impl_int_datum_from_uint!(u16);
142impl_int_datum_from_uint!(u32);
143impl_int_datum_from_uint!(u64);
144impl_int_datum_from_uint!(u128);
145
146impl Debug for Datum {
147	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
148		match self {
149			IntegerDatum(i) => write!(f, "I {}", i),
150			ByteStringDatum(bs) => write!(f, "B {}", hex::encode(bs)),
151			ConstructorDatum { constructor, fields } => write!(f, "C {} {:?}", constructor, fields),
152			ListDatum(ds) => write!(f, "L {:?}", ds),
153			MapDatum(map) => {
154				write!(f, "M {:?}", map)
155			},
156		}
157	}
158}
159
160/// Encodes a message as a Plutus [Datum] and returns [CBOR] encoding of this datum.
161///
162/// tip: this function accepts `Option<T>` for any `T: Datum`
163///      so it's better to call it with explicit type to avoid hard to
164///      find bugs when types change
165///
166/// [CBOR]: https://cbor.io
167pub fn to_datum_cbor_bytes<T: ToDatum>(msg: T) -> Vec<u8> {
168	minicbor::to_vec(msg.to_datum()).expect("Infallible error type never fails")
169}