contract_transcode/
encode.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 super::{
18    env_types::EnvTypesTranscoder,
19    scon::Value,
20    CompositeTypeFields,
21};
22use anyhow::Result;
23use itertools::Itertools;
24use scale::{
25    Compact,
26    Encode,
27    Output,
28};
29use scale_info::{
30    form::{
31        Form,
32        PortableForm,
33    },
34    Field,
35    PortableRegistry,
36    TypeDef,
37    TypeDefCompact,
38    TypeDefPrimitive,
39    TypeDefTuple,
40    TypeDefVariant,
41};
42use std::{
43    convert::{
44        TryFrom,
45        TryInto,
46    },
47    error::Error,
48    fmt::Debug,
49    str::FromStr,
50};
51
52pub struct Encoder<'a> {
53    registry: &'a PortableRegistry,
54    env_types: &'a EnvTypesTranscoder,
55}
56
57impl<'a> Encoder<'a> {
58    pub fn new(
59        registry: &'a PortableRegistry,
60        env_types: &'a EnvTypesTranscoder,
61    ) -> Self {
62        Self {
63            registry,
64            env_types,
65        }
66    }
67
68    pub fn encode<O>(&self, type_id: u32, value: &Value, output: &mut O) -> Result<()>
69    where
70        O: Output + Debug,
71    {
72        let ty = self.registry.resolve(type_id).ok_or_else(|| {
73            anyhow::anyhow!("Failed to resolve type with id '{:?}'", type_id)
74        })?;
75
76        tracing::debug!(
77            "Encoding value `{:?}` with type id `{:?}` and definition `{:?}`",
78            value,
79            type_id,
80            ty.type_def,
81        );
82        if !self.env_types.try_encode(type_id, value, output)? {
83            match &ty.type_def {
84                TypeDef::Composite(composite) => {
85                    self.encode_composite(&composite.fields, value, output)
86                }
87                TypeDef::Variant(variant) => {
88                    self.encode_variant_type(variant, value, output)
89                }
90                TypeDef::Array(array) => {
91                    self.encode_seq(&array.type_param, value, false, output)
92                }
93                TypeDef::Tuple(tuple) => self.encode_tuple(tuple, value, output),
94                TypeDef::Sequence(sequence) => {
95                    self.encode_seq(&sequence.type_param, value, true, output)
96                }
97                TypeDef::Primitive(primitive) => {
98                    self.encode_primitive(primitive, value, output)
99                }
100                TypeDef::Compact(compact) => self.encode_compact(compact, value, output),
101                TypeDef::BitSequence(_) => {
102                    Err(anyhow::anyhow!("bitvec encoding not yet supported"))
103                }
104            }?;
105        }
106        Ok(())
107    }
108
109    fn encode_composite<O: Output + Debug>(
110        &self,
111        fields: &[Field<PortableForm>],
112        value: &Value,
113        output: &mut O,
114    ) -> Result<()> {
115        let struct_type = CompositeTypeFields::from_fields(fields)?;
116
117        match value {
118            Value::Map(map) => {
119                match struct_type {
120                    CompositeTypeFields::Unnamed(fields) => {
121                        for (field, value) in fields.iter().zip(map.values()) {
122                            self.encode(field.ty.id, value, output)?;
123                        }
124                        Ok(())
125                    }
126                    CompositeTypeFields::NoFields => Ok(()),
127                    CompositeTypeFields::Named(named_fields) => {
128                        for named_field in named_fields {
129                            let field_name = named_field.name();
130                            let value = map.get_by_str(field_name).ok_or_else(|| {
131                                anyhow::anyhow!("Missing a field named `{}`", field_name)
132                            })?;
133                            self.encode(named_field.field().ty.id, value, output)
134                                .map_err(|e| {
135                                    anyhow::anyhow!(
136                                        "Error encoding field `{}`: {}",
137                                        field_name,
138                                        e
139                                    )
140                                })?;
141                        }
142                        Ok(())
143                    }
144                }
145            }
146            Value::Tuple(tuple) => {
147                match struct_type {
148                    CompositeTypeFields::Unnamed(fields) => {
149                        for (field, value) in fields.iter().zip(tuple.values()) {
150                            self.encode(field.ty.id, value, output)?;
151                        }
152                        Ok(())
153                    }
154                    CompositeTypeFields::NoFields => Ok(()),
155                    CompositeTypeFields::Named(_) => {
156                        Err(anyhow::anyhow!("Type is a struct requiring named fields"))
157                    }
158                }
159            }
160            v => {
161                if let Ok(single_field) = fields.iter().exactly_one() {
162                    self.encode(single_field.ty.id, value, output)
163                } else {
164                    Err(anyhow::anyhow!(
165                        "Expected a Map or a Tuple or a single Value for a composite data type, found {:?}",
166                        v
167                    ))
168                }
169            }
170        }
171    }
172
173    fn encode_tuple<O: Output + Debug>(
174        &self,
175        tuple: &TypeDefTuple<PortableForm>,
176        value: &Value,
177        output: &mut O,
178    ) -> Result<()> {
179        match value {
180            Value::Tuple(tuple_val) => {
181                for (field_type, value) in tuple.fields.iter().zip(tuple_val.values()) {
182                    self.encode(field_type.id, value, output)?;
183                }
184                Ok(())
185            }
186            v => {
187                if let Ok(single_field) = tuple.fields.iter().exactly_one() {
188                    self.encode(single_field.id, value, output)
189                } else {
190                    Err(anyhow::anyhow!(
191                        "Expected a Tuple or a single Value for a tuple data type, found {:?}",
192                        v
193                    ))
194                }
195            }
196        }
197    }
198
199    fn encode_variant_type<O: Output + Debug>(
200        &self,
201        variant_def: &TypeDefVariant<PortableForm>,
202        value: &Value,
203        output: &mut O,
204    ) -> Result<()> {
205        let variant_ident = match value {
206            Value::Map(map) => {
207                map.ident().ok_or_else(|| {
208                    anyhow::anyhow!("Missing enum variant identifier for map")
209                })
210            }
211            Value::Tuple(tuple) => {
212                tuple.ident().ok_or_else(|| {
213                    anyhow::anyhow!("Missing enum variant identifier for tuple")
214                })
215            }
216            v => Err(anyhow::anyhow!("Invalid enum variant value '{:?}'", v)),
217        }?;
218
219        let (index, variant) = variant_def
220            .variants
221            .iter()
222            .find_position(|v| v.name == variant_ident)
223            .ok_or_else(|| anyhow::anyhow!("No variant '{}' found", variant_ident))?;
224
225        let index: u8 = index
226            .try_into()
227            .map_err(|_| anyhow::anyhow!("Variant index > 255"))?;
228        output.push_byte(index);
229
230        self.encode_composite(&variant.fields, value, output)
231    }
232
233    fn encode_seq<O: Output + Debug>(
234        &self,
235        ty: &<PortableForm as Form>::Type,
236        value: &Value,
237        encode_len: bool,
238        output: &mut O,
239    ) -> Result<()> {
240        match value {
241            Value::Seq(values) => {
242                if encode_len {
243                    Compact(values.len() as u32).encode_to(output);
244                }
245                for value in values.elems() {
246                    self.encode(ty.id, value, output)?;
247                }
248            }
249            Value::Hex(hex) => {
250                if encode_len {
251                    Compact(hex.bytes().len() as u32).encode_to(output);
252                }
253                for byte in hex.bytes() {
254                    output.push_byte(*byte);
255                }
256            }
257            value => {
258                return Err(anyhow::anyhow!("{:?} cannot be encoded as an array", value))
259            }
260        }
261        Ok(())
262    }
263
264    fn encode_primitive<O: Output + Debug>(
265        &self,
266        primitive: &TypeDefPrimitive,
267        value: &Value,
268        output: &mut O,
269    ) -> Result<()> {
270        match primitive {
271            TypeDefPrimitive::Bool => {
272                if let Value::Bool(b) = value {
273                    b.encode_to(output);
274                    Ok(())
275                } else {
276                    Err(anyhow::anyhow!("Expected a bool value"))
277                }
278            }
279            TypeDefPrimitive::Char => {
280                Err(anyhow::anyhow!("scale codec not implemented for char"))
281            }
282            TypeDefPrimitive::Str => {
283                if let Value::String(s) = value {
284                    s.encode_to(output);
285                    Ok(())
286                } else {
287                    Err(anyhow::anyhow!("Expected a String value\n\
288                    On the command-line, String values need to be wrapped in escaped quotes: \
289                    \\\"…\\\"."))
290                }
291            }
292            TypeDefPrimitive::U8 => encode_uint::<u8, O>(value, "u8", output),
293            TypeDefPrimitive::U16 => encode_uint::<u16, O>(value, "u16", output),
294            TypeDefPrimitive::U32 => encode_uint::<u32, O>(value, "u32", output),
295            TypeDefPrimitive::U64 => encode_uint::<u64, O>(value, "u64", output),
296            TypeDefPrimitive::U128 => encode_uint::<u128, O>(value, "u128", output),
297            TypeDefPrimitive::U256 => {
298                Err(anyhow::anyhow!("U256 currently not supported"))
299            }
300            TypeDefPrimitive::I8 => encode_int::<i8, O>(value, "i8", output),
301            TypeDefPrimitive::I16 => encode_int::<i16, O>(value, "i16", output),
302            TypeDefPrimitive::I32 => encode_int::<i32, O>(value, "i32", output),
303            TypeDefPrimitive::I64 => encode_int::<i64, O>(value, "i64", output),
304            TypeDefPrimitive::I128 => encode_int::<i128, O>(value, "i128", output),
305            TypeDefPrimitive::I256 => {
306                Err(anyhow::anyhow!("I256 currently not supported"))
307            }
308        }
309    }
310
311    fn encode_compact<O: Output + Debug>(
312        &self,
313        compact: &TypeDefCompact<PortableForm>,
314        value: &Value,
315        output: &mut O,
316    ) -> Result<()> {
317        let mut encode_compact_primitive =
318            |primitive: &TypeDefPrimitive, value: &Value| {
319                match primitive {
320                    TypeDefPrimitive::U8 => {
321                        let uint = uint_from_value::<u8>(value, "u8")?;
322                        Compact(uint).encode_to(output);
323                        Ok(())
324                    }
325                    TypeDefPrimitive::U16 => {
326                        let uint = uint_from_value::<u16>(value, "u16")?;
327                        Compact(uint).encode_to(output);
328                        Ok(())
329                    }
330                    TypeDefPrimitive::U32 => {
331                        let uint = uint_from_value::<u32>(value, "u32")?;
332                        Compact(uint).encode_to(output);
333                        Ok(())
334                    }
335                    TypeDefPrimitive::U64 => {
336                        let uint = uint_from_value::<u64>(value, "u64")?;
337                        Compact(uint).encode_to(output);
338                        Ok(())
339                    }
340                    TypeDefPrimitive::U128 => {
341                        let uint = uint_from_value::<u128>(value, "u128")?;
342                        Compact(uint).encode_to(output);
343                        Ok(())
344                    }
345                    _ => {
346                        Err(anyhow::anyhow!(
347                            "Compact encoding not supported for {:?}",
348                            primitive
349                        ))
350                    }
351                }
352            };
353
354        let ty = self
355            .registry
356            .resolve(compact.type_param.id)
357            .ok_or_else(|| {
358                anyhow::anyhow!(
359                    "Failed to resolve type with id '{:?}'",
360                    compact.type_param.id
361                )
362            })?;
363        match &ty.type_def {
364            TypeDef::Primitive(primitive) => encode_compact_primitive(primitive, value),
365            TypeDef::Composite(composite) => {
366                match &composite.fields[..] {
367                    [field] => {
368                        let type_id = field.ty.id;
369                        let field_ty =
370                            self.registry.resolve(type_id).ok_or_else(|| {
371                                anyhow::anyhow!(
372                                    "Failed to resolve type with id `{:?}`",
373                                    type_id
374                                )
375                            })?;
376                        if let TypeDef::Primitive(primitive) = &field_ty.type_def {
377                            let field_values: Vec<_> = match value {
378                            Value::Map(map) => Ok(map.values().collect()),
379                            Value::Tuple(tuple) => Ok(tuple.values().collect()),
380                            x => Err(anyhow::anyhow!(
381                                "Compact composite value must be a Map or a Tuple. Found {}",
382                                x
383                            )),
384                        }?;
385                            if field_values.len() == 1 {
386                                let field_value = field_values[0];
387                                encode_compact_primitive(primitive, field_value)
388                            } else {
389                                Err(anyhow::anyhow!(
390                                    "Compact composite value must have a single field"
391                                ))
392                            }
393                        } else {
394                            Err(anyhow::anyhow!(
395                                "Composite type must have a single primitive field"
396                            ))
397                        }
398                    }
399                    _ => Err(anyhow::anyhow!("Composite type must have a single field")),
400                }
401            }
402            _ => {
403                Err(anyhow::anyhow!(
404                    "Compact type must be a primitive or a composite type"
405                ))
406            }
407        }
408    }
409}
410
411fn uint_from_value<T>(value: &Value, expected: &str) -> Result<T>
412where
413    T: TryFrom<u128> + TryFromHex + FromStr,
414    <T as TryFrom<u128>>::Error: Error + Send + Sync + 'static,
415    <T as FromStr>::Err: Error + Send + Sync + 'static,
416{
417    match value {
418        Value::UInt(i) => {
419            let uint = (*i).try_into()?;
420            Ok(uint)
421        }
422        Value::String(s) => {
423            let sanitized = s.replace(&['_', ','][..], "");
424            let uint = T::from_str(&sanitized)?;
425            Ok(uint)
426        }
427        Value::Hex(hex) => {
428            let uint = T::try_from_hex(hex.as_str())?;
429            Ok(uint)
430        }
431        _ => {
432            Err(anyhow::anyhow!(
433                "Expected a {} or a String value, got {}",
434                expected,
435                value
436            ))
437        }
438    }
439}
440
441fn encode_uint<T, O>(value: &Value, expected: &str, output: &mut O) -> Result<()>
442where
443    T: TryFrom<u128> + TryFromHex + FromStr + Encode,
444    <T as TryFrom<u128>>::Error: Error + Send + Sync + 'static,
445    <T as FromStr>::Err: Error + Send + Sync + 'static,
446    O: Output,
447{
448    let uint: T = uint_from_value(value, expected)?;
449    uint.encode_to(output);
450    Ok(())
451}
452
453fn encode_int<T, O>(value: &Value, expected: &str, output: &mut O) -> Result<()>
454where
455    T: TryFrom<i128> + TryFrom<u128> + FromStr + Encode,
456    <T as TryFrom<i128>>::Error: Error + Send + Sync + 'static,
457    <T as TryFrom<u128>>::Error: Error + Send + Sync + 'static,
458    <T as FromStr>::Err: Error + Send + Sync + 'static,
459    O: Output,
460{
461    let int = match value {
462        Value::Int(i) => {
463            let i: T = (*i).try_into()?;
464            Ok(i)
465        }
466        Value::UInt(u) => {
467            let i: T = (*u).try_into()?;
468            Ok(i)
469        }
470        Value::String(s) => {
471            let sanitized = s.replace(&['_', ','][..], "");
472            let i = T::from_str(&sanitized)?;
473            Ok(i)
474        }
475        _ => {
476            Err(anyhow::anyhow!(
477                "Expected a {} or a String value, got {}",
478                expected,
479                value
480            ))
481        }
482    }?;
483    int.encode_to(output);
484    Ok(())
485}
486
487/// Attempt to instantiate a type from its hex-encoded bytes representation.
488pub trait TryFromHex: Sized {
489    /// Create a new instance from the hex-encoded bytes representation.
490    fn try_from_hex(hex: &str) -> Result<Self>;
491}
492
493macro_rules! impl_try_from_hex {
494    ( $($ty:ident),* ) => { $(
495        impl TryFromHex for $ty {
496            fn try_from_hex(hex: &str) -> Result<Self> {
497                $ty::from_str_radix(hex, 16).map_err(Into::into)
498            }
499        }
500    )* }
501}
502
503impl_try_from_hex!(u8, u16, u32, u64, u128);