Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

human readable format tool prototype #1088

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ resolver = "2"
members = [
"data/codec",
"data/codec-derive",
"data/human-readable",

"framework/base",
"framework/derive",
Expand Down
2 changes: 1 addition & 1 deletion data/codec/src/single/top_en.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<O>(&self, output: O) -> Result<(), EncodeError>
where
Expand Down
25 changes: 25 additions & 0 deletions data/human-readable/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "multiversx-sc-codec-human-readable"
version = "0.1.0"
edition = "2018"
publish = false

authors = ["Andrei Marinica <[email protected]>", "MultiversX <[email protected]>"]
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.41.0"
path = "../../framework/scenario"

[dependencies.multiversx-sc-meta]
version = "=0.41.0"
path = "../../framework/meta"
56 changes: 56 additions & 0 deletions data/human-readable/src/interpret.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
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<AnyValue, Box<dyn Error>> {
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: &[],
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<AnyValue, Box<dyn Error>> {
match &type_description.contents {
TypeContents::NotSpecified => interpret_single_value(input, type_description.name.as_str()),
TypeContents::Enum(_) => todo!(),
TypeContents::Struct(_) => todo!(),
}
}

fn interpret_single_value(input: &str, type_name: &str) -> Result<AnyValue, Box<dyn Error>> {
match type_name {
"BigUint" | "u64" | "u32" | "u16" | "usize" | "u8" => {
let value = input.parse::<BigUint>()?;
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 {}
6 changes: 6 additions & 0 deletions data/human-readable/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
mod interpret;
mod value;

use multiversx_sc_scenario::multiversx_sc;
pub use interpret::*;
pub use value::*;
39 changes: 39 additions & 0 deletions data/human-readable/src/value/any_value.rs
Original file line number Diff line number Diff line change
@@ -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<EnumVariant>),
}

impl NestedEncode for AnyValue {
fn dep_encode_or_handle_err<O, H>(&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<O, H>(&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!(),
}
}
}
6 changes: 6 additions & 0 deletions data/human-readable/src/value/enum_value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use crate::AnyValue;

pub struct EnumVariant {
pub discriminant: usize,
pub value: AnyValue,
}
9 changes: 9 additions & 0 deletions data/human-readable/src/value/mod.rs
Original file line number Diff line number Diff line change
@@ -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::*;
41 changes: 41 additions & 0 deletions data/human-readable/src/value/single_value.rs
Original file line number Diff line number Diff line change
@@ -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<O, H>(&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<O, H>(&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),
}
}
}
38 changes: 38 additions & 0 deletions data/human-readable/src/value/struct_value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use multiversx_sc_scenario::multiversx_sc::codec::{
EncodeErrorHandler, NestedEncode, NestedEncodeOutput, TopEncode, TopEncodeOutput,
};

use crate::AnyValue;

pub struct StructValue(Vec<StructField>);

pub struct StructField {
pub name: String,
pub value: AnyValue,
}

impl NestedEncode for StructValue {
fn dep_encode_or_handle_err<O, H>(&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<O, H>(&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(())
}
}
36 changes: 36 additions & 0 deletions data/human-readable/tests/single_value_basic_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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#"{
"buildInfo": {
"rustc": {
"version": "1.62.0-nightly",
"commitHash": "306ba8357fb36212b7d30efb9eb9e41659ac1445",
"commitDate": "2022-04-05",
"channel": "Nightly",
"short": "rustc 1.62.0-nightly (306ba8357 2022-04-05)"
},
"contractCrate": {
"name": "adder",
"version": "0.0.0"
},
"framework": {
"name": "elrond-wasm",
"version": "0.30.0"
}
},
"name": "Test",
"endpoints": [],
"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]);
}
17 changes: 15 additions & 2 deletions framework/base/src/abi/type_description.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use alloc::{string::String, vec::Vec};
use alloc::{
string::{String, ToString},
vec::Vec,
};

#[derive(Clone, Debug)]
pub struct TypeDescription {
Expand Down Expand Up @@ -43,6 +46,16 @@ pub struct EnumVariantDescription {
#[derive(Clone, Debug)]
pub struct StructFieldDescription {
pub docs: &'static [&'static str],
pub name: &'static str,
pub name: String,
pub field_type: String,
}

impl StructFieldDescription {
pub fn new(docs: &'static [&'static str], name: &str, field_type: String) -> Self {
Self {
docs,
name: name.to_string(),
field_type,
}
}
}
10 changes: 5 additions & 5 deletions framework/derive/src/type_abi_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ fn field_snippet(index: usize, field: &syn::Field) -> proc_macro2::TokenStream {
};
let field_ty = &field.ty;
quote! {
field_descriptions.push(multiversx_sc::abi::StructFieldDescription {
docs: &[ #(#field_docs),* ],
name: #field_name_str,
field_type: <#field_ty>::type_name(),
});
field_descriptions.push(multiversx_sc::abi::StructFieldDescription::new(
&[ #(#field_docs),* ],
#field_name_str,
<#field_ty>::type_name(),
));
<#field_ty>::provide_type_descriptions(accumulator);
}
}
Expand Down
1 change: 1 addition & 0 deletions framework/meta/src/abi_json/build_info_abi_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ impl RustcAbiJson {
pub struct ContractCrateBuildAbiJson {
pub name: String,
pub version: String,
#[serde(default)]
#[serde(skip_serializing_if = "String::is_empty")]
pub git_version: String,
}
Expand Down
8 changes: 8 additions & 0 deletions framework/meta/src/abi_json/contract_abi_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ pub struct ContractAbiJson {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub build_info: Option<BuildInfoAbiJson>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub docs: Vec<String>,
pub name: String,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub constructor: Option<ConstructorAbiJson>,
pub endpoints: Vec<EndpointAbiJson>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub events: Vec<EventAbiJson>,
pub has_callback: bool,
pub types: BTreeMap<String, TypeDescriptionJson>,
Expand Down Expand Up @@ -53,3 +57,7 @@ pub fn serialize_abi_to_json(abi_json: &ContractAbiJson) -> String {
serialized.push('\n');
serialized
}

pub fn deserialize_abi_from_json(input: &str) -> Result<ContractAbiJson, String> {
serde_json::from_str(input).map_err(|err| err.to_string())
}
Loading