sigma_ser/
scorex_serialize.rs

1use std::convert::TryInto;
2use std::io;
3
4use crate::vlq_encode;
5use crate::vlq_encode::*;
6use bounded_vec::{BoundedVec, BoundedVecOutOfBounds};
7use thiserror::Error;
8
9/// Ways serialization might fail
10#[derive(Error, Eq, PartialEq, Debug, Clone)]
11pub enum ScorexSerializationError {
12    /// IO fail (EOF, etc.)
13    #[error("IO error: {0}")]
14    Io(String),
15    /// Serialization not yet implemented
16    #[error("serialization not yet implemented: {0}")]
17    NotImplementedYet(&'static str),
18    /// Serialization not supported
19    #[error("serialization not supported: {0}")]
20    NotSupported(&'static str),
21    /// Integer type conversion failed
22    #[error("Bounds check error: {0}")]
23    TryFrom(#[from] std::num::TryFromIntError),
24    /// Misc error
25    #[error("error: {0}")]
26    Misc(&'static str),
27}
28
29impl From<io::Error> for ScorexSerializationError {
30    fn from(error: io::Error) -> Self {
31        ScorexSerializationError::Io(error.to_string())
32    }
33}
34
35impl From<ScorexSerializationError> for io::Error {
36    fn from(e: ScorexSerializationError) -> Self {
37        io::Error::new(io::ErrorKind::InvalidInput, e.to_string())
38    }
39}
40
41/// Ways parsing might fail
42#[derive(Error, Eq, PartialEq, Debug, Clone)]
43pub enum ScorexParsingError {
44    /// Invalid op code
45    #[error("invalid op code: {0}")]
46    InvalidOpCode(u8),
47    /// Lacking support for the op
48    #[error("not implemented op error")]
49    NotImplementedOpCode(String),
50    /// Failed to parse type
51    #[error("type parsing error, invalid type code: {0}({0:#04X})")]
52    InvalidTypeCode(u8),
53    /// Failed to decode VLQ
54    #[error("vlq encode error: {0}")]
55    VlqEncode(#[from] vlq_encode::VlqEncodingError),
56    /// IO fail (EOF, etc.)
57    #[error("IO error: {0}")]
58    Io(String),
59    /// Misc fail
60    #[error("misc error")]
61    Misc(String),
62    /// Feature not yet implemented
63    #[error("parsing not yet implemented: {0}")]
64    NotImplementedYet(String),
65    /// Value out of bounds
66    #[error("Value out of bounds: {0}")]
67    ValueOutOfBounds(String),
68    /// Tuple items out of bounds
69    #[error("Tuple items out of bounds: {0}")]
70    TupleItemsOutOfBounds(usize),
71    /// Feature not supported
72    #[error("parsing not supported: {0}")]
73    NotSupported(&'static str),
74    /// Serialization error
75    #[error("serialization error: {0}")]
76    SerializationError(#[from] ScorexSerializationError),
77    /// Invalid item quantity for BoundedVec
78    #[error("Invalid item quantity for BoundedVec: {0}")]
79    BoundedVecOutOfBounds(#[from] BoundedVecOutOfBounds),
80    /// Failed to convert integer type
81    #[error("Bounds check error: {0}")]
82    TryFrom(#[from] std::num::TryFromIntError),
83}
84
85impl From<io::Error> for ScorexParsingError {
86    fn from(error: io::Error) -> Self {
87        ScorexParsingError::Io(error.to_string())
88    }
89}
90
91impl From<&io::Error> for ScorexParsingError {
92    fn from(error: &io::Error) -> Self {
93        ScorexParsingError::Io(error.to_string())
94    }
95}
96
97/// Result type for [`ScorexSerializable::scorex_serialize`]
98pub type ScorexSerializeResult = Result<(), ScorexSerializationError>;
99
100/// Scorex Serializable Trait.
101pub trait ScorexSerializable: Sized {
102    /// Write `self` to the `writer`
103    fn scorex_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> ScorexSerializeResult;
104    /// parse `self` from `reader`
105    fn scorex_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, ScorexParsingError>;
106
107    /// Serialize a ScorexSerializable value into bytes
108    fn scorex_serialize_bytes(&self) -> Result<Vec<u8>, ScorexSerializationError> {
109        let mut w = vec![];
110        self.scorex_serialize(&mut w)?;
111        Ok(w)
112    }
113    /// Parse `self` from the bytes
114    fn scorex_parse_bytes(mut bytes: &[u8]) -> Result<Self, ScorexParsingError> {
115        Self::scorex_parse(&mut bytes)
116    }
117}
118
119impl<T: ScorexSerializable> ScorexSerializable for Vec<T> {
120    fn scorex_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> ScorexSerializeResult {
121        w.put_u32(self.len() as u32)?;
122        self.iter().try_for_each(|i| i.scorex_serialize(w))
123    }
124
125    fn scorex_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, ScorexParsingError> {
126        let items_count = r.get_u32()?;
127        let mut items = Vec::with_capacity(items_count as usize);
128        for _ in 0..items_count {
129            items.push(T::scorex_parse(r)?);
130        }
131        Ok(items)
132    }
133}
134
135impl<T: ScorexSerializable, const L: usize, const U: usize> ScorexSerializable
136    for BoundedVec<T, L, U>
137{
138    fn scorex_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> ScorexSerializeResult {
139        self.as_vec().scorex_serialize(w)
140    }
141
142    fn scorex_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, ScorexParsingError> {
143        Ok(Vec::<T>::scorex_parse(r)?.try_into()?)
144    }
145}
146
147/// Corresponds to `VLQ(UInt)` format from `ErgoTree` spec.
148impl ScorexSerializable for u32 {
149    fn scorex_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> ScorexSerializeResult {
150        w.put_u32(*self)?;
151        Ok(())
152    }
153    fn scorex_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, ScorexParsingError> {
154        let v = r.get_u32()?;
155        Ok(v)
156    }
157}
158
159impl<T: ScorexSerializable> ScorexSerializable for Option<Box<T>> {
160    fn scorex_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> ScorexSerializeResult {
161        match self {
162            Some(v) => {
163                w.put_u8(1)?;
164                v.scorex_serialize(w)
165            }
166            None => Ok(w.put_u8(0)?),
167        }
168    }
169
170    fn scorex_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, ScorexParsingError> {
171        let tag = r.get_u8()?;
172        Ok(if tag != 0 {
173            Some(T::scorex_parse(r)?.into())
174        } else {
175            None
176        })
177    }
178}
179
180/// serialization roundtrip
181#[allow(clippy::expect_used)]
182pub fn scorex_serialize_roundtrip<T: ScorexSerializable>(v: &T) -> T {
183    let mut data = Vec::new();
184    v.scorex_serialize(&mut data).expect("serialization failed");
185    let reader = &mut &data[..];
186    T::scorex_parse(reader).expect("parse failed")
187}
188
189#[allow(clippy::unwrap_used)]
190#[cfg(test)]
191#[allow(clippy::panic)]
192mod test {
193    use super::*;
194    use proptest::collection::vec;
195    use proptest::prelude::*;
196
197    proptest! {
198        #[test]
199        fn u32_roundtrip(val in any::<u32>()) {
200            assert_eq!(scorex_serialize_roundtrip(&val), val);
201        }
202
203        #[test]
204        fn vec_roundtrip(val in vec(any::<u32>(), 0..255)) {
205            assert_eq!(scorex_serialize_roundtrip(&val), val);
206        }
207
208        #[test]
209        fn box_roundtrip(val in any::<Option<Box<u32>>>()) {
210            assert_eq!(scorex_serialize_roundtrip(&val), val);
211        }
212    }
213}