Skip to content

Commit

Permalink
Merge pull request #1272 from multiversx/abi-decode
Browse files Browse the repository at this point in the history
ABI decode fix, type desciption from json
  • Loading branch information
andrei-marinica authored Nov 20, 2023
2 parents 6b125fc + dbdfd20 commit 9b2f741
Show file tree
Hide file tree
Showing 20 changed files with 413 additions and 141 deletions.
30 changes: 29 additions & 1 deletion contracts/feature-tests/abi-tester/tests/abi_tester_abi_test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::{fs, fs::File, io::Write};

use multiversx_sc::contract_base::ContractAbiProvider;
use multiversx_sc::{
abi::{EnumVariantDescription, TypeContents},
contract_base::ContractAbiProvider,
};
use multiversx_sc_meta::{
abi_json::{self, EsdtAttributeAbiJson},
esdt_attr_file_json::serialize_esdt_attribute_json,
Expand Down Expand Up @@ -82,3 +85,28 @@ fn check_multi_contract_config() {
vec!["external_view", "payable_any_token", "label_a"]
);
}

#[test]
fn abi_deserialization_check() {
let main_json = fs::read_to_string("./abi_tester_expected_main.abi.json").unwrap();
let main_abi = multiversx_sc_meta::abi_json::deserialize_abi_from_json(&main_json).unwrap();
let abi_enum_type = main_abi
.types
.get("AbiEnum")
.unwrap()
.to_type_description("AbiEnum");
if let TypeContents::Enum(variants) = abi_enum_type.contents {
assert_eq!(variants.len(), 4);
assert_eq!(
variants[0],
EnumVariantDescription {
docs: vec![],
name: "Nothing".to_string(),
discriminant: 0,
fields: vec![]
}
);
} else {
panic!("wrong AbiEnum type contents")
}
}
25 changes: 22 additions & 3 deletions framework/base/src/abi/contract_abi.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use super::*;
use alloc::{string::String, vec::Vec};
use alloc::{
string::{String, ToString},
vec::Vec,
};

#[derive(Debug, Default, Clone)]
pub struct ContractAbi {
pub build_info: BuildInfoAbi,
pub docs: &'static [&'static str],
pub name: &'static str,
pub docs: Vec<String>,
pub name: String,
pub constructors: Vec<EndpointAbi>,
pub endpoints: Vec<EndpointAbi>,
pub promise_callbacks: Vec<EndpointAbi>,
Expand All @@ -16,6 +19,22 @@ pub struct ContractAbi {
}

impl ContractAbi {
/// Used in code generation.
pub fn new(build_info: BuildInfoAbi, docs: &[&str], name: &str, has_callback: bool) -> Self {
ContractAbi {
build_info,
docs: docs.iter().map(|s| s.to_string()).collect(),
name: name.to_string(),
constructors: Vec::new(),
endpoints: Vec::new(),
promise_callbacks: Vec::new(),
events: Vec::new(),
esdt_attributes: Vec::new(),
has_callback,
type_descriptions: TypeDescriptionContainerImpl::new(),
}
}

pub fn coalesce(&mut self, other: Self) {
self.constructors
.extend_from_slice(other.constructors.as_slice());
Expand Down
59 changes: 47 additions & 12 deletions framework/base/src/abi/endpoint_abi.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
use super::*;
use alloc::vec::Vec;
use alloc::{
string::{String, ToString},
vec::Vec,
};

#[derive(Clone, Debug)]
pub struct InputAbi {
pub arg_name: &'static str,
pub arg_name: String,
pub type_name: TypeName,
// pub original_type_name: TypeName,
pub multi_arg: bool,
}

#[derive(Clone, Debug)]
pub struct OutputAbi {
pub output_name: &'static str,
pub output_name: String,
pub type_name: TypeName,
pub multi_result: bool,
}
Expand All @@ -36,24 +39,56 @@ pub enum EndpointTypeAbi {

#[derive(Clone, Default, Debug)]
pub struct EndpointAbi {
pub docs: &'static [&'static str],
pub name: &'static str,
pub rust_method_name: &'static str,
pub docs: Vec<String>,
pub name: String,
pub rust_method_name: String,
pub only_owner: bool,
pub only_admin: bool,
pub labels: &'static [&'static str],
pub labels: Vec<String>,
pub endpoint_type: EndpointTypeAbi,
pub mutability: EndpointMutabilityAbi,
pub payable_in_tokens: &'static [&'static str],
pub payable_in_tokens: Vec<String>,
pub inputs: Vec<InputAbi>,
pub outputs: OutputAbis,
pub allow_multiple_var_args: bool,
}

