substrate_stellar_sdk/xdr/
xdr_codec.rs

1//! Trait for types that can be XDR encoded/decoded
2
3use base64::{decode_config_slice, encode_config_slice};
4use core::convert::{AsRef, TryInto};
5use sp_std::{boxed::Box, vec::Vec};
6
7use super::streams::{DecodeError, ReadStream, WriteStream};
8
9/// The XDR decoder/encoder trait
10///
11/// A type that implements this trait can be encoded as XDR or decoded from XDR
12pub trait XdrCodec: Sized {
13    /// Encode this type as XDR
14    ///
15    /// The binary XDR is returned as a byte vector
16    fn to_xdr(&self) -> Vec<u8> {
17        let mut write_stream = WriteStream::new();
18        self.to_xdr_buffered(&mut write_stream);
19        write_stream.get_result()
20    }
21
22    /// Decode XDR provided as a reference to a byte vector
23    ///
24    /// This will return error if decoding was not successful
25    fn from_xdr<T: AsRef<[u8]>>(input: T) -> Result<Self, DecodeError> {
26        let mut read_stream = ReadStream::new(input);
27        let value = Self::from_xdr_buffered(&mut read_stream)?;
28        if read_stream.no_of_bytes_left_to_read() != 0 {
29            return Err(DecodeError::TypeEndsTooEarly { remaining_no_of_bytes: read_stream.no_of_bytes_left_to_read() })
30        }
31
32        Ok(value)
33    }
34
35    /// Encode this type as base64 encoded XDR
36    ///
37    /// This returns an ASCII string (as a byte vector) that is the base64 encoding
38    /// of the XDR encoding of this type.
39    fn to_base64_xdr(&self) -> Vec<u8> {
40        let xdr = self.to_xdr();
41        let mut base64_buffer = Vec::new();
42        base64_buffer.resize(xdr.len() * 4 / 3 + 4, 0);
43        let bytes_written = encode_config_slice(xdr, base64::STANDARD, &mut base64_buffer);
44        base64_buffer.resize(bytes_written, 0);
45        base64_buffer
46    }
47
48    /// Decode this type from base64 encoded XDR
49    ///
50    /// This takes a reference to an ASCII string (as a byte vector), decodes it as base64
51    /// and then decodes the resulting binary array as XDR.
52    fn from_base64_xdr<T: AsRef<[u8]>>(input: T) -> Result<Self, DecodeError> {
53        let input = input.as_ref();
54        let mut buf = Vec::new();
55        buf.resize(input.len() * 4 / 3 + 4, 0);
56
57        match decode_config_slice(input, base64::STANDARD, &mut buf) {
58            Ok(bytes_written) => {
59                buf.resize(bytes_written, 0);
60                Self::from_xdr(buf)
61            },
62            Err(_) => Err(DecodeError::InvalidBase64),
63        }
64    }
65
66    /// Encode the XDR to a write stream
67    ///
68    /// This is the basic implementation of the XDR encoder of this type. The methods
69    /// `to_xdr` and `to_base64_xdr` call this function to do the heavy lifting.
70    fn to_xdr_buffered(&self, write_stream: &mut WriteStream);
71
72    /// Decode the XDR from a read stream
73    ///
74    /// This is the basic implementation of the XDR decoder of this type. The methods
75    /// `from_xdr` and `from_base64_xdr` call this function to do the heavy lifting.
76    fn from_xdr_buffered<T: AsRef<[u8]>>(read_stream: &mut ReadStream<T>) -> Result<Self, DecodeError>;
77}
78
79/// Implementation of the XDR decoder/encoder for `u64`
80impl XdrCodec for u64 {
81    fn to_xdr_buffered(&self, write_stream: &mut WriteStream) {
82        write_stream.write_next_u64(*self);
83    }
84
85    fn from_xdr_buffered<T: AsRef<[u8]>>(read_stream: &mut ReadStream<T>) -> Result<Self, DecodeError> {
86        read_stream.read_next_u64()
87    }
88}
89
90/// Implementation of the XDR decoder/encoder for `i64`
91impl XdrCodec for i64 {
92    fn to_xdr_buffered(&self, write_stream: &mut WriteStream) {
93        write_stream.write_next_i64(*self);
94    }
95
96    fn from_xdr_buffered<T: AsRef<[u8]>>(read_stream: &mut ReadStream<T>) -> Result<Self, DecodeError> {
97        read_stream.read_next_i64()
98    }
99}
100
101/// Implementation of the XDR decoder/encoder for `u32`
102impl XdrCodec for u32 {
103    fn to_xdr_buffered(&self, write_stream: &mut WriteStream) {
104        write_stream.write_next_u32(*self);
105    }
106
107    fn from_xdr_buffered<T: AsRef<[u8]>>(read_stream: &mut ReadStream<T>) -> Result<Self, DecodeError> {
108        read_stream.read_next_u32()
109    }
110}
111
112/// Implementation of the XDR decoder/encoder for `i32`
113impl XdrCodec for i32 {
114    fn to_xdr_buffered(&self, write_stream: &mut WriteStream) {
115        write_stream.write_next_i32(*self);
116    }
117
118    fn from_xdr_buffered<T: AsRef<[u8]>>(read_stream: &mut ReadStream<T>) -> Result<Self, DecodeError> {
119        read_stream.read_next_i32()
120    }
121}
122
123/// Implementation of the XDR decoder/encoder for `bool`
124impl XdrCodec for bool {
125    fn to_xdr_buffered(&self, write_stream: &mut WriteStream) {
126        write_stream.write_next_i32(if *self { 1 } else { 0 });
127    }
128
129    fn from_xdr_buffered<T: AsRef<[u8]>>(read_stream: &mut ReadStream<T>) -> Result<Self, DecodeError> {
130        let parsed_int = read_stream.read_next_i32()?;
131        match parsed_int {
132            0 => Ok(false),
133            1 => Ok(true),
134            _ =>
135                Err(DecodeError::InvalidBoolean { found_integer: parsed_int, at_position: read_stream.get_position() }),
136        }
137    }
138}
139
140/// Implementation of the XDR decoder/encoder for a fixed size array
141///
142/// This requires that the inner type already implements `XdrCodec`
143impl<T: XdrCodec, const N: usize> XdrCodec for [T; N] {
144    fn to_xdr_buffered(&self, write_stream: &mut WriteStream) {
145        for item in self.iter() {
146            item.to_xdr_buffered(write_stream);
147        }
148    }
149
150    fn from_xdr_buffered<R: AsRef<[u8]>>(read_stream: &mut ReadStream<R>) -> Result<Self, DecodeError> {
151        let mut result = Vec::<T>::with_capacity(N);
152        for _ in 0..N {
153            result.push(T::from_xdr_buffered(read_stream)?)
154        }
155        result.try_into().map_err(|_| unreachable!())
156    }
157}
158
159/// Implementation of the XDR decoder/encoder for fixed length binary data
160impl<const N: usize> XdrCodec for [u8; N] {
161    fn to_xdr_buffered(&self, write_stream: &mut WriteStream) {
162        write_stream.write_next_binary_data(self);
163    }
164
165    fn from_xdr_buffered<T: AsRef<[u8]>>(read_stream: &mut ReadStream<T>) -> Result<Self, DecodeError> {
166        let value = read_stream.read_next_binary_data(N)?;
167        value.try_into().map_err(|_| unreachable!())
168    }
169}
170
171/// Implementation of the XDR decoder/encoder for an `Option`.
172///
173/// This requires that the inner type already implements `XdrCodec`
174impl<T: XdrCodec> XdrCodec for Option<T> {
175    fn to_xdr_buffered(&self, write_stream: &mut WriteStream) {
176        match self {
177            None => write_stream.write_next_u32(0),
178            Some(value) => {
179                write_stream.write_next_u32(1);
180                value.to_xdr_buffered(write_stream);
181            },
182        }
183    }
184
185    fn from_xdr_buffered<R: AsRef<[u8]>>(read_stream: &mut ReadStream<R>) -> Result<Self, DecodeError> {
186        match read_stream.read_next_u32()? {
187            0 => Ok(None),
188            1 => T::from_xdr_buffered(read_stream).map(|ok| Some(ok)),
189            code => Err(DecodeError::InvalidOptional { at_position: read_stream.get_position(), has_code: code }),
190        }
191    }
192}
193
194/// Implementation of the XDR decoder/encoder for an `Box`.
195///
196/// This requires that the inner type already implements `XdrCodec`
197impl<T: XdrCodec> XdrCodec for Box<T> {
198    fn to_xdr_buffered(&self, write_stream: &mut WriteStream) {
199        self.as_ref().to_xdr_buffered(write_stream)
200    }
201
202    fn from_xdr_buffered<R: AsRef<[u8]>>(read_stream: &mut ReadStream<R>) -> Result<Self, DecodeError> {
203        Ok(Box::new(T::from_xdr_buffered(read_stream)?))
204    }
205}
206
207/// To easily convert any bytes to a Stellar type.
208///
209/// # Examples
210///
211/// Basic usage:
212/// 
213#[cfg_attr(feature = "all-types", doc = "```")]
214#[cfg_attr(not(feature = "all-types"), doc = "```ignore")]
215/// # // For this test, we require the usage of `Auth` which is only compiled with
216/// # // the `all-types` feature.
217/// # // We will ignore the test if the feature is not enabled.
218/// use substrate_stellar_sdk::{types::Auth, parse_stellar_type};
219/// let auth_xdr =  [0, 0, 0, 1];
220/// let result = parse_stellar_type!(auth_xdr,Auth);
221/// assert_eq!(result, Ok(Auth { flags: 1 }))
222/// ```
223#[macro_export]
224macro_rules! parse_stellar_type {
225    ($ref:ident, $struct_str:ident) => {{
226        use $crate::{types::$struct_str, StellarSdkError, XdrCodec};
227
228        let ret: Result<$struct_str, StellarSdkError> =
229            $struct_str::from_xdr($ref).map_err(|_| StellarSdkError::DecodeError(stringify!($struct_str).into()));
230        ret
231    }};
232}