plutus_data/
lib.rs

1#![feature(box_into_inner)]
2
3use std::collections::HashMap;
4use std::collections::BTreeMap;
5
6use hex::ToHex;
7use pallas_primitives::Fragment;
8pub use pallas_primitives::babbage::PlutusData;
9pub use plutus_data_derive::FromPlutusDataDerive;
10pub use plutus_data_derive::ToPlutusDataDerive;
11
12mod custom_plutus;
13
14pub use custom_plutus::CustomPlutus as cp;
15use custom_plutus::*;
16
17pub trait ToPlutusData {
18    fn to_plutus_data(&self,attributes:&[String]) -> Result<PlutusData,String>;
19}
20
21pub use custom_plutus::CustomPlutus as pd;
22
23pub trait FromPlutusData<T> {
24    
25    fn from_plutus_data(x:PlutusData,attributes:&[String]) -> Result<T,String>;
26}
27use std::error::Error;
28pub fn from_bytes(x:&[u8]) -> Result<PlutusData, Box<dyn Error>>  {
29    PlutusData::decode_fragment(x)
30}
31pub fn from_bytes_specific<T:FromPlutusData<T>>(x:&[u8]) -> Result<T, String>  {
32    let pd = PlutusData::decode_fragment(x).map_err(|e|format!("{:?}",e))?;
33    T::from_plutus_data(pd, &vec![]).map_err(|e|format!("{:?}",e))
34}
35pub fn from_bytes_specific_debug<T:FromPlutusData<T>>(x:&[u8]) -> Result<T, String>  {
36    let pd = PlutusData::decode_fragment(x).map_err(|e|format!("{:?}",e))?;
37    T::from_plutus_data(pd, &vec!["debug_re_encoding".into()]).map_err(|e|format!("{:?}",e))
38}
39pub fn to_bytes(x:&PlutusData) -> Result<Vec<u8>, Box<dyn Error>>  {
40    x.encode_fragment()
41}
42pub fn from_hex<T:FromPlutusData<T>>(x:&str) -> Result<T,String> {
43    let bytes = hex::decode(&x).map_err(|e|format!("{:?}",e))?;
44    let pd = PlutusData::decode_fragment(&bytes).map_err(|e|format!("{:?}",e))?;
45    T::from_plutus_data(pd, &vec![]).map_err(|e|format!("{:?}",e))
46}   
47pub fn from_hex_debug<T:FromPlutusData<T>>(x:&str) -> Result<T,String> {
48    let bytes = hex::decode(&x).map_err(|e|format!("{:?}",e))?;
49    let pd = PlutusData::decode_fragment(&bytes).map_err(|e|format!("{:?}",e))?;
50    T::from_plutus_data(pd, &vec!["debug_re_encoding".into()]).map_err(|e|format!("{:?}",e))
51}   
52pub fn to_hex(x:&PlutusData) -> Result<String, Box<dyn Error>> {
53    let xx = x.encode_fragment()?;
54    let hexed : String = xx.encode_hex();
55    Ok(hexed)
56}
57pub fn encode<T : ToPlutusData>(x:&T) -> Result<PlutusData, String>  {
58    x.to_plutus_data(&[])
59}
60pub fn encode_vec<T : ToPlutusData + Clone + std::fmt::Debug>(x:&Vec<T>) -> Result<PlutusData, String>  {
61    x.to_plutus_data(&[])
62}
63
64impl<T1,T2> FromPlutusData<(T1,T2)> for (T1,T2) where T1: FromPlutusData<T1>, T2: FromPlutusData<T2> {
65    fn from_plutus_data(x:PlutusData,attribs:&[String]) -> Result<(T1,T2),String> {
66        match x {
67            PlutusData::Constr(p) => {
68                
69                if p.fields.len() != 2 {
70                    return Err(format!("expected tuple (list) with two items.. found {:?} with {} items.",p.tag,p.fields.len()))
71                }
72                
73                let key_a_plutus_item = p.fields[0].clone();
74                let key_b_plutus_item = p.fields[1].clone();
75                Ok((T1::from_plutus_data(key_a_plutus_item,attribs)?,
76                    T2::from_plutus_data(key_b_plutus_item,attribs)?
77                ))
78            },
79            PlutusData::Array(p) => {
80                if p.len() == 2 {
81                    let key_a_plutus_item = p[0].clone();
82                    let key_b_plutus_item = p[1].clone();
83                    Ok((T1::from_plutus_data(key_a_plutus_item,attribs)?,
84                        T2::from_plutus_data(key_b_plutus_item,attribs)?
85                    ))
86                } else {
87                    Err(format!("invalid length for tuple data {p:?}"))
88                }
89            },
90                _ => Err(format!("invalid tuple data {x:?}"))
91            }
92        }
93    }
94
95
96impl<T1,T2> ToPlutusData for (T1,T2) where T1: ToPlutusData , T2: ToPlutusData {
97    fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
98        let k = self.0.to_plutus_data(attribs)?;
99        let v = self.1.to_plutus_data(attribs)?;
100        Ok(CustomPlutus::make_tup(k,v))
101
102    }
103}
104
105
106impl<K : ToPlutusData + Clone,V : ToPlutusData + Clone> ToPlutusData for HashMap<K,V> {
107    fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
108        if let Some(p) = CustomPlutus::make_map(self,attribs)?.as_pallas() {
109            Ok(p.clone())
110        } else {
111            Err(String::from("to_plutus_data for hashmap failed."))
112        }
113    }
114}
115
116impl<K : ToPlutusData + Clone + Ord,V : ToPlutusData + Clone + Ord> ToPlutusData for BTreeMap<K,V> {
117    fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
118        if let Some(p) = CustomPlutus::make_bt_map(self,attribs)?.as_pallas() {
119            Ok(p.clone())
120        } else {
121            Err(String::from("to_plutus_data for btreemap failed."))
122        }
123    }
124}
125
126
127impl<T : FromPlutusData<T>> FromPlutusData<Box<T>> for Box<T> {
128    fn from_plutus_data(x:PlutusData,attribs:&[String]) -> Result<Box<T>,String> {
129        let result = T::from_plutus_data(x,attribs);
130        match result {
131            Ok(v) => Ok(Box::new(v)),
132            Err(e) => Err(format!("Failed to unpack option value from plutus data! Error: {}",e))
133        }
134    }
135}
136
137impl<T : ToPlutusData + Clone + std::fmt::Debug> ToPlutusData for Vec<T> {
138    fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
139        if let Some(p) = CustomPlutus::make_list(self,attribs)?.as_pallas() {
140            Ok(p.clone())
141        } else {
142            Err(String::from("to_plutus_data for vec<T> failed."))
143        }
144    }
145}
146
147impl<T1 :std::hash::Hash + std::cmp::Eq + FromPlutusData<T1>,T2 : FromPlutusData<T2>> FromPlutusData<HashMap<T1,T2>> for HashMap<T1,T2> {
148    fn from_plutus_data(p:PlutusData,attribs:&[String]) -> Result<HashMap<T1,T2>,String> {
149        match p {
150            PlutusData::Map(m) => {
151                let mut result = HashMap::new();
152                for kvp in m.iter() {
153                    
154                    let the_key = kvp.0.clone();
155                    let k = T1::from_plutus_data(the_key.clone(),attribs);
156
157                    let the_val = kvp.1.clone();
158                    let v = T2::from_plutus_data(the_val.clone(),attribs);
159
160
161                    // let the_value = m.get(&the_key).map_or(Err(String::from("found null value in plutus data. not supported")),|x|Ok(x))?;
162                    // let v = T2::from_plutus_data(the_value,attribs);
163
164                    result.insert(k?,v?);
165                }
166
167                Ok(result)
168            },
169            _ => Err(format!("Attempting to decode a hashmap but instead found: {:?}.",p))
170        }
171    }
172}
173
174
175impl<T1 : Ord + std::cmp::Eq + FromPlutusData<T1>,T2 : FromPlutusData<T2>> FromPlutusData<BTreeMap<T1,T2>> for BTreeMap<T1,T2> {
176    fn from_plutus_data(p:PlutusData,attribs:&[String]) -> Result<BTreeMap<T1,T2>,String> {
177        match p {
178            PlutusData::Map(m) => {
179                let mut result = BTreeMap::new();
180                for kvp in m.iter() {
181                    
182                    let the_key = kvp.0.clone();
183                    let k = T1::from_plutus_data(the_key.clone(),attribs);
184
185                    let the_val = kvp.1.clone();
186                    let v = T2::from_plutus_data(the_val.clone(),attribs);
187
188                    result.insert(k?,v?);
189                }
190
191                Ok(result)
192            },
193            _ => Err(format!("Attempting to decode a btreemap but instead found: {:?}.",p))
194        }
195    }
196}
197
198impl<T : FromPlutusData<T>> FromPlutusData<Vec<T>> for Vec<T> {
199    fn from_plutus_data(p:PlutusData,attribs:&[String]) -> Result<Vec<T>,String> {
200        match p {
201            PlutusData::Array(pl) => {
202                let mut result : Vec<T> = vec![];
203                for x in pl {
204                    match T::from_plutus_data(x,attribs) {
205                        Ok(v) => { result.push(v) },
206                        Err(e) => return Err(format!("when decoding a vector, we got this error: {}",e))
207                    } 
208                }
209               
210                Ok(result)
211            },
212            _ => Err(String::from("Failed to decode vec from plutus data because it was not a plutus list."))
213        }
214
215    }
216}
217
218impl FromPlutusData<String> for String {
219    fn from_plutus_data(x:PlutusData,attribs:&[String]) -> Result<String,String> {
220        let b16 : bool = attribs.iter().any(|a|a.to_lowercase() == "base_16");
221        match x {
222            PlutusData::BoundedBytes(bytes) if b16 => {
223                Ok(bytes.encode_hex())
224            },
225            PlutusData::BoundedBytes(bytes) => {
226                match std::str::from_utf8(&bytes) {
227                    Ok(s) => Ok(s.to_owned()),
228                    Err(e) => Err(format!("{:?}",e))
229                }  
230            },            
231            _ => Err(format!("expected string bytes, found something else: {:?}..",x))
232        }
233    }
234}
235
236impl ToPlutusData for String {
237    fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
238        let b16 : bool = attribs.iter().any(|a|a.to_lowercase() == "base_16");
239        let bytes = String::as_bytes(self).to_vec();
240        if b16 {
241            match hex::decode(bytes) {
242                Ok(hex_bytes) => Ok(pallas_primitives::alonzo::PlutusData::BoundedBytes(hex_bytes.into())),
243                Err(e) => Err(format!("{:?}",e))
244            }
245        } else {
246            Ok(pallas_primitives::alonzo::PlutusData::BoundedBytes(bytes.into()))
247        }
248    }
249}
250
251impl<T: ToPlutusData> ToPlutusData for &Option<T> {
252    fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
253        let ignore_option_container : bool = attribs.iter().any(|a|a.to_lowercase() == "ignore_option_container");
254        match self {
255            None if ignore_option_container => Err(String::from("Not possible to encode &None to plutus data when using attribute 'ignore_option_container'.")),
256            Some(v) if ignore_option_container => v.to_plutus_data(attribs),
257            None  => Ok(empty_constr(1)),            
258            Some(v)  => {
259                Ok(
260                    wrap_with_constr(
261                        0, 
262                        v.to_plutus_data(attribs)?
263                    )
264                )
265            }
266        }
267    }
268}
269
270pub struct ByteVec(Vec<u8>);
271
272impl ToPlutusData for ByteVec {
273    fn to_plutus_data(&self,_attributes:&[String]) -> Result<PlutusData,String> {
274        Ok(pallas_primitives::alonzo::PlutusData::BoundedBytes(self.0.clone().into()))
275    }
276}
277
278impl<T : FromPlutusData<T>> FromPlutusData<Option<T>> for Option<T> {
279    fn from_plutus_data(x:PlutusData,attribs:&[String]) -> Result<Option<T>,String> {
280        let ignore_option_container : bool = attribs.iter().any(|a|a.to_lowercase() == "ignore_option_container");
281        
282        if ignore_option_container {
283            let result = T::from_plutus_data(x,attribs);
284            match result {
285                Ok(v) => Ok(Some(v)),
286                Err(e) => Err(format!("Failed to unpack (ignore_option_container) option value from plutus data! Error: {}",e))
287            }
288        } else {
289            match x {
290                PlutusData::Constr(c) => {
291                    //println!("TAG IS {} AND CONSTR IS {:?}",c.tag,c.any_constructor);
292                    
293                    let constr = match c.tag {
294                        121..=127 => c.tag - 121,
295                        1280..=1400 => c.tag - 1280 + 7,
296                        102=> if let Some(xq) = c.any_constructor { xq } else {
297                            return  Err(format!("constructor 102 was not expected"));
298                        },
299                        xxx => return  Err(format!("Unexpected constructor {} when decoding an item of type {}",xxx,std::any::type_name::<T>()))
300                    };
301
302                    //let constr = if let Some(a) = c.any_constructor { a } else { c.tag - 121} ;
303                    match (constr,c.fields.len()) {
304                        (0,1) => {
305                            Ok(Some(T::from_plutus_data(c.fields[0].clone(),attribs)?))
306                        },
307                        (1,0) => Ok(None),
308                        _ => {
309                            Err(String::from("failed to unpack option value. not valid const representation."))
310                        }
311                    }
312                },
313                _ => Err(format!("failed to decode option value form plutus data... expected constr, found: {:?}",x)),
314            }
315        }
316    }
317}
318
319impl<T: ToPlutusData> ToPlutusData for Option<T> {
320    fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
321        let ignore_option_container : bool = attribs.iter().any(|a|a.to_lowercase() == "ignore_option_container");
322        match self {
323            None if ignore_option_container => Err(String::from("Not possible to encode None to plutus data when using attribute 'ignore_option_container'.")),
324            Some(v) if ignore_option_container => {
325                //println!("Encoding without an option container.");
326                v.to_plutus_data(attribs)
327            },
328            None  => Ok(empty_constr(1)),            
329            Some(v)  => {
330                //println!("Wrapping an item inside of an option constr 0: {:?}",attribs);
331                Ok(
332                    wrap_with_constr(
333                        0, 
334                        v.to_plutus_data(attribs)?
335                    )
336                )
337            }
338        }
339    }
340}
341
342impl<T: ToPlutusData + Clone + ?Sized> ToPlutusData for Box<T> {
343    fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
344        let inner_item : T = Box::into_inner(self.to_owned());
345        inner_item.to_plutus_data(attribs)
346    }
347}
348
349
350impl FromPlutusData<bool> for bool {
351    fn from_plutus_data(x:PlutusData,attribs:&[String]) -> Result<bool,String> {
352        let num_rep : bool = attribs.iter().any(|a|a.to_lowercase() == "repr_bool_as_num");
353        if num_rep {
354            match x {
355                PlutusData::BigInt(n) => {
356                    let s = match n {
357                        pallas_primitives::babbage::BigInt::Int(nn) => nn.0.to_string(),
358                        BigInt::BigUInt(nn) => nn.to_string(),
359                        BigInt::BigNInt(nn) => nn.to_string(),
360                    };
361                    Ok(s=="1")
362                },
363                PlutusData::Constr(c) => {
364                    Err(format!("cannot decode bool using repr_bool_as_num. it seems to be encoded as a constr, perhaps you did not mean to apply repr_bool_as_num? {:?}",c))
365                }
366                _ => Err(format!("cannot decode bool with num_rep from {:?}",x))
367            }
368        } else {
369            match x {
370                PlutusData::Constr(c) => {
371                   
372                    let t = match c.tag {
373                        121..=127 => c.tag - 121,
374                        1280..=1400 => c.tag - 1280 + 7,
375                        102 => if let Some(xq) = c.any_constructor { xq } else {
376                            return  Err(format!("constructor 102 was not expected"));
377                        },
378                        xxx => return  Err(format!("Unexpected constructor for {:?}",xxx))
379                    };
380                    
381                    if t == 1 {
382                        return Ok(true);
383                    }
384
385                    if t == 0 {
386                        return Ok(false);
387                    }
388
389                    return  Err(format!("Unexpected constructor a boolean! expected 1 or 0 but found: {t}"))
390                }
391                _ => {
392                    match x {
393                        PlutusData::BigInt(n) => {
394                            Err(format!("failed to decode this plutus data to bool using constr repr. it does seem to be a valid integer encoded bool, perhaps try with using repr_bool_as_num? --> {:?}",n))
395                        },
396                        _ => Err(format!("cannot decode bool using constr_rep from {:?}",x))
397                    }
398                    
399                }
400            }
401        }
402        
403    }
404}
405
406impl ToPlutusData for bool {
407    fn to_plutus_data(&self,attribs:&[String]) -> Result<PlutusData,String> {
408        let num_rep : bool = attribs.iter().any(|a|a.to_lowercase() == "repr_bool_as_num");        
409        match self {
410            true if num_rep => Ok(CustomPlutus::big_int(1).as_pallas().unwrap().clone()), // todo : fix
411            false if num_rep => Ok(CustomPlutus::big_int(0).as_pallas().unwrap().clone()), // todo : fix
412            true => Ok(CustomPlutus::make_constr(1, vec![])),
413            false => Ok(CustomPlutus::make_constr(0, vec![])),
414        }
415    }
416}
417
418
419
420ImplPlutusForNum!(@i8);
421ImplPlutusForNum!(@i16);
422ImplPlutusForNum!(@i32);
423ImplPlutusForNum!(@i64);
424ImplPlutusForNum!(@i128);
425ImplPlutusForNum!(@u8);
426ImplPlutusForNum!(@u16);
427ImplPlutusForNum!(@u32);
428ImplPlutusForNum!(@u64);
429ImplPlutusForNum!(@u128);
430ImplPlutusForNum!(@usize);
431
432use pallas_primitives::babbage::BigInt;
433pub fn convert_to_big_int(i:&i64) -> BigInt { CustomPlutus::to_big_int(*i) }
434pub fn convert_u64_to_big_int(i:&u64) -> BigInt { CustomPlutus::to_big_uint(*i) }
435pub fn convert_to_big_i128(i:&i128) -> Result<BigInt,String> { CustomPlutus::to_big_int128(*i) }
436pub fn convert_u64_to_u128(i:&u128) -> Result<BigInt,String> { CustomPlutus::to_big_uint128(*i) }
437
438mod macros {
439    #[macro_export]
440    #[doc(hidden)]
441    macro_rules! ImplPlutusForNum {
442        (@$T:ident) => {
443            impl ToPlutusData for $T {
444                fn to_plutus_data(&self,_attribs:&[String]) -> Result<PlutusData,String> {
445                    match &self.to_string().parse::<i128>() {
446                        Ok(big_number) => {
447                            let num = pallas_codec::utils::Int::try_from(*big_number).map_err(|e|format!("{e:?}"))?;
448                            Ok(PlutusData::BigInt(pallas_primitives::alonzo::BigInt::Int(num)))
449                        },
450                        Err(e) => Err(format!("failed to parse {} to BigInt. {e:?}",self)),
451                    }
452                }
453            }
454            impl ToPlutusData for &$T {
455                fn to_plutus_data(&self,_attribs:&[String]) -> Result<PlutusData,String> {
456                    match &self.to_string().parse::<i128>() {
457                        Ok(big_number) => {
458                            let num = pallas_codec::utils::Int::try_from(*big_number).map_err(|e|format!("{e:?}"))?;
459                            Ok(PlutusData::BigInt(pallas_primitives::alonzo::BigInt::Int(num)))
460                        }
461                        Err(_) => Err(format!("failed to parse {} to BigInt.",self)),
462                    }
463                }
464            }
465            impl FromPlutusData<$T> for $T {
466                fn from_plutus_data(p:PlutusData,_attribs:&[String]) -> Result<$T,String> {
467                    match p {
468                        PlutusData::BigInt(n) => {
469                            let s = match n {
470                                pallas_primitives::babbage::BigInt::Int(nn) => nn.0.to_string(),
471                                pallas_primitives::babbage::BigInt::BigUInt(nn) => nn.to_string(),
472                                pallas_primitives::babbage::BigInt::BigNInt(nn) => nn.to_string()
473                            };
474                            match s.parse::<$T>() {
475                                Ok(vc) => Ok(vc),
476                                Err(e) => Err(format!("Failed to convert string to number. {}.",e)),
477                            }
478                        },
479                        _ => Err(format!("failed to parse plutus data to num! input kind: {:?} - inner plutus data: {:?}",p,p)),
480                    }
481                }
482            }
483        }
484    }
485}
486
487fn empty_constr(tag_num: u64) -> PlutusData {
488    CustomPlutus::make_constr(tag_num, vec![])
489}
490
491fn wrap_with_constr(tag_num: u64, data: PlutusData) -> PlutusData {
492    CustomPlutus::make_constr(tag_num, vec![data])
493}