From 9d99077c6acfde68c06845f2a1eb2b5ed7983401 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Fri, 19 Mar 2021 13:56:49 -0400 Subject: [PATCH] fix(near-sdk-sim): allow borsh serialized arguments in sim macro (#345) * fix(near-sdk-sim): allow borsh serialized arguments in sim macro * Update near-sdk-core/src/code_generator/trait_item_method_info.rs * fix: formatting; ran `cargo fmt` at root Added a format check to CI * fix: address PR comment --- .github/workflows/test.yml | 2 + .../code_generator/impl_item_method_info.rs | 55 +++++++++-------- .../src/code_generator/item_impl_info.rs | 30 +++++++++- .../src/code_generator/item_trait_info.rs | 2 - .../code_generator/trait_item_method_info.rs | 60 +++++++++++-------- near-sdk-sim/src/cache.rs | 14 +---- near-sdk/src/utils/mod.rs | 14 +++-- 7 files changed, 109 insertions(+), 68 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 01a4b170c..fd231f42d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,5 +22,7 @@ jobs: components: rustfmt target: wasm32-unknown-unknown - uses: Swatinem/rust-cache@v1 + - name: Test Format + run: cargo fmt -- --check - name: Test run: cargo test --all diff --git a/near-sdk-core/src/code_generator/impl_item_method_info.rs b/near-sdk-core/src/code_generator/impl_item_method_info.rs index c8997f1ec..227d3b02b 100644 --- a/near-sdk-core/src/code_generator/impl_item_method_info.rs +++ b/near-sdk-core/src/code_generator/impl_item_method_info.rs @@ -173,24 +173,17 @@ impl ImplItemMethodInfo { let has_input_args = attr_signature_info.input_args().next().is_some(); let pat_type_list = attr_signature_info.pat_type_list(); - let json_args = if has_input_args { - let args: TokenStream2 = attr_signature_info - .input_args() - .fold(None, |acc: Option, value| { - let ident = &value.ident; - let ident_str = format!("{}", ident.to_string()); - Some(match acc { - None => quote! { #ident_str: #ident }, - Some(a) => quote! { #a, #ident_str: #ident }, - }) - }) - .unwrap(); - quote! { - let args = near_sdk::serde_json::json!({#args}); + let serialize_args = if has_input_args { + match &attr_signature_info.input_serializer { + SerializerType::Borsh => crate::TraitItemMethodInfo::generate_serialier( + &attr_signature_info, + &attr_signature_info.input_serializer, + ), + SerializerType::JSON => json_serialize(&attr_signature_info), } } else { quote! { - let args = near_sdk::serde_json::json!({}); + let args = vec![]; } }; @@ -210,15 +203,12 @@ impl ImplItemMethodInfo { &self, #pat_type_list }; let ident_str = format!("{}", ident.to_string()); - let body = if matches!(method_type, MethodType::View) { - quote! { - near_sdk::PendingContractTx::new(&self.account_id, #ident_str, args, true) - } + let is_view = if matches!(method_type, MethodType::View) { + quote! {true} } else { - quote! { - near_sdk::PendingContractTx::new(&self.account_id, #ident_str, args, false) - } + quote! {false} }; + let non_bindgen_attrs = non_bindgen_attrs.iter().fold(TokenStream2::new(), |acc, value| { quote! { #acc @@ -230,9 +220,26 @@ impl ImplItemMethodInfo { #[cfg(not(target_arch = "wasm32"))] #non_bindgen_attrs pub fn #ident#generics(#params) #return_ident { - #json_args - #body + #serialize_args + near_sdk::PendingContractTx::new_from_bytes(&self.account_id, #ident_str, args, #is_view) } } } } + +fn json_serialize(attr_signature_info: &AttrSigInfo) -> TokenStream2 { + let args: TokenStream2 = attr_signature_info + .input_args() + .fold(None, |acc: Option, value| { + let ident = &value.ident; + let ident_str = ident.to_string(); + Some(match acc { + None => quote! { #ident_str: #ident }, + Some(a) => quote! { #a, #ident_str: #ident }, + }) + }) + .unwrap(); + quote! { + let args = near_sdk::serde_json::json!({#args}).to_string().into_bytes(); + } +} diff --git a/near-sdk-core/src/code_generator/item_impl_info.rs b/near-sdk-core/src/code_generator/item_impl_info.rs index 49aeb4a07..834de60c5 100644 --- a/near-sdk-core/src/code_generator/item_impl_info.rs +++ b/near-sdk-core/src/code_generator/item_impl_info.rs @@ -657,8 +657,34 @@ mod tests { let expected = quote!( #[cfg(not(target_arch = "wasm32"))] pub fn method(&self, k: String,) -> near_sdk::PendingContractTx { - let args = near_sdk::serde_json::json!({ "k" : k }); - near_sdk::PendingContractTx::new(& self . account_id, "method", args, true) + let args = near_sdk::serde_json::json!({ "k": k }) + .to_string() + .into_bytes(); + near_sdk::PendingContractTx::new_from_bytes(&self.account_id, "method", args, true) + } + ); + assert_eq!(expected.to_string(), actual.to_string()); + } + + #[test] + fn marshall_borsh() { + let impl_type: Type = syn::parse_str("Hello").unwrap(); + let mut method: ImplItemMethod = syn::parse_str(r#" + pub fn borsh_test(&mut self, #[serializer(borsh)] a: String) {} + "#).unwrap(); + let method_info = ImplItemMethodInfo::new(&mut method, impl_type).unwrap(); + let actual = method_info.marshal_method(); + let expected = quote!( + #[cfg(not(target_arch = "wasm32"))] + pub fn borsh_test(&self, a: String,) -> near_sdk::PendingContractTx { + #[derive(near_sdk :: borsh :: BorshSerialize)] + struct Input { + a: String, + } + let args = Input { a, }; + let args = near_sdk::borsh::BorshSerialize::try_to_vec(&args) + .expect("Failed to serialize the cross contract args using Borsh."); + near_sdk::PendingContractTx::new_from_bytes(&self.account_id, "borsh_test", args, false) } ); assert_eq!(expected.to_string(), actual.to_string()); diff --git a/near-sdk-core/src/code_generator/item_trait_info.rs b/near-sdk-core/src/code_generator/item_trait_info.rs index 324bfa87c..1b94457a5 100644 --- a/near-sdk-core/src/code_generator/item_trait_info.rs +++ b/near-sdk-core/src/code_generator/item_trait_info.rs @@ -133,5 +133,3 @@ mod tests { assert_eq!(actual.to_string(), expected.to_string()); } } - - diff --git a/near-sdk-core/src/code_generator/trait_item_method_info.rs b/near-sdk-core/src/code_generator/trait_item_method_info.rs index 87a360f3d..a30d3f005 100644 --- a/near-sdk-core/src/code_generator/trait_item_method_info.rs +++ b/near-sdk-core/src/code_generator/trait_item_method_info.rs @@ -1,4 +1,7 @@ -use crate::info_extractor::{InputStructType, SerializerType, TraitItemMethodInfo}; +use crate::{ + info_extractor::{InputStructType, SerializerType, TraitItemMethodInfo}, + AttrSigInfo, +}; use quote::quote; use syn::export::TokenStream2; @@ -8,31 +11,13 @@ impl TraitItemMethodInfo { let ident = &self.attr_sig_info.ident; let ident_byte_str = &self.ident_byte_str; let pat_type_list = self.attr_sig_info.pat_type_list(); - let has_input_args = self.attr_sig_info.input_args().next().is_some(); - let struct_decl; - let constructor; - let value_ser = if !has_input_args { - struct_decl = TokenStream2::new(); - constructor = TokenStream2::new(); - quote! {let args = vec![]; } - } else { - struct_decl = self.attr_sig_info.input_struct(InputStructType::Serialization); - let constructor_call = self.attr_sig_info.constructor_expr(); - constructor = quote! {let args = #constructor_call;}; - match self.attr_sig_info.result_serializer { - SerializerType::JSON => quote! { - let args = near_sdk::serde_json::to_vec(&args).expect("Failed to serialize the cross contract args using JSON."); - }, - SerializerType::Borsh => quote! { - let args = near_sdk::borsh::BorshSerialize::try_to_vec(&args).expect("Failed to serialize the cross contract args using Borsh."); - }, - } - }; + let serialize = TraitItemMethodInfo::generate_serialier( + &self.attr_sig_info, + &self.attr_sig_info.result_serializer, + ); quote! { pub fn #ident(#pat_type_list __account_id: &T, __balance: near_sdk::Balance, __gas: near_sdk::Gas) -> near_sdk::Promise { - #struct_decl - #constructor - #value_ser + #serialize near_sdk::Promise::new(__account_id.to_string()) .function_call( #ident_byte_str.to_vec(), @@ -43,4 +28,31 @@ impl TraitItemMethodInfo { } } } + + pub fn generate_serialier( + attr_sig_info: &AttrSigInfo, + serializer: &SerializerType, + ) -> TokenStream2 { + let has_input_args = attr_sig_info.input_args().next().is_some(); + if !has_input_args { + return quote! { let args = vec![]; }; + } + let struct_decl = attr_sig_info.input_struct(InputStructType::Serialization); + let constructor_call = attr_sig_info.constructor_expr(); + let constructor = quote! { let args = #constructor_call; }; + let value_ser = match serializer { + SerializerType::JSON => quote! { + let args = near_sdk::serde_json::to_vec(&args).expect("Failed to serialize the cross contract args using JSON."); + }, + SerializerType::Borsh => quote! { + let args = near_sdk::borsh::BorshSerialize::try_to_vec(&args).expect("Failed to serialize the cross contract args using Borsh."); + }, + }; + + quote! { + #struct_decl + #constructor + #value_ser + } + } } diff --git a/near-sdk-sim/src/cache.rs b/near-sdk-sim/src/cache.rs index ac3ccc910..8b7eefab3 100644 --- a/near-sdk-sim/src/cache.rs +++ b/near-sdk-sim/src/cache.rs @@ -21,9 +21,7 @@ pub(crate) fn key_to_b58(key: &[u8]) -> String { impl ContractCache { #[allow(dead_code)] pub fn new() -> Self { - Self { - data: Rc::new(RefCell::new(HashMap::new())), - } + Self { data: Rc::new(RefCell::new(HashMap::new())) } } fn path() -> PathBuf { @@ -37,11 +35,7 @@ impl ContractCache { let prefix = path.parent().unwrap(); std::fs::create_dir_all(prefix).unwrap(); // Ensure we can read, write, and create file if it doesn't exist - OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(path) + OpenOptions::new().read(true).write(true).create(true).open(path) } fn get_path(&self, key: &[u8]) -> PathBuf { @@ -53,9 +47,7 @@ impl ContractCache { } pub fn insert(&self, key: &[u8], value: &[u8]) -> Option> { - (*self.data) - .borrow_mut() - .insert((*key).to_owned(), (*value).to_owned()) + (*self.data).borrow_mut().insert((*key).to_owned(), (*value).to_owned()) } pub fn get(&self, key: &[u8]) -> Option> { diff --git a/near-sdk/src/utils/mod.rs b/near-sdk/src/utils/mod.rs index 428789872..b91a19d1b 100644 --- a/near-sdk/src/utils/mod.rs +++ b/near-sdk/src/utils/mod.rs @@ -47,12 +47,16 @@ pub struct PendingContractTx { impl PendingContractTx { pub fn new(receiver_id: &str, method: &str, args: serde_json::Value, is_view: bool) -> Self { - Self { - receiver_id: receiver_id.to_string(), - method: method.to_string(), - args: args.to_string().into_bytes(), + PendingContractTx::new_from_bytes( + receiver_id, + method, + args.to_string().into_bytes(), is_view, - } + ) + } + + pub fn new_from_bytes(receiver_id: &str, method: &str, args: Vec, is_view: bool) -> Self { + Self { receiver_id: receiver_id.to_string(), method: method.to_string(), args, is_view } } }