diff --git a/.config/cargo_spellcheck.dic b/.config/cargo_spellcheck.dic index 416855e72bc..5d6a9cfb7be 100644 --- a/.config/cargo_spellcheck.dic +++ b/.config/cargo_spellcheck.dic @@ -68,6 +68,7 @@ scalability scalable scale_derive sr25519 +SS58 stdin stdout subber @@ -127,4 +128,4 @@ GB BufferTooSmall KeyNotFound ink_env -^ \ No newline at end of file +^ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e016d71f6d..8660aeb36f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ on: - 'FILE_HEADER' env: - IMAGE: paritytech/ci-unified:bullseye-1.74.0 + IMAGE: paritytech/ci-unified:bullseye-1.75.0 CARGO_TARGET_DIR: /ci-cache/${{ github.repository }}/targets/${{ github.ref_name }}/${{ github.job }} CARGO_INCREMENTAL: 0 PURELY_STD_CRATES: ink/codegen metadata engine e2e e2e/macro ink/ir @@ -491,6 +491,24 @@ jobs: cargo clean --manifest-path integration-tests/static-buffer/Cargo.toml INK_STATIC_BUFFER_SIZE=30 cargo test --verbose --manifest-path integration-tests/static-buffer/Cargo.toml --all-features + - name: Run E2E test with on-chain contract + env: + # Fix linking of `linkme`: https://github.com/dtolnay/linkme/issues/49 + RUSTFLAGS: -Clink-arg=-z -Clink-arg=nostart-stop-gc + run: | + # run flipper E2E test with on-chain contract + substrate-contracts-node -lruntime::contracts=debug 2>&1 & + cargo contract build --release --manifest-path integration-tests/flipper/Cargo.toml + export CONTRACT_ADDR_HEX=$(cargo contract instantiate \ + --manifest-path integration-tests/flipper/Cargo.toml \ + --suri //Alice --args true -x -y --output-json | \ + jq -r .contract | xargs subkey inspect | grep -o "0x.*" | head -n1) + CONTRACTS_NODE_URL=ws://127.0.0.1:9944 cargo test \ + --features e2e-tests \ + --manifest-path integration-tests/flipper/Cargo.toml \ + e2e_test_deployed_contract \ + -- --ignored --nocapture + examples-contract-build: runs-on: ubuntu-latest needs: [set-image, build] diff --git a/.github/workflows/measurements.yml b/.github/workflows/measurements.yml index 1c8d810ca65..5cea238d4c9 100644 --- a/.github/workflows/measurements.yml +++ b/.github/workflows/measurements.yml @@ -14,7 +14,7 @@ jobs: run: shell: bash container: - image: paritytech/ci-unified:bullseye-1.74.0 + image: paritytech/ci-unified:bullseye-1.75.0 steps: - name: Checkout uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 348b70f7f46..231ca36d2b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Custom signature topic in Events - [#2031](https://github.com/paritytech/ink/pull/2031) - Linter: `non_fallible_api` lint - [#2004](https://github.com/paritytech/ink/pull/2004) - Linter: Publish the linting crates on crates.io - [#2060](https://github.com/paritytech/ink/pull/2060) +- [E2E] Added `create_call_builder` for testing existing contracts - [#2075](https://github.com/paritytech/ink/pull/2075) ### Fixed - Fix the `StorageVec` type by excluding the `len_cached` field from its type info - [#2052](https://github.com/paritytech/ink/pull/2052) diff --git a/crates/e2e/src/lib.rs b/crates/e2e/src/lib.rs index ad8bc898950..a44e094a1f1 100644 --- a/crates/e2e/src/lib.rs +++ b/crates/e2e/src/lib.rs @@ -82,6 +82,12 @@ pub use { drink_client::Client as DrinkClient, }; +use ink::codegen::ContractCallBuilder; +use ink_env::{ + call::FromAccountId, + ContractEnv, + Environment, +}; use pallet_contracts_primitives::{ ContractExecResult, ContractInstantiateResult, @@ -128,6 +134,21 @@ pub fn account_id(account: AccountKeyring) -> ink_primitives::AccountId { .expect("account keyring has a valid account id") } +/// Creates a call builder builder for `Contract`, based on an account id. +pub fn create_call_builder( + acc_id: <::Env as Environment>::AccountId, +) -> ::Type +where + ::Env: Environment, + Contract: ContractCallBuilder, + Contract: ContractEnv, + Contract::Type: FromAccountId<::Env>, +{ + <::Type as FromAccountId< + ::Env, + >>::from_account_id(acc_id) +} + /// Builds a contract and imports its scaffolded structure as a module. #[macro_export] macro_rules! build { diff --git a/crates/ink/tests/ui/contract/fail/impl-block-for-non-storage-01.stderr b/crates/ink/tests/ui/contract/fail/impl-block-for-non-storage-01.stderr index b5dba1afb28..afe52ca0748 100644 --- a/crates/ink/tests/ui/contract/fail/impl-block-for-non-storage-01.stderr +++ b/crates/ink/tests/ui/contract/fail/impl-block-for-non-storage-01.stderr @@ -25,6 +25,12 @@ error[E0599]: no function or associated item named `constructor_2` found for str | | |function or associated item not found in `Contract` | |_______________|help: there is an associated function with a similar name: `constructor_1` | + | +note: if you're trying to build a new `Contract`, consider using `contract::_::::constructor_1` which returns `Contract` + --> tests/ui/contract/fail/impl-block-for-non-storage-01.rs:8:9 + | +8 | pub fn constructor_1() -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0599]: no function or associated item named `message_2` found for struct `Contract` in the current scope --> tests/ui/contract/fail/impl-block-for-non-storage-01.rs:25:16 @@ -44,3 +50,9 @@ error[E0599]: no function or associated item named `message_2` found for struct | | |function or associated item not found in `Contract` | |_______________|help: there is a method with a similar name: `message_1` | + | +note: if you're trying to build a new `Contract`, consider using `contract::_::::constructor_1` which returns `Contract` + --> tests/ui/contract/fail/impl-block-for-non-storage-01.rs:8:9 + | +8 | pub fn constructor_1() -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/ink/tests/ui/contract/fail/impl-block-using-static-env-no-marker.stderr b/crates/ink/tests/ui/contract/fail/impl-block-using-static-env-no-marker.stderr index 2be15ec8121..82ca51e2862 100644 --- a/crates/ink/tests/ui/contract/fail/impl-block-using-static-env-no-marker.stderr +++ b/crates/ink/tests/ui/contract/fail/impl-block-using-static-env-no-marker.stderr @@ -7,6 +7,16 @@ error[E0599]: no function or associated item named `env` found for struct `Contr 20 | let _ = Self::env().caller(); | ^^^ function or associated item not found in `Contract` | +note: if you're trying to build a new `Contract` consider using one of the following associated functions: + contract::_::::constructor + Contract::constructor_impl + --> tests/ui/contract/fail/impl-block-using-static-env-no-marker.rs:8:9 + | +8 | pub fn constructor() -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +19 | fn constructor_impl() -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: | diff --git a/crates/ink/tests/ui/storage_item/fail/collections_only_packed_1.stderr b/crates/ink/tests/ui/storage_item/fail/collections_only_packed_1.stderr index 00902e1fed5..75dbb12921c 100644 --- a/crates/ink/tests/ui/storage_item/fail/collections_only_packed_1.stderr +++ b/crates/ink/tests/ui/storage_item/fail/collections_only_packed_1.stderr @@ -114,3 +114,48 @@ note: required by a bound in `Result` | pub enum Result { | ^ required by this bound in `Result` = note: this error originates in the derive macro `::ink::storage::traits::Storable` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Vec: ink::parity_scale_codec::Decode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ the trait `ink::parity_scale_codec::Decode` is not implemented for `Vec` + | + = help: the trait `ink::parity_scale_codec::Decode` is implemented for `Vec` + = note: required for `Vec` to implement `Packed` + = note: required for `Vec` to implement `StorableHint<()>` + = note: required for `Vec` to implement `AutoStorableHint>` +note: required because it appears within the type `Contract` + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ +note: required by a bound in `ink_storage::ink_storage_traits::StorableHint::Type` + --> $WORKSPACE/crates/storage/traits/src/storage.rs + | + | type Type: Storable; + | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `StorableHint::Type` + +error[E0277]: the trait bound `[NonPacked]: Encode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ the trait `Encode` is not implemented for `[NonPacked]` + | + = help: the following other types implement trait `Encode`: + [T; N] + [T] + = note: required for `Vec` to implement `Encode` + = note: required for `Vec` to implement `Packed` + = note: required for `Vec` to implement `StorableHint<()>` + = note: required for `Vec` to implement `AutoStorableHint>` +note: required because it appears within the type `Contract` + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ +note: required by a bound in `ink_storage::ink_storage_traits::StorableHint::Type` + --> $WORKSPACE/crates/storage/traits/src/storage.rs + | + | type Type: Storable; + | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `StorableHint::Type` diff --git a/crates/ink/tests/ui/storage_item/fail/collections_only_packed_2.stderr b/crates/ink/tests/ui/storage_item/fail/collections_only_packed_2.stderr index b44e9672c74..33c98c16deb 100644 --- a/crates/ink/tests/ui/storage_item/fail/collections_only_packed_2.stderr +++ b/crates/ink/tests/ui/storage_item/fail/collections_only_packed_2.stderr @@ -105,3 +105,45 @@ note: required by a bound in `Result` | pub enum Result { | ^ required by this bound in `Result` = note: this error originates in the derive macro `::ink::storage::traits::Storable` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `BTreeMap: ink::parity_scale_codec::Decode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ the trait `ink::parity_scale_codec::Decode` is not implemented for `BTreeMap` + | + = help: the trait `ink::parity_scale_codec::Decode` is implemented for `BTreeMap` + = note: required for `BTreeMap` to implement `Packed` + = note: required for `BTreeMap` to implement `StorableHint<()>` + = note: required for `BTreeMap` to implement `AutoStorableHint>` +note: required because it appears within the type `Contract` + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ +note: required by a bound in `ink_storage::ink_storage_traits::StorableHint::Type` + --> $WORKSPACE/crates/storage/traits/src/storage.rs + | + | type Type: Storable; + | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `StorableHint::Type` + +error[E0277]: the trait bound `BTreeMap: Encode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ the trait `Encode` is not implemented for `BTreeMap` + | + = help: the trait `Encode` is implemented for `BTreeMap` + = note: required for `BTreeMap` to implement `Packed` + = note: required for `BTreeMap` to implement `StorableHint<()>` + = note: required for `BTreeMap` to implement `AutoStorableHint>` +note: required because it appears within the type `Contract` + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ +note: required by a bound in `ink_storage::ink_storage_traits::StorableHint::Type` + --> $WORKSPACE/crates/storage/traits/src/storage.rs + | + | type Type: Storable; + | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `StorableHint::Type` diff --git a/integration-tests/flipper/Cargo.toml b/integration-tests/flipper/Cargo.toml index 3227a40778b..9547f91c7ef 100644 --- a/integration-tests/flipper/Cargo.toml +++ b/integration-tests/flipper/Cargo.toml @@ -10,6 +10,7 @@ ink = { path = "../../crates/ink", default-features = false } [dev-dependencies] ink_e2e = { path = "../../crates/e2e" } +hex = { version = "0.4.3" } [lib] path = "lib.rs" diff --git a/integration-tests/flipper/lib.rs b/integration-tests/flipper/lib.rs index ccc6aa2cff7..f799e303a47 100644 --- a/integration-tests/flipper/lib.rs +++ b/integration-tests/flipper/lib.rs @@ -110,5 +110,55 @@ pub mod flipper { Ok(()) } + + /// This test illustrates how to test an existing on-chain contract. + /// + /// You can utilize this to e.g. create a snapshot of a production chain + /// and run the E2E tests against a deployed contract there. + /// This process is explained [here](https://use.ink/5.x/basics/contract-testing/chain-snapshot). + /// + /// Before executing the test: + /// * Make sure you have a node running in the background, + /// * Supply the environment variable `CONTRACT_HEX` that points to a deployed + /// flipper contract. You can take the SS58 address which `cargo contract + /// instantiate` gives you and convert it to hex using `subkey inspect + /// `. + /// + /// The test is then run like this: + /// + /// ``` + /// # The env variable needs to be set, otherwise `ink_e2e` will spawn a new + /// # node process for each test. + /// $ export CONTRACTS_NODE_URL=ws://127.0.0.1:9944 + /// + /// $ export CONTRACT_HEX=0x2c75f0aa09dbfbfd49e6286a0f2edd3b4913f04a58b13391c79e96782f5713e3 + /// $ cargo test --features e2e-tests e2e_test_deployed_contract -- --ignored + /// ``` + /// + /// # Developer Note + /// + /// The test is marked as ignored, as it has the above pre-conditions to succeed. + #[ink_e2e::test] + #[ignore] + async fn e2e_test_deployed_contract( + mut client: Client, + ) -> E2EResult<()> { + // given + let addr = std::env::var("CONTRACT_ADDR_HEX") + .unwrap() + .replace("0x", ""); + let acc_id = hex::decode(addr).unwrap(); + let acc_id = AccountId::try_from(&acc_id[..]).unwrap(); + + // when + // Invoke `Flipper::get()` from Bob's account + let call_builder = ink_e2e::create_call_builder::(acc_id); + let get = call_builder.get(); + let get_res = client.call(&ink_e2e::bob(), &get).dry_run().await?; + + // then + assert!(matches!(get_res.return_value(), true)); + Ok(()) + } } }