From b3c3bb24078ae390542b2e10469bdc88ba66b9e1 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Mon, 20 Nov 2023 10:59:26 +0200 Subject: [PATCH 1/9] human readable format tool prototype --- Cargo.lock | 8 +++ Cargo.toml | 1 + data/codec/src/single/top_en.rs | 2 +- data/human-readable/Cargo.toml | 25 ++++++++ data/human-readable/src/interpret.rs | 57 +++++++++++++++++++ data/human-readable/src/lib.rs | 6 ++ data/human-readable/src/value/any_value.rs | 39 +++++++++++++ data/human-readable/src/value/enum_value.rs | 6 ++ data/human-readable/src/value/mod.rs | 9 +++ data/human-readable/src/value/single_value.rs | 41 +++++++++++++ data/human-readable/src/value/struct_value.rs | 38 +++++++++++++ .../tests/single_value_basic_test.rs | 21 +++++++ .../wrappers/blockchain_wrapper.rs | 18 +++--- .../src/types/managed/basic/elliptic_curve.rs | 14 ++--- .../standalone/upgrade/upgrade_selector.rs | 2 +- framework/meta/src/version_history.rs | 2 +- 16 files changed, 270 insertions(+), 19 deletions(-) create mode 100644 data/human-readable/Cargo.toml create mode 100644 data/human-readable/src/interpret.rs create mode 100644 data/human-readable/src/lib.rs create mode 100644 data/human-readable/src/value/any_value.rs create mode 100644 data/human-readable/src/value/enum_value.rs create mode 100644 data/human-readable/src/value/mod.rs create mode 100644 data/human-readable/src/value/single_value.rs create mode 100644 data/human-readable/src/value/struct_value.rs create mode 100644 data/human-readable/tests/single_value_basic_test.rs diff --git a/Cargo.lock b/Cargo.lock index 9dd97ceba2..388b753b00 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -1918,6 +1918,14 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "multiversx-sc-codec-human-readable" +version = "0.1.0" +dependencies = [ + "multiversx-sc-meta", + "multiversx-sc-scenario", +] + [[package]] name = "multiversx-sc-derive" version = "0.44.0" diff --git a/Cargo.toml b/Cargo.toml index ef92a8a7b9..51f5300809 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ resolver = "2" members = [ "data/codec", "data/codec-derive", + "data/human-readable", "framework/base", "framework/derive", diff --git a/data/codec/src/single/top_en.rs b/data/codec/src/single/top_en.rs index 2826c7d3d2..68d86667a1 100644 --- a/data/codec/src/single/top_en.rs +++ b/data/codec/src/single/top_en.rs @@ -4,7 +4,7 @@ use crate::{ }; use alloc::vec::Vec; -pub trait TopEncode: Sized { +pub trait TopEncode { /// Attempt to serialize the value to ouput. fn top_encode(&self, output: O) -> Result<(), EncodeError> where diff --git a/data/human-readable/Cargo.toml b/data/human-readable/Cargo.toml new file mode 100644 index 0000000000..500d566244 --- /dev/null +++ b/data/human-readable/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "multiversx-sc-codec-human-readable" +version = "0.1.0" +edition = "2018" +publish = false + +authors = ["MultiversX "] +license = "GPL-3.0-only" +readme = "README.md" +repository = "https://github.com/multiversx/mx-sdk-rs" +homepage = "https://multiversx.com/" +documentation = "https://docs.multiversx.com/" +description = "Conversions from a human readable format to the multiversx-sc-codec" +keywords = ["multiversx", "wasm", "webassembly", "blockchain", "contract"] +categories = ["cryptography::cryptocurrencies", "development-tools"] + +[dependencies] + +[dependencies.multiversx-sc-scenario] +version = "=0.44.0" +path = "../../framework/scenario" + +[dependencies.multiversx-sc-meta] +version = "=0.44.0" +path = "../../framework/meta" diff --git a/data/human-readable/src/interpret.rs b/data/human-readable/src/interpret.rs new file mode 100644 index 0000000000..6b8b4caaf2 --- /dev/null +++ b/data/human-readable/src/interpret.rs @@ -0,0 +1,57 @@ +use std::{error::Error, fmt::Display}; + +use crate::multiversx_sc::abi::{TypeContents, TypeDescription}; +use multiversx_sc_meta::abi_json::ContractAbiJson; +use multiversx_sc_scenario::num_bigint::BigUint; + +use crate::{AnyValue, SingleValue::UnsignedNumber}; + +pub fn interpret_value_according_to_abi( + input: &str, + type_name: &str, + contract_abi: &ContractAbiJson, // TODO: will need to convert to high-level ContractAbi first, this is just a prototype +) -> Result> { + let type_description = if let Some(type_description_json) = contract_abi.types.get(type_name) { + type_description_json.to_type_description(type_name) + } else { + TypeDescription { + docs: Vec::new(), + name: type_name.to_string(), + contents: TypeContents::NotSpecified, + } + }; + interpret_any_value(input, &type_description) +} + +pub fn interpret_any_value( + input: &str, + type_description: &TypeDescription, +) -> Result> { + match &type_description.contents { + TypeContents::NotSpecified => interpret_single_value(input, type_description.name.as_str()), + TypeContents::Enum(_) => todo!(), + TypeContents::Struct(_) => todo!(), + TypeContents::ExplicitEnum(_) => panic!("not supported"), + } +} + +fn interpret_single_value(input: &str, type_name: &str) -> Result> { + match type_name { + "BigUint" | "u64" | "u32" | "u16" | "usize" | "u8" => { + let value = input.parse::()?; + Ok(AnyValue::SingleValue(UnsignedNumber(value))) + }, + _ => Err(Box::new(InterpretError("unknown type"))), + } +} + +#[derive(Debug)] +pub struct InterpretError(&'static str); + +impl Display for InterpretError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl Error for InterpretError {} diff --git a/data/human-readable/src/lib.rs b/data/human-readable/src/lib.rs new file mode 100644 index 0000000000..613e656b7e --- /dev/null +++ b/data/human-readable/src/lib.rs @@ -0,0 +1,6 @@ +mod interpret; +mod value; + +use multiversx_sc_scenario::multiversx_sc; +pub use interpret::*; +pub use value::*; diff --git a/data/human-readable/src/value/any_value.rs b/data/human-readable/src/value/any_value.rs new file mode 100644 index 0000000000..97bf8ec17e --- /dev/null +++ b/data/human-readable/src/value/any_value.rs @@ -0,0 +1,39 @@ +use multiversx_sc_scenario::multiversx_sc::codec::{ + EncodeErrorHandler, NestedEncode, NestedEncodeOutput, TopEncode, TopEncodeOutput, +}; + +use crate::{EnumVariant, SingleValue, StructValue}; + +pub enum AnyValue { + SingleValue(SingleValue), + Struct(StructValue), + Enum(Box), +} + +impl NestedEncode for AnyValue { + fn dep_encode_or_handle_err(&self, dest: &mut O, h: H) -> Result<(), H::HandledErr> + where + O: NestedEncodeOutput, + H: EncodeErrorHandler, + { + match self { + AnyValue::SingleValue(sv) => sv.dep_encode_or_handle_err(dest, h), + AnyValue::Struct(s) => s.dep_encode_or_handle_err(dest, h), + AnyValue::Enum(_) => todo!(), + } + } +} + +impl TopEncode for AnyValue { + fn top_encode_or_handle_err(&self, output: O, h: H) -> Result<(), H::HandledErr> + where + O: TopEncodeOutput, + H: EncodeErrorHandler, + { + match self { + AnyValue::SingleValue(sv) => sv.top_encode_or_handle_err(output, h), + AnyValue::Struct(s) => s.top_encode_or_handle_err(output, h), + AnyValue::Enum(_) => todo!(), + } + } +} diff --git a/data/human-readable/src/value/enum_value.rs b/data/human-readable/src/value/enum_value.rs new file mode 100644 index 0000000000..864b889e8b --- /dev/null +++ b/data/human-readable/src/value/enum_value.rs @@ -0,0 +1,6 @@ +use crate::AnyValue; + +pub struct EnumVariant { + pub discriminant: usize, + pub value: AnyValue, +} diff --git a/data/human-readable/src/value/mod.rs b/data/human-readable/src/value/mod.rs new file mode 100644 index 0000000000..5c87ded058 --- /dev/null +++ b/data/human-readable/src/value/mod.rs @@ -0,0 +1,9 @@ +mod any_value; +mod enum_value; +mod single_value; +mod struct_value; + +pub use any_value::*; +pub use enum_value::*; +pub use single_value::*; +pub use struct_value::*; diff --git a/data/human-readable/src/value/single_value.rs b/data/human-readable/src/value/single_value.rs new file mode 100644 index 0000000000..fbb972daa8 --- /dev/null +++ b/data/human-readable/src/value/single_value.rs @@ -0,0 +1,41 @@ +use multiversx_sc_scenario::multiversx_sc::codec::{ + num_bigint::{BigInt, BigUint}, + EncodeErrorHandler, NestedEncode, NestedEncodeOutput, TopEncode, TopEncodeOutput, +}; + +pub enum SingleValue { + UnsignedNumber(BigUint), + SignedNumber(BigInt), + Bytes(Box<[u8]>), + Bool(bool), +} + +impl NestedEncode for SingleValue { + fn dep_encode_or_handle_err(&self, dest: &mut O, h: H) -> Result<(), H::HandledErr> + where + O: NestedEncodeOutput, + H: EncodeErrorHandler, + { + match self { + SingleValue::UnsignedNumber(bu) => bu.dep_encode_or_handle_err(dest, h), + SingleValue::SignedNumber(bi) => bi.dep_encode_or_handle_err(dest, h), + SingleValue::Bytes(bytes) => bytes.dep_encode_or_handle_err(dest, h), + SingleValue::Bool(b) => b.dep_encode_or_handle_err(dest, h), + } + } +} + +impl TopEncode for SingleValue { + fn top_encode_or_handle_err(&self, output: O, h: H) -> Result<(), H::HandledErr> + where + O: TopEncodeOutput, + H: EncodeErrorHandler, + { + match self { + SingleValue::UnsignedNumber(bu) => bu.top_encode_or_handle_err(output, h), + SingleValue::SignedNumber(bi) => bi.top_encode_or_handle_err(output, h), + SingleValue::Bytes(bytes) => bytes.top_encode_or_handle_err(output, h), + SingleValue::Bool(b) => b.top_encode_or_handle_err(output, h), + } + } +} diff --git a/data/human-readable/src/value/struct_value.rs b/data/human-readable/src/value/struct_value.rs new file mode 100644 index 0000000000..d90d5a1b85 --- /dev/null +++ b/data/human-readable/src/value/struct_value.rs @@ -0,0 +1,38 @@ +use multiversx_sc_scenario::multiversx_sc::codec::{ + EncodeErrorHandler, NestedEncode, NestedEncodeOutput, TopEncode, TopEncodeOutput, +}; + +use crate::AnyValue; + +pub struct StructValue(Vec); + +pub struct StructField { + pub name: String, + pub value: AnyValue, +} + +impl NestedEncode for StructValue { + fn dep_encode_or_handle_err(&self, dest: &mut O, h: H) -> Result<(), H::HandledErr> + where + O: NestedEncodeOutput, + H: EncodeErrorHandler, + { + for field in &self.0 { + field.value.dep_encode_or_handle_err(dest, h)?; + } + Ok(()) + } +} + +impl TopEncode for StructValue { + fn top_encode_or_handle_err(&self, output: O, h: H) -> Result<(), H::HandledErr> + where + O: TopEncodeOutput, + H: EncodeErrorHandler, + { + let mut buffer = output.start_nested_encode(); + self.dep_encode_or_handle_err(&mut buffer, h)?; + output.finalize_nested_encode(buffer); + Ok(()) + } +} diff --git a/data/human-readable/tests/single_value_basic_test.rs b/data/human-readable/tests/single_value_basic_test.rs new file mode 100644 index 0000000000..0728a30902 --- /dev/null +++ b/data/human-readable/tests/single_value_basic_test.rs @@ -0,0 +1,21 @@ +use multiversx_sc_codec_human_readable::interpret_value_according_to_abi; +use multiversx_sc_meta::abi_json::{deserialize_abi_from_json, ContractAbiJson}; +use multiversx_sc_scenario::multiversx_sc::codec::top_encode_to_vec_u8; + +const TEST_ABI_JSON: &str = r#"{ + "name": "Test", + "endpoints": [], + "events": [], + "esdtAttributes": [], + "hasCallback": false, + "types": {} +}"#; + +#[test] +fn test_display_unsigned() { + let abi_json: ContractAbiJson = deserialize_abi_from_json(TEST_ABI_JSON).unwrap(); + + let result = interpret_value_according_to_abi("123", "BigUint", &abi_json).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + assert_eq!(serialized, vec![123]); +} diff --git a/framework/base/src/contract_base/wrappers/blockchain_wrapper.rs b/framework/base/src/contract_base/wrappers/blockchain_wrapper.rs index ec80ca9cc1..79aa391b0d 100644 --- a/framework/base/src/contract_base/wrappers/blockchain_wrapper.rs +++ b/framework/base/src/contract_base/wrappers/blockchain_wrapper.rs @@ -39,7 +39,7 @@ where } } - #[deprecated(since = "0.41.0", note = "Please use method `get_caller` instead.")] + #[deprecated(since = "0.44.0", note = "Please use method `get_caller` instead.")] #[cfg(feature = "alloc")] #[inline] pub fn get_caller_legacy(&self) -> crate::types::Address { @@ -53,7 +53,7 @@ where ManagedAddress::from_handle(handle) } - #[deprecated(since = "0.41.0", note = "Please use method `get_sc_address` instead.")] + #[deprecated(since = "0.44.0", note = "Please use method `get_sc_address` instead.")] #[cfg(feature = "alloc")] #[inline] pub fn get_sc_address_legacy(&self) -> crate::types::Address { @@ -89,7 +89,7 @@ where } #[deprecated( - since = "0.41.0", + since = "0.44.0", note = "Please use method `get_shard_of_address` instead." )] #[cfg(feature = "alloc")] @@ -104,7 +104,7 @@ where } #[deprecated( - since = "0.41.0", + since = "0.44.0", note = "Please use method `is_smart_contract` instead." )] #[cfg(feature = "alloc")] @@ -118,7 +118,7 @@ where A::blockchain_api_impl().is_smart_contract(address.get_handle()) } - #[deprecated(since = "0.41.0", note = "Please use method `get_balance` instead.")] + #[deprecated(since = "0.44.0", note = "Please use method `get_balance` instead.")] #[cfg(feature = "alloc")] #[inline] pub fn get_balance_legacy(&self, address: &crate::types::Address) -> BigUint { @@ -145,7 +145,7 @@ where } #[deprecated( - since = "0.41.0", + since = "0.44.0", note = "Please use method `get_state_root_hash` instead." )] #[cfg(feature = "alloc")] @@ -161,7 +161,7 @@ where ManagedByteArray::from_handle(handle) } - #[deprecated(since = "0.41.0", note = "Please use method `get_tx_hash` instead.")] + #[deprecated(since = "0.44.0", note = "Please use method `get_tx_hash` instead.")] #[cfg(feature = "alloc")] #[inline] pub fn get_tx_hash_legacy(&self) -> crate::types::H256 { @@ -201,7 +201,7 @@ where } #[deprecated( - since = "0.41.0", + since = "0.44.0", note = "Please use method `get_block_random_seed` instead." )] #[cfg(feature = "alloc")] @@ -238,7 +238,7 @@ where } #[deprecated( - since = "0.41.0", + since = "0.44.0", note = "Please use method `get_prev_block_random_seed` instead." )] #[cfg(feature = "alloc")] diff --git a/framework/base/src/types/managed/basic/elliptic_curve.rs b/framework/base/src/types/managed/basic/elliptic_curve.rs index d70f7f738d..7c40ff1256 100644 --- a/framework/base/src/types/managed/basic/elliptic_curve.rs +++ b/framework/base/src/types/managed/basic/elliptic_curve.rs @@ -151,7 +151,7 @@ impl EllipticCurve { api.ec_is_on_curve(self.handle.clone(), x_point.handle, y_point.handle) } - #[deprecated(since = "0.41.0", note = "Please use method `scalar_mult` instead.")] + #[deprecated(since = "0.44.0", note = "Please use method `scalar_mult` instead.")] pub fn scalar_mult_legacy( &self, x_point: BigUint, @@ -199,7 +199,7 @@ impl EllipticCurve { } #[deprecated( - since = "0.41.0", + since = "0.44.0", note = "Please use method `scalar_base_mult` instead." )] pub fn scalar_base_mult_legacy(&self, data: &[u8]) -> (BigUint, BigUint) { @@ -234,7 +234,7 @@ impl EllipticCurve { ) } - #[deprecated(since = "0.41.0", note = "Please use method `marshal` instead.")] + #[deprecated(since = "0.44.0", note = "Please use method `marshal` instead.")] #[cfg(feature = "alloc")] pub fn marshal_legacy( &self, @@ -258,7 +258,7 @@ impl EllipticCurve { } #[deprecated( - since = "0.41.0", + since = "0.44.0", note = "Please use method `marshal_compressed` instead." )] #[cfg(feature = "alloc")] @@ -283,7 +283,7 @@ impl EllipticCurve { ManagedBuffer::from_handle(result_handle) } - #[deprecated(since = "0.41.0", note = "Please use method `unmarshal` instead.")] + #[deprecated(since = "0.44.0", note = "Please use method `unmarshal` instead.")] pub fn unmarshal_legacy(&self, data: &[u8]) -> (BigUint, BigUint) { let api = M::managed_type_impl(); let x_pair_handle = api.bi_new_zero(); @@ -317,7 +317,7 @@ impl EllipticCurve { } #[deprecated( - since = "0.41.0", + since = "0.44.0", note = "Please use method `unmarshal_compressed` instead." )] pub fn unmarshal_compressed_legacy(&self, data: &[u8]) -> (BigUint, BigUint) { @@ -352,7 +352,7 @@ impl EllipticCurve { ) } - #[deprecated(since = "0.41.0", note = "Please use method `generate_key` instead.")] + #[deprecated(since = "0.44.0", note = "Please use method `generate_key` instead.")] #[cfg(feature = "alloc")] pub fn generate_key_legacy(&self) -> (BigUint, BigUint, crate::types::heap::BoxedBytes) { let api = M::managed_type_impl(); diff --git a/framework/meta/src/cmd/standalone/upgrade/upgrade_selector.rs b/framework/meta/src/cmd/standalone/upgrade/upgrade_selector.rs index db8f0ef404..83c442d5ce 100644 --- a/framework/meta/src/cmd/standalone/upgrade/upgrade_selector.rs +++ b/framework/meta/src/cmd/standalone/upgrade/upgrade_selector.rs @@ -86,7 +86,7 @@ fn upgrade_post_processing(dir: &RelevantDirectory) { match dir.upgrade_in_progress { Some((_, "0.28.0")) | Some((_, "0.29.0")) | Some((_, "0.30.0")) | Some((_, "0.31.0")) | Some((_, "0.32.0")) | Some((_, "0.33.0")) | Some((_, "0.34.0")) | Some((_, "0.35.0")) - | Some((_, "0.36.0")) | Some((_, "0.37.0")) | Some((_, "0.40.0")) | Some((_, "0.41.0")) + | Some((_, "0.36.0")) | Some((_, "0.37.0")) | Some((_, "0.40.0")) | Some((_, "0.44.0")) | Some((_, "0.42.0")) | Some((_, "0.43.0")) => { print_post_processing(dir); cargo_check(dir); diff --git a/framework/meta/src/version_history.rs b/framework/meta/src/version_history.rs index 206d8017ea..037ad02f98 100644 --- a/framework/meta/src/version_history.rs +++ b/framework/meta/src/version_history.rs @@ -39,7 +39,7 @@ pub const VERSIONS: &[&str] = &[ "0.39.8", "0.40.0", "0.40.1", - "0.41.0", + "0.44.0", "0.41.1", "0.41.2", "0.41.3", From 4043d4f6218e71f9316fcc7a37e9942945fec222 Mon Sep 17 00:00:00 2001 From: Laurentiu Ciobanu Date: Thu, 5 Sep 2024 07:15:47 +0000 Subject: [PATCH 2/9] sync from older repo --- Cargo.lock | 64 +-- data/human-readable/Cargo.toml | 2 + data/human-readable/src/decode.rs | 256 ++++++++++ data/human-readable/src/defaults.rs | 136 +++++ data/human-readable/src/encode.rs | 236 +++++++++ data/human-readable/src/format.rs | 37 ++ data/human-readable/src/interpret.rs | 57 --- data/human-readable/src/lib.rs | 9 +- data/human-readable/src/value/any_value.rs | 7 +- data/human-readable/src/value/enum_value.rs | 29 ++ data/human-readable/src/value/single_value.rs | 3 + data/human-readable/src/value/struct_value.rs | 2 +- data/human-readable/tests/enum_test.rs | 471 ++++++++++++++++++ data/human-readable/tests/integrated_test.rs | 94 ++++ .../tests/single_value_basic_test.rs | 21 - .../tests/single_value_tests.rs | 202 ++++++++ data/human-readable/tests/struct_test.rs | 202 ++++++++ framework/base/src/abi/type_description.rs | 15 + .../meta/src/abi_json/build_info_abi_json.rs | 52 ++ .../meta/src/abi_json/contract_abi_json.rs | 52 ++ .../meta/src/abi_json/endpoint_abi_json.rs | 94 ++++ .../src/abi_json/esdt_attribute_abi_json.rs | 15 +- .../meta/src/abi_json/esdt_attribute_json.rs | 16 + framework/meta/src/abi_json/event_abi_json.rs | 32 ++ framework/meta/src/abi_json/type_abi_json.rs | 27 + framework/scenario/Cargo.toml | 2 +- framework/snippets/Cargo.toml | 2 +- rust-toolchain.toml | 2 + sdk/core/Cargo.toml | 2 +- 29 files changed, 1990 insertions(+), 149 deletions(-) create mode 100644 data/human-readable/src/decode.rs create mode 100644 data/human-readable/src/defaults.rs create mode 100644 data/human-readable/src/encode.rs create mode 100644 data/human-readable/src/format.rs delete mode 100644 data/human-readable/src/interpret.rs create mode 100644 data/human-readable/tests/enum_test.rs create mode 100644 data/human-readable/tests/integrated_test.rs delete mode 100644 data/human-readable/tests/single_value_basic_test.rs create mode 100644 data/human-readable/tests/single_value_tests.rs create mode 100644 data/human-readable/tests/struct_test.rs create mode 100644 rust-toolchain.toml diff --git a/Cargo.lock b/Cargo.lock index 388b753b00..6734d008da 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -1664,16 +1664,6 @@ dependencies = [ "multiversx-sc-meta", ] -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.20" @@ -1922,8 +1912,10 @@ dependencies = [ name = "multiversx-sc-codec-human-readable" version = "0.1.0" dependencies = [ + "bech32", "multiversx-sc-meta", "multiversx-sc-scenario", + "serde_json", ] [[package]] @@ -2309,29 +2301,6 @@ dependencies = [ "parent", ] -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - [[package]] name = "pathdiff" version = "0.2.1" @@ -2807,12 +2776,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "second-contract" version = "0.0.0" @@ -3019,15 +2982,6 @@ dependencies = [ "keccak", ] -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - [[package]] name = "signature" version = "1.6.4" @@ -3247,25 +3201,11 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot", "pin-project-lite", - "signal-hook-registry", "socket2 0.5.5", - "tokio-macros", "windows-sys", ] -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] - [[package]] name = "tokio-native-tls" version = "0.3.1" diff --git a/data/human-readable/Cargo.toml b/data/human-readable/Cargo.toml index 500d566244..9a6561117e 100644 --- a/data/human-readable/Cargo.toml +++ b/data/human-readable/Cargo.toml @@ -15,6 +15,8 @@ keywords = ["multiversx", "wasm", "webassembly", "blockchain", "contract"] categories = ["cryptography::cryptocurrencies", "development-tools"] [dependencies] +serde_json = { version = "1.0.68" } +bech32 = "0.9" [dependencies.multiversx-sc-scenario] version = "=0.44.0" diff --git a/data/human-readable/src/decode.rs b/data/human-readable/src/decode.rs new file mode 100644 index 0000000000..74d32a2627 --- /dev/null +++ b/data/human-readable/src/decode.rs @@ -0,0 +1,256 @@ +use std::{error::Error, fmt::Display}; + +use crate::{ + format::HumanReadableValue, + multiversx_sc::abi::{TypeContents, TypeDescription}, + SingleValue, StructField, StructValue, +}; +use bech32::FromBase32; +use multiversx_sc_scenario::{ + multiversx_sc::abi::{ContractAbi, EnumVariantDescription, StructFieldDescription}, + num_bigint::{BigInt, BigUint}, +}; + +use crate::AnyValue; + +pub fn decode_human_readable_value( + input: &HumanReadableValue, + type_name: &str, + contract_abi: &ContractAbi, +) -> Result> { + let type_description = + if let Some(type_description) = contract_abi.type_descriptions.0.get(type_name) { + type_description.to_owned() + } else { + TypeDescription { + docs: Vec::new(), + name: type_name.to_string(), + contents: TypeContents::NotSpecified, + } + }; + + decode_any_value(input, &type_description, &contract_abi) +} + +pub fn decode_any_value( + input: &HumanReadableValue, + type_description: &TypeDescription, + contract_abi: &ContractAbi, +) -> Result> { + match &type_description.contents { + TypeContents::NotSpecified => decode_single_value(input, type_description.name.as_str()), + TypeContents::Enum(variants) => decode_enum(input, &variants, &contract_abi), + TypeContents::Struct(fields) => decode_struct(input, &fields, &contract_abi), + TypeContents::ExplicitEnum(_) => panic!("not supported"), + } +} + +fn decode_single_value( + input: &HumanReadableValue, + type_name: &str, +) -> Result> { + match type_name { + "BigUint" | "u64" | "u32" | "u16" | "usize" | "u8" => { + let number_value = input + .get_value() + .as_number() + .ok_or_else(|| Box::new(DecodeError("expected unsigned number value")))?; + + let value = number_value.to_string().parse::()?; + Ok(AnyValue::SingleValue(SingleValue::UnsignedNumber(value))) + }, + "BigInt" | "i64" | "i32" | "i16" | "isize" | "i8" => { + let number_value = input + .get_value() + .as_number() + .ok_or_else(|| Box::new(DecodeError("expected number value")))?; + + let value = number_value.to_string().parse::()?; + Ok(AnyValue::SingleValue(SingleValue::SignedNumber(value))) + }, + "ManagedBuffer" => { + let array_value = input + .get_value() + .as_array() + .ok_or_else(|| Box::new(DecodeError("expected bytes value")))?; + + let mut bytes = vec![0u8; array_value.len()]; + for (i, value) in array_value.iter().enumerate() { + let number_value = value + .as_u64() + .ok_or_else(|| Box::new(DecodeError("expected byte value")))?; + if number_value > 255 { + return Err(Box::new(DecodeError("expected byte value"))); + } + bytes[i] = number_value as u8; + } + + Ok(AnyValue::SingleValue(SingleValue::Bytes(bytes.into()))) + }, + "string" | "utf-8 string" => { + let str_value = input + .get_value() + .as_str() + .ok_or_else(|| Box::new(DecodeError("expected string value")))?; + + Ok(AnyValue::SingleValue(SingleValue::String( + str_value.to_string(), + ))) + }, + "Address" => { + let str_value = input + .get_value() + .as_str() + .ok_or_else(|| Box::new(DecodeError("expected string value")))?; + + let (_, address_bytes_u5, _) = bech32::decode(str_value) + .map_err(|_| Box::new(DecodeError("failed to parse address")))?; + let address_bytes = Vec::::from_base32(&address_bytes_u5) + .map_err(|_| Box::new(DecodeError("failed to parse address")))?; + + if address_bytes.len() != 32 { + return Err(Box::new(DecodeError( + "Invalid address length after decoding", + ))); + } + + Ok(AnyValue::SingleValue(SingleValue::Bytes( + address_bytes.into(), + ))) + }, + "bool" => { + let bool_value = input + .get_value() + .as_bool() + .ok_or_else(|| Box::new(DecodeError("expected bool value")))?; + + Ok(AnyValue::SingleValue(SingleValue::Bool(bool_value.into()))) + }, + _ => Err(Box::new(DecodeError("unknown type"))), + } +} + +pub fn decode_struct( + input: &HumanReadableValue, + fields: &Vec, + contract_abi: &ContractAbi, +) -> Result> { + let mut field_values: Vec = vec![]; + + for field in fields.iter() { + let value = input + .child(&field.name) + .ok_or_else(|| Box::new(DecodeError("missing field")))?; + let value = decode_human_readable_value(&value, &field.field_type, &contract_abi)?; + field_values.push(StructField { + name: field.name.clone(), + value, + }); + } + + Ok(AnyValue::Struct(StructValue(field_values))) +} + +pub fn decode_enum( + input: &HumanReadableValue, + variants: &Vec, + contract_abi: &ContractAbi, +) -> Result> { + if input.get_value().is_string() { + let discriminant_name = input.get_value().as_str().unwrap(); + let variant = variants + .iter() + .find(|el| el.name == discriminant_name) + .ok_or_else(|| Box::new(DecodeError("enum variant not found")))?; + + if !variant.is_empty_variant() { + return Err(Box::new(DecodeError("enum variant is not a tuple variant"))); + } + + return Ok(AnyValue::Enum(Box::new(crate::EnumVariant { + discriminant: variant.discriminant, + value: AnyValue::None, + }))); + } + + if !input.get_value().is_object() { + return Err(Box::new(DecodeError( + "expected object or string value for enum", + ))); + } + + let obj_value = input.get_value().as_object().unwrap(); + if obj_value.keys().len() != 1 { + return Err(Box::new(DecodeError( + "expected object with single key for enum", + ))); + } + + let discriminant_name = obj_value.keys().next().unwrap().as_str(); + let variant = variants + .iter() + .find(|el| el.name == discriminant_name) + .ok_or_else(|| Box::new(DecodeError("enum variant not found")))?; + + // handle tuple with only one field as a special case (we don't need a wrapper array) + if variant.is_tuple_variant() && variant.fields.len() == 1 { + let value = input.child(variant.name.as_str()).unwrap(); + let value = + decode_human_readable_value(&value, &variant.fields[0].field_type, contract_abi)?; + return Ok(AnyValue::Enum(Box::new(crate::EnumVariant { + discriminant: variant.discriminant, + value, + }))); + } else if variant.is_tuple_variant() { + let value = input.child(variant.name.as_str()).unwrap(); + let value = value + .get_value() + .as_array() + .ok_or_else(|| Box::new(DecodeError("expected array for enum tuple variant")))?; + + if value.len() != variant.fields.len() { + return Err(Box::new(DecodeError( + "expected array with the same length as the tuple variant fields", + ))); + } + + let mut field_values: Vec = vec![]; + for (i, field) in variant.fields.iter().enumerate() { + let value = value.get(i).unwrap(); + let value = decode_human_readable_value( + &(value.to_owned().into()), + &field.field_type, + &contract_abi, + )?; + field_values.push(StructField { + name: field.name.clone(), + value, + }); + } + + return Ok(AnyValue::Enum(Box::new(crate::EnumVariant { + discriminant: variant.discriminant, + value: AnyValue::Struct(StructValue(field_values)), + }))); + } + + // is not empty and is not a tuple so just try to parse a struct from the fields + let value = input.child(variant.name.as_str()).unwrap(); + let value = decode_struct(&value, &variant.fields, contract_abi)?; + + Ok(AnyValue::Enum(Box::new(crate::EnumVariant { + discriminant: variant.discriminant, + value, + }))) +} + +#[derive(Debug)] +pub struct DecodeError(&'static str); + +impl Display for DecodeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl Error for DecodeError {} diff --git a/data/human-readable/src/defaults.rs b/data/human-readable/src/defaults.rs new file mode 100644 index 0000000000..42b68513fb --- /dev/null +++ b/data/human-readable/src/defaults.rs @@ -0,0 +1,136 @@ +use std::{error::Error, fmt::Display}; + +use multiversx_sc_scenario::{ + multiversx_sc::abi::{ + ContractAbi, EnumVariantDescription, StructFieldDescription, TypeContents, TypeDescription, + }, + num_bigint::{BigInt, BigUint}, +}; + +use crate::{AnyValue, SingleValue, StructField, StructValue}; + +pub fn default_value_for_abi_type( + type_name: &str, + contract_abi: &ContractAbi, +) -> Result> { + let type_description = + if let Some(type_description) = contract_abi.type_descriptions.0.get(type_name) { + type_description.to_owned() + } else { + TypeDescription { + docs: Vec::new(), + name: type_name.to_string(), + contents: TypeContents::NotSpecified, + } + }; + + default_value_for_any_value(&type_description, contract_abi) +} + +pub fn default_value_for_any_value( + type_description: &TypeDescription, + contract_abi: &ContractAbi, +) -> Result> { + match &type_description.contents { + TypeContents::NotSpecified => { + default_value_for_single_value(type_description.name.as_str()) + }, + TypeContents::Enum(variants) => default_value_for_enum(&variants, contract_abi), + TypeContents::Struct(fields) => default_value_for_struct(&fields, contract_abi), + TypeContents::ExplicitEnum(_) => panic!("not supported"), + } +} + +pub fn default_value_for_single_value(type_name: &str) -> Result> { + match type_name { + "BigUint" | "u64" | "u32" | "u16" | "usize" | "u8" => Ok(AnyValue::SingleValue( + SingleValue::UnsignedNumber(BigUint::default()), + )), + "BigInt" | "i64" | "i32" | "i16" | "isize" | "i8" => Ok(AnyValue::SingleValue( + SingleValue::SignedNumber(BigInt::default()), + )), + "ManagedBuffer" => Ok(AnyValue::SingleValue(SingleValue::Bytes(Vec::new().into()))), + "string" | "utf-8 string" => Ok(AnyValue::SingleValue(SingleValue::String("".to_owned()))), + "Address" => Ok(AnyValue::SingleValue(SingleValue::Bytes( + vec![0u8; 32].into(), + ))), + "bool" => Ok(AnyValue::SingleValue(SingleValue::Bool(false))), + _ => Err(Box::new(DefaultValueError("unknown type"))), + } +} + +pub fn default_value_for_struct( + fields: &Vec, + contract_abi: &ContractAbi, +) -> Result> { + let mut field_values: Vec = vec![]; + + for field in fields.iter() { + let value = default_value_for_abi_type(&field.field_type, &contract_abi)?; + field_values.push(StructField { + name: field.name.clone(), + value, + }); + } + + Ok(AnyValue::Struct(StructValue(field_values))) +} + +pub fn default_value_for_enum( + variants: &Vec, + contract_abi: &ContractAbi, +) -> Result> { + let variant = variants + .iter() + .find(|el| el.discriminant == 0) + .ok_or_else(|| Box::new(DefaultValueError("enum variant not found")))?; + + if variant.is_empty_variant() { + return Ok(AnyValue::Enum(Box::new(crate::EnumVariant { + discriminant: variant.discriminant, + value: AnyValue::None, + }))); + } + + // handle tuple with only one field as a special case (we don't need a wrapper array) + if variant.is_tuple_variant() && variant.fields.len() == 1 { + let value = default_value_for_abi_type(&variant.fields[0].field_type, contract_abi)?; + return Ok(AnyValue::Enum(Box::new(crate::EnumVariant { + discriminant: variant.discriminant, + value, + }))); + } else if variant.is_tuple_variant() { + let mut field_values: Vec = vec![]; + for field in variant.fields.iter() { + let value = default_value_for_abi_type(&field.field_type, &contract_abi)?; + field_values.push(StructField { + name: field.name.clone(), + value, + }); + } + + return Ok(AnyValue::Enum(Box::new(crate::EnumVariant { + discriminant: variant.discriminant, + value: AnyValue::Struct(StructValue(field_values)), + }))); + } + + // is not empty and is not a tuple so just try to parse a struct from the fields + let value = default_value_for_struct(&variant.fields, contract_abi)?; + + Ok(AnyValue::Enum(Box::new(crate::EnumVariant { + discriminant: variant.discriminant, + value, + }))) +} + +#[derive(Debug)] +pub struct DefaultValueError(&'static str); + +impl Display for DefaultValueError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl Error for DefaultValueError {} diff --git a/data/human-readable/src/encode.rs b/data/human-readable/src/encode.rs new file mode 100644 index 0000000000..2701df684d --- /dev/null +++ b/data/human-readable/src/encode.rs @@ -0,0 +1,236 @@ +use std::{error::Error, fmt::Display}; + +use bech32::ToBase32; +use multiversx_sc_scenario::multiversx_sc::abi::{ + ContractAbi, EnumVariantDescription, StructFieldDescription, TypeContents, TypeDescription, +}; +use serde_json::{Map, Value as JsonValue}; + +use crate::{format::HumanReadableValue, AnyValue, SingleValue}; + +pub fn encode_human_readable_value( + input: &AnyValue, + type_name: &str, + contract_abi: &ContractAbi, +) -> Result> { + let type_description = + if let Some(type_description) = contract_abi.type_descriptions.0.get(type_name) { + type_description.to_owned() + } else { + TypeDescription { + docs: Vec::new(), + name: type_name.to_string(), + contents: TypeContents::NotSpecified, + } + }; + + encode_any_value(input, &type_description, &contract_abi) +} + +pub fn encode_any_value( + input: &AnyValue, + type_description: &TypeDescription, + contract_abi: &ContractAbi, +) -> Result> { + match &type_description.contents { + TypeContents::NotSpecified => encode_single_value(input, type_description.name.as_str()), + TypeContents::Enum(variants) => encode_enum(input, &variants, &contract_abi), + TypeContents::Struct(fields) => encode_struct(input, &fields, &contract_abi), + TypeContents::ExplicitEnum(_) => panic!("not supported"), + } +} + +fn encode_single_value( + input: &AnyValue, + type_name: &str, +) -> Result> { + match type_name { + "BigUint" | "u64" | "u32" | "u16" | "usize" | "u8" => { + let AnyValue::SingleValue(value) = input else { + return Err(Box::new(EncodeError("expected single value"))); + }; + let SingleValue::UnsignedNumber(value) = value else { + return Err(Box::new(EncodeError("expected unsigned number value"))); + }; + + // could be biguint, so we convert to string first + let json_value: JsonValue = serde_json::from_str(&value.to_string()) + .map_err(|_| Box::new(EncodeError("expected number value")))?; + + Ok(json_value.into()) + }, + "BigInt" | "i64" | "i32" | "i16" | "isize" | "i8" => { + let AnyValue::SingleValue(value) = input else { + return Err(Box::new(EncodeError("expected single value"))); + }; + let SingleValue::SignedNumber(value) = value else { + return Err(Box::new(EncodeError("expected signed number value"))); + }; + + // could be bigint, so we convert to string first + let json_value: JsonValue = serde_json::from_str(&value.to_string()) + .map_err(|_| Box::new(EncodeError("expected number value")))?; + + Ok(json_value.into()) + }, + "ManagedBuffer" => { + let AnyValue::SingleValue(value) = input else { + return Err(Box::new(EncodeError("expected single value"))); + }; + let SingleValue::Bytes(value) = value else { + return Err(Box::new(EncodeError("expected bytes value"))); + }; + + Ok(JsonValue::Array( + value + .iter() + .map(|b| JsonValue::Number(b.to_owned().into())) + .collect(), + ) + .into()) + }, + "string" | "utf-8 string" => { + let AnyValue::SingleValue(value) = input else { + return Err(Box::new(EncodeError("expected single value"))); + }; + let SingleValue::String(value) = value else { + return Err(Box::new(EncodeError("expected string value"))); + }; + + Ok(JsonValue::String(value.to_owned()).into()) + }, + "Address" => { + let AnyValue::SingleValue(value) = input else { + return Err(Box::new(EncodeError("expected single value"))); + }; + let SingleValue::Bytes(value) = value else { + return Err(Box::new(EncodeError("expected bytes value"))); + }; + + let address = bech32::encode("erd", value.to_base32(), bech32::Variant::Bech32) + .map_err(|_| Box::new(EncodeError("failed to encode address")))?; + + Ok(JsonValue::String(address).into()) + }, + "bool" => { + let AnyValue::SingleValue(value) = input else { + return Err(Box::new(EncodeError("expected single value"))); + }; + let SingleValue::Bool(value) = value else { + return Err(Box::new(EncodeError("expected bool value"))); + }; + + Ok(JsonValue::Bool(value.to_owned()).into()) + }, + _ => { + println!("unknown type: {}", type_name); + Err(Box::new(EncodeError("unknown type"))) + }, + } +} + +pub fn encode_struct( + input: &AnyValue, + fields: &Vec, + contract_abi: &ContractAbi, +) -> Result> { + let AnyValue::Struct(struct_value) = input else { + return Err(Box::new(EncodeError("expected struct value"))); + }; + let mut struct_fields = struct_value.0.iter(); + + let mut field_values: Map = Map::new(); + + for field in fields.iter() { + let value = struct_fields + .find(|f| f.name == field.name) + .ok_or_else(|| Box::new(EncodeError("missing field")))?; + + let value = encode_human_readable_value(&value.value, &field.field_type, contract_abi)?; + field_values.insert(field.name.to_owned(), value.get_value().to_owned()); + } + + Ok(JsonValue::Object(field_values).into()) +} + +pub fn encode_enum( + input: &AnyValue, + variants: &Vec, + contract_abi: &ContractAbi, +) -> Result> { + let AnyValue::Enum(enum_value) = input else { + return Err(Box::new(EncodeError("expected enum value"))); + }; + let variant = variants + .iter() + .find(|v| v.discriminant == enum_value.discriminant) + .ok_or_else(|| Box::new(EncodeError("missing variant")))?; + + if variant.is_empty_variant() { + return Ok(JsonValue::String(variant.name.to_owned()).into()); + } + + if variant.is_tuple_variant() && variant.fields.len() == 1 { + let value = encode_human_readable_value( + &enum_value.value, + &variant.fields[0].field_type, + contract_abi, + )?; + return Ok(JsonValue::Object( + vec![(variant.name.to_owned(), value.get_value().to_owned())] + .into_iter() + .collect(), + ) + .into()); + } + + if variant.is_tuple_variant() { + let AnyValue::Struct(variant_fields) = &enum_value.value else { + return Err(Box::new(EncodeError("expected struct value"))); + }; + + let mut field_values: Vec = vec![]; + + for (field, field_type) in variant.fields.iter().zip(variant_fields.0.iter()) { + let value = + encode_human_readable_value(&field_type.value, &field.field_type, contract_abi)?; + field_values.push(value.get_value().to_owned()); + } + + return Ok(JsonValue::Object( + vec![(variant.name.to_owned(), JsonValue::Array(field_values))] + .into_iter() + .collect(), + ) + .into()); + } + + let AnyValue::Struct(variant_fields) = &enum_value.value else { + return Err(Box::new(EncodeError("expected struct value"))); + }; + + let mut field_values: Map = Map::new(); + for (field, field_type) in variant.fields.iter().zip(variant_fields.0.iter()) { + let value = + encode_human_readable_value(&field_type.value, &field.field_type, contract_abi)?; + field_values.insert(field.name.to_owned(), value.get_value().to_owned()); + } + + Ok(JsonValue::Object( + vec![(variant.name.to_owned(), JsonValue::Object(field_values))] + .into_iter() + .collect(), + ) + .into()) +} + +#[derive(Debug)] +pub struct EncodeError(&'static str); + +impl Display for EncodeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl Error for EncodeError {} diff --git a/data/human-readable/src/format.rs b/data/human-readable/src/format.rs new file mode 100644 index 0000000000..a6dff0483a --- /dev/null +++ b/data/human-readable/src/format.rs @@ -0,0 +1,37 @@ +pub use serde_json::Value as JsonValue; +use std::str::FromStr; + +pub struct HumanReadableValue { + value: JsonValue, +} + +impl FromStr for HumanReadableValue { + type Err = String; + + fn from_str(s: &str) -> Result { + let value = serde_json::from_str(s).map_err(|e| e.to_string())?; + Ok(HumanReadableValue { value }) + } +} + +impl From for HumanReadableValue { + fn from(value: JsonValue) -> Self { + HumanReadableValue { value } + } +} + +impl HumanReadableValue { + pub fn get_value(&self) -> &JsonValue { + &self.value + } + + pub fn child(&self, key: &str) -> Option { + self.value.get(key).map(|value| HumanReadableValue { + value: value.clone(), + }) + } + + pub fn to_string(&self) -> String { + self.value.to_string() + } +} diff --git a/data/human-readable/src/interpret.rs b/data/human-readable/src/interpret.rs deleted file mode 100644 index 6b8b4caaf2..0000000000 --- a/data/human-readable/src/interpret.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::{error::Error, fmt::Display}; - -use crate::multiversx_sc::abi::{TypeContents, TypeDescription}; -use multiversx_sc_meta::abi_json::ContractAbiJson; -use multiversx_sc_scenario::num_bigint::BigUint; - -use crate::{AnyValue, SingleValue::UnsignedNumber}; - -pub fn interpret_value_according_to_abi( - input: &str, - type_name: &str, - contract_abi: &ContractAbiJson, // TODO: will need to convert to high-level ContractAbi first, this is just a prototype -) -> Result> { - let type_description = if let Some(type_description_json) = contract_abi.types.get(type_name) { - type_description_json.to_type_description(type_name) - } else { - TypeDescription { - docs: Vec::new(), - name: type_name.to_string(), - contents: TypeContents::NotSpecified, - } - }; - interpret_any_value(input, &type_description) -} - -pub fn interpret_any_value( - input: &str, - type_description: &TypeDescription, -) -> Result> { - match &type_description.contents { - TypeContents::NotSpecified => interpret_single_value(input, type_description.name.as_str()), - TypeContents::Enum(_) => todo!(), - TypeContents::Struct(_) => todo!(), - TypeContents::ExplicitEnum(_) => panic!("not supported"), - } -} - -fn interpret_single_value(input: &str, type_name: &str) -> Result> { - match type_name { - "BigUint" | "u64" | "u32" | "u16" | "usize" | "u8" => { - let value = input.parse::()?; - Ok(AnyValue::SingleValue(UnsignedNumber(value))) - }, - _ => Err(Box::new(InterpretError("unknown type"))), - } -} - -#[derive(Debug)] -pub struct InterpretError(&'static str); - -impl Display for InterpretError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - -impl Error for InterpretError {} diff --git a/data/human-readable/src/lib.rs b/data/human-readable/src/lib.rs index 613e656b7e..6a810391bf 100644 --- a/data/human-readable/src/lib.rs +++ b/data/human-readable/src/lib.rs @@ -1,6 +1,11 @@ -mod interpret; +mod decode; +mod defaults; +mod encode; +pub mod format; mod value; +pub use decode::*; +pub use defaults::*; +pub use encode::*; use multiversx_sc_scenario::multiversx_sc; -pub use interpret::*; pub use value::*; diff --git a/data/human-readable/src/value/any_value.rs b/data/human-readable/src/value/any_value.rs index 97bf8ec17e..165143ba85 100644 --- a/data/human-readable/src/value/any_value.rs +++ b/data/human-readable/src/value/any_value.rs @@ -5,6 +5,7 @@ use multiversx_sc_scenario::multiversx_sc::codec::{ use crate::{EnumVariant, SingleValue, StructValue}; pub enum AnyValue { + None, SingleValue(SingleValue), Struct(StructValue), Enum(Box), @@ -17,9 +18,10 @@ impl NestedEncode for AnyValue { H: EncodeErrorHandler, { match self { + AnyValue::None => Ok(()), AnyValue::SingleValue(sv) => sv.dep_encode_or_handle_err(dest, h), AnyValue::Struct(s) => s.dep_encode_or_handle_err(dest, h), - AnyValue::Enum(_) => todo!(), + AnyValue::Enum(e) => e.dep_encode_or_handle_err(dest, h), } } } @@ -31,9 +33,10 @@ impl TopEncode for AnyValue { H: EncodeErrorHandler, { match self { + AnyValue::None => Ok(()), AnyValue::SingleValue(sv) => sv.top_encode_or_handle_err(output, h), AnyValue::Struct(s) => s.top_encode_or_handle_err(output, h), - AnyValue::Enum(_) => todo!(), + AnyValue::Enum(e) => e.top_encode_or_handle_err(output, h), } } } diff --git a/data/human-readable/src/value/enum_value.rs b/data/human-readable/src/value/enum_value.rs index 864b889e8b..0a85d668e1 100644 --- a/data/human-readable/src/value/enum_value.rs +++ b/data/human-readable/src/value/enum_value.rs @@ -1,6 +1,35 @@ +use multiversx_sc_scenario::multiversx_sc::codec::{ + EncodeErrorHandler, NestedEncode, NestedEncodeOutput, TopEncode, TopEncodeOutput, +}; + use crate::AnyValue; pub struct EnumVariant { pub discriminant: usize, pub value: AnyValue, } + +impl NestedEncode for EnumVariant { + fn dep_encode_or_handle_err(&self, dest: &mut O, h: H) -> Result<(), H::HandledErr> + where + O: NestedEncodeOutput, + H: EncodeErrorHandler, + { + (self.discriminant as u8).dep_encode_or_handle_err(dest, h)?; + self.value.dep_encode_or_handle_err(dest, h)?; + Ok(()) + } +} + +impl TopEncode for EnumVariant { + fn top_encode_or_handle_err(&self, output: O, h: H) -> Result<(), H::HandledErr> + where + O: TopEncodeOutput, + H: EncodeErrorHandler, + { + let mut buffer = output.start_nested_encode(); + self.dep_encode_or_handle_err(&mut buffer, h)?; + output.finalize_nested_encode(buffer); + Ok(()) + } +} diff --git a/data/human-readable/src/value/single_value.rs b/data/human-readable/src/value/single_value.rs index fbb972daa8..a46e811bbd 100644 --- a/data/human-readable/src/value/single_value.rs +++ b/data/human-readable/src/value/single_value.rs @@ -7,6 +7,7 @@ pub enum SingleValue { UnsignedNumber(BigUint), SignedNumber(BigInt), Bytes(Box<[u8]>), + String(String), Bool(bool), } @@ -20,6 +21,7 @@ impl NestedEncode for SingleValue { SingleValue::UnsignedNumber(bu) => bu.dep_encode_or_handle_err(dest, h), SingleValue::SignedNumber(bi) => bi.dep_encode_or_handle_err(dest, h), SingleValue::Bytes(bytes) => bytes.dep_encode_or_handle_err(dest, h), + SingleValue::String(s) => s.as_bytes().dep_encode_or_handle_err(dest, h), SingleValue::Bool(b) => b.dep_encode_or_handle_err(dest, h), } } @@ -35,6 +37,7 @@ impl TopEncode for SingleValue { SingleValue::UnsignedNumber(bu) => bu.top_encode_or_handle_err(output, h), SingleValue::SignedNumber(bi) => bi.top_encode_or_handle_err(output, h), SingleValue::Bytes(bytes) => bytes.top_encode_or_handle_err(output, h), + SingleValue::String(s) => s.as_bytes().top_encode_or_handle_err(output, h), SingleValue::Bool(b) => b.top_encode_or_handle_err(output, h), } } diff --git a/data/human-readable/src/value/struct_value.rs b/data/human-readable/src/value/struct_value.rs index d90d5a1b85..3f1c6603c9 100644 --- a/data/human-readable/src/value/struct_value.rs +++ b/data/human-readable/src/value/struct_value.rs @@ -4,7 +4,7 @@ use multiversx_sc_scenario::multiversx_sc::codec::{ use crate::AnyValue; -pub struct StructValue(Vec); +pub struct StructValue(pub Vec); pub struct StructField { pub name: String, diff --git a/data/human-readable/tests/enum_test.rs b/data/human-readable/tests/enum_test.rs new file mode 100644 index 0000000000..c360d645d1 --- /dev/null +++ b/data/human-readable/tests/enum_test.rs @@ -0,0 +1,471 @@ +use multiversx_sc_codec_human_readable::{ + decode_human_readable_value, default_value_for_abi_type, encode_human_readable_value, + format::HumanReadableValue, AnyValue, EnumVariant, SingleValue, StructField, StructValue, +}; +use multiversx_sc_meta::abi_json::deserialize_abi_from_json; +use multiversx_sc_scenario::multiversx_sc::{abi::ContractAbi, codec::top_encode_to_vec_u8}; + +const ABI_JSON: &str = r#"{ + "name": "Test", + "endpoints": [], + "events": [], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "TwoU8s": { + "type": "struct", + "fields": [ + { + "name": "first", + "type": "u8" + }, + { + "name": "second", + "type": "u8" + } + ] + }, + "SimpleEnum": { + "type": "enum", + "variants": [ + { + "name": "First", + "discriminant": 0 + }, + { + "name": "Second", + "discriminant": 1 + } + ] + }, + "EnumWithStruct": { + "type": "enum", + "variants": [ + { + "name": "First", + "discriminant": 0 + }, + { + "name": "Second", + "discriminant": 1, + "fields": [ + { + "name": "first", + "type": "u8" + }, + { + "name": "second", + "type": "u8" + } + ] + } + ] + }, + "EnumWithTupleStruct": { + "type": "enum", + "variants": [ + { + "name": "First", + "discriminant": 0 + }, + { + "name": "Second", + "discriminant": 1, + "fields": [ + { + "name": "0", + "type": "TwoU8s" + } + ] + } + ] + }, + "EnumWithTupleValues": { + "type": "enum", + "variants": [ + { + "name": "First", + "discriminant": 0 + }, + { + "name": "Second", + "discriminant": 1, + "fields": [ + { + "name": "0", + "type": "u8" + }, + { + "name": "1", + "type": "u8" + } + ] + } + ] + }, + "EnumWithTupleValuesAndStruct": { + "type": "enum", + "variants": [ + { + "name": "First", + "discriminant": 0 + }, + { + "name": "Second", + "discriminant": 1, + "fields": [ + { + "name": "0", + "type": "u8" + }, + { + "name": "1", + "type": "u8" + }, + { + "name": "2", + "type": "TwoU8s" + } + ] + } + ] + }, + "EnumWithDiscriminantOnlyDefault": { + "type": "enum", + "variants": [ + { + "name": "First", + "discriminant": 0 + }, + { + "name": "Second", + "discriminant": 1 + } + ] + }, + "EnumWithStructDefault": { + "type": "enum", + "variants": [ + { + "name": "First", + "discriminant": 0, + "fields": [ + { + "name": "first", + "type": "u8" + }, + { + "name": "second", + "type": "u8" + } + ] + }, + { + "name": "Second", + "discriminant": 1 + } + ] + }, + "EnumWithTupleValuesDefault": { + "type": "enum", + "variants": [ + { + "name": "First", + "discriminant": 0, + "fields": [ + { + "name": "0", + "type": "u8" + }, + { + "name": "1", + "type": "u8" + } + ] + }, + { + "name": "Second", + "discriminant": 1 + } + ] + } + } +}"#; + +#[test] +fn serialize_enum_only_discriminant() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = r#""Second""#.parse::().unwrap(); + + let result = decode_human_readable_value(&value, "SimpleEnum", &abi).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + assert_eq!(serialized, vec![1]); +} + +#[test] +fn deserialize_enum_only_discriminant() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = AnyValue::Enum(Box::new(EnumVariant { + discriminant: 1, + value: AnyValue::None, + })); + + let result = encode_human_readable_value(&value, "SimpleEnum", &abi).unwrap(); + assert_eq!(result.to_string(), "\"Second\""); +} + +#[test] +fn serialize_enum_with_struct() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = + r#"{ "Second": { "first": 1, "second": 2 } }"#.parse::().unwrap(); + + let result = decode_human_readable_value(&value, "EnumWithStruct", &abi).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + assert_eq!( + serialized, + vec![ + 1, // discriminant + 0, 0, 0, 1, 1, // first + 0, 0, 0, 1, 2 // second + ] + ); +} + +#[test] +fn deserialize_enum_with_struct() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = AnyValue::Enum(Box::new(EnumVariant { + discriminant: 1, + value: AnyValue::Struct(StructValue(vec![ + StructField { + name: "first".to_owned(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(1u8.into())), + }, + StructField { + name: "second".to_owned(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(2u8.into())), + }, + ])), + })); + + let result = encode_human_readable_value(&value, "EnumWithStruct", &abi).unwrap(); + assert_eq!(result.to_string(), r#"{"Second":{"first":1,"second":2}}"#); +} + +#[test] +fn serialize_enum_tuple_with_struct() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = + r#"{ "Second": { "first": 1, "second": 2 } }"#.parse::().unwrap(); + + let result = decode_human_readable_value(&value, "EnumWithTupleStruct", &abi).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + assert_eq!( + serialized, + vec![ + 1, // discriminant + 0, 0, 0, 1, 1, // first + 0, 0, 0, 1, 2 // second + ] + ); +} + +#[test] +fn deserialize_enum_tuple_with_struct() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = AnyValue::Enum(Box::new(EnumVariant { + discriminant: 1, + value: AnyValue::Struct(StructValue(vec![ + StructField { + name: "first".to_owned(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(1u8.into())), + }, + StructField { + name: "second".to_owned(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(2u8.into())), + }, + ])), + })); + + let result = encode_human_readable_value(&value, "EnumWithTupleStruct", &abi).unwrap(); + assert_eq!(result.to_string(), r#"{"Second":{"first":1,"second":2}}"#); +} + +#[test] +fn serialize_enum_tuple_with_values() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = r#"{ "Second": [1, 2] }"#.parse::().unwrap(); + + let result = decode_human_readable_value(&value, "EnumWithTupleValues", &abi).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + assert_eq!( + serialized, + vec![ + 1, // discriminant + 0, 0, 0, 1, 1, // 0 + 0, 0, 0, 1, 2 // 1 + ] + ); +} + +#[test] +fn deserialize_enum_tuple_with_values() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = AnyValue::Enum(Box::new(EnumVariant { + discriminant: 1, + value: AnyValue::Struct(StructValue(vec![ + StructField { + name: "0".to_owned(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(1u8.into())), + }, + StructField { + name: "1".to_owned(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(2u8.into())), + }, + ])), + })); + + let result = encode_human_readable_value(&value, "EnumWithTupleValues", &abi).unwrap(); + assert_eq!(result.to_string(), r#"{"Second":[1,2]}"#); +} + +#[test] +fn serialize_enum_tuple_with_values_and_struct() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = r#"{ "Second": [1, 2, { "first": 1, "second": 2 }] }"# + .parse::() + .unwrap(); + + let result = decode_human_readable_value(&value, "EnumWithTupleValuesAndStruct", &abi).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + assert_eq!( + serialized, + vec![ + 1, // discriminant + 0, 0, 0, 1, 1, // 0 + 0, 0, 0, 1, 2, // 1 + 0, 0, 0, 1, 1, // 2.first + 0, 0, 0, 1, 2 // 2.second + ] + ); +} + +#[test] +fn deserialize_enum_tuple_with_values_and_struct() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = AnyValue::Enum(Box::new(EnumVariant { + discriminant: 1, + value: AnyValue::Struct(StructValue(vec![ + StructField { + name: "0".to_owned(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(1u8.into())), + }, + StructField { + name: "1".to_owned(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(2u8.into())), + }, + StructField { + name: "2".to_owned(), + value: AnyValue::Struct(StructValue(vec![ + StructField { + name: "first".to_owned(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(1u8.into())), + }, + StructField { + name: "second".to_owned(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(2u8.into())), + }, + ])), + }, + ])), + })); + + let result = encode_human_readable_value(&value, "EnumWithTupleValuesAndStruct", &abi).unwrap(); + assert_eq!( + result.to_string(), + r#"{"Second":[1,2,{"first":1,"second":2}]}"# + ); +} + +#[test] +fn default_enum_discriminant_only() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = default_value_for_abi_type("EnumWithDiscriminantOnlyDefault", &abi).unwrap(); + + let AnyValue::Enum(variant) = value else { + panic!("Expected enum variant"); + }; + assert_eq!(variant.discriminant, 0); + match variant.value { + AnyValue::None => {}, + _ => panic!("Expected value none"), + }; +} + +#[test] +fn default_enum_with_struct() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = default_value_for_abi_type("EnumWithStructDefault", &abi).unwrap(); + + let AnyValue::Enum(variant) = value else { + panic!("Expected enum variant"); + }; + assert_eq!(variant.discriminant, 0); + let AnyValue::Struct(StructValue(fields)) = variant.value else { + panic!("Expected struct value"); + }; + assert_eq!(fields.len(), 2); + + assert_eq!(fields[0].name, "first"); + let AnyValue::SingleValue(SingleValue::UnsignedNumber(num)) = &fields[0].value else { + panic!("Expected unsigned number"); + }; + assert_eq!(*num, 0u8.into()); + + assert_eq!(fields[1].name, "second"); + let AnyValue::SingleValue(SingleValue::UnsignedNumber(num)) = &fields[1].value else { + panic!("Expected unsigned number"); + }; + assert_eq!(*num, 0u8.into()); +} + +#[test] +fn default_enum_with_tuple_values() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = default_value_for_abi_type("EnumWithTupleValuesDefault", &abi).unwrap(); + + let AnyValue::Enum(variant) = value else { + panic!("Expected enum variant"); + }; + assert_eq!(variant.discriminant, 0); + let AnyValue::Struct(StructValue(fields)) = variant.value else { + panic!("Expected struct value"); + }; + assert_eq!(fields.len(), 2); + + assert_eq!(fields[0].name, "0"); + let AnyValue::SingleValue(SingleValue::UnsignedNumber(num)) = &fields[0].value else { + panic!("Expected unsigned number"); + }; + assert_eq!(*num, 0u8.into()); + + assert_eq!(fields[1].name, "1"); + let AnyValue::SingleValue(SingleValue::UnsignedNumber(num)) = &fields[1].value else { + panic!("Expected unsigned number"); + }; + assert_eq!(*num, 0u8.into()); +} diff --git a/data/human-readable/tests/integrated_test.rs b/data/human-readable/tests/integrated_test.rs new file mode 100644 index 0000000000..a864d203d4 --- /dev/null +++ b/data/human-readable/tests/integrated_test.rs @@ -0,0 +1,94 @@ +use multiversx_sc_codec_human_readable::{ + decode_human_readable_value, default_value_for_abi_type, encode_human_readable_value, +}; +use multiversx_sc_meta::abi_json::deserialize_abi_from_json; +use multiversx_sc_scenario::multiversx_sc::{abi::ContractAbi, codec::top_encode_to_vec_u8}; + +const ABI_JSON: &str = r#"{ + "name": "Test", + "endpoints": [], + "events": [], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "TwoU8s": { + "type": "struct", + "fields": [ + { + "name": "first", + "type": "u8" + }, + { + "name": "second", + "type": "u8" + } + ] + }, + "EnumWithTupleValuesAndStruct": { + "type": "enum", + "variants": [ + { + "name": "First", + "discriminant": 0, + "fields": [ + { + "name": "first", + "type": "utf-8 string" + }, + { + "name": "second", + "type": "TwoU8s" + } + ] + }, + { + "name": "Second", + "discriminant": 1, + "fields": [ + { + "name": "0", + "type": "u8" + }, + { + "name": "1", + "type": "u8" + }, + { + "name": "2", + "type": "TwoU8s" + } + ] + } + ] + }, + "Integrated": { + "type": "struct", + "fields": [ + { + "name": "first", + "type": "EnumWithTupleValuesAndStruct" + }, + { + "name": "second", + "type": "EnumWithTupleValuesAndStruct" + } + ] + } + } +}"#; + +#[test] +fn integrated_test() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let default_value = default_value_for_abi_type("Integrated", &abi).unwrap(); + let default_value_human = + encode_human_readable_value(&default_value, "Integrated", &abi).unwrap(); + let default_value_decoded = + decode_human_readable_value(&default_value_human, "Integrated", &abi).unwrap(); + + let default_bytes = top_encode_to_vec_u8(&default_value).unwrap(); + let processed_bytes = top_encode_to_vec_u8(&default_value_decoded).unwrap(); + + assert_eq!(default_bytes, processed_bytes); +} diff --git a/data/human-readable/tests/single_value_basic_test.rs b/data/human-readable/tests/single_value_basic_test.rs deleted file mode 100644 index 0728a30902..0000000000 --- a/data/human-readable/tests/single_value_basic_test.rs +++ /dev/null @@ -1,21 +0,0 @@ -use multiversx_sc_codec_human_readable::interpret_value_according_to_abi; -use multiversx_sc_meta::abi_json::{deserialize_abi_from_json, ContractAbiJson}; -use multiversx_sc_scenario::multiversx_sc::codec::top_encode_to_vec_u8; - -const TEST_ABI_JSON: &str = r#"{ - "name": "Test", - "endpoints": [], - "events": [], - "esdtAttributes": [], - "hasCallback": false, - "types": {} -}"#; - -#[test] -fn test_display_unsigned() { - let abi_json: ContractAbiJson = deserialize_abi_from_json(TEST_ABI_JSON).unwrap(); - - let result = interpret_value_according_to_abi("123", "BigUint", &abi_json).unwrap(); - let serialized = top_encode_to_vec_u8(&result).unwrap(); - assert_eq!(serialized, vec![123]); -} diff --git a/data/human-readable/tests/single_value_tests.rs b/data/human-readable/tests/single_value_tests.rs new file mode 100644 index 0000000000..b92cc758dc --- /dev/null +++ b/data/human-readable/tests/single_value_tests.rs @@ -0,0 +1,202 @@ +use bech32::FromBase32; +use multiversx_sc_codec_human_readable::{ + decode_human_readable_value, default_value_for_abi_type, encode_human_readable_value, + format::HumanReadableValue, AnyValue, SingleValue, +}; +use multiversx_sc_meta::abi_json::deserialize_abi_from_json; +use multiversx_sc_scenario::multiversx_sc::{abi::ContractAbi, codec::top_encode_to_vec_u8}; + +const EMPTY_ABI_JSON: &str = r#"{ + "name": "Test", + "endpoints": [], + "events": [], + "esdtAttributes": [], + "hasCallback": false, + "types": {} +}"#; + +const TEST_ADDRESS: &str = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"; + +#[test] +fn serialize_single_value_unsigned() { + let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); + + let value = "1234".parse::().unwrap(); + + let result = decode_human_readable_value(&value, "u32", &abi).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + assert_eq!(serialized, 1234u16.to_be_bytes().to_vec()); // should take only 2 bytes (top encoded) +} + +#[test] +fn deserialize_single_value_unsigned() { + let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); + + let value = AnyValue::SingleValue(SingleValue::UnsignedNumber(1234u16.into())); + let result = encode_human_readable_value(&value, "u32", &abi).unwrap(); + + assert_eq!(result.to_string(), "1234"); +} + +#[test] +fn serialize_single_value_signed() { + let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); + + let value = "-1234".parse::().unwrap(); + + let result = decode_human_readable_value(&value, "i32", &abi).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + assert_eq!(serialized, (-1234 as i16).to_be_bytes().to_vec()); // should take only 2 bytes (top encoded) +} + +#[test] +fn deserialize_single_value_signed() { + let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); + + let value = AnyValue::SingleValue(SingleValue::SignedNumber((-1234 as i16).into())); + let result = encode_human_readable_value(&value, "i32", &abi).unwrap(); + + assert_eq!(result.to_string(), "-1234"); +} + +#[test] +fn serialize_single_value_managed_buffer() { + let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); + + let value = "[12, 34]".parse::().unwrap(); + + let result = decode_human_readable_value(&value, "ManagedBuffer", &abi).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + assert_eq!(serialized, vec![12, 34]); +} + +#[test] +fn deserialize_single_value_managed_buffer() { + let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); + + let value = AnyValue::SingleValue(SingleValue::Bytes(vec![0x1, 0x2, 0x3].into())); + let result = encode_human_readable_value(&value, "ManagedBuffer", &abi).unwrap(); + + assert_eq!(result.to_string(), "[1,2,3]"); +} + +#[test] +fn serialize_single_value_string() { + let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); + + let value = r#""hello""#.parse::().unwrap(); + + let result = decode_human_readable_value(&value, "utf-8 string", &abi).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + assert_eq!(serialized, "hello".as_bytes().to_vec()); +} + +#[test] +fn deserialize_single_value_string() { + let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); + + let value = AnyValue::SingleValue(SingleValue::String("hello".to_owned())); + let result = encode_human_readable_value(&value, "utf-8 string", &abi).unwrap(); + + assert_eq!(result.to_string(), "\"hello\""); +} + +#[test] +fn serialize_single_value_bool() { + let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); + + let value = "true".parse::().unwrap(); + + let result = decode_human_readable_value(&value, "bool", &abi).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + assert_eq!(serialized, vec![1]); +} + +#[test] +fn deserialize_single_value_bool() { + let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); + + let value = AnyValue::SingleValue(SingleValue::Bool(true.into())); + let result = encode_human_readable_value(&value, "bool", &abi).unwrap(); + + assert_eq!(result.to_string(), "true"); +} + +#[test] +fn serialize_single_value_address() { + let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); + + let value = format!("\"{}\"", TEST_ADDRESS) + .parse::() + .unwrap(); + + let result = decode_human_readable_value(&value, "Address", &abi).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + + let (_, address_bytes, _) = bech32::decode(TEST_ADDRESS).unwrap(); + let address_bytes = Vec::::from_base32(&address_bytes).unwrap(); + + assert_eq!(serialized, address_bytes); +} + +#[test] +fn deserialize_single_value_address() { + let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); + let (_, address_bytes, _) = bech32::decode(TEST_ADDRESS).unwrap(); + let address_bytes = Vec::::from_base32(&address_bytes).unwrap(); + + let value = AnyValue::SingleValue(SingleValue::Bytes(address_bytes.into())); + let result = encode_human_readable_value(&value, "Address", &abi).unwrap(); + + assert_eq!(result.to_string(), format!("\"{}\"", TEST_ADDRESS)); +} + +#[test] +fn default_single_values() { + let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); + + let AnyValue::SingleValue(SingleValue::UnsignedNumber(default_u32)) = + default_value_for_abi_type("u32", &abi).unwrap() + else { + panic!("Expected default value to be a SingleValue::UnsignedNumber") + }; + assert_eq!(default_u32, 0u32.into()); + + let AnyValue::SingleValue(SingleValue::SignedNumber(default_i32)) = + default_value_for_abi_type("i32", &abi).unwrap() + else { + panic!("Expected default value to be a SingleValue::SignedNumber") + }; + assert_eq!(default_i32, 0u32.into()); + + let AnyValue::SingleValue(SingleValue::Bytes(default_buffer)) = + default_value_for_abi_type("ManagedBuffer", &abi).unwrap() + else { + panic!("Expected default value to be a SingleValue::Bytes") + }; + assert_eq!(default_buffer.len(), 0); + + let AnyValue::SingleValue(SingleValue::String(default_string)) = + default_value_for_abi_type("utf-8 string", &abi).unwrap() + else { + panic!("Expected default value to be a SingleValue::String") + }; + assert_eq!(default_string, "".to_string()); + + let AnyValue::SingleValue(SingleValue::Bytes(default_address)) = + default_value_for_abi_type("Address", &abi).unwrap() + else { + panic!("Expected default value to be a SingleValue::Bytes") + }; + assert_eq!(default_address.len(), 32); + for byte in default_address.iter() { + assert_eq!(*byte, 0); + } + + let AnyValue::SingleValue(SingleValue::Bool(default_bool)) = + default_value_for_abi_type("bool", &abi).unwrap() + else { + panic!("Expected default value to be a SingleValue::Bool") + }; + assert_eq!(default_bool, false); +} diff --git a/data/human-readable/tests/struct_test.rs b/data/human-readable/tests/struct_test.rs new file mode 100644 index 0000000000..4e93c65f07 --- /dev/null +++ b/data/human-readable/tests/struct_test.rs @@ -0,0 +1,202 @@ +use multiversx_sc_codec_human_readable::{ + decode_human_readable_value, default_value_for_abi_type, encode_human_readable_value, + format::HumanReadableValue, AnyValue, SingleValue, StructField, StructValue, +}; +use multiversx_sc_meta::abi_json::deserialize_abi_from_json; +use multiversx_sc_scenario::multiversx_sc::{abi::ContractAbi, codec::top_encode_to_vec_u8}; + +const ABI_JSON: &str = r#"{ + "name": "Test", + "endpoints": [], + "events": [], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "TwoU8s": { + "type": "struct", + "fields": [ + { + "name": "first", + "type": "u8" + }, + { + "name": "second", + "type": "u8" + } + ] + }, + "NestedStruct": { + "type": "struct", + "fields": [ + { + "name": "first", + "type": "u8" + }, + { + "name": "second", + "type": "TwoU8s" + } + ] + } + } +}"#; + +#[test] +fn serialize_struct_two_u8s() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = r#"{ "first": 1, "second": 2 }"#.parse::().unwrap(); + + let result = decode_human_readable_value(&value, "TwoU8s", &abi).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + assert_eq!( + serialized, + vec![ + 0, 0, 0, 1, 1, // first + 0, 0, 0, 1, 2 // second + ] + ); +} + +#[test] +fn deserialize_struct_two_u8s() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = AnyValue::Struct(StructValue(vec![ + StructField { + name: "first".to_string(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(1u8.into())), + }, + StructField { + name: "second".to_string(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(2u8.into())), + }, + ])); + + let result = encode_human_readable_value(&value, "TwoU8s", &abi).unwrap(); + assert_eq!(result.to_string(), r#"{"first":1,"second":2}"#.to_string()); +} + +#[test] +fn default_struct_simple() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let AnyValue::Struct(struct_value) = default_value_for_abi_type("TwoU8s", &abi).unwrap() else { + panic!("Expected default value to be a SingleValue::UnsignedNumber") + }; + assert_eq!(struct_value.0.len(), 2); + + let first_field = struct_value.0.get(0).unwrap(); + assert_eq!(first_field.name, "first"); + let AnyValue::SingleValue(SingleValue::UnsignedNumber(first_value)) = &first_field.value else { + panic!("Expected default value to be a SingleValue::UnsignedNumber") + }; + assert_eq!(*first_value, 0u8.into()); + + let second_field = struct_value.0.get(1).unwrap(); + assert_eq!(second_field.name, "second"); + let AnyValue::SingleValue(SingleValue::UnsignedNumber(second_value)) = &second_field.value + else { + panic!("Expected default value to be a SingleValue::UnsignedNumber") + }; + assert_eq!(*second_value, 0u8.into()); +} + +#[test] +fn serialize_struct_nested() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = r#"{ + "first": 1, + "second": { + "first": 1, + "second": 2 + } + }"# + .parse::() + .unwrap(); + + let result = decode_human_readable_value(&value, "NestedStruct", &abi).unwrap(); + let serialized = top_encode_to_vec_u8(&result).unwrap(); + assert_eq!( + serialized, + vec![ + 0, 0, 0, 1, 1, // first + 0, 0, 0, 1, 1, // second.first + 0, 0, 0, 1, 2 // second.second + ] + ); +} + +#[test] +fn deserialize_struct_nested() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let value = AnyValue::Struct(StructValue(vec![ + StructField { + name: "first".to_string(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(1u8.into())), + }, + StructField { + name: "second".to_string(), + value: AnyValue::Struct(StructValue(vec![ + StructField { + name: "first".to_string(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(1u8.into())), + }, + StructField { + name: "second".to_string(), + value: AnyValue::SingleValue(SingleValue::UnsignedNumber(2u8.into())), + }, + ])), + }, + ])); + + let result = encode_human_readable_value(&value, "NestedStruct", &abi).unwrap(); + assert_eq!( + result.to_string(), + r#"{"first":1,"second":{"first":1,"second":2}}"#.to_string() + ); +} + +#[test] +fn default_struct_nested() { + let abi: ContractAbi = deserialize_abi_from_json(ABI_JSON).unwrap().into(); + + let AnyValue::Struct(struct_value) = default_value_for_abi_type("NestedStruct", &abi).unwrap() + else { + panic!("Expected default value to be a SingleValue::UnsignedNumber") + }; + assert_eq!(struct_value.0.len(), 2); + + let first_field = struct_value.0.get(0).unwrap(); + assert_eq!(first_field.name, "first"); + let AnyValue::SingleValue(SingleValue::UnsignedNumber(first_value)) = &first_field.value else { + panic!("Expected default value to be a SingleValue::UnsignedNumber") + }; + assert_eq!(*first_value, 0u8.into()); + + let second_field = struct_value.0.get(1).unwrap(); + assert_eq!(second_field.name, "second"); + let AnyValue::Struct(nested_struct_value) = &second_field.value else { + panic!("Expected default value to be a SingleValue::Struct") + }; + + assert_eq!(nested_struct_value.0.len(), 2); + + let first_nested_field = nested_struct_value.0.get(0).unwrap(); + let AnyValue::SingleValue(SingleValue::UnsignedNumber(first_nested_value)) = + &first_nested_field.value + else { + panic!("Expected default value to be a SingleValue::UnsignedNumber") + }; + assert_eq!(*first_nested_value, 0u8.into()); + + let second_nested_field = nested_struct_value.0.get(1).unwrap(); + let AnyValue::SingleValue(SingleValue::UnsignedNumber(second_nested_value)) = + &second_nested_field.value + else { + panic!("Expected default value to be a SingleValue::UnsignedNumber") + }; + assert_eq!(*second_nested_value, 0u8.into()); +} diff --git a/framework/base/src/abi/type_description.rs b/framework/base/src/abi/type_description.rs index 2f0f5a4336..77d2a9f168 100644 --- a/framework/base/src/abi/type_description.rs +++ b/framework/base/src/abi/type_description.rs @@ -72,6 +72,21 @@ impl EnumVariantDescription { fields, } } + + pub fn is_empty_variant(&self) -> bool { + self.fields.is_empty() + } + + pub fn is_tuple_variant(&self) -> bool { + // all fields are numbers + for field in self.fields.iter() { + if field.name.parse::().is_err() { + return false; + } + } + + return true; + } } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/framework/meta/src/abi_json/build_info_abi_json.rs b/framework/meta/src/abi_json/build_info_abi_json.rs index fb79f1cbb6..2b852b56a8 100644 --- a/framework/meta/src/abi_json/build_info_abi_json.rs +++ b/framework/meta/src/abi_json/build_info_abi_json.rs @@ -19,6 +19,21 @@ impl From<&BuildInfoAbi> for BuildInfoAbiJson { } } +impl From<&BuildInfoAbiJson> for BuildInfoAbi { + fn from(abi_json: &BuildInfoAbiJson) -> Self { + BuildInfoAbi { + contract_crate: ContractCrateBuildAbi::from(&abi_json.contract_crate), + framework: FrameworkBuildAbi::from(&abi_json.framework), + } + } +} + +impl From for BuildInfoAbi { + fn from(abi_json: BuildInfoAbiJson) -> Self { + BuildInfoAbi::from(&abi_json) + } +} + #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RustcAbiJson { @@ -62,6 +77,27 @@ impl From<&ContractCrateBuildAbi> for ContractCrateBuildAbiJson { } } +impl From<&ContractCrateBuildAbiJson> for ContractCrateBuildAbi { + fn from(_abi: &ContractCrateBuildAbiJson) -> Self { + // TODO: @Laur the abi struct should probably just own the strings + let name: &'static str = ""; + let version: &'static str = ""; + let git_version: &'static str = ""; + + ContractCrateBuildAbi { + name, + version, + git_version, + } + } +} + +impl From for ContractCrateBuildAbi { + fn from(abi: ContractCrateBuildAbiJson) -> Self { + ContractCrateBuildAbi::from(&abi) + } +} + #[derive(Serialize, Deserialize)] pub struct FrameworkBuildAbiJson { pub name: String, @@ -76,3 +112,19 @@ impl From<&FrameworkBuildAbi> for FrameworkBuildAbiJson { } } } + +impl From<&FrameworkBuildAbiJson> for FrameworkBuildAbi { + fn from(_abi: &FrameworkBuildAbiJson) -> Self { + // TODO: @Laur the abi struct should probably just own the strings + let name: &'static str = ""; + let version: &'static str = ""; + + FrameworkBuildAbi { name, version } + } +} + +impl From for FrameworkBuildAbi { + fn from(abi: FrameworkBuildAbiJson) -> Self { + FrameworkBuildAbi::from(&abi) + } +} diff --git a/framework/meta/src/abi_json/contract_abi_json.rs b/framework/meta/src/abi_json/contract_abi_json.rs index 6bb5dfeecf..c9b168c86b 100644 --- a/framework/meta/src/abi_json/contract_abi_json.rs +++ b/framework/meta/src/abi_json/contract_abi_json.rs @@ -41,6 +41,44 @@ pub struct ContractAbiJson { pub types: BTreeMap, } +impl From for ContractAbi { + fn from(abi_json: ContractAbiJson) -> Self { + ContractAbi { + build_info: abi_json + .build_info + .map(BuildInfoAbi::from) + .unwrap_or_default(), + docs: abi_json.docs, + name: abi_json.name, + constructors: abi_json + .constructor + .map(|c| vec![EndpointAbi::from(&c)]) + .unwrap_or_default(), + endpoints: abi_json + .endpoints + .into_iter() + .map(EndpointAbi::from) + .collect(), + promise_callbacks: abi_json + .promises_callback_names + .into_iter() + .map(|name| EndpointAbi { + name, + ..Default::default() + }) + .collect(), + events: abi_json.events.into_iter().map(EventAbi::from).collect(), + esdt_attributes: abi_json + .esdt_attributes + .into_iter() + .map(EsdtAttributeAbi::from) + .collect(), + has_callback: abi_json.has_callback, + type_descriptions: convert_json_to_type_descriptions(abi_json.types), + } + } +} + impl From<&ContractAbi> for ContractAbiJson { fn from(abi: &ContractAbi) -> Self { ContractAbiJson { @@ -81,6 +119,20 @@ pub fn convert_type_descriptions_to_json( types } +pub fn convert_json_to_type_descriptions( + types: BTreeMap, +) -> TypeDescriptionContainerImpl { + let mut type_descriptions = TypeDescriptionContainerImpl::new(); + for (type_name, type_description) in types.into_iter() { + type_descriptions.insert(type_name, TypeDescription::from(&type_description)); + } + type_descriptions +} + +pub fn empty_type_description_container() -> TypeDescriptionContainerImpl { + TypeDescriptionContainerImpl::new() +} + pub fn serialize_abi_to_json(abi_json: &ContractAbiJson) -> String { let buf = Vec::new(); let formatter = serde_json::ser::PrettyFormatter::with_indent(b" "); diff --git a/framework/meta/src/abi_json/endpoint_abi_json.rs b/framework/meta/src/abi_json/endpoint_abi_json.rs index 5882fcd961..954f309f92 100644 --- a/framework/meta/src/abi_json/endpoint_abi_json.rs +++ b/framework/meta/src/abi_json/endpoint_abi_json.rs @@ -25,6 +25,22 @@ impl From<&InputAbi> for InputAbiJson { } } +impl From<&InputAbiJson> for InputAbi { + fn from(abi: &InputAbiJson) -> Self { + InputAbi { + arg_name: abi.arg_name.to_string(), + type_name: abi.type_name.clone(), + multi_arg: abi.multi_arg.unwrap_or(false), + } + } +} + +impl From for InputAbi { + fn from(abi: InputAbiJson) -> Self { + InputAbi::from(&abi) + } +} + #[derive(Serialize, Deserialize)] pub struct OutputAbiJson { #[serde(rename = "name")] @@ -49,6 +65,22 @@ impl From<&OutputAbi> for OutputAbiJson { } } +impl From<&OutputAbiJson> for OutputAbi { + fn from(abi: &OutputAbiJson) -> Self { + OutputAbi { + output_name: abi.output_name.clone(), + type_name: abi.type_name.clone(), + multi_result: abi.multi_result.unwrap_or(false), + } + } +} + +impl From for OutputAbi { + fn from(abi: OutputAbiJson) -> Self { + OutputAbi::from(&abi) + } +} + #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum EndpointMutabilityAbiJson { @@ -151,3 +183,65 @@ impl From<&EndpointAbi> for EndpointAbiJson { } } } + +impl From<&EndpointAbiJson> for EndpointAbi { + fn from(abi: &EndpointAbiJson) -> Self { + EndpointAbi { + docs: abi.docs.iter().map(|d| d.to_string()).collect(), + name: abi.name.to_string(), + only_owner: abi.only_owner.unwrap_or(false), + only_admin: abi.only_admin.unwrap_or(false), + mutability: match abi.mutability { + EndpointMutabilityAbiJson::Mutable => EndpointMutabilityAbi::Mutable, + EndpointMutabilityAbiJson::Readonly => EndpointMutabilityAbi::Readonly, + EndpointMutabilityAbiJson::Pure => EndpointMutabilityAbi::Pure, + }, + payable_in_tokens: abi + .payable_in_tokens + .iter() + .map(|d| d.to_string()) + .collect(), + inputs: abi.inputs.iter().map(InputAbi::from).collect(), + outputs: abi.outputs.iter().map(OutputAbi::from).collect(), + labels: abi.labels.clone(), + allow_multiple_var_args: abi.allow_multiple_var_args.unwrap_or(false), + rust_method_name: abi.name.clone(), + endpoint_type: EndpointTypeAbi::Endpoint, + } + } +} + +impl From for EndpointAbi { + fn from(abi: EndpointAbiJson) -> Self { + EndpointAbi::from(&abi) + } +} + +impl From<&ConstructorAbiJson> for EndpointAbi { + fn from(abi: &ConstructorAbiJson) -> Self { + EndpointAbi { + docs: abi.docs.iter().map(|d| d.to_string()).collect(), + name: "".to_string(), + only_owner: false, + only_admin: false, + mutability: EndpointMutabilityAbi::Mutable, + payable_in_tokens: abi + .payable_in_tokens + .iter() + .map(|d| d.to_string()) + .collect(), + inputs: abi.inputs.iter().map(InputAbi::from).collect(), + outputs: abi.outputs.iter().map(OutputAbi::from).collect(), + labels: vec![], + allow_multiple_var_args: false, + rust_method_name: "".to_string(), + endpoint_type: EndpointTypeAbi::Init, + } + } +} + +impl From for EndpointAbi { + fn from(abi: ConstructorAbiJson) -> Self { + EndpointAbi::from(&abi) + } +} diff --git a/framework/meta/src/abi_json/esdt_attribute_abi_json.rs b/framework/meta/src/abi_json/esdt_attribute_abi_json.rs index b6d3e1b278..2afcd85774 100644 --- a/framework/meta/src/abi_json/esdt_attribute_abi_json.rs +++ b/framework/meta/src/abi_json/esdt_attribute_abi_json.rs @@ -3,7 +3,10 @@ use std::collections::BTreeMap; use multiversx_sc::abi::EsdtAttributeAbi; use serde::{Deserialize, Serialize}; -use super::{convert_type_descriptions_to_json, EsdtAttributeJson, TypeDescriptionJson}; +use super::{ + convert_type_descriptions_to_json, empty_type_description_container, EsdtAttributeJson, + TypeDescriptionJson, +}; /// Represents an entire ESDT attribute ABI file. The type descriptions only show up here. #[derive(Serialize, Deserialize)] @@ -24,3 +27,13 @@ impl EsdtAttributeAbiJson { } } } + +impl From<&EsdtAttributeAbiJson> for EsdtAttributeAbi { + fn from(abi_json: &EsdtAttributeAbiJson) -> Self { + EsdtAttributeAbi { + ticker: abi_json.esdt_attribute.ticker.clone(), + ty: abi_json.esdt_attribute.ty.clone(), + type_descriptions: empty_type_description_container(), // TODO: @Laur should recursively call convert_json_to_type_descriptions + } + } +} diff --git a/framework/meta/src/abi_json/esdt_attribute_json.rs b/framework/meta/src/abi_json/esdt_attribute_json.rs index a908203643..5600d891b0 100644 --- a/framework/meta/src/abi_json/esdt_attribute_json.rs +++ b/framework/meta/src/abi_json/esdt_attribute_json.rs @@ -16,3 +16,19 @@ impl From<&EsdtAttributeAbi> for EsdtAttributeJson { } } } + +impl From<&EsdtAttributeJson> for EsdtAttributeAbi { + fn from(attr: &EsdtAttributeJson) -> Self { + EsdtAttributeAbi { + ticker: attr.ticker.to_owned(), + ty: attr.ty.clone(), + type_descriptions: Default::default(), + } + } +} + +impl From for EsdtAttributeAbi { + fn from(attr: EsdtAttributeJson) -> Self { + EsdtAttributeAbi::from(&attr) + } +} diff --git a/framework/meta/src/abi_json/event_abi_json.rs b/framework/meta/src/abi_json/event_abi_json.rs index ddde69b8ab..9fa6b96cd3 100644 --- a/framework/meta/src/abi_json/event_abi_json.rs +++ b/framework/meta/src/abi_json/event_abi_json.rs @@ -25,6 +25,22 @@ impl From<&EventInputAbi> for EventInputAbiJson { } } +impl From<&EventInputAbiJson> for EventInputAbi { + fn from(abi: &EventInputAbiJson) -> Self { + EventInputAbi { + arg_name: abi.arg_name.to_string(), + type_name: abi.type_name.clone(), + indexed: abi.indexed.unwrap_or(false), + } + } +} + +impl From for EventInputAbi { + fn from(abi: EventInputAbiJson) -> Self { + EventInputAbi::from(&abi) + } +} + #[derive(Serialize, Deserialize)] pub struct EventAbiJson { #[serde(default)] @@ -43,3 +59,19 @@ impl From<&EventAbi> for EventAbiJson { } } } + +impl From<&EventAbiJson> for EventAbi { + fn from(abi: &EventAbiJson) -> Self { + EventAbi { + docs: abi.docs.iter().map(|d| d.to_string()).collect(), + identifier: abi.identifier.to_string(), + inputs: abi.inputs.iter().map(EventInputAbi::from).collect(), + } + } +} + +impl From for EventAbi { + fn from(abi: EventAbiJson) -> Self { + EventAbi::from(&abi) + } +} diff --git a/framework/meta/src/abi_json/type_abi_json.rs b/framework/meta/src/abi_json/type_abi_json.rs index 5602ae98cc..7d53399a3a 100644 --- a/framework/meta/src/abi_json/type_abi_json.rs +++ b/framework/meta/src/abi_json/type_abi_json.rs @@ -24,6 +24,33 @@ pub struct TypeDescriptionJson { pub fields: Vec, } +impl From<&TypeDescriptionJson> for TypeDescription { + fn from(abi: &TypeDescriptionJson) -> Self { + let content_type = match abi.content_type.as_str() { + TYPE_DESCRIPTION_JSON_TYPE_NOT_SPECIFIED => TypeContents::NotSpecified, + TYPE_DESCRIPTION_JSON_TYPE_ENUM => TypeContents::Enum( + abi.variants + .iter() + .map(EnumVariantDescriptionJson::to_enum_variant_description) + .collect(), + ), + TYPE_DESCRIPTION_JSON_TYPE_EXPLICIT_ENUM => TypeContents::ExplicitEnum(vec![]), // TODO: @Laur implement explicit enum + TYPE_DESCRIPTION_JSON_TYPE_STRUCT => TypeContents::Struct( + abi.fields + .iter() + .map(StructFieldDescriptionJson::to_struct_field_description) + .collect(), + ), + _ => TypeContents::NotSpecified, + }; + TypeDescription { + docs: abi.docs.iter().map(|line| line.to_string()).collect(), + name: "".to_string(), + contents: content_type, + } + } +} + impl From<&TypeDescription> for TypeDescriptionJson { fn from(abi: &TypeDescription) -> Self { let content_type = match &abi.contents { diff --git a/framework/scenario/Cargo.toml b/framework/scenario/Cargo.toml index 99705270d2..337f62c983 100644 --- a/framework/scenario/Cargo.toml +++ b/framework/scenario/Cargo.toml @@ -30,7 +30,7 @@ pathdiff = "0.2.1" itertools = "0.11" colored = "2.0" clap = { version = "4.4.7", features = ["derive"] } -tokio = { version = "1.24", features = ["full"] } +tokio = { version = "1.24" } [[bin]] name = "sc-scenario" diff --git a/framework/snippets/Cargo.toml b/framework/snippets/Cargo.toml index e9922919e7..02056ffe2c 100644 --- a/framework/snippets/Cargo.toml +++ b/framework/snippets/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["multiversx", "blockchain", "contract", "snippets"] categories = ["cryptography::cryptocurrencies"] [dependencies] -tokio = { version = "1.24", features = ["full"] } +tokio = { version = "1.24" } hex = "0.4" base64 = "0.13.0" log = "0.4.17" diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000..5d56faf9ae --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/sdk/core/Cargo.toml b/sdk/core/Cargo.toml index f3b22ad471..2f2703b3aa 100644 --- a/sdk/core/Cargo.toml +++ b/sdk/core/Cargo.toml @@ -16,7 +16,7 @@ categories = ["cryptography::cryptocurrencies", "api-bindings"] keywords = ["multiversx", "blockchain", "sdk", "api"] [dependencies] -tokio = { version = "1.24", features = ["full"] } +tokio = { version = "1.24" } reqwest = { version = "0.11.4", features = ["blocking", "json"] } serde = { version = "1.0.130", features = ["derive"] } serde_json = { version = "1.0.68", features = ["preserve_order"] } From f634b3f427b0a26cc96a4aeca8155c62e6e94270 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Thu, 5 Sep 2024 11:47:53 +0300 Subject: [PATCH 3/9] human-readable fix after merge --- Cargo.lock | 26 ++++++++--------- data/human-readable/src/decode.rs | 22 +++++--------- data/human-readable/src/defaults.rs | 20 ++++--------- data/human-readable/src/encode.rs | 29 ++++++++----------- framework/base/src/abi.rs | 7 +++++ .../src/abi/type_description_container.rs | 23 +++++++++++++++ .../src/abi_json/contract_abi_json.rs | 5 +++- .../src/abi_json/endpoint_abi_json.rs | 4 +-- .../meta-lib/src/abi_json/type_abi_json.rs | 3 +- framework/scenario/Cargo.toml | 6 ---- 10 files changed, 74 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c89baca761..65015f5d69 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -398,9 +398,9 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.1.15" +version = "1.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" dependencies = [ "shlex", ] @@ -458,9 +458,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" dependencies = [ "clap_builder", "clap_derive", @@ -468,9 +468,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" dependencies = [ "anstream", "anstyle", @@ -1486,9 +1486,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http", @@ -2121,7 +2121,6 @@ version = "0.53.0" dependencies = [ "base64", "bech32 0.11.0", - "clap", "colored", "hex", "itertools", @@ -2137,7 +2136,6 @@ dependencies = [ "serde", "serde_json", "sha2", - "tokio", "unwrap-infallible", ] @@ -3176,9 +3174,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "indexmap", "itoa", @@ -3549,9 +3547,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", diff --git a/data/human-readable/src/decode.rs b/data/human-readable/src/decode.rs index 74d32a2627..7c75d35cde 100644 --- a/data/human-readable/src/decode.rs +++ b/data/human-readable/src/decode.rs @@ -18,17 +18,7 @@ pub fn decode_human_readable_value( type_name: &str, contract_abi: &ContractAbi, ) -> Result> { - let type_description = - if let Some(type_description) = contract_abi.type_descriptions.0.get(type_name) { - type_description.to_owned() - } else { - TypeDescription { - docs: Vec::new(), - name: type_name.to_string(), - contents: TypeContents::NotSpecified, - } - }; - + let type_description = contract_abi.type_descriptions.find_or_default(type_name); decode_any_value(input, &type_description, &contract_abi) } @@ -38,7 +28,9 @@ pub fn decode_any_value( contract_abi: &ContractAbi, ) -> Result> { match &type_description.contents { - TypeContents::NotSpecified => decode_single_value(input, type_description.name.as_str()), + TypeContents::NotSpecified => { + decode_single_value(input, type_description.names.abi.as_str()) + }, TypeContents::Enum(variants) => decode_enum(input, &variants, &contract_abi), TypeContents::Struct(fields) => decode_struct(input, &fields, &contract_abi), TypeContents::ExplicitEnum(_) => panic!("not supported"), @@ -141,7 +133,7 @@ pub fn decode_struct( let value = input .child(&field.name) .ok_or_else(|| Box::new(DecodeError("missing field")))?; - let value = decode_human_readable_value(&value, &field.field_type, &contract_abi)?; + let value = decode_human_readable_value(&value, &field.field_type.abi, &contract_abi)?; field_values.push(StructField { name: field.name.clone(), value, @@ -196,7 +188,7 @@ pub fn decode_enum( if variant.is_tuple_variant() && variant.fields.len() == 1 { let value = input.child(variant.name.as_str()).unwrap(); let value = - decode_human_readable_value(&value, &variant.fields[0].field_type, contract_abi)?; + decode_human_readable_value(&value, &variant.fields[0].field_type.abi, contract_abi)?; return Ok(AnyValue::Enum(Box::new(crate::EnumVariant { discriminant: variant.discriminant, value, @@ -219,7 +211,7 @@ pub fn decode_enum( let value = value.get(i).unwrap(); let value = decode_human_readable_value( &(value.to_owned().into()), - &field.field_type, + &field.field_type.abi, &contract_abi, )?; field_values.push(StructField { diff --git a/data/human-readable/src/defaults.rs b/data/human-readable/src/defaults.rs index 42b68513fb..9046ce9c18 100644 --- a/data/human-readable/src/defaults.rs +++ b/data/human-readable/src/defaults.rs @@ -13,17 +13,7 @@ pub fn default_value_for_abi_type( type_name: &str, contract_abi: &ContractAbi, ) -> Result> { - let type_description = - if let Some(type_description) = contract_abi.type_descriptions.0.get(type_name) { - type_description.to_owned() - } else { - TypeDescription { - docs: Vec::new(), - name: type_name.to_string(), - contents: TypeContents::NotSpecified, - } - }; - + let type_description = contract_abi.type_descriptions.find_or_default(type_name); default_value_for_any_value(&type_description, contract_abi) } @@ -33,7 +23,7 @@ pub fn default_value_for_any_value( ) -> Result> { match &type_description.contents { TypeContents::NotSpecified => { - default_value_for_single_value(type_description.name.as_str()) + default_value_for_single_value(type_description.names.abi.as_str()) }, TypeContents::Enum(variants) => default_value_for_enum(&variants, contract_abi), TypeContents::Struct(fields) => default_value_for_struct(&fields, contract_abi), @@ -66,7 +56,7 @@ pub fn default_value_for_struct( let mut field_values: Vec = vec![]; for field in fields.iter() { - let value = default_value_for_abi_type(&field.field_type, &contract_abi)?; + let value = default_value_for_abi_type(&field.field_type.abi, &contract_abi)?; field_values.push(StructField { name: field.name.clone(), value, @@ -94,7 +84,7 @@ pub fn default_value_for_enum( // handle tuple with only one field as a special case (we don't need a wrapper array) if variant.is_tuple_variant() && variant.fields.len() == 1 { - let value = default_value_for_abi_type(&variant.fields[0].field_type, contract_abi)?; + let value = default_value_for_abi_type(&variant.fields[0].field_type.abi, contract_abi)?; return Ok(AnyValue::Enum(Box::new(crate::EnumVariant { discriminant: variant.discriminant, value, @@ -102,7 +92,7 @@ pub fn default_value_for_enum( } else if variant.is_tuple_variant() { let mut field_values: Vec = vec![]; for field in variant.fields.iter() { - let value = default_value_for_abi_type(&field.field_type, &contract_abi)?; + let value = default_value_for_abi_type(&field.field_type.abi, &contract_abi)?; field_values.push(StructField { name: field.name.clone(), value, diff --git a/data/human-readable/src/encode.rs b/data/human-readable/src/encode.rs index 2701df684d..dfc17139d4 100644 --- a/data/human-readable/src/encode.rs +++ b/data/human-readable/src/encode.rs @@ -13,17 +13,7 @@ pub fn encode_human_readable_value( type_name: &str, contract_abi: &ContractAbi, ) -> Result> { - let type_description = - if let Some(type_description) = contract_abi.type_descriptions.0.get(type_name) { - type_description.to_owned() - } else { - TypeDescription { - docs: Vec::new(), - name: type_name.to_string(), - contents: TypeContents::NotSpecified, - } - }; - + let type_description = contract_abi.type_descriptions.find_or_default(type_name); encode_any_value(input, &type_description, &contract_abi) } @@ -33,7 +23,9 @@ pub fn encode_any_value( contract_abi: &ContractAbi, ) -> Result> { match &type_description.contents { - TypeContents::NotSpecified => encode_single_value(input, type_description.name.as_str()), + TypeContents::NotSpecified => { + encode_single_value(input, type_description.names.abi.as_str()) + }, TypeContents::Enum(variants) => encode_enum(input, &variants, &contract_abi), TypeContents::Struct(fields) => encode_struct(input, &fields, &contract_abi), TypeContents::ExplicitEnum(_) => panic!("not supported"), @@ -146,7 +138,7 @@ pub fn encode_struct( .find(|f| f.name == field.name) .ok_or_else(|| Box::new(EncodeError("missing field")))?; - let value = encode_human_readable_value(&value.value, &field.field_type, contract_abi)?; + let value = encode_human_readable_value(&value.value, &field.field_type.abi, contract_abi)?; field_values.insert(field.name.to_owned(), value.get_value().to_owned()); } @@ -173,7 +165,7 @@ pub fn encode_enum( if variant.is_tuple_variant() && variant.fields.len() == 1 { let value = encode_human_readable_value( &enum_value.value, - &variant.fields[0].field_type, + &variant.fields[0].field_type.abi, contract_abi, )?; return Ok(JsonValue::Object( @@ -192,8 +184,11 @@ pub fn encode_enum( let mut field_values: Vec = vec![]; for (field, field_type) in variant.fields.iter().zip(variant_fields.0.iter()) { - let value = - encode_human_readable_value(&field_type.value, &field.field_type, contract_abi)?; + let value = encode_human_readable_value( + &field_type.value, + &field.field_type.abi, + contract_abi, + )?; field_values.push(value.get_value().to_owned()); } @@ -212,7 +207,7 @@ pub fn encode_enum( let mut field_values: Map = Map::new(); for (field, field_type) in variant.fields.iter().zip(variant_fields.0.iter()) { let value = - encode_human_readable_value(&field_type.value, &field.field_type, contract_abi)?; + encode_human_readable_value(&field_type.value, &field.field_type.abi, contract_abi)?; field_values.insert(field.name.to_owned(), value.get_value().to_owned()); } diff --git a/framework/base/src/abi.rs b/framework/base/src/abi.rs index c031a62166..8479e1921f 100644 --- a/framework/base/src/abi.rs +++ b/framework/base/src/abi.rs @@ -38,4 +38,11 @@ impl TypeNames { rust: alloc::string::String::new(), } } + + pub const fn from_abi(abi_name: alloc::string::String) -> Self { + TypeNames { + abi: abi_name, + rust: alloc::string::String::new(), + } + } } diff --git a/framework/base/src/abi/type_description_container.rs b/framework/base/src/abi/type_description_container.rs index 16dda2c8b9..14f1bbd6e6 100644 --- a/framework/base/src/abi/type_description_container.rs +++ b/framework/base/src/abi/type_description_container.rs @@ -1,4 +1,5 @@ use super::*; +use alloc::borrow::ToOwned; use multiversx_sc_codec::Vec; pub trait TypeDescriptionContainer { @@ -20,6 +21,28 @@ pub trait TypeDescriptionContainer { #[derive(Clone, Default, Debug)] pub struct TypeDescriptionContainerImpl(pub Vec<(TypeNames, TypeDescription)>); +impl TypeDescriptionContainerImpl { + pub fn find(&self, abi_type_name: &str) -> Option<&TypeDescription> { + self.0 + .iter() + .find(|(existing_type_name, _)| &existing_type_name.abi == abi_type_name) + .map(|(_, description)| description) + } + + pub fn find_or_default(&self, abi_type_name: &str) -> TypeDescription { + if let Some(type_description) = self.find(abi_type_name) { + type_description.clone() + } else { + TypeDescription { + docs: Vec::new(), + names: TypeNames::from_abi(abi_type_name.to_owned()), + contents: TypeContents::NotSpecified, + macro_attributes: Vec::new(), + } + } + } +} + impl TypeDescriptionContainer for TypeDescriptionContainerImpl { fn new() -> Self { TypeDescriptionContainerImpl(Vec::new()) diff --git a/framework/meta-lib/src/abi_json/contract_abi_json.rs b/framework/meta-lib/src/abi_json/contract_abi_json.rs index 2c0a3ec353..80a6f1f7e8 100644 --- a/framework/meta-lib/src/abi_json/contract_abi_json.rs +++ b/framework/meta-lib/src/abi_json/contract_abi_json.rs @@ -136,7 +136,10 @@ pub fn convert_json_to_type_descriptions( ) -> TypeDescriptionContainerImpl { let mut type_descriptions = TypeDescriptionContainerImpl::new(); for (type_name, type_description) in types.into_iter() { - type_descriptions.insert(type_name, TypeDescription::from(&type_description)); + type_descriptions.insert( + TypeNames::from_abi(type_name), + TypeDescription::from(&type_description), + ); } type_descriptions } diff --git a/framework/meta-lib/src/abi_json/endpoint_abi_json.rs b/framework/meta-lib/src/abi_json/endpoint_abi_json.rs index ad808879b1..c5d2427fdc 100644 --- a/framework/meta-lib/src/abi_json/endpoint_abi_json.rs +++ b/framework/meta-lib/src/abi_json/endpoint_abi_json.rs @@ -29,7 +29,7 @@ impl From<&InputAbiJson> for InputAbi { fn from(abi: &InputAbiJson) -> Self { InputAbi { arg_name: abi.arg_name.to_string(), - type_names: abi.type_names.clone(), + type_names: TypeNames::from_abi(abi.type_name.clone()), multi_arg: abi.multi_arg.unwrap_or(false), } } @@ -69,7 +69,7 @@ impl From<&OutputAbiJson> for OutputAbi { fn from(abi: &OutputAbiJson) -> Self { OutputAbi { output_name: abi.output_name.clone(), - type_names: abi.type_name.clone(), + type_names: TypeNames::from_abi(abi.type_name.clone()), multi_result: abi.multi_result.unwrap_or(false), } } diff --git a/framework/meta-lib/src/abi_json/type_abi_json.rs b/framework/meta-lib/src/abi_json/type_abi_json.rs index 0db166f65e..0bff582bbd 100644 --- a/framework/meta-lib/src/abi_json/type_abi_json.rs +++ b/framework/meta-lib/src/abi_json/type_abi_json.rs @@ -45,8 +45,9 @@ impl From<&TypeDescriptionJson> for TypeDescription { }; TypeDescription { docs: abi.docs.iter().map(|line| line.to_string()).collect(), - name: "".to_string(), + names: TypeNames::new(), contents: content_type, + macro_attributes: Vec::new(), } } } diff --git a/framework/scenario/Cargo.toml b/framework/scenario/Cargo.toml index c555ccb3c2..24eb8b918b 100644 --- a/framework/scenario/Cargo.toml +++ b/framework/scenario/Cargo.toml @@ -30,12 +30,6 @@ pathdiff = "0.2.1" itertools = "0.13.0" colored = "2.0" unwrap-infallible = "0.1.5" -clap = { version = "4.4.7", features = ["derive"] } -tokio = { version = "1.24" } - -[[bin]] -name = "sc-scenario" -path = "src/main.rs" [features] default = ["wasm-incompatible"] From a7251668bfc27d6cb93f74110c9c67a2e2b50edb Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Thu, 5 Sep 2024 11:55:03 +0300 Subject: [PATCH 4/9] dependency cleanup --- Cargo.lock | 1 - framework/snippets/Cargo.toml | 1 - framework/snippets/src/imports.rs | 2 +- framework/snippets/src/interactor.rs | 2 +- framework/snippets/src/lib.rs | 2 +- sdk/core/src/lib.rs | 3 +++ 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65015f5d69..4b7b673991 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -2152,7 +2152,6 @@ dependencies = [ "multiversx-sc-scenario", "multiversx-sdk", "serde_json", - "tokio", ] [[package]] diff --git a/framework/snippets/Cargo.toml b/framework/snippets/Cargo.toml index 036c4a327f..45cf5f39fc 100644 --- a/framework/snippets/Cargo.toml +++ b/framework/snippets/Cargo.toml @@ -14,7 +14,6 @@ keywords = ["multiversx", "blockchain", "contract", "snippets"] categories = ["cryptography::cryptocurrencies"] [dependencies] -tokio = { version = "1.24" } hex = "0.4" base64 = "0.22" log = "0.4.17" diff --git a/framework/snippets/src/imports.rs b/framework/snippets/src/imports.rs index a22c969887..e7fbdf2436 100644 --- a/framework/snippets/src/imports.rs +++ b/framework/snippets/src/imports.rs @@ -7,4 +7,4 @@ pub use crate::{ pub use multiversx_sdk::{data::keystore::InsertPassword, wallet::Wallet}; pub use env_logger; -pub use tokio; +pub use multiversx_sdk::tokio; diff --git a/framework/snippets/src/interactor.rs b/framework/snippets/src/interactor.rs index bb38873afe..76d2b37a35 100644 --- a/framework/snippets/src/interactor.rs +++ b/framework/snippets/src/interactor.rs @@ -61,7 +61,7 @@ impl Interactor { pub async fn sleep(&mut self, duration: Duration) { self.waiting_time_ms += duration.as_millis() as u64; - tokio::time::sleep(duration).await; + crate::tokio::time::sleep(duration).await; } pub async fn with_tracer>(mut self, path: P) -> Self { diff --git a/framework/snippets/src/lib.rs b/framework/snippets/src/lib.rs index 7d3b52c696..61d4ad485e 100644 --- a/framework/snippets/src/lib.rs +++ b/framework/snippets/src/lib.rs @@ -19,7 +19,7 @@ pub use multi::*; pub use multiversx_sc_scenario::{self, multiversx_sc}; pub use multiversx_sdk as erdrs; // TODO: remove pub use multiversx_sdk as sdk; -pub use tokio; +pub use multiversx_sdk::tokio; /// Imports normally needed in interactors, grouped together. pub mod imports; diff --git a/sdk/core/src/lib.rs b/sdk/core/src/lib.rs index 3a1db99fd7..c0e0379587 100644 --- a/sdk/core/src/lib.rs +++ b/sdk/core/src/lib.rs @@ -3,3 +3,6 @@ pub mod data; pub mod gateway; pub mod utils; pub mod wallet; + +/// Re-exported for convenience. +pub use tokio; From 01c8f636a569f8be8fb213749215923e3d66ca6c Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Thu, 5 Sep 2024 12:00:26 +0300 Subject: [PATCH 5/9] clippy fix --- data/human-readable/src/decode.rs | 16 ++++++++-------- data/human-readable/src/defaults.rs | 12 ++++++------ data/human-readable/src/encode.rs | 10 +++++----- data/human-readable/src/format.rs | 8 +++++--- framework/base/src/abi/type_description.rs | 2 +- .../base/src/abi/type_description_container.rs | 2 +- 6 files changed, 26 insertions(+), 24 deletions(-) diff --git a/data/human-readable/src/decode.rs b/data/human-readable/src/decode.rs index 7c75d35cde..76520a654a 100644 --- a/data/human-readable/src/decode.rs +++ b/data/human-readable/src/decode.rs @@ -19,7 +19,7 @@ pub fn decode_human_readable_value( contract_abi: &ContractAbi, ) -> Result> { let type_description = contract_abi.type_descriptions.find_or_default(type_name); - decode_any_value(input, &type_description, &contract_abi) + decode_any_value(input, &type_description, contract_abi) } pub fn decode_any_value( @@ -31,8 +31,8 @@ pub fn decode_any_value( TypeContents::NotSpecified => { decode_single_value(input, type_description.names.abi.as_str()) }, - TypeContents::Enum(variants) => decode_enum(input, &variants, &contract_abi), - TypeContents::Struct(fields) => decode_struct(input, &fields, &contract_abi), + TypeContents::Enum(variants) => decode_enum(input, variants, contract_abi), + TypeContents::Struct(fields) => decode_struct(input, fields, contract_abi), TypeContents::ExplicitEnum(_) => panic!("not supported"), } } @@ -116,7 +116,7 @@ fn decode_single_value( .as_bool() .ok_or_else(|| Box::new(DecodeError("expected bool value")))?; - Ok(AnyValue::SingleValue(SingleValue::Bool(bool_value.into()))) + Ok(AnyValue::SingleValue(SingleValue::Bool(bool_value))) }, _ => Err(Box::new(DecodeError("unknown type"))), } @@ -124,7 +124,7 @@ fn decode_single_value( pub fn decode_struct( input: &HumanReadableValue, - fields: &Vec, + fields: &[StructFieldDescription], contract_abi: &ContractAbi, ) -> Result> { let mut field_values: Vec = vec![]; @@ -133,7 +133,7 @@ pub fn decode_struct( let value = input .child(&field.name) .ok_or_else(|| Box::new(DecodeError("missing field")))?; - let value = decode_human_readable_value(&value, &field.field_type.abi, &contract_abi)?; + let value = decode_human_readable_value(&value, &field.field_type.abi, contract_abi)?; field_values.push(StructField { name: field.name.clone(), value, @@ -145,7 +145,7 @@ pub fn decode_struct( pub fn decode_enum( input: &HumanReadableValue, - variants: &Vec, + variants: &[EnumVariantDescription], contract_abi: &ContractAbi, ) -> Result> { if input.get_value().is_string() { @@ -212,7 +212,7 @@ pub fn decode_enum( let value = decode_human_readable_value( &(value.to_owned().into()), &field.field_type.abi, - &contract_abi, + contract_abi, )?; field_values.push(StructField { name: field.name.clone(), diff --git a/data/human-readable/src/defaults.rs b/data/human-readable/src/defaults.rs index 9046ce9c18..a48aedf4de 100644 --- a/data/human-readable/src/defaults.rs +++ b/data/human-readable/src/defaults.rs @@ -25,8 +25,8 @@ pub fn default_value_for_any_value( TypeContents::NotSpecified => { default_value_for_single_value(type_description.names.abi.as_str()) }, - TypeContents::Enum(variants) => default_value_for_enum(&variants, contract_abi), - TypeContents::Struct(fields) => default_value_for_struct(&fields, contract_abi), + TypeContents::Enum(variants) => default_value_for_enum(variants, contract_abi), + TypeContents::Struct(fields) => default_value_for_struct(fields, contract_abi), TypeContents::ExplicitEnum(_) => panic!("not supported"), } } @@ -50,13 +50,13 @@ pub fn default_value_for_single_value(type_name: &str) -> Result, + fields: &[StructFieldDescription], contract_abi: &ContractAbi, ) -> Result> { let mut field_values: Vec = vec![]; for field in fields.iter() { - let value = default_value_for_abi_type(&field.field_type.abi, &contract_abi)?; + let value = default_value_for_abi_type(&field.field_type.abi, contract_abi)?; field_values.push(StructField { name: field.name.clone(), value, @@ -67,7 +67,7 @@ pub fn default_value_for_struct( } pub fn default_value_for_enum( - variants: &Vec, + variants: &[EnumVariantDescription], contract_abi: &ContractAbi, ) -> Result> { let variant = variants @@ -92,7 +92,7 @@ pub fn default_value_for_enum( } else if variant.is_tuple_variant() { let mut field_values: Vec = vec![]; for field in variant.fields.iter() { - let value = default_value_for_abi_type(&field.field_type.abi, &contract_abi)?; + let value = default_value_for_abi_type(&field.field_type.abi, contract_abi)?; field_values.push(StructField { name: field.name.clone(), value, diff --git a/data/human-readable/src/encode.rs b/data/human-readable/src/encode.rs index dfc17139d4..6207f64414 100644 --- a/data/human-readable/src/encode.rs +++ b/data/human-readable/src/encode.rs @@ -14,7 +14,7 @@ pub fn encode_human_readable_value( contract_abi: &ContractAbi, ) -> Result> { let type_description = contract_abi.type_descriptions.find_or_default(type_name); - encode_any_value(input, &type_description, &contract_abi) + encode_any_value(input, &type_description, contract_abi) } pub fn encode_any_value( @@ -26,8 +26,8 @@ pub fn encode_any_value( TypeContents::NotSpecified => { encode_single_value(input, type_description.names.abi.as_str()) }, - TypeContents::Enum(variants) => encode_enum(input, &variants, &contract_abi), - TypeContents::Struct(fields) => encode_struct(input, &fields, &contract_abi), + TypeContents::Enum(variants) => encode_enum(input, variants, contract_abi), + TypeContents::Struct(fields) => encode_struct(input, fields, contract_abi), TypeContents::ExplicitEnum(_) => panic!("not supported"), } } @@ -123,7 +123,7 @@ fn encode_single_value( pub fn encode_struct( input: &AnyValue, - fields: &Vec, + fields: &[StructFieldDescription], contract_abi: &ContractAbi, ) -> Result> { let AnyValue::Struct(struct_value) = input else { @@ -147,7 +147,7 @@ pub fn encode_struct( pub fn encode_enum( input: &AnyValue, - variants: &Vec, + variants: &[EnumVariantDescription], contract_abi: &ContractAbi, ) -> Result> { let AnyValue::Enum(enum_value) = input else { diff --git a/data/human-readable/src/format.rs b/data/human-readable/src/format.rs index a6dff0483a..69b244cdf5 100644 --- a/data/human-readable/src/format.rs +++ b/data/human-readable/src/format.rs @@ -1,5 +1,5 @@ pub use serde_json::Value as JsonValue; -use std::str::FromStr; +use std::{fmt::Display, str::FromStr}; pub struct HumanReadableValue { value: JsonValue, @@ -30,8 +30,10 @@ impl HumanReadableValue { value: value.clone(), }) } +} - pub fn to_string(&self) -> String { - self.value.to_string() +impl Display for HumanReadableValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.value.fmt(f) } } diff --git a/framework/base/src/abi/type_description.rs b/framework/base/src/abi/type_description.rs index cd03f5d549..85ae3f1054 100644 --- a/framework/base/src/abi/type_description.rs +++ b/framework/base/src/abi/type_description.rs @@ -98,7 +98,7 @@ impl EnumVariantDescription { } } - return true; + true } } diff --git a/framework/base/src/abi/type_description_container.rs b/framework/base/src/abi/type_description_container.rs index 14f1bbd6e6..6b178ca5f4 100644 --- a/framework/base/src/abi/type_description_container.rs +++ b/framework/base/src/abi/type_description_container.rs @@ -25,7 +25,7 @@ impl TypeDescriptionContainerImpl { pub fn find(&self, abi_type_name: &str) -> Option<&TypeDescription> { self.0 .iter() - .find(|(existing_type_name, _)| &existing_type_name.abi == abi_type_name) + .find(|(existing_type_name, _)| existing_type_name.abi == abi_type_name) .map(|(_, description)| description) } From 4a14d8388ad7d5fbf76f20d73e593e4005aac6bc Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Thu, 5 Sep 2024 12:08:45 +0300 Subject: [PATCH 6/9] dependency cleanup --- Cargo.lock | 1 - data/human-readable/Cargo.toml | 4 ---- data/human-readable/tests/enum_test.rs | 6 ++++-- data/human-readable/tests/integrated_test.rs | 6 ++++-- data/human-readable/tests/single_value_tests.rs | 6 ++++-- data/human-readable/tests/struct_test.rs | 6 ++++-- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b7b673991..9c843b94cb 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -2049,7 +2049,6 @@ name = "multiversx-sc-codec-human-readable" version = "0.1.0" dependencies = [ "bech32 0.9.1", - "multiversx-sc-meta", "multiversx-sc-scenario", "serde_json", ] diff --git a/data/human-readable/Cargo.toml b/data/human-readable/Cargo.toml index 330dba7e31..fdfe8b969b 100644 --- a/data/human-readable/Cargo.toml +++ b/data/human-readable/Cargo.toml @@ -21,7 +21,3 @@ bech32 = "0.9" [dependencies.multiversx-sc-scenario] version = "=0.53.0" path = "../../framework/scenario" - -[dependencies.multiversx-sc-meta] -version = "=0.53.0" -path = "../../framework/meta" diff --git a/data/human-readable/tests/enum_test.rs b/data/human-readable/tests/enum_test.rs index c360d645d1..88b6840262 100644 --- a/data/human-readable/tests/enum_test.rs +++ b/data/human-readable/tests/enum_test.rs @@ -2,8 +2,10 @@ use multiversx_sc_codec_human_readable::{ decode_human_readable_value, default_value_for_abi_type, encode_human_readable_value, format::HumanReadableValue, AnyValue, EnumVariant, SingleValue, StructField, StructValue, }; -use multiversx_sc_meta::abi_json::deserialize_abi_from_json; -use multiversx_sc_scenario::multiversx_sc::{abi::ContractAbi, codec::top_encode_to_vec_u8}; +use multiversx_sc_scenario::{ + meta::abi_json::deserialize_abi_from_json, + multiversx_sc::{abi::ContractAbi, codec::top_encode_to_vec_u8}, +}; const ABI_JSON: &str = r#"{ "name": "Test", diff --git a/data/human-readable/tests/integrated_test.rs b/data/human-readable/tests/integrated_test.rs index a864d203d4..49621b0033 100644 --- a/data/human-readable/tests/integrated_test.rs +++ b/data/human-readable/tests/integrated_test.rs @@ -1,8 +1,10 @@ use multiversx_sc_codec_human_readable::{ decode_human_readable_value, default_value_for_abi_type, encode_human_readable_value, }; -use multiversx_sc_meta::abi_json::deserialize_abi_from_json; -use multiversx_sc_scenario::multiversx_sc::{abi::ContractAbi, codec::top_encode_to_vec_u8}; +use multiversx_sc_scenario::{ + meta::abi_json::deserialize_abi_from_json, + multiversx_sc::{abi::ContractAbi, codec::top_encode_to_vec_u8}, +}; const ABI_JSON: &str = r#"{ "name": "Test", diff --git a/data/human-readable/tests/single_value_tests.rs b/data/human-readable/tests/single_value_tests.rs index b92cc758dc..c9072d6261 100644 --- a/data/human-readable/tests/single_value_tests.rs +++ b/data/human-readable/tests/single_value_tests.rs @@ -3,8 +3,10 @@ use multiversx_sc_codec_human_readable::{ decode_human_readable_value, default_value_for_abi_type, encode_human_readable_value, format::HumanReadableValue, AnyValue, SingleValue, }; -use multiversx_sc_meta::abi_json::deserialize_abi_from_json; -use multiversx_sc_scenario::multiversx_sc::{abi::ContractAbi, codec::top_encode_to_vec_u8}; +use multiversx_sc_scenario::{ + meta::abi_json::deserialize_abi_from_json, + multiversx_sc::{abi::ContractAbi, codec::top_encode_to_vec_u8}, +}; const EMPTY_ABI_JSON: &str = r#"{ "name": "Test", diff --git a/data/human-readable/tests/struct_test.rs b/data/human-readable/tests/struct_test.rs index 4e93c65f07..ed322f8fca 100644 --- a/data/human-readable/tests/struct_test.rs +++ b/data/human-readable/tests/struct_test.rs @@ -2,8 +2,10 @@ use multiversx_sc_codec_human_readable::{ decode_human_readable_value, default_value_for_abi_type, encode_human_readable_value, format::HumanReadableValue, AnyValue, SingleValue, StructField, StructValue, }; -use multiversx_sc_meta::abi_json::deserialize_abi_from_json; -use multiversx_sc_scenario::multiversx_sc::{abi::ContractAbi, codec::top_encode_to_vec_u8}; +use multiversx_sc_scenario::{ + meta::abi_json::deserialize_abi_from_json, + multiversx_sc::{abi::ContractAbi, codec::top_encode_to_vec_u8}, +}; const ABI_JSON: &str = r#"{ "name": "Test", From 775b73695bfb65832963fa7f10d2dc97983d0cd7 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Thu, 5 Sep 2024 12:12:28 +0300 Subject: [PATCH 7/9] clippy fix --- data/human-readable/tests/single_value_tests.rs | 8 ++++---- data/human-readable/tests/struct_test.rs | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/data/human-readable/tests/single_value_tests.rs b/data/human-readable/tests/single_value_tests.rs index c9072d6261..4e4f92c105 100644 --- a/data/human-readable/tests/single_value_tests.rs +++ b/data/human-readable/tests/single_value_tests.rs @@ -48,14 +48,14 @@ fn serialize_single_value_signed() { let result = decode_human_readable_value(&value, "i32", &abi).unwrap(); let serialized = top_encode_to_vec_u8(&result).unwrap(); - assert_eq!(serialized, (-1234 as i16).to_be_bytes().to_vec()); // should take only 2 bytes (top encoded) + assert_eq!(serialized, (-1234i16).to_be_bytes().to_vec()); // should take only 2 bytes (top encoded) } #[test] fn deserialize_single_value_signed() { let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); - let value = AnyValue::SingleValue(SingleValue::SignedNumber((-1234 as i16).into())); + let value = AnyValue::SingleValue(SingleValue::SignedNumber((-1234i16).into())); let result = encode_human_readable_value(&value, "i32", &abi).unwrap(); assert_eq!(result.to_string(), "-1234"); @@ -118,7 +118,7 @@ fn serialize_single_value_bool() { fn deserialize_single_value_bool() { let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); - let value = AnyValue::SingleValue(SingleValue::Bool(true.into())); + let value = AnyValue::SingleValue(SingleValue::Bool(true)); let result = encode_human_readable_value(&value, "bool", &abi).unwrap(); assert_eq!(result.to_string(), "true"); @@ -200,5 +200,5 @@ fn default_single_values() { else { panic!("Expected default value to be a SingleValue::Bool") }; - assert_eq!(default_bool, false); + assert!(!default_bool); } diff --git a/data/human-readable/tests/struct_test.rs b/data/human-readable/tests/struct_test.rs index ed322f8fca..b4a6e8b1f3 100644 --- a/data/human-readable/tests/struct_test.rs +++ b/data/human-readable/tests/struct_test.rs @@ -88,7 +88,7 @@ fn default_struct_simple() { }; assert_eq!(struct_value.0.len(), 2); - let first_field = struct_value.0.get(0).unwrap(); + let first_field = struct_value.0.first().unwrap(); assert_eq!(first_field.name, "first"); let AnyValue::SingleValue(SingleValue::UnsignedNumber(first_value)) = &first_field.value else { panic!("Expected default value to be a SingleValue::UnsignedNumber") @@ -171,7 +171,7 @@ fn default_struct_nested() { }; assert_eq!(struct_value.0.len(), 2); - let first_field = struct_value.0.get(0).unwrap(); + let first_field = struct_value.0.first().unwrap(); assert_eq!(first_field.name, "first"); let AnyValue::SingleValue(SingleValue::UnsignedNumber(first_value)) = &first_field.value else { panic!("Expected default value to be a SingleValue::UnsignedNumber") @@ -186,7 +186,7 @@ fn default_struct_nested() { assert_eq!(nested_struct_value.0.len(), 2); - let first_nested_field = nested_struct_value.0.get(0).unwrap(); + let first_nested_field = nested_struct_value.0.first().unwrap(); let AnyValue::SingleValue(SingleValue::UnsignedNumber(first_nested_value)) = &first_nested_field.value else { From 3a7c82b5038c6075737da39d8186ceb9990cb49a Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Fri, 6 Sep 2024 19:15:14 +0300 Subject: [PATCH 8/9] human readable uses bech32 from scenario --- Cargo.lock | 23 ++++++---------- data/human-readable/Cargo.toml | 1 - data/human-readable/src/decode.rs | 14 +++------- data/human-readable/src/encode.rs | 15 ++++++----- .../tests/single_value_tests.rs | 12 ++++----- framework/scenario/src/bech32.rs | 26 +++++++++++++++---- 6 files changed, 45 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9c843b94cb..fd2304ce10 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -264,12 +264,6 @@ dependencies = [ "toml", ] -[[package]] -name = "bech32" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" - [[package]] name = "bech32" version = "0.11.0" @@ -578,9 +572,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -1957,7 +1951,7 @@ dependencies = [ name = "multiversx-chain-scenario-format" version = "0.23.0" dependencies = [ - "bech32 0.11.0", + "bech32", "hex", "num-bigint", "num-traits", @@ -2048,7 +2042,6 @@ dependencies = [ name = "multiversx-sc-codec-human-readable" version = "0.1.0" dependencies = [ - "bech32 0.9.1", "multiversx-sc-scenario", "serde_json", ] @@ -2119,7 +2112,7 @@ name = "multiversx-sc-scenario" version = "0.53.0" dependencies = [ "base64", - "bech32 0.11.0", + "bech32", "colored", "hex", "itertools", @@ -2167,7 +2160,7 @@ dependencies = [ "aes", "anyhow", "base64", - "bech32 0.11.0", + "bech32", "bip39", "ctr", "hex", @@ -2211,7 +2204,7 @@ dependencies = [ name = "mxpy-snippet-generator" version = "0.0.0" dependencies = [ - "bech32 0.11.0", + "bech32", "hex", "multiversx-sc", "num-bigint", @@ -2953,9 +2946,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.35" +version = "0.38.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" +checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" dependencies = [ "bitflags", "errno", diff --git a/data/human-readable/Cargo.toml b/data/human-readable/Cargo.toml index fdfe8b969b..9ceacbac6a 100644 --- a/data/human-readable/Cargo.toml +++ b/data/human-readable/Cargo.toml @@ -16,7 +16,6 @@ categories = ["cryptography::cryptocurrencies", "development-tools"] [dependencies] serde_json = { version = "1.0.68" } -bech32 = "0.9" [dependencies.multiversx-sc-scenario] version = "=0.53.0" diff --git a/data/human-readable/src/decode.rs b/data/human-readable/src/decode.rs index 76520a654a..5a0e0b850d 100644 --- a/data/human-readable/src/decode.rs +++ b/data/human-readable/src/decode.rs @@ -5,8 +5,8 @@ use crate::{ multiversx_sc::abi::{TypeContents, TypeDescription}, SingleValue, StructField, StructValue, }; -use bech32::FromBase32; use multiversx_sc_scenario::{ + bech32, multiversx_sc::abi::{ContractAbi, EnumVariantDescription, StructFieldDescription}, num_bigint::{BigInt, BigUint}, }; @@ -95,19 +95,11 @@ fn decode_single_value( .as_str() .ok_or_else(|| Box::new(DecodeError("expected string value")))?; - let (_, address_bytes_u5, _) = bech32::decode(str_value) + let address = bech32::try_decode(str_value) .map_err(|_| Box::new(DecodeError("failed to parse address")))?; - let address_bytes = Vec::::from_base32(&address_bytes_u5) - .map_err(|_| Box::new(DecodeError("failed to parse address")))?; - - if address_bytes.len() != 32 { - return Err(Box::new(DecodeError( - "Invalid address length after decoding", - ))); - } Ok(AnyValue::SingleValue(SingleValue::Bytes( - address_bytes.into(), + address.into_boxed_bytes().into_box(), ))) }, "bool" => { diff --git a/data/human-readable/src/encode.rs b/data/human-readable/src/encode.rs index 6207f64414..b7da4e3875 100644 --- a/data/human-readable/src/encode.rs +++ b/data/human-readable/src/encode.rs @@ -1,8 +1,11 @@ use std::{error::Error, fmt::Display}; -use bech32::ToBase32; -use multiversx_sc_scenario::multiversx_sc::abi::{ - ContractAbi, EnumVariantDescription, StructFieldDescription, TypeContents, TypeDescription, +use multiversx_sc_scenario::{ + bech32, + imports::Address, + multiversx_sc::abi::{ + ContractAbi, EnumVariantDescription, StructFieldDescription, TypeContents, TypeDescription, + }, }; use serde_json::{Map, Value as JsonValue}; @@ -99,10 +102,8 @@ fn encode_single_value( return Err(Box::new(EncodeError("expected bytes value"))); }; - let address = bech32::encode("erd", value.to_base32(), bech32::Variant::Bech32) - .map_err(|_| Box::new(EncodeError("failed to encode address")))?; - - Ok(JsonValue::String(address).into()) + let bech32_addres_string = bech32::encode(&Address::from_slice(value)); + Ok(JsonValue::String(bech32_addres_string).into()) }, "bool" => { let AnyValue::SingleValue(value) = input else { diff --git a/data/human-readable/tests/single_value_tests.rs b/data/human-readable/tests/single_value_tests.rs index 4e4f92c105..1d4535d97e 100644 --- a/data/human-readable/tests/single_value_tests.rs +++ b/data/human-readable/tests/single_value_tests.rs @@ -1,9 +1,9 @@ -use bech32::FromBase32; use multiversx_sc_codec_human_readable::{ decode_human_readable_value, default_value_for_abi_type, encode_human_readable_value, format::HumanReadableValue, AnyValue, SingleValue, }; use multiversx_sc_scenario::{ + bech32, meta::abi_json::deserialize_abi_from_json, multiversx_sc::{abi::ContractAbi, codec::top_encode_to_vec_u8}, }; @@ -135,19 +135,17 @@ fn serialize_single_value_address() { let result = decode_human_readable_value(&value, "Address", &abi).unwrap(); let serialized = top_encode_to_vec_u8(&result).unwrap(); - let (_, address_bytes, _) = bech32::decode(TEST_ADDRESS).unwrap(); - let address_bytes = Vec::::from_base32(&address_bytes).unwrap(); + let address = bech32::decode(TEST_ADDRESS); - assert_eq!(serialized, address_bytes); + assert_eq!(serialized, address.into_boxed_bytes().into_vec()); } #[test] fn deserialize_single_value_address() { let abi: ContractAbi = deserialize_abi_from_json(EMPTY_ABI_JSON).unwrap().into(); - let (_, address_bytes, _) = bech32::decode(TEST_ADDRESS).unwrap(); - let address_bytes = Vec::::from_base32(&address_bytes).unwrap(); + let address = bech32::decode(TEST_ADDRESS); - let value = AnyValue::SingleValue(SingleValue::Bytes(address_bytes.into())); + let value = AnyValue::SingleValue(SingleValue::Bytes(address.into_boxed_bytes().into_box())); let result = encode_human_readable_value(&value, "Address", &abi).unwrap(); assert_eq!(result.to_string(), format!("\"{}\"", TEST_ADDRESS)); diff --git a/framework/scenario/src/bech32.rs b/framework/scenario/src/bech32.rs index ef934cf225..a744fc5d35 100644 --- a/framework/scenario/src/bech32.rs +++ b/framework/scenario/src/bech32.rs @@ -1,14 +1,30 @@ +use std::{error::Error, fmt::Display}; + use bech32::{Bech32, Hrp}; use multiversx_sc::types::heap::Address; -pub fn decode(bech32_address: &str) -> Address { - let (_hrp, dest_address_bytes) = bech32::decode(bech32_address) - .unwrap_or_else(|err| panic!("bech32 decode error for {bech32_address}: {err}")); +#[derive(Debug)] +pub struct InvalidAddressLengthError; + +impl Display for InvalidAddressLengthError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + "Invalid address length after decoding".fmt(f) + } +} + +impl Error for InvalidAddressLengthError {} + +pub fn try_decode(bech32_address: &str) -> Result> { + let (_hrp, dest_address_bytes) = bech32::decode(bech32_address)?; if dest_address_bytes.len() != 32 { - panic!("Invalid address length after decoding") + return Err(Box::new(InvalidAddressLengthError)); } - Address::from_slice(&dest_address_bytes) + Ok(Address::from_slice(&dest_address_bytes)) +} + +pub fn decode(bech32_address: &str) -> Address { + try_decode(bech32_address).expect("bech32 Address decode failed") } pub fn encode(address: &Address) -> String { From 5ee275ee62940ef16d9a8ca22ded585f7dff914f Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Fri, 6 Sep 2024 19:25:02 +0300 Subject: [PATCH 9/9] cleanup --- .../wrappers/blockchain_wrapper.rs | 18 +++++++++--------- .../src/types/managed/basic/elliptic_curve.rs | 14 +++++++------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/framework/base/src/contract_base/wrappers/blockchain_wrapper.rs b/framework/base/src/contract_base/wrappers/blockchain_wrapper.rs index 684e8524f2..c499c9f663 100644 --- a/framework/base/src/contract_base/wrappers/blockchain_wrapper.rs +++ b/framework/base/src/contract_base/wrappers/blockchain_wrapper.rs @@ -40,7 +40,7 @@ where } } - #[deprecated(since = "0.44.0", note = "Please use method `get_caller` instead.")] + #[deprecated(since = "0.41.0", note = "Please use method `get_caller` instead.")] #[cfg(feature = "alloc")] #[inline] pub fn get_caller_legacy(&self) -> crate::types::Address { @@ -54,7 +54,7 @@ where ManagedAddress::from_handle(handle) } - #[deprecated(since = "0.44.0", note = "Please use method `get_sc_address` instead.")] + #[deprecated(since = "0.41.0", note = "Please use method `get_sc_address` instead.")] #[cfg(feature = "alloc")] #[inline] pub fn get_sc_address_legacy(&self) -> crate::types::Address { @@ -90,7 +90,7 @@ where } #[deprecated( - since = "0.44.0", + since = "0.41.0", note = "Please use method `get_shard_of_address` instead." )] #[cfg(feature = "alloc")] @@ -105,7 +105,7 @@ where } #[deprecated( - since = "0.44.0", + since = "0.41.0", note = "Please use method `is_smart_contract` instead." )] #[cfg(feature = "alloc")] @@ -119,7 +119,7 @@ where A::blockchain_api_impl().is_smart_contract(address.get_handle()) } - #[deprecated(since = "0.44.0", note = "Please use method `get_balance` instead.")] + #[deprecated(since = "0.41.0", note = "Please use method `get_balance` instead.")] #[cfg(feature = "alloc")] #[inline] pub fn get_balance_legacy(&self, address: &crate::types::Address) -> BigUint { @@ -162,7 +162,7 @@ where } #[deprecated( - since = "0.44.0", + since = "0.41.0", note = "Please use method `get_state_root_hash` instead." )] #[cfg(feature = "alloc")] @@ -178,7 +178,7 @@ where ManagedByteArray::from_handle(handle) } - #[deprecated(since = "0.44.0", note = "Please use method `get_tx_hash` instead.")] + #[deprecated(since = "0.41.0", note = "Please use method `get_tx_hash` instead.")] #[cfg(feature = "alloc")] #[inline] pub fn get_tx_hash_legacy(&self) -> crate::types::H256 { @@ -218,7 +218,7 @@ where } #[deprecated( - since = "0.44.0", + since = "0.41.0", note = "Please use method `get_block_random_seed` instead." )] #[cfg(feature = "alloc")] @@ -255,7 +255,7 @@ where } #[deprecated( - since = "0.44.0", + since = "0.41.0", note = "Please use method `get_prev_block_random_seed` instead." )] #[cfg(feature = "alloc")] diff --git a/framework/base/src/types/managed/basic/elliptic_curve.rs b/framework/base/src/types/managed/basic/elliptic_curve.rs index b05c7e3a5c..99189d06c5 100644 --- a/framework/base/src/types/managed/basic/elliptic_curve.rs +++ b/framework/base/src/types/managed/basic/elliptic_curve.rs @@ -155,7 +155,7 @@ impl EllipticCurve { ) } - #[deprecated(since = "0.44.0", note = "Please use method `scalar_mult` instead.")] + #[deprecated(since = "0.41.0", note = "Please use method `scalar_mult` instead.")] pub fn scalar_mult_legacy( &self, x_point: BigUint, @@ -203,7 +203,7 @@ impl EllipticCurve { } #[deprecated( - since = "0.44.0", + since = "0.41.0", note = "Please use method `scalar_base_mult` instead." )] pub fn scalar_base_mult_legacy(&self, data: &[u8]) -> (BigUint, BigUint) { @@ -238,7 +238,7 @@ impl EllipticCurve { ) } - #[deprecated(since = "0.44.0", note = "Please use method `marshal` instead.")] + #[deprecated(since = "0.41.0", note = "Please use method `marshal` instead.")] #[cfg(feature = "alloc")] pub fn marshal_legacy( &self, @@ -266,7 +266,7 @@ impl EllipticCurve { } #[deprecated( - since = "0.44.0", + since = "0.41.0", note = "Please use method `marshal_compressed` instead." )] #[cfg(feature = "alloc")] @@ -295,7 +295,7 @@ impl EllipticCurve { ManagedBuffer::from_handle(result_handle) } - #[deprecated(since = "0.44.0", note = "Please use method `unmarshal` instead.")] + #[deprecated(since = "0.41.0", note = "Please use method `unmarshal` instead.")] pub fn unmarshal_legacy(&self, data: &[u8]) -> (BigUint, BigUint) { let api = M::managed_type_impl(); let x_pair_handle = api.bi_new_zero(); @@ -329,7 +329,7 @@ impl EllipticCurve { } #[deprecated( - since = "0.44.0", + since = "0.41.0", note = "Please use method `unmarshal_compressed` instead." )] pub fn unmarshal_compressed_legacy(&self, data: &[u8]) -> (BigUint, BigUint) { @@ -364,7 +364,7 @@ impl EllipticCurve { ) } - #[deprecated(since = "0.44.0", note = "Please use method `generate_key` instead.")] + #[deprecated(since = "0.41.0", note = "Please use method `generate_key` instead.")] #[cfg(feature = "alloc")] pub fn generate_key_legacy(&self) -> (BigUint, BigUint, crate::types::heap::BoxedBytes) { let api = M::managed_type_impl();