impl EndpointAbi {
pub fn add_input<T: TypeAbi>(&mut self, arg_name: &'static str) {
/// Used in code generation.
///
/// TODO: replace with builder pattern to gt rid of the too many arguments.
#[allow(clippy::too_many_arguments)]
pub fn new(
docs: &[&str],
name: &str,
rust_method_name: &str,
only_owner: bool,
only_admin: bool,
mutability: EndpointMutabilityAbi,
endpoint_type: EndpointTypeAbi,
payable_in_tokens: &[&str],
labels: &[&str],
allow_multiple_var_args: bool,
) -> Self {
EndpointAbi {
docs: docs.iter().map(|s| s.to_string()).collect(),
name: name.to_string(),
rust_method_name: rust_method_name.to_string(),
only_owner,
only_admin,
labels: labels.iter().map(|s| s.to_string()).collect(),
endpoint_type,
mutability,
payable_in_tokens: payable_in_tokens.iter().map(|s| s.to_string()).collect(),
inputs: Vec::new(),
outputs: Vec::new(),
allow_multiple_var_args,
}
}

pub fn add_input<T: TypeAbi>(&mut self, arg_name: &str) {
self.inputs.push(InputAbi {
arg_name,
arg_name: arg_name.to_string(),
type_name: T::type_name(),
multi_arg: T::is_variadic(),
});
Expand All @@ -69,8 +104,8 @@ impl EndpointAbi {
labels: &'static [&'static str],
) -> Self {
EndpointAbi {
name,
labels,
name: name.to_string(),
labels: labels.iter().map(|s| s.to_string()).collect(),
endpoint_type: EndpointTypeAbi::Endpoint,
..Default::default()
}
Expand Down
9 changes: 6 additions & 3 deletions framework/base/src/abi/esdt_attribute_abi.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
use alloc::string::{String, ToString};

use super::{TypeAbi, TypeDescriptionContainerImpl, TypeName};

#[derive(Clone, Debug)]
pub struct EsdtAttributeAbi {
pub ticker: &'static str,
pub ticker: String,
pub ty: TypeName,
pub type_descriptions: TypeDescriptionContainerImpl,
}

impl EsdtAttributeAbi {
pub fn new<T: TypeAbi>(arg_name: &'static str) -> EsdtAttributeAbi {
/// Used in code generation.
pub fn new<T: TypeAbi>(arg_name: &str) -> EsdtAttributeAbi {
let mut type_descriptions = TypeDescriptionContainerImpl::default();
T::provide_type_descriptions(&mut type_descriptions);
EsdtAttributeAbi {
ticker: arg_name,
ticker: arg_name.to_string(),
ty: T::type_name(),
type_descriptions,
}
Expand Down
25 changes: 19 additions & 6 deletions framework/base/src/abi/event_abi.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
use super::*;
use alloc::vec::Vec;
use alloc::{
string::{String, ToString},
vec::Vec,
};

#[derive(Clone, Debug)]
pub struct EventInputAbi {
pub arg_name: &'static str,
pub arg_name: String,
pub type_name: TypeName,
pub indexed: bool,
}

#[derive(Clone, Debug)]
pub struct EventAbi {
pub docs: &'static [&'static str],
pub identifier: &'static str,
pub docs: Vec<String>,
pub identifier: String,
pub inputs: Vec<EventInputAbi>,
}

impl EventAbi {
pub fn add_input<T: TypeAbi>(&mut self, arg_name: &'static str, indexed: bool) {
/// Used in code generation.
pub fn new(docs: &[&str], identifier: &str) -> Self {
EventAbi {
docs: docs.iter().map(|s| s.to_string()).collect(),
identifier: identifier.to_string(),
inputs: Vec::new(),
}
}

/// Used in code generation.
pub fn add_input<T: TypeAbi>(&mut self, arg_name: &str, indexed: bool) {
self.inputs.push(EventInputAbi {
arg_name,
arg_name: arg_name.to_string(),
type_name: T::type_name(),
indexed,
});
Expand Down
6 changes: 3 additions & 3 deletions framework/base/src/abi/type_abi.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::*;
use alloc::vec::Vec;
use alloc::{string::ToString, vec::Vec};

pub trait TypeAbi {
fn type_name() -> TypeName {
Expand All @@ -15,7 +15,7 @@ pub trait TypeAbi {
accumulator.insert(
type_name,
TypeDescription {
docs: &[],
docs: Vec::new(),
name: Self::type_name(),
contents: TypeContents::NotSpecified,
},
Expand Down Expand Up @@ -43,7 +43,7 @@ pub trait TypeAbi {
""
};
result.push(OutputAbi {
output_name,
output_name: output_name.to_string(),
type_name: Self::type_name(),
multi_result: Self::is_variadic(),
});
Expand Down
76 changes: 65 additions & 11 deletions framework/base/src/abi/type_description.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use alloc::{string::String, vec::Vec};
use alloc::{
string::{String, ToString},
vec::Vec,
};

#[derive(Clone, Debug)]
pub struct TypeDescription {
pub docs: &'static [&'static str],
pub docs: Vec<String>,
pub name: String,
pub contents: TypeContents,
}
Expand All @@ -13,12 +16,23 @@ impl TypeDescription {
/// we must reserve the type key (type name) before computing its fields.
/// We use this as value while the fields are being computed.
pub const PLACEHOLDER: TypeDescription = TypeDescription {
docs: &[],
docs: Vec::new(),
name: String::new(),
contents: TypeContents::NotSpecified,
};
}

impl TypeDescription {
/// Used in code generation.
pub fn new(docs: &[&str], name: String, contents: TypeContents) -> Self {
TypeDescription {
docs: docs.iter().map(|s| s.to_string()).collect(),
name,
contents,
}
}
}

#[derive(Clone, Debug)]
pub enum TypeContents {
NotSpecified,
Expand All @@ -33,28 +47,68 @@ impl TypeContents {
}
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EnumVariantDescription {
pub docs: &'static [&'static str],
pub name: &'static str,
pub docs: Vec<String>,
pub name: String,
pub discriminant: usize,
pub fields: Vec<StructFieldDescription>,
}

#[derive(Clone, Debug)]
impl EnumVariantDescription {
/// Used in code generation.
///
/// TODO: builder pattern for more elegant code.
pub fn new(
docs: &[&str],
name: &str,
discriminant: usize,
fields: Vec<StructFieldDescription>,
) -> Self {
EnumVariantDescription {
docs: docs.iter().map(|s| s.to_string()).collect(),
name: name.to_string(),
discriminant,
fields,
}
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StructFieldDescription {
pub docs: &'static [&'static str],
pub name: &'static str,
pub docs: Vec<String>,
pub name: String,
pub field_type: String,
}

impl StructFieldDescription {
/// Used in code generation.
pub fn new(docs: &[&str], name: &str, field_type: String) -> Self {
Self {
docs: docs.iter().map(|s| s.to_string()).collect(),
name: name.to_string(),
field_type,
}
}
}

/// An explicit enum is an enum that gets serialized by name instead of discriminant.
///
/// This makes it easier for humans to read readable in the transaction output.
///
/// It cannot have data fields, only simple enums allowed.
#[derive(Clone, Debug)]
pub struct ExplicitEnumVariantDescription {
pub docs: &'static [&'static str],
pub name: &'static str,
pub docs: Vec<String>,
pub name: String,
}

impl ExplicitEnumVariantDescription {
/// Used in code generation.
pub fn new(docs: &[&str], name: &str) -> Self {
Self {
docs: docs.iter().map(|s| s.to_string()).collect(),
name: name.to_string(),
}
}
}
Loading

0 comments on commit 9b2f741

Please sign in to comment.