Skip to content

Commit 4bf2cf7

Browse files
committed
Implement ToPlutusData and FromPlutusData for primitive types and collections
1 parent fc26782 commit 4bf2cf7

File tree

3 files changed

+295
-5
lines changed

3 files changed

+295
-5
lines changed

runtimes/rust/plutus-ledger-types/Cargo.lock

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

runtimes/rust/plutus-ledger-types/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ num-bigint = "0.4.4"
1515
serde = { version = "1.0.189", features = ["derive"], optional = true }
1616
true = { version = "0.1.0", optional = true }
1717
data-encoding = { version = "2.4.0", optional = true }
18+
thiserror = "1.0.50"
1819

1920
[features]
2021
serde = ["dep:serde", "num-bigint/serde"]

runtimes/rust/plutus-ledger-types/src/plutus_data.rs

Lines changed: 289 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use num_bigint::BigInt;
66
use serde::{Deserialize, Serialize};
77

88
/// Data representation of on-chain data such as Datums and Redeemers
9-
#[derive(Debug, PartialEq, Eq)]
9+
#[derive(Debug, PartialEq, Eq, Clone)]
1010
#[cfg_attr(feature = "lbf", derive(Json))]
1111
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1212
pub enum PlutusData {
@@ -16,3 +16,291 @@ pub enum PlutusData {
1616
Integer(BigInt),
1717
Bytes(Vec<u8>),
1818
}
19+
20+
pub trait ToPlutusData {
21+
fn to_plutus_data(&self) -> PlutusData;
22+
}
23+
24+
pub trait FromPlutusData {
25+
fn from_plutus_data(plutus_data: PlutusData) -> Result<Self, PlutusDataError>
26+
where
27+
Self: Sized;
28+
}
29+
30+
#[derive(Debug, Clone)]
31+
pub enum PlutusType {
32+
Constr,
33+
Map,
34+
List,
35+
Integer,
36+
Bytes,
37+
}
38+
39+
impl From<&PlutusData> for PlutusType {
40+
fn from(plutus_data: &PlutusData) -> Self {
41+
match plutus_data {
42+
PlutusData::Constr(_, _) => PlutusType::Constr,
43+
PlutusData::Map(_) => PlutusType::Map,
44+
PlutusData::List(_) => PlutusType::List,
45+
PlutusData::Integer(_) => PlutusType::Integer,
46+
PlutusData::Bytes(_) => PlutusType::Bytes,
47+
}
48+
}
49+
}
50+
51+
#[derive(thiserror::Error, Debug)]
52+
pub enum PlutusDataError {
53+
#[error("Expected a PlutusData type {wanted:?}, but got {got:?}")]
54+
UnexpectedPlutusType { got: PlutusType, wanted: PlutusType },
55+
#[error("Expected a PlutusData type as {wanted:?}, but got {got:?}")]
56+
UnexpectedPlutusInvariant { got: String, wanted: String },
57+
#[error("Expected a Plutus List with {wanted:?} elements, but got {got:?} elements")]
58+
UnexpectedListLength { got: usize, wanted: usize },
59+
#[error("Some internal error happened: {0}")]
60+
InternalError(String),
61+
}
62+
63+
impl ToPlutusData for BigInt {
64+
fn to_plutus_data(&self) -> PlutusData {
65+
PlutusData::Integer(self.clone())
66+
}
67+
}
68+
69+
impl FromPlutusData for BigInt {
70+
fn from_plutus_data(plutus_data: PlutusData) -> Result<Self, PlutusDataError> {
71+
match plutus_data {
72+
PlutusData::Integer(int) => Ok(int),
73+
_ => Err(PlutusDataError::UnexpectedPlutusType {
74+
wanted: PlutusType::Integer,
75+
got: PlutusType::from(&plutus_data),
76+
}),
77+
}
78+
}
79+
}
80+
81+
impl ToPlutusData for bool {
82+
fn to_plutus_data(&self) -> PlutusData {
83+
if *self {
84+
PlutusData::Constr(BigInt::from(1), Vec::with_capacity(0))
85+
} else {
86+
PlutusData::Constr(BigInt::from(0), Vec::with_capacity(0))
87+
}
88+
}
89+
}
90+
91+
impl FromPlutusData for bool {
92+
fn from_plutus_data(plutus_data: PlutusData) -> Result<Self, PlutusDataError> {
93+
match plutus_data {
94+
PlutusData::Constr(flag, fields) => match u32::try_from(&flag) {
95+
Ok(0) => {
96+
verify_fields_len(&fields, 0)?;
97+
Ok(false)
98+
}
99+
Ok(1) => {
100+
verify_fields_len(&fields, 0)?;
101+
Ok(true)
102+
}
103+
_ => Err(PlutusDataError::UnexpectedPlutusInvariant {
104+
wanted: "Constr field between 0 and 1".to_owned(),
105+
got: flag.to_string(),
106+
}),
107+
},
108+
109+
_ => Err(PlutusDataError::UnexpectedPlutusType {
110+
wanted: PlutusType::Constr,
111+
got: PlutusType::from(&plutus_data),
112+
}),
113+
}
114+
}
115+
}
116+
117+
impl ToPlutusData for char {
118+
fn to_plutus_data(&self) -> PlutusData {
119+
String::from(*self).to_plutus_data()
120+
}
121+
}
122+
123+
impl FromPlutusData for char {
124+
fn from_plutus_data(plutus_data: PlutusData) -> Result<Self, PlutusDataError> {
125+
String::from_plutus_data(plutus_data).and_then(|str| {
126+
let mut chars = str.chars();
127+
let ch = chars.next();
128+
let rest = chars.next();
129+
match (ch, rest) {
130+
(Some(ch), None) => Ok(ch),
131+
_ => Err(PlutusDataError::UnexpectedPlutusInvariant {
132+
got: "string".to_owned(),
133+
wanted: "char".to_owned(),
134+
}),
135+
}
136+
})
137+
}
138+
}
139+
140+
impl ToPlutusData for Vec<u8> {
141+
fn to_plutus_data(&self) -> PlutusData {
142+
PlutusData::Bytes(self.clone())
143+
}
144+
}
145+
146+
impl FromPlutusData for Vec<u8> {
147+
fn from_plutus_data(plutus_data: PlutusData) -> Result<Self, PlutusDataError> {
148+
match plutus_data {
149+
PlutusData::Bytes(bytes) => Ok(bytes),
150+
_ => Err(PlutusDataError::UnexpectedPlutusType {
151+
wanted: PlutusType::Bytes,
152+
got: PlutusType::from(&plutus_data),
153+
}),
154+
}
155+
}
156+
}
157+
158+
impl ToPlutusData for String {
159+
fn to_plutus_data(&self) -> PlutusData {
160+
PlutusData::Bytes(self.as_bytes().into())
161+
}
162+
}
163+
164+
impl FromPlutusData for String {
165+
fn from_plutus_data(plutus_data: PlutusData) -> Result<Self, PlutusDataError> {
166+
match plutus_data {
167+
PlutusData::Bytes(bytes) => String::from_utf8(bytes).map_err(|err| {
168+
PlutusDataError::InternalError(format!(
169+
"Couldn't convert Plutus bytes to String: {:?}",
170+
err
171+
))
172+
}),
173+
_ => Err(PlutusDataError::UnexpectedPlutusType {
174+
wanted: PlutusType::Integer,
175+
got: PlutusType::from(&plutus_data),
176+
}),
177+
}
178+
}
179+
}
180+
181+
impl<T> ToPlutusData for Option<T>
182+
where
183+
T: ToPlutusData,
184+
{
185+
fn to_plutus_data(&self) -> PlutusData {
186+
match self {
187+
Some(val) => PlutusData::Constr(BigInt::from(0), vec![val.to_plutus_data()]),
188+
None => PlutusData::Constr(BigInt::from(1), Vec::with_capacity(0)),
189+
}
190+
}
191+
}
192+
193+
impl<T> FromPlutusData for Option<T>
194+
where
195+
T: FromPlutusData,
196+
{
197+
fn from_plutus_data(plutus_data: PlutusData) -> Result<Self, PlutusDataError> {
198+
match plutus_data {
199+
PlutusData::Constr(flag, fields) => match u32::try_from(&flag) {
200+
Ok(0) => {
201+
verify_fields_len(&fields, 1)?;
202+
Ok(Some(T::from_plutus_data(fields[0].clone())?))
203+
}
204+
Ok(1) => {
205+
verify_fields_len(&fields, 0)?;
206+
Ok(None)
207+
}
208+
_ => Err(PlutusDataError::UnexpectedPlutusInvariant {
209+
wanted: "Constr field between 0 and 1".to_owned(),
210+
got: flag.to_string(),
211+
}),
212+
},
213+
214+
_ => Err(PlutusDataError::UnexpectedPlutusType {
215+
wanted: PlutusType::Constr,
216+
got: PlutusType::from(&plutus_data),
217+
}),
218+
}
219+
}
220+
}
221+
222+
impl<T, E> ToPlutusData for Result<T, E>
223+
where
224+
T: ToPlutusData,
225+
E: ToPlutusData,
226+
{
227+
fn to_plutus_data(&self) -> PlutusData {
228+
match self {
229+
Err(val) => PlutusData::Constr(BigInt::from(0), vec![val.to_plutus_data()]),
230+
Ok(val) => PlutusData::Constr(BigInt::from(1), vec![val.to_plutus_data()]),
231+
}
232+
}
233+
}
234+
235+
impl<T, E> FromPlutusData for Result<T, E>
236+
where
237+
T: FromPlutusData,
238+
E: FromPlutusData,
239+
{
240+
fn from_plutus_data(plutus_data: PlutusData) -> Result<Self, PlutusDataError> {
241+
match plutus_data {
242+
PlutusData::Constr(flag, fields) => match u32::try_from(&flag) {
243+
Ok(0) => {
244+
verify_fields_len(&fields, 1)?;
245+
Ok(Err(E::from_plutus_data(fields[0].clone())?))
246+
}
247+
Ok(1) => {
248+
verify_fields_len(&fields, 1)?;
249+
Ok(Ok(T::from_plutus_data(fields[0].clone())?))
250+
}
251+
_ => Err(PlutusDataError::UnexpectedPlutusInvariant {
252+
wanted: "Constr field between 0 and 1".to_owned(),
253+
got: flag.to_string(),
254+
}),
255+
},
256+
257+
_ => Err(PlutusDataError::UnexpectedPlutusType {
258+
wanted: PlutusType::Constr,
259+
got: PlutusType::from(&plutus_data),
260+
}),
261+
}
262+
}
263+
}
264+
265+
impl<T> ToPlutusData for Vec<T>
266+
where
267+
T: ToPlutusData,
268+
{
269+
fn to_plutus_data(&self) -> PlutusData {
270+
let values = self
271+
.iter()
272+
.map(|val| val.to_plutus_data())
273+
.collect::<Vec<PlutusData>>();
274+
275+
PlutusData::List(values)
276+
}
277+
}
278+
279+
impl<T> FromPlutusData for Vec<T>
280+
where
281+
T: FromPlutusData,
282+
{
283+
fn from_plutus_data(plutus_data: PlutusData) -> Result<Self, PlutusDataError> {
284+
match plutus_data {
285+
PlutusData::List(vec) => vec
286+
.iter()
287+
.map(|val| T::from_plutus_data(val.clone()))
288+
.collect::<Result<Vec<T>, PlutusDataError>>(),
289+
_ => Err(PlutusDataError::UnexpectedPlutusType {
290+
wanted: PlutusType::List,
291+
got: PlutusType::from(&plutus_data),
292+
}),
293+
}
294+
}
295+
}
296+
297+
fn verify_fields_len(fields: &Vec<PlutusData>, expected: usize) -> Result<(), PlutusDataError> {
298+
if fields.len() != expected {
299+
Err(PlutusDataError::UnexpectedPlutusInvariant {
300+
wanted: format!("Constr with {} fields", expected),
301+
got: format!("{:?}", fields),
302+
})
303+
} else {
304+
Ok(())
305+
}
306+
}

0 commit comments

Comments
 (0)