diff --git a/Cargo.lock b/Cargo.lock index 0553fe875..460d12aac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,9 +56,11 @@ dependencies = [ "hex", "log", "parity-scale-codec", + "scale-bits", "scale-info", "serde", "serde_json", + "smallvec", "sp-application-crypto", "sp-core", "sp-runtime", @@ -4391,6 +4393,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "scale-bits" +version = "0.3.0" +source = "git+https://github.com/haerdib/scale-bits.git?branch=bh/no-std#6b1aa9d4e1cb5c0d70dd826da05e912086409764" +dependencies = [ + "parity-scale-codec", + "scale-info", +] + [[package]] name = "scale-info" version = "2.7.0" @@ -5942,7 +5953,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "digest 0.10.7", "rand 0.8.5", "static_assertions", diff --git a/node-api/Cargo.toml b/node-api/Cargo.toml index 744375d22..ef0cef447 100644 --- a/node-api/Cargo.toml +++ b/node-api/Cargo.toml @@ -20,16 +20,20 @@ log = { version = "0.4.14", default-features = false } scale-info = { version = "2.0.1", features = ["derive", "decode", "bitvec"], default-features = false } serde = { version = "1.0.136", features = ["derive"], default-features = false } serde_json = { version = "1.0.79", default-features = false, features = ["alloc"] } +smallvec = "1.10.0" -# substrate +# parity sp-core = { default-features = false, features = ["full_crypto", "serde"], git = "https://github.com/paritytech/substrate.git", branch = "master" } sp-runtime = { default-features = false, features = ["serde"], git = "https://github.com/paritytech/substrate.git", branch = "master" } sp-storage = { default-features = false, features = ["serde"], git = "https://github.com/paritytech/substrate.git", branch = "master" } +scale-bits = { default-features = false, features = ["scale-info"], git = "https://github.com/haerdib/scale-bits.git", branch = "bh/no-std" } # need to add this for `no_std` sp-application-crypto = { default-features = false, git = "https://github.com/paritytech/substrate.git", features = ["full_crypto"], branch = "master" } sp-runtime-interface = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "master" } + + [features] default = ["std"] # To support `no_std` builds in non-32 bit environments. diff --git a/node-api/src/lib.rs b/node-api/src/lib.rs index 368f15d77..1c343e664 100644 --- a/node-api/src/lib.rs +++ b/node-api/src/lib.rs @@ -14,6 +14,7 @@ //! Contains stuff to instantiate communication with a substrate node. #![cfg_attr(not(feature = "std"), no_std)] +#![feature(error_in_core)] extern crate alloc; @@ -28,6 +29,7 @@ pub mod decoder; pub mod error; pub mod events; pub mod metadata; +pub mod scale_decode; pub mod storage; #[cfg(any(feature = "mocks", test))] diff --git a/node-api/src/scale_decode/error/context.rs b/node-api/src/scale_decode/error/context.rs new file mode 100644 index 000000000..9080371ca --- /dev/null +++ b/node-api/src/scale_decode/error/context.rs @@ -0,0 +1,100 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module provides a [`Context`] type, which tracks the path +//! that we're attempting to encode to aid in error reporting. + +use alloc::{borrow::Cow, vec::Vec}; + +/// A cheaply clonable opaque context which allows us to track the current +/// location into a type that we're trying to encode, to aid in +/// error reporting. +#[derive(Clone, Default, Debug)] +pub struct Context { + path: Vec, +} + +impl Context { + /// Construct a new, empty context. + pub fn new() -> Context { + Default::default() + } + /// Return a new context with the given location appended. + pub fn push(&mut self, loc: Location) { + self.path.push(loc); + } + /// Return the current path. + pub fn path(&self) -> Path<'_> { + Path(Cow::Borrowed(&self.path)) + } +} + +/// The current path that we're trying to encode. +pub struct Path<'a>(Cow<'a, Vec>); + +impl<'a> Path<'a> { + /// Cheaply convert the path to an owned version. + pub fn into_owned(self) -> Path<'static> { + Path(Cow::Owned(self.0.into_owned())) + } + /// Return each location visited, oldest first + pub fn locations(&self) -> impl Iterator { + self.0.iter() + } +} + +impl<'a> core::fmt::Display for Path<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + for (idx, loc) in self.0.iter().enumerate() { + if idx != 0 { + f.write_str(".")?; + } + match &loc.inner { + Loc::Field(name) => f.write_str(name)?, + Loc::Index(i) => write!(f, "[{i}]")?, + Loc::Variant(name) => write!(f, "({name})")?, + } + } + Ok(()) + } +} + +/// Some location, like a field, variant or index in an array. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Location { + inner: Loc, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +enum Loc { + Field(Cow<'static, str>), + Index(usize), + Variant(Cow<'static, str>), +} + +impl Location { + /// This represents some struct field. + pub fn field(name: impl Into>) -> Self { + Location { inner: Loc::Field(name.into()) } + } + /// This represents some variant name. + pub fn variant(name: impl Into>) -> Self { + Location { inner: Loc::Variant(name.into()) } + } + /// This represents a tuple or array index. + pub fn idx(i: usize) -> Self { + Location { inner: Loc::Index(i) } + } +} diff --git a/node-api/src/scale_decode/error/mod.rs b/node-api/src/scale_decode/error/mod.rs new file mode 100644 index 000000000..98b6660fd --- /dev/null +++ b/node-api/src/scale_decode/error/mod.rs @@ -0,0 +1,151 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! An error that is emitted whenever some decoding fails. +mod context; + +use crate::scale_decode::visitor::DecodeError; +use alloc::{borrow::Cow, boxed::Box, string::String, vec::Vec}; +use core::fmt::Display; + +pub use context::{Context, Location}; +use sp_core::RuntimeDebug; + +/// An error produced while attempting to decode some type. +#[derive(Debug)] +pub struct Error { + context: Context, + kind: ErrorKind, +} + +impl Error { + /// Construct a new error given an error kind. + pub fn new(kind: ErrorKind) -> Error { + Error { context: Context::new(), kind } + } + /// Construct a new, custom error. + pub fn custom(error: impl Into) -> Error { + Error::new(ErrorKind::Custom(error.into())) + } + /// Retrieve more information about what went wrong. + pub fn kind(&self) -> &ErrorKind { + &self.kind + } + /// Retrieve details about where the error occurred. + pub fn context(&self) -> &Context { + &self.context + } + /// Give some context to the error. + pub fn at(mut self, loc: Location) -> Self { + self.context.push(loc); + Error { context: self.context, kind: self.kind } + } + /// Note which sequence index the error occurred in. + pub fn at_idx(mut self, idx: usize) -> Self { + self.context.push(Location::idx(idx)); + Error { context: self.context, kind: self.kind } + } + /// Note which field the error occurred in. + pub fn at_field(mut self, field: impl Into>) -> Self { + self.context.push(Location::field(field)); + Error { context: self.context, kind: self.kind } + } + /// Note which variant the error occurred in. + pub fn at_variant(mut self, variant: impl Into>) -> Self { + self.context.push(Location::variant(variant)); + Error { context: self.context, kind: self.kind } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let path = self.context.path(); + let kind = &self.kind; + write!(f, "Error at {path}: {kind:?}") + } +} + +impl From for Error { + fn from(err: DecodeError) -> Error { + Error::new(err.into()) + } +} + +/// The underlying nature of the error. +#[derive(RuntimeDebug)] +pub enum ErrorKind { + /// Something went wrong decoding the bytes based on the type + /// and type registry provided. + VisitorDecodeError(DecodeError), + /// We cannot decode the number seen into the target type; it's out of range. + NumberOutOfRange { + /// A string representation of the numeric value that was out of range. + value: String, + }, + /// We cannot find the variant we're trying to decode from in the target type. + CannotFindVariant { + /// The variant that we are given back from the encoded bytes. + got: String, + /// The possible variants that we can decode into. + expected: Vec<&'static str>, + }, + /// The types line up, but the expected length of the target type is different from the length of the input value. + WrongLength { + /// Length of the type we are trying to decode from + actual_len: usize, + /// Length fo the type we're trying to decode into + expected_len: usize, + }, + /// Cannot find a field that we need to decode to our target type + CannotFindField { + /// Name of the field which was not provided. + name: String, + }, + /// A custom error + Custom(CustomError), +} + +impl From for ErrorKind { + fn from(err: DecodeError) -> ErrorKind { + ErrorKind::VisitorDecodeError(err) + } +} + +impl From for ErrorKind { + fn from(err: CustomError) -> ErrorKind { + ErrorKind::Custom(err) + } +} + +type CustomError = Box; + +#[cfg(test)] +mod test { + use super::*; + use derive_more::Display; + + #[derive(Debug, Display)] + enum MyError { + Foo, + } + + impl core::error::Error for MyError {} + + #[test] + fn custom_error() { + // Just a compile-time check that we can ergonomically provide an arbitrary custom error: + Error::custom(MyError::Foo); + } +} diff --git a/node-api/src/scale_decode/impls/mod.rs b/node-api/src/scale_decode/impls/mod.rs new file mode 100644 index 000000000..9cc8b4c32 --- /dev/null +++ b/node-api/src/scale_decode/impls/mod.rs @@ -0,0 +1,1113 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cfg(feature = "primitive-types")] +mod primitive_types; + +use crate::scale_decode::{ + error::{Error, ErrorKind}, + visitor::{ + self, decode_with_visitor, types::*, DecodeAsTypeResult, DecodeItemIterator, TypeId, + Visitor, + }, + DecodeAsFields, FieldIter, IntoVisitor, +}; +use alloc::{ + borrow::{Cow, ToOwned}, + boxed::Box, + string::{String, ToString}, + vec, + vec::Vec, +}; +use codec::Compact; +use core::num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16, + NonZeroU32, NonZeroU64, NonZeroU8, +}; +use scale_bits::Bits; + +use alloc::{ + collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque}, + rc::Rc, + sync::Arc, +}; +use core::{ + marker::PhantomData, + ops::{Range, RangeInclusive}, + time::Duration, +}; + +pub struct BasicVisitor { + _marker: core::marker::PhantomData, +} + +/// Generate an [`IntoVisitor`] impl for basic types `T` where `BasicVisitor` impls `Visitor`. +macro_rules! impl_into_visitor { + ($ty:ident $(< $($lt:lifetime,)* $($param:ident),* >)? $(where $( $where:tt )* )?) => { + impl $(< $($lt,)* $($param),* >)? crate::scale_decode::IntoVisitor for $ty $(< $($lt,)* $($param),* >)? + where + BasicVisitor<$ty $(< $($lt,)* $($param),* >)?>: for<'scale, 'info> Visitor = Self>, + $( $($where)* )? + { + type Visitor = BasicVisitor<$ty $(< $($lt,)* $($param),* >)?>; + fn into_visitor() -> Self::Visitor { + BasicVisitor { _marker: core::marker::PhantomData } + } + } + }; +} + +/// Ignore single-field tuples/composites and visit the single field inside instead. +macro_rules! visit_single_field_composite_tuple_impls { + () => { + fn visit_composite<'scale, 'info>( + self, + value: &mut $crate::scale_decode::visitor::types::Composite<'scale, 'info>, + _type_id: $crate::scale_decode::visitor::TypeId, + ) -> Result, Self::Error> { + if value.remaining() != 1 { + return self.visit_unexpected($crate::scale_decode::visitor::Unexpected::Composite) + } + value.decode_item(self).unwrap() + } + fn visit_tuple<'scale, 'info>( + self, + value: &mut $crate::scale_decode::visitor::types::Tuple<'scale, 'info>, + _type_id: $crate::scale_decode::visitor::TypeId, + ) -> Result, Self::Error> { + if value.remaining() != 1 { + return self.visit_unexpected($crate::scale_decode::visitor::Unexpected::Tuple) + } + value.decode_item(self).unwrap() + } + }; +} + +impl Visitor for BasicVisitor { + type Error = Error; + type Value<'scale, 'info> = char; + + fn visit_char<'scale, 'info>( + self, + value: char, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + Ok(value) + } + visit_single_field_composite_tuple_impls!(); +} +impl_into_visitor!(char); + +impl Visitor for BasicVisitor { + type Error = Error; + type Value<'scale, 'info> = bool; + + fn visit_bool<'scale, 'info>( + self, + value: bool, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + Ok(value) + } + visit_single_field_composite_tuple_impls!(); +} +impl_into_visitor!(bool); + +impl Visitor for BasicVisitor { + type Error = Error; + type Value<'scale, 'info> = String; + + fn visit_str<'scale, 'info>( + self, + value: &mut Str<'scale>, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + let s = value.as_str()?.to_owned(); + Ok(s) + } + visit_single_field_composite_tuple_impls!(); +} +impl_into_visitor!(String); + +impl Visitor for BasicVisitor { + type Error = Error; + type Value<'scale, 'info> = Bits; + + fn visit_bitsequence<'scale, 'info>( + self, + value: &mut BitSequence<'scale>, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + value + .decode()? + .collect::>() + .map_err(|e| Error::new(ErrorKind::VisitorDecodeError(e.into()))) + } + visit_single_field_composite_tuple_impls!(); +} +impl_into_visitor!(Bits); + +impl Visitor for BasicVisitor> { + type Error = Error; + type Value<'scale, 'info> = PhantomData; + + fn visit_tuple<'scale, 'info>( + self, + value: &mut Tuple<'scale, 'info>, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + if value.remaining() == 0 { + Ok(PhantomData) + } else { + self.visit_unexpected(visitor::Unexpected::Tuple) + } + } + fn visit_composite<'scale, 'info>( + self, + value: &mut Composite<'scale, 'info>, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + if value.remaining() == 0 { + Ok(PhantomData) + } else { + self.visit_unexpected(visitor::Unexpected::Composite) + } + } +} +impl_into_visitor!(PhantomData); + +// Generate impls to encode things based on some other type. We do this by implementing +// `IntoVisitor` and using the `AndThen` combinator to map from an existing one to the desired output. +macro_rules! impl_into_visitor_like { + ($target:ident $(< $($lt:lifetime,)* $($param:ident),* >)? as $source:ty $( [where $($where:tt)*] )?: $mapper:expr) => { + impl $(< $($lt,)* $($param),* >)? Visitor for BasicVisitor<$target $(< $($lt,)* $($param),* >)?> + where + $source: IntoVisitor, + $( $($where)* )? + { + type Value<'scale, 'info> = $target $(< $($lt,)* $($param),* >)?; + type Error = <<$source as IntoVisitor>::Visitor as Visitor>::Error; + + fn unchecked_decode_as_type<'scale, 'info>( + self, + input: &mut &'scale [u8], + type_id: TypeId, + types: &'info scale_info::PortableRegistry, + ) -> DecodeAsTypeResult, Self::Error>> { + // Use the source visitor to decode into some type: + let inner_res = decode_with_visitor(input, type_id.0, types, <$source>::into_visitor()); + // map this type into our desired output and return it: + let res = inner_res.map($mapper); + DecodeAsTypeResult::Decoded(res) + } + } + impl_into_visitor!($target $(< $($lt,)* $($param),* >)?); + } +} + +impl_into_visitor_like!(Compact as T: |res| Compact(res)); +impl_into_visitor_like!(Arc as T: |res| Arc::new(res)); +impl_into_visitor_like!(Rc as T: |res| Rc::new(res)); +impl_into_visitor_like!(Box as T: |res| Box::new(res)); +impl_into_visitor_like!(Duration as (u64, u32): |res: (u64,u32)| Duration::from_secs(res.0) + Duration::from_nanos(res.1 as u64)); +impl_into_visitor_like!(Range as (T, T): |res: (T,T)| res.0..res.1); +impl_into_visitor_like!(RangeInclusive as (T, T): |res: (T,T)| res.0..=res.1); + +// A custom implementation for `Cow` because it's rather tricky; the visitor we want is whatever the +// `ToOwned` value for the Cow is, and Cow's have specific constraints, too. +impl<'a, T> Visitor for BasicVisitor> +where + T: 'a + ToOwned + ?Sized, + ::Owned: IntoVisitor, +{ + type Value<'scale, 'info> = Cow<'a, T>; + type Error = <<::Owned as IntoVisitor>::Visitor as Visitor>::Error; + + fn unchecked_decode_as_type<'scale, 'info>( + self, + input: &mut &'scale [u8], + type_id: TypeId, + types: &'info scale_info::PortableRegistry, + ) -> DecodeAsTypeResult, Self::Error>> { + // Use the ToOwned visitor to decode into some type: + let inner_res = + decode_with_visitor(input, type_id.0, types, <::Owned>::into_visitor()); + // map this type into our owned Cow to return: + let res = inner_res.map(Cow::Owned); + DecodeAsTypeResult::Decoded(res) + } +} +impl<'a, T> IntoVisitor for Cow<'a, T> +where + T: 'a + ToOwned + ?Sized, + ::Owned: IntoVisitor, +{ + type Visitor = BasicVisitor>; + fn into_visitor() -> Self::Visitor { + BasicVisitor { _marker: core::marker::PhantomData } + } +} + +macro_rules! impl_decode_seq_via_collect { + ($ty:ident<$generic:ident> $(where $($where:tt)*)?) => { + impl <$generic> Visitor for BasicVisitor<$ty<$generic>> + where + $generic: IntoVisitor, + Error: From<<$generic::Visitor as Visitor>::Error>, + $( $($where)* )? + { + type Value<'scale, 'info> = $ty<$generic>; + type Error = Error; + + fn visit_sequence<'scale, 'info>( + self, + value: &mut Sequence<'scale, 'info>, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + decode_items_using::<_, $generic>(value).collect() + } + fn visit_array<'scale, 'info>( + self, + value: &mut Array<'scale, 'info>, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + decode_items_using::<_, $generic>(value).collect() + } + + visit_single_field_composite_tuple_impls!(); + } + impl_into_visitor!($ty < $generic > $( where $($where)* )?); + } +} +impl_decode_seq_via_collect!(Vec); +impl_decode_seq_via_collect!(VecDeque); +impl_decode_seq_via_collect!(LinkedList); +impl_decode_seq_via_collect!(BinaryHeap where T: Ord); +impl_decode_seq_via_collect!(BTreeSet where T: Ord); + +// For arrays of fixed lengths, we decode to a vec first and then try to turn that into the fixed size array. +// Like vecs, we can decode from tuples, sequences or arrays if the types line up ok. +macro_rules! array_method_impl { + ($value:ident, [$t:ident; $n:ident]) => {{ + let val = decode_items_using::<_, $t>($value).collect::, _>>()?; + let actual_len = val.len(); + let arr = val + .try_into() + .map_err(|_e| Error::new(ErrorKind::WrongLength { actual_len, expected_len: $n }))?; + Ok(arr) + }}; +} +impl Visitor for BasicVisitor<[T; N]> +where + T: IntoVisitor, + Error: From<::Error>, +{ + type Value<'scale, 'info> = [T; N]; + type Error = Error; + + fn visit_sequence<'scale, 'info>( + self, + value: &mut Sequence<'scale, 'info>, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + array_method_impl!(value, [T; N]) + } + fn visit_array<'scale, 'info>( + self, + value: &mut Array<'scale, 'info>, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + array_method_impl!(value, [T; N]) + } + + visit_single_field_composite_tuple_impls!(); +} +impl IntoVisitor for [T; N] +where + T: IntoVisitor, + Error: From<::Error>, +{ + type Visitor = BasicVisitor<[T; N]>; + fn into_visitor() -> Self::Visitor { + BasicVisitor { _marker: core::marker::PhantomData } + } +} + +impl Visitor for BasicVisitor> +where + T: IntoVisitor, + Error: From<::Error>, +{ + type Error = Error; + type Value<'scale, 'info> = BTreeMap; + + fn visit_composite<'scale, 'info>( + self, + value: &mut Composite<'scale, 'info>, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + let mut map = BTreeMap::new(); + while value.remaining() > 0 { + // Get the name. If no name, skip over the corresponding value. + let Some(key) = value.peek_name() else { + value.decode_item(crate::scale_decode::visitor::IgnoreVisitor).transpose()?; + continue; + }; + // Decode the value now that we have a valid name. + let Some(val) = value.decode_item(T::into_visitor()) else { + break + }; + // Save to the map. + let val = val.map_err(|e| Error::from(e).at_field(key.to_owned()))?; + map.insert(key.to_owned(), val); + } + Ok(map) + } +} +impl_into_visitor!(BTreeMap); + +impl Visitor for BasicVisitor> +where + T: IntoVisitor, + Error: From<::Error>, +{ + type Error = Error; + type Value<'scale, 'info> = Option; + + fn visit_variant<'scale, 'info>( + self, + value: &mut Variant<'scale, 'info>, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + if value.name() == "Some" && value.fields().remaining() == 1 { + let val = value + .fields() + .decode_item(T::into_visitor()) + .transpose() + .map_err(|e| Error::from(e).at_variant("Some"))? + .expect("checked for 1 field already so should be ok"); + Ok(Some(val)) + } else if value.name() == "None" && value.fields().remaining() == 0 { + Ok(None) + } else { + Err(Error::new(ErrorKind::CannotFindVariant { + got: value.name().to_string(), + expected: vec!["Some", "None"], + })) + } + } + visit_single_field_composite_tuple_impls!(); +} +impl_into_visitor!(Option); + +impl Visitor for BasicVisitor> +where + T: IntoVisitor, + Error: From<::Error>, + E: IntoVisitor, + Error: From<::Error>, +{ + type Error = Error; + type Value<'scale, 'info> = Result; + + fn visit_variant<'scale, 'info>( + self, + value: &mut Variant<'scale, 'info>, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + if value.name() == "Ok" && value.fields().remaining() == 1 { + let val = value + .fields() + .decode_item(T::into_visitor()) + .transpose() + .map_err(|e| Error::from(e).at_variant("Ok"))? + .expect("checked for 1 field already so should be ok"); + Ok(Ok(val)) + } else if value.name() == "Err" && value.fields().remaining() == 1 { + let val = value + .fields() + .decode_item(E::into_visitor()) + .transpose() + .map_err(|e| Error::from(e).at_variant("Err"))? + .expect("checked for 1 field already so should be ok"); + Ok(Err(val)) + } else { + Err(Error::new(ErrorKind::CannotFindVariant { + got: value.name().to_string(), + expected: vec!["Ok", "Err"], + })) + } + } + visit_single_field_composite_tuple_impls!(); +} +impl_into_visitor!(Result); + +// Impl Visitor/DecodeAsType for all primitive number types +macro_rules! visit_number_fn_impl { + ($name:ident : $ty:ty where |$res:ident| $expr:expr) => { + fn $name<'scale, 'info>( + self, + value: $ty, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + let $res = value; + let n = $expr.ok_or_else(|| { + Error::new(ErrorKind::NumberOutOfRange { value: value.to_string() }) + })?; + Ok(n) + } + }; +} +macro_rules! visit_number_impl { + ($ty:ident where |$res:ident| $expr:expr) => { + #[allow(clippy::useless_conversion)] + impl Visitor for BasicVisitor<$ty> { + type Error = Error; + type Value<'scale, 'info> = $ty; + + visit_number_fn_impl!(visit_u8: u8 where |$res| $expr); + visit_number_fn_impl!(visit_u16: u16 where |$res| $expr); + visit_number_fn_impl!(visit_u32: u32 where |$res| $expr); + visit_number_fn_impl!(visit_u64: u64 where |$res| $expr); + visit_number_fn_impl!(visit_u128: u128 where |$res| $expr); + visit_number_fn_impl!(visit_i8: i8 where |$res| $expr); + visit_number_fn_impl!(visit_i16: i16 where |$res| $expr); + visit_number_fn_impl!(visit_i32: i32 where |$res| $expr); + visit_number_fn_impl!(visit_i64: i64 where |$res| $expr); + visit_number_fn_impl!(visit_i128: i128 where |$res| $expr); + + visit_single_field_composite_tuple_impls!(); + } + impl_into_visitor!($ty); + }; +} +visit_number_impl!(u8 where |res| res.try_into().ok()); +visit_number_impl!(u16 where |res| res.try_into().ok()); +visit_number_impl!(u32 where |res| res.try_into().ok()); +visit_number_impl!(u64 where |res| res.try_into().ok()); +visit_number_impl!(u128 where |res| res.try_into().ok()); +visit_number_impl!(usize where |res| res.try_into().ok()); +visit_number_impl!(i8 where |res| res.try_into().ok()); +visit_number_impl!(i16 where |res| res.try_into().ok()); +visit_number_impl!(i32 where |res| res.try_into().ok()); +visit_number_impl!(i64 where |res| res.try_into().ok()); +visit_number_impl!(i128 where |res| res.try_into().ok()); +visit_number_impl!(isize where |res| res.try_into().ok()); +visit_number_impl!(NonZeroU8 where |res| res.try_into().ok().and_then(NonZeroU8::new)); +visit_number_impl!(NonZeroU16 where |res| res.try_into().ok().and_then(NonZeroU16::new)); +visit_number_impl!(NonZeroU32 where |res| res.try_into().ok().and_then(NonZeroU32::new)); +visit_number_impl!(NonZeroU64 where |res| res.try_into().ok().and_then(NonZeroU64::new)); +visit_number_impl!(NonZeroU128 where |res| res.try_into().ok().and_then(NonZeroU128::new)); +visit_number_impl!(NonZeroI8 where |res| res.try_into().ok().and_then(NonZeroI8::new)); +visit_number_impl!(NonZeroI16 where |res| res.try_into().ok().and_then(NonZeroI16::new)); +visit_number_impl!(NonZeroI32 where |res| res.try_into().ok().and_then(NonZeroI32::new)); +visit_number_impl!(NonZeroI64 where |res| res.try_into().ok().and_then(NonZeroI64::new)); +visit_number_impl!(NonZeroI128 where |res| res.try_into().ok().and_then(NonZeroI128::new)); + +macro_rules! count_idents { + ($t:ident $($rest:ident)*) => { + 1 + count_idents!( $($rest)* ) + }; + () => { + 0 + } +} + +// Decode tuple types from any matching type. +macro_rules! tuple_method_impl { + (($($t:ident,)*), $value:ident) => {{ + const EXPECTED_LEN: usize = count_idents!($($t)*); + if $value.remaining() != EXPECTED_LEN { + return Err(Error::new(ErrorKind::WrongLength { + actual_len: $value.remaining(), + expected_len: EXPECTED_LEN + })) + } + + #[allow(unused)] + let mut idx = 0; + + Ok(( + $( + #[allow(unused_assignments)] + { + let v = $value + .decode_item($t::into_visitor()) + .transpose() + .map_err(|e| Error::from(e).at_idx(idx))? + .expect("length already checked via .remaining()"); + idx += 1; + v + } + ,)* + )) + }} +} +macro_rules! decode_inner_type_when_one_tuple_entry { + ($t:ident) => { + fn unchecked_decode_as_type<'scale, 'info>( + self, + input: &mut &'scale [u8], + type_id: TypeId, + types: &'info scale_info::PortableRegistry, + ) -> DecodeAsTypeResult, Self::Error>> { + // [jsdw]: See https://github.com/rust-lang/rustfmt/issues/5062. + // let else formatting not stable in macros; will keep indenting! + #[rustfmt::skip] + let Some(ty) = types.resolve(type_id.0) else { + return DecodeAsTypeResult::Skipped(self); + }; + + // Get the inner type ID if the thing we're trying to decode isn't + // a tuple or composite value. Else, fall back to default behaviour. + // This ensures that this function strictly improves on the default + // which would be to fail. + let inner_type_id = match &ty.type_def { + scale_info::TypeDef::Composite(_) => return DecodeAsTypeResult::Skipped(self), + scale_info::TypeDef::Tuple(_) => return DecodeAsTypeResult::Skipped(self), + _ => type_id.0, + }; + + let inner_res = decode_with_visitor(input, inner_type_id, types, <$t>::into_visitor()); + let res = inner_res.map(|val| (val,)).map_err(|e| e.into()); + DecodeAsTypeResult::Decoded(res) + } + }; + ($($tt:tt)*) => { + /* nothing */ + }; +} +macro_rules! impl_decode_tuple { + ($($t:ident)*) => { + impl < $($t),* > Visitor for BasicVisitor<($($t,)*)> + where $( + $t: IntoVisitor, + Error: From<<$t::Visitor as Visitor>::Error>, + )* + { + type Value<'scale, 'info> = ($($t,)*); + type Error = Error; + + // If we're trying to decode to a 1-tuple, and the type we're decoding + // isn't a tuple or composite, then decode thye inner type and add the tuple. + decode_inner_type_when_one_tuple_entry!($($t)*); + + fn visit_composite<'scale, 'info>( + self, + value: &mut Composite<'scale, 'info>, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + tuple_method_impl!(($($t,)*), value) + } + fn visit_tuple<'scale, 'info>( + self, + value: &mut Tuple<'scale, 'info>, + _type_id: visitor::TypeId, + ) -> Result, Self::Error> { + tuple_method_impl!(($($t,)*), value) + } + } + + // We can turn this tuple into a visitor which knows how to decode it: + impl < $($t),* > IntoVisitor for ($($t,)*) + where $( $t: IntoVisitor, Error: From<<$t::Visitor as Visitor>::Error>, )* + { + type Visitor = BasicVisitor<($($t,)*)>; + fn into_visitor() -> Self::Visitor { + BasicVisitor { _marker: core::marker::PhantomData } + } + } + + // We can decode given a list of fields (just delegate to the visitor impl: + impl < $($t),* > DecodeAsFields for ($($t,)*) + where $( $t: IntoVisitor, Error: From<<$t::Visitor as Visitor>::Error>, )* + { + fn decode_as_fields<'info>(input: &mut &[u8], fields: &mut dyn FieldIter<'info>, types: &'info scale_info::PortableRegistry) -> Result { + let mut composite = crate::scale_decode::visitor::types::Composite::new(input, crate::scale_decode::EMPTY_SCALE_INFO_PATH, fields, types); + let val = <($($t,)*)>::into_visitor().visit_composite(&mut composite, crate::scale_decode::visitor::TypeId(0)); + + // Skip over bytes that we decoded: + composite.skip_decoding()?; + *input = composite.bytes_from_undecoded(); + + val + } + } + } +} + +impl_decode_tuple!(); +impl_decode_tuple!(A); +impl_decode_tuple!(A B); +impl_decode_tuple!(A B C); +impl_decode_tuple!(A B C D); +impl_decode_tuple!(A B C D E); +impl_decode_tuple!(A B C D E F); +impl_decode_tuple!(A B C D E F G); +impl_decode_tuple!(A B C D E F G H); +impl_decode_tuple!(A B C D E F G H I); +impl_decode_tuple!(A B C D E F G H I J); +impl_decode_tuple!(A B C D E F G H I J K); +impl_decode_tuple!(A B C D E F G H I J K L); +impl_decode_tuple!(A B C D E F G H I J K L M); +impl_decode_tuple!(A B C D E F G H I J K L M N); +impl_decode_tuple!(A B C D E F G H I J K L M N O); +impl_decode_tuple!(A B C D E F G H I J K L M N O P); +impl_decode_tuple!(A B C D E F G H I J K L M N O P Q); +impl_decode_tuple!(A B C D E F G H I J K L M N O P Q R); +impl_decode_tuple!(A B C D E F G H I J K L M N O P Q R S); +impl_decode_tuple!(A B C D E F G H I J K L M N O P Q R S T); +// ^ Note: We make sure to support as many as parity-scale-codec's impls do. + +/// This takes anything that can decode a stream if items and return an iterator over them. +fn decode_items_using<'a, 'scale, 'info, D: DecodeItemIterator<'scale, 'info>, T>( + decoder: &'a mut D, +) -> impl Iterator> + 'a +where + T: IntoVisitor, + Error: From<::Error>, + D: DecodeItemIterator<'scale, 'info>, +{ + let mut idx = 0; + core::iter::from_fn(move || { + let item = decoder + .decode_item(T::into_visitor()) + .map(|res| res.map_err(|e| Error::from(e).at_idx(idx))); + idx += 1; + item + }) +} + +#[cfg(all(feature = "derive", feature = "primitive-types"))] +#[cfg(test)] +mod test { + use super::*; + use crate::{DecodeAsType, Field}; + use codec::Encode; + + /// Given a type definition, return type ID and registry representing it. + fn make_type() -> (u32, scale_info::PortableRegistry) { + let m = scale_info::MetaType::new::(); + let mut types = scale_info::Registry::new(); + let id = types.register_type(&m); + let portable_registry: scale_info::PortableRegistry = types.into(); + + (id.id, portable_registry) + } + + // For most of our tests, we'll assert that whatever type we encode, we can decode back again to the given type. + fn assert_encode_decode_to_with(a: &A, b: &B) + where + A: Encode, + B: DecodeAsType + PartialEq + std::fmt::Debug, + T: scale_info::TypeInfo + 'static, + { + let (type_id, types) = make_type::(); + let encoded = a.encode(); + let decoded = + B::decode_as_type(&mut &*encoded, type_id, &types).expect("should be able to decode"); + assert_eq!(&decoded, b); + } + + // Normally, the type info we want to use comes along with the type we're encoding. + fn assert_encode_decode_to(a: &A, b: &B) + where + A: Encode + scale_info::TypeInfo + 'static, + B: DecodeAsType + PartialEq + std::fmt::Debug, + { + assert_encode_decode_to_with::(a, b); + } + + // Most of the time we'll just make sure that we can encode and decode back to the same type. + fn assert_encode_decode_with(a: &A) + where + A: Encode + DecodeAsType + PartialEq + std::fmt::Debug, + T: scale_info::TypeInfo + 'static, + { + assert_encode_decode_to_with::(a, a) + } + + // Most of the time we'll just make sure that we can encode and decode back to the same type. + fn assert_encode_decode(a: &A) + where + A: Encode + scale_info::TypeInfo + 'static + DecodeAsType + PartialEq + std::fmt::Debug, + { + assert_encode_decode_to(a, a) + } + + // Test that something can be encoded and then DecodeAsFields will work to decode it again. + fn assert_encode_decode_as_fields(foo: Foo) + where + Foo: scale_info::TypeInfo + + DecodeAsFields + + PartialEq + + std::fmt::Debug + + codec::Encode + + 'static, + { + let foo_encoded = foo.encode(); + let foo_encoded_cursor = &mut &*foo_encoded; + + let (ty, types) = make_type::(); + + let new_foo = match &types.resolve(ty).unwrap().type_def { + scale_info::TypeDef::Composite(c) => { + let mut field_iter = + c.fields.iter().map(|f| Field::new(f.ty.id, f.name.as_deref())); + Foo::decode_as_fields(foo_encoded_cursor, &mut field_iter, &types).unwrap() + }, + scale_info::TypeDef::Tuple(t) => { + let mut field_iter = t.fields.iter().map(|f| Field::unnamed(f.id)); + Foo::decode_as_fields(foo_encoded_cursor, &mut field_iter, &types).unwrap() + }, + _ => { + panic!("Expected composite or tuple type def") + }, + }; + + assert_eq!(foo, new_foo); + assert_eq!( + foo_encoded_cursor.len(), + 0, + "leftover len when total was {}", + foo_encoded.len() + ); + } + + #[test] + fn decode_primitives() { + assert_encode_decode(&true); + assert_encode_decode(&false); + assert_encode_decode(&"hello".to_string()); + } + + #[test] + fn decode_pointer_types() { + assert_encode_decode_to(&true, &Box::new(true)); + assert_encode_decode_to(&true, &Arc::new(true)); + assert_encode_decode_to(&true, &Rc::new(true)); + assert_encode_decode_to(&true, &Cow::Borrowed(&true)); + } + + #[test] + fn decode_duration() { + assert_encode_decode_with::<(u64, u32), _>(&Duration::from_millis(12345)); + } + + #[test] + fn decode_ranges() { + assert_encode_decode(&(1..10)); + assert_encode_decode(&(1..=10)); + } + + #[test] + fn decode_basic_numbers() { + fn decode_all_types(n: u128) { + assert_encode_decode_to(&n, &(n as u8)); + assert_encode_decode_to(&n, &(n as u16)); + assert_encode_decode_to(&n, &(n as u32)); + assert_encode_decode_to(&n, &(n as u64)); + assert_encode_decode_to(&n, &n); + + assert_encode_decode_to(&n, &(n as i8)); + assert_encode_decode_to(&n, &(n as i16)); + assert_encode_decode_to(&n, &(n as i32)); + assert_encode_decode_to(&n, &(n as i64)); + assert_encode_decode_to(&n, &(n as i128)); + } + + decode_all_types(0); + decode_all_types(1); + decode_all_types(127); + } + + #[test] + fn decode_cows() { + let a = "hello"; + assert_encode_decode_to(&a, &Cow::<'_, str>::Borrowed(a)); + // Decoding a Cow means being able to jump into the inner composite type + // (Cow's are a one-field composite type in TypeInfo by the looks of it). + assert_encode_decode(&Cow::<'_, str>::Borrowed(a)); + } + + #[test] + fn decode_sequences() { + assert_encode_decode_to(&vec![1u8, 2, 3], &[1u8, 2, 3]); + assert_encode_decode_to(&vec![1u8, 2, 3], &vec![1u8, 2, 3]); + assert_encode_decode_to(&vec![1u8, 2, 3], &LinkedList::from_iter([1u8, 2, 3])); + assert_encode_decode_to(&vec![1u8, 2, 3], &VecDeque::from_iter([1u8, 2, 3])); + assert_encode_decode_to(&vec![1u8, 2, 3, 2], &BTreeSet::from_iter([1u8, 2, 3, 2])); + // assert_encode_decode_to(&vec![1u8,2,3], &BinaryHeap::from_iter([1u8,2,3])); // No partialEq for BinaryHeap + } + + #[test] + fn decode_types_via_tuples_or_composites() { + // Some type we know will be a composite type because we made it.. + #[derive(DecodeAsType, codec::Encode, scale_info::TypeInfo)] + #[decode_as_type(crate_path = "crate")] + struct Foo { + val: A, + } + + // Make our own enum just to check that it can be decoded through tuples etc too: + #[derive(DecodeAsType, codec::Encode, scale_info::TypeInfo, Debug, PartialEq, Clone)] + #[decode_as_type(crate_path = "crate")] + enum Wibble { + Bar(u64), + } + + fn check(a: A) + where + A: Encode + + scale_info::TypeInfo + + 'static + + DecodeAsType + + PartialEq + + std::fmt::Debug + + Clone, + { + let tup = ((a.clone(),),); + let struc = Foo { val: Foo { val: a.clone() } }; + + assert_encode_decode_to(&tup, &a); + assert_encode_decode_to(&struc, &a); + } + + // All of these types can be decoded through nested + // tuples or composite types that have exactly one field. + check(123u8); + check(123u16); + check(123u32); + check(123u64); + check(123u128); + check(123i8); + check(123i16); + check(123i32); + check(123i64); + check(123i128); + check(true); + check("hello".to_string()); + check(Bits::from_iter([true, false, true, true])); + check([1, 2, 3, 4, 5]); + check(vec![1, 2, 3, 4, 5]); + check(NonZeroU8::new(100).unwrap()); + check(Some(123)); + check(Ok::<_, bool>(123)); + check(Wibble::Bar(12345)); + } + + #[test] + fn decode_tuples() { + // Some struct with the same shape as our tuples. + #[derive(DecodeAsType, codec::Encode, scale_info::TypeInfo, Debug, PartialEq, Clone)] + #[decode_as_type(crate_path = "crate")] + struct Foo { + a: u8, + b: u16, + c: bool, + } + + // Decode to the same: + assert_encode_decode(&(1u8, 2u16, true)); + // Decode to struct of similar shape: + assert_encode_decode_to(&(1u8, 2u8, true), &Foo { a: 1u8, b: 2u16, c: true }); + // Decode from struct of similar shape: + assert_encode_decode_to(&Foo { a: 1u8, b: 2u16, c: true }, &(1u8, 2u8, true)); + } + + #[test] + fn decode_composites_to_tuples() { + #[derive(codec::Encode, scale_info::TypeInfo)] + struct Foo { + hello: bool, + other: (u8, u32), + } + + let input = Foo { hello: true, other: (1, 3) }; + // Same: + assert_encode_decode_to(&input, &(true, (1u8, 3u32))); + // Different: + assert_encode_decode_to(&input, &(true, (1u64, 3u64))); + } + + #[test] + fn decode_compacts() { + assert_encode_decode(&Compact(126u64)); + } + + #[test] + fn decode_options_and_results() { + // These are hardcoded so let's make sure they work.. + assert_encode_decode(&Some(123i128)); + assert_encode_decode(&(None as Option)); + assert_encode_decode(&Ok::<_, bool>(123i128)); + assert_encode_decode(&Err::(123i128)); + } + + #[test] + fn decode_bits() { + assert_encode_decode(&Bits::new()); + assert_encode_decode(&Bits::from_iter([true, false, false, true, false])); + } + + #[test] + #[cfg(feature = "primitive-types")] + fn decode_hxxx() { + use ::primitive_types::{H128, H160, H256, H384, H512, H768}; + + fn try_decode_hxxx(input: impl IntoIterator) { + let mut bytes: Vec = input.into_iter().collect(); + + macro_rules! check_ty { + ($bytes:expr, $bits:literal, $ty:ident) => { + while $bytes.len() < $bits / 8 { + $bytes.push(0) + } + assert_encode_decode(&$ty::from_slice(&$bytes)); + assert_encode_decode_to(&$ty::from_slice(&$bytes), &$bytes); + assert_encode_decode_to(&$bytes, &$ty::from_slice(&$bytes)); + }; + } + check_ty!(bytes, 128, H128); + check_ty!(bytes, 160, H160); + check_ty!(bytes, 256, H256); + check_ty!(bytes, 384, H384); + check_ty!(bytes, 512, H512); + check_ty!(bytes, 768, H768); + } + + try_decode_hxxx([0]); + try_decode_hxxx([1, 2, 3, 4]); + try_decode_hxxx([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + } + + #[test] + fn decoding_can_skip_named_struct_fields() { + #[derive(DecodeAsType, PartialEq, Debug)] + #[decode_as_type(crate_path = "crate")] + struct Foo { + some_field: u8, + value: u16, + #[decode_as_type(skip)] + some_field_to_skip: bool, + #[decode_as_type(skip)] + other_field_to_skip: usize, + } + + #[derive(codec::Encode, scale_info::TypeInfo)] + struct FooPartial { + some_field: u8, + value: u16, + } + + assert_encode_decode_to( + &FooPartial { some_field: 123, value: 456 }, + &Foo { + some_field: 123, + value: 456, + // fields will be defaulted if skipped: + some_field_to_skip: false, + other_field_to_skip: 0, + }, + ); + } + + #[test] + fn decoding_can_skip_unnamed_struct_fields() { + #[derive(DecodeAsType, PartialEq, Debug)] + #[decode_as_type(crate_path = "crate")] + struct Foo(u8, #[decode_as_type(skip)] bool, #[decode_as_type(skip)] usize); + + #[derive(codec::Encode, scale_info::TypeInfo)] + struct FooPartial { + some_field: u8, + } + + assert_encode_decode_to( + &FooPartial { some_field: 123 }, + &Foo( + 123, // fields will be defaulted if skipped: + false, 0, + ), + ); + } + + #[test] + fn decoding_can_skip_enum_variant_fields() { + #[derive(DecodeAsType, PartialEq, Debug)] + #[decode_as_type(crate_path = "crate")] + enum Foo { + NamedField { + some_field: u8, + #[decode_as_type(skip)] + some_field_to_skip: bool, + // the codec attr should work too: + #[codec(skip)] + another_field_to_skip: String, + value: u16, + }, + UnnamedField(bool, #[decode_as_type(skip)] usize, String), + } + + #[derive(codec::Encode, scale_info::TypeInfo)] + enum FooPartial { + NamedField { some_field: u8, value: u16 }, + UnnamedField(bool, String), + } + + assert_encode_decode_to( + &FooPartial::NamedField { some_field: 123, value: 456 }, + &Foo::NamedField { + some_field: 123, + some_field_to_skip: false, + another_field_to_skip: String::new(), + value: 456, + }, + ); + assert_encode_decode_to( + &FooPartial::UnnamedField(true, "hello".to_string()), + &Foo::UnnamedField(true, 0, "hello".to_string()), + ); + } + + #[test] + fn decode_as_fields_works() { + use std::fmt::Debug; + + #[derive(DecodeAsType, codec::Encode, PartialEq, Debug, scale_info::TypeInfo)] + #[decode_as_type(crate_path = "crate")] + struct Foo { + some_field: u8, + value: u16, + } + + assert_encode_decode_as_fields(Foo { some_field: 123, value: 456 }); + + #[derive(DecodeAsType, codec::Encode, PartialEq, Debug, scale_info::TypeInfo)] + #[decode_as_type(crate_path = "crate")] + struct Foo2(String, bool, u64); + + assert_encode_decode_as_fields(Foo2("hello".to_string(), true, 12345)); + + #[derive(DecodeAsType, codec::Encode, PartialEq, Debug, scale_info::TypeInfo)] + #[decode_as_type(crate_path = "crate")] + struct Foo3; + + assert_encode_decode_as_fields(Foo3); + + // Tuples should work, too: + assert_encode_decode_as_fields((true, 123u8, "hello".to_string())); + } +} diff --git a/node-api/src/scale_decode/impls/primitive_types.rs b/node-api/src/scale_decode/impls/primitive_types.rs new file mode 100644 index 000000000..fe9e5706a --- /dev/null +++ b/node-api/src/scale_decode/impls/primitive_types.rs @@ -0,0 +1,63 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::BasicVisitor; +use crate::{ + error::Error, + visitor::{decode_with_visitor, DecodeAsTypeResult, Visitor}, + IntoVisitor, +}; +use primitive_types::{H128, H160, H256, H384, H512, H768}; + +macro_rules! impl_visitor { + ($ty:ty: $len:literal) => { + impl Visitor for BasicVisitor<$ty> { + type Error = Error; + type Value<'scale, 'info> = $ty; + + fn unchecked_decode_as_type<'scale, 'info>( + self, + input: &mut &'scale [u8], + type_id: crate::scale_decode::visitor::TypeId, + types: &'info scale_info::PortableRegistry, + ) -> crate::scale_decode::visitor::DecodeAsTypeResult< + Self, + Result, Self::Error>, + > { + let res = decode_with_visitor( + input, + type_id.0, + types, + BasicVisitor::<[u8; $len / 8]> { _marker: std::marker::PhantomData }, + ) + .map(|res| <$ty>::from(res)); + DecodeAsTypeResult::Decoded(res) + } + } + + impl IntoVisitor for $ty { + type Visitor = BasicVisitor<$ty>; + fn into_visitor() -> Self::Visitor { + BasicVisitor { _marker: std::marker::PhantomData } + } + } + }; +} +impl_visitor!(H128: 128); +impl_visitor!(H160: 160); +impl_visitor!(H256: 256); +impl_visitor!(H384: 384); +impl_visitor!(H512: 512); +impl_visitor!(H768: 768); diff --git a/node-api/src/scale_decode/mod.rs b/node-api/src/scale_decode/mod.rs new file mode 100644 index 000000000..83bdf57e3 --- /dev/null +++ b/node-api/src/scale_decode/mod.rs @@ -0,0 +1,118 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use alloc::vec::Vec; + +mod impls; + +pub mod error; +pub mod visitor; + +pub use error::Error; +pub use visitor::{DecodeError, IgnoreVisitor, Visitor}; + +// Used in trait definitions. +pub use scale_info::PortableRegistry; + +/// This trait is implemented for any type `T` where `T` implements [`IntoVisitor`] and the errors returned +/// from this [`Visitor`] can be converted into [`Error`]. It's essentially a convenience wrapper around +/// [`visitor::decode_with_visitor`] that mirrors `scale-encode`'s `EncodeAsType`. +pub trait DecodeAsType: Sized { + /// Given some input bytes, a `type_id`, and type registry, attempt to decode said bytes into + /// `Self`. Implementations should modify the `&mut` reference to the bytes such that any bytes + /// not used in the course of decoding are still pointed to after decoding is complete. + fn decode_as_type( + input: &mut &[u8], + type_id: u32, + types: &PortableRegistry, + ) -> Result; +} + +impl DecodeAsType for T +where + T: IntoVisitor, + Error: From<::Error>, +{ + fn decode_as_type( + input: &mut &[u8], + type_id: u32, + types: &scale_info::PortableRegistry, + ) -> Result { + let res = visitor::decode_with_visitor(input, type_id, types, T::into_visitor())?; + Ok(res) + } +} + +/// This is similar to [`DecodeAsType`], except that it's instead implemented for types that can be given a list of +/// fields denoting the type being decoded from and attempt to do this decoding. This is generally implemented just +/// for tuple and struct types, and is automatically implemented via the [`macro@DecodeAsType`] macro. +pub trait DecodeAsFields: Sized { + /// Given some bytes and some fields denoting their structure, attempt to decode. + fn decode_as_fields<'info>( + input: &mut &[u8], + fields: &mut dyn FieldIter<'info>, + types: &'info PortableRegistry, + ) -> Result; +} + +/// A representation of a single field to be encoded via [`DecodeAsFields::decode_as_fields`]. +#[derive(Debug, Clone, Copy)] +pub struct Field<'a> { + name: Option<&'a str>, + id: u32, +} + +impl<'a> Field<'a> { + /// Construct a new field with an ID and optional name. + pub fn new(id: u32, name: Option<&'a str>) -> Self { + Field { id, name } + } + /// Create a new unnamed field. + pub fn unnamed(id: u32) -> Self { + Field { name: None, id } + } + /// Create a new named field. + pub fn named(id: u32, name: &'a str) -> Self { + Field { name: Some(name), id } + } + /// The field name, if any. + pub fn name(&self) -> Option<&'a str> { + self.name + } + /// The field ID. + pub fn id(&self) -> u32 { + self.id + } +} + +/// An iterator over a set of fields. +pub trait FieldIter<'a>: Iterator> {} +impl<'a, T> FieldIter<'a> for T where T: Iterator> {} + +/// This trait can be implemented on any type that has an associated [`Visitor`] responsible for decoding +/// SCALE encoded bytes to it. If you implement this on some type and the [`Visitor`] that you return has +/// an error type that converts into [`Error`], then you'll also get a [`DecodeAsType`] implementation for free. +pub trait IntoVisitor { + /// The visitor type used to decode SCALE encoded bytes to `Self`. + type Visitor: for<'scale, 'info> visitor::Visitor = Self>; + /// A means of obtaining this visitor. + fn into_visitor() -> Self::Visitor; +} + +// In a few places, we need an empty path with a lifetime that outlives 'info, +// so here's one that lives forever that we can use. +#[doc(hidden)] +pub static EMPTY_SCALE_INFO_PATH: &scale_info::Path = + &scale_info::Path { segments: Vec::new() }; diff --git a/node-api/src/scale_decode/visitor/decode.rs b/node-api/src/scale_decode/visitor/decode.rs new file mode 100644 index 000000000..81fcb5bbf --- /dev/null +++ b/node-api/src/scale_decode/visitor/decode.rs @@ -0,0 +1,359 @@ +// Copyright (C) 2022 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::scale_decode::{ + visitor::{ + Array, BitSequence, Compact, CompactLocation, Composite, DecodeAsTypeResult, DecodeError, + Sequence, Str, Tuple, TypeId, Variant, Visitor, + }, + Field, +}; +use codec::{self, Decode}; +use scale_info::{ + form::PortableForm, Path, PortableRegistry, TypeDef, TypeDefArray, TypeDefBitSequence, + TypeDefCompact, TypeDefComposite, TypeDefPrimitive, TypeDefSequence, TypeDefTuple, + TypeDefVariant, +}; + +/// Decode data according to the type ID and [`PortableRegistry`] provided. +/// The provided pointer to the data slice will be moved forwards as needed +/// depending on what was decoded, and a method on the provided [`Visitor`] +/// will be called depending on the type that needs to be decoded. +pub fn decode_with_visitor<'scale, 'info, V: Visitor>( + data: &mut &'scale [u8], + ty_id: u32, + types: &'info PortableRegistry, + visitor: V, +) -> Result, V::Error> { + // Provide option to "bail out" and do something custom first. + let visitor = match visitor.unchecked_decode_as_type(data, TypeId(ty_id), types) { + DecodeAsTypeResult::Decoded(r) => return r, + DecodeAsTypeResult::Skipped(v) => v, + }; + + let ty = types.resolve(ty_id).ok_or(DecodeError::TypeIdNotFound(ty_id))?; + let ty_id = TypeId(ty_id); + let path = &ty.path; + + match &ty.type_def { + TypeDef::Composite(inner) => + decode_composite_value(data, ty_id, path, inner, types, visitor), + TypeDef::Variant(inner) => decode_variant_value(data, ty_id, path, inner, types, visitor), + TypeDef::Sequence(inner) => decode_sequence_value(data, ty_id, inner, types, visitor), + TypeDef::Array(inner) => decode_array_value(data, ty_id, inner, types, visitor), + TypeDef::Tuple(inner) => decode_tuple_value(data, ty_id, inner, types, visitor), + TypeDef::Primitive(inner) => decode_primitive_value(data, ty_id, inner, visitor), + TypeDef::Compact(inner) => decode_compact_value(data, ty_id, inner, types, visitor), + TypeDef::BitSequence(inner) => + decode_bit_sequence_value(data, ty_id, inner, types, visitor), + } +} + +fn decode_composite_value<'scale, 'info, V: Visitor>( + data: &mut &'scale [u8], + ty_id: TypeId, + path: &'info Path, + ty: &'info TypeDefComposite, + types: &'info PortableRegistry, + visitor: V, +) -> Result, V::Error> { + let mut fields = ty.fields.iter().map(|f| Field::new(f.ty.id, f.name.as_deref())); + let mut items = Composite::new(data, path, &mut fields, types); + let res = visitor.visit_composite(&mut items, ty_id); + + // Skip over any bytes that the visitor chose not to decode: + items.skip_decoding()?; + *data = items.bytes_from_undecoded(); + + res +} + +fn decode_variant_value<'scale, 'info, V: Visitor>( + data: &mut &'scale [u8], + ty_id: TypeId, + path: &'info Path, + ty: &'info TypeDefVariant, + types: &'info PortableRegistry, + visitor: V, +) -> Result, V::Error> { + let mut variant = Variant::new(data, path, ty, types)?; + let res = visitor.visit_variant(&mut variant, ty_id); + + // Skip over any bytes that the visitor chose not to decode: + variant.skip_decoding()?; + *data = variant.bytes_from_undecoded(); + + res +} + +fn decode_sequence_value<'scale, 'info, V: Visitor>( + data: &mut &'scale [u8], + ty_id: TypeId, + ty: &'info TypeDefSequence, + types: &'info PortableRegistry, + visitor: V, +) -> Result, V::Error> { + let mut items = Sequence::new(data, ty.type_param.id, types)?; + let res = visitor.visit_sequence(&mut items, ty_id); + + // Skip over any bytes that the visitor chose not to decode: + items.skip_decoding()?; + *data = items.bytes_from_undecoded(); + + res +} + +fn decode_array_value<'scale, 'info, V: Visitor>( + data: &mut &'scale [u8], + ty_id: TypeId, + ty: &'info TypeDefArray, + types: &'info PortableRegistry, + visitor: V, +) -> Result, V::Error> { + let len = ty.len as usize; + let mut arr = Array::new(data, ty.type_param.id, len, types); + let res = visitor.visit_array(&mut arr, ty_id); + + // Skip over any bytes that the visitor chose not to decode: + arr.skip_decoding()?; + *data = arr.bytes_from_undecoded(); + + res +} + +fn decode_tuple_value<'scale, 'info, V: Visitor>( + data: &mut &'scale [u8], + ty_id: TypeId, + ty: &'info TypeDefTuple, + types: &'info PortableRegistry, + visitor: V, +) -> Result, V::Error> { + let mut fields = ty.fields.iter().map(|f| Field::unnamed(f.id)); + let mut items = Tuple::new(data, &mut fields, types); + let res = visitor.visit_tuple(&mut items, ty_id); + + // Skip over any bytes that the visitor chose not to decode: + items.skip_decoding()?; + *data = items.bytes_from_undecoded(); + + res +} + +fn decode_primitive_value<'scale, 'info, V: Visitor>( + data: &mut &'scale [u8], + ty_id: TypeId, + ty: &'info TypeDefPrimitive, + visitor: V, +) -> Result, V::Error> { + fn decode_32_bytes<'scale>(data: &mut &'scale [u8]) -> Result<&'scale [u8; 32], DecodeError> { + // Pull an array from the data if we can, preserving the lifetime. + let arr: &'scale [u8; 32] = match (*data).try_into() { + Ok(arr) => arr, + Err(_) => return Err(DecodeError::NotEnoughInput), + }; + // If this works out, remember to shift data 32 bytes forward. + *data = &data[32..]; + Ok(arr) + } + + match ty { + TypeDefPrimitive::Bool => { + let b = bool::decode(data).map_err(|e| e.into())?; + visitor.visit_bool(b, ty_id) + }, + TypeDefPrimitive::Char => { + // Treat chars as u32's + let val = u32::decode(data).map_err(|e| e.into())?; + let c = char::from_u32(val).ok_or(DecodeError::InvalidChar(val))?; + visitor.visit_char(c, ty_id) + }, + TypeDefPrimitive::Str => { + // Avoid allocating; don't decode into a String. instead, pull the bytes + // and let the visitor decide whether to use them or not. + let mut s = Str::new(data)?; + // Since we aren't decoding here, shift our bytes along to after the str: + *data = s.bytes_after(); + visitor.visit_str(&mut s, ty_id) + }, + TypeDefPrimitive::U8 => { + let n = u8::decode(data).map_err(|e| e.into())?; + visitor.visit_u8(n, ty_id) + }, + TypeDefPrimitive::U16 => { + let n = u16::decode(data).map_err(|e| e.into())?; + visitor.visit_u16(n, ty_id) + }, + TypeDefPrimitive::U32 => { + let n = u32::decode(data).map_err(|e| e.into())?; + visitor.visit_u32(n, ty_id) + }, + TypeDefPrimitive::U64 => { + let n = u64::decode(data).map_err(|e| e.into())?; + visitor.visit_u64(n, ty_id) + }, + TypeDefPrimitive::U128 => { + let n = u128::decode(data).map_err(|e| e.into())?; + visitor.visit_u128(n, ty_id) + }, + TypeDefPrimitive::U256 => { + let arr = decode_32_bytes(data)?; + visitor.visit_u256(arr, ty_id) + }, + TypeDefPrimitive::I8 => { + let n = i8::decode(data).map_err(|e| e.into())?; + visitor.visit_i8(n, ty_id) + }, + TypeDefPrimitive::I16 => { + let n = i16::decode(data).map_err(|e| e.into())?; + visitor.visit_i16(n, ty_id) + }, + TypeDefPrimitive::I32 => { + let n = i32::decode(data).map_err(|e| e.into())?; + visitor.visit_i32(n, ty_id) + }, + TypeDefPrimitive::I64 => { + let n = i64::decode(data).map_err(|e| e.into())?; + visitor.visit_i64(n, ty_id) + }, + TypeDefPrimitive::I128 => { + let n = i128::decode(data).map_err(|e| e.into())?; + visitor.visit_i128(n, ty_id) + }, + TypeDefPrimitive::I256 => { + let arr = decode_32_bytes(data)?; + visitor.visit_i256(arr, ty_id) + }, + } +} + +fn decode_compact_value<'scale, 'info, V: Visitor>( + data: &mut &'scale [u8], + ty_id: TypeId, + ty: &'info TypeDefCompact, + types: &'info PortableRegistry, + visitor: V, +) -> Result, V::Error> { + #[allow(clippy::too_many_arguments)] + fn decode_compact<'scale, 'info, V: Visitor>( + data: &mut &'scale [u8], + outermost_ty_id: TypeId, + current_type_id: TypeId, + mut locations: smallvec::SmallVec<[CompactLocation<'info>; 8]>, + inner: &'info scale_info::Type, + types: &'info PortableRegistry, + visitor: V, + ) -> Result, V::Error> { + use TypeDefPrimitive::*; + match &inner.type_def { + // It's obvious how to decode basic primitive unsigned types, since we have impls for them. + TypeDef::Primitive(U8) => { + locations.push(CompactLocation::Primitive(current_type_id)); + let n = codec::Compact::::decode(data).map_err(|e| e.into())?.0; + let c = Compact::new(n, locations.as_slice()); + visitor.visit_compact_u8(c, outermost_ty_id) + }, + TypeDef::Primitive(U16) => { + locations.push(CompactLocation::Primitive(current_type_id)); + let n = codec::Compact::::decode(data).map_err(|e| e.into())?.0; + let c = Compact::new(n, locations.as_slice()); + visitor.visit_compact_u16(c, outermost_ty_id) + }, + TypeDef::Primitive(U32) => { + locations.push(CompactLocation::Primitive(current_type_id)); + let n = codec::Compact::::decode(data).map_err(|e| e.into())?.0; + let c = Compact::new(n, locations.as_slice()); + visitor.visit_compact_u32(c, outermost_ty_id) + }, + TypeDef::Primitive(U64) => { + locations.push(CompactLocation::Primitive(current_type_id)); + let n = codec::Compact::::decode(data).map_err(|e| e.into())?.0; + let c = Compact::new(n, locations.as_slice()); + visitor.visit_compact_u64(c, outermost_ty_id) + }, + TypeDef::Primitive(U128) => { + locations.push(CompactLocation::Primitive(current_type_id)); + let n = codec::Compact::::decode(data).map_err(|e| e.into())?.0; + let c = Compact::new(n, locations.as_slice()); + visitor.visit_compact_u128(c, outermost_ty_id) + }, + // A struct with exactly 1 field containing one of the above types can be sensibly compact encoded/decoded. + TypeDef::Composite(composite) => { + if composite.fields.len() != 1 { + return Err(DecodeError::CannotDecodeCompactIntoType(inner.clone()).into()) + } + + // What type is the 1 field that we are able to decode? + let field = &composite.fields[0]; + + // Record this composite location. + match &field.name { + Some(name) => + locations.push(CompactLocation::NamedComposite(current_type_id, name)), + None => locations.push(CompactLocation::UnnamedComposite(current_type_id)), + } + + let field_type_id = field.ty.id; + let inner_ty = types + .resolve(field_type_id) + .ok_or(DecodeError::TypeIdNotFound(field_type_id))?; + + // Decode this inner type via compact decoding. This can recurse, in case + // the inner type is also a 1-field composite type. + decode_compact( + data, + outermost_ty_id, + TypeId(field_type_id), + locations, + inner_ty, + types, + visitor, + ) + }, + // For now, we give up if we have been asked for any other type: + _cannot_decode_from => + Err(DecodeError::CannotDecodeCompactIntoType(inner.clone()).into()), + } + } + + // The type ID of the thing encoded into a Compact type. + let inner_ty_id = ty.type_param.id; + + // Attempt to compact-decode this inner type. + let inner = types.resolve(inner_ty_id).ok_or(DecodeError::TypeIdNotFound(inner_ty_id))?; + + // Track any inner type IDs we encounter. + let locations = smallvec::SmallVec::<[CompactLocation; 8]>::new(); + + decode_compact(data, ty_id, TypeId(inner_ty_id), locations, inner, types, visitor) +} + +fn decode_bit_sequence_value<'scale, 'info, V: Visitor>( + data: &mut &'scale [u8], + ty_id: TypeId, + ty: &'info TypeDefBitSequence, + types: &'info PortableRegistry, + visitor: V, +) -> Result, V::Error> { + use scale_bits::Format; + + let format = Format::from_metadata(ty, types).map_err(DecodeError::BitSequenceError)?; + let mut bitseq = BitSequence::new(format, data); + let res = visitor.visit_bitsequence(&mut bitseq, ty_id); + + // Move to the bytes after the bit sequence. + *data = bitseq.bytes_after()?; + + res +} diff --git a/node-api/src/scale_decode/visitor/mod.rs b/node-api/src/scale_decode/visitor/mod.rs new file mode 100644 index 000000000..669fcf291 --- /dev/null +++ b/node-api/src/scale_decode/visitor/mod.rs @@ -0,0 +1,1051 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The [`Visitor`] trait and associated types. + +mod decode; +pub mod types; + +use scale_info::form::PortableForm; +use types::*; + +pub use decode::decode_with_visitor; + +/// An implementation of the [`Visitor`] trait can be passed to the [`decode_with_visitor()`] +/// function, and is handed back values as they are encountered. It's up to the implementation +/// to decide what to do with these values. +pub trait Visitor: Sized { + /// The type of the value to hand back from the [`decode_with_visitor()`] function. + type Value<'scale, 'info>; + /// The error type (which we must be able to convert a combination of [`Self`] and [`DecodeError`]s + /// into, to handle any internal errors that crop up trying to decode things). + type Error: From; + + /// This method is called immediately upon running [`decode_with_visitor()`]. By default we ignore + /// this call and return our visitor back (ie [`DecodeAsTypeResult::Skipped(visitor)`]). If you choose to + /// do some decoding at this stage, return [`DecodeAsTypeResult::Decoded(result)`]. In either case, any bytes + /// that you consume from the input (by altering what it points to) will be consumed for any subsequent visiting. + /// + /// # Warning + /// + /// Unlike the other `visit_*` methods, it is completely up to the implementor to decode and advance the + /// bytes in a sensible way, and thus also possible for the implementor to screw this up. As a result, + /// it's suggested that you don't implement this unless you know what you're doing. + fn unchecked_decode_as_type<'scale, 'info>( + self, + _input: &mut &'scale [u8], + _type_id: TypeId, + _types: &'info scale_info::PortableRegistry, + ) -> DecodeAsTypeResult, Self::Error>> { + DecodeAsTypeResult::Skipped(self) + } + + /// This is called when a visitor function that you've not provided an implementation is called. + /// You are provided an enum value corresponding to the function call, and can decide what to return + /// in this case. The default is to return an error to announce the unexpected value. + fn visit_unexpected<'scale, 'info>( + self, + unexpected: Unexpected, + ) -> Result, Self::Error> { + Err(DecodeError::Unexpected(unexpected).into()) + } + + /// Called when a bool is seen in the input bytes. + fn visit_bool<'scale, 'info>( + self, + _value: bool, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::Bool) + } + /// Called when a char is seen in the input bytes. + fn visit_char<'scale, 'info>( + self, + _value: char, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::Char) + } + /// Called when a u8 is seen in the input bytes. + fn visit_u8<'scale, 'info>( + self, + _value: u8, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::U8) + } + /// Called when a u16 is seen in the input bytes. + fn visit_u16<'scale, 'info>( + self, + _value: u16, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::U16) + } + /// Called when a u32 is seen in the input bytes. + fn visit_u32<'scale, 'info>( + self, + _value: u32, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::U32) + } + /// Called when a u64 is seen in the input bytes. + fn visit_u64<'scale, 'info>( + self, + _value: u64, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::U64) + } + /// Called when a u128 is seen in the input bytes. + fn visit_u128<'scale, 'info>( + self, + _value: u128, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::U128) + } + /// Called when a u256 is seen in the input bytes. + fn visit_u256<'info>( + self, + _value: &'_ [u8; 32], + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::U256) + } + /// Called when an i8 is seen in the input bytes. + fn visit_i8<'scale, 'info>( + self, + _value: i8, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::I8) + } + /// Called when an i16 is seen in the input bytes. + fn visit_i16<'scale, 'info>( + self, + _value: i16, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::I16) + } + /// Called when an i32 is seen in the input bytes. + fn visit_i32<'scale, 'info>( + self, + _value: i32, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::I32) + } + /// Called when an i64 is seen in the input bytes. + fn visit_i64<'scale, 'info>( + self, + _value: i64, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::I64) + } + /// Called when an i128 is seen in the input bytes. + fn visit_i128<'scale, 'info>( + self, + _value: i128, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::I128) + } + /// Called when an i256 is seen in the input bytes. + fn visit_i256<'info>( + self, + _value: &'_ [u8; 32], + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::I256) + } + /// Called when a sequence of values is seen in the input bytes. + fn visit_sequence<'scale, 'info>( + self, + _value: &mut Sequence<'scale, 'info>, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::Sequence) + } + /// Called when a composite value is seen in the input bytes. + fn visit_composite<'scale, 'info>( + self, + _value: &mut Composite<'scale, 'info>, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::Composite) + } + /// Called when a tuple of values is seen in the input bytes. + fn visit_tuple<'scale, 'info>( + self, + _value: &mut Tuple<'scale, 'info>, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::Tuple) + } + /// Called when a string value is seen in the input bytes. + fn visit_str<'scale, 'info>( + self, + _value: &mut Str<'scale>, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::Str) + } + /// Called when a variant is seen in the input bytes. + fn visit_variant<'scale, 'info>( + self, + _value: &mut Variant<'scale, 'info>, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::Variant) + } + /// Called when an array is seen in the input bytes. + fn visit_array<'scale, 'info>( + self, + _value: &mut Array<'scale, 'info>, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::Array) + } + /// Called when a bit sequence is seen in the input bytes. + fn visit_bitsequence<'scale, 'info>( + self, + _value: &mut BitSequence<'scale>, + _type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_unexpected(Unexpected::Bitsequence) + } + + // Default implementations for visiting compact values just delegate and + // ignore the compactness, but they are here if decoders would like to know + // that the thing was compact encoded: + + /// Called when a compact encoded u8 is seen in the input bytes. + fn visit_compact_u8<'scale, 'info>( + self, + value: Compact, + type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_u8(value.value(), type_id) + } + /// Called when a compact encoded u16 is seen in the input bytes. + fn visit_compact_u16<'scale, 'info>( + self, + value: Compact, + type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_u16(value.value(), type_id) + } + /// Called when a compact encoded u32 is seen in the input bytes. + fn visit_compact_u32<'scale, 'info>( + self, + value: Compact, + type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_u32(value.value(), type_id) + } + /// Called when a compact encoded u64 is seen in the input bytes. + fn visit_compact_u64<'scale, 'info>( + self, + value: Compact, + type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_u64(value.value(), type_id) + } + /// Called when a compact encoded u128 is seen in the input bytes. + fn visit_compact_u128<'scale, 'info>( + self, + value: Compact, + type_id: TypeId, + ) -> Result, Self::Error> { + self.visit_u128(value.value(), type_id) + } +} + +/// An error decoding SCALE bytes. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum DecodeError { + /// We ran into an error trying to decode a bit sequence. + BitSequenceError(BitSequenceError), + /// The type we're trying to decode is supposed to be compact encoded, but that is not possible. + CannotDecodeCompactIntoType(scale_info::Type), + /// Failure to decode bytes into a string. + InvalidStr(alloc::str::Utf8Error), + /// We could not convert the [`u32`] that we found into a valid [`char`]. + InvalidChar(u32), + /// We expected more bytes to finish decoding, but could not find them. + NotEnoughInput, + /// We found a variant that does not match with any in the type we're trying to decode from. + VariantNotFound(u8, scale_info::TypeDefVariant), + /// Some error emitted from a [`codec::Decode`] impl. + CodecError(codec::Error), + /// We could not find the type given in the type registry provided. + TypeIdNotFound(u32), + /// This is returned by default if a visitor function is not implemented. + Unexpected(Unexpected), +} + +impl From for DecodeError { + fn from(err: codec::Error) -> Self { + Self::CodecError(err) + } +} + +impl From for DecodeError { + fn from(err: BitSequenceError) -> Self { + Self::BitSequenceError(err) + } +} + +impl From for DecodeError { + fn from(err: alloc::str::Utf8Error) -> Self { + Self::InvalidStr(err) + } +} + +/// This is returned by default when a visitor function isn't implemented. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[allow(missing_docs)] +pub enum Unexpected { + Bool, + Char, + U8, + U16, + U32, + U64, + U128, + U256, + I8, + I16, + I32, + I64, + I128, + I256, + Sequence, + Composite, + Tuple, + Str, + Variant, + Array, + Bitsequence, +} + +impl core::fmt::Display for Unexpected { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let s = match self { + Unexpected::Bool => "bool", + Unexpected::Char => "char", + Unexpected::U8 => "u8", + Unexpected::U16 => "u16", + Unexpected::U32 => "u32", + Unexpected::U64 => "u64", + Unexpected::U128 => "u128", + Unexpected::U256 => "u256", + Unexpected::I8 => "i8", + Unexpected::I16 => "i16", + Unexpected::I32 => "i32", + Unexpected::I64 => "i64", + Unexpected::I128 => "i128", + Unexpected::I256 => "i256", + Unexpected::Sequence => "sequence", + Unexpected::Composite => "composite", + Unexpected::Tuple => "tuple", + Unexpected::Str => "str", + Unexpected::Variant => "variant", + Unexpected::Array => "array", + Unexpected::Bitsequence => "bitsequence", + }; + f.write_str(s) + } +} + +/// The response from [`Visitor::unchecked_decode_as_type()`]. +pub enum DecodeAsTypeResult { + /// Skip any manual decoding and return the visitor instead. + Skipped(V), + /// Some manually decoded result. + Decoded(R), +} + +/// This is implemented for visitor related types which have a `decode_item` method, +/// and allows you to generically talk about decoding unnamed items. +pub trait DecodeItemIterator<'scale, 'info> { + /// Use a visitor to decode a single item. + fn decode_item( + &mut self, + visitor: V, + ) -> Option, V::Error>>; +} + +/// An error that can occur trying to decode a bit sequence. +pub type BitSequenceError = scale_bits::scale::format::FromMetadataError; + +/// The ID of the type being decoded. +#[derive(Clone, Copy, Debug, Default)] +pub struct TypeId(pub u32); + +/// A [`Visitor`] implementation that just ignores all of the bytes. +pub struct IgnoreVisitor; +impl Visitor for IgnoreVisitor { + type Value<'scale, 'info> = (); + type Error = DecodeError; + + // Whatever the value we visit is, just ignore it. + fn visit_unexpected<'scale, 'info>( + self, + _unexpected: Unexpected, + ) -> Result, Self::Error> { + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::scale_decode::visitor::TypeId; + + use super::*; + use codec::{self, Encode}; + use scale_info::PortableRegistry; + + /// A silly Value type for testing with a basic Visitor impl + /// that tries to mirror what is called as best as possible. + #[derive(Debug, PartialEq)] + enum Value { + Bool(bool), + Char(char), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + U128(u128), + U256([u8; 32]), + I8(i8), + I16(i16), + I32(i32), + I64(i64), + I128(i128), + I256([u8; 32]), + CompactU8(Vec, u8), + CompactU16(Vec, u16), + CompactU32(Vec, u32), + CompactU64(Vec, u64), + CompactU128(Vec, u128), + Sequence(Vec), + Composite(Vec<(String, Value)>), + Tuple(Vec), + Str(String), + Array(Vec), + Variant(String, Vec<(String, Value)>), + BitSequence(scale_bits::Bits), + } + + #[derive(Clone, Debug, PartialEq)] + enum Loc { + Unnamed, + Named(String), + Primitive, + } + + impl<'a> From> for Loc { + fn from(l: CompactLocation) -> Self { + match l { + CompactLocation::UnnamedComposite(_) => Loc::Unnamed, + CompactLocation::NamedComposite(_, s) => Loc::Named(s.to_owned()), + CompactLocation::Primitive(_) => Loc::Primitive, + } + } + } + + struct ValueVisitor; + impl Visitor for ValueVisitor { + type Value<'scale, 'info> = Value; + type Error = DecodeError; + + fn visit_bool<'scale, 'info>( + self, + value: bool, + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::Bool(value)) + } + fn visit_char<'scale, 'info>( + self, + value: char, + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::Char(value)) + } + fn visit_u8<'scale, 'info>( + self, + value: u8, + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::U8(value)) + } + fn visit_u16<'scale, 'info>( + self, + value: u16, + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::U16(value)) + } + fn visit_u32<'scale, 'info>( + self, + value: u32, + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::U32(value)) + } + fn visit_u64<'scale, 'info>( + self, + value: u64, + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::U64(value)) + } + fn visit_u128<'scale, 'info>( + self, + value: u128, + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::U128(value)) + } + fn visit_u256<'info>( + self, + value: &'_ [u8; 32], + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::U256(*value)) + } + fn visit_i8<'scale, 'info>( + self, + value: i8, + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::I8(value)) + } + fn visit_i16<'scale, 'info>( + self, + value: i16, + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::I16(value)) + } + fn visit_i32<'scale, 'info>( + self, + value: i32, + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::I32(value)) + } + fn visit_i64<'scale, 'info>( + self, + value: i64, + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::I64(value)) + } + fn visit_i128<'scale, 'info>( + self, + value: i128, + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::I128(value)) + } + fn visit_i256<'info>( + self, + value: &'_ [u8; 32], + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::I256(*value)) + } + fn visit_compact_u8<'scale, 'info>( + self, + value: Compact, + _type_id: TypeId, + ) -> Result, Self::Error> { + let locs = value.locations().iter().map(|&l| l.into()).collect(); + Ok(Value::CompactU8(locs, value.value())) + } + fn visit_compact_u16<'scale, 'info>( + self, + value: Compact, + _type_id: TypeId, + ) -> Result, Self::Error> { + let locs = value.locations().iter().map(|&l| l.into()).collect(); + Ok(Value::CompactU16(locs, value.value())) + } + fn visit_compact_u32<'scale, 'info>( + self, + value: Compact, + _type_id: TypeId, + ) -> Result, Self::Error> { + let locs = value.locations().iter().map(|&l| l.into()).collect(); + Ok(Value::CompactU32(locs, value.value())) + } + fn visit_compact_u64<'scale, 'info>( + self, + value: Compact, + _type_id: TypeId, + ) -> Result, Self::Error> { + let locs = value.locations().iter().map(|&l| l.into()).collect(); + Ok(Value::CompactU64(locs, value.value())) + } + fn visit_compact_u128<'scale, 'info>( + self, + value: Compact, + _type_id: TypeId, + ) -> Result, Self::Error> { + let locs = value.locations().iter().map(|&l| l.into()).collect(); + Ok(Value::CompactU128(locs, value.value())) + } + fn visit_sequence<'scale, 'info>( + self, + value: &mut Sequence<'scale, 'info>, + _type_id: TypeId, + ) -> Result, Self::Error> { + let mut vals = vec![]; + while let Some(val) = value.decode_item(ValueVisitor) { + let val = val?; + vals.push(val); + } + Ok(Value::Sequence(vals)) + } + fn visit_composite<'scale, 'info>( + self, + value: &mut Composite<'scale, 'info>, + _type_id: TypeId, + ) -> Result, Self::Error> { + let mut vals = vec![]; + for item in value.by_ref() { + let item = item?; + let val = item.decode_with_visitor(ValueVisitor)?; + let name = item.name().unwrap_or("").to_owned(); + vals.push((name, val)); + } + Ok(Value::Composite(vals)) + } + fn visit_tuple<'scale, 'info>( + self, + value: &mut Tuple<'scale, 'info>, + _type_id: TypeId, + ) -> Result, Self::Error> { + let mut vals = vec![]; + while let Some(val) = value.decode_item(ValueVisitor) { + let val = val?; + vals.push(val); + } + Ok(Value::Tuple(vals)) + } + fn visit_str<'scale, 'info>( + self, + value: &mut Str<'scale>, + _type_id: TypeId, + ) -> Result, Self::Error> { + Ok(Value::Str(value.as_str()?.to_owned())) + } + fn visit_variant<'scale, 'info>( + self, + value: &mut Variant<'scale, 'info>, + _type_id: TypeId, + ) -> Result, Self::Error> { + let mut vals = vec![]; + let fields = value.fields(); + for item in fields.by_ref() { + let item = item?; + let val = item.decode_with_visitor(ValueVisitor)?; + let name = item.name().unwrap_or("").to_owned(); + vals.push((name, val)); + } + Ok(Value::Variant(value.name().to_owned(), vals)) + } + fn visit_array<'scale, 'info>( + self, + value: &mut Array<'scale, 'info>, + _type_id: TypeId, + ) -> Result, Self::Error> { + let mut vals = vec![]; + while let Some(val) = value.decode_item(ValueVisitor) { + let val = val?; + vals.push(val); + } + Ok(Value::Array(vals)) + } + fn visit_bitsequence<'scale, 'info>( + self, + value: &mut BitSequence<'scale>, + _type_id: TypeId, + ) -> Result, Self::Error> { + let bools: Result = value.decode()?.collect(); + Ok(Value::BitSequence(bools?)) + } + } + + /// Given a type definition, return the PortableType and PortableRegistry + /// that our decode functions expect. + fn make_type() -> (u32, PortableRegistry) { + let m = scale_info::MetaType::new::(); + let mut types = scale_info::Registry::new(); + let id = types.register_type(&m); + let portable_registry: PortableRegistry = types.into(); + + (id.id, portable_registry) + } + + /// This just tests that if we try to decode some values we've encoded using a visitor + /// which just ignores everything by default, that we'll consume all of the bytes. + fn encode_decode_check_explicit_info( + val: T, + expected: Value, + ) { + let encoded = val.encode(); + let (id, types) = make_type::(); + + let bytes = &mut &*encoded; + let val = decode_with_visitor(bytes, id, &types, ValueVisitor) + .expect("decoding should not error"); + + assert_eq!(bytes.len(), 0, "Decoding should consume all bytes"); + assert_eq!(val, expected); + } + + fn encode_decode_check(val: T, expected: Value) { + encode_decode_check_explicit_info::(val, expected); + } + + #[test] + fn encode_decode_primitives() { + encode_decode_check(123u8, Value::U8(123)); + encode_decode_check(123u16, Value::U16(123)); + encode_decode_check(123u32, Value::U32(123)); + encode_decode_check(123u64, Value::U64(123)); + encode_decode_check(123u128, Value::U128(123)); + encode_decode_check(codec::Compact(123u8), Value::CompactU8(vec![Loc::Primitive], 123)); + encode_decode_check(codec::Compact(123u16), Value::CompactU16(vec![Loc::Primitive], 123)); + encode_decode_check(codec::Compact(123u32), Value::CompactU32(vec![Loc::Primitive], 123)); + encode_decode_check(codec::Compact(123u64), Value::CompactU64(vec![Loc::Primitive], 123)); + encode_decode_check(codec::Compact(123u128), Value::CompactU128(vec![Loc::Primitive], 123)); + encode_decode_check(true, Value::Bool(true)); + encode_decode_check(false, Value::Bool(false)); + encode_decode_check_explicit_info::('c' as u32, Value::Char('c')); + encode_decode_check("Hello there", Value::Str("Hello there".to_owned())); + encode_decode_check("Hello there".to_string(), Value::Str("Hello there".to_owned())); + } + + #[test] + fn decode_compact_named_wrapper_struct() { + // A struct that can be compact encoded: + #[derive(Encode, scale_info::TypeInfo)] + struct MyWrapper { + inner: u32, + } + impl From> for MyWrapper { + fn from(val: codec::Compact) -> MyWrapper { + val.0 + } + } + impl codec::CompactAs for MyWrapper { + type As = u32; + + fn encode_as(&self) -> &Self::As { + &self.inner + } + fn decode_from(inner: Self::As) -> Result { + Ok(MyWrapper { inner }) + } + } + + encode_decode_check( + codec::Compact(MyWrapper { inner: 123 }), + // Currently we ignore any composite types and just give back + // the compact value directly: + Value::CompactU32(vec![Loc::Named("inner".to_owned()), Loc::Primitive], 123), + ); + } + + #[test] + fn decode_compact_unnamed_wrapper_struct() { + // A struct that can be compact encoded: + #[derive(Encode, scale_info::TypeInfo)] + struct MyWrapper(u32); + impl From> for MyWrapper { + fn from(val: codec::Compact) -> MyWrapper { + val.0 + } + } + impl codec::CompactAs for MyWrapper { + type As = u32; + + // Node the requirement to return something with a lifetime tied + // to self here. This means that we can't implement this for things + // more complex than wrapper structs (eg `Foo(u32,u32,u32,u32)`) without + // shenanigans, meaning that (hopefully) supporting wrapper struct + // decoding and nothing fancier is sufficient. + fn encode_as(&self) -> &Self::As { + &self.0 + } + fn decode_from(inner: Self::As) -> Result { + Ok(MyWrapper(inner)) + } + } + + encode_decode_check( + codec::Compact(MyWrapper(123)), + // Currently we ignore any composite types and just give back + // the compact value directly: + Value::CompactU32(vec![Loc::Unnamed, Loc::Primitive], 123), + ); + } + + #[test] + fn decode_sequence_array_tuple_types() { + encode_decode_check( + vec![1i32, 2, 3], + Value::Sequence(vec![Value::I32(1), Value::I32(2), Value::I32(3)]), + ); + encode_decode_check( + [1i32, 2, 3], // compile-time length known + Value::Array(vec![Value::I32(1), Value::I32(2), Value::I32(3)]), + ); + encode_decode_check( + (1i32, true, 123456u128), + Value::Tuple(vec![Value::I32(1), Value::Bool(true), Value::U128(123456)]), + ); + } + + #[test] + fn decode_variant_types() { + #[derive(Encode, scale_info::TypeInfo)] + enum MyEnum { + Foo(bool), + Bar { hi: String, other: u128 }, + } + + encode_decode_check( + MyEnum::Foo(true), + Value::Variant("Foo".to_owned(), vec![(String::new(), Value::Bool(true))]), + ); + encode_decode_check( + MyEnum::Bar { hi: "hello".to_string(), other: 123 }, + Value::Variant( + "Bar".to_owned(), + vec![ + ("hi".to_string(), Value::Str("hello".to_string())), + ("other".to_string(), Value::U128(123)), + ], + ), + ); + } + + #[test] + fn decode_composite_types() { + #[derive(Encode, scale_info::TypeInfo)] + struct Unnamed(bool, String, Vec); + + #[derive(Encode, scale_info::TypeInfo)] + struct Named { + is_valid: bool, + name: String, + bytes: Vec, + } + + encode_decode_check( + Unnamed(true, "James".into(), vec![1, 2, 3]), + Value::Composite(vec![ + (String::new(), Value::Bool(true)), + (String::new(), Value::Str("James".to_string())), + (String::new(), Value::Sequence(vec![Value::U8(1), Value::U8(2), Value::U8(3)])), + ]), + ); + encode_decode_check( + Named { is_valid: true, name: "James".into(), bytes: vec![1, 2, 3] }, + Value::Composite(vec![ + ("is_valid".to_string(), Value::Bool(true)), + ("name".to_string(), Value::Str("James".to_string())), + ( + "bytes".to_string(), + Value::Sequence(vec![Value::U8(1), Value::U8(2), Value::U8(3)]), + ), + ]), + ); + } + + #[test] + fn decode_arrays() { + encode_decode_check( + [1u8, 2, 3], + Value::Array(vec![Value::U8(1), Value::U8(2), Value::U8(3)]), + ) + } + + #[test] + fn zero_copy_string_decoding() { + let input = ("hello", "world"); + + // The SCALE encoded bytes we want to zero-copy-decode from: + let input_encoded = input.encode(); + + // This can just zero-copy decode a string: + struct ZeroCopyStrVisitor; + impl Visitor for ZeroCopyStrVisitor { + type Value<'scale, 'info> = &'scale str; + type Error = DecodeError; + + fn visit_str<'scale, 'info>( + self, + value: &mut Str<'scale>, + _type_id: TypeId, + ) -> Result, Self::Error> { + value.as_str() + } + } + + // This can zero-copy decode the pair of strings we have as input: + struct ZeroCopyPairVisitor; + impl Visitor for ZeroCopyPairVisitor { + type Value<'scale, 'info> = (&'scale str, &'scale str); + type Error = DecodeError; + + fn visit_tuple<'scale, 'info>( + self, + value: &mut Tuple<'scale, 'info>, + _type_id: TypeId, + ) -> Result, Self::Error> { + let fst = value.decode_item(ZeroCopyStrVisitor).unwrap()?; + let snd = value.decode_item(ZeroCopyStrVisitor).unwrap()?; + Ok((fst, snd)) + } + } + + let (ty_id, types) = make_type::<(&str, &str)>(); + let decoded = + decode_with_visitor(&mut &*input_encoded, ty_id, &types, ZeroCopyPairVisitor).unwrap(); + assert_eq!(decoded, ("hello", "world")); + } + + #[test] + fn zero_copy_using_info_and_scale_lifetimes() { + use std::collections::BTreeMap; + + #[derive(codec::Encode, scale_info::TypeInfo)] + struct Foo { + hello: String, + world: String, + } + + // The SCALE encoded bytes we want to zero-copy-decode from: + let input_encoded = Foo { hello: "hi".to_string(), world: "planet".to_string() }.encode(); + + // This can just zero-copy decode a string: + struct ZeroCopyStrVisitor; + impl Visitor for ZeroCopyStrVisitor { + type Value<'scale, 'info> = &'scale str; + type Error = DecodeError; + + fn visit_str<'scale, 'info>( + self, + value: &mut Str<'scale>, + _type_id: TypeId, + ) -> Result, Self::Error> { + value.as_str() + } + } + + // This zero-copy decodes a composite into map of strings: + struct ZeroCopyMapVisitor; + impl Visitor for ZeroCopyMapVisitor { + type Value<'scale, 'info> = std::collections::BTreeMap<&'info str, &'scale str>; + type Error = DecodeError; + + fn visit_composite<'scale, 'info>( + self, + value: &mut Composite<'scale, 'info>, + _type_id: TypeId, + ) -> Result, Self::Error> { + let mut vals = std::collections::BTreeMap::<&'info str, &'scale str>::new(); + for item in value { + let item = item?; + let Some(key) = item.name() else { continue }; + let val = item.decode_with_visitor(ZeroCopyStrVisitor)?; + vals.insert(key, val); + } + Ok(vals) + } + } + + // Decode and check: + let (ty_id, types) = make_type::(); + let decoded = + decode_with_visitor(&mut &*input_encoded, ty_id, &types, ZeroCopyMapVisitor).unwrap(); + assert_eq!(decoded, BTreeMap::from_iter([("hello", "hi"), ("world", "planet")])); + } + + #[test] + fn bailout_works() { + let input = ("hello", "world"); + let (ty_id, types) = make_type::<(&str, &str)>(); + let input_encoded = input.encode(); + + // Just return the scale encoded bytes and type ID to prove + // that we can successfully "bail out". + struct BailOutVisitor; + impl Visitor for BailOutVisitor { + type Value<'scale, 'info> = (&'scale [u8], u32); + type Error = DecodeError; + + fn unchecked_decode_as_type<'scale, 'info>( + self, + input: &mut &'scale [u8], + type_id: TypeId, + _types: &'info scale_info::PortableRegistry, + ) -> DecodeAsTypeResult, Self::Error>> { + DecodeAsTypeResult::Decoded(Ok((*input, type_id.0))) + } + } + + let decoded = + decode_with_visitor(&mut &*input_encoded, ty_id, &types, BailOutVisitor).unwrap(); + assert_eq!(decoded, (&*input_encoded, ty_id)); + + // We can also use this functionality to "fall-back" to a Decode impl + // (though obviously with the caveat that this may be incorrect). + struct CodecDecodeVisitor(std::marker::PhantomData); + impl Visitor for CodecDecodeVisitor { + type Value<'scale, 'info> = T; + type Error = DecodeError; + + fn unchecked_decode_as_type<'scale, 'info>( + self, + input: &mut &'scale [u8], + _type_id: TypeId, + _types: &'info scale_info::PortableRegistry, + ) -> DecodeAsTypeResult, Self::Error>> { + DecodeAsTypeResult::Decoded(T::decode(input).map_err(|e| e.into())) + } + } + + let decoded: (String, String) = decode_with_visitor( + &mut &*input_encoded, + ty_id, + &types, + CodecDecodeVisitor(std::marker::PhantomData), + ) + .unwrap(); + assert_eq!(decoded, ("hello".to_string(), "world".to_string())); + } +} diff --git a/node-api/src/scale_decode/visitor/types/array.rs b/node-api/src/scale_decode/visitor/types/array.rs new file mode 100644 index 000000000..078ff2a15 --- /dev/null +++ b/node-api/src/scale_decode/visitor/types/array.rs @@ -0,0 +1,146 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::scale_decode::{DecodeAsType, DecodeError, IgnoreVisitor, Visitor}; +use scale_info::PortableRegistry; + +/// This enables a visitor to decode items from an array type. +pub struct Array<'scale, 'info> { + bytes: &'scale [u8], + item_bytes: &'scale [u8], + type_id: u32, + types: &'info PortableRegistry, + remaining: usize, +} + +impl<'scale, 'info> Array<'scale, 'info> { + pub(crate) fn new( + bytes: &'scale [u8], + type_id: u32, + len: usize, + types: &'info PortableRegistry, + ) -> Array<'scale, 'info> { + Array { bytes, item_bytes: bytes, type_id, types, remaining: len } + } + /// Skip over all bytes associated with this array. After calling this, + /// [`Self::bytes_from_undecoded()`] will represent the bytes after this array. + pub fn skip_decoding(&mut self) -> Result<(), DecodeError> { + while self.remaining > 0 { + self.decode_item(IgnoreVisitor).transpose()?; + } + Ok(()) + } + /// The bytes representing this array and anything following it. + pub fn bytes_from_start(&self) -> &'scale [u8] { + self.bytes + } + /// The bytes that have not yet been decoded in this array and anything following + /// it. + pub fn bytes_from_undecoded(&self) -> &'scale [u8] { + self.item_bytes + } + /// The number of un-decoded items remaining in this array. + pub fn remaining(&self) -> usize { + self.remaining + } + /// Are there any un-decoded items remaining in this array. + pub fn is_empty(&self) -> bool { + self.remaining == 0 + } + /// Decode an item from the array by providing a visitor to handle it. + pub fn decode_item( + &mut self, + visitor: V, + ) -> Option, V::Error>> { + if self.remaining == 0 { + return None + } + + let b = &mut self.item_bytes; + // Don't return here; decrement bytes and remaining properly first and then return, so that + // calling decode_item again works as expected. + let res = + crate::scale_decode::visitor::decode_with_visitor(b, self.type_id, self.types, visitor); + self.item_bytes = *b; + self.remaining -= 1; + Some(res) + } +} + +// Iterating returns a representation of each field in the tuple type. +impl<'scale, 'info> Iterator for Array<'scale, 'info> { + type Item = Result, DecodeError>; + fn next(&mut self) -> Option { + // Record details we need before we decode and skip over the thing: + let num_bytes_before = self.item_bytes.len(); + let item_bytes = self.item_bytes; + + if let Err(e) = self.decode_item(IgnoreVisitor)? { + return Some(Err(e)) + }; + + // How many bytes did we skip over? What bytes represent the thing we decoded? + let num_bytes_after = self.item_bytes.len(); + let res_bytes = &item_bytes[..num_bytes_before - num_bytes_after]; + + Some(Ok(ArrayItem { bytes: res_bytes, type_id: self.type_id, types: self.types })) + } +} + +/// A single item in the array. +#[derive(Copy, Clone)] +pub struct ArrayItem<'scale, 'info> { + bytes: &'scale [u8], + type_id: u32, + types: &'info PortableRegistry, +} + +impl<'scale, 'info> ArrayItem<'scale, 'info> { + /// The bytes associated with this item. + pub fn bytes(&self) -> &'scale [u8] { + self.bytes + } + /// The type ID associated with this item. + pub fn type_id(&self) -> u32 { + self.type_id + } + /// Decode this item using a visitor. + pub fn decode_with_visitor( + &self, + visitor: V, + ) -> Result, V::Error> { + crate::scale_decode::visitor::decode_with_visitor( + &mut &*self.bytes, + self.type_id, + self.types, + visitor, + ) + } + /// Decode this item into a specific type via [`DecodeAsType`]. + pub fn decode_as_type(&self) -> Result { + T::decode_as_type(&mut &*self.bytes, self.type_id, self.types) + } +} + +impl<'scale, 'info> crate::scale_decode::visitor::DecodeItemIterator<'scale, 'info> + for Array<'scale, 'info> +{ + fn decode_item<'a, V: Visitor>( + &mut self, + visitor: V, + ) -> Option, V::Error>> { + self.decode_item(visitor) + } +} diff --git a/node-api/src/scale_decode/visitor/types/bit_sequence.rs b/node-api/src/scale_decode/visitor/types/bit_sequence.rs new file mode 100644 index 000000000..aabf6619b --- /dev/null +++ b/node-api/src/scale_decode/visitor/types/bit_sequence.rs @@ -0,0 +1,124 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::scale_decode::visitor::DecodeError; +use scale_bits::{ + decode_using_format_from, + scale::{Decoder, Format}, +}; + +/// This represents a BitSequence, deferring decoding until the implementation wants to. +pub struct BitSequence<'scale> { + format: Format, + bytes: &'scale [u8], + // If we decode the bit sequence, we'll populate this too to cache it, since + // we must decode fully to figure it out at the mo. + bytes_after: Option<&'scale [u8]>, +} + +impl<'scale> BitSequence<'scale> { + pub(crate) fn new(format: Format, bytes: &'scale [u8]) -> Self { + BitSequence { format, bytes, bytes_after: None } + } + + /// The bytes left in the input, starting from this bit sequence. + pub fn bytes_from_start(&self) -> &'scale [u8] { + self.bytes + } + + /// The bytes after this bit sequence. Note that at present, this needs to + /// decode the bit sequence fully, so if you intend to do that anyway, call + /// `decode` first to cache this result and save repeating the effort here. + pub fn bytes_after(&self) -> Result<&'scale [u8], DecodeError> { + if let Some(bytes_after) = self.bytes_after { + Ok(bytes_after) + } else { + let decoder = decode_using_format_from(self.bytes, self.format)?; + Ok(&self.bytes[decoder.encoded_size()..]) + } + } + + /// Return a decoder to decode the bits in this bit sequence. + pub fn decode(&mut self) -> Result, DecodeError> { + let decoder = decode_using_format_from(self.bytes, self.format)?; + self.bytes_after = Some(&self.bytes[decoder.encoded_size()..]); + Ok(decoder) + } +} + +#[cfg(test)] +mod test { + use super::*; + use bitvec::{ + order::{Lsb0, Msb0}, + vec::BitVec, + }; + use codec::Encode; + use scale_bits::{ + bits, + scale::format::{OrderFormat, StoreFormat}, + Bits, + }; + + fn assert_remaining_bytes_works( + bits: Input, + store: StoreFormat, + order: OrderFormat, + ) { + let bytes = bits.encode(); + let format = Format::new(store, order); + + // Test skipping works: + let seq = BitSequence::new(format, &bytes); + let leftover = seq.bytes_after().expect("can skip bitseq without error"); + assert_eq!(leftover.len(), 0, "No bytes should remain after skipping over"); + + // Test that this works when we've called decode explicitly too: + let mut seq = BitSequence::new(format, &bytes); + let _ = seq.decode().unwrap(); + let leftover = seq.bytes_after().expect("can skip bitseq without error"); + assert_eq!(leftover.len(), 0, "No bytes should remain after skipping over"); + } + + fn assert_remaining_bytes_works_all(bits: Bits) { + let b: BitVec = bits.iter().collect(); + assert_remaining_bytes_works(b, StoreFormat::U8, OrderFormat::Lsb0); + let b: BitVec = bits.iter().collect(); + assert_remaining_bytes_works(b, StoreFormat::U16, OrderFormat::Lsb0); + let b: BitVec = bits.iter().collect(); + assert_remaining_bytes_works(b, StoreFormat::U32, OrderFormat::Lsb0); + let b: BitVec = bits.iter().collect(); + assert_remaining_bytes_works(b, StoreFormat::U64, OrderFormat::Lsb0); + let b: BitVec = bits.iter().collect(); + assert_remaining_bytes_works(b, StoreFormat::U8, OrderFormat::Msb0); + let b: BitVec = bits.iter().collect(); + assert_remaining_bytes_works(b, StoreFormat::U16, OrderFormat::Msb0); + let b: BitVec = bits.iter().collect(); + assert_remaining_bytes_works(b, StoreFormat::U32, OrderFormat::Msb0); + let b: BitVec = bits.iter().collect(); + assert_remaining_bytes_works(b, StoreFormat::U64, OrderFormat::Msb0); + } + + #[test] + fn skipping_remaining_bytes_works() { + assert_remaining_bytes_works_all(bits![]); + assert_remaining_bytes_works_all(bits![0]); + assert_remaining_bytes_works_all(bits![0, 1]); + assert_remaining_bytes_works_all(bits![1, 0, 1, 1, 0, 1, 1, 0, 1]); + assert_remaining_bytes_works_all(bits![ + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1 + ]); + } +} diff --git a/node-api/src/scale_decode/visitor/types/compact.rs b/node-api/src/scale_decode/visitor/types/compact.rs new file mode 100644 index 000000000..da6ad0f09 --- /dev/null +++ b/node-api/src/scale_decode/visitor/types/compact.rs @@ -0,0 +1,68 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::scale_decode::visitor::TypeId; + +/// This represents a compact encoded type. +pub struct Compact<'info, 'c, T> { + val: T, + locations: &'c [CompactLocation<'info>], +} + +impl<'info, 'c, T: Copy> Compact<'info, 'c, T> { + pub(crate) fn new(val: T, locations: &'c [CompactLocation<'info>]) -> Compact<'info, 'c, T> { + Compact { val, locations } + } + /// Return the value that was compact-encoded. + pub fn value(&self) -> T { + self.val + } + /// Compact values can be nested inside named or unnamed fields in structs. + /// This provides back a slice of these locations, in case such nesting matters. + pub fn locations(&self) -> &'c [CompactLocation<'info>] { + self.locations + } +} + +/// A pointer to what the compact value is contained within. +#[derive(Clone, Copy, Debug)] +pub enum CompactLocation<'info> { + /// We're in an unnamed composite (struct) with the type ID given. + UnnamedComposite(TypeId), + /// We're in a named composite (struct) with the type ID given, and the compact + /// value lives inside the field with the given name. + NamedComposite(TypeId, &'info str), + /// We're at a primitive type with the type ID given; the compact value itself. + Primitive(TypeId), +} + +impl<'info> CompactLocation<'info> { + /// Return the Primitive type of this location, if one exists. + pub fn as_primitive(self) -> Option { + match self { + CompactLocation::Primitive(t) => Some(t), + _ => None, + } + } +} + +// Default values for locations are never handed back, but they are +// stored on the StackArray in the "unused" positions. We could avoid needing +// this with some unsafe code. +impl<'info> Default for CompactLocation<'info> { + fn default() -> Self { + CompactLocation::Primitive(TypeId::default()) + } +} diff --git a/node-api/src/scale_decode/visitor/types/composite.rs b/node-api/src/scale_decode/visitor/types/composite.rs new file mode 100644 index 000000000..8ac2a6859 --- /dev/null +++ b/node-api/src/scale_decode/visitor/types/composite.rs @@ -0,0 +1,184 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::scale_decode::{ + visitor::{DecodeError, IgnoreVisitor, Visitor}, + DecodeAsType, Field, FieldIter, +}; +use scale_info::{form::PortableForm, Path, PortableRegistry}; + +/// This represents a composite type. +pub struct Composite<'scale, 'info> { + bytes: &'scale [u8], + item_bytes: &'scale [u8], + path: &'info Path, + fields: smallvec::SmallVec<[Field<'info>; 16]>, + next_field_idx: usize, + types: &'info PortableRegistry, +} + +impl<'scale, 'info> Composite<'scale, 'info> { + // Used in macros, but not really expected to be used elsewhere. + #[doc(hidden)] + pub fn new( + bytes: &'scale [u8], + path: &'info Path, + fields: &mut dyn FieldIter<'info>, + types: &'info PortableRegistry, + ) -> Composite<'scale, 'info> { + let fields = smallvec::SmallVec::from_iter(fields); + Composite { bytes, path, item_bytes: bytes, fields, types, next_field_idx: 0 } + } + /// Skip over all bytes associated with this composite type. After calling this, + /// [`Self::bytes_from_undecoded()`] will represent the bytes after this composite type. + pub fn skip_decoding(&mut self) -> Result<(), DecodeError> { + while let Some(res) = self.decode_item(IgnoreVisitor) { + res?; + } + Ok(()) + } + /// The bytes representing this composite type and anything following it. + pub fn bytes_from_start(&self) -> &'scale [u8] { + self.bytes + } + /// The bytes that have not yet been decoded in this composite type and anything + /// following it. + pub fn bytes_from_undecoded(&self) -> &'scale [u8] { + self.item_bytes + } + /// The number of un-decoded items remaining in this composite type. + pub fn remaining(&self) -> usize { + self.fields.len() - self.next_field_idx + } + /// Path to this type. + pub fn path(&self) -> &'info Path { + self.path + } + /// All of the fields present in this composite type. + pub fn fields(&self) -> &[Field<'info>] { + &self.fields + } + /// Return whether any of the fields are unnamed. + pub fn has_unnamed_fields(&self) -> bool { + self.fields.iter().any(|f| f.name().is_none()) + } + /// Convert the remaining fields in this Composite type into a [`super::Tuple`]. This allows them to + /// be parsed in the same way as a tuple type, discarding name information. + pub fn as_tuple(&self) -> super::Tuple<'scale, 'info> { + super::Tuple::new(self.item_bytes, &mut self.fields.iter().copied(), self.types) + } + /// Return the name of the next field to be decoded; `None` if either the field has no name, + /// or there are no fields remaining. + pub fn peek_name(&self) -> Option<&'info str> { + self.fields.get(self.next_field_idx).and_then(|f| f.name()) + } + /// Decode the next field in the composite type by providing a visitor to handle it. This is more + /// efficient than iterating over the key/value pairs if you already know how you want to decode the + /// values. + pub fn decode_item( + &mut self, + visitor: V, + ) -> Option, V::Error>> { + let field = self.fields.get(self.next_field_idx)?; + let b = &mut &*self.item_bytes; + + // Decode the bytes: + let res = + crate::scale_decode::visitor::decode_with_visitor(b, field.id(), self.types, visitor); + + if res.is_ok() { + // Move our cursors forwards only if decode was OK: + self.item_bytes = *b; + self.next_field_idx += 1; + } else { + // Otherwise, skip to end to prevent any future iterations: + self.next_field_idx = self.fields.len() + } + + Some(res) + } +} + +// Iterating returns a representation of each field in the composite type. +impl<'scale, 'info> Iterator for Composite<'scale, 'info> { + type Item = Result, DecodeError>; + fn next(&mut self) -> Option { + // Record details we need before we decode and skip over the thing: + let field = *self.fields.get(self.next_field_idx)?; + let num_bytes_before = self.item_bytes.len(); + let item_bytes = self.item_bytes; + + // Now, decode and skip over the item we're going to hand back: + if let Err(e) = self.decode_item(IgnoreVisitor)? { + return Some(Err(e)) + }; + + // How many bytes did we skip over? What bytes represent the thing we decoded? + let num_bytes_after = self.item_bytes.len(); + let res_bytes = &item_bytes[..num_bytes_before - num_bytes_after]; + + Some(Ok(CompositeField { bytes: res_bytes, field, types: self.types })) + } +} + +/// A single field in the composite type. +#[derive(Copy, Clone)] +pub struct CompositeField<'scale, 'info> { + bytes: &'scale [u8], + field: Field<'info>, + types: &'info PortableRegistry, +} + +impl<'scale, 'info> CompositeField<'scale, 'info> { + /// The field name. + pub fn name(&self) -> Option<&'info str> { + self.field.name() + } + /// The bytes associated with this field. + pub fn bytes(&self) -> &'scale [u8] { + self.bytes + } + /// The type ID associated with this field. + pub fn type_id(&self) -> u32 { + self.field.id() + } + /// Decode this field using a visitor. + pub fn decode_with_visitor( + &self, + visitor: V, + ) -> Result, V::Error> { + crate::scale_decode::visitor::decode_with_visitor( + &mut &*self.bytes, + self.field.id(), + self.types, + visitor, + ) + } + /// Decode this field into a specific type via [`DecodeAsType`]. + pub fn decode_as_type(&self) -> Result { + T::decode_as_type(&mut &*self.bytes, self.field.id(), self.types) + } +} + +impl<'scale, 'info> crate::scale_decode::visitor::DecodeItemIterator<'scale, 'info> + for Composite<'scale, 'info> +{ + fn decode_item<'a, V: Visitor>( + &mut self, + visitor: V, + ) -> Option, V::Error>> { + self.decode_item(visitor) + } +} diff --git a/node-api/src/scale_decode/visitor/types/mod.rs b/node-api/src/scale_decode/visitor/types/mod.rs new file mode 100644 index 000000000..d1723bb3e --- /dev/null +++ b/node-api/src/scale_decode/visitor/types/mod.rs @@ -0,0 +1,34 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Types used in the [`super::Visitor`] trait definition. + +mod array; +mod bit_sequence; +mod compact; +mod composite; +mod sequence; +mod str; +mod tuple; +mod variant; + +pub use self::str::Str; +pub use array::Array; +pub use bit_sequence::BitSequence; +pub use compact::{Compact, CompactLocation}; +pub use composite::Composite; +pub use sequence::Sequence; +pub use tuple::Tuple; +pub use variant::Variant; diff --git a/node-api/src/scale_decode/visitor/types/sequence.rs b/node-api/src/scale_decode/visitor/types/sequence.rs new file mode 100644 index 000000000..27c91dff7 --- /dev/null +++ b/node-api/src/scale_decode/visitor/types/sequence.rs @@ -0,0 +1,119 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::array::{Array, ArrayItem}; +use crate::scale_decode::{ + visitor::{DecodeError, Visitor}, + DecodeAsType, +}; +use codec::{Compact, Decode}; +use scale_info::PortableRegistry; + +/// This enables a visitor to decode items from a sequence type. +pub struct Sequence<'scale, 'info> { + bytes: &'scale [u8], + // Mostly we just delegate to our Array logic for working with sequences. + // The only thing we need to do otherwise is decode the compact encoded + // length from the beginning and keep track of the bytes including that. + values: Array<'scale, 'info>, +} + +impl<'scale, 'info> Sequence<'scale, 'info> { + pub(crate) fn new( + bytes: &'scale [u8], + type_id: u32, + types: &'info PortableRegistry, + ) -> Result, DecodeError> { + // Sequences are prefixed with their length in bytes. Make a note of this, + // as well as the number of bytes + let item_bytes = &mut &*bytes; + let len = >::decode(item_bytes)?.0 as usize; + + Ok(Sequence { bytes, values: Array::new(item_bytes, type_id, len, types) }) + } + /// Skip over all bytes associated with this sequence. After calling this, + /// [`Self::bytes_from_undecoded()`] will represent the bytes after this sequence. + pub fn skip_decoding(&mut self) -> Result<(), DecodeError> { + self.values.skip_decoding() + } + /// The bytes representing this sequence and anything following it. + pub fn bytes_from_start(&self) -> &'scale [u8] { + self.bytes + } + /// The bytes that have not yet been decoded in this sequence (this never includes the + /// compact length preceeding the sequence items) and anything following it. + pub fn bytes_from_undecoded(&self) -> &'scale [u8] { + self.values.bytes_from_undecoded() + } + /// The number of un-decoded items remaining in this sequence. + pub fn remaining(&self) -> usize { + self.values.remaining() + } + /// Decode an item from the sequence by providing a visitor to handle it. + pub fn decode_item( + &mut self, + visitor: V, + ) -> Option, V::Error>> { + self.values.decode_item(visitor) + } +} + +// Iterating returns a representation of each field in the tuple type. +impl<'scale, 'info> Iterator for Sequence<'scale, 'info> { + type Item = Result, DecodeError>; + fn next(&mut self) -> Option { + Some(self.values.next()?.map(|item| SequenceItem { item })) + } +} + +/// A single item in the Sequence. +#[derive(Copy, Clone)] +pub struct SequenceItem<'scale, 'info> { + // Same implementation under the hood as ArrayItem: + item: ArrayItem<'scale, 'info>, +} + +impl<'scale, 'info> SequenceItem<'scale, 'info> { + /// The bytes associated with this item. + pub fn bytes(&self) -> &'scale [u8] { + self.item.bytes() + } + /// The type ID associated with this item. + pub fn type_id(&self) -> u32 { + self.item.type_id() + } + /// Decode this item using a visitor. + pub fn decode_with_visitor( + &self, + visitor: V, + ) -> Result, V::Error> { + self.item.decode_with_visitor(visitor) + } + /// Decode this item into a specific type via [`DecodeAsType`]. + pub fn decode_as_type(&self) -> Result { + self.item.decode_as_type() + } +} + +impl<'scale, 'info> crate::scale_decode::visitor::DecodeItemIterator<'scale, 'info> + for Sequence<'scale, 'info> +{ + fn decode_item<'a, V: Visitor>( + &mut self, + visitor: V, + ) -> Option, V::Error>> { + self.decode_item(visitor) + } +} diff --git a/node-api/src/scale_decode/visitor/types/str.rs b/node-api/src/scale_decode/visitor/types/str.rs new file mode 100644 index 000000000..0268b662b --- /dev/null +++ b/node-api/src/scale_decode/visitor/types/str.rs @@ -0,0 +1,61 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::scale_decode::visitor::DecodeError; +use codec::{Compact, Decode}; + +/// This represents a string, but defers proper decoding of it until it's asked for, +/// and avoids allocating. +pub struct Str<'scale> { + len: usize, + compact_len: usize, + bytes: &'scale [u8], +} + +impl<'scale> Str<'scale> { + pub(crate) fn new(bytes: &'scale [u8]) -> Result, DecodeError> { + // Strings are just encoded the same as bytes; a length prefix and then + // the raw bytes. decode the length but keep all of the bytes that represent this + // encoded string (and the rest of the input) around so that we can provide a + // consistent interface with Array/Sequence etc. + let remaining_bytes = &mut &*bytes; + let len = >::decode(remaining_bytes)?.0 as usize; + let compact_len = bytes.len() - remaining_bytes.len(); + + Ok(Str { len, bytes, compact_len }) + } + /// The length of the string. + pub fn len(&self) -> usize { + self.len + } + /// The bytes left in the input, starting from this string. + pub fn bytes_from_start(&self) -> &'scale [u8] { + self.bytes + } + /// The bytes remaining in the input after this string. + pub fn bytes_after(&self) -> &'scale [u8] { + &self.bytes[self.compact_len + self.len..] + } + /// Is the string zero bytes long? + pub fn is_empty(&self) -> bool { + self.len == 0 + } + /// return a string, failing if the bytes could not be properly utf8-decoded. + pub fn as_str(&self) -> Result<&'scale str, DecodeError> { + let start = self.compact_len; + let end = start + self.len; + alloc::str::from_utf8(&self.bytes[start..end]).map_err(DecodeError::InvalidStr) + } +} diff --git a/node-api/src/scale_decode/visitor/types/tuple.rs b/node-api/src/scale_decode/visitor/types/tuple.rs new file mode 100644 index 000000000..16d9cd912 --- /dev/null +++ b/node-api/src/scale_decode/visitor/types/tuple.rs @@ -0,0 +1,152 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::scale_decode::{ + visitor::{DecodeError, IgnoreVisitor, Visitor}, + DecodeAsType, Field, FieldIter, +}; +use scale_info::PortableRegistry; + +/// This represents a tuple of values. +pub struct Tuple<'scale, 'info> { + bytes: &'scale [u8], + item_bytes: &'scale [u8], + fields: smallvec::SmallVec<[Field<'info>; 16]>, + next_field_idx: usize, + types: &'info PortableRegistry, +} + +impl<'scale, 'info> Tuple<'scale, 'info> { + pub(crate) fn new( + bytes: &'scale [u8], + fields: &mut dyn FieldIter<'info>, + types: &'info PortableRegistry, + ) -> Tuple<'scale, 'info> { + let fields = smallvec::SmallVec::from_iter(fields); + Tuple { bytes, item_bytes: bytes, fields, types, next_field_idx: 0 } + } + /// Skip over all bytes associated with this tuple. After calling this, + /// [`Self::bytes_from_undecoded()`] will represent the bytes after this tuple. + pub fn skip_decoding(&mut self) -> Result<(), DecodeError> { + while let Some(res) = self.decode_item(IgnoreVisitor) { + res?; + } + Ok(()) + } + /// The bytes representing this tuple and anything following it. + pub fn bytes_from_start(&self) -> &'scale [u8] { + self.bytes + } + /// The bytes that have not yet been decoded in this tuple, and anything + /// following it. + pub fn bytes_from_undecoded(&self) -> &'scale [u8] { + self.item_bytes + } + /// The number of un-decoded items remaining in the tuple. + pub fn remaining(&self) -> usize { + self.fields.len() + } + /// Decode the next item from the tuple by providing a visitor to handle it. + pub fn decode_item( + &mut self, + visitor: V, + ) -> Option, V::Error>> { + let field = self.fields.get(self.next_field_idx)?; + let b = &mut &*self.item_bytes; + + // Decode the bytes: + let res = + crate::scale_decode::visitor::decode_with_visitor(b, field.id(), self.types, visitor); + + if res.is_ok() { + // Move our cursors forwards only if decode was OK: + self.item_bytes = *b; + self.next_field_idx += 1; + } else { + // Otherwise, skip to end to prevent any future iterations: + self.next_field_idx = self.fields.len() + } + + Some(res) + } +} + +// Iterating returns a representation of each field in the tuple type. +impl<'scale, 'info> Iterator for Tuple<'scale, 'info> { + type Item = Result, DecodeError>; + fn next(&mut self) -> Option { + // Record details we need before we decode and skip over the thing: + let field = *self.fields.get(self.next_field_idx)?; + let num_bytes_before = self.item_bytes.len(); + let item_bytes = self.item_bytes; + + // Now, decode and skip over the item we're going to hand back: + if let Err(e) = self.decode_item(IgnoreVisitor)? { + return Some(Err(e)) + }; + + // How many bytes did we skip over? What bytes represent the thing we decoded? + let num_bytes_after = self.item_bytes.len(); + let res_bytes = &item_bytes[..num_bytes_before - num_bytes_after]; + + Some(Ok(TupleField { bytes: res_bytes, type_id: field.id(), types: self.types })) + } +} + +/// A single field in the tuple type. +#[derive(Copy, Clone)] +pub struct TupleField<'scale, 'info> { + bytes: &'scale [u8], + type_id: u32, + types: &'info PortableRegistry, +} + +impl<'scale, 'info> TupleField<'scale, 'info> { + /// The bytes associated with this field. + pub fn bytes(&self) -> &'scale [u8] { + self.bytes + } + /// The type ID associated with this field. + pub fn type_id(&self) -> u32 { + self.type_id + } + /// Decode this field using a visitor. + pub fn decode_with_visitor( + &self, + visitor: V, + ) -> Result, V::Error> { + crate::scale_decode::visitor::decode_with_visitor( + &mut &*self.bytes, + self.type_id, + self.types, + visitor, + ) + } + /// Decode this field into a specific type via [`DecodeAsType`]. + pub fn decode_as_type(&self) -> Result { + T::decode_as_type(&mut &*self.bytes, self.type_id, self.types) + } +} + +impl<'scale, 'info> crate::scale_decode::visitor::DecodeItemIterator<'scale, 'info> + for Tuple<'scale, 'info> +{ + fn decode_item<'a, V: Visitor>( + &mut self, + visitor: V, + ) -> Option, V::Error>> { + self.decode_item(visitor) + } +} diff --git a/node-api/src/scale_decode/visitor/types/variant.rs b/node-api/src/scale_decode/visitor/types/variant.rs new file mode 100644 index 000000000..088560a60 --- /dev/null +++ b/node-api/src/scale_decode/visitor/types/variant.rs @@ -0,0 +1,85 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io) +// This file is a part of the scale-decode crate. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::scale_decode::{ + visitor::{Composite, DecodeError}, + Field, +}; +use scale_info::{form::PortableForm, Path, PortableRegistry, TypeDefVariant}; + +/// A representation of the a variant type. +pub struct Variant<'scale, 'info> { + bytes: &'scale [u8], + variant: &'info scale_info::Variant, + fields: Composite<'scale, 'info>, +} + +impl<'scale, 'info> Variant<'scale, 'info> { + pub(crate) fn new( + bytes: &'scale [u8], + path: &'info Path, + ty: &'info TypeDefVariant, + types: &'info PortableRegistry, + ) -> Result, DecodeError> { + let index = *bytes.first().ok_or(DecodeError::NotEnoughInput)?; + let item_bytes = &bytes[1..]; + + // Does a variant exist with the index we're looking for? + let variant = ty + .variants + .iter() + .find(|v| v.index == index) + .ok_or_else(|| DecodeError::VariantNotFound(index, ty.clone()))?; + + // Allow decoding of the fields: + let mut fields_iter = variant.fields.iter().map(|f| Field::new(f.ty.id, f.name.as_deref())); + let fields = Composite::new(item_bytes, path, &mut fields_iter, types); + + Ok(Variant { bytes, variant, fields }) + } +} + +impl<'scale, 'info> Variant<'scale, 'info> { + /// Skip over all bytes associated with this variant. After calling this, + /// [`Self::bytes_from_undecoded()`] will represent the bytes after this variant. + pub fn skip_decoding(&mut self) -> Result<(), DecodeError> { + self.fields.skip_decoding() + } + /// The bytes representing this sequence and anything following it. + pub fn bytes_from_start(&self) -> &'scale [u8] { + self.bytes + } + /// The bytes that have not yet been decoded in this variant (this never includes the + /// variant index at the front) and anything following it. + pub fn bytes_from_undecoded(&self) -> &'scale [u8] { + self.fields.bytes_from_undecoded() + } + /// Path to this type. + pub fn path(&self) -> &'info Path { + self.fields.path() + } + /// The name of the variant. + pub fn name(&self) -> &'info str { + &self.variant.name + } + /// The index of the variant. + pub fn index(&self) -> u8 { + self.variant.index + } + /// Access the variant fields. + pub fn fields(&mut self) -> &mut Composite<'scale, 'info> { + &mut self.fields + } +}