From 9a8680246ea87bb8f3f9a9e2f252dffa574a2720 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 14 Sep 2020 13:44:11 +0100 Subject: [PATCH 01/16] Revert contracts put_code test to pure code (not using the macro) --- Cargo.toml | 3 ++ src/frame/contracts.rs | 106 +++++++++++++++++++++++++++-------------- src/runtimes.rs | 33 +++++++++++++ 3 files changed, 105 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 16b02b4932..210a5c760e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,9 @@ include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] [features] client = ["substrate-subxt-client"] +# enable this feature to run tests which require a local dev chain node +integration-tests = [] + [dependencies] log = "0.4.11" thiserror = "1.0.20" diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index 0a0bc1da34..c36311f80f 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -117,43 +117,75 @@ pub struct InstantiatedEvent { #[cfg(test)] mod tests { + use sp_keyring::AccountKeyring; + use super::*; + use crate::{ClientBuilder, PairSigner, ContractsTemplateRuntime}; + + fn contract_wasm() -> Vec { + const CONTRACT: &str = r#" + (module + (func (export "call")) + (func (export "deploy")) + ) + "#; + wabt::wat2wasm(CONTRACT).expect("invalid wabt") + } + + #[async_std::test] + #[cfg(feature = "integration-tests")] + async fn tx_put_code() { + env_logger::try_init().ok(); + + let signer = PairSigner::new(AccountKeyring::Alice.pair()); + let client = ClientBuilder::::new().build().await.unwrap(); + + let code = contract_wasm(); + let result = client.put_code_and_watch(&signer, &code).await.unwrap(); + let code_stored = result.code_stored().unwrap(); + + assert!( + code_stored.is_some(), + format!( + "Error calling put_code and receiving CodeStored Event: {:?}", + code_stored + ) + ); + } - subxt_test!({ - name: test_put_code_and_instantiate, - prelude: { - const CONTRACT: &str = r#" -(module - (func (export "call")) - (func (export "deploy")) -) -"#; - let wasm = wabt::wat2wasm(CONTRACT).expect("invalid wabt"); - let code_hash; - }, - step: { - call: PutCodeCall { - _runtime: PhantomData, - code: &wasm, - }, - event: CodeStoredEvent { - code_hash: { - code_hash = event.code_hash.clone(); - event.code_hash.clone() - }, - }, - }, - step: { - call: InstantiateCall { - endowment: 100_000_000_000_000, - gas_limit: 500_000_000, - code_hash: &code_hash, - data: &[], - }, - event: InstantiatedEvent { - caller: alice.clone(), - contract: event.contract.clone(), - }, - }, - }); + // #[test] + // #[cfg(feature = "integration-tests")] + // fn tx_instantiate() { + // env_logger::try_init().ok(); + // let result: Result<_, Error> = async_std::task::block_on(async move { + // let signer = AccountKeyring::Bob.pair(); + // let client = test_client().await; + // + // let code_hash = put_code(&client, signer.clone()).await?; + // + // log::info!("Code hash: {:?}", code_hash); + // + // let xt = client.xt(signer, None).await?; + // let result = xt + // .watch() + // .submit(InstantiateCall { + // endowment: 100_000_000_000_000, + // gas_limit: 500_000_000, + // code_hash: &code_hash, + // data: &[], + // }) + // .await?; + // let event = result + // .find_event::>()? + // .ok_or(Error::Other("Failed to find Instantiated event".into()))?; + // Ok(event) + // }); + // + // log::info!("Instantiate result: {:?}", result); + // + // assert!( + // result.is_ok(), + // format!("Error instantiating contract: {:?}", result) + // ); + // } } diff --git a/src/runtimes.rs b/src/runtimes.rs index 13779d71b8..5e1a55ce42 100644 --- a/src/runtimes.rs +++ b/src/runtimes.rs @@ -116,6 +116,39 @@ impl Balances for NodeTemplateRuntime { impl Sudo for NodeTemplateRuntime {} +/// Concrete type definitions compatible with the node template, with the +/// contracts pallet enabled. +/// +/// Inherits types from [`NodeTemplateRuntime`], but adds an implementation for +/// the contracts pallet trait. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ContractsTemplateRuntime; + +impl Runtime for ContractsTemplateRuntime { + type Signature = ::Signature; + type Extra = DefaultExtra; +} + +impl System for ContractsTemplateRuntime { + type Index = ::Index; + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hashing = ::Hashing; + type AccountId = ::AccountId; + type Address = ::Address; + type Header = ::Header; + type Extrinsic = ::Extrinsic; + type AccountData = ::AccountData; +} + +impl Balances for ContractsTemplateRuntime { + type Balance = ::Balance; +} + +impl Contracts for ContractsTemplateRuntime {} + +impl Sudo for ContractsTemplateRuntime {} + /// Concrete type definitions compatible with those for kusama, v0.7 /// /// # Note From 0e7bc2d2e57389580922f1c3ca236d61335aa75b Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 14 Sep 2020 16:12:48 +0100 Subject: [PATCH 02/16] Test contract instantiate --- src/frame/contracts.rs | 70 +++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index c36311f80f..2f0498a96c 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -153,39 +153,39 @@ mod tests { ); } - // #[test] - // #[cfg(feature = "integration-tests")] - // fn tx_instantiate() { - // env_logger::try_init().ok(); - // let result: Result<_, Error> = async_std::task::block_on(async move { - // let signer = AccountKeyring::Bob.pair(); - // let client = test_client().await; - // - // let code_hash = put_code(&client, signer.clone()).await?; - // - // log::info!("Code hash: {:?}", code_hash); - // - // let xt = client.xt(signer, None).await?; - // let result = xt - // .watch() - // .submit(InstantiateCall { - // endowment: 100_000_000_000_000, - // gas_limit: 500_000_000, - // code_hash: &code_hash, - // data: &[], - // }) - // .await?; - // let event = result - // .find_event::>()? - // .ok_or(Error::Other("Failed to find Instantiated event".into()))?; - // Ok(event) - // }); - // - // log::info!("Instantiate result: {:?}", result); - // - // assert!( - // result.is_ok(), - // format!("Error instantiating contract: {:?}", result) - // ); - // } + #[async_std::test] + #[cfg(feature = "integration-tests")] + async fn tx_instantiate() { + env_logger::try_init().ok(); + let signer = PairSigner::new(AccountKeyring::Bob.pair()); + let client = ClientBuilder::::new().build().await.unwrap(); + + // call put_code extrinsic + let code = contract_wasm(); + let result = client.put_code_and_watch(&signer, &code).await.unwrap(); + let code_stored = result.code_stored().unwrap(); + let code_hash = code_stored.unwrap().code_hash; + + log::info!("Code hash: {:?}", code_hash); + + // call instantiate extrinsic + let result = client + .instantiate_and_watch( + &signer, + 100_000_000_000_000, // endowment + 500_000_000, // gas_limit + &code_hash, + &[], // data + ) + .await + .unwrap(); + + log::info!("Instantiate result: {:?}", result); + let event = result.instantiated().unwrap(); + + assert!( + event.is_some(), + format!("Error instantiating contract: {:?}", result) + ); + } } From 92bcd26c810522b1e4766a9a521b2b1d297f26ed Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 14 Sep 2020 16:13:17 +0100 Subject: [PATCH 03/16] Fmt --- src/frame/contracts.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index 2f0498a96c..a3cfd149ed 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -120,7 +120,11 @@ mod tests { use sp_keyring::AccountKeyring; use super::*; - use crate::{ClientBuilder, PairSigner, ContractsTemplateRuntime}; + use crate::{ + ClientBuilder, + ContractsTemplateRuntime, + PairSigner, + }; fn contract_wasm() -> Vec { const CONTRACT: &str = r#" @@ -138,7 +142,10 @@ mod tests { env_logger::try_init().ok(); let signer = PairSigner::new(AccountKeyring::Alice.pair()); - let client = ClientBuilder::::new().build().await.unwrap(); + let client = ClientBuilder::::new() + .build() + .await + .unwrap(); let code = contract_wasm(); let result = client.put_code_and_watch(&signer, &code).await.unwrap(); @@ -158,7 +165,10 @@ mod tests { async fn tx_instantiate() { env_logger::try_init().ok(); let signer = PairSigner::new(AccountKeyring::Bob.pair()); - let client = ClientBuilder::::new().build().await.unwrap(); + let client = ClientBuilder::::new() + .build() + .await + .unwrap(); // call put_code extrinsic let code = contract_wasm(); @@ -172,10 +182,10 @@ mod tests { let result = client .instantiate_and_watch( &signer, - 100_000_000_000_000, // endowment - 500_000_000, // gas_limit + 100_000_000_000_000, // endowment + 500_000_000, // gas_limit &code_hash, - &[], // data + &[], // data ) .await .unwrap(); From e5800f41307abdcb787cf1dd160e2e70aefff3e3 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 14 Sep 2020 17:19:08 +0100 Subject: [PATCH 04/16] Extract put_code and new_client functions --- src/frame/contracts.rs | 47 +++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index a3cfd149ed..de3e612497 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -121,19 +121,36 @@ mod tests { use super::*; use crate::{ + Client, ClientBuilder, ContractsTemplateRuntime, + Error, PairSigner, }; + use sp_core::sr25519::Pair; - fn contract_wasm() -> Vec { + async fn new_client() -> Client { + ClientBuilder::::new() + .build() + .await + .expect("Error creating client") + } + + async fn put_code(signer: &PairSigner) -> Result, Error> { const CONTRACT: &str = r#" (module (func (export "call")) (func (export "deploy")) ) "#; - wabt::wat2wasm(CONTRACT).expect("invalid wabt") + let code = wabt::wat2wasm(CONTRACT).expect("invalid wabt"); + + let client = new_client().await; + + let result = client.put_code_and_watch(signer, &code).await?; + result + .code_stored()? + .ok_or_else(|| "Failed to find a CodeStored event".into()) } #[async_std::test] @@ -142,17 +159,10 @@ mod tests { env_logger::try_init().ok(); let signer = PairSigner::new(AccountKeyring::Alice.pair()); - let client = ClientBuilder::::new() - .build() - .await - .unwrap(); - - let code = contract_wasm(); - let result = client.put_code_and_watch(&signer, &code).await.unwrap(); - let code_stored = result.code_stored().unwrap(); + let code_stored = put_code(&signer).await; assert!( - code_stored.is_some(), + code_stored.is_ok(), format!( "Error calling put_code and receiving CodeStored Event: {:?}", code_stored @@ -165,18 +175,13 @@ mod tests { async fn tx_instantiate() { env_logger::try_init().ok(); let signer = PairSigner::new(AccountKeyring::Bob.pair()); - let client = ClientBuilder::::new() - .build() - .await - .unwrap(); // call put_code extrinsic - let code = contract_wasm(); - let result = client.put_code_and_watch(&signer, &code).await.unwrap(); - let code_stored = result.code_stored().unwrap(); - let code_hash = code_stored.unwrap().code_hash; + let code_stored = put_code(&signer).await.unwrap(); + + log::info!("Code hash: {:?}", code_stored.code_hash); - log::info!("Code hash: {:?}", code_hash); + let client = new_client().await; // call instantiate extrinsic let result = client @@ -184,7 +189,7 @@ mod tests { &signer, 100_000_000_000_000, // endowment 500_000_000, // gas_limit - &code_hash, + &code_stored.code_hash, &[], // data ) .await From c6906c0a058699b722ab865e821af4a4eb11dc48 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 15 Sep 2020 10:34:39 +0100 Subject: [PATCH 05/16] Generate fresh accounts for contract tests to allow reruns without a chain purge --- src/frame/contracts.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index de3e612497..452df96347 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -121,6 +121,7 @@ mod tests { use super::*; use crate::{ + balances::*, Client, ClientBuilder, ContractsTemplateRuntime, @@ -128,6 +129,7 @@ mod tests { PairSigner, }; use sp_core::sr25519::Pair; + use sp_core::crypto::AccountId32; async fn new_client() -> Client { ClientBuilder::::new() @@ -153,12 +155,28 @@ mod tests { .ok_or_else(|| "Failed to find a CodeStored event".into()) } + /// generate a new keypair for an account, and fund it so it can perform smart contract operations + async fn generate_account() -> PairSigner { + use sp_core::Pair as _; + let new_account = Pair::generate().0; + let new_account_id: AccountId32 = new_account.public().into(); + // fund the account + let benefactor = PairSigner::new(AccountKeyring::Alice.pair()); + let client = new_client().await; + let endowment = 200_000_000_000_000; + let _ = client.transfer_and_watch(&benefactor, &new_account_id, endowment) + .await + .expect("New account balance transfer failed"); + PairSigner::new(new_account) + } + #[async_std::test] #[cfg(feature = "integration-tests")] async fn tx_put_code() { env_logger::try_init().ok(); - let signer = PairSigner::new(AccountKeyring::Alice.pair()); + let signer = generate_account().await; + let code_stored = put_code(&signer).await; assert!( @@ -174,7 +192,7 @@ mod tests { #[cfg(feature = "integration-tests")] async fn tx_instantiate() { env_logger::try_init().ok(); - let signer = PairSigner::new(AccountKeyring::Bob.pair()); + let signer = generate_account().await; // call put_code extrinsic let code_stored = put_code(&signer).await.unwrap(); @@ -203,4 +221,10 @@ mod tests { format!("Error instantiating contract: {:?}", result) ); } + + // #[async_std::test] + // #[cfg(feature = "integration-tests")] + // async fn tx_call() { + // + // } } From 477cac9a6ae25a1666ab2930cd8bdea5a3d4ca1e Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 15 Sep 2020 17:26:59 +0100 Subject: [PATCH 06/16] Fetch and increment nonce to allow concurrent test runs --- src/frame/contracts.rs | 116 +++++++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 50 deletions(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index 452df96347..be803d9427 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -120,64 +120,85 @@ mod tests { use sp_keyring::AccountKeyring; use super::*; - use crate::{ - balances::*, - Client, - ClientBuilder, - ContractsTemplateRuntime, - Error, - PairSigner, + use crate::{balances::*, system::*, Client, ClientBuilder, ContractsTemplateRuntime, Error, PairSigner, Signer}; + use sp_core::{ + crypto::AccountId32, + sr25519::Pair, }; - use sp_core::sr25519::Pair; - use sp_core::crypto::AccountId32; + use std::sync::atomic::{AtomicU32, Ordering}; - async fn new_client() -> Client { - ClientBuilder::::new() - .build() - .await - .expect("Error creating client") - } - - async fn put_code(signer: &PairSigner) -> Result, Error> { - const CONTRACT: &str = r#" - (module - (func (export "call")) - (func (export "deploy")) - ) - "#; - let code = wabt::wat2wasm(CONTRACT).expect("invalid wabt"); - - let client = new_client().await; - - let result = client.put_code_and_watch(signer, &code).await?; - result - .code_stored()? - .ok_or_else(|| "Failed to find a CodeStored event".into()) - } + static STASH_NONCE: std::sync::atomic::AtomicU32 = AtomicU32::new(0); /// generate a new keypair for an account, and fund it so it can perform smart contract operations - async fn generate_account() -> PairSigner { + async fn generate_account( + client: &Client, + stash: &mut PairSigner, + ) -> PairSigner { use sp_core::Pair as _; let new_account = Pair::generate().0; let new_account_id: AccountId32 = new_account.public().into(); // fund the account - let benefactor = PairSigner::new(AccountKeyring::Alice.pair()); - let client = new_client().await; let endowment = 200_000_000_000_000; - let _ = client.transfer_and_watch(&benefactor, &new_account_id, endowment) + let _ = client + .transfer_and_watch(stash, &new_account_id, endowment) .await .expect("New account balance transfer failed"); + stash.increment_nonce(); PairSigner::new(new_account) } + struct TestContext { + client: Client, + signer: PairSigner, + } + + impl TestContext { + async fn init() -> Self + { + env_logger::try_init().ok(); + + let client = ClientBuilder::::new() + .build() + .await + .expect("Error creating client"); + let mut stash = PairSigner::new(AccountKeyring::Alice.pair()); + let nonce = client + .account(&stash.account_id(), None) + .await + .unwrap() + .nonce; + let local_nonce = STASH_NONCE.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)).unwrap(); + stash.set_nonce(nonce + local_nonce); + + let signer = generate_account(&client, &mut stash).await; + + TestContext { + client, + signer, + } + } + + async fn put_code(&self) -> Result, Error> { + const CONTRACT: &str = r#" + (module + (func (export "call")) + (func (export "deploy")) + ) + "#; + let code = wabt::wat2wasm(CONTRACT).expect("invalid wabt"); + + let result = self.client.put_code_and_watch(&self.signer, &code).await?; + result + .code_stored()? + .ok_or_else(|| "Failed to find a CodeStored event".into()) + } + } + #[async_std::test] #[cfg(feature = "integration-tests")] async fn tx_put_code() { - env_logger::try_init().ok(); - - let signer = generate_account().await; - - let code_stored = put_code(&signer).await; + let ctx = TestContext::init().await; + let code_stored = ctx.put_code().await; assert!( code_stored.is_ok(), @@ -191,20 +212,15 @@ mod tests { #[async_std::test] #[cfg(feature = "integration-tests")] async fn tx_instantiate() { - env_logger::try_init().ok(); - let signer = generate_account().await; - - // call put_code extrinsic - let code_stored = put_code(&signer).await.unwrap(); + let ctx = TestContext::init().await; + let code_stored = ctx.put_code().await.unwrap(); log::info!("Code hash: {:?}", code_stored.code_hash); - let client = new_client().await; - // call instantiate extrinsic - let result = client + let result = ctx.client .instantiate_and_watch( - &signer, + &ctx.signer, 100_000_000_000_000, // endowment 500_000_000, // gas_limit &code_stored.code_hash, From f12013eafaa473c270af8efac7439329e3d24d9a Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 15 Sep 2020 17:27:27 +0100 Subject: [PATCH 07/16] fmt --- src/frame/contracts.rs | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index be803d9427..197550dde4 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -120,12 +120,24 @@ mod tests { use sp_keyring::AccountKeyring; use super::*; - use crate::{balances::*, system::*, Client, ClientBuilder, ContractsTemplateRuntime, Error, PairSigner, Signer}; + use crate::{ + balances::*, + system::*, + Client, + ClientBuilder, + ContractsTemplateRuntime, + Error, + PairSigner, + Signer, + }; use sp_core::{ crypto::AccountId32, sr25519::Pair, }; - use std::sync::atomic::{AtomicU32, Ordering}; + use std::sync::atomic::{ + AtomicU32, + Ordering, + }; static STASH_NONCE: std::sync::atomic::AtomicU32 = AtomicU32::new(0); @@ -153,8 +165,7 @@ mod tests { } impl TestContext { - async fn init() -> Self - { + async fn init() -> Self { env_logger::try_init().ok(); let client = ClientBuilder::::new() @@ -167,18 +178,19 @@ mod tests { .await .unwrap() .nonce; - let local_nonce = STASH_NONCE.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)).unwrap(); + let local_nonce = STASH_NONCE + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)) + .unwrap(); stash.set_nonce(nonce + local_nonce); let signer = generate_account(&client, &mut stash).await; - TestContext { - client, - signer, - } + TestContext { client, signer } } - async fn put_code(&self) -> Result, Error> { + async fn put_code( + &self, + ) -> Result, Error> { const CONTRACT: &str = r#" (module (func (export "call")) @@ -218,7 +230,8 @@ mod tests { log::info!("Code hash: {:?}", code_stored.code_hash); // call instantiate extrinsic - let result = ctx.client + let result = ctx + .client .instantiate_and_watch( &ctx.signer, 100_000_000_000_000, // endowment From e7c9eb2083150eb292aa56b38cc0cf2190b2f78e Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 16 Sep 2020 12:28:22 +0100 Subject: [PATCH 08/16] Failing contract call test --- src/frame/contracts.rs | 144 +++++++++++++++++++++++++++-------------- 1 file changed, 95 insertions(+), 49 deletions(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index 197550dde4..6982ce427e 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -115,6 +115,17 @@ pub struct InstantiatedEvent { pub contract: ::AccountId, } +/// Contract execution event. +/// +/// Raised upon successful executionFailing of a contract call +#[derive(Clone, Debug, Eq, PartialEq, Event, Decode)] +pub struct ContractExecutionEvent { + /// Caller of the contract. + pub caller: ::AccountId, + /// Raw contract event data + pub data: Vec, +} + #[cfg(test)] mod tests { use sp_keyring::AccountKeyring; @@ -141,30 +152,13 @@ mod tests { static STASH_NONCE: std::sync::atomic::AtomicU32 = AtomicU32::new(0); - /// generate a new keypair for an account, and fund it so it can perform smart contract operations - async fn generate_account( - client: &Client, - stash: &mut PairSigner, - ) -> PairSigner { - use sp_core::Pair as _; - let new_account = Pair::generate().0; - let new_account_id: AccountId32 = new_account.public().into(); - // fund the account - let endowment = 200_000_000_000_000; - let _ = client - .transfer_and_watch(stash, &new_account_id, endowment) - .await - .expect("New account balance transfer failed"); - stash.increment_nonce(); - PairSigner::new(new_account) - } - struct TestContext { client: Client, signer: PairSigner, } - impl TestContext { + impl TestContext + { async fn init() -> Self { env_logger::try_init().ok(); @@ -181,16 +175,33 @@ mod tests { let local_nonce = STASH_NONCE .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)) .unwrap(); + stash.set_nonce(nonce + local_nonce); - let signer = generate_account(&client, &mut stash).await; + let signer = Self::generate_account(&client, &mut stash).await; TestContext { client, signer } } - async fn put_code( - &self, - ) -> Result, Error> { + /// generate a new keypair for an account, and fund it so it can perform smart contract operations + async fn generate_account( + client: &Client, + stash: &mut PairSigner, + ) -> PairSigner { + use sp_core::Pair as _; + let new_account = Pair::generate().0; + let new_account_id: AccountId32 = new_account.public().into(); + // fund the account + let endowment = 200_000_000_000_000; + let _ = client + .transfer_and_watch(stash, &new_account_id, endowment) + .await + .expect("New account balance transfer failed"); + stash.increment_nonce(); + PairSigner::new(new_account) + } + + async fn put_code(&self) -> Result, Error> { const CONTRACT: &str = r#" (module (func (export "call")) @@ -200,9 +211,51 @@ mod tests { let code = wabt::wat2wasm(CONTRACT).expect("invalid wabt"); let result = self.client.put_code_and_watch(&self.signer, &code).await?; - result + let code_stored = result .code_stored()? - .ok_or_else(|| "Failed to find a CodeStored event".into()) + .ok_or_else(|| Error::Other("Failed to find a CodeStored event".into()))?; + log::info!("Code hash: {:?}", code_stored.code_hash); + Ok(code_stored) + } + + async fn instantiate(&self, code_hash: &::Hash, data: &[u8]) -> Result, Error> { + // call instantiate extrinsic + let result = self + .client + .instantiate_and_watch( + &self.signer, + 100_000_000_000_000, // endowment + 500_000_000, // gas_limit + code_hash, + data, + ) + .await?; + + log::info!("Instantiate result: {:?}", result); + let instantiated = result + .instantiated()? + .ok_or_else(|| Error::Other("Failed to find a Instantiated event".into()))?; + + Ok(instantiated) + } + + async fn call(&self, contract: &::Address, input_data: &[u8]) -> Result, Error> { + let result = self + .client + .call_and_watch( + &self.signer, + contract, + 0, // value + 500_000_000, // gas_limit + input_data, + ) + .await?; + log::info!("Call result: {:?}", result); + let executed = result + .contract_execution()? + .ok_or_else(|| Error::Other("Failed to find a ContractExecution event".into()))?; + + Ok(executed) } } @@ -227,33 +280,26 @@ mod tests { let ctx = TestContext::init().await; let code_stored = ctx.put_code().await.unwrap(); - log::info!("Code hash: {:?}", code_stored.code_hash); - - // call instantiate extrinsic - let result = ctx - .client - .instantiate_and_watch( - &ctx.signer, - 100_000_000_000_000, // endowment - 500_000_000, // gas_limit - &code_stored.code_hash, - &[], // data - ) - .await - .unwrap(); - - log::info!("Instantiate result: {:?}", result); - let event = result.instantiated().unwrap(); + let instantiated = ctx.instantiate(&code_stored.code_hash, &[]).await; assert!( - event.is_some(), - format!("Error instantiating contract: {:?}", result) + instantiated.is_ok(), + format!("Error instantiating contract: {:?}", instantiated) ); } - // #[async_std::test] - // #[cfg(feature = "integration-tests")] - // async fn tx_call() { - // - // } + #[async_std::test] + #[cfg(feature = "integration-tests")] + async fn tx_call() { + let ctx = TestContext::init().await; + let code_stored = ctx.put_code().await.unwrap(); + + let instantiated = ctx.instantiate(&code_stored.code_hash, &[]).await.unwrap(); + let executed = ctx.call(&instantiated.contract, &[]).await.unwrap(); + + assert!( + executed.is_ok(), + format!("Error calling contract: {:?}", instantiated) + ); + } } From 4b5ef630e0150a725717c7f5a090baba2eee5ecf Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 16 Sep 2020 12:29:59 +0100 Subject: [PATCH 09/16] Fmt and fix compilation --- src/frame/contracts.rs | 43 +++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index 6982ce427e..8e8fa73151 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -157,8 +157,7 @@ mod tests { signer: PairSigner, } - impl TestContext - { + impl TestContext { async fn init() -> Self { env_logger::try_init().ok(); @@ -201,7 +200,9 @@ mod tests { PairSigner::new(new_account) } - async fn put_code(&self) -> Result, Error> { + async fn put_code( + &self, + ) -> Result, Error> { const CONTRACT: &str = r#" (module (func (export "call")) @@ -211,14 +212,18 @@ mod tests { let code = wabt::wat2wasm(CONTRACT).expect("invalid wabt"); let result = self.client.put_code_and_watch(&self.signer, &code).await?; - let code_stored = result - .code_stored()? - .ok_or_else(|| Error::Other("Failed to find a CodeStored event".into()))?; + let code_stored = result.code_stored()?.ok_or_else(|| { + Error::Other("Failed to find a CodeStored event".into()) + })?; log::info!("Code hash: {:?}", code_stored.code_hash); Ok(code_stored) } - async fn instantiate(&self, code_hash: &::Hash, data: &[u8]) -> Result, Error> { + async fn instantiate( + &self, + code_hash: &::Hash, + data: &[u8], + ) -> Result, Error> { // call instantiate extrinsic let result = self .client @@ -232,28 +237,32 @@ mod tests { .await?; log::info!("Instantiate result: {:?}", result); - let instantiated = result - .instantiated()? - .ok_or_else(|| Error::Other("Failed to find a Instantiated event".into()))?; + let instantiated = result.instantiated()?.ok_or_else(|| { + Error::Other("Failed to find a Instantiated event".into()) + })?; Ok(instantiated) } - async fn call(&self, contract: &::Address, input_data: &[u8]) -> Result, Error> { + async fn call( + &self, + contract: &::Address, + input_data: &[u8], + ) -> Result, Error> { let result = self .client .call_and_watch( &self.signer, contract, - 0, // value - 500_000_000, // gas_limit + 0, // value + 500_000_000, // gas_limit input_data, ) .await?; log::info!("Call result: {:?}", result); - let executed = result - .contract_execution()? - .ok_or_else(|| Error::Other("Failed to find a ContractExecution event".into()))?; + let executed = result.contract_execution()?.ok_or_else(|| { + Error::Other("Failed to find a ContractExecution event".into()) + })?; Ok(executed) } @@ -295,7 +304,7 @@ mod tests { let code_stored = ctx.put_code().await.unwrap(); let instantiated = ctx.instantiate(&code_stored.code_hash, &[]).await.unwrap(); - let executed = ctx.call(&instantiated.contract, &[]).await.unwrap(); + let executed = ctx.call(&instantiated.contract, &[]).await; assert!( executed.is_ok(), From f6f9c610b3210efcf5f5ba29f2d10492849948d5 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 16 Sep 2020 12:38:32 +0100 Subject: [PATCH 10/16] Fix error message for contract call --- src/frame/contracts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index 8e8fa73151..8a5a916d25 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -308,7 +308,7 @@ mod tests { assert!( executed.is_ok(), - format!("Error calling contract: {:?}", instantiated) + format!("Error calling contract: {:?}", executed) ); } } From 635f958d7c4c24bc0b9f71806b73ffaa540fc753 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 22 Sep 2020 16:28:32 +0100 Subject: [PATCH 11/16] Fix call test --- src/frame/contracts.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index 8a5a916d25..bda862f936 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -91,6 +91,7 @@ pub struct CallCall<'a, T: Contracts> { /// Address of the contract. pub dest: &'a ::Address, /// Value to transfer to the contract. + #[codec(compact)] pub value: ::Balance, /// Gas limit. #[codec(compact)] @@ -138,6 +139,7 @@ mod tests { ClientBuilder, ContractsTemplateRuntime, Error, + ExtrinsicSuccess, PairSigner, Signer, }; @@ -248,7 +250,7 @@ mod tests { &self, contract: &::Address, input_data: &[u8], - ) -> Result, Error> { + ) -> Result, Error> { let result = self .client .call_and_watch( @@ -260,11 +262,7 @@ mod tests { ) .await?; log::info!("Call result: {:?}", result); - let executed = result.contract_execution()?.ok_or_else(|| { - Error::Other("Failed to find a ContractExecution event".into()) - })?; - - Ok(executed) + Ok(result) } } From ae826172e6bda0983ea68db03ad4295065d1333a Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 23 Sep 2020 10:23:59 +0100 Subject: [PATCH 12/16] Update contract execution event comment --- src/frame/contracts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index bda862f936..a404dcb7e4 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -118,7 +118,7 @@ pub struct InstantiatedEvent { /// Contract execution event. /// -/// Raised upon successful executionFailing of a contract call +/// Emitted upon successful execution of a contract, if any contract events were produced. #[derive(Clone, Debug, Eq, PartialEq, Event, Decode)] pub struct ContractExecutionEvent { /// Caller of the contract. From 3f4ae6838857e54451cadb2f6fe56d23f68f24db Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 24 Sep 2020 16:31:18 +0100 Subject: [PATCH 13/16] Remove redundant feature flags, now on module --- src/frame/contracts.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index da9eaa7932..d8f8edcb16 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -268,7 +268,6 @@ mod tests { } #[async_std::test] - #[cfg(feature = "integration-tests")] async fn tx_put_code() { let ctx = TestContext::init().await; let code_stored = ctx.put_code().await; @@ -283,7 +282,6 @@ mod tests { } #[async_std::test] - #[cfg(feature = "integration-tests")] async fn tx_instantiate() { let ctx = TestContext::init().await; let code_stored = ctx.put_code().await.unwrap(); @@ -297,7 +295,6 @@ mod tests { } #[async_std::test] - #[cfg(feature = "integration-tests")] async fn tx_call() { let ctx = TestContext::init().await; let code_stored = ctx.put_code().await.unwrap(); From 779cb18432c1c8f0bfee16b8325b618daec82608 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 24 Sep 2020 16:33:40 +0100 Subject: [PATCH 14/16] Update event data comment --- src/frame/contracts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index d8f8edcb16..474841bafc 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -123,7 +123,7 @@ pub struct InstantiatedEvent { pub struct ContractExecutionEvent { /// Caller of the contract. pub caller: ::AccountId, - /// Raw contract event data + /// SCALE encoded contract event data. pub data: Vec, } From 6684552e3c748befc7f3e594432b0a64af7c4d92 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 24 Sep 2020 16:34:53 +0100 Subject: [PATCH 15/16] Use fetch_add --- src/frame/contracts.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index 474841bafc..2173f1f55c 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -175,8 +175,7 @@ mod tests { .unwrap() .nonce; let local_nonce = STASH_NONCE - .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)) - .unwrap(); + .fetch_add(1, Ordering::SeqCst); stash.set_nonce(nonce + local_nonce); From 93b4e61ae1260019574a79519323202eca37c1bb Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 24 Sep 2020 16:44:02 +0100 Subject: [PATCH 16/16] Fmt --- src/frame/contracts.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index 2173f1f55c..0b3a1d72ed 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -174,8 +174,7 @@ mod tests { .await .unwrap() .nonce; - let local_nonce = STASH_NONCE - .fetch_add(1, Ordering::SeqCst); + let local_nonce = STASH_NONCE.fetch_add(1, Ordering::SeqCst); stash.set_nonce(nonce + local_nonce);