From 991e7736e19ffd5ad08f41af8b1d2416d4a39a5e Mon Sep 17 00:00:00 2001 From: ascjones Date: Fri, 6 May 2022 13:16:21 +0100 Subject: [PATCH 1/5] Fix comment --- src/cmd/extrinsics/transcode/encode.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmd/extrinsics/transcode/encode.rs b/src/cmd/extrinsics/transcode/encode.rs index 86fdc833..e7617953 100644 --- a/src/cmd/extrinsics/transcode/encode.rs +++ b/src/cmd/extrinsics/transcode/encode.rs @@ -484,9 +484,9 @@ where Ok(()) } -/// Attempt to instantiate a type from its little-endian bytes representation. +/// Attempt to instantiate a type from its hex-encoded bytes representation. pub trait TryFromHex: Sized { - /// Create a new instance from the little-endian bytes representation. + /// Create a new instance from the hex-encoded bytes representation. fn try_from_hex(hex: &str) -> Result; } -- GitLab From 972a857d8208334762895e787dc2f7f8d6243dd8 Mon Sep 17 00:00:00 2001 From: ascjones Date: Fri, 6 May 2022 13:19:11 +0100 Subject: [PATCH 2/5] Remove some unnecessary path prefixes --- src/main.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 92f4ec46..6f526a9c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -78,7 +78,7 @@ pub(crate) struct ContractArgs { #[derive(Debug, Default, Clone, PartialEq, Eq)] pub(crate) struct HexData(pub Vec); -impl std::str::FromStr for HexData { +impl FromStr for HexData { type Err = hex::FromHexError; fn from_str(input: &str) -> std::result::Result { @@ -118,7 +118,7 @@ impl Default for OptimizationPasses { } } -impl std::str::FromStr for OptimizationPasses { +impl FromStr for OptimizationPasses { type Err = Error; fn from_str(input: &str) -> std::result::Result { @@ -139,7 +139,7 @@ impl std::str::FromStr for OptimizationPasses { } } -impl From for OptimizationPasses { +impl From for OptimizationPasses { fn from(str: String) -> Self { OptimizationPasses::from_str(&str).expect("conversion failed") } @@ -553,14 +553,14 @@ mod tests { "verbosity": "Quiet" }"#; - let build_result = crate::BuildResult { + let build_result = BuildResult { dest_wasm: Some(PathBuf::from("/path/to/contract.wasm")), - metadata_result: Some(crate::cmd::metadata::MetadataResult { + metadata_result: Some(MetadataResult { dest_metadata: PathBuf::from("/path/to/metadata.json"), dest_bundle: PathBuf::from("/path/to/contract.contract"), }), target_directory: PathBuf::from("/path/to/target"), - optimization_result: Some(crate::OptimizationResult { + optimization_result: Some(OptimizationResult { dest_wasm: PathBuf::from("/path/to/contract.wasm"), original_size: 64.0, optimized_size: 32.0, -- GitLab From fcedfdd8bdf20ca57be0fef4a5fa9deded44f9ae Mon Sep 17 00:00:00 2001 From: ascjones Date: Fri, 6 May 2022 14:53:19 +0100 Subject: [PATCH 3/5] Allow custom encoding OR decoding, add tests --- src/cmd/extrinsics/events.rs | 3 +- src/cmd/extrinsics/transcode/env_types.rs | 60 ++++++++----- src/cmd/extrinsics/transcode/mod.rs | 2 +- src/cmd/extrinsics/transcode/transcoder.rs | 100 ++++++++++++++++++--- 4 files changed, 129 insertions(+), 36 deletions(-) diff --git a/src/cmd/extrinsics/events.rs b/src/cmd/extrinsics/events.rs index 4cf9251f..58669f5d 100644 --- a/src/cmd/extrinsics/events.rs +++ b/src/cmd/extrinsics/events.rs @@ -17,7 +17,6 @@ use super::{ runtime_api::api::contracts::events::ContractEmitted, transcode::{ - env_types, ContractMessageTranscoder, TranscoderBuilder, }, @@ -54,7 +53,7 @@ pub fn display_events( let runtime_metadata = subxt_metadata.runtime_metadata(); let events_transcoder = TranscoderBuilder::new(&runtime_metadata.types) - .register_custom_type::(env_types::AccountId) + .with_default_custom_type_transcoders() .done(); const EVENT_FIELD_INDENT: usize = DEFAULT_KEY_COL_WIDTH - 3; diff --git a/src/cmd/extrinsics/transcode/env_types.rs b/src/cmd/extrinsics/transcode/env_types.rs index 7ecaad99..e65a4d5a 100644 --- a/src/cmd/extrinsics/transcode/env_types.rs +++ b/src/cmd/extrinsics/transcode/env_types.rs @@ -40,17 +40,19 @@ use std::{ convert::TryFrom, str::FromStr, }; +use crate::cmd::extrinsics::transcode::scon::Hex; /// Provides custom encoding and decoding for predefined environment types. #[derive(Default)] pub struct EnvTypesTranscoder { - transcoders: HashMap>, + encoders: HashMap>, + decoders: HashMap>, } impl EnvTypesTranscoder { /// Construct an `EnvTypesTranscoder` from the given type registry. - pub fn new(transcoders: HashMap>) -> Self { - Self { transcoders } + pub fn new(encoders: HashMap>, decoders: HashMap>) -> Self { + Self { encoders, decoders } } /// If the given type id is for a type with custom encoding, encodes the given value with the @@ -68,10 +70,10 @@ impl EnvTypesTranscoder { where O: Output, { - match self.transcoders.get(&type_id) { - Some(transcoder) => { + match self.encoders.get(&type_id) { + Some(encoder) => { log::debug!("Encoding type {:?} with custom encoder", type_id); - let encoded_env_type = transcoder + let encoded_env_type = encoder .encode_value(value) .context("Error encoding custom type")?; output.write(&encoded_env_type); @@ -89,10 +91,10 @@ impl EnvTypesTranscoder { /// /// - If the custom decoding fails. pub fn try_decode(&self, type_id: u32, input: &mut &[u8]) -> Result> { - match self.transcoders.get(&type_id) { - Some(transcoder) => { + match self.decoders.get(&type_id) { + Some(decoder) => { log::debug!("Decoding type {:?} with custom decoder", type_id); - let decoded = transcoder.decode_value(input)?; + let decoded = decoder.decode_value(input)?; Ok(Some(decoded)) } None => { @@ -103,13 +105,6 @@ impl EnvTypesTranscoder { } } -/// Implement this trait to define custom transcoding for a type in a `scale-info` type registry. -pub trait CustomTypeTranscoder { - fn aliases(&self) -> &[&str]; - fn encode_value(&self, value: &Value) -> Result>; - fn decode_value(&self, input: &mut &[u8]) -> Result; -} - #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct PathKey(Vec); @@ -135,12 +130,24 @@ impl From<&Path> for PathKey { pub type TypesByPath = HashMap; +/// Implement this trait to define custom encoding for a type in a `scale-info` type registry. +pub trait CustomTypeEncoder { + fn encode_value(&self, value: &Value) -> Result>; +} + +/// Implement this trait to define custom decoding for a type in a `scale-info` type registry. +pub trait CustomTypeDecoder { + fn decode_value(&self, input: &mut &[u8]) -> Result; +} + +/// Custom encoding/decoding for the Substrate `AccountId` type. +/// +/// Enables an `AccountId` to be input/ouput as an SS58 Encoded literal e.g. +/// 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY +#[derive(Clone)] pub struct AccountId; -impl CustomTypeTranscoder for AccountId { - fn aliases(&self) -> &[&'static str] { - &["AccountId"] - } +impl CustomTypeEncoder for AccountId { fn encode_value(&self, value: &Value) -> Result> { let account_id = match value { Value::Literal(literal) => { @@ -177,9 +184,22 @@ impl CustomTypeTranscoder for AccountId { }; Ok(account_id.encode()) } +} +impl CustomTypeDecoder for AccountId { fn decode_value(&self, input: &mut &[u8]) -> Result { let account_id = AccountId32::decode(input)?; Ok(Value::Literal(account_id.to_ss58check())) } } + +/// Custom decoding for the `Hash` or `[u8; 32]` type so that it is displayed as a hex encoded +/// string. +pub struct Hash; + +impl CustomTypeDecoder for Hash { + fn decode_value(&self, input: &mut &[u8]) -> Result { + let hash = sp_core::H256::decode(input)?; + Ok(Value::Hex(Hex::from_str(&format!("{:?}", hash))?)) + } +} diff --git a/src/cmd/extrinsics/transcode/mod.rs b/src/cmd/extrinsics/transcode/mod.rs index bac8be42..d42c98a9 100644 --- a/src/cmd/extrinsics/transcode/mod.rs +++ b/src/cmd/extrinsics/transcode/mod.rs @@ -128,7 +128,7 @@ pub struct ContractMessageTranscoder<'a> { impl<'a> ContractMessageTranscoder<'a> { pub fn new(metadata: &'a InkProject) -> Self { let transcoder = TranscoderBuilder::new(metadata.registry()) - .register_custom_type::<::AccountId, _>(env_types::AccountId) + .register_custom_type_transcoder::<::AccountId, _>(env_types::AccountId) .done(); Self { metadata, diff --git a/src/cmd/extrinsics/transcode/transcoder.rs b/src/cmd/extrinsics/transcode/transcoder.rs index c575c442..3c30479c 100644 --- a/src/cmd/extrinsics/transcode/transcoder.rs +++ b/src/cmd/extrinsics/transcode/transcoder.rs @@ -18,7 +18,9 @@ use super::{ decode::Decoder, encode::Encoder, env_types::{ - CustomTypeTranscoder, + self, + CustomTypeEncoder, + CustomTypeDecoder, EnvTypesTranscoder, PathKey, TypesByPath, @@ -76,7 +78,8 @@ impl<'a> Transcoder<'a> { pub struct TranscoderBuilder<'a> { registry: &'a PortableRegistry, types_by_path: TypesByPath, - transcoders: HashMap>, + encoders: HashMap>, + decoders: HashMap>, } impl<'a> TranscoderBuilder<'a> { @@ -89,14 +92,59 @@ impl<'a> TranscoderBuilder<'a> { Self { registry, types_by_path, - transcoders: HashMap::new(), + encoders: HashMap::new(), + decoders: HashMap::new(), } } - pub fn register_custom_type(self, transcoder: U) -> Self + pub fn with_default_custom_type_transcoders(self) -> Self { + self + .register_custom_type_transcoder::(env_types::AccountId) + .register_custom_type_decoder::(env_types::Hash) + } + + pub fn register_custom_type_transcoder(self, transcoder: U) -> Self + where + T: TypeInfo + 'static, + U: CustomTypeEncoder + CustomTypeDecoder + Clone + 'static, + { + self.register_custom_type_encoder::(transcoder.clone()) + .register_custom_type_decoder::(transcoder) + } + + pub fn register_custom_type_encoder(self, encoder: U) -> Self where T: TypeInfo + 'static, - U: CustomTypeTranscoder + 'static, + U: CustomTypeEncoder + 'static, + { + let mut this = self; + + let path_key = PathKey::from_type::(); + let type_id = this.types_by_path.get(&path_key); + + match type_id { + Some(type_id) => { + let existing = this.encoders.insert(*type_id, Box::new(encoder)); + log::debug!("Registered custom encoder for type `{:?}`", type_id); + if existing.is_some() { + panic!( + "Attempted to register encoder with existing type id {:?}", + type_id + ); + } + } + None => { + // if the type is not present in the registry, it just means it has not been used. + log::info!("No matching type in registry for path {:?}.", path_key); + } + } + this + } + + pub fn register_custom_type_decoder(self, encoder: U) -> Self + where + T: TypeInfo + 'static, + U: CustomTypeDecoder + 'static, { let mut this = self; @@ -105,11 +153,11 @@ impl<'a> TranscoderBuilder<'a> { match type_id { Some(type_id) => { - let existing = this.transcoders.insert(*type_id, Box::new(transcoder)); - log::debug!("Registered environment type `{:?}`", type_id); + let existing = this.decoders.insert(*type_id, Box::new(encoder)); + log::debug!("Registered custom decoder for type `{:?}`", type_id); if existing.is_some() { panic!( - "Attempted to register transcoder with existing type id {:?}", + "Attempted to register decoder with existing type id {:?}", type_id ); } @@ -123,7 +171,7 @@ impl<'a> TranscoderBuilder<'a> { } pub fn done(self) -> Transcoder<'a> { - let env_types_transcoder = EnvTypesTranscoder::new(self.transcoders); + let env_types_transcoder = EnvTypesTranscoder::new(self.encoders, self.decoders); Transcoder::new(self.registry, env_types_transcoder) } } @@ -141,7 +189,6 @@ mod tests { }, *, }; - use crate::cmd::extrinsics::transcode; use scale::Encode; use scale_info::{ MetaType, @@ -167,9 +214,7 @@ mod tests { { let (registry, ty) = registry_with_type::()?; let transcoder = TranscoderBuilder::new(®istry) - .register_custom_type::( - transcode::env_types::AccountId, - ) + .with_default_custom_type_transcoders() .done(); let value = scon::parse_value(input)?; @@ -714,6 +759,35 @@ mod tests { ) } + #[test] + fn decode_h256_as_hex_string() -> Result<()> { + #[allow(dead_code)] + #[derive(TypeInfo)] + struct S { + hash: sp_core::H256, + } + + transcode_roundtrip::( + r#"S( + hash: 0x3428ebe146f5b82415da82724bcf75f053768738dcac5f83ab7d82a70a0ff2de, + )"#, + Value::Map(Map::new( + Some("S"), + vec![ + ( + Value::String("hash".into()), + Value::Hex( + Hex::from_str("0x3428ebe146f5b82415da82724bcf75f053768738dcac5f83ab7d82a70a0ff2de")?, + ), + ), + ] + .into_iter() + .collect(), + )), + ) + } + + #[test] fn transcode_compact_primitives() -> Result<()> { transcode_roundtrip::>(r#"33"#, Value::UInt(33))?; -- GitLab From a9b8162f6bd7b7441922b6d3904f17c730fc2042 Mon Sep 17 00:00:00 2001 From: ascjones Date: Fri, 6 May 2022 14:53:35 +0100 Subject: [PATCH 4/5] Fmt --- src/cmd/extrinsics/transcode/env_types.rs | 7 +++++-- src/cmd/extrinsics/transcode/transcoder.rs | 16 ++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/cmd/extrinsics/transcode/env_types.rs b/src/cmd/extrinsics/transcode/env_types.rs index e65a4d5a..f3135ab6 100644 --- a/src/cmd/extrinsics/transcode/env_types.rs +++ b/src/cmd/extrinsics/transcode/env_types.rs @@ -15,6 +15,7 @@ // along with cargo-contract. If not, see . use super::scon::Value; +use crate::cmd::extrinsics::transcode::scon::Hex; use anyhow::{ Context, Result, @@ -40,7 +41,6 @@ use std::{ convert::TryFrom, str::FromStr, }; -use crate::cmd::extrinsics::transcode::scon::Hex; /// Provides custom encoding and decoding for predefined environment types. #[derive(Default)] @@ -51,7 +51,10 @@ pub struct EnvTypesTranscoder { impl EnvTypesTranscoder { /// Construct an `EnvTypesTranscoder` from the given type registry. - pub fn new(encoders: HashMap>, decoders: HashMap>) -> Self { + pub fn new( + encoders: HashMap>, + decoders: HashMap>, + ) -> Self { Self { encoders, decoders } } diff --git a/src/cmd/extrinsics/transcode/transcoder.rs b/src/cmd/extrinsics/transcode/transcoder.rs index 3c30479c..dc07bd70 100644 --- a/src/cmd/extrinsics/transcode/transcoder.rs +++ b/src/cmd/extrinsics/transcode/transcoder.rs @@ -19,8 +19,8 @@ use super::{ encode::Encoder, env_types::{ self, - CustomTypeEncoder, CustomTypeDecoder, + CustomTypeEncoder, EnvTypesTranscoder, PathKey, TypesByPath, @@ -98,9 +98,10 @@ impl<'a> TranscoderBuilder<'a> { } pub fn with_default_custom_type_transcoders(self) -> Self { - self - .register_custom_type_transcoder::(env_types::AccountId) - .register_custom_type_decoder::(env_types::Hash) + self.register_custom_type_transcoder::( + env_types::AccountId, + ) + .register_custom_type_decoder::(env_types::Hash) } pub fn register_custom_type_transcoder(self, transcoder: U) -> Self @@ -142,9 +143,9 @@ impl<'a> TranscoderBuilder<'a> { } pub fn register_custom_type_decoder(self, encoder: U) -> Self - where - T: TypeInfo + 'static, - U: CustomTypeDecoder + 'static, + where + T: TypeInfo + 'static, + U: CustomTypeDecoder + 'static, { let mut this = self; @@ -787,7 +788,6 @@ mod tests { ) } - #[test] fn transcode_compact_primitives() -> Result<()> { transcode_roundtrip::>(r#"33"#, Value::UInt(33))?; -- GitLab From b616b24cddb7004c2a396ee5ab5fc92900cf33d5 Mon Sep 17 00:00:00 2001 From: ascjones Date: Fri, 6 May 2022 15:35:01 +0100 Subject: [PATCH 5/5] Register custom type decoder for contract event transcoder --- src/cmd/extrinsics/transcode/mod.rs | 38 ++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/cmd/extrinsics/transcode/mod.rs b/src/cmd/extrinsics/transcode/mod.rs index d42c98a9..cd1ed8e3 100644 --- a/src/cmd/extrinsics/transcode/mod.rs +++ b/src/cmd/extrinsics/transcode/mod.rs @@ -129,6 +129,7 @@ impl<'a> ContractMessageTranscoder<'a> { pub fn new(metadata: &'a InkProject) -> Self { let transcoder = TranscoderBuilder::new(metadata.registry()) .register_custom_type_transcoder::<::AccountId, _>(env_types::AccountId) + .register_custom_type_decoder::<::Hash, _>(env_types::Hash) .done(); Self { metadata, @@ -346,6 +347,7 @@ mod tests { use scon::Value; use std::str::FromStr; + use crate::cmd::extrinsics::transcode::scon::Hex; use ink_lang as ink; #[ink::contract] @@ -568,7 +570,8 @@ mod tests { let metadata = generate_metadata(); let transcoder = ContractMessageTranscoder::new(&metadata); - let encoded = ([0u32; 32], [1u32; 32]).encode(); + // raw encoded event with event index prefix + let encoded = (0u8, [0u32; 32], [1u32; 32]).encode(); // encode again as a Vec which has a len prefix. let encoded_bytes = encoded.encode(); let _ = transcoder.decode_contract_event(&mut &encoded_bytes[..])?; @@ -576,6 +579,39 @@ mod tests { Ok(()) } + #[test] + fn decode_hash_as_hex_encoded_string() -> Result<()> { + let metadata = generate_metadata(); + let transcoder = ContractMessageTranscoder::new(&metadata); + + let hash = [ + 52u8, 40, 235, 225, 70, 245, 184, 36, 21, 218, 130, 114, 75, 207, 117, 240, + 83, 118, 135, 56, 220, 172, 95, 131, 171, 125, 130, 167, 10, 15, 242, 222, + ]; + // raw encoded event with event index prefix + let encoded = (0u8, hash, [0u32; 32]).encode(); + // encode again as a Vec which has a len prefix. + let encoded_bytes = encoded.encode(); + let decoded = transcoder.decode_contract_event(&mut &encoded_bytes[..])?; + + if let Value::Map(ref map) = decoded { + let name_field = &map[&Value::String("name".into())]; + if let Value::Hex(hex) = name_field { + assert_eq!(&Hex::from_str("0x3428ebe146f5b82415da82724bcf75f053768738dcac5f83ab7d82a70a0ff2de")?, hex); + Ok(()) + } else { + Err(anyhow::anyhow!( + "Expected a name field hash encoded as Hex value, was {:?}", + name_field + )) + } + } else { + Err(anyhow::anyhow!( + "Expected a Value::Map for the decoded event" + )) + } + } + #[test] fn decode_contract_message() -> Result<()> { let metadata = generate_metadata(); -- GitLab