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

feat: add interface validation method for dynamic link functions #245

Merged
merged 7 commits into from
Oct 26, 2022
Merged
Show file tree
Hide file tree
Changes from 6 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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: 1.51.0
toolchain: 1.57.0
target: wasm32-unknown-unknown
profile: minimal
override: true
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/burner/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/call-number/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/call-number/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ cosmwasm-storage = { path = "../../packages/storage", features = ["iterator"] }
schemars = "0.8.1"
serde = { version = "1.0.125", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.24" }
wasmer-types = { version = "1.0.2", features = ["enable-serde"] }

[dev-dependencies]
cosmwasm-schema = { path = "../../packages/schema" }
Expand Down
1 change: 1 addition & 0 deletions contracts/crypto-verify/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/dynamic-callee-contract/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/dynamic-callee-contract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ cosmwasm-storage = { path = "../../packages/storage", features = ["iterator"] }
schemars = "0.8.1"
serde = { version = "1.0.125", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.24" }
wasmer-types = { version = "1.0.2", features = ["enable-serde"] }

[dev-dependencies]
cosmwasm-schema = { path = "../../packages/schema" }
Expand Down
1 change: 1 addition & 0 deletions contracts/dynamic-caller-contract/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/dynamic-caller-contract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ cosmwasm-storage = { path = "../../packages/storage", features = ["iterator"] }
schemars = "0.8.1"
serde = { version = "1.0.125", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.24" }
wasmer-types = { version = "1.0.2", features = ["enable-serde"] }

[dev-dependencies]
cosmwasm-schema = { path = "../../packages/schema" }
Expand Down
24 changes: 24 additions & 0 deletions contracts/dynamic-caller-contract/schema/execute_msg.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,30 @@
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"validate_interface"
],
"properties": {
"validate_interface": {
"type": "object"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"validate_interface_err"
],
"properties": {
"validate_interface_err": {
"type": "object"
}
},
"additionalProperties": false
}
],
"definitions": {
Expand Down
28 changes: 27 additions & 1 deletion contracts/dynamic-caller-contract/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ impl Callee for CalleeContract {
fn do_panic(&self) {
panic!()
}

fn validate_interface(&self, _deps: Deps) -> cosmwasm_std::StdResult<()> {
Ok(())
}
}

// Note, you can use StdResult in some functions where you do not
Expand Down Expand Up @@ -96,11 +100,13 @@ pub fn execute(
ExecuteMsg::Ping { ping_num } => try_ping(deps, ping_num),
ExecuteMsg::TryReEntrancy {} => try_re_entrancy(deps, env),
ExecuteMsg::DoPanic {} => try_do_panic(deps, env),
ExecuteMsg::ValidateInterface {} => try_validate_interface(deps.as_ref(), env),
ExecuteMsg::ValidateInterfaceErr {} => try_validate_interface_err(deps.as_ref(), env),
}
}

pub fn try_ping(deps: DepsMut, ping_num: Uint128) -> Result<Response, ContractError> {
let address = from_slice(&deps.storage.get(b"dynamic_callee_contract").unwrap())?;
let address: Addr = from_slice(&deps.storage.get(b"dynamic_callee_contract").unwrap())?;
let contract = CalleeContract { address };
let pong_ret = contract.pong(ping_num.u128() as u64);
let struct_ret = contract.pong_with_struct(ExampleStruct {
Expand Down Expand Up @@ -144,6 +150,26 @@ pub fn try_do_panic(deps: DepsMut, _env: Env) -> Result<Response, ContractError>
Ok(Response::default())
}

pub fn try_validate_interface(deps: Deps, _env: Env) -> Result<Response, ContractError> {
let address = from_slice(&deps.storage.get(b"dynamic_callee_contract").unwrap())?;
loloicci marked this conversation as resolved.
Show resolved Hide resolved
let contract = CalleeContract { address };
contract.validate_interface(deps)?;
Ok(Response::default())
}

// should error
pub fn try_validate_interface_err(deps: Deps, _env: Env) -> Result<Response, ContractError> {
let address = from_slice(&deps.storage.get(b"dynamic_callee_contract").unwrap())?;
let err_interface: Vec<wasmer_types::ExportType<wasmer_types::FunctionType>> =
vec![wasmer_types::ExportType::new(
"not_exist",
([wasmer_types::Type::I32], [wasmer_types::Type::I32]).into(),
)];
deps.api
.validate_dynamic_link_interface(&address, &err_interface)?;
Ok(Response::default())
}

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

Expand Down
2 changes: 2 additions & 0 deletions contracts/dynamic-caller-contract/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub enum ExecuteMsg {
Ping { ping_num: Uint128 },
TryReEntrancy {},
DoPanic {},
ValidateInterface {},
ValidateInterfaceErr {},
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
Expand Down
1 change: 1 addition & 0 deletions contracts/hackatom/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/ibc-reflect-send/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/ibc-reflect/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/number/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/queue/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/reflect/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/staking/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/voting-with-uuid/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ proc-macro = true
default = []

[dependencies]
syn = { version = "1.0", features = ["full"] }
syn = { version = "1.0", features = ["full", "parsing"] }
quote = "1.0"
proc-macro2 = "1.0"
proc-macro-error = { version = "1", default-features = false }
Expand Down
52 changes: 48 additions & 4 deletions packages/derive/src/dynamic_link.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{
AttributeArgs, Ident, ItemTrait, Lit, Meta, NestedMeta, Signature, TraitItem, TypeParamBound,
parse_quote, AttributeArgs, Ident, ItemTrait, Lit, Meta, NestedMeta, ReturnType, Signature,
TraitItem, TypeParamBound,
};

use crate::utils::{abort_by, collect_available_arg_types, has_return_value, make_typed_return};
Expand Down Expand Up @@ -86,9 +87,16 @@ pub fn generate_import_contract_declaration(
contract_struct_id,
&signatures,
);

let mut new_trait_def = trait_def.clone();
let method_validate_interface: TraitItem = parse_quote! {
fn validate_interface(&self, deps: cosmwasm_std::Deps) -> cosmwasm_std::StdResult<()>;
};
new_trait_def.items.push(method_validate_interface);

if does_use_user_defined_mock {
quote! {
#trait_def
#new_trait_def

#[cfg(target_arch = "wasm32")]
#extern_block
Expand All @@ -98,7 +106,7 @@ pub fn generate_import_contract_declaration(
}
} else {
quote! {
#trait_def
#new_trait_def

#extern_block

Expand Down Expand Up @@ -145,6 +153,30 @@ fn generate_extern_block(module_name: &str, methods: &[&Signature]) -> TokenStre
}
}

fn generate_validate_interface_method(methods: &[&Signature]) -> TokenStream {
let interfaces = methods.iter().map(|sig| {
let name = sig.ident.to_string();
// -1 for &self and +1 for arg `env`, so equals to len()
let input_len = sig.inputs.len();
let result_len = match sig.output {
ReturnType::Default => 0_usize,
ReturnType::Type(..) => 1_usize,
};
quote! {
wasmer_types::ExportType::new(#name, ([wasmer_types::Type::I32; #input_len], [wasmer_types::Type::I32; #result_len]).into())
}
});
quote! {
fn validate_interface(&self, deps: cosmwasm_std::Deps) -> cosmwasm_std::StdResult<()> {
let address = self.get_address();
let expected_interface: Vec<wasmer_types::ExportType<wasmer_types::FunctionType>> = vec![
#(#interfaces,)*
];
deps.api.validate_dynamic_link_interface(&address, &expected_interface)
}
}
}

fn generate_implements(
module_name: &str,
trait_id: &Ident,
Expand All @@ -154,9 +186,11 @@ fn generate_implements(
let impl_funcs = methods
.iter()
.map(|sig| generate_serialization_func(module_name, sig));
let impl_validate_interface = generate_validate_interface_method(methods);
quote! {
impl #trait_id for #struct_id {
#(#impl_funcs)*
#impl_validate_interface
}
}
}
Expand Down Expand Up @@ -223,7 +257,7 @@ fn make_call_function_and_return(
#[cfg(test)]
mod tests {
use super::*;
use syn::{parse_quote, ItemTrait, Signature};
use syn::{ItemTrait, Signature};

#[test]
fn make_call_function_and_return_works() {
Expand Down Expand Up @@ -474,6 +508,16 @@ mod tests {
cosmwasm_std::from_slice(&vec_result).unwrap()
}
}

fn validate_interface(&self, deps: cosmwasm_std::Deps) -> cosmwasm_std::StdResult<()> {
let address = self.get_address();
let expected_interface: Vec<wasmer_types::ExportType<wasmer_types::FunctionType>> = vec![
wasmer_types::ExportType::new("foo", ([wasmer_types::Type::I32; 3usize], [wasmer_types::Type::I32; 1usize]).into()),
wasmer_types::ExportType::new("bar", ([wasmer_types::Type::I32; 1usize], [wasmer_types::Type::I32; 0usize]).into()),
wasmer_types::ExportType::new("foobar", ([wasmer_types::Type::I32; 3usize], [wasmer_types::Type::I32; 1usize]).into()),
];
deps.api.validate_dynamic_link_interface(&address, &expected_interface)
}
}
};
assert_eq!(expected.to_string(), result_code);
Expand Down
12 changes: 11 additions & 1 deletion packages/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ pub fn callable_point(_attr: TokenStream, item: TokenStream) -> TokenStream {

/// This macro implements functions to call dynamic linked function for attributed trait.
///
/// To use this macro, the contract must declare the import
/// `wasmer-types = { version = "1.0.2", features = ["enable-serde"] }`
/// in Cargo.toml
///
/// 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.
Expand All @@ -138,7 +142,7 @@ pub fn callable_point(_attr: TokenStream, item: TokenStream) -> TokenStream {
/// example usage:
///
/// ```
/// use cosmwasm_std::{Addr, Contract, dynamic_link};
/// use cosmwasm_std::{Addr, Contract, Deps, StdResult, dynamic_link};
///
/// #[derive(Contract)]
/// struct ContractStruct {
Expand All @@ -157,6 +161,12 @@ pub fn callable_point(_attr: TokenStream, item: TokenStream) -> TokenStream {
/// fn callable_point_on_another_contract(&self, x: i32) -> i32 {
/// 42
/// }
///
/// // validate_interface is auto generated function from `dynamic_link` macro.
/// // this function must be defined in the mock.
/// fn validate_interface(&self, dep: Deps) -> StdResult<()> {
/// Ok(())
/// }
/// }
/// ```
#[proc_macro_error]
Expand Down
1 change: 1 addition & 0 deletions packages/std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ schemars = "0.8.1"
serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"] }
thiserror = "1.0"
uuid = { version = "1.0.0-alpha.1", features = ["v5", "serde"] }
wasmer-types = { version = "1.0.2", features = ["enable-serde"] }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
cosmwasm-crypto = { path = "../crypto", version = "0.14.0-0.4.0" }
Expand Down
Loading