From d66a194804b6f3d1b4fc9850b3669811f43d9144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 8 Feb 2023 08:56:41 +0100 Subject: [PATCH 01/18] Remove seemingly unused struct --- crates/e2e/macro/src/config.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/crates/e2e/macro/src/config.rs b/crates/e2e/macro/src/config.rs index 58b53ca2ef7..5019571c5cd 100644 --- a/crates/e2e/macro/src/config.rs +++ b/crates/e2e/macro/src/config.rs @@ -82,21 +82,6 @@ impl E2EConfig { } } -/// The environmental types definition. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Environment { - /// The underlying Rust type. - pub path: syn::Path, -} - -impl Default for Environment { - fn default() -> Self { - Self { - path: syn::parse_quote! { ::ink_env::DefaultEnvironment }, - } - } -} - #[cfg(test)] mod tests { use super::*; From d91ab9c6c7b07addd8e5ade52e5dc06444c5ef9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 8 Feb 2023 09:59:43 +0100 Subject: [PATCH 02/18] Customize environment in e2e --- crates/e2e/macro/src/codegen.rs | 8 +++- crates/e2e/macro/src/config.rs | 70 ++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/crates/e2e/macro/src/codegen.rs b/crates/e2e/macro/src/codegen.rs index b8167c19f3a..a38aec97c75 100644 --- a/crates/e2e/macro/src/codegen.rs +++ b/crates/e2e/macro/src/codegen.rs @@ -72,6 +72,12 @@ impl InkE2ETest { syn::ReturnType::Type(rarrow, ret_type) => quote! { #rarrow #ret_type }, }; + let environment = self + .test + .config + .environment() + .unwrap_or_else(|| syn::parse_quote! { ink::env::DefaultEnvironment }); + let mut additional_contracts: Vec = self.test.config.additional_contracts(); let default_main_contract_manifest_path = String::from("Cargo.toml"); @@ -158,7 +164,7 @@ impl InkE2ETest { let mut client = ::ink_e2e::Client::< ::ink_e2e::PolkadotConfig, - ink::env::DefaultEnvironment + #environment >::new( node_proc.client(), [ #( #contracts ),* ] diff --git a/crates/e2e/macro/src/config.rs b/crates/e2e/macro/src/config.rs index 5019571c5cd..cb4d6a4ebba 100644 --- a/crates/e2e/macro/src/config.rs +++ b/crates/e2e/macro/src/config.rs @@ -20,6 +20,7 @@ use ink_ir::{ WhitelistedAttributes, }, }; +use syn::Path; /// The End-to-End test configuration. #[derive(Debug, Default, PartialEq, Eq)] @@ -28,6 +29,8 @@ pub struct E2EConfig { whitelisted_attributes: WhitelistedAttributes, /// Additional contracts that have to be built before executing the test. additional_contracts: Vec, + /// Custom environment for the contracts, if specified. + environment: Option, } impl TryFrom for E2EConfig { @@ -36,6 +39,7 @@ impl TryFrom for E2EConfig { fn try_from(args: ast::AttributeArgs) -> Result { let mut whitelisted_attributes = WhitelistedAttributes::default(); let mut additional_contracts: Option<(syn::LitStr, ast::MetaNameValue)> = None; + let mut environment: Option<(Path, ast::MetaNameValue)> = None; for arg in args.into_iter() { if arg.name.is_ident("keep_attr") { @@ -54,7 +58,19 @@ impl TryFrom for E2EConfig { } else { return Err(format_err_spanned!( arg, - "expected a bool literal for `additional_contracts` ink! e2e test configuration argument", + "expected a string literal for `additional_contracts` ink! e2e test configuration argument", + )) + } + } else if arg.name.is_ident("environment") { + if let Some((_, ast)) = environment { + return Err(duplicate_config_err(ast, arg, "environment", "e2e test")) + } + if let ast::PathOrLit::Path(path) = &arg.value { + environment = Some((path.clone(), arg)) + } else { + return Err(format_err_spanned!( + arg, + "expected a path for `environment` ink! e2e test configuration argument", )) } } else { @@ -67,9 +83,12 @@ impl TryFrom for E2EConfig { let additional_contracts = additional_contracts .map(|(value, _)| value.value().split(' ').map(String::from).collect()) .unwrap_or_else(Vec::new); + let environment = environment.map(|(path, _)| path); + Ok(E2EConfig { additional_contracts, whitelisted_attributes, + environment, }) } } @@ -80,6 +99,11 @@ impl E2EConfig { pub fn additional_contracts(&self) -> Vec { self.additional_contracts.clone() } + + /// Custom environment for the contracts, if specified. + pub fn environment(&self) -> Option { + self.environment.clone() + } } #[cfg(test)] @@ -113,7 +137,7 @@ mod tests { } #[test] - fn duplicate_args_fails() { + fn duplicate_additional_contracts_fails() { assert_try_from( syn::parse_quote! { additional_contracts = "adder/Cargo.toml", @@ -125,6 +149,47 @@ mod tests { ); } + #[test] + fn duplicate_environment_fails() { + assert_try_from( + syn::parse_quote! { + environment = crate::CustomEnvironment, + environment = crate::CustomEnvironment, + }, + Err( + "encountered duplicate ink! e2e test `environment` configuration argument", + ), + ); + } + + #[test] + fn environment_as_literal_fails() { + assert_try_from( + syn::parse_quote! { + environment = "crate::CustomEnvironment", + }, + Err("expected a path for `environment` ink! e2e test configuration argument"), + ); + } + + #[test] + fn full_config_works() { + assert_try_from( + syn::parse_quote! { + additional_contracts = "adder/Cargo.toml flipper/Cargo.toml", + environment = crate::CustomEnvironment, + }, + Ok(E2EConfig { + whitelisted_attributes: Default::default(), + additional_contracts: vec![ + "adder/Cargo.toml".into(), + "flipper/Cargo.toml".into(), + ], + environment: Some(syn::parse_quote! { crate::CustomEnvironment }), + }), + ); + } + #[test] fn keep_attr_works() { let mut attrs = WhitelistedAttributes::default(); @@ -137,6 +202,7 @@ mod tests { Ok(E2EConfig { whitelisted_attributes: attrs, additional_contracts: Vec::new(), + environment: None, }), ) } From 3ded505aa1317cbf193ac4cd256f286ca8b8d77b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 8 Feb 2023 11:00:46 +0100 Subject: [PATCH 03/18] Example contract --- examples/custom-environment/.gitignore | 9 ++ examples/custom-environment/Cargo.toml | 34 ++++++++ examples/custom-environment/README.md | 19 +++++ examples/custom-environment/lib.rs | 114 +++++++++++++++++++++++++ 4 files changed, 176 insertions(+) create mode 100644 examples/custom-environment/.gitignore create mode 100644 examples/custom-environment/Cargo.toml create mode 100644 examples/custom-environment/README.md create mode 100644 examples/custom-environment/lib.rs diff --git a/examples/custom-environment/.gitignore b/examples/custom-environment/.gitignore new file mode 100644 index 00000000000..bf910de10af --- /dev/null +++ b/examples/custom-environment/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock \ No newline at end of file diff --git a/examples/custom-environment/Cargo.toml b/examples/custom-environment/Cargo.toml new file mode 100644 index 00000000000..dc50d9c259c --- /dev/null +++ b/examples/custom-environment/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "custom-environment" +version = "4.0.0-rc" +authors = ["Parity Technologies "] +edition = "2021" +publish = false + +[dependencies] +ink = { path = "../../crates/ink", default-features = false, features = ["call-runtime"] } + +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } +sp-io = { version = "12.0.0", default-features = false, features = ["disable_panic_handler", "disable_oom", "disable_allocator"] } +sp-runtime = { version = "12.0.0", default-features = false } + +[dev-dependencies] +ink_e2e = { path = "../../crates/e2e" } + +[lib] +name = "custom_environment" +path = "lib.rs" +crate-type = ["cdylib"] + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", + "sp-runtime/std", + "sp-io/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/examples/custom-environment/README.md b/examples/custom-environment/README.md new file mode 100644 index 00000000000..11dc9715081 --- /dev/null +++ b/examples/custom-environment/README.md @@ -0,0 +1,19 @@ +# `call-runtime` example + +## What is this example about? + +It demonstrates how to call a runtime dispatchable from an ink! contract. + +## Chain-side configuration + +To integrate this example into Substrate you need to adjust pallet contracts configuration in your runtime: + `pallet_contracts::Config`: + ```rust + impl pallet_contracts::Config for Runtime { + … + // `Everything` or anything that will allow for Balances::transfer extrinsic + type CallFilter = frame_support::traits::Everything; + type UnsafeUnstableInterface = ConstBool; + … + } + ``` diff --git a/examples/custom-environment/lib.rs b/examples/custom-environment/lib.rs new file mode 100644 index 00000000000..6f9ce65d431 --- /dev/null +++ b/examples/custom-environment/lib.rs @@ -0,0 +1,114 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use ink::env::Environment; + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub enum EnvironmentWithManyTopics {} + +impl Environment for EnvironmentWithManyTopics { + const MAX_EVENT_TOPICS: usize = + ::MAX_EVENT_TOPICS + 1; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Hash = ::Hash; + type BlockNumber = ::BlockNumber; + type Timestamp = ::Timestamp; + + type ChainExtension = ::ChainExtension; +} + +#[ink::contract(env = crate::EnvironmentWithManyTopics)] +mod runtime_call { + #[ink(storage)] + #[derive(Default)] + pub struct Topicer; + + #[ink(event)] + #[derive(Default)] + pub struct TopicedEvent { + #[ink(topic)] + first_topic: Balance, + #[ink(topic)] + second_topic: Balance, + #[ink(topic)] + third_topic: Balance, + #[ink(topic)] + fourth_topic: Balance, + #[ink(topic)] + fifth_topic: Balance, + } + + impl Topicer { + #[ink(constructor)] + pub fn new() -> Self { + Self {} + } + + #[ink(message)] + pub fn trigger(&mut self) { + self.env().emit_event(TopicedEvent::default()); + } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[ink::test] + fn emits_event_with_many_topics() { + let mut contract = Topicer::new(); + contract.trigger(); + + let emitted_events = ink::env::test::recorded_events().collect::>(); + assert_eq!(1, emitted_events.len()); + + let _ = <::Type as scale::Decode>::decode(&mut &emitted_events[0].data[..]) + .expect("encountered invalid contract event data buffer"); + } + } + + #[cfg(all(test, feature = "e2e-tests"))] + mod e2e_tests { + use super::*; + use crate::EnvironmentWithManyTopics; + use ink_e2e::MessageBuilder; + + type E2EResult = Result>; + + #[ink_e2e::test(environment = crate::EnvironmentWithManyTopics)] + async fn it_works(mut client: Client) -> E2EResult<()> { + // given + let constructor = TopicerRef::new(); + let contract_acc_id = client + .instantiate( + "custom-environment", + &ink_e2e::alice(), + constructor, + 0, + None, + ) + .await + .expect("instantiate failed") + .account_id; + + // when + let message = + MessageBuilder::::from_account_id( + contract_acc_id, + ) + .call(|caller| caller.trigger()); + + let call_res = client + .call(&ink_e2e::alice(), message, 0, None) + .await + .expect("call failed"); + + // then + call_res.contains_event("Contracts", "ContractEmitted"); + + Ok(()) + } + } +} From 0821d69ad74bed552b3b6f77d56373f5301e390e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 8 Feb 2023 11:03:33 +0100 Subject: [PATCH 04/18] Ignore test --- examples/custom-environment/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/examples/custom-environment/lib.rs b/examples/custom-environment/lib.rs index 6f9ce65d431..3cb6c0e629a 100644 --- a/examples/custom-environment/lib.rs +++ b/examples/custom-environment/lib.rs @@ -78,6 +78,17 @@ mod runtime_call { type E2EResult = Result>; #[ink_e2e::test(environment = crate::EnvironmentWithManyTopics)] + #[ignore = "Requires that the pallet contract is configured with a schedule allowing for \ + more event topics. For example:\ + ```rust + pub Schedule: pallet_contracts::Schedule = pallet_contracts::Schedule:: { + limits: pallet_contracts::Limits { + event_topics: 6, + ..Default::default() + }, + ..Default::default() + }; + ```"] async fn it_works(mut client: Client) -> E2EResult<()> { // given let constructor = TopicerRef::new(); From 1bd6c99151a9c74b2d93a9e07e095e2fee789606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 8 Feb 2023 11:07:45 +0100 Subject: [PATCH 05/18] CHANGELOG.md, README.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b033fb26c0..b7ab1e21ec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +### Added +- Support custom environment in e2e tests - [#1645](https://github.com/paritytech/ink/pull/1645) + ### Changed - E2E: spawn a separate contracts node instance per test ‒ [#1642](https://github.com/paritytech/ink/pull/1642) From f968ffec9d9af704bcdcaa969ff38fe2993bd890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 8 Feb 2023 11:11:12 +0100 Subject: [PATCH 06/18] Remove feature leftover --- examples/custom-environment/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/custom-environment/Cargo.toml b/examples/custom-environment/Cargo.toml index dc50d9c259c..9483c900ef4 100644 --- a/examples/custom-environment/Cargo.toml +++ b/examples/custom-environment/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" publish = false [dependencies] -ink = { path = "../../crates/ink", default-features = false, features = ["call-runtime"] } +ink = { path = "../../crates/ink", default-features = false } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } From e289ded84dafbefeadb70a2be4740bcaed1765a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 8 Feb 2023 14:32:42 +0100 Subject: [PATCH 07/18] Review comments --- CHANGELOG.md | 2 +- crates/e2e/macro/src/config.rs | 16 ++++++++-------- examples/custom-environment/README.md | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7ab1e21ec4..4d3f4e86e13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added -- Support custom environment in e2e tests - [#1645](https://github.com/paritytech/ink/pull/1645) +- Support custom environment in E2E tests - [#1645](https://github.com/paritytech/ink/pull/1645) ### Changed - E2E: spawn a separate contracts node instance per test ‒ [#1642](https://github.com/paritytech/ink/pull/1642) diff --git a/crates/e2e/macro/src/config.rs b/crates/e2e/macro/src/config.rs index cb4d6a4ebba..51001f5ae15 100644 --- a/crates/e2e/macro/src/config.rs +++ b/crates/e2e/macro/src/config.rs @@ -29,7 +29,7 @@ pub struct E2EConfig { whitelisted_attributes: WhitelistedAttributes, /// Additional contracts that have to be built before executing the test. additional_contracts: Vec, - /// Custom environment for the contracts, if specified. + /// Custom environment for the contracts, if specified. Otherwise `DefaultEnvironment` is used. environment: Option, } @@ -50,7 +50,7 @@ impl TryFrom for E2EConfig { ast, arg, "additional_contracts", - "e2e test", + "E2E test", )) } if let ast::PathOrLit::Lit(syn::Lit::Str(lit_str)) = &arg.value { @@ -58,19 +58,19 @@ impl TryFrom for E2EConfig { } else { return Err(format_err_spanned!( arg, - "expected a string literal for `additional_contracts` ink! e2e test configuration argument", + "expected a string literal for `additional_contracts` ink! E2E test configuration argument", )) } } else if arg.name.is_ident("environment") { if let Some((_, ast)) = environment { - return Err(duplicate_config_err(ast, arg, "environment", "e2e test")) + return Err(duplicate_config_err(ast, arg, "environment", "E2E test")) } if let ast::PathOrLit::Path(path) = &arg.value { environment = Some((path.clone(), arg)) } else { return Err(format_err_spanned!( arg, - "expected a path for `environment` ink! e2e test configuration argument", + "expected a path for `environment` ink! E2E test configuration argument", )) } } else { @@ -144,7 +144,7 @@ mod tests { additional_contracts = "adder/Cargo.toml", }, Err( - "encountered duplicate ink! e2e test `additional_contracts` configuration argument", + "encountered duplicate ink! E2E test `additional_contracts` configuration argument", ), ); } @@ -157,7 +157,7 @@ mod tests { environment = crate::CustomEnvironment, }, Err( - "encountered duplicate ink! e2e test `environment` configuration argument", + "encountered duplicate ink! E2E test `environment` configuration argument", ), ); } @@ -168,7 +168,7 @@ mod tests { syn::parse_quote! { environment = "crate::CustomEnvironment", }, - Err("expected a path for `environment` ink! e2e test configuration argument"), + Err("expected a path for `environment` ink! E2E test configuration argument"), ); } diff --git a/examples/custom-environment/README.md b/examples/custom-environment/README.md index 11dc9715081..af06c542e91 100644 --- a/examples/custom-environment/README.md +++ b/examples/custom-environment/README.md @@ -1,4 +1,4 @@ -# `call-runtime` example +# `custom-environment` example ## What is this example about? From 6a63605c68f50e042d1a448de28b5907dd0bb1a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Thu, 9 Feb 2023 09:19:10 +0100 Subject: [PATCH 08/18] Remove dependencies --- examples/custom-environment/Cargo.toml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/examples/custom-environment/Cargo.toml b/examples/custom-environment/Cargo.toml index 9483c900ef4..b6e72eb9803 100644 --- a/examples/custom-environment/Cargo.toml +++ b/examples/custom-environment/Cargo.toml @@ -10,16 +10,12 @@ ink = { path = "../../crates/ink", default-features = false } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } -sp-io = { version = "12.0.0", default-features = false, features = ["disable_panic_handler", "disable_oom", "disable_allocator"] } -sp-runtime = { version = "12.0.0", default-features = false } [dev-dependencies] ink_e2e = { path = "../../crates/e2e" } [lib] -name = "custom_environment" path = "lib.rs" -crate-type = ["cdylib"] [features] default = ["std"] @@ -27,8 +23,6 @@ std = [ "ink/std", "scale/std", "scale-info/std", - "sp-runtime/std", - "sp-io/std", ] ink-as-dependency = [] e2e-tests = [] From a7499b06cae06ccb4087ac87ba559e94645a010c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Thu, 9 Feb 2023 09:20:57 +0100 Subject: [PATCH 09/18] syn::Path --- crates/e2e/macro/src/config.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/e2e/macro/src/config.rs b/crates/e2e/macro/src/config.rs index 51001f5ae15..50b6b69ccfa 100644 --- a/crates/e2e/macro/src/config.rs +++ b/crates/e2e/macro/src/config.rs @@ -20,7 +20,6 @@ use ink_ir::{ WhitelistedAttributes, }, }; -use syn::Path; /// The End-to-End test configuration. #[derive(Debug, Default, PartialEq, Eq)] @@ -30,7 +29,7 @@ pub struct E2EConfig { /// Additional contracts that have to be built before executing the test. additional_contracts: Vec, /// Custom environment for the contracts, if specified. Otherwise `DefaultEnvironment` is used. - environment: Option, + environment: Option, } impl TryFrom for E2EConfig { @@ -39,7 +38,7 @@ impl TryFrom for E2EConfig { fn try_from(args: ast::AttributeArgs) -> Result { let mut whitelisted_attributes = WhitelistedAttributes::default(); let mut additional_contracts: Option<(syn::LitStr, ast::MetaNameValue)> = None; - let mut environment: Option<(Path, ast::MetaNameValue)> = None; + let mut environment: Option<(syn::Path, ast::MetaNameValue)> = None; for arg in args.into_iter() { if arg.name.is_ident("keep_attr") { @@ -101,7 +100,7 @@ impl E2EConfig { } /// Custom environment for the contracts, if specified. - pub fn environment(&self) -> Option { + pub fn environment(&self) -> Option { self.environment.clone() } } From 557b4ea003007311d4a9d8aaf6e26779f9cec444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Thu, 9 Feb 2023 09:21:36 +0100 Subject: [PATCH 10/18] ::ink --- crates/e2e/macro/src/codegen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/e2e/macro/src/codegen.rs b/crates/e2e/macro/src/codegen.rs index a38aec97c75..5286739f0f6 100644 --- a/crates/e2e/macro/src/codegen.rs +++ b/crates/e2e/macro/src/codegen.rs @@ -76,7 +76,7 @@ impl InkE2ETest { .test .config .environment() - .unwrap_or_else(|| syn::parse_quote! { ink::env::DefaultEnvironment }); + .unwrap_or_else(|| syn::parse_quote! { ::ink::env::DefaultEnvironment }); let mut additional_contracts: Vec = self.test.config.additional_contracts(); From 6a914d5ecd1226f4b9b3f6126a2cb3d4146cac49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Thu, 9 Feb 2023 09:33:52 +0100 Subject: [PATCH 11/18] README.md --- examples/custom-environment/README.md | 31 +++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/examples/custom-environment/README.md b/examples/custom-environment/README.md index af06c542e91..8bd35b4614e 100644 --- a/examples/custom-environment/README.md +++ b/examples/custom-environment/README.md @@ -2,18 +2,27 @@ ## What is this example about? -It demonstrates how to call a runtime dispatchable from an ink! contract. +It demonstrates how to use custom environment, both in the contract and in the E2E tests. ## Chain-side configuration To integrate this example into Substrate you need to adjust pallet contracts configuration in your runtime: - `pallet_contracts::Config`: - ```rust - impl pallet_contracts::Config for Runtime { - … - // `Everything` or anything that will allow for Balances::transfer extrinsic - type CallFilter = frame_support::traits::Everything; - type UnsafeUnstableInterface = ConstBool; - … - } - ``` + +```rust +// In your node's runtime configuration file (runtime.rs) +parameter_types! { + pub Schedule: pallet_contracts::Schedule = pallet_contracts::Schedule:: { + limits: pallet_contracts::Limits { + event_topics: 6, + ..Default::default() + }, + ..Default::default() + }; +} + +impl pallet_contracts::Config for Runtime { + … + type Schedule = Schedule; + … +} + ``` From 192047f43116f5ff78a4ffa24efc42ae3fe92b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Thu, 9 Feb 2023 09:45:35 +0100 Subject: [PATCH 12/18] Example docs --- examples/custom-environment/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/custom-environment/lib.rs b/examples/custom-environment/lib.rs index 3cb6c0e629a..fa8c27f9146 100644 --- a/examples/custom-environment/lib.rs +++ b/examples/custom-environment/lib.rs @@ -2,11 +2,14 @@ use ink::env::Environment; +/// Our custom environment diverges from the `DefaultEnvironment` in the event topics limit. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum EnvironmentWithManyTopics {} impl Environment for EnvironmentWithManyTopics { + // We allow for 5 topics in the event, therefore the contract pallet's schedule must allow for + // 6 of them (to allow the implicit topic for the event signature). const MAX_EVENT_TOPICS: usize = ::MAX_EVENT_TOPICS + 1; @@ -21,10 +24,13 @@ impl Environment for EnvironmentWithManyTopics { #[ink::contract(env = crate::EnvironmentWithManyTopics)] mod runtime_call { + /// Trivial contract with a single message that emits an event with many topics. #[ink(storage)] #[derive(Default)] pub struct Topicer; + /// An event that would be forbidden in the default environment, but is completely valid in + /// our custom one. #[ink(event)] #[derive(Default)] pub struct TopicedEvent { @@ -46,6 +52,7 @@ mod runtime_call { Self {} } + /// Emit an event with many topics. #[ink(message)] pub fn trigger(&mut self) { self.env().emit_event(TopicedEvent::default()); From d22506be6d64b498038a8ff586f055c73631c970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Thu, 9 Feb 2023 23:47:12 +0100 Subject: [PATCH 13/18] Review --- crates/e2e/macro/src/config.rs | 18 +++++++++- examples/custom-environment/lib.rs | 58 ++++++++++++++++-------------- 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/crates/e2e/macro/src/config.rs b/crates/e2e/macro/src/config.rs index 50b6b69ccfa..1745436d4cb 100644 --- a/crates/e2e/macro/src/config.rs +++ b/crates/e2e/macro/src/config.rs @@ -28,7 +28,10 @@ pub struct E2EConfig { whitelisted_attributes: WhitelistedAttributes, /// Additional contracts that have to be built before executing the test. additional_contracts: Vec, - /// Custom environment for the contracts, if specified. Otherwise `DefaultEnvironment` is used. + /// Custom [environment](https://docs.rs/ink_env/4.0.0-rc/ink_env/trait.Environment.html) for + /// the contracts, if specified. Otherwise + /// [`DefaultEnvironment`](https://docs.rs/ink_env/4.0.0-rc/ink_env/enum.DefaultEnvironment.html) + /// is used. environment: Option, } @@ -171,6 +174,19 @@ mod tests { ); } + #[test] + fn specifying_environment_works() { + assert_try_from( + syn::parse_quote! { + environment = crate::CustomEnvironment, + }, + Ok(E2EConfig { + environment: Some(syn::parse_quote! { crate::CustomEnvironment }), + ..Default::default() + }), + ); + } + #[test] fn full_config_works() { assert_try_from( diff --git a/examples/custom-environment/lib.rs b/examples/custom-environment/lib.rs index fa8c27f9146..bb8cf9371fb 100644 --- a/examples/custom-environment/lib.rs +++ b/examples/custom-environment/lib.rs @@ -1,6 +1,9 @@ #![cfg_attr(not(feature = "std"), no_std)] -use ink::env::Environment; +use ink::env::{ + DefaultEnvironment, + Environment, +}; /// Our custom environment diverges from the `DefaultEnvironment` in the event topics limit. #[derive(Debug, Clone, PartialEq, Eq)] @@ -11,15 +14,15 @@ impl Environment for EnvironmentWithManyTopics { // We allow for 5 topics in the event, therefore the contract pallet's schedule must allow for // 6 of them (to allow the implicit topic for the event signature). const MAX_EVENT_TOPICS: usize = - ::MAX_EVENT_TOPICS + 1; + ::MAX_EVENT_TOPICS + 1; - type AccountId = ::AccountId; - type Balance = ::Balance; - type Hash = ::Hash; - type BlockNumber = ::BlockNumber; - type Timestamp = ::Timestamp; + type AccountId = ::AccountId; + type Balance = ::Balance; + type Hash = ::Hash; + type BlockNumber = ::BlockNumber; + type Timestamp = ::Timestamp; - type ChainExtension = ::ChainExtension; + type ChainExtension = ::ChainExtension; } #[ink::contract(env = crate::EnvironmentWithManyTopics)] @@ -27,13 +30,13 @@ mod runtime_call { /// Trivial contract with a single message that emits an event with many topics. #[ink(storage)] #[derive(Default)] - pub struct Topicer; + pub struct Topics; /// An event that would be forbidden in the default environment, but is completely valid in /// our custom one. #[ink(event)] #[derive(Default)] - pub struct TopicedEvent { + pub struct EventWithTopics { #[ink(topic)] first_topic: Balance, #[ink(topic)] @@ -46,16 +49,16 @@ mod runtime_call { fifth_topic: Balance, } - impl Topicer { + impl Topics { #[ink(constructor)] pub fn new() -> Self { - Self {} + Default::default() } /// Emit an event with many topics. #[ink(message)] pub fn trigger(&mut self) { - self.env().emit_event(TopicedEvent::default()); + self.env().emit_event(EventWithTopics::default()); } } @@ -65,13 +68,13 @@ mod runtime_call { #[ink::test] fn emits_event_with_many_topics() { - let mut contract = Topicer::new(); + let mut contract = Topics::new(); contract.trigger(); let emitted_events = ink::env::test::recorded_events().collect::>(); - assert_eq!(1, emitted_events.len()); + assert_eq!(emitted_events.len(), 1); - let _ = <::Type as scale::Decode>::decode(&mut &emitted_events[0].data[..]) + let _ = <::Type as scale::Decode>::decode(&mut &emitted_events[0].data[..]) .expect("encountered invalid contract event data buffer"); } } @@ -79,7 +82,7 @@ mod runtime_call { #[cfg(all(test, feature = "e2e-tests"))] mod e2e_tests { use super::*; - use crate::EnvironmentWithManyTopics; + use ink_e2e::MessageBuilder; type E2EResult = Result>; @@ -89,16 +92,19 @@ mod runtime_call { more event topics. For example:\ ```rust pub Schedule: pallet_contracts::Schedule = pallet_contracts::Schedule:: { - limits: pallet_contracts::Limits { - event_topics: 6, - ..Default::default() - }, - ..Default::default() - }; + limits: pallet_contracts::Limits { + event_topics: 6, + ..Default::default() + }, + ..Default::default() + }; + }; ```"] - async fn it_works(mut client: Client) -> E2EResult<()> { + async fn calling_custom_environment_works( + mut client: Client, + ) -> E2EResult<()> { // given - let constructor = TopicerRef::new(); + let constructor = TopicsRef::new(); let contract_acc_id = client .instantiate( "custom-environment", @@ -113,7 +119,7 @@ mod runtime_call { // when let message = - MessageBuilder::::from_account_id( + MessageBuilder::::from_account_id( contract_acc_id, ) .call(|caller| caller.trigger()); From 5abff86e30c7eeeae127c212c4553be6776bf3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Thu, 9 Feb 2023 23:58:01 +0100 Subject: [PATCH 14/18] Add feature instead of ignoring --- examples/custom-environment/Cargo.toml | 3 +++ examples/custom-environment/lib.rs | 14 +------------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/examples/custom-environment/Cargo.toml b/examples/custom-environment/Cargo.toml index b6e72eb9803..98810d18aea 100644 --- a/examples/custom-environment/Cargo.toml +++ b/examples/custom-environment/Cargo.toml @@ -26,3 +26,6 @@ std = [ ] ink-as-dependency = [] e2e-tests = [] + +# Assumes that the node used in E2E testing allows for at least 6 event topics. +permissive-node = [] diff --git a/examples/custom-environment/lib.rs b/examples/custom-environment/lib.rs index bb8cf9371fb..7c2e5f177cd 100644 --- a/examples/custom-environment/lib.rs +++ b/examples/custom-environment/lib.rs @@ -79,7 +79,7 @@ mod runtime_call { } } - #[cfg(all(test, feature = "e2e-tests"))] + #[cfg(all(test, feature = "e2e-tests", feature = "permissive-node"))] mod e2e_tests { use super::*; @@ -88,18 +88,6 @@ mod runtime_call { type E2EResult = Result>; #[ink_e2e::test(environment = crate::EnvironmentWithManyTopics)] - #[ignore = "Requires that the pallet contract is configured with a schedule allowing for \ - more event topics. For example:\ - ```rust - pub Schedule: pallet_contracts::Schedule = pallet_contracts::Schedule:: { - limits: pallet_contracts::Limits { - event_topics: 6, - ..Default::default() - }, - ..Default::default() - }; - }; - ```"] async fn calling_custom_environment_works( mut client: Client, ) -> E2EResult<()> { From 9e9d0deb186564963e466b185f227daf81f61de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 13 Feb 2023 13:55:44 +0100 Subject: [PATCH 15/18] Doc rewording. Event type matching --- crates/e2e/macro/src/config.rs | 8 +++++--- examples/custom-environment/lib.rs | 12 ++++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/crates/e2e/macro/src/config.rs b/crates/e2e/macro/src/config.rs index 1745436d4cb..af1b918ddc4 100644 --- a/crates/e2e/macro/src/config.rs +++ b/crates/e2e/macro/src/config.rs @@ -28,10 +28,12 @@ pub struct E2EConfig { whitelisted_attributes: WhitelistedAttributes, /// Additional contracts that have to be built before executing the test. additional_contracts: Vec, - /// Custom [environment](https://docs.rs/ink_env/4.0.0-rc/ink_env/trait.Environment.html) for - /// the contracts, if specified. Otherwise + /// The [`Environment`](https://docs.rs/ink_env/4.0.0-rc/ink_env/trait.Environment.html) to use + /// during test execution. + /// + /// If no `Environment` is specified, the /// [`DefaultEnvironment`](https://docs.rs/ink_env/4.0.0-rc/ink_env/enum.DefaultEnvironment.html) - /// is used. + /// will be used. environment: Option, } diff --git a/examples/custom-environment/lib.rs b/examples/custom-environment/lib.rs index 7c2e5f177cd..f24570268c3 100644 --- a/examples/custom-environment/lib.rs +++ b/examples/custom-environment/lib.rs @@ -66,6 +66,8 @@ mod runtime_call { mod tests { use super::*; + type Event = ::Type; + #[ink::test] fn emits_event_with_many_topics() { let mut contract = Topics::new(); @@ -74,8 +76,14 @@ mod runtime_call { let emitted_events = ink::env::test::recorded_events().collect::>(); assert_eq!(emitted_events.len(), 1); - let _ = <::Type as scale::Decode>::decode(&mut &emitted_events[0].data[..]) - .expect("encountered invalid contract event data buffer"); + let emitted_event = + ::decode(&mut &emitted_events[0].data[..]) + .expect("encountered invalid contract event data buffer"); + + assert!(matches!( + emitted_event, + Event::EventWithTopics(EventWithTopics { .. }) + )); } } From 21f065199449f472c3b81d63e505af0e19a91721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 13 Feb 2023 14:16:39 +0100 Subject: [PATCH 16/18] Missing testcase --- examples/custom-environment/lib.rs | 39 +++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/examples/custom-environment/lib.rs b/examples/custom-environment/lib.rs index f24570268c3..139dc08d75d 100644 --- a/examples/custom-environment/lib.rs +++ b/examples/custom-environment/lib.rs @@ -87,7 +87,7 @@ mod runtime_call { } } - #[cfg(all(test, feature = "e2e-tests", feature = "permissive-node"))] + #[cfg(all(test, feature = "e2e-tests"))] mod e2e_tests { use super::*; @@ -95,6 +95,7 @@ mod runtime_call { type E2EResult = Result>; + #[cfg(feature = "permissive-node")] #[ink_e2e::test(environment = crate::EnvironmentWithManyTopics)] async fn calling_custom_environment_works( mut client: Client, @@ -130,5 +131,41 @@ mod runtime_call { Ok(()) } + + #[cfg(not(feature = "permissive-node"))] + #[ink_e2e::test(environment = crate::EnvironmentWithManyTopics)] + async fn calling_custom_environment_fails_if_incompatible_with_node( + mut client: Client, + ) -> E2EResult<()> { + // given + let constructor = TopicsRef::new(); + let contract_acc_id = client + .instantiate( + "custom-environment", + &ink_e2e::alice(), + constructor, + 0, + None, + ) + .await + .expect("instantiate failed") + .account_id; + + let message = + MessageBuilder::::from_account_id( + contract_acc_id, + ) + .call(|caller| caller.trigger()); + + // when + let call_res = client + .call_dry_run(&ink_e2e::alice(), &message, 0, None) + .await; + + // then + assert!(call_res.is_err()); + + Ok(()) + } } } From 0953a0f2d7d8a17cae20c7fb5aabf38e359accab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 13 Feb 2023 14:40:29 +0100 Subject: [PATCH 17/18] Fix test --- .../ink/tests/ui/contract/fail/message-returns-non-codec.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ink/tests/ui/contract/fail/message-returns-non-codec.stderr b/crates/ink/tests/ui/contract/fail/message-returns-non-codec.stderr index 7e71ef2dea2..8464dbc60fd 100644 --- a/crates/ink/tests/ui/contract/fail/message-returns-non-codec.stderr +++ b/crates/ink/tests/ui/contract/fail/message-returns-non-codec.stderr @@ -46,7 +46,7 @@ error[E0599]: the method `try_invoke` exists for struct `ink::ink_env::call::Cal = note: the following trait bounds were not satisfied: `NonCodecType: parity_scale_codec::Decode` note: the following trait must be implemented - --> $CARGO/parity-scale-codec-3.3.0/src/codec.rs + --> $CARGO/parity-scale-codec-3.4.0/src/codec.rs | | pub trait Decode: Sized { | ^^^^^^^^^^^^^^^^^^^^^^^ From 392121748d8a2986ea2cd91c10f64eceab2f11ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 13 Feb 2023 14:47:23 +0100 Subject: [PATCH 18/18] Fix test --- .../ink/tests/ui/trait_def/fail/message_output_non_codec.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ink/tests/ui/trait_def/fail/message_output_non_codec.stderr b/crates/ink/tests/ui/trait_def/fail/message_output_non_codec.stderr index d3221e593be..15f1692e6a3 100644 --- a/crates/ink/tests/ui/trait_def/fail/message_output_non_codec.stderr +++ b/crates/ink/tests/ui/trait_def/fail/message_output_non_codec.stderr @@ -33,7 +33,7 @@ error[E0599]: the method `try_invoke` exists for struct `CallBuilder $CARGO/parity-scale-codec-3.3.0/src/codec.rs + --> $CARGO/parity-scale-codec-3.4.0/src/codec.rs | | pub trait Decode: Sized { | ^^^^^^^^^^^^^^^^^^^^^^^