diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9914934777..7b0e555e8e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -37,4 +37,4 @@ jobs: run: cargo build --workspace --verbose - name: test - run: cargo test --workspace --verbose -- --test-threads=1 + run: cargo test --workspace --verbose diff --git a/src/frame/balances.rs b/src/frame/balances.rs index aa487b4ac7..f88102a81f 100644 --- a/src/frame/balances.rs +++ b/src/frame/balances.rs @@ -226,26 +226,22 @@ mod tests { } #[async_std::test] - #[cfg(feature = "integration-tests")] async fn test_state_balance_lock() -> Result<(), crate::Error> { - use crate::{ - frame::staking::{ - BondCallExt, - RewardDestination, - }, - runtimes::KusamaRuntime as RT, - ClientBuilder, + use crate::frame::staking::{ + BondCallExt, + RewardDestination, }; env_logger::try_init().ok(); - let bob = PairSigner::::new(AccountKeyring::Bob.pair()); - let client = ClientBuilder::::new().build().await?; + let bob = PairSigner::::new(AccountKeyring::Bob.pair()); + let test_node_proc = test_node_process().await; + let client = test_node_proc.client(); client .bond_and_watch( &bob, - AccountKeyring::Charlie.to_account_id(), - 100_000_000_000, + &AccountKeyring::Charlie.to_account_id().into(), + 100_000_000_000_000, RewardDestination::Stash, ) .await?; @@ -258,7 +254,7 @@ mod tests { locks, vec![BalanceLock { id: *b"staking ", - amount: 100_000_000_000, + amount: 100_000_000_000_000, reasons: Reasons::All, }] ); diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index c06436df1f..ae7e894262 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -24,7 +24,6 @@ use codec::{ Decode, Encode, }; -use core::marker::PhantomData; /// Gas units are chosen to be represented by u64 so that gas metering /// instructions can operate on them efficiently. @@ -34,52 +33,63 @@ pub type Gas = u64; #[module] pub trait Contracts: System + Balances {} -/// Stores the given binary Wasm code into the chain's storage and returns -/// its `codehash`. -/// You can instantiate contracts only with stored code. -#[derive(Clone, Debug, Eq, PartialEq, Call, Encode)] -pub struct PutCodeCall<'a, T: Contracts> { - /// Runtime marker. - pub _runtime: PhantomData, - /// Wasm blob. - pub code: &'a [u8], -} - -/// Creates a new contract from the `codehash` generated by `put_code`, -/// optionally transferring some balance. +/// Instantiates a new contract from the supplied `code` optionally transferring +/// some balance. /// -/// Creation is executed as follows: +/// This is the only function that can deploy new code to the chain. /// -/// - The destination address is computed based on the sender and hash of -/// the code. -/// - The smart-contract account is instantiated at the computed address. -/// - The `ctor_code` is executed in the context of the newly-instantiated -/// account. Buffer returned after the execution is saved as the `code`https://www.bbc.co.uk/ -/// of the account. That code will be invoked upon any call received by -/// this account. -/// - The contract is initialized. +/// Instantiation is executed as follows: +/// +/// - The supplied `code` is instrumented, deployed, and a `code_hash` is created for that code. +/// - If the `code_hash` already exists on the chain the underlying `code` will be shared. +/// - The destination address is computed based on the sender, code_hash and the salt. +/// - The smart-contract account is created at the computed address. +/// - The `endowment` is transferred to the new account. +/// - The `deploy` function is executed in the context of the newly-created account. #[derive(Clone, Debug, Eq, PartialEq, Call, Encode)] -pub struct InstantiateCall<'a, T: Contracts> { - /// Initial balance transferred to the contract. +pub struct InstantiateWithCodeCall<'a, T: Contracts> { + /// The balance to transfer from the `origin` to the newly created contract. #[codec(compact)] pub endowment: ::Balance, - /// Gas limit. + /// The gas limit enforced when executing the constructor. #[codec(compact)] pub gas_limit: Gas, - /// Code hash returned by the put_code call. + /// The contract code to deploy in raw bytes. + pub code: &'a [u8], + /// The input data to pass to the contract constructor. + pub data: &'a [u8], + /// Used for the address derivation. + pub salt: &'a [u8], +} + +/// Instantiates a contract from a previously deployed wasm binary. +/// +/// This function is identical to [`InstantiateWithCodeCall`] but without the +/// code deployment step. Instead, the `code_hash` of an on-chain deployed wasm binary +/// must be supplied. +#[derive(Clone, Debug, Eq, PartialEq, Call, Encode)] +pub struct InstantiateCall<'a, T: Contracts> { + /// The balance to transfer from the `origin` to the newly created contract. + #[codec(compact)] + pub endowment: ::Balance, + /// The gas limit enforced when executing the constructor. + #[codec(compact)] + pub gas_limit: Gas, + /// Code hash of the already deployed on-chain deployed wasm binary. pub code_hash: &'a ::Hash, /// Data to initialize the contract with. pub data: &'a [u8], + /// Used for the address derivation. + pub salt: &'a [u8], } /// Makes a call to an account, optionally transferring some balance. /// -/// * If the account is a smart-contract account, the associated code will -/// be executed and any value will be transferred. +/// * If the account is a smart-contract account, the associated code will be +/// executed and any value will be transferred. /// * If the account is a regular account, any value will be transferred. -/// * If no account exists and the call value is not less than -/// `existential_deposit`, a regular account will be created and any value -/// will be transferred. +/// * If no account exists and the call value is not less than `existential_deposit`, +/// a regular account will be created and any value will be transferred. #[derive(Clone, Debug, PartialEq, Call, Encode)] pub struct CallCall<'a, T: Contracts> { /// Address of the contract. @@ -122,82 +132,44 @@ pub struct ContractExecutionEvent { } #[cfg(test)] -#[cfg(feature = "integration-tests")] mod tests { use sp_keyring::AccountKeyring; use super::*; use crate::{ - balances::*, - system::*, + tests::{ + test_node_process, + TestNodeProcess, + TestRuntime, + }, Client, - ClientBuilder, - ContractsTemplateRuntime, Error, ExtrinsicSuccess, PairSigner, - Signer, }; - use sp_core::{ - crypto::AccountId32, - sr25519::Pair, - }; - use std::sync::atomic::{ - AtomicU32, - Ordering, - }; - - static STASH_NONCE: std::sync::atomic::AtomicU32 = AtomicU32::new(0); + use sp_core::sr25519::Pair; struct TestContext { - client: Client, - signer: PairSigner, + node_process: TestNodeProcess, + 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_add(1, Ordering::SeqCst); + let node_process = test_node_process().await; + let signer = PairSigner::new(AccountKeyring::Alice.pair()); - stash.set_nonce(nonce + local_nonce); - - let signer = Self::generate_account(&client, &mut stash).await; - - TestContext { client, signer } + TestContext { + node_process, + signer, + } } - /// 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.into(), endowment) - .await - .expect("New account balance transfer failed"); - stash.increment_nonce(); - PairSigner::new(new_account) - } - - async fn put_code( + async fn instantiate_with_code( &self, - ) -> Result, Error> { + ) -> Result, Error> { const CONTRACT: &str = r#" (module (func (export "call")) @@ -206,28 +178,40 @@ 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(|| { + let result = self + .client() + .instantiate_with_code_and_watch( + &self.signer, + 100_000_000_000_000_000, // endowment + 500_000_000_000, // gas_limit + &code, + &[], // data + &[], // salt + ) + .await?; + let event = 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) + log::info!("Code hash: {:?}", event.code_hash); + Ok(event) } async fn instantiate( &self, - code_hash: &::Hash, + code_hash: &::Hash, data: &[u8], - ) -> Result, Error> { + salt: &[u8], + ) -> Result, Error> { // call instantiate extrinsic let result = self - .client + .client() .instantiate_and_watch( &self.signer, - 100_000_000_000_000, // endowment - 500_000_000, // gas_limit + 100_000_000_000_000_000, // endowment + 500_000_000_000, // gas_limit code_hash, data, + salt, ) .await?; @@ -241,11 +225,11 @@ mod tests { async fn call( &self, - contract: &::Address, + contract: &::Address, input_data: &[u8], - ) -> Result, Error> { + ) -> Result, Error> { let result = self - .client + .client() .call_and_watch( &self.signer, contract, @@ -257,17 +241,21 @@ mod tests { log::info!("Call result: {:?}", result); Ok(result) } + + fn client(&self) -> &Client { + self.node_process.client() + } } #[async_std::test] - async fn tx_put_code() { + async fn tx_instantiate_with_code() { let ctx = TestContext::init().await; - let code_stored = ctx.put_code().await; + let code_stored = ctx.instantiate_with_code().await; assert!( code_stored.is_ok(), format!( - "Error calling put_code and receiving CodeStored Event: {:?}", + "Error calling instantiate_with_code and receiving CodeStored Event: {:?}", code_stored ) ); @@ -276,9 +264,9 @@ mod tests { #[async_std::test] async fn tx_instantiate() { let ctx = TestContext::init().await; - let code_stored = ctx.put_code().await.unwrap(); + let code_stored = ctx.instantiate_with_code().await.unwrap(); - let instantiated = ctx.instantiate(&code_stored.code_hash, &[]).await; + let instantiated = ctx.instantiate(&code_stored.code_hash, &[], &[1u8]).await; assert!( instantiated.is_ok(), @@ -289,10 +277,10 @@ mod tests { #[async_std::test] async fn tx_call() { let ctx = TestContext::init().await; - let code_stored = ctx.put_code().await.unwrap(); + let code_stored = ctx.instantiate_with_code().await.unwrap(); let instantiated = ctx - .instantiate(&code_stored.code_hash.into(), &[]) + .instantiate(&code_stored.code_hash.into(), &[], &[1u8]) .await .unwrap(); let executed = ctx.call(&instantiated.contract.into(), &[]).await; diff --git a/src/frame/staking.rs b/src/frame/staking.rs index 899db767e3..37cedffbce 100644 --- a/src/frame/staking.rs +++ b/src/frame/staking.rs @@ -200,9 +200,9 @@ pub struct NominateCall { /// Take the origin account as a stash and lock up `value` of its balance. /// `controller` will be the account that controls it. #[derive(Call, Encode, Debug)] -pub struct BondCall { +pub struct BondCall<'a, T: Staking> { /// Tู—he controller account - pub contrller: T::AccountId, + pub controller: &'a T::Address, /// Lock up `value` of its balance. #[codec(compact)] pub value: T::Balance, @@ -211,7 +211,6 @@ pub struct BondCall { } #[cfg(test)] -#[cfg(feature = "integration-tests")] mod tests { use super::*; use crate::{ @@ -221,8 +220,10 @@ mod tests { Signer, }, frame::balances::*, - runtimes::KusamaRuntime as RT, - ClientBuilder, + tests::{ + test_node_process, + TestRuntime, + }, Error, ExtrinsicSuccess, }; @@ -242,14 +243,15 @@ mod tests { #[async_std::test] async fn test_validate_with_controller_account() -> Result<(), Error> { env_logger::try_init().ok(); - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let client = ClientBuilder::::new().build().await?; + let alice = PairSigner::::new(AccountKeyring::Alice.pair()); + let test_node_proc = test_node_process().await; + let client = test_node_proc.client(); let announce_validator = client .validate_and_watch(&alice, ValidatorPrefs::default()) .await; assert_matches!(announce_validator, Ok(ExtrinsicSuccess {block: _, extrinsic: _, events}) => { // TOOD: this is unsatisfying โ€“ can we do better? - assert_eq!(events.len(), 3); + assert_eq!(events.len(), 2); }); Ok(()) @@ -258,8 +260,10 @@ mod tests { #[async_std::test] async fn test_validate_not_possible_for_stash_account() -> Result<(), Error> { env_logger::try_init().ok(); - let alice_stash = PairSigner::::new(get_from_seed("Alice//stash")); - let client = ClientBuilder::::new().build().await?; + let alice_stash = + PairSigner::::new(get_from_seed("Alice//stash")); + let test_node_proc = test_node_process().await; + let client = test_node_proc.client(); let announce_validator = client .validate_and_watch(&alice_stash, ValidatorPrefs::default()) .await; @@ -273,16 +277,17 @@ mod tests { #[async_std::test] async fn test_nominate_with_controller_account() -> Result<(), Error> { env_logger::try_init().ok(); - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let bob = PairSigner::::new(AccountKeyring::Bob.pair()); - let client = ClientBuilder::::new().build().await?; + let alice = PairSigner::::new(AccountKeyring::Alice.pair()); + let bob = PairSigner::::new(AccountKeyring::Bob.pair()); + let test_node_proc = test_node_process().await; + let client = test_node_proc.client(); let nomination = client - .nominate_and_watch(&alice, vec![bob.account_id().clone()]) + .nominate_and_watch(&alice, vec![bob.account_id().clone().into()]) .await; assert_matches!(nomination, Ok(ExtrinsicSuccess {block: _, extrinsic: _, events}) => { // TOOD: this is unsatisfying โ€“ can we do better? - assert_eq!(events.len(), 3); + assert_eq!(events.len(), 2); }); Ok(()) } @@ -291,12 +296,13 @@ mod tests { async fn test_nominate_not_possible_for_stash_account() -> Result<(), Error> { env_logger::try_init().ok(); let alice_stash = - PairSigner::::new(get_from_seed("Alice//stash")); - let bob = PairSigner::::new(AccountKeyring::Bob.pair()); - let client = ClientBuilder::::new().build().await?; + PairSigner::::new(get_from_seed("Alice//stash")); + let bob = PairSigner::::new(AccountKeyring::Bob.pair()); + let test_node_proc = test_node_process().await; + let client = test_node_proc.client(); let nomination = client - .nominate_and_watch(&alice_stash, vec![bob.account_id().clone()]) + .nominate_and_watch(&alice_stash, vec![bob.account_id().clone().into()]) .await; assert_matches!(nomination, Err(Error::Runtime(RuntimeError::Module(module_err))) => { assert_eq!(module_err.module, "Staking"); @@ -309,14 +315,16 @@ mod tests { async fn test_chill_works_for_controller_only() -> Result<(), Error> { env_logger::try_init().ok(); let alice_stash = - PairSigner::::new(get_from_seed("Alice//stash")); - let bob_stash = PairSigner::::new(get_from_seed("Bob//stash")); - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let client = ClientBuilder::::new().build().await?; + PairSigner::::new(get_from_seed("Alice//stash")); + let bob_stash = + PairSigner::::new(get_from_seed("Bob//stash")); + let alice = PairSigner::::new(AccountKeyring::Alice.pair()); + let test_node_proc = test_node_process().await; + let client = test_node_proc.client(); // this will fail the second time, which is why this is one test, not two client - .nominate_and_watch(&alice, vec![bob_stash.account_id().clone()]) + .nominate_and_watch(&alice, vec![bob_stash.account_id().clone().into()]) .await?; let store = LedgerStore { controller: alice.account_id().clone(), @@ -333,7 +341,7 @@ mod tests { let chill = client.chill_and_watch(&alice).await; assert_matches!(chill, Ok(ExtrinsicSuccess {block: _, extrinsic: _, events}) => { // TOOD: this is unsatisfying โ€“ can we do better? - assert_eq!(events.len(), 3); + assert_eq!(events.len(), 2); }); Ok(()) } @@ -341,14 +349,15 @@ mod tests { #[async_std::test] async fn test_bond() -> Result<(), Error> { env_logger::try_init().ok(); - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let client = ClientBuilder::::new().build().await.unwrap(); + let alice = PairSigner::::new(AccountKeyring::Alice.pair()); + let test_node_proc = test_node_process().await; + let client = test_node_proc.client(); let bond = client .bond_and_watch( &alice, - AccountKeyring::Bob.to_account_id(), - 100_000_000_000, + &AccountKeyring::Bob.to_account_id().into(), + 100_000_000_000_000, RewardDestination::Stash, ) .await; @@ -361,7 +370,7 @@ mod tests { let bond_again = client .bond_and_watch( &alice, - AccountKeyring::Bob.to_account_id(), + &AccountKeyring::Bob.to_account_id().into(), 100_000_000_000, RewardDestination::Stash, ) @@ -378,7 +387,8 @@ mod tests { #[async_std::test] async fn test_total_issuance_is_okay() -> Result<(), Error> { env_logger::try_init().ok(); - let client = ClientBuilder::::new().build().await?; + let test_node_proc = test_node_process().await; + let client = test_node_proc.client(); let total_issuance = client.total_issuance(None).await?; assert!(total_issuance > 1u128 << 32); Ok(()) @@ -387,7 +397,8 @@ mod tests { #[async_std::test] async fn test_history_depth_is_okay() -> Result<(), Error> { env_logger::try_init().ok(); - let client = ClientBuilder::::new().build().await?; + let test_node_proc = test_node_process().await; + let client = test_node_proc.client(); let history_depth = client.history_depth(None).await?; assert_eq!(history_depth, 84); Ok(()) @@ -396,7 +407,8 @@ mod tests { #[async_std::test] async fn test_current_era_is_okay() -> Result<(), Error> { env_logger::try_init().ok(); - let client = ClientBuilder::::new().build().await?; + let test_node_proc = test_node_process().await; + let client = test_node_proc.client(); let _current_era = client .current_era(None) .await? @@ -407,16 +419,17 @@ mod tests { #[async_std::test] async fn test_era_reward_points_is_okay() -> Result<(), Error> { env_logger::try_init().ok(); - let client = ClientBuilder::::new().build().await?; + let test_node_proc = test_node_process().await; + let client = test_node_proc.client(); let store = ErasRewardPointsStore { _phantom: PhantomData, index: 0, }; - let _current_era = client - .fetch(&store, None) - .await? - .expect("current era always exists"); + let current_era_result = client.fetch(&store, None).await?; + + assert_matches!(current_era_result, Some(_)); + Ok(()) } } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 8681a904e0..b63d9a58bb 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -17,7 +17,7 @@ mod node_proc; use super::*; -use node_proc::TestNodeProcess; +pub use node_proc::TestNodeProcess; use sp_core::storage::{ well_known_keys, StorageKey,