Skip to content

Commit

Permalink
feat: make callable point takes deps as the first arg (#233)
Browse files Browse the repository at this point in the history
* feat: make callable_point function takes deps as the first arg

* feat: fix dynamic callee contracts' callable points

* test: add and format docs for macros

* feat: remove functions to get deps in global_api

* fix: fix merged dynamic_callee_contract
  • Loading branch information
loloicci authored Sep 2, 2022
1 parent 8501d46 commit d601ee1
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 77 deletions.
16 changes: 8 additions & 8 deletions contracts/dynamic-callee-contract/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use cosmwasm_std::{
callable_point, dynamic_link, entry_point, Addr, Contract, DepsMut, Env, GlobalApi,
callable_point, dynamic_link, entry_point, Addr, Contract, Deps, DepsMut, Env, GlobalApi,
MessageInfo, Response,
};
use serde::{Deserialize, Serialize};
Expand All @@ -20,7 +20,7 @@ pub fn instantiate(
}

#[callable_point]
fn pong(x: u64) -> u64 {
fn pong(_deps: Deps, x: u64) -> u64 {
x + 1
}

Expand All @@ -31,30 +31,30 @@ pub struct ExampleStruct {
}

#[callable_point]
fn pong_with_struct(example: ExampleStruct) -> ExampleStruct {
fn pong_with_struct(_deps: Deps, example: ExampleStruct) -> ExampleStruct {
ExampleStruct {
str_field: example.str_field + " world",
u64_field: example.u64_field + 1,
}
}

#[callable_point]
fn pong_with_tuple(input: (String, i32)) -> (String, i32) {
fn pong_with_tuple(_deps: Deps, input: (String, i32)) -> (String, i32) {
(input.0 + " world", input.1 + 1)
}

#[callable_point]
fn pong_with_tuple_takes_2_args(input1: String, input2: i32) -> (String, i32) {
fn pong_with_tuple_takes_2_args(_deps: Deps, input1: String, input2: i32) -> (String, i32) {
(input1 + " world", input2 + 1)
}

#[callable_point]
fn pong_env() -> Env {
fn pong_env(_deps: Deps) -> Env {
GlobalApi::env()
}

#[callable_point]
fn do_panic() {
fn do_panic(_deps: Deps) {
panic!();
}

Expand All @@ -69,7 +69,7 @@ trait ReEntrance: Contract {
}

#[callable_point]
fn reentrancy(address: Addr) {
fn reentrancy(_deps: Deps, address: Addr) {
let me = Me { address };
me.should_never_be_called()
}
Expand Down
6 changes: 3 additions & 3 deletions contracts/dynamic-caller-contract/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use cosmwasm_std::{
callable_point, dynamic_link, entry_point, from_slice, to_vec, Addr, Contract, DepsMut, Env,
MessageInfo, Response, Uint128,
callable_point, dynamic_link, entry_point, from_slice, to_vec, Addr, Contract, Deps, DepsMut,
Env, MessageInfo, Response, Uint128,
};
use serde::{Deserialize, Serialize};
use std::fmt;
Expand Down Expand Up @@ -145,7 +145,7 @@ pub fn try_do_panic(deps: DepsMut, _env: Env) -> Result<Response, ContractError>
}

#[callable_point]
fn should_never_be_called() {}
fn should_never_be_called(_deps: Deps) {}

#[cfg(test)]
mod tests {
Expand Down
20 changes: 10 additions & 10 deletions contracts/number/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use cosmwasm_std::{
callable_point, entry_point, to_binary, Binary, Deps, DepsMut, Env, GlobalApi, MessageInfo,
Response, Storage,
callable_point, entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response,
Storage,
};

use crate::error::ContractError;
Expand Down Expand Up @@ -77,21 +77,21 @@ fn query_number(deps: Deps) -> Result<NumberResponse, ContractError> {
}

#[callable_point]
fn add(by: i32) {
GlobalApi::with_deps_mut(|deps| handle_add(deps, by)).unwrap();
fn add(deps: DepsMut, by: i32) {
handle_add(deps, by).unwrap();
}

#[callable_point]
fn sub(by: i32) {
GlobalApi::with_deps_mut(|deps| handle_sub(deps, by)).unwrap();
fn sub(deps: DepsMut, by: i32) {
handle_sub(deps, by).unwrap();
}

#[callable_point]
fn mul(by: i32) {
GlobalApi::with_deps_mut(|deps| handle_mul(deps, by)).unwrap();
fn mul(deps: DepsMut, by: i32) {
handle_mul(deps, by).unwrap();
}

#[callable_point]
fn number() -> i32 {
GlobalApi::with_deps(|deps| read(deps.storage).unwrap())
fn number(deps: Deps) -> i32 {
read(deps.storage).unwrap()
}
74 changes: 63 additions & 11 deletions packages/derive/src/callable_point.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote};

use crate::utils::{collect_available_arg_types, has_return_value, make_typed_return};
use crate::utils::{abort_by, collect_available_arg_types, has_return_value, make_typed_return};

pub fn make_callable_point(function: syn::ItemFn) -> TokenStream {
let function_name_ident = &function.sig.ident;
let mod_name_ident = format_ident!("__wasm_export_{}", function_name_ident);
let args_len = function.sig.inputs.len();
// The first argument is `deps`, the rest is region pointers
if function.sig.inputs.is_empty() {
abort_by!(
function,
"callable_point",
"the first argument of callable_point function needs `deps` typed `Deps` or `DepsMut`"
)
}
let args_len = function.sig.inputs.len() - 1;

let arg_idents: Vec<_> = (0..args_len).map(|i| format_ident!("arg{}", i)).collect();
let vec_arg_idents: Vec<_> = (0..args_len)
.map(|i| format_ident!("vec_arg{}", i))
.collect();
let ptr_idents: Vec<_> = (0..args_len).map(|i| format_ident!("ptr{}", i)).collect();

let arg_types = collect_available_arg_types(&function.sig, "callable_point".to_string());
let orig_arg_types = collect_available_arg_types(&function.sig, "callable_point".to_string());
let arg_types = &orig_arg_types[1..];
let is_dep_mutable = match &orig_arg_types[0] {
syn::Type::Path(p) => {
if p.path.is_ident("Deps") {
false
} else if p.path.is_ident("DepsMut") {
true
} else {
abort_by!(
function, "callable_point",
"the first argument of callable_point function needs `deps` typed `Deps` or `DepsMut`"
)
}
}
_ => {
abort_by!(
function, "callable_point",
"the first argument of callable_point function needs `deps` typed `Deps` or `DepsMut`"
)
}
};

let renamed_param_defs: Vec<_> = ptr_idents
.iter()
.map(|id| {
Expand All @@ -23,8 +53,18 @@ pub fn make_callable_point(function: syn::ItemFn) -> TokenStream {
.collect();
let typed_return = make_typed_return(&function.sig.output);

let call_origin_return =
make_call_origin_and_return(function_name_ident, args_len, &function.sig.output);
let call_origin_return = make_call_origin_and_return(
is_dep_mutable,
function_name_ident,
args_len,
&function.sig.output,
);

let deps_def = if is_dep_mutable {
quote! { let mut deps = cosmwasm_std::make_dependencies() }
} else {
quote! { let deps = cosmwasm_std::make_dependencies() }
};

quote! {
#[cfg(target_arch = "wasm32")]
Expand All @@ -35,27 +75,37 @@ pub fn make_callable_point(function: syn::ItemFn) -> TokenStream {
extern "C" fn #function_name_ident(#(#renamed_param_defs),*) #typed_return {
#(let #vec_arg_idents: Vec<u8> = unsafe { cosmwasm_std::memory::consume_region(#ptr_idents as *mut cosmwasm_std::memory::Region)};)*
#(let #arg_idents: #arg_types = cosmwasm_std::from_slice(&#vec_arg_idents).unwrap();)*

#deps_def;

#call_origin_return
}
}
}
}

fn make_call_origin_and_return(
is_dep_mutable: bool,
func_name_ident: &syn::Ident,
args_len: usize,
return_type: &syn::ReturnType,
) -> TokenStream {
let arguments: Vec<_> = (0..args_len).map(|n| format_ident!("arg{}", n)).collect();

let call_func = if is_dep_mutable {
quote! { super::#func_name_ident(deps.as_mut() #(, #arguments)*) }
} else {
quote! { super::#func_name_ident(deps.as_ref() #(, #arguments)*) }
};

if has_return_value(return_type) {
quote! {
let result = super::#func_name_ident(#(#arguments),*);
let result = #call_func;
let vec_result = cosmwasm_std::to_vec(&result).unwrap();
cosmwasm_std::memory::release_buffer(vec_result) as u32
}
} else {
quote! { super::#func_name_ident(#(#arguments),*); }
call_func
}
}

Expand All @@ -68,19 +118,20 @@ mod tests {
fn make_call_origin_and_return_works() {
{
let function_foo_ret1: ItemFn = parse_quote! {
fn foo() -> u64 {
fn foo(deps: DepsMut) -> u64 {
1
}
};
let result_code = make_call_origin_and_return(
true,
&function_foo_ret1.sig.ident,
0,
&function_foo_ret1.sig.output,
)
.to_string();

let expected: TokenStream = parse_quote! {
let result = super::foo();
let result = super::foo(deps.as_mut());
let vec_result = cosmwasm_std::to_vec(&result).unwrap();
cosmwasm_std::memory::release_buffer(vec_result) as u32
};
Expand All @@ -89,19 +140,20 @@ mod tests {

{
let function_foo_ret2: ItemFn = parse_quote! {
fn foo() -> (u64, u64) {
fn foo(deps: Deps) -> (u64, u64) {
(1, 2)
}
};
let result_code = make_call_origin_and_return(
false,
&function_foo_ret2.sig.ident,
0,
&function_foo_ret2.sig.output,
)
.to_string();

let expected: TokenStream = parse_quote! {
let result = super::foo();
let result = super::foo(deps.as_ref());
let vec_result = cosmwasm_std::to_vec(&result).unwrap();
cosmwasm_std::memory::release_buffer(vec_result) as u32
};
Expand Down
27 changes: 23 additions & 4 deletions packages/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod callable_point;
mod contract;
mod dynamic_link;
mod utils;

/// This attribute macro generates the boilerplate required to call into the
/// contract-specific logic from the entry-points to the Wasm module.
///
Expand Down Expand Up @@ -93,6 +94,21 @@ pub fn entry_point(_attr: TokenStream, item: TokenStream) -> TokenStream {
res
}

/// This macro generate callable point function which can be called with dynamic link.
///
/// Function attributed with this macro must take `deps` typed `Deps` or `DepsMut`
/// as the first argument.
///
/// example usage:
/// ```
/// use cosmwasm_std::{Addr, Deps, callable_point};
///
/// #[callable_point]
/// fn validate_address_callable_from_other_contracts(deps: Deps) -> Addr {
/// // do something with deps, for example, using api.
/// deps.api.addr_validate("dummy_human_address").unwrap()
/// }
/// ```
#[proc_macro_error]
#[proc_macro_attribute]
pub fn callable_point(_attr: TokenStream, item: TokenStream) -> TokenStream {
Expand All @@ -107,15 +123,17 @@ pub fn callable_point(_attr: TokenStream, item: TokenStream) -> TokenStream {
res
}

/// This macro implements dynamic call functions for attributed trait.
/// This macro implements functions to call dynamic linked function for attributed trait.
///
/// This macro must take an attribute specifying a struct to implement the traits for.
/// The trait must have `cosmwasm_std::Contract` as a supertrait and each
/// methods of the trait must have `&self` receiver as its first argument.
///
/// This macro can take a bool value as a named attribute `user_defined_mock`
/// When this value is true, this macro generates implement of the trait for specified struct for only `target_arch = "wasm32"`.
/// So, with `user_defined_mock = true`, user can and must write mock implement of the trait for specified struct with `#[cfg(not(target_arch = "wasm32"))]`.
/// When this value is true, this macro generates implement of the trait for
/// specified struct for only `target_arch = "wasm32"`.
/// So, with `user_defined_mock = true`, user can and must write mock implement of
/// the trait for specified struct with `#[cfg(not(target_arch = "wasm32"))]`.
///
/// example usage:
///
Expand All @@ -132,7 +150,8 @@ pub fn callable_point(_attr: TokenStream, item: TokenStream) -> TokenStream {
/// fn callable_point_on_another_contract(&self, x: i32) -> i32;
/// }
///
/// // When `user_defined_mock = true` is specified, implement is generated only for "wasm32"
/// // When `user_defined_mock = true` is specified, implement is generated
/// // only for "wasm32"
/// #[cfg(not(target_arch = "wasm32"))]
/// impl TraitName for ContractStruct {
/// fn callable_point_on_another_contract(&self, x: i32) -> i32 {
Expand Down
2 changes: 1 addition & 1 deletion packages/std/src/exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ where
}

/// Makes all bridges to external dependencies (i.e. Wasm imports) that are injected by the VM
pub(crate) fn make_dependencies() -> OwnedDeps<ExternalStorage, ExternalApi, ExternalQuerier> {
pub fn make_dependencies() -> OwnedDeps<ExternalStorage, ExternalApi, ExternalQuerier> {
OwnedDeps {
storage: ExternalStorage::new(),
api: ExternalApi::new(),
Expand Down
Loading

0 comments on commit d601ee1

Please sign in to comment.