contract_transcode/
env_types.rs

1// Copyright (C) Use Ink (UK) Ltd.
2// This file is part of cargo-contract.
3//
4// cargo-contract is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// cargo-contract is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with cargo-contract.  If not, see <http://www.gnu.org/licenses/>.
16
17use crate::{
18    AccountId32,
19    Hex,
20    Value,
21    assert_not_shortened_hex,
22};
23use anyhow::{
24    Context,
25    Result,
26};
27use primitive_types::U128;
28use scale::{
29    Decode,
30    Encode,
31    Output,
32};
33use scale_info::{
34    IntoPortable,
35    Path,
36    TypeInfo,
37    form::PortableForm,
38};
39use std::{
40    boxed::Box,
41    collections::HashMap,
42    convert::TryFrom,
43    str::FromStr,
44};
45
46/// Provides custom encoding and decoding for predefined environment types.
47#[derive(Default)]
48pub struct EnvTypesTranscoder {
49    encoders: HashMap<u32, Box<dyn CustomTypeEncoder>>,
50    decoders: HashMap<u32, Box<dyn CustomTypeDecoder>>,
51}
52
53impl EnvTypesTranscoder {
54    /// Construct an `EnvTypesTranscoder` from the given type registry.
55    pub fn new(
56        encoders: HashMap<u32, Box<dyn CustomTypeEncoder>>,
57        decoders: HashMap<u32, Box<dyn CustomTypeDecoder>>,
58    ) -> Self {
59        Self { encoders, decoders }
60    }
61
62    /// If the given type id is for a type with custom encoding, encodes the given value
63    /// with the custom encoder and returns `true`. Otherwise returns `false`.
64    ///
65    /// # Errors
66    ///
67    /// - If the custom encoding fails.
68    pub fn try_encode<O>(
69        &self,
70        type_id: u32,
71        value: &Value,
72        output: &mut O,
73    ) -> Result<bool>
74    where
75        O: Output,
76    {
77        match self.encoders.get(&type_id) {
78            Some(encoder) => {
79                tracing::debug!("Encoding type {:?} with custom encoder", type_id);
80                let encoded_env_type = encoder
81                    .encode_value(value)
82                    .context("Error encoding custom type")?;
83                output.write(&encoded_env_type);
84                Ok(true)
85            }
86            None => Ok(false),
87        }
88    }
89
90    /// If the given type lookup id is for an environment type with custom
91    /// decoding, decodes the given input with the custom decoder and returns
92    /// `Some(value)`. Otherwise returns `None`.
93    ///
94    /// # Errors
95    ///
96    /// - If the custom decoding fails.
97    pub fn try_decode(&self, type_id: u32, input: &mut &[u8]) -> Result<Option<Value>> {
98        match self.decoders.get(&type_id) {
99            Some(decoder) => {
100                tracing::debug!("Decoding type {:?} with custom decoder", type_id);
101                let decoded = decoder.decode_value(input)?;
102                Ok(Some(decoded))
103            }
104            None => {
105                tracing::debug!("No custom decoder found for type {:?}", type_id);
106                Ok(None)
107            }
108        }
109    }
110}
111
112#[derive(Clone, Debug, Eq, PartialEq, Hash)]
113pub struct PathKey(Vec<String>);
114
115impl PathKey {
116    pub fn from_type<T>() -> Self
117    where
118        T: TypeInfo,
119    {
120        let type_info = T::type_info();
121        let path = type_info.path.into_portable(&mut Default::default());
122        PathKey::from(&path)
123    }
124}
125
126impl From<&Path<PortableForm>> for PathKey {
127    fn from(path: &Path<PortableForm>) -> Self {
128        PathKey(path.segments.to_vec())
129    }
130}
131
132pub type TypesByPath = HashMap<PathKey, u32>;
133
134/// Implement this trait to define custom encoding for a type in a `scale-info` type
135/// registry.
136pub trait CustomTypeEncoder: Send + Sync {
137    fn encode_value(&self, value: &Value) -> Result<Vec<u8>>;
138}
139
140/// Implement this trait to define custom decoding for a type in a `scale-info` type
141/// registry.
142pub trait CustomTypeDecoder: Send + Sync {
143    fn decode_value(&self, input: &mut &[u8]) -> Result<Value>;
144}
145
146/// Custom encoding/decoding for the Substrate `AccountId` type.
147///
148/// Enables an `AccountId` to be input/output as an SS58 Encoded literal e.g.
149/// `5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY`.
150#[derive(Clone)]
151pub struct AccountId;
152
153impl CustomTypeEncoder for AccountId {
154    fn encode_value(&self, value: &Value) -> Result<Vec<u8>> {
155        let account_id = match value {
156            Value::Literal(literal) => {
157                AccountId32::from_str(literal).map_err(|e| {
158                    anyhow::anyhow!(
159                        "Error parsing AccountId from literal `{literal}`: {e}"
160                    )
161                })?
162            }
163            Value::String(string) => {
164                AccountId32::from_str(string).map_err(|e| {
165                    anyhow::anyhow!("Error parsing AccountId from string '{string}': {e}")
166                })?
167            }
168            Value::Hex(hex) => {
169                AccountId32::try_from(hex.bytes()).map_err(|_| {
170                    anyhow::anyhow!(
171                        "Error converting hex bytes `{:?}` to AccountId",
172                        hex.bytes()
173                    )
174                })?
175            }
176            _ => {
177                return Err(anyhow::anyhow!(
178                    "Expected a string, literal, or hex for an AccountId"
179                ))
180            }
181        };
182        Ok(account_id.encode())
183    }
184}
185
186impl CustomTypeDecoder for AccountId {
187    fn decode_value(&self, input: &mut &[u8]) -> Result<Value> {
188        let account_id = AccountId32::decode(input)?;
189        Ok(Value::Literal(account_id.to_ss58check()))
190    }
191}
192
193/// Custom decoding for the `Hash` or `[u8; 32]` type so that it is displayed as a hex
194/// encoded string.
195#[derive(Clone)]
196pub struct Hash;
197
198impl CustomTypeEncoder for Hash {
199    fn encode_value(&self, value: &Value) -> Result<Vec<u8>> {
200        // todo currently using H256 here
201        let h256 = match value {
202            Value::Literal(literal) => {
203                primitive_types::H256::from_str(literal).map_err(|e| {
204                    anyhow::anyhow!("Error parsing H256 from literal `{literal}`: {e}")
205                })?
206            }
207            Value::String(string) => {
208                primitive_types::H256::from_str(string).map_err(|e| {
209                    anyhow::anyhow!("Error parsing H256 from string '{string}': {e}")
210                })?
211            }
212            Value::Hex(hex) => primitive_types::H256::from_slice(hex.bytes()),
213            _ => {
214                return Err(anyhow::anyhow!(
215                    "Expected a string, hex, uint, or literal for a U256"
216                ))
217            }
218        };
219        Ok(h256.encode())
220    }
221}
222
223impl CustomTypeDecoder for Hash {
224    fn decode_value(&self, input: &mut &[u8]) -> Result<Value> {
225        let hash = primitive_types::H256::decode(input)?;
226        Ok(Value::Hex(Hex::from_str(&format!("{hash:?}"))?))
227    }
228}
229
230/// Custom decoding for the `H160` or `[u8; 20]` type so that it is displayed as a hex
231/// encoded string.
232#[derive(Clone)]
233pub struct H160;
234
235impl CustomTypeDecoder for H160 {
236    fn decode_value(&self, input: &mut &[u8]) -> Result<Value> {
237        let h160 = primitive_types::H160::decode(input)?;
238        Ok(Value::Hex(Hex::from_str(&format!("{h160:?}"))?))
239    }
240}
241
242impl CustomTypeEncoder for H160 {
243    fn encode_value(&self, value: &Value) -> Result<Vec<u8>> {
244        let h160 = match value {
245            Value::Literal(literal) => {
246                primitive_types::H160::from_str(literal).map_err(|e| {
247                    anyhow::anyhow!("Error parsing H160 from literal `{literal}`: {e}")
248                })?
249            }
250            Value::String(string) => {
251                assert_not_shortened_hex(string);
252                primitive_types::H160::from_str(string).map_err(|e| {
253                    anyhow::anyhow!("Error parsing H160 from string '{string}': {e}")
254                })?
255            }
256            Value::Hex(hex) => primitive_types::H160::from_slice(hex.bytes()),
257            _ => {
258                return Err(anyhow::anyhow!(
259                    "Expected a string, literal, or hex for an H160"
260                ))
261            }
262        };
263        Ok(h160.encode())
264    }
265}
266
267/// Custom decoding for the `U256` or `[u8; 32]` type so that it is displayed as a hex
268/// encoded string.
269#[derive(Clone)]
270pub struct U256;
271
272impl CustomTypeDecoder for U256 {
273    fn decode_value(&self, input: &mut &[u8]) -> Result<Value> {
274        let u256 = primitive_types::U256::decode(input)?;
275        Ok(Value::Literal(format!("{u256}")))
276    }
277}
278
279impl CustomTypeEncoder for U256 {
280    fn encode_value(&self, value: &Value) -> Result<Vec<u8>> {
281        let u256 = match value {
282            Value::Literal(literal) => {
283                primitive_types::U256::from_str(literal).map_err(|e| {
284                    anyhow::anyhow!("Error parsing U256 from literal `{literal}`: {e}")
285                })?
286            }
287            Value::String(string) => {
288                primitive_types::U256::from_str(string).map_err(|e| {
289                    anyhow::anyhow!("Error parsing U256 from string '{string}': {e}")
290                })?
291            }
292            Value::UInt(uint128) => {
293                let u_128 = U128::from(*uint128);
294                primitive_types::U256::from(u_128)
295            }
296            // todo from_slice?
297            Value::Hex(hex) => primitive_types::U256::from_little_endian(hex.bytes()),
298            _ => {
299                return Err(anyhow::anyhow!(
300                    "Expected a string, hex, uint, or literal for a U256"
301                ))
302            }
303        };
304        let ret = u256.encode();
305        Ok(ret)
306    }
307}
308
309/// Custom decoding for the `H256` or `[u8; 32]` type so that it is displayed as a hex
310/// encoded string.
311#[derive(Clone)]
312pub struct H256;
313
314impl CustomTypeDecoder for H256 {
315    fn decode_value(&self, input: &mut &[u8]) -> Result<Value> {
316        let h256 = primitive_types::H256::decode(input)?;
317        Ok(Value::Hex(Hex::from_str(&format!("{h256:?}"))?))
318    }
319}
320
321impl CustomTypeEncoder for H256 {
322    fn encode_value(&self, value: &Value) -> Result<Vec<u8>> {
323        let h256 = match value {
324            Value::Literal(literal) => {
325                primitive_types::H256::from_str(literal).map_err(|e| {
326                    anyhow::anyhow!("Error parsing H256 from literal `{literal}`: {e}")
327                })?
328            }
329            Value::String(string) => {
330                primitive_types::H256::from_str(string).map_err(|e| {
331                    anyhow::anyhow!("Error parsing H256 from string '{string}': {e}")
332                })?
333            }
334            Value::Hex(hex) => primitive_types::H256::from_slice(hex.bytes()),
335            _ => {
336                return Err(anyhow::anyhow!(
337                    "Expected a string, hex, uint, or literal for a H256"
338                ))
339            }
340        };
341        Ok(h256.encode())
342    }
343}
344
345/*
346#[cfg(test)]
347mod tests {
348    use super::*;
349}
350*/