Skip to content

Commit

Permalink
[E2E] Add create_call_builder for testing on-chain contracts (#2075)
Browse files Browse the repository at this point in the history
* Reexport `ContractCallBuilder` from `ink_e2e`

* Add `create_call_builder(AccountId)`

* Revert "Reexport `ContractCallBuilder` from `ink_e2e`"

This reverts commit 6377e2d.

* Apply `cargo fmt`

* Add CI test

* Improve code

* Make spellchecker happy

* Fix CI

* Revert me: Run CI with isolated examples

* Fix CI

* Fix CI

* Make `rustfmt` happy

* Make `rustfmt` happy

* Revert me: Install `subkey`

* Revert me: Debug CI

* Fix CI

* Improve formatting

* Remove local `subkey` installation

* Revert "Revert me: Run CI with isolated examples"

This reverts commit 9900cc9.

* Use newer Docker image

* Improve variable name

* Update UI tests for new Rust version from Docker

* Improve structure

* Update UI tests for new Rust version from Docker

* Make `rustfmt` happy

* Update  image to 1.75
  • Loading branch information
Michael Müller authored Jan 29, 2024
1 parent 11db525 commit 1ea20a2
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 3 deletions.
3 changes: 2 additions & 1 deletion .config/cargo_spellcheck.dic
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ scalability
scalable
scale_derive
sr25519
SS58
stdin
stdout
subber
Expand Down Expand Up @@ -127,4 +128,4 @@ GB
BufferTooSmall
KeyNotFound
ink_env
^
^
20 changes: 19 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/measurements.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
21 changes: 21 additions & 0 deletions crates/e2e/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<Contract>(
acc_id: <<Contract as ContractEnv>::Env as Environment>::AccountId,
) -> <Contract as ContractCallBuilder>::Type
where
<Contract as ContractEnv>::Env: Environment,
Contract: ContractCallBuilder,
Contract: ContractEnv,
Contract::Type: FromAccountId<<Contract as ContractEnv>::Env>,
{
<<Contract as ContractCallBuilder>::Type as FromAccountId<
<Contract as ContractEnv>::Env,
>>::from_account_id(acc_id)
}

/// Builds a contract and imports its scaffolded structure as a module.
#[macro_export]
macro_rules! build {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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::_::<impl 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
Expand All @@ -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::_::<impl 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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Original file line number Diff line number Diff line change
Expand Up @@ -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::_::<impl 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:
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,48 @@ note: required by a bound in `Result`
| pub enum Result<T, E> {
| ^ 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<NonPacked>: 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<NonPacked>`
|
= help: the trait `ink::parity_scale_codec::Decode` is implemented for `Vec<T>`
= note: required for `Vec<NonPacked>` to implement `Packed`
= note: required for `Vec<NonPacked>` to implement `StorableHint<()>`
= note: required for `Vec<NonPacked>` to implement `AutoStorableHint<ManualKey<453539032>>`
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<NonPacked>` to implement `Encode`
= note: required for `Vec<NonPacked>` to implement `Packed`
= note: required for `Vec<NonPacked>` to implement `StorableHint<()>`
= note: required for `Vec<NonPacked>` to implement `AutoStorableHint<ManualKey<453539032>>`
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`
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,45 @@ note: required by a bound in `Result`
| pub enum Result<T, E> {
| ^ 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<u128, NonPacked>: 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<u128, NonPacked>`
|
= help: the trait `ink::parity_scale_codec::Decode` is implemented for `BTreeMap<K, V>`
= note: required for `BTreeMap<u128, NonPacked>` to implement `Packed`
= note: required for `BTreeMap<u128, NonPacked>` to implement `StorableHint<()>`
= note: required for `BTreeMap<u128, NonPacked>` to implement `AutoStorableHint<ManualKey<453539032>>`
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<u128, NonPacked>: 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<u128, NonPacked>`
|
= help: the trait `Encode` is implemented for `BTreeMap<K, V>`
= note: required for `BTreeMap<u128, NonPacked>` to implement `Packed`
= note: required for `BTreeMap<u128, NonPacked>` to implement `StorableHint<()>`
= note: required for `BTreeMap<u128, NonPacked>` to implement `AutoStorableHint<ManualKey<453539032>>`
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`
1 change: 1 addition & 0 deletions integration-tests/flipper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
50 changes: 50 additions & 0 deletions integration-tests/flipper/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
/// <SS58>`.
///
/// 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<Client: E2EBackend>(
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::<Flipper>(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(())
}
}
}

0 comments on commit 1ea20a2

Please sign in to comment.