From 777151680f72f564c65e770a7d3e7bddcfa12da1 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Wed, 23 Feb 2022 06:32:01 +0100 Subject: [PATCH] Replace default off-chain engine with experimental one, remove old one --- .gitlab-ci.yml | 27 - crates/engine/Cargo.toml | 2 +- crates/env/Cargo.toml | 2 +- crates/env/src/backend.rs | 6 - .../engine/experimental_off_chain/impls.rs | 463 ------------------ .../src/engine/experimental_off_chain/mod.rs | 69 --- .../engine/experimental_off_chain/test_api.rs | 331 ------------- .../engine/experimental_off_chain/tests.rs | 43 -- .../engine/experimental_off_chain/types.rs | 68 --- crates/env/src/engine/mod.rs | 8 - crates/env/src/engine/off_chain/call_data.rs | 98 ---- .../src/engine/off_chain/chain_extension.rs | 97 ---- .../env/src/engine/off_chain/db/accounts.rs | 348 ------------- crates/env/src/engine/off_chain/db/block.rs | 113 ----- .../env/src/engine/off_chain/db/chain_spec.rs | 143 ------ .../env/src/engine/off_chain/db/debug_buf.rs | 81 --- crates/env/src/engine/off_chain/db/events.rs | 128 ----- .../src/engine/off_chain/db/exec_context.rs | 224 --------- crates/env/src/engine/off_chain/db/mod.rs | 49 -- crates/env/src/engine/off_chain/hashing.rs | 67 --- crates/env/src/engine/off_chain/impls.rs | 336 ++++++------- crates/env/src/engine/off_chain/mod.rs | 260 ++-------- crates/env/src/engine/off_chain/test_api.rs | 362 ++++++-------- crates/env/src/engine/off_chain/tests.rs | 87 +--- .../env/src/engine/off_chain/typed_encoded.rs | 329 ------------- crates/env/src/engine/off_chain/types.rs | 86 ++-- crates/lang/Cargo.toml | 7 - crates/lang/tests/unique_topics.rs | 26 - crates/storage/Cargo.toml | 1 - .../src/collections/binary_heap/tests.rs | 133 ----- .../storage/src/collections/hashmap/tests.rs | 69 --- .../storage/src/collections/smallvec/tests.rs | 68 --- crates/storage/src/collections/stash/tests.rs | 68 --- crates/storage/src/collections/vec/tests.rs | 73 --- crates/storage/src/lazy/lazy_cell.rs | 108 ---- crates/storage/src/test_utils.rs | 20 +- examples/contract-terminate/Cargo.toml | 1 - examples/contract-terminate/lib.rs | 60 --- examples/contract-transfer/Cargo.toml | 1 - examples/contract-transfer/lib.rs | 150 ------ examples/erc1155/Cargo.toml | 1 - examples/erc1155/lib.rs | 20 - examples/erc20/Cargo.toml | 1 - examples/erc20/lib.rs | 317 ------------ 44 files changed, 418 insertions(+), 4533 deletions(-) delete mode 100644 crates/env/src/engine/experimental_off_chain/impls.rs delete mode 100644 crates/env/src/engine/experimental_off_chain/mod.rs delete mode 100644 crates/env/src/engine/experimental_off_chain/test_api.rs delete mode 100644 crates/env/src/engine/experimental_off_chain/tests.rs delete mode 100644 crates/env/src/engine/experimental_off_chain/types.rs delete mode 100644 crates/env/src/engine/off_chain/call_data.rs delete mode 100644 crates/env/src/engine/off_chain/chain_extension.rs delete mode 100644 crates/env/src/engine/off_chain/db/accounts.rs delete mode 100644 crates/env/src/engine/off_chain/db/block.rs delete mode 100644 crates/env/src/engine/off_chain/db/chain_spec.rs delete mode 100644 crates/env/src/engine/off_chain/db/debug_buf.rs delete mode 100644 crates/env/src/engine/off_chain/db/events.rs delete mode 100644 crates/env/src/engine/off_chain/db/exec_context.rs delete mode 100644 crates/env/src/engine/off_chain/db/mod.rs delete mode 100644 crates/env/src/engine/off_chain/hashing.rs delete mode 100644 crates/env/src/engine/off_chain/typed_encoded.rs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5bb61474826..a2ec85a4658 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -257,12 +257,6 @@ test: - cargo test --verbose --all-features --no-fail-fast --workspace - cargo test --verbose --all-features --no-fail-fast --workspace --doc - # Just needed as long as we have the `ink-experimental-engine` feature. - # We do not invoke `--all-features` here -- this would imply the feature - # `ink-experimental-engine`. So in order to still run the tests without the - # experimental engine feature we need this command. - - cargo test --verbose --features std --no-fail-fast --workspace - docs: stage: workspace <<: *docker-env @@ -318,11 +312,6 @@ codecov: # RUSTFLAGS are the cause target cache can't be used here - cargo build --verbose --all-features --workspace - cargo test --verbose --all-features --no-fail-fast --workspace - # Just needed as long as we have the `ink-experimental-engine` feature. - # We must additionally run the coverage without `--all-features` here -- this - # would imply the feature `ink-experimental-engine`. So in order to still run - # the tests without the experimental engine feature we need this command. - - cargo test --verbose --features std --no-fail-fast --workspace # coverage with branches - grcov . --binary-path ./target/debug/ --source-dir . --output-type lcov --llvm --branch --ignore-not-existing --ignore "/*" --ignore "tests/*" --output-path lcov-w-branch.info @@ -352,22 +341,6 @@ examples-test: cargo test --verbose --manifest-path examples/delegator/${contract}/Cargo.toml; done -examples-test-experimental-engine: - stage: examples - <<: *docker-env - <<: *test-refs - needs: - - job: clippy-std - artifacts: false - script: - # We test only the examples for which the tests have already been migrated to - # use the experimental engine. - - cargo test --no-default-features --features std, ink-experimental-engine --verbose --manifest-path examples/erc20/Cargo.toml - - cargo test --no-default-features --features std, ink-experimental-engine --verbose --manifest-path examples/erc1155/Cargo.toml - - cargo test --no-default-features --features std, ink-experimental-engine --verbose --manifest-path examples/contract-terminate/Cargo.toml - - cargo test --no-default-features --features std, ink-experimental-engine --verbose --manifest-path examples/contract-transfer/Cargo.toml - - examples-contract-build: stage: examples <<: *docker-env diff --git a/crates/engine/Cargo.toml b/crates/engine/Cargo.toml index 089f7198c42..f550a0f9f8b 100644 --- a/crates/engine/Cargo.toml +++ b/crates/engine/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" repository = "https://github.com/paritytech/ink" documentation = "https://docs.rs/ink_engine/" homepage = "https://www.parity.io/" -description = "[ink!] Experimental off-chain environment for testing." +description = "[ink!] Off-chain environment for testing." keywords = ["wasm", "parity", "webassembly", "blockchain", "edsl"] categories = ["no-std", "embedded"] include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] diff --git a/crates/env/Cargo.toml b/crates/env/Cargo.toml index 282ec29713a..58bc025ca94 100644 --- a/crates/env/Cargo.toml +++ b/crates/env/Cargo.toml @@ -56,6 +56,7 @@ std = [ "ink_allocator/std", "ink_prelude/std", "ink_primitives/std", + "ink_engine/std", "scale/std", "scale-info/std", "secp256k1", @@ -69,5 +70,4 @@ std = [ ] # Enable contract debug messages via `debug_print!` and `debug_println!`. ink-debug = [] -ink-experimental-engine = ["ink_engine"] wee-alloc = ["ink_allocator/wee-alloc"] diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 733bce851b7..be4aed8afd8 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -44,12 +44,6 @@ impl ReturnFlags { } self } - - /// Returns the underlying `u32` representation. - #[cfg(not(feature = "ink-experimental-engine"))] - pub(crate) fn into_u32(self) -> u32 { - self.value - } } /// The flags used to change the behavior of a contract call. diff --git a/crates/env/src/engine/experimental_off_chain/impls.rs b/crates/env/src/engine/experimental_off_chain/impls.rs deleted file mode 100644 index 62932548640..00000000000 --- a/crates/env/src/engine/experimental_off_chain/impls.rs +++ /dev/null @@ -1,463 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::EnvInstance; -use crate::{ - call::{ - utils::ReturnType, - CallParams, - CreateParams, - }, - hash::{ - Blake2x128, - Blake2x256, - CryptoHash, - HashOutput, - Keccak256, - Sha2x256, - }, - topics::{ - Topics, - TopicsBuilderBackend, - }, - Clear, - EnvBackend, - Environment, - Error, - Result, - ReturnFlags, - TypedEnvBackend, -}; -use ink_engine::{ - ext, - ext::Engine, -}; -use ink_primitives::Key; - -/// The capacity of the static buffer. -/// This is the same size as the ink! on-chain environment. We chose to use the same size -/// to be as close to the on-chain behavior as possible. -const BUFFER_SIZE: usize = 1 << 14; // 16 kB - -impl CryptoHash for Blake2x128 { - fn hash(input: &[u8], output: &mut ::Type) { - type OutputType = [u8; 16]; - static_assertions::assert_type_eq_all!( - ::Type, - OutputType - ); - let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 16); - Engine::hash_blake2_128(input, output); - } -} - -impl CryptoHash for Blake2x256 { - fn hash(input: &[u8], output: &mut ::Type) { - type OutputType = [u8; 32]; - static_assertions::assert_type_eq_all!( - ::Type, - OutputType - ); - let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32); - Engine::hash_blake2_256(input, output); - } -} - -impl CryptoHash for Sha2x256 { - fn hash(input: &[u8], output: &mut ::Type) { - type OutputType = [u8; 32]; - static_assertions::assert_type_eq_all!( - ::Type, - OutputType - ); - let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32); - Engine::hash_sha2_256(input, output); - } -} - -impl CryptoHash for Keccak256 { - fn hash(input: &[u8], output: &mut ::Type) { - type OutputType = [u8; 32]; - static_assertions::assert_type_eq_all!( - ::Type, - OutputType - ); - let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32); - Engine::hash_keccak_256(input, output); - } -} - -impl From for crate::Error { - fn from(ext_error: ext::Error) -> Self { - match ext_error { - ext::Error::Unknown => Self::Unknown, - ext::Error::CalleeTrapped => Self::CalleeTrapped, - ext::Error::CalleeReverted => Self::CalleeReverted, - ext::Error::KeyNotFound => Self::KeyNotFound, - ext::Error::_BelowSubsistenceThreshold => Self::_BelowSubsistenceThreshold, - ext::Error::TransferFailed => Self::TransferFailed, - ext::Error::_EndowmentTooLow => Self::_EndowmentTooLow, - ext::Error::CodeNotFound => Self::CodeNotFound, - ext::Error::NotCallable => Self::NotCallable, - ext::Error::LoggingDisabled => Self::LoggingDisabled, - ext::Error::EcdsaRecoveryFailed => Self::EcdsaRecoveryFailed, - } - } -} - -#[derive(Default)] -pub struct TopicsBuilder { - pub topics: Vec>, -} - -impl TopicsBuilderBackend for TopicsBuilder -where - E: Environment, -{ - type Output = Vec; - - fn expect(&mut self, _expected_topics: usize) {} - - fn push_topic(&mut self, topic_value: &T) - where - T: scale::Encode, - { - let encoded = topic_value.encode(); - let len_encoded = encoded.len(); - let mut result = ::Hash::clear(); - let len_result = result.as_ref().len(); - if len_encoded <= len_result { - result.as_mut()[..len_encoded].copy_from_slice(&encoded[..]); - } else { - let mut hash_output = ::Type::default(); - ::hash(&encoded[..], &mut hash_output); - let copy_len = core::cmp::min(hash_output.len(), len_result); - result.as_mut()[0..copy_len].copy_from_slice(&hash_output[0..copy_len]); - } - let off_hash = result.as_ref(); - let off_hash = off_hash.to_vec(); - debug_assert!( - !self.topics.contains(&off_hash), - "duplicate topic hash discovered!" - ); - self.topics.push(off_hash); - } - - fn output(self) -> Self::Output { - let mut all: Vec = Vec::new(); - - let topics_len_compact = &scale::Compact(self.topics.len() as u32); - let topics_encoded = &scale::Encode::encode(&topics_len_compact)[..]; - all.append(&mut topics_encoded.to_vec()); - - self.topics.into_iter().for_each(|mut v| all.append(&mut v)); - all - } -} - -impl EnvInstance { - /// Returns the contract property value. - fn get_property( - &mut self, - ext_fn: fn(engine: &Engine, output: &mut &mut [u8]), - ) -> Result - where - T: scale::Decode, - { - let mut full_scope: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; - let full_scope = &mut &mut full_scope[..]; - ext_fn(&self.engine, full_scope); - scale::Decode::decode(&mut &full_scope[..]).map_err(Into::into) - } -} - -impl EnvBackend for EnvInstance { - fn set_contract_storage(&mut self, key: &Key, value: &V) - where - V: scale::Encode, - { - let v = scale::Encode::encode(value); - self.engine.set_storage(key.as_ref(), &v[..]); - } - - fn get_contract_storage(&mut self, key: &Key) -> Result> - where - R: scale::Decode, - { - let mut output: [u8; 9600] = [0; 9600]; - match self.engine.get_storage(key.as_ref(), &mut &mut output[..]) { - Ok(_) => (), - Err(ext::Error::KeyNotFound) => return Ok(None), - Err(_) => panic!("encountered unexpected error"), - } - let decoded = scale::Decode::decode(&mut &output[..])?; - Ok(Some(decoded)) - } - - fn clear_contract_storage(&mut self, key: &Key) { - self.engine.clear_storage(key.as_ref()) - } - - fn decode_input(&mut self) -> Result - where - T: scale::Decode, - { - unimplemented!("the experimental off chain env does not implement `seal_input`") - } - - fn return_value(&mut self, _flags: ReturnFlags, _return_value: &R) -> ! - where - R: scale::Encode, - { - unimplemented!( - "the experimental off chain env does not implement `seal_return_value`" - ) - } - - fn debug_message(&mut self, message: &str) { - self.engine.debug_message(message) - } - - fn hash_bytes(&mut self, input: &[u8], output: &mut ::Type) - where - H: CryptoHash, - { - ::hash(input, output) - } - - fn hash_encoded(&mut self, input: &T, output: &mut ::Type) - where - H: CryptoHash, - T: scale::Encode, - { - let enc_input = &scale::Encode::encode(input)[..]; - ::hash(enc_input, output) - } - - fn ecdsa_recover( - &mut self, - signature: &[u8; 65], - message_hash: &[u8; 32], - output: &mut [u8; 33], - ) -> Result<()> { - use secp256k1::{ - ecdsa::{ - RecoverableSignature, - RecoveryId, - }, - Message, - SECP256K1, - }; - - // In most implementations, the v is just 0 or 1 internally, but 27 was added - // as an arbitrary number for signing Bitcoin messages and Ethereum adopted that as well. - let recovery_byte = if signature[64] > 26 { - signature[64] - 27 - } else { - signature[64] - }; - let recovery_id = RecoveryId::from_i32(recovery_byte as i32) - .unwrap_or_else(|error| panic!("Unable to parse the recovery id: {}", error)); - let message = Message::from_slice(message_hash).unwrap_or_else(|error| { - panic!("Unable to create the message from hash: {}", error) - }); - let signature = - RecoverableSignature::from_compact(&signature[0..64], recovery_id) - .unwrap_or_else(|error| { - panic!("Unable to parse the signature: {}", error) - }); - - let pub_key = SECP256K1.recover_ecdsa(&message, &signature); - match pub_key { - Ok(pub_key) => { - *output = pub_key.serialize(); - Ok(()) - } - Err(_) => Err(Error::EcdsaRecoveryFailed), - } - } - - fn call_chain_extension( - &mut self, - func_id: u32, - input: &I, - status_to_result: F, - decode_to_result: D, - ) -> ::core::result::Result - where - I: scale::Encode, - T: scale::Decode, - E: From, - F: FnOnce(u32) -> ::core::result::Result<(), ErrorCode>, - D: FnOnce(&[u8]) -> ::core::result::Result, - { - let enc_input = &scale::Encode::encode(input)[..]; - let mut output: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; - - status_to_result(self.engine.call_chain_extension( - func_id, - enc_input, - &mut &mut output[..], - ))?; - let decoded = decode_to_result(&output[..])?; - Ok(decoded) - } -} - -impl TypedEnvBackend for EnvInstance { - fn caller(&mut self) -> T::AccountId { - self.get_property::(Engine::caller) - .unwrap_or_else(|error| { - panic!("could not read `caller` property: {:?}", error) - }) - } - - fn transferred_value(&mut self) -> T::Balance { - self.get_property::(Engine::value_transferred) - .unwrap_or_else(|error| { - panic!("could not read `transferred_value` property: {:?}", error) - }) - } - - fn gas_left(&mut self) -> u64 { - self.get_property::(Engine::gas_left) - .unwrap_or_else(|error| { - panic!("could not read `gas_left` property: {:?}", error) - }) - } - - fn block_timestamp(&mut self) -> T::Timestamp { - self.get_property::(Engine::block_timestamp) - .unwrap_or_else(|error| { - panic!("could not read `block_timestamp` property: {:?}", error) - }) - } - - fn account_id(&mut self) -> T::AccountId { - self.get_property::(Engine::address) - .unwrap_or_else(|error| { - panic!("could not read `account_id` property: {:?}", error) - }) - } - - fn balance(&mut self) -> T::Balance { - self.get_property::(Engine::balance) - .unwrap_or_else(|error| { - panic!("could not read `balance` property: {:?}", error) - }) - } - - fn block_number(&mut self) -> T::BlockNumber { - self.get_property::(Engine::block_number) - .unwrap_or_else(|error| { - panic!("could not read `block_number` property: {:?}", error) - }) - } - - fn minimum_balance(&mut self) -> T::Balance { - self.get_property::(Engine::minimum_balance) - .unwrap_or_else(|error| { - panic!("could not read `minimum_balance` property: {:?}", error) - }) - } - - fn emit_event(&mut self, event: Event) - where - T: Environment, - Event: Topics + scale::Encode, - { - let builder = TopicsBuilder::default(); - let enc_topics = event.topics::(builder.into()); - let enc_data = &scale::Encode::encode(&event)[..]; - self.engine.deposit_event(&enc_topics[..], enc_data); - } - - fn invoke_contract(&mut self, params: &CallParams) -> Result<()> - where - T: Environment, - Args: scale::Encode, - { - let _gas_limit = params.gas_limit(); - let _callee = params.callee(); - let _call_flags = params.call_flags().into_u32(); - let _transferred_value = params.transferred_value(); - let _input = params.exec_input(); - unimplemented!("off-chain environment does not support contract invocation") - } - - fn eval_contract( - &mut self, - _call_params: &CallParams>, - ) -> Result - where - T: Environment, - Args: scale::Encode, - R: scale::Decode, - { - unimplemented!("off-chain environment does not support contract evaluation") - } - - fn instantiate_contract( - &mut self, - params: &CreateParams, - ) -> Result - where - T: Environment, - Args: scale::Encode, - Salt: AsRef<[u8]>, - { - let _code_hash = params.code_hash(); - let _gas_limit = params.gas_limit(); - let _endowment = params.endowment(); - let _input = params.exec_input(); - let _salt_bytes = params.salt_bytes(); - unimplemented!("off-chain environment does not support contract instantiation") - } - - fn terminate_contract(&mut self, beneficiary: T::AccountId) -> ! - where - T: Environment, - { - let buffer = scale::Encode::encode(&beneficiary); - self.engine.terminate(&buffer[..]) - } - - fn transfer(&mut self, destination: T::AccountId, value: T::Balance) -> Result<()> - where - T: Environment, - { - let enc_destination = &scale::Encode::encode(&destination)[..]; - let enc_value = &scale::Encode::encode(&value)[..]; - self.engine - .transfer(enc_destination, enc_value) - .map_err(Into::into) - } - - fn weight_to_fee(&mut self, gas: u64) -> T::Balance { - let mut output: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; - self.engine.weight_to_fee(gas, &mut &mut output[..]); - scale::Decode::decode(&mut &output[..]).unwrap_or_else(|error| { - panic!("could not read `weight_to_fee` property: {:?}", error) - }) - } - - fn random(&mut self, subject: &[u8]) -> Result<(T::Hash, T::BlockNumber)> - where - T: Environment, - { - let mut output: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; - self.engine.random(subject, &mut &mut output[..]); - scale::Decode::decode(&mut &output[..]).map_err(Into::into) - } -} diff --git a/crates/env/src/engine/experimental_off_chain/mod.rs b/crates/env/src/engine/experimental_off_chain/mod.rs deleted file mode 100644 index c97b9d69b0a..00000000000 --- a/crates/env/src/engine/experimental_off_chain/mod.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod impls; -pub mod test_api; -mod types; - -#[cfg(test)] -mod tests; - -use super::OnInstance; -use crate::Error; - -use derive_more::From; -use ink_engine::ext::Engine; - -/// The experimental off-chain environment. -pub struct EnvInstance { - engine: Engine, -} - -impl OnInstance for EnvInstance { - fn on_instance(f: F) -> R - where - F: FnOnce(&mut Self) -> R, - { - use core::cell::RefCell; - thread_local!( - static INSTANCE: RefCell = RefCell::new( - EnvInstance { - engine: Engine::new() - } - ) - ); - INSTANCE.with(|instance| f(&mut instance.borrow_mut())) - } -} - -#[derive(Debug, From, PartialEq, Eq)] -pub enum OffChainError { - Account(AccountError), - #[from(ignore)] - UninitializedBlocks, - #[from(ignore)] - UninitializedExecutionContext, - #[from(ignore)] - UnregisteredChainExtension, -} - -/// Errors encountered upon interacting with the accounts database. -#[derive(Debug, From, PartialEq, Eq)] -pub enum AccountError { - Decoding(scale::Error), - #[from(ignore)] - UnexpectedUserAccount, - #[from(ignore)] - NoAccountForId(Vec), -} diff --git a/crates/env/src/engine/experimental_off_chain/test_api.rs b/crates/env/src/engine/experimental_off_chain/test_api.rs deleted file mode 100644 index 5155f1b7631..00000000000 --- a/crates/env/src/engine/experimental_off_chain/test_api.rs +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Operations on the off-chain testing environment. - -use super::{ - EnvInstance, - OnInstance, -}; -use crate::{ - Environment, - Result, -}; -use core::fmt::Debug; -use ink_engine::test_api::RecordedDebugMessages; -use std::panic::UnwindSafe; - -/// Record for an emitted event. -#[derive(Clone)] -pub struct EmittedEvent { - /// Recorded topics of the emitted event. - pub topics: Vec>, - /// Recorded encoding of the emitted event. - pub data: Vec, -} - -/// Sets the balance of the account to the given balance. -/// -/// # Note -/// -/// Note that account could refer to either a user account or -/// a smart contract account. -/// -/// # Errors -/// -/// - If `account` does not exist. -/// - If the underlying `account` type does not match. -/// - If the underlying `new_balance` type does not match. -pub fn set_account_balance(account_id: T::AccountId, new_balance: T::Balance) -where - T: Environment, // Just temporary for the MVP! -{ - ::on_instance(|instance| { - instance - .engine - .set_balance(scale::Encode::encode(&account_id), new_balance); - }) -} - -/// Returns the balance of the account. -/// -/// # Note -/// -/// Note that account could refer to either a user account or -/// a smart contract account. This returns the same as `env::api::balance` -/// if given the account id of the currently executed smart contract. -/// -/// # Errors -/// -/// - If `account` does not exist. -/// - If the underlying `account` type does not match. -pub fn get_account_balance(account_id: T::AccountId) -> Result -where - T: Environment, // Just temporary for the MVP! -{ - ::on_instance(|instance| { - instance - .engine - .get_balance(scale::Encode::encode(&account_id)) - .map_err(Into::into) - }) -} - -/// Set the entropy hash of the current block. -/// -/// # Note -/// -/// This allows to control what [`random`][`crate::random`] returns. -pub fn set_block_entropy(_entropy: T::Hash) -> Result<()> -where - T: Environment, -{ - unimplemented!("off-chain environment does not yet support `set_block_entropy`"); -} - -/// Returns the contents of the past performed environmental debug messages in order. -pub fn recorded_debug_messages() -> RecordedDebugMessages { - ::on_instance(|instance| { - instance.engine.get_emitted_debug_messages() - }) -} - -/// Set to true to disable clearing storage -/// -/// # Note -/// -/// Useful for benchmarks because it ensures the initialized storage is maintained across runs, -/// because lazy storage structures automatically clear their associated cells when they are dropped. -pub fn set_clear_storage_disabled(_disable: bool) { - unimplemented!( - "off-chain environment does not yet support `set_clear_storage_disabled`" - ); -} - -/// Sets a caller for the next call. -pub fn set_caller(caller: T::AccountId) -where - T: Environment, - ::AccountId: From<[u8; 32]>, -{ - ::on_instance(|instance| { - instance.engine.set_caller(scale::Encode::encode(&caller)); - }) -} - -/// Sets the callee for the next call. -pub fn set_callee(callee: T::AccountId) -where - T: Environment, - ::AccountId: From<[u8; 32]>, -{ - ::on_instance(|instance| { - instance.engine.set_callee(scale::Encode::encode(&callee)); - }) -} - -/// Gets the currently set callee. -/// -/// This is account id of the currently executing contract. -pub fn callee() -> T::AccountId -where - T: Environment, -{ - ::on_instance(|instance| { - let callee = instance.engine.get_callee(); - scale::Decode::decode(&mut &callee[..]).expect("encoding failed") - }) -} - -/// Returns the total number of reads and writes of the contract's storage. -pub fn get_contract_storage_rw(account_id: &T::AccountId) -> (usize, usize) -where - T: Environment, -{ - ::on_instance(|instance| { - instance - .engine - .get_contract_storage_rw(scale::Encode::encode(&account_id)) - }) -} - -/// Sets the balance of `account_id` to `new_balance`. -pub fn set_balance(account_id: T::AccountId, new_balance: T::Balance) -where - T: Environment, // Just temporary for the MVP! - ::AccountId: From<[u8; 32]>, -{ - ::on_instance(|instance| { - instance - .engine - .set_balance(scale::Encode::encode(&account_id), new_balance); - }) -} - -/// Sets the value transferred from the caller to the callee as part of the call. -pub fn set_value_transferred(value: T::Balance) -where - T: Environment, // Just temporary for the MVP! -{ - ::on_instance(|instance| { - instance.engine.set_value_transferred(value); - }) -} - -/// Returns the amount of storage cells used by the account `account_id`. -/// -/// Returns `None` if the `account_id` is non-existent. -pub fn count_used_storage_cells(account_id: &T::AccountId) -> Result -where - T: Environment, -{ - ::on_instance(|instance| { - instance - .engine - .count_used_storage_cells(&scale::Encode::encode(&account_id)) - .map_err(Into::into) - }) -} - -/// Runs the given closure test function with the default configuration -/// for the off-chain environment. -pub fn run_test(f: F) -> Result<()> -where - T: Environment, - F: FnOnce(DefaultAccounts) -> Result<()>, - ::AccountId: From<[u8; 32]>, -{ - let default_accounts = default_accounts::(); - ::on_instance(|instance| { - instance.engine.initialize_or_reset(); - - let encoded_alice = scale::Encode::encode(&default_accounts.alice); - instance.engine.set_caller(encoded_alice.clone()); - instance.engine.set_callee(encoded_alice.clone()); - - // set up the funds for the default accounts - let substantial = 1_000_000; - let some = 1_000; - instance.engine.set_balance(encoded_alice, substantial); - instance - .engine - .set_balance(scale::Encode::encode(&default_accounts.bob), some); - instance - .engine - .set_balance(scale::Encode::encode(&default_accounts.charlie), some); - instance - .engine - .set_balance(scale::Encode::encode(&default_accounts.django), 0); - instance - .engine - .set_balance(scale::Encode::encode(&default_accounts.eve), 0); - instance - .engine - .set_balance(scale::Encode::encode(&default_accounts.frank), 0); - }); - f(default_accounts) -} - -/// Returns the default accounts for testing purposes: -/// Alice, Bob, Charlie, Django, Eve and Frank. -pub fn default_accounts() -> DefaultAccounts -where - T: Environment, - ::AccountId: From<[u8; 32]>, -{ - DefaultAccounts { - alice: T::AccountId::from([0x01; 32]), - bob: T::AccountId::from([0x02; 32]), - charlie: T::AccountId::from([0x03; 32]), - django: T::AccountId::from([0x04; 32]), - eve: T::AccountId::from([0x05; 32]), - frank: T::AccountId::from([0x06; 32]), - } -} - -/// The default accounts. -pub struct DefaultAccounts -where - T: Environment, -{ - /// The predefined `ALICE` account holding substantial amounts of value. - pub alice: T::AccountId, - /// The predefined `BOB` account holding some amounts of value. - pub bob: T::AccountId, - /// The predefined `CHARLIE` account holding some amounts of value. - pub charlie: T::AccountId, - /// The predefined `DJANGO` account holding no value. - pub django: T::AccountId, - /// The predefined `EVE` account holding no value. - pub eve: T::AccountId, - /// The predefined `FRANK` account holding no value. - pub frank: T::AccountId, -} - -/// Returns the recorded emitted events in order. -pub fn recorded_events() -> impl Iterator { - ::on_instance(|instance| { - instance - .engine - .get_emitted_events() - .into_iter() - .map(|evt: ink_engine::test_api::EmittedEvent| evt.into()) - }) -} - -/// Tests if a contract terminates successfully after `self.env().terminate()` -/// has been called. -/// -/// The arguments denote: -/// -/// * `should_terminate`: A closure in which the function supposed to terminate is called. -/// * `expected_beneficiary`: The beneficiary account who should have received the -/// remaining value in the contract -/// * `expected_value_transferred_to_beneficiary`: The value which should have been transferred -/// to the `expected_beneficiary`. -/// # Usage -/// -/// ```no_compile -/// let should_terminate = move || your_contract.fn_which_should_terminate(); -/// ink_env::test::assert_contract_termination::( -/// should_terminate, -/// expected_beneficiary, -/// expected_value_transferred_to_beneficiary -/// ); -/// ``` -/// -/// See `examples/contract-terminate` for a complete usage example. -pub fn assert_contract_termination( - should_terminate: F, - expected_beneficiary: T::AccountId, - expected_value_transferred_to_beneficiary: T::Balance, -) where - T: Environment, - F: FnMut() + UnwindSafe, - ::AccountId: Debug, - ::Balance: Debug, -{ - let value_any = ::std::panic::catch_unwind(should_terminate) - .expect_err("contract did not terminate"); - let encoded_input = value_any - .downcast_ref::>() - .expect("panic object can not be cast"); - let (value_transferred, encoded_beneficiary): (T::Balance, Vec) = - scale::Decode::decode(&mut &encoded_input[..]).expect("input can not be decoded"); - let beneficiary = - ::decode(&mut &encoded_beneficiary[..]) - .expect("input can not be decoded"); - assert_eq!(value_transferred, expected_value_transferred_to_beneficiary); - assert_eq!(beneficiary, expected_beneficiary); -} diff --git a/crates/env/src/engine/experimental_off_chain/tests.rs b/crates/env/src/engine/experimental_off_chain/tests.rs deleted file mode 100644 index 3bebc374dc1..00000000000 --- a/crates/env/src/engine/experimental_off_chain/tests.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::{ - engine::experimental_off_chain::impls::TopicsBuilder, - topics::TopicsBuilderBackend, - Result, -}; - -#[test] -fn topics_builder() -> Result<()> { - crate::test::run_test::(|_| { - // given - let mut builder = TopicsBuilder::default(); - - // when - TopicsBuilderBackend::::push_topic(&mut builder, &13); - TopicsBuilderBackend::::push_topic(&mut builder, &17); - - // then - assert_eq!(builder.topics.len(), 2); - - let topics_len_compact = &scale::Compact(2u32); - let topics_len_encoded = scale::Encode::encode(&topics_len_compact); - let output = TopicsBuilderBackend::::output(builder); - #[rustfmt::skip] - let expected = vec![topics_len_encoded[0], 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - assert_eq!(output, expected); - - Ok(()) - }) -} diff --git a/crates/env/src/engine/experimental_off_chain/types.rs b/crates/env/src/engine/experimental_off_chain/types.rs deleted file mode 100644 index cd9e8644b6b..00000000000 --- a/crates/env/src/engine/experimental_off_chain/types.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Contains the necessary conversions from `ink_engine` types to types -//! of this crate. - -use super::{ - test_api::EmittedEvent, - AccountError, - Error, - OffChainError, -}; - -impl From for EmittedEvent { - fn from(evt: ink_engine::test_api::EmittedEvent) -> Self { - EmittedEvent { - topics: evt.topics, - data: evt.data, - } - } -} - -impl From for Error { - fn from(err: ink_engine::Error) -> Self { - let e = match err { - ink_engine::Error::Account(acc) => OffChainError::Account(acc.into()), - ink_engine::Error::UninitializedBlocks => OffChainError::UninitializedBlocks, - ink_engine::Error::UninitializedExecutionContext => { - OffChainError::UninitializedExecutionContext - } - ink_engine::Error::UnregisteredChainExtension => { - OffChainError::UnregisteredChainExtension - } - }; - Error::OffChain(e) - } -} - -impl From for AccountError { - fn from(err: ink_engine::AccountError) -> Self { - match err { - ink_engine::AccountError::Decoding(e) => AccountError::Decoding(e), - ink_engine::AccountError::UnexpectedUserAccount => { - AccountError::UnexpectedUserAccount - } - ink_engine::AccountError::NoAccountForId(acc) => { - AccountError::NoAccountForId(acc) - } - } - } -} - -impl From for Error { - fn from(account_error: ink_engine::AccountError) -> Self { - Error::OffChain(OffChainError::Account(account_error.into())) - } -} diff --git a/crates/env/src/engine/mod.rs b/crates/env/src/engine/mod.rs index 129820a1751..2e74d582e12 100644 --- a/crates/env/src/engine/mod.rs +++ b/crates/env/src/engine/mod.rs @@ -28,17 +28,9 @@ cfg_if! { if #[cfg(all(not(feature = "std"), target_arch = "wasm32"))] { mod on_chain; pub use self::on_chain::EnvInstance; - } else if #[cfg(all(feature = "std", feature = "ink-experimental-engine"))] { - pub mod experimental_off_chain; - pub use experimental_off_chain as off_chain; - pub use self::experimental_off_chain::EnvInstance; } else if #[cfg(feature = "std")] { pub mod off_chain; pub use self::off_chain::EnvInstance; - pub use self::off_chain::{ - AccountError, - TypedEncodedError, - }; } else { compile_error! { "ink! only support compilation as `std` or `no_std` + `wasm32-unknown`" diff --git a/crates/env/src/engine/off_chain/call_data.rs b/crates/env/src/engine/off_chain/call_data.rs deleted file mode 100644 index 69c89b671b4..00000000000 --- a/crates/env/src/engine/off_chain/call_data.rs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::call::Selector; -use ink_prelude::{ - vec, - vec::Vec, -}; - -/// The raw ABI respecting input data to a call. -/// -/// # Note -/// -/// The first four bytes are the function selector and the rest are SCALE encoded inputs. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CallData { - /// Already encoded function selector and inputs. - /// - /// # Note - /// - /// Has the invariant of always holding at least 4 bytes (the selector). - bytes: Vec, -} - -impl CallData { - /// Creates new call ABI data for the given selector. - pub fn new(selector: Selector) -> Self { - let bytes = selector.to_bytes(); - Self { - bytes: vec![bytes[0], bytes[1], bytes[2], bytes[3]], - } - } - - /// Pushes the given argument onto the call ABI data in encoded form. - pub fn push_arg(&mut self, arg: &A) - where - A: scale::Encode, - { - arg.encode_to(&mut self.bytes) - } - - /// Returns the selector of `self`. - pub fn selector(&self) -> Selector { - debug_assert!(self.bytes.len() >= 4); - let bytes = [self.bytes[0], self.bytes[1], self.bytes[2], self.bytes[3]]; - bytes.into() - } - - /// Returns the underlying bytes of the encoded input parameters. - pub fn params(&self) -> &[u8] { - debug_assert!(self.bytes.len() >= 4); - &self.bytes[4..] - } - - /// Returns the underlying byte representation. - pub fn to_bytes(&self) -> &[u8] { - &self.bytes - } -} - -impl scale::Encode for CallData { - fn size_hint(&self) -> usize { - self.bytes.len() - } - - fn encode_to(&self, dest: &mut T) { - dest.write(self.bytes.as_slice()); - } -} - -impl scale::Decode for CallData { - fn decode( - input: &mut I, - ) -> core::result::Result { - let remaining_len = input.remaining_len().unwrap_or(None).unwrap_or(0); - let mut bytes = Vec::with_capacity(remaining_len); - while let Ok(byte) = input.read_byte() { - bytes.push(byte); - } - if bytes.len() < 4 { - return Err(scale::Error::from( - "require at least 4 bytes for input data", - )) - } - Ok(Self { bytes }) - } -} diff --git a/crates/env/src/engine/off_chain/chain_extension.rs b/crates/env/src/engine/off_chain/chain_extension.rs deleted file mode 100644 index 82af4366b1f..00000000000 --- a/crates/env/src/engine/off_chain/chain_extension.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::OffChainError; -use crate::Result; -use derive_more::From; -use std::collections::{ - hash_map::Entry, - HashMap, -}; - -/// Chain extension registry. -/// -/// Allows to register chain extension methods and call them. -pub struct ChainExtensionHandler { - /// The currently registered runtime call handler. - registered: HashMap>, - /// The output buffer used and reused for chain extension method call results. - output: Vec, -} - -/// The unique ID of the registered chain extension method. -#[derive( - Debug, From, scale::Encode, scale::Decode, PartialEq, Eq, PartialOrd, Ord, Hash, -)] -pub struct ExtensionId(u32); - -/// Types implementing this trait can be used as chain extensions. -/// -/// This trait is only useful for testing contract via the off-chain environment. -pub trait ChainExtension { - /// The static function ID of the chain extension. - /// - /// # Note - /// - /// This is expected to return a constant value. - fn func_id(&self) -> u32; - - /// Calls the chain extension with the given input. - /// - /// Returns an error code and may fill the `output` buffer with a SCALE encoded result. - #[allow(clippy::ptr_arg)] - fn call(&mut self, input: &[u8], output: &mut Vec) -> u32; -} - -impl ChainExtensionHandler { - /// Creates a new chain extension handler. - /// - /// Initialized with an empty set of chain extensions. - pub fn new() -> Self { - Self { - registered: HashMap::new(), - output: Vec::new(), - } - } - - /// Resets the chain extension handler to uninitialized state. - pub fn reset(&mut self) { - self.registered.clear(); - self.output.clear(); - } - - /// Register a new chain extension. - pub fn register(&mut self, extension: Box) { - let func_id = extension.func_id(); - self.registered - .insert(ExtensionId::from(func_id), extension); - } - - /// Evaluates the chain extension with the given parameters. - /// - /// Upon success returns the values returned by the evaluated chain extension. - pub fn eval(&mut self, func_id: u32, input: &[u8]) -> Result<(u32, &[u8])> { - self.output.clear(); - let extension_id = ExtensionId::from(func_id); - match self.registered.entry(extension_id) { - Entry::Occupied(occupied) => { - let status_code = occupied.into_mut().call(input, &mut self.output); - Ok((status_code, &mut self.output)) - } - Entry::Vacant(_vacant) => { - Err(OffChainError::UnregisteredChainExtension.into()) - } - } - } -} diff --git a/crates/env/src/engine/off_chain/db/accounts.rs b/crates/env/src/engine/off_chain/db/accounts.rs deleted file mode 100644 index 1971c5043d4..00000000000 --- a/crates/env/src/engine/off_chain/db/accounts.rs +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::{ - super::{ - OffChainError, - TypedEncodedError, - }, - OffAccountId, - OffBalance, -}; -use crate::{ - Environment, - Error, -}; -use core::cell::Cell; -use derive_more::From; -use ink_prelude::collections::BTreeMap; -use ink_primitives::Key; - -/// Errors encountered upon interacting with the accounts database. -#[derive(Debug, From, PartialEq, Eq)] -pub enum AccountError { - TypedEncoded(TypedEncodedError), - #[from(ignore)] - UnexpectedUserAccount, - #[from(ignore)] - NoAccountForId(OffAccountId), -} - -impl From for Error { - fn from(account_error: AccountError) -> Self { - Error::OffChain(OffChainError::Account(account_error)) - } -} - -impl AccountError { - /// Creates a new error to indicate a missing account. - pub fn no_account_for_id(account_id: &T::AccountId) -> Self - where - T: Environment, - { - Self::NoAccountForId(OffAccountId::new(account_id)) - } -} - -impl From for AccountError { - fn from(err: scale::Error) -> Self { - AccountError::TypedEncoded(err.into()) - } -} - -/// Result type encountered while operating on accounts. -pub type Result = core::result::Result; - -/// The database that stores all accounts. -pub struct AccountsDb { - /// The mapping from account ID to an actual account. - accounts: BTreeMap, -} - -impl AccountsDb { - /// Creates a new empty accounts database. - pub fn new() -> Self { - Self { - accounts: BTreeMap::new(), - } - } - - /// Resets the account DB to uninitialized state. - pub fn reset(&mut self) { - self.accounts.clear() - } - - /// Returns the account at the given account ID or creates it. - pub fn get_or_create_account(&mut self, at: &T::AccountId) -> &mut Account - where - T: Environment, - { - // Note: We cannot do a normal match for `Some(account)` here since - // the borrow-checker somehow cannot make sense of it according - // to its lifetime analysis. Consider this to be a hack until - // the borrow-checker eventually let's us do this. - if self.get_account::(at).is_some() { - self.get_account_mut::(at) - .expect("just checked that account exists") - } else { - self.add_user_account::(at.clone(), 0u32.into()); - self.get_account_mut::(at) - .expect("just added the account so it must exist") - } - } - - /// Returns the account for the given account ID if any. - pub fn get_account(&self, at: &T::AccountId) -> Option<&Account> - where - T: Environment, - { - self.accounts.get(&OffAccountId::new(at)) - } - - /// Returns the account for the given account ID if any. - pub fn get_account_mut(&mut self, at: &T::AccountId) -> Option<&mut Account> - where - T: Environment, - { - self.accounts.get_mut(&OffAccountId::new(at)) - } - - /// Returns the account for the given off-account ID if any. - pub fn get_account_off<'a>(&'a self, at: &OffAccountId) -> Option<&'a Account> { - self.accounts.get(at) - } - - /// Returns the account for the given off-account ID if any. - pub fn get_account_off_mut(&mut self, at: &OffAccountId) -> Option<&mut Account> { - self.accounts.get_mut(at) - } - - /// Adds the given user account with the initial balance. - pub fn add_user_account( - &mut self, - account_id: T::AccountId, - initial_balance: T::Balance, - ) where - T: Environment, - { - self.accounts.insert( - OffAccountId::new(&account_id), - Account { - balance: OffBalance::new(&initial_balance), - kind: AccountKind::User, - }, - ); - } - - /// Creates a new contract account. - pub fn add_contract_account( - &mut self, - account_id: T::AccountId, - initial_balance: T::Balance, - ) where - T: Environment, - { - self.accounts.insert( - OffAccountId::new(&account_id), - Account { - balance: OffBalance::new(&initial_balance), - kind: AccountKind::Contract(ContractAccount::new::()), - }, - ); - } - - /// Removes an account. - pub fn remove_account(&mut self, account_id: T::AccountId) - where - T: Environment, - { - self.accounts.remove(&OffAccountId::new(&account_id)); - } -} - -/// An account within the chain. -pub struct Account { - /// The balance of the account. - balance: OffBalance, - /// The kind of the account. - kind: AccountKind, -} - -impl Account { - /// Returns the balance of the account. - pub fn balance(&self) -> Result - where - T: Environment, - { - self.balance.decode().map_err(Into::into) - } - - /// Sets the balance of the account. - pub fn set_balance(&mut self, new_balance: T::Balance) -> Result<()> - where - T: Environment, - { - self.balance.assign(&new_balance).map_err(Into::into) - } - - /// Returns the contract account or an error if it is a user account. - fn contract_or_err(&self) -> Result<&ContractAccount> { - match &self.kind { - AccountKind::User => { - Err(AccountError::UnexpectedUserAccount).map_err(Into::into) - } - AccountKind::Contract(contract_account) => Ok(contract_account), - } - } - - /// Returns the contract account or an error if it is a user account. - fn contract_or_err_mut(&mut self) -> Result<&mut ContractAccount> { - match &mut self.kind { - AccountKind::User => { - Err(AccountError::UnexpectedUserAccount).map_err(Into::into) - } - AccountKind::Contract(contract_account) => Ok(contract_account), - } - } - - /// Sets the contract storage of key to the new value. - pub fn set_storage(&mut self, at: Key, new_value: &T) -> Result<()> - where - T: scale::Encode, - { - self.contract_or_err_mut() - .map(|contract| contract.storage.set_storage::(at, new_value)) - } - - /// Clears the contract storage at key. - pub fn clear_storage(&mut self, at: Key) -> Result<()> { - self.contract_or_err_mut() - .map(|contract| contract.storage.clear_storage(at)) - } - - /// Returns the value stored in the contract storage at the given key. - pub fn get_storage(&self, at: Key) -> Result> - where - T: scale::Decode, - { - self.contract_or_err() - .and_then(|contract| contract.storage.get_storage::(at)) - } - - /// Returns the total number of reads and write from and to the contract's storage. - pub fn get_storage_rw(&self) -> Result<(usize, usize)> { - self.contract_or_err().map(|contract| contract.get_rw()) - } - - /// Returns the amount of used storage entries. - pub fn count_used_storage_cells(&self) -> Result { - self.contract_or_err() - .map(|contract| contract.count_used_storage_cells()) - } -} - -/// The kind of the account. -/// -/// Can be either a user account or a (more complicated) contract account. -pub enum AccountKind { - User, - Contract(ContractAccount), -} - -/// Extraneous fields for contract accounts. -pub struct ContractAccount { - /// The contract storage. - pub storage: ContractStorage, -} - -impl ContractAccount { - /// Creates a new contract account. - pub fn new() -> Self - where - T: Environment, - { - Self { - storage: ContractStorage::new(), - } - } - - /// Returns the number of reads and writes from and to the contract storage. - pub fn get_rw(&self) -> (usize, usize) { - self.storage.get_rw() - } - - /// Returns the number of used storage entries. - pub fn count_used_storage_cells(&self) -> usize { - self.storage.count_used_storage_cells() - } -} - -/// The storage of a contract instance. -pub struct ContractStorage { - /// The entries within the contract storage. - entries: BTreeMap>, - /// The total number of reads to the storage. - count_reads: Cell, - /// The total number of writes to the storage. - count_writes: usize, -} - -impl ContractStorage { - /// Creates a new empty contract storage. - pub fn new() -> Self { - Self { - entries: BTreeMap::new(), - count_reads: Cell::new(0), - count_writes: 0, - } - } - - /// Returns the number of reads and writes from and to the contract storage. - pub fn get_rw(&self) -> (usize, usize) { - (self.count_reads.get(), self.count_writes) - } - - /// Returns the decoded storage at the key if any. - pub fn get_storage(&self, at: Key) -> Result> - where - T: scale::Decode, - { - self.count_reads.set(self.count_reads.get() + 1); - self.entries - .get(&at) - .map(|encoded| T::decode(&mut &encoded[..])) - .transpose() - .map_err(Into::into) - } - - /// Writes the encoded value into the contract storage at the given key. - pub fn set_storage(&mut self, at: Key, new_value: &T) - where - T: scale::Encode, - { - self.count_writes += 1; - self.entries.insert(at, new_value.encode()); - } - - /// Removes the value from storage entries at the given key. - pub fn clear_storage(&mut self, at: Key) { - self.count_writes += 1; - self.entries.remove(&at); - } - - /// Returns the number of used storage entries. - pub fn count_used_storage_cells(&self) -> usize { - self.entries.len() - } -} diff --git a/crates/env/src/engine/off_chain/db/block.rs b/crates/env/src/engine/off_chain/db/block.rs deleted file mode 100644 index d4270792d53..00000000000 --- a/crates/env/src/engine/off_chain/db/block.rs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::{ - super::{ - Result, - TypedEncoded, - }, - OffBlockNumber, - OffHash, - OffTimestamp, -}; -use crate::Environment; - -/// An emulated block in the chain. -pub struct Block { - /// The current block number. - number: OffBlockNumber, - /// The timestamp of the block. - timestamp: OffTimestamp, - /// The randomization entropy for a block. - /// - /// # Note - /// - /// - Can optionally be set for more control via - /// [`test::set_block_entropy`][`crate::test::set_block_entropy`]. - entropy: OffHash, -} - -impl Block { - /// Creates a new block for the given number and time stamp. - pub fn new(number: T::BlockNumber, timestamp: T::Timestamp) -> Self - where - T: Environment, - { - use crate::Clear; - use rand::Rng as _; - let mut entropy = ::Hash::clear(); - rand::thread_rng().fill(entropy.as_mut()); - Self { - number: TypedEncoded::new(&number), - timestamp: TypedEncoded::new(×tamp), - entropy: TypedEncoded::new(&entropy), - } - } - - /// Returns the block number. - pub fn number(&self) -> Result - where - T: Environment, - { - self.number.decode().map_err(Into::into) - } - - /// Returns the timestamp of the block. - pub fn timestamp(&self) -> Result - where - T: Environment, - { - self.timestamp.decode().map_err(Into::into) - } - - /// Sets the entropy of this block to the given entropy. - /// - /// # Note - /// - /// This is mainly used to control what [`random`][`crate::random`] returns - /// in the off-chain environment. - pub fn set_entropy(&mut self, new_entropy: T::Hash) -> Result<()> - where - T: Environment, - { - self.entropy.assign(&new_entropy).map_err(Into::into) - } - - /// Returns a randomized hash. - /// - /// # Note - /// - /// - This is the off-chain environment implementation of - /// [`random`][`crate::random`]. It provides the same behavior in that it - /// will likely yield the same hash for the same subjects within the same - /// block (or execution context). - /// - /// - Returned hashes on the surface might appear random, however for - /// testing purposes the actual implementation is quite simple and - /// computes those "random" hashes by wrapping XOR of the internal entry hash - /// with the eventually repeated sequence of the subject buffer. - pub fn random(&self, subject: &[u8]) -> Result - where - T: Environment, - { - let mut entropy = self.entropy.clone(); - let entropy_bytes = entropy.encoded_bytes_mut()?; - let len_entropy = entropy_bytes.len(); - for (n, subject) in subject.iter().enumerate() { - let id = n % len_entropy; - entropy_bytes[id] = entropy_bytes[id] ^ subject ^ (n as u8); - } - Ok(entropy.decode::()?) - } -} diff --git a/crates/env/src/engine/off_chain/db/chain_spec.rs b/crates/env/src/engine/off_chain/db/chain_spec.rs deleted file mode 100644 index 75d334eb667..00000000000 --- a/crates/env/src/engine/off_chain/db/chain_spec.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::{ - super::Result, - OffBalance, - OffTimestamp, -}; -use crate::Environment; - -/// The chain specification. -pub struct ChainSpec { - /// The current gas price. - gas_price: OffBalance, - /// The minimum value an account of the chain must have. - minimum_balance: OffBalance, - /// The targeted block time. - block_time: OffTimestamp, - /// The balance a contract needs to deposit per storage byte to stay alive indefinitely. - deposit_per_storage_byte: OffBalance, - /// The balance every contract needs to deposit to stay alive indefinitely. - deposit_per_contract: OffBalance, - /// The balance a contract needs to deposit per storage item to stay alive indefinitely. - deposit_per_storage_item: OffBalance, -} - -impl ChainSpec { - /// Creates a new uninitialized chain specification. - pub fn uninitialized() -> Self { - Self { - gas_price: OffBalance::uninitialized(), - minimum_balance: OffBalance::uninitialized(), - block_time: OffTimestamp::uninitialized(), - deposit_per_storage_byte: OffBalance::uninitialized(), - deposit_per_contract: OffBalance::uninitialized(), - deposit_per_storage_item: OffBalance::uninitialized(), - } - } - - /// Resets the chain spec to uninitialized state. - pub fn reset(&mut self) { - self.gas_price = OffBalance::uninitialized(); - self.minimum_balance = OffBalance::uninitialized(); - self.block_time = OffTimestamp::uninitialized(); - self.deposit_per_storage_byte = OffBalance::uninitialized(); - self.deposit_per_contract = OffBalance::uninitialized(); - self.deposit_per_storage_item = OffBalance::uninitialized(); - } - - /// Default initialization for the off-chain specification. - pub fn initialize_as_default(&mut self) -> crate::Result<()> - where - T: Environment, - ::AccountId: From<[u8; 32]>, - { - self.gas_price - .try_initialize::(&T::Balance::from(100u32))?; - self.minimum_balance - .try_initialize::(&T::Balance::from(42u32))?; - self.block_time - .try_initialize::(&T::Timestamp::from(5u32))?; - - let deposit_per_storage_byte = 10_000u32; - self.deposit_per_storage_byte - .try_initialize::(&T::Balance::from(deposit_per_storage_byte))?; - self.deposit_per_contract - .try_initialize::(&T::Balance::from( - 8 * deposit_per_storage_byte, - ))?; - self.deposit_per_storage_item - .try_initialize::(&T::Balance::from(10_000u32))?; - - Ok(()) - } - - /// Returns the gas price for the chain. - pub fn gas_price(&self) -> Result - where - T: Environment, - { - self.gas_price.decode().map_err(Into::into) - } - - /// Set the gas price for the chain. - pub fn set_gas_price(&mut self, gas_price: T::Balance) - where - T: Environment, - { - self.gas_price = OffBalance::new(&gas_price) - } - - /// Returns the minimum balance that is required for creating an account - /// (i.e. the chain's existential deposit). - pub fn minimum_balance(&self) -> Result - where - T: Environment, - { - self.minimum_balance.decode().map_err(Into::into) - } - - /// Returns the targeted block time for the chain. - pub fn block_time(&self) -> Result - where - T: Environment, - { - self.block_time.decode().map_err(Into::into) - } - - /// The balance a contract needs to deposit per storage byte to stay alive indefinitely. - pub fn deposit_per_storage_byte(&self) -> Result - where - T: Environment, - { - self.deposit_per_storage_byte.decode().map_err(Into::into) - } - - /// The balance every contract needs to deposit to stay alive indefinitely. - pub fn deposit_per_contract(&self) -> Result - where - T: Environment, - { - self.deposit_per_contract.decode().map_err(Into::into) - } - - /// The balance a contract needs to deposit per storage item to stay alive indefinitely. - pub fn deposit_per_storage_item(&self) -> Result - where - T: Environment, - { - self.deposit_per_storage_item.decode().map_err(Into::into) - } -} diff --git a/crates/env/src/engine/off_chain/db/debug_buf.rs b/crates/env/src/engine/off_chain/db/debug_buf.rs deleted file mode 100644 index c6436e95403..00000000000 --- a/crates/env/src/engine/off_chain/db/debug_buf.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ink_prelude::string::String; - -/// A debug buffer used to store debug messages and print them to stdout. -pub struct DebugBuffer { - /// The buffer to store the emitted debug messages. - past_debug_messages: Vec, -} - -impl DebugBuffer { - /// Creates a new empty console. - pub fn new() -> Self { - Self { - past_debug_messages: Vec::new(), - } - } - - /// Resets the debug buffer to uninitialized state. - pub fn reset(&mut self) { - self.past_debug_messages.clear(); - } - - /// Prints the message to stdout and stores it. - pub fn debug_message(&mut self, message: &str) { - self.past_debug_messages.push(message.to_string()); - print!("{}", message); - } - - /// Returns an iterator over the past debug messages. - pub fn past_debug_messages(&self) -> DebugMessages { - DebugMessages::new(self) - } -} - -/// Iterator over the past debug messages. -pub struct DebugMessages<'a> { - /// Iterator over the past debug messages. - iter: core::slice::Iter<'a, String>, -} - -impl<'a> DebugMessages<'a> { - /// Creates a new iterator over the past debug messages. - fn new(console: &'a DebugBuffer) -> Self { - Self { - iter: console.past_debug_messages.iter(), - } - } -} - -impl<'a> Iterator for DebugMessages<'a> { - type Item = &'a str; - - fn next(&mut self) -> Option { - self.iter.next().map(AsRef::as_ref) - } -} - -impl<'a> ExactSizeIterator for DebugMessages<'a> { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl<'a> DoubleEndedIterator for DebugMessages<'a> { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(AsRef::as_ref) - } -} diff --git a/crates/env/src/engine/off_chain/db/events.rs b/crates/env/src/engine/off_chain/db/events.rs deleted file mode 100644 index 8916316051d..00000000000 --- a/crates/env/src/engine/off_chain/db/events.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::super::OffHash; -use crate::{ - hash::{ - Blake2x256, - CryptoHash, - HashOutput, - }, - topics::{ - Topics, - TopicsBuilderBackend, - }, - Clear, - Environment, -}; - -#[derive(Default)] -pub struct TopicsBuilder { - topics: Vec, -} - -impl TopicsBuilderBackend for TopicsBuilder -where - E: Environment, -{ - type Output = Vec; - - fn expect(&mut self, _expected_topics: usize) {} - - fn push_topic(&mut self, topic_value: &T) - where - T: scale::Encode, - { - let encoded = topic_value.encode(); - let len_encoded = encoded.len(); - let mut result = ::Hash::clear(); - let len_result = result.as_ref().len(); - if len_encoded <= len_result { - result.as_mut()[..len_encoded].copy_from_slice(&encoded[..]); - } else { - let mut hash_output = ::Type::default(); - ::hash(&encoded[..], &mut hash_output); - let copy_len = core::cmp::min(hash_output.len(), len_result); - result.as_mut()[0..copy_len].copy_from_slice(&hash_output[0..copy_len]); - } - let off_hash = OffHash::new(&result); - debug_assert!( - !self.topics.contains(&off_hash), - "duplicate topic hash discovered!" - ); - self.topics.push(off_hash); - } - - fn output(self) -> Self::Output { - self.topics - } -} - -/// Record for an emitted event. -#[derive(Debug, Clone)] -pub struct EmittedEvent { - /// Recorded topics of the emitted event. - pub topics: Vec, - /// Recorded encoding of the emitted event. - pub data: Vec, -} - -impl EmittedEvent { - /// Creates a new emitted event. - pub fn new(emitted_event: E) -> Self - where - T: Environment, - E: Topics + scale::Encode, - { - let topics = emitted_event.topics::(TopicsBuilder::default().into()); - Self { - topics, - data: emitted_event.encode(), - } - } -} - -/// Records all emitted events for later inspection. -pub struct EmittedEventsRecorder { - emitted_events: Vec, -} - -impl EmittedEventsRecorder { - /// Creates a new empty emitted event recorder. - pub fn new() -> Self { - Self { - emitted_events: Vec::new(), - } - } - - /// Resets the emitted events to none. - pub fn reset(&mut self) { - self.emitted_events.clear(); - } - - /// Records a new emitted event. - pub fn record(&mut self, new_event: E) - where - T: Environment, - E: Topics + scale::Encode, - { - self.emitted_events - .push(EmittedEvent::new::(new_event)); - } - - /// Returns an iterator over the emitted events in their emission order. - pub fn emitted_events(&self) -> core::slice::Iter { - self.emitted_events.iter() - } -} diff --git a/crates/env/src/engine/off_chain/db/exec_context.rs b/crates/env/src/engine/off_chain/db/exec_context.rs deleted file mode 100644 index d6c5afffd52..00000000000 --- a/crates/env/src/engine/off_chain/db/exec_context.rs +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::{ - super::{ - CallData, - Result, - TypedEncoded, - }, - OffAccountId, - OffBalance, -}; -use crate::Environment; -use ink_prelude::vec::Vec; - -pub type Bytes = Vec; - -/// The context of a contract execution. -pub struct ExecContext { - /// The caller of the contract execution. - /// - /// Might be user or another contract. - pub caller: OffAccountId, - /// The callee of the contract execution. - pub callee: OffAccountId, - /// The transferred value from caller to callee. - pub transferred_value: OffBalance, - /// The gas provided for the whole execution. - pub gas: u64, - /// The inputs provided for the whole execution. - /// - /// # Note - /// - /// This includes selector and encoded arguments. - pub call_data: CallData, - /// The output of the contract execution. - pub output: Option, -} - -impl ExecContext { - /// Constructs a new execution context. - pub fn build() -> ExecContextBuilder - where - T: Environment, - { - ExecContextBuilder::new() - } - - /// Returns the caller. - pub fn caller(&self) -> Result - where - T: Environment, - { - self.caller.decode().map_err(Into::into) - } - - /// Returns the callee. - pub fn callee(&self) -> Result - where - T: Environment, - { - self.callee.decode().map_err(Into::into) - } - - /// Returns the transferred value. - pub fn transferred_value(&self) -> Result - where - T: Environment, - { - self.transferred_value.decode().map_err(Into::into) - } - - /// Returns the gas. - pub fn gas(&self) -> u64 - where - T: Environment, - { - self.gas - } - - /// Returns the call data. - #[allow( - dead_code, - // Needed as soon as we support to execute contracts - // directly through the off-chain environment. - )] - pub fn call_data(&self) -> &CallData { - &self.call_data - } - - /// Returns the contract execution output. - #[allow( - dead_code, - // Needed as soon as we support to execute contracts - // directly through the off-chain environment. - )] - pub fn output(&self) -> Option<&Bytes> { - self.output.as_ref() - } -} - -/// Builder for execution contexts. -pub struct ExecContextBuilder -where - T: Environment, -{ - /// The caller of the newly created execution context. - caller: Option, - /// The callee of the newly created execution context. - callee: Option, - /// The transferred value from caller to callee. - transferred_value: Option, - /// The gas provided for the contract execution from caller to callee. - gas: Option, - /// The inputs given to the contract execution. - call_data: Option, -} - -impl ExecContextBuilder -where - T: Environment, -{ - /// Constructs a new execution context builder. - pub fn new() -> Self { - Self { - caller: None, - callee: None, - transferred_value: None, - gas: None, - call_data: None, - } - } - - /// Sets caller of the execution context. - /// - /// # Panics - /// - /// If there has already been set a caller. - pub fn caller(mut self, caller: T::AccountId) -> Self { - assert!(self.caller.is_none(), "already has a caller"); - self.caller = Some(caller); - self - } - - /// Sets callee of the execution context. - /// - /// # Panics - /// - /// If there has already been set a callee. - pub fn callee(mut self, callee: T::AccountId) -> Self { - assert!(self.callee.is_none(), "already has a callee"); - self.callee = Some(callee); - self - } - - /// Sets the provided gas for the execution. - /// - /// # Panics - /// - /// If there has already been set provided gas. - pub fn gas(mut self, gas: u64) -> Self { - assert!(self.gas.is_none(), "already has provided gas"); - self.gas = Some(gas); - self - } - - /// Sets the transferred value (endowment) for the execution. - /// - /// # Panics - /// - /// If there has already been set transferred value (endowment). - pub fn transferred_value(mut self, transferred_value: T::Balance) -> Self { - assert!( - self.transferred_value.is_none(), - "already has set transferred value (endowment)" - ); - self.transferred_value = Some(transferred_value); - self - } - - /// Sets the call data for the execution. - /// - /// # Panics - /// - /// If there has already been set call data. - pub fn call_data(mut self, call_data: CallData) -> Self { - assert!(self.call_data.is_none(), "already has set call data"); - self.call_data = Some(call_data); - self - } - - /// Finishes construction of execution context. - /// - /// # Panics - /// - /// If any parameter has not yet been set. - pub fn finish(self) -> ExecContext { - let caller = self.caller.expect("need a valid caller at this point"); - let callee = self.callee.expect("need a valid callee at this point"); - let transferred_value = self - .transferred_value - .expect("need a valid transferred value (endowment) at this point"); - let gas = self.gas.expect("need valid provided gas at this point"); - ExecContext { - caller: TypedEncoded::new(&caller), - callee: TypedEncoded::new(&callee), - transferred_value: TypedEncoded::new(&transferred_value), - gas, - call_data: self.call_data.unwrap(), - output: None, - } - } -} diff --git a/crates/env/src/engine/off_chain/db/mod.rs b/crates/env/src/engine/off_chain/db/mod.rs deleted file mode 100644 index c9231fb97dc..00000000000 --- a/crates/env/src/engine/off_chain/db/mod.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod accounts; -mod block; -mod chain_spec; -mod debug_buf; -mod events; -mod exec_context; - -pub use self::{ - accounts::{ - Account, - AccountError, - AccountKind, - AccountsDb, - ContractAccount, - ContractStorage, - }, - block::Block, - chain_spec::ChainSpec, - debug_buf::{ - DebugBuffer, - DebugMessages, - }, - events::{ - EmittedEvent, - EmittedEventsRecorder, - }, - exec_context::ExecContext, -}; -use super::{ - OffAccountId, - OffBalance, - OffBlockNumber, - OffHash, - OffTimestamp, -}; diff --git a/crates/env/src/engine/off_chain/hashing.rs b/crates/env/src/engine/off_chain/hashing.rs deleted file mode 100644 index 7f42d42bd81..00000000000 --- a/crates/env/src/engine/off_chain/hashing.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Implementations of supported cryptographic hash functions. - -/// Conduct the BLAKE-2 256-bit hash and place the result into `output`. -pub fn blake2b_256(input: &[u8], output: &mut [u8; 32]) { - use ::blake2::digest::{ - consts::U32, - Digest as _, - }; - - type Blake2b128 = ::blake2::Blake2b; - - let mut blake2 = Blake2b128::new(); - blake2.update(input); - let result = blake2.finalize(); - output.copy_from_slice(&result); -} - -/// Conduct the BLAKE-2 128-bit hash and place the result into `output`. -pub fn blake2b_128(input: &[u8], output: &mut [u8; 16]) { - use ::blake2::digest::{ - consts::U16, - Digest as _, - }; - - type Blake2b128 = ::blake2::Blake2b; - - let mut blake2 = Blake2b128::new(); - blake2.update(input); - let result = blake2.finalize(); - output.copy_from_slice(&result); -} - -/// Conduct the KECCAK 256-bit hash and place the result into `output`. -pub fn keccak_256(input: &[u8], output: &mut [u8; 32]) { - use ::sha3::{ - digest::generic_array::GenericArray, - Digest as _, - }; - let mut hasher = ::sha3::Keccak256::new(); - hasher.update(input); - hasher.finalize_into(<&mut GenericArray>::from(&mut output[..])); -} - -/// Conduct the SHA-2 256-bit hash and place the result into `output`. -pub fn sha2_256(input: &[u8], output: &mut [u8; 32]) { - use ::sha2::{ - digest::generic_array::GenericArray, - Digest as _, - }; - let mut hasher = ::sha2::Sha256::new(); - hasher.update(input); - hasher.finalize_into(<&mut GenericArray>::from(&mut output[..])); -} diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 625ddd2e72c..c32f53962be 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -12,11 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{ - hashing, - Account, - EnvInstance, -}; +use super::EnvInstance; use crate::{ call::{ utils::ReturnType, @@ -31,7 +27,11 @@ use crate::{ Keccak256, Sha2x256, }, - topics::Topics, + topics::{ + Topics, + TopicsBuilderBackend, + }, + Clear, EnvBackend, Environment, Error, @@ -39,38 +39,16 @@ use crate::{ ReturnFlags, TypedEnvBackend, }; -use core::convert::TryInto; +use ink_engine::{ + ext, + ext::Engine, +}; use ink_primitives::Key; -use num_traits::Bounded; -const UNINITIALIZED_EXEC_CONTEXT: &str = "uninitialized execution context: \ -a possible source of error could be that you are using `#[test]` instead of `#[ink::test]`."; - -impl EnvInstance { - /// Returns the callee account. - fn callee_account(&self) -> &Account { - let callee = self - .exec_context() - .expect(UNINITIALIZED_EXEC_CONTEXT) - .callee - .clone(); - self.accounts - .get_account_off(&callee) - .expect("callee account does not exist") - } - - /// Returns the callee account as mutable reference. - fn callee_account_mut(&mut self) -> &mut Account { - let callee = self - .exec_context() - .expect(UNINITIALIZED_EXEC_CONTEXT) - .callee - .clone(); - self.accounts - .get_account_off_mut(&callee) - .expect("callee account does not exist") - } -} +/// The capacity of the static buffer. +/// This is the same size as the ink! on-chain environment. We chose to use the same size +/// to be as close to the on-chain behavior as possible. +const BUFFER_SIZE: usize = 1 << 14; // 16 kB impl CryptoHash for Blake2x128 { fn hash(input: &[u8], output: &mut ::Type) { @@ -80,7 +58,7 @@ impl CryptoHash for Blake2x128 { OutputType ); let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 16); - hashing::blake2b_128(input, output); + Engine::hash_blake2_128(input, output); } } @@ -92,7 +70,7 @@ impl CryptoHash for Blake2x256 { OutputType ); let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32); - hashing::blake2b_256(input, output); + Engine::hash_blake2_256(input, output); } } @@ -104,7 +82,7 @@ impl CryptoHash for Sha2x256 { OutputType ); let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32); - hashing::sha2_256(input, output); + Engine::hash_sha2_256(input, output); } } @@ -116,7 +94,91 @@ impl CryptoHash for Keccak256 { OutputType ); let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32); - hashing::keccak_256(input, output); + Engine::hash_keccak_256(input, output); + } +} + +impl From for crate::Error { + fn from(ext_error: ext::Error) -> Self { + match ext_error { + ext::Error::Unknown => Self::Unknown, + ext::Error::CalleeTrapped => Self::CalleeTrapped, + ext::Error::CalleeReverted => Self::CalleeReverted, + ext::Error::KeyNotFound => Self::KeyNotFound, + ext::Error::_BelowSubsistenceThreshold => Self::_BelowSubsistenceThreshold, + ext::Error::TransferFailed => Self::TransferFailed, + ext::Error::_EndowmentTooLow => Self::_EndowmentTooLow, + ext::Error::CodeNotFound => Self::CodeNotFound, + ext::Error::NotCallable => Self::NotCallable, + ext::Error::LoggingDisabled => Self::LoggingDisabled, + ext::Error::EcdsaRecoveryFailed => Self::EcdsaRecoveryFailed, + } + } +} + +#[derive(Default)] +pub struct TopicsBuilder { + pub topics: Vec>, +} + +impl TopicsBuilderBackend for TopicsBuilder +where + E: Environment, +{ + type Output = Vec; + + fn expect(&mut self, _expected_topics: usize) {} + + fn push_topic(&mut self, topic_value: &T) + where + T: scale::Encode, + { + let encoded = topic_value.encode(); + let len_encoded = encoded.len(); + let mut result = ::Hash::clear(); + let len_result = result.as_ref().len(); + if len_encoded <= len_result { + result.as_mut()[..len_encoded].copy_from_slice(&encoded[..]); + } else { + let mut hash_output = ::Type::default(); + ::hash(&encoded[..], &mut hash_output); + let copy_len = core::cmp::min(hash_output.len(), len_result); + result.as_mut()[0..copy_len].copy_from_slice(&hash_output[0..copy_len]); + } + let off_hash = result.as_ref(); + let off_hash = off_hash.to_vec(); + debug_assert!( + !self.topics.contains(&off_hash), + "duplicate topic hash discovered!" + ); + self.topics.push(off_hash); + } + + fn output(self) -> Self::Output { + let mut all: Vec = Vec::new(); + + let topics_len_compact = &scale::Compact(self.topics.len() as u32); + let topics_encoded = &scale::Encode::encode(&topics_len_compact)[..]; + all.append(&mut topics_encoded.to_vec()); + + self.topics.into_iter().for_each(|mut v| all.append(&mut v)); + all + } +} + +impl EnvInstance { + /// Returns the contract property value. + fn get_property( + &mut self, + ext_fn: fn(engine: &Engine, output: &mut &mut [u8]), + ) -> Result + where + T: scale::Decode, + { + let mut full_scope: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; + let full_scope = &mut &mut full_scope[..]; + ext_fn(&self.engine, full_scope); + scale::Decode::decode(&mut &full_scope[..]).map_err(Into::into) } } @@ -125,54 +187,44 @@ impl EnvBackend for EnvInstance { where V: scale::Encode, { - self.callee_account_mut() - .set_storage(*key, value) - .expect("callee account is not a smart contract"); + let v = scale::Encode::encode(value); + self.engine.set_storage(key.as_ref(), &v[..]); } fn get_contract_storage(&mut self, key: &Key) -> Result> where R: scale::Decode, { - self.callee_account() - .get_storage::(*key) - .map_err(Into::into) + let mut output: [u8; 9600] = [0; 9600]; + match self.engine.get_storage(key.as_ref(), &mut &mut output[..]) { + Ok(_) => (), + Err(ext::Error::KeyNotFound) => return Ok(None), + Err(_) => panic!("encountered unexpected error"), + } + let decoded = scale::Decode::decode(&mut &output[..])?; + Ok(Some(decoded)) } fn clear_contract_storage(&mut self, key: &Key) { - if !self.clear_storage_disabled { - self.callee_account_mut() - .clear_storage(*key) - .expect("callee account is not a smart contract"); - } + self.engine.clear_storage(key.as_ref()) } fn decode_input(&mut self) -> Result where T: scale::Decode, { - self.exec_context() - .map(|exec_ctx| &exec_ctx.call_data) - .map(scale::Encode::encode) - .map_err(Into::into) - .and_then(|encoded| { - ::decode(&mut &encoded[..]) - .map_err(|_| scale::Error::from("could not decode input call data")) - .map_err(Into::into) - }) + unimplemented!("the off-chain testing envoronment does not implement `seal_input`") } - fn return_value(&mut self, flags: ReturnFlags, return_value: &R) -> ! + fn return_value(&mut self, _flags: ReturnFlags, _return_value: &R) -> ! where R: scale::Encode, { - let ctx = self.exec_context_mut().expect(UNINITIALIZED_EXEC_CONTEXT); - ctx.output = Some(return_value.encode()); - std::process::exit(flags.into_u32() as i32) + unimplemented!("the off-chain testing envoronment does not implement `seal_return_value`") } fn debug_message(&mut self, message: &str) { - self.debug_buf.debug_message(message) + self.engine.debug_message(message) } fn hash_bytes(&mut self, input: &[u8], output: &mut ::Type) @@ -187,8 +239,8 @@ impl EnvBackend for EnvInstance { H: CryptoHash, T: scale::Encode, { - let encoded = input.encode(); - self.hash_bytes::(&encoded[..], output) + let enc_input = &scale::Encode::encode(input)[..]; + ::hash(enc_input, output) } fn ecdsa_recover( @@ -248,163 +300,85 @@ impl EnvBackend for EnvInstance { F: FnOnce(u32) -> ::core::result::Result<(), ErrorCode>, D: FnOnce(&[u8]) -> ::core::result::Result, { - let encoded_input = input.encode(); - let (status_code, output) = self - .chain_extension_handler - .eval(func_id, &encoded_input) - .expect("encountered unexpected missing chain extension method"); - status_to_result(status_code)?; - let decoded = decode_to_result(output)?; + let enc_input = &scale::Encode::encode(input)[..]; + let mut output: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; + + status_to_result(self.engine.call_chain_extension( + func_id, + enc_input, + &mut &mut output[..], + ))?; + let decoded = decode_to_result(&output[..])?; Ok(decoded) } } -impl EnvInstance { - fn transfer_impl( - &mut self, - destination: &T::AccountId, - value: T::Balance, - ) -> Result<()> - where - T: Environment, - { - let src_id = self.account_id::(); - let src_value = self - .accounts - .get_account::(&src_id) - .expect("account of executed contract must exist") - .balance::()?; - if src_value < value { - return Err(Error::TransferFailed) - } - let dst_value = self - .accounts - .get_or_create_account::(destination) - .balance::()?; - self.accounts - .get_account_mut::(&src_id) - .expect("account of executed contract must exist") - .set_balance::(src_value - value)?; - self.accounts - .get_account_mut::(destination) - .expect("the account must exist already or has just been created") - .set_balance::(dst_value + value)?; - Ok(()) - } - - // Remove the calling account and transfer remaining balance. - // - // This function never returns. Either the termination was successful and the - // execution of the destroyed contract is halted. Or it failed during the termination - // which is considered fatal. - fn terminate_contract_impl(&mut self, beneficiary: T::AccountId) -> ! - where - T: Environment, - { - // Send the remaining balance to the beneficiary - let all: T::Balance = self.balance::(); - self.transfer_impl::(&beneficiary, all) - .expect("transfer did not work "); - - // Remove account - let contract_id = self.account_id::(); - self.accounts.remove_account::(contract_id); - - // Encode the result of the termination and panic with it. - // This enables testing for the proper result and makes sure this - // method returns `Never`. - let res = crate::test::ContractTerminationResult:: { - beneficiary, - transferred: all, - }; - std::panic::panic_any(scale::Encode::encode(&res)); - } -} - impl TypedEnvBackend for EnvInstance { fn caller(&mut self) -> T::AccountId { - self.exec_context() - .expect(UNINITIALIZED_EXEC_CONTEXT) - .caller::() + self.get_property::(Engine::caller) .unwrap_or_else(|error| { panic!("could not read `caller` property: {:?}", error) }) } fn transferred_value(&mut self) -> T::Balance { - self.exec_context() - .expect(UNINITIALIZED_EXEC_CONTEXT) - .transferred_value::() + self.get_property::(Engine::value_transferred) .unwrap_or_else(|error| { panic!("could not read `transferred_value` property: {:?}", error) }) } - /// Emulates gas price calculation - fn weight_to_fee(&mut self, gas: u64) -> T::Balance { - use crate::arithmetic::Saturating as _; - - let gas_price = self.chain_spec.gas_price::().unwrap_or_else(|error| { - panic!("could not read `gas_price` property: {:?}", error) - }); - gas_price.saturating_mul(gas.try_into().unwrap_or_else(|_| Bounded::max_value())) - } - fn gas_left(&mut self) -> u64 { - self.exec_context() - .expect(UNINITIALIZED_EXEC_CONTEXT) - .gas::() + self.get_property::(Engine::gas_left) + .unwrap_or_else(|error| { + panic!("could not read `gas_left` property: {:?}", error) + }) } fn block_timestamp(&mut self) -> T::Timestamp { - self.current_block() - .expect(UNINITIALIZED_EXEC_CONTEXT) - .timestamp::() + self.get_property::(Engine::block_timestamp) .unwrap_or_else(|error| { panic!("could not read `block_timestamp` property: {:?}", error) }) } fn account_id(&mut self) -> T::AccountId { - self.exec_context() - .expect(UNINITIALIZED_EXEC_CONTEXT) - .callee::() + self.get_property::(Engine::address) .unwrap_or_else(|error| { panic!("could not read `account_id` property: {:?}", error) }) } fn balance(&mut self) -> T::Balance { - self.callee_account() - .balance::() + self.get_property::(Engine::balance) .unwrap_or_else(|error| { panic!("could not read `balance` property: {:?}", error) }) } fn block_number(&mut self) -> T::BlockNumber { - self.current_block() - .expect(UNINITIALIZED_EXEC_CONTEXT) - .number::() + self.get_property::(Engine::block_number) .unwrap_or_else(|error| { panic!("could not read `block_number` property: {:?}", error) }) } fn minimum_balance(&mut self) -> T::Balance { - self.chain_spec - .minimum_balance::() + self.get_property::(Engine::minimum_balance) .unwrap_or_else(|error| { panic!("could not read `minimum_balance` property: {:?}", error) }) } - fn emit_event(&mut self, new_event: Event) + fn emit_event(&mut self, event: Event) where T: Environment, Event: Topics + scale::Encode, { - self.emitted_events.record::(new_event) + let builder = TopicsBuilder::default(); + let enc_topics = event.topics::(builder.into()); + let enc_data = &scale::Encode::encode(&event)[..]; + self.engine.deposit_event(&enc_topics[..], enc_data); } fn invoke_contract(&mut self, params: &CallParams) -> Result<()> @@ -453,21 +427,35 @@ impl TypedEnvBackend for EnvInstance { where T: Environment, { - self.terminate_contract_impl::(beneficiary) + let buffer = scale::Encode::encode(&beneficiary); + self.engine.terminate(&buffer[..]) } fn transfer(&mut self, destination: T::AccountId, value: T::Balance) -> Result<()> where T: Environment, { - self.transfer_impl::(&destination, value) + let enc_destination = &scale::Encode::encode(&destination)[..]; + let enc_value = &scale::Encode::encode(&value)[..]; + self.engine + .transfer(enc_destination, enc_value) + .map_err(Into::into) + } + + fn weight_to_fee(&mut self, gas: u64) -> T::Balance { + let mut output: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; + self.engine.weight_to_fee(gas, &mut &mut output[..]); + scale::Decode::decode(&mut &output[..]).unwrap_or_else(|error| { + panic!("could not read `weight_to_fee` property: {:?}", error) + }) } fn random(&mut self, subject: &[u8]) -> Result<(T::Hash, T::BlockNumber)> where T: Environment, { - let block = self.current_block().expect(UNINITIALIZED_EXEC_CONTEXT); - Ok((block.random::(subject)?, block.number::()?)) + let mut output: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; + self.engine.random(subject, &mut &mut output[..]); + scale::Decode::decode(&mut &output[..]).map_err(Into::into) } } diff --git a/crates/env/src/engine/off_chain/mod.rs b/crates/env/src/engine/off_chain/mod.rs index c962239fc44..bddc3044d2c 100644 --- a/crates/env/src/engine/off_chain/mod.rs +++ b/crates/env/src/engine/off_chain/mod.rs @@ -12,248 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod call_data; -mod chain_extension; -mod db; -mod hashing; mod impls; pub mod test_api; -mod typed_encoded; mod types; #[cfg(test)] mod tests; -pub use self::{ - call_data::CallData, - db::{ - AccountError, - DebugMessages, - EmittedEvent, - }, - typed_encoded::TypedEncodedError, -}; -use self::{ - chain_extension::ChainExtensionHandler, - db::{ - Account, - AccountsDb, - Block, - ChainSpec, - DebugBuffer, - EmittedEventsRecorder, - ExecContext, - }, - typed_encoded::TypedEncoded, - types::{ - OffAccountId, - OffBalance, - OffBlockNumber, - OffHash, - OffTimestamp, - }, -}; use super::OnInstance; -use crate::Environment; -use core::cell::RefCell; -use derive_more::From; - -#[derive(Debug, From, PartialEq, Eq)] -pub enum OffChainError { - Account(AccountError), - TypedEncoded(TypedEncodedError), - #[from(ignore)] - UninitializedBlocks, - #[from(ignore)] - UninitializedExecutionContext, - #[from(ignore)] - UnregisteredChainExtension, -} +use crate::Error; -pub type Result = core::result::Result; +use derive_more::From; +use ink_engine::ext::Engine; /// The off-chain environment. -/// -/// Mainly used for off-chain testing. pub struct EnvInstance { - /// The accounts database of the environment. - accounts: AccountsDb, - /// Current execution context and context. - exec_context: Vec, - /// The general chain spec. - chain_spec: ChainSpec, - /// The blocks of the chain. - blocks: Vec, - /// The debug buffer to collect debug messages and print them to stdout. - debug_buf: DebugBuffer, - /// Handler for registered chain extensions. - chain_extension_handler: ChainExtensionHandler, - /// Emitted events recorder. - emitted_events: EmittedEventsRecorder, - /// Set to true to disable clearing storage - clear_storage_disabled: bool, -} - -impl EnvInstance { - /// Creates a new uninitialized off-chain environment. - pub fn uninitialized() -> Self { - Self { - accounts: AccountsDb::new(), - exec_context: Vec::new(), - chain_spec: ChainSpec::uninitialized(), - blocks: Vec::new(), - debug_buf: DebugBuffer::new(), - chain_extension_handler: ChainExtensionHandler::new(), - emitted_events: EmittedEventsRecorder::new(), - clear_storage_disabled: false, - } - } - - /// Returns `true` if the off-chain environment is uninitialized. - pub fn is_initialized(&self) -> bool { - !self.exec_context.is_empty() - } - - /// Either resets or initializes the off-chain environment to default values. - pub fn initialize_or_reset_as_default(&mut self) -> crate::Result<()> - where - T: Environment, - ::AccountId: From<[u8; 32]>, - { - if self.is_initialized() { - self.reset() - } - self.initialize_as_default::()?; - Ok(()) - } - - /// Resets the off-chain environment to uninitialized state. - pub fn reset(&mut self) { - self.accounts.reset(); - self.exec_context.clear(); - self.chain_spec.reset(); - self.blocks.clear(); - self.debug_buf.reset(); - self.chain_extension_handler.reset(); - self.emitted_events.reset(); - self.clear_storage_disabled = false; - } - - /// Initializes the whole off-chain environment. - /// - /// # Note - /// - /// This is needed since we operate on a static instance that cannot be - /// made generic over all environmental types and thus we are required to - /// initialize it upon program start uninitialized which is why we have - /// `TypedEncoded` wrappers. - /// - /// The off-chain environment requires to be initialized before every usage. - /// - /// This routine implements a default initialization that should be fine - /// for most use cases. - pub fn initialize_as_default(&mut self) -> crate::Result<()> - where - T: Environment, - ::AccountId: From<[u8; 32]>, - { - use core::ops::Div as _; - use num_traits::{ - Bounded as _, - Zero as _, - }; - let default_accounts = test_api::default_accounts::()?; - // Alice has half of the maximum possible amount. - self.accounts.add_user_account::( - default_accounts.alice.clone(), - T::Balance::max_value().div(T::Balance::from(2u32)), - ); - // Bob has half the balance that alice got. - self.accounts.add_user_account::( - default_accounts.bob, - T::Balance::max_value().div(T::Balance::from(4u32)), - ); - // All other default accounts have zero balance. - self.accounts - .add_user_account::(default_accounts.charlie, T::Balance::zero()); - self.accounts - .add_user_account::(default_accounts.django, T::Balance::zero()); - self.accounts - .add_user_account::(default_accounts.eve, T::Balance::zero()); - self.accounts - .add_user_account::(default_accounts.frank, T::Balance::zero()); - // Initialize our first block. - self.blocks.push(Block::new::( - T::BlockNumber::from(0u32), - T::Timestamp::from(0u32), - )); - // Initialize chain specification. - self.chain_spec.initialize_as_default::()?; - // Initialize the called contract account. - let contract_account_id = T::AccountId::from([0x07; 32]); - self.accounts.add_contract_account::( - contract_account_id.clone(), - T::Balance::from(0u32), - ); - // Initialize the execution context for the first contract execution. - use crate::call::Selector; - // The below selector bytes are incorrect but since calling does not work - // yet we do not have to fix this now. - let selector_bytes_for_call = [0x00; 4]; - self.exec_context.push( - ExecContext::build::() - .caller(default_accounts.alice) - .callee(contract_account_id) - .gas(500_000u64) - .transferred_value(T::Balance::from(500u32)) - .call_data(CallData::new(Selector::new(selector_bytes_for_call))) - .finish(), - ); - Ok(()) - } - - /// Advances the chain by a single block. - pub fn advance_block(&mut self) -> crate::Result<()> - where - T: Environment, - { - let new_block_number = T::BlockNumber::from(self.blocks.len() as u32); - let new_timestamp = self.current_block()?.timestamp::()? - + self.chain_spec.block_time::()?; - self.blocks - .push(Block::new::(new_block_number, new_timestamp)); - Ok(()) - } - - /// Returns the current execution context. - fn exec_context(&self) -> Result<&ExecContext> { - self.exec_context - .last() - .ok_or(OffChainError::UninitializedExecutionContext) - } - - /// Returns the current execution context. - fn exec_context_mut(&mut self) -> Result<&mut ExecContext> { - self.exec_context - .last_mut() - .ok_or(OffChainError::UninitializedExecutionContext) - } - - /// Returns the current block of the chain. - fn current_block(&self) -> Result<&Block> { - self.blocks.last().ok_or(OffChainError::UninitializedBlocks) - } - - /// Returns a mutable reference to the current block of the chain. - fn current_block_mut(&mut self) -> Result<&mut Block> { - self.blocks - .last_mut() - .ok_or(OffChainError::UninitializedBlocks) - } - - fn chain_spec_mut(&mut self) -> &mut ChainSpec { - &mut self.chain_spec - } + engine: Engine, } impl OnInstance for EnvInstance { @@ -261,11 +35,35 @@ impl OnInstance for EnvInstance { where F: FnOnce(&mut Self) -> R, { + use core::cell::RefCell; thread_local!( static INSTANCE: RefCell = RefCell::new( - EnvInstance::uninitialized() + EnvInstance { + engine: Engine::new() + } ) ); INSTANCE.with(|instance| f(&mut instance.borrow_mut())) } } + +#[derive(Debug, From, PartialEq, Eq)] +pub enum OffChainError { + Account(AccountError), + #[from(ignore)] + UninitializedBlocks, + #[from(ignore)] + UninitializedExecutionContext, + #[from(ignore)] + UnregisteredChainExtension, +} + +/// Errors encountered upon interacting with the accounts database. +#[derive(Debug, From, PartialEq, Eq)] +pub enum AccountError { + Decoding(scale::Error), + #[from(ignore)] + UnexpectedUserAccount, + #[from(ignore)] + NoAccountForId(Vec), +} diff --git a/crates/env/src/engine/off_chain/test_api.rs b/crates/env/src/engine/off_chain/test_api.rs index 0b5584d6782..5155f1b7631 100644 --- a/crates/env/src/engine/off_chain/test_api.rs +++ b/crates/env/src/engine/off_chain/test_api.rs @@ -14,15 +14,7 @@ //! Operations on the off-chain testing environment. -pub use super::{ - chain_extension::ChainExtension, - db::ChainSpec, - CallData, - EmittedEvent, -}; use super::{ - db::ExecContext, - AccountError, EnvInstance, OnInstance, }; @@ -30,49 +22,17 @@ use crate::{ Environment, Result, }; -use ink_prelude::string::String; +use core::fmt::Debug; +use ink_engine::test_api::RecordedDebugMessages; use std::panic::UnwindSafe; -/// Pushes a contract execution context. -/// -/// This is the data behind a single instance of a contract call. -/// -/// # Note -/// -/// Together with [`pop_execution_context`] this can be used to emulated -/// nested calls. -pub fn push_execution_context( - caller: T::AccountId, - callee: T::AccountId, - gas_limit: u64, - endowment: T::Balance, - call_data: CallData, -) where - T: Environment, -{ - ::on_instance(|instance| { - instance.exec_context.push( - ExecContext::build::() - .caller(caller) - .callee(callee) - .gas(gas_limit) - .transferred_value(endowment) - .call_data(call_data) - .finish(), - ) - }) -} - -/// Pops the top contract execution context. -/// -/// # Note -/// -/// Together with [`push_execution_context`] this can be used to emulated -/// nested calls. -pub fn pop_execution_context() { - ::on_instance(|instance| { - instance.exec_context.pop(); - }) +/// Record for an emitted event. +#[derive(Clone)] +pub struct EmittedEvent { + /// Recorded topics of the emitted event. + pub topics: Vec>, + /// Recorded encoding of the emitted event. + pub data: Vec, } /// Sets the balance of the account to the given balance. @@ -87,20 +47,14 @@ pub fn pop_execution_context() { /// - If `account` does not exist. /// - If the underlying `account` type does not match. /// - If the underlying `new_balance` type does not match. -pub fn set_account_balance( - account_id: T::AccountId, - new_balance: T::Balance, -) -> Result<()> +pub fn set_account_balance(account_id: T::AccountId, new_balance: T::Balance) where - T: Environment, + T: Environment, // Just temporary for the MVP! { ::on_instance(|instance| { instance - .accounts - .get_account_mut::(&account_id) - .ok_or_else(|| AccountError::no_account_for_id::(&account_id)) - .map_err(Into::into) - .and_then(|account| account.set_balance::(new_balance).map_err(Into::into)) + .engine + .set_balance(scale::Encode::encode(&account_id), new_balance); }) } @@ -110,7 +64,7 @@ where /// /// Note that account could refer to either a user account or /// a smart contract account. This returns the same as `env::api::balance` -/// if given the account ID of the currently executed smart contract. +/// if given the account id of the currently executed smart contract. /// /// # Errors /// @@ -118,27 +72,13 @@ where /// - If the underlying `account` type does not match. pub fn get_account_balance(account_id: T::AccountId) -> Result where - T: Environment, + T: Environment, // Just temporary for the MVP! { ::on_instance(|instance| { instance - .accounts - .get_account::(&account_id) - .ok_or_else(|| AccountError::no_account_for_id::(&account_id)) + .engine + .get_balance(scale::Encode::encode(&account_id)) .map_err(Into::into) - .and_then(|account| account.balance::().map_err(Into::into)) - }) -} - -/// Registers a new chain extension. -pub fn register_chain_extension(extension: E) -where - E: ChainExtension + 'static, -{ - ::on_instance(|instance| { - instance - .chain_extension_handler - .register(Box::new(extension)); }) } @@ -147,156 +87,99 @@ where /// # Note /// /// This allows to control what [`random`][`crate::random`] returns. -pub fn set_block_entropy(entropy: T::Hash) -> Result<()> +pub fn set_block_entropy(_entropy: T::Hash) -> Result<()> where T: Environment, { - ::on_instance(|instance| { - instance.current_block_mut()?.set_entropy::(entropy) - }) - .map_err(Into::into) -} - -/// Update the [`ChainSpec`](`crate::test::ChainSpec`) for the test environment -pub fn update_chain_spec(f: F) -> Result<()> -where - F: FnOnce(&mut ChainSpec), -{ - ::on_instance(|instance| f(instance.chain_spec_mut())); - Ok(()) + unimplemented!("off-chain environment does not yet support `set_block_entropy`"); } /// Returns the contents of the past performed environmental debug messages in order. -pub fn recorded_debug_messages() -> impl Iterator { - ::on_instance(|instance| { - // We return a clone of the recorded strings instead of - // references to them since this would require the whole `on_instance` - // API to operate on `'static` environmental instances which would - // ultimately allow leaking those `'static` references to the outside - // and potentially lead to terrible bugs such as iterator invalidation. - instance - .debug_buf - .past_debug_messages() - .map(ToOwned::to_owned) - .collect::>() - .into_iter() - }) -} - -/// Returns the recorded emitted events in order. -pub fn recorded_events() -> impl Iterator { +pub fn recorded_debug_messages() -> RecordedDebugMessages { ::on_instance(|instance| { - // We return a clone of the recorded emitted events instead of - // references to them since this would require the whole `on_instance` - // API to operate on `'static` environmental instances which would - // ultimately allow leaking those `'static` references to the outside - // and potentially lead to terrible bugs such as iterator invalidation. - instance - .emitted_events - .emitted_events() - .map(Clone::clone) - .collect::>() - .into_iter() + instance.engine.get_emitted_debug_messages() }) } -/// Advances the chain by a single block. -pub fn advance_block() -> Result<()> -where - T: Environment, -{ - ::on_instance(|instance| instance.advance_block::()) -} - /// Set to true to disable clearing storage /// /// # Note /// /// Useful for benchmarks because it ensures the initialized storage is maintained across runs, /// because lazy storage structures automatically clear their associated cells when they are dropped. -pub fn set_clear_storage_disabled(disable: bool) { - ::on_instance(|instance| { - instance.clear_storage_disabled = disable - }) +pub fn set_clear_storage_disabled(_disable: bool) { + unimplemented!( + "off-chain environment does not yet support `set_clear_storage_disabled`" + ); } -/// The default accounts. -pub struct DefaultAccounts +/// Sets a caller for the next call. +pub fn set_caller(caller: T::AccountId) where T: Environment, + ::AccountId: From<[u8; 32]>, { - /// The predefined `ALICE` account holding substantial amounts of value. - pub alice: T::AccountId, - /// The predefined `BOB` account holding some amounts of value. - pub bob: T::AccountId, - /// The predefined `CHARLIE` account holding some amounts of value. - pub charlie: T::AccountId, - /// The predefined `DJANGO` account holding no value. - pub django: T::AccountId, - /// The predefined `EVE` account holding no value. - pub eve: T::AccountId, - /// The predefined `FRANK` account holding no value. - pub frank: T::AccountId, + ::on_instance(|instance| { + instance.engine.set_caller(scale::Encode::encode(&caller)); + }) } -/// Returns the default accounts for testing purposes: -/// Alice, Bob, Charlie, Django, Eve and Frank. -pub fn default_accounts() -> Result> +/// Sets the callee for the next call. +pub fn set_callee(callee: T::AccountId) where T: Environment, ::AccountId: From<[u8; 32]>, { - Ok(DefaultAccounts { - alice: T::AccountId::from([0x01; 32]), - bob: T::AccountId::from([0x02; 32]), - charlie: T::AccountId::from([0x03; 32]), - django: T::AccountId::from([0x04; 32]), - eve: T::AccountId::from([0x05; 32]), - frank: T::AccountId::from([0x06; 32]), + ::on_instance(|instance| { + instance.engine.set_callee(scale::Encode::encode(&callee)); }) } -/// Initializes the whole off-chain environment. +/// Gets the currently set callee. /// -/// # Note -/// -/// - Initializes the off-chain environment with default values that fit most -/// uses cases. -pub fn initialize_or_reset_as_default() -> Result<()> +/// This is account id of the currently executing contract. +pub fn callee() -> T::AccountId where T: Environment, - ::AccountId: From<[u8; 32]>, { ::on_instance(|instance| { - instance.initialize_or_reset_as_default::() + let callee = instance.engine.get_callee(); + scale::Decode::decode(&mut &callee[..]).expect("encoding failed") }) } -/// Runs the given closure test function with the default configuration -/// for the off-chain environment. -pub fn run_test(f: F) -> Result<()> +/// Returns the total number of reads and writes of the contract's storage. +pub fn get_contract_storage_rw(account_id: &T::AccountId) -> (usize, usize) where T: Environment, - F: FnOnce(DefaultAccounts) -> Result<()>, - ::AccountId: From<[u8; 32]>, { - initialize_or_reset_as_default::()?; - let default_accounts = default_accounts::()?; - f(default_accounts) + ::on_instance(|instance| { + instance + .engine + .get_contract_storage_rw(scale::Encode::encode(&account_id)) + }) } -/// Returns the total number of reads and writes of the contract's storage. -pub fn get_contract_storage_rw(account_id: &T::AccountId) -> Result<(usize, usize)> +/// Sets the balance of `account_id` to `new_balance`. +pub fn set_balance(account_id: T::AccountId, new_balance: T::Balance) where - T: Environment, + T: Environment, // Just temporary for the MVP! + ::AccountId: From<[u8; 32]>, { ::on_instance(|instance| { instance - .accounts - .get_account::(account_id) - .ok_or_else(|| AccountError::no_account_for_id::(account_id)) - .map_err(Into::into) - .and_then(|account| account.get_storage_rw().map_err(Into::into)) + .engine + .set_balance(scale::Encode::encode(&account_id), new_balance); + }) +} + +/// Sets the value transferred from the caller to the callee as part of the call. +pub fn set_value_transferred(value: T::Balance) +where + T: Environment, // Just temporary for the MVP! +{ + ::on_instance(|instance| { + instance.engine.set_value_transferred(value); }) } @@ -309,41 +192,108 @@ where { ::on_instance(|instance| { instance - .accounts - .get_account::(account_id) - .ok_or_else(|| AccountError::no_account_for_id::(account_id)) + .engine + .count_used_storage_cells(&scale::Encode::encode(&account_id)) .map_err(Into::into) - .and_then(|account| account.count_used_storage_cells().map_err(Into::into)) }) } -/// Returns the account id of the currently executing contract. -pub fn get_current_contract_account_id() -> Result +/// Runs the given closure test function with the default configuration +/// for the off-chain environment. +pub fn run_test(f: F) -> Result<()> where T: Environment, + F: FnOnce(DefaultAccounts) -> Result<()>, + ::AccountId: From<[u8; 32]>, { + let default_accounts = default_accounts::(); ::on_instance(|instance| { - let exec_context = instance.exec_context()?; - let callee = exec_context.callee.decode()?; - Ok(callee) - }) + instance.engine.initialize_or_reset(); + + let encoded_alice = scale::Encode::encode(&default_accounts.alice); + instance.engine.set_caller(encoded_alice.clone()); + instance.engine.set_callee(encoded_alice.clone()); + + // set up the funds for the default accounts + let substantial = 1_000_000; + let some = 1_000; + instance.engine.set_balance(encoded_alice, substantial); + instance + .engine + .set_balance(scale::Encode::encode(&default_accounts.bob), some); + instance + .engine + .set_balance(scale::Encode::encode(&default_accounts.charlie), some); + instance + .engine + .set_balance(scale::Encode::encode(&default_accounts.django), 0); + instance + .engine + .set_balance(scale::Encode::encode(&default_accounts.eve), 0); + instance + .engine + .set_balance(scale::Encode::encode(&default_accounts.frank), 0); + }); + f(default_accounts) +} + +/// Returns the default accounts for testing purposes: +/// Alice, Bob, Charlie, Django, Eve and Frank. +pub fn default_accounts() -> DefaultAccounts +where + T: Environment, + ::AccountId: From<[u8; 32]>, +{ + DefaultAccounts { + alice: T::AccountId::from([0x01; 32]), + bob: T::AccountId::from([0x02; 32]), + charlie: T::AccountId::from([0x03; 32]), + django: T::AccountId::from([0x04; 32]), + eve: T::AccountId::from([0x05; 32]), + frank: T::AccountId::from([0x06; 32]), + } } -/// The result of a successful contract termination. -#[derive(scale::Encode, scale::Decode)] -pub struct ContractTerminationResult +/// The default accounts. +pub struct DefaultAccounts where - E: Environment, + T: Environment, { - /// The beneficiary account who received the remaining value in the contract. - pub beneficiary: ::AccountId, - /// The value which was transferred to the `beneficiary`. - pub transferred: ::Balance, + /// The predefined `ALICE` account holding substantial amounts of value. + pub alice: T::AccountId, + /// The predefined `BOB` account holding some amounts of value. + pub bob: T::AccountId, + /// The predefined `CHARLIE` account holding some amounts of value. + pub charlie: T::AccountId, + /// The predefined `DJANGO` account holding no value. + pub django: T::AccountId, + /// The predefined `EVE` account holding no value. + pub eve: T::AccountId, + /// The predefined `FRANK` account holding no value. + pub frank: T::AccountId, +} + +/// Returns the recorded emitted events in order. +pub fn recorded_events() -> impl Iterator { + ::on_instance(|instance| { + instance + .engine + .get_emitted_events() + .into_iter() + .map(|evt: ink_engine::test_api::EmittedEvent| evt.into()) + }) } /// Tests if a contract terminates successfully after `self.env().terminate()` /// has been called. /// +/// The arguments denote: +/// +/// * `should_terminate`: A closure in which the function supposed to terminate is called. +/// * `expected_beneficiary`: The beneficiary account who should have received the +/// remaining value in the contract +/// * `expected_value_transferred_to_beneficiary`: The value which should have been transferred +/// to the `expected_beneficiary`. /// # Usage /// /// ```no_compile @@ -359,21 +309,23 @@ where pub fn assert_contract_termination( should_terminate: F, expected_beneficiary: T::AccountId, - expected_balance: T::Balance, + expected_value_transferred_to_beneficiary: T::Balance, ) where T: Environment, F: FnMut() + UnwindSafe, - ::AccountId: core::fmt::Debug, - ::Balance: core::fmt::Debug, + ::AccountId: Debug, + ::Balance: Debug, { let value_any = ::std::panic::catch_unwind(should_terminate) .expect_err("contract did not terminate"); let encoded_input = value_any .downcast_ref::>() .expect("panic object can not be cast"); - let res: ContractTerminationResult = + let (value_transferred, encoded_beneficiary): (T::Balance, Vec) = scale::Decode::decode(&mut &encoded_input[..]).expect("input can not be decoded"); - - assert_eq!(res.beneficiary, expected_beneficiary); - assert_eq!(res.transferred, expected_balance); + let beneficiary = + ::decode(&mut &encoded_beneficiary[..]) + .expect("input can not be decoded"); + assert_eq!(value_transferred, expected_value_transferred_to_beneficiary); + assert_eq!(beneficiary, expected_beneficiary); } diff --git a/crates/env/src/engine/off_chain/tests.rs b/crates/env/src/engine/off_chain/tests.rs index 5e99423ae75..f94b6001f8c 100644 --- a/crates/env/src/engine/off_chain/tests.rs +++ b/crates/env/src/engine/off_chain/tests.rs @@ -12,84 +12,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::Result; -use ink_primitives::Key; +use crate::{ + engine::off_chain::impls::TopicsBuilder, + topics::TopicsBuilderBackend, + Result, +}; #[test] -fn store_load_clear() -> Result<()> { - crate::test::run_test::(|_| { - let key = Key::from([0x42; 32]); - assert_eq!(crate::get_contract_storage::<()>(&key), Ok(None)); - crate::set_contract_storage(&key, &[0x05_u8; 5]); - assert_eq!( - crate::get_contract_storage::<[i8; 5]>(&key), - Ok(Some([0x05; 5])), - ); - crate::clear_contract_storage(&key); - assert_eq!(crate::get_contract_storage::<[u8; 5]>(&key), Ok(None)); - Ok(()) - }) -} - -fn add_key(key: &Key, offset: u64) -> Key { - let mut result = *key; - result += offset; - result -} - -#[test] -fn key_add() -> Result<()> { - crate::test::run_test::(|_| { - let key00 = Key::from([0x0; 32]); - let key05 = add_key(&key00, 5); // -> 5 - let key10 = add_key(&key00, 10); // -> 10 | same as key55 - let key55 = add_key(&key05, 5); // -> 5 + 5 = 10 | same as key10 - crate::set_contract_storage(&key55, &42); - assert_eq!(crate::get_contract_storage::(&key10), Ok(Some(42))); - crate::set_contract_storage(&key10, &1337); - assert_eq!(crate::get_contract_storage::(&key55), Ok(Some(1337))); - Ok(()) - }) -} - -#[test] -fn key_add_sub() -> Result<()> { +fn topics_builder() -> Result<()> { crate::test::run_test::(|_| { // given - let key0a = Key::from([0x0; 32]); - let key1a = add_key(&key0a, 1337); - let key2a = add_key(&key0a, 42); - let key3a = add_key(&key0a, 52); + let mut builder = TopicsBuilder::default(); // when - crate::set_contract_storage(&key0a, &1); - crate::set_contract_storage(&key1a, &2); - crate::set_contract_storage(&key2a, &3); - crate::set_contract_storage(&key3a, &4); + TopicsBuilderBackend::::push_topic(&mut builder, &13); + TopicsBuilderBackend::::push_topic(&mut builder, &17); // then - assert_eq!(crate::get_contract_storage::(&key0a), Ok(Some(1))); - assert_eq!(crate::get_contract_storage::(&key1a), Ok(Some(2))); - assert_eq!(crate::get_contract_storage::(&key2a), Ok(Some(3))); - assert_eq!(crate::get_contract_storage::(&key3a), Ok(Some(4))); - Ok(()) - }) -} - -#[test] -fn gas_price() -> crate::Result<()> { - crate::test::run_test::(|_| { - let gas_price = 2u32; - crate::test::update_chain_spec(|chain_spec| { - chain_spec.set_gas_price::(gas_price.into()) - })?; - - assert_eq!(2u128, crate::weight_to_fee::(1)); - assert_eq!( - 20u128, - crate::weight_to_fee::(10) - ); - assert_eq!(6u128, crate::weight_to_fee::(3)); + assert_eq!(builder.topics.len(), 2); + + let topics_len_compact = &scale::Compact(2u32); + let topics_len_encoded = scale::Encode::encode(&topics_len_compact); + let output = TopicsBuilderBackend::::output(builder); + #[rustfmt::skip] + let expected = vec![topics_len_encoded[0], 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + assert_eq!(output, expected); Ok(()) }) diff --git a/crates/env/src/engine/off_chain/typed_encoded.rs b/crates/env/src/engine/off_chain/typed_encoded.rs deleted file mode 100644 index 8785debfac1..00000000000 --- a/crates/env/src/engine/off_chain/typed_encoded.rs +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::OffChainError; -use crate::Error; -use core::{ - any::TypeId, - cmp::Ordering, - hash::{ - Hash, - Hasher, - }, - marker::PhantomData, -}; -use derive_more::From; - -/// A wrapper around an encoded entity that only allows type safe accesses. -/// -/// # Note -/// -/// Checks are implemented at runtime. -#[derive(Debug, Clone)] -pub struct TypedEncoded { - /// The bytes of the encoded representation of the type. - encoded: Vec, - /// The unique identifier of the encoded type. - /// - /// # Note - /// - /// - If this is `None` it means that the instance is currently untyped - /// and will take over any given type upon the first typed interaction. - /// - This is needed since instances of `TypedEncoded` are going to be used - /// in static memory where it is not possible to decide about the used types - /// given by `Environment` at initialization. - type_id: Option, - /// Classification marker. - /// - /// # Note - /// - /// - This should not be the typed that is actually stored as encoded - /// representation in `self.encoded` but should primarily be an - /// abstract marker type that may be used for classification. - /// - The idea behind the marker is to say that whenever two instances - /// of `TypedEncoded` share a marker they are guaranteed to also have - /// a common (but unknown) `type_id` so they can decode to the same - /// original type and thus we can allow to interoperate on them. - /// - /// # Example - /// - /// The `TestEnv` might use one abstract marker for every - /// of the fundamental FRAME types: `Balance`, `AccountId`, `Hash`, etc. - /// With this and the explicit guarantee that two instances of `TypedEncoded` - /// with the same abstract marker also share the same (unknown) `type_id` - /// it is possible to allow them to interoperate. - marker: PhantomData T>, -} - -/// Errors that may be encountered upon operating on typed encoded instances. -#[derive(Debug, From, PartialEq, Eq)] -pub enum TypedEncodedError { - /// Error upon decoding. - Decode(scale::Error), - /// When operating on instances with different types. - #[from(ignore)] - DifferentTypes { - lhs: core::any::TypeId, - rhs: core::any::TypeId, - }, - /// When an already initialized instance is about to be initialized. - #[from(ignore)] - AlreadyInitialized { - initialized_id: core::any::TypeId, - new_id: core::any::TypeId, - }, - /// When operating on still uninitialized types. - #[from(ignore)] - StillUninitialized, -} - -impl From for Error { - fn from(typed_encoded_error: TypedEncodedError) -> Self { - Error::OffChain(OffChainError::TypedEncoded(typed_encoded_error)) - } -} - -/// The result type for typed encoded operations. -pub type Result = core::result::Result; - -impl Default for TypedEncoded { - /// Creates an uninitialized instance. - /// - /// # Note - /// - /// The resulting instance can be properly initialized at a later point - /// using a call to [`TypedEncoded::try_initialize`]. - fn default() -> Self { - Self { - encoded: Vec::new(), - type_id: None, - marker: Default::default(), - } - } -} - -impl TypedEncoded { - /// Creates a new uninitialized instance. - pub fn uninitialized() -> Self { - Self { - encoded: Vec::new(), - type_id: None, - marker: Default::default(), - } - } - - /// Creates a new typed-encoded initialized by `value` of type `T`. - pub fn new(value: &T) -> Self - where - T: scale::Encode + 'static, - { - Self { - encoded: value.encode(), - type_id: Some(core::any::TypeId::of::()), - marker: Default::default(), - } - } - - /// Initializes `self` with a given encodable value. - /// - /// # Errors - /// - /// If `self` has already been initialized or is an initialized instance. - pub fn try_initialize(&mut self, value: &T) -> Result<()> - where - T: scale::Encode + 'static, - { - if let Some(id) = self.type_id { - return Err(TypedEncodedError::AlreadyInitialized { - initialized_id: id, - new_id: core::any::TypeId::of::(), - }) - } - value.encode_to(&mut self.encoded); - self.type_id = Some(core::any::TypeId::of::()); - Ok(()) - } - - /// Returns the encoded bytes representation. - /// - /// # Errors - /// - /// If the instance is still uninitialized. - pub fn encoded_bytes(&self) -> Result<&[u8]> { - if self.type_id.is_none() { - return Err(TypedEncodedError::StillUninitialized) - } - Ok(&self.encoded[..]) - } - - /// Returns a mutable reference to the encoded bytes representation. - /// - /// # Errors - /// - /// If the instance is still uninitialized. - pub fn encoded_bytes_mut(&mut self) -> Result<&mut [u8]> { - if self.type_id.is_none() { - return Err(TypedEncodedError::StillUninitialized) - } - Ok(&mut self.encoded[..]) - } - - /// Returns the type ID if the instance has already been initialized. - /// - /// # Errors - /// - /// Returns an appropriate error in case the instance is uninitialized. - fn type_id(&self) -> Result { - match self.type_id { - Some(type_id) => Ok(type_id), - None => Err(TypedEncodedError::StillUninitialized), - } - } - - /// Returns `Ok` if both types are encoded with the same type. - fn check_matching_types(&self, other: &Self) -> Result<()> { - let id_lhs = self.type_id()?; - let id_rhs = other.type_id()?; - if id_lhs != id_rhs { - return Err(TypedEncodedError::DifferentTypes { - lhs: id_lhs, - rhs: id_rhs, - }) - } - Ok(()) - } - - /// Returns `Ok` if `T` is the type represented by the typed encoded instance. - fn check_enforced_type(&self) -> Result<()> - where - T: 'static, - { - let id_self = self.type_id()?; - let id_enforced = core::any::TypeId::of::(); - if core::any::TypeId::of::() != id_self { - return Err(TypedEncodedError::DifferentTypes { - lhs: id_self, - rhs: id_enforced, - }) - } - Ok(()) - } - - /// Decodes the instance. - /// - /// # Note - /// - /// This effectively creates a clone of the encoded value. - pub fn decode(&self) -> Result - where - T: scale::Decode + 'static, - { - self.check_enforced_type::()?; - ::decode(&mut &self.encoded[..]).map_err(Into::into) - } - - /// Assigns the given `T` to `self`. - pub fn assign(&mut self, value: &T) -> Result<()> - where - T: scale::Encode + 'static, - { - self.check_enforced_type::()?; - self.encoded.clear(); - value.encode_to(&mut self.encoded); - self.type_id = Some(core::any::TypeId::of::()); - Ok(()) - } - - /// Evaluates the given closure on the given typed encoded instances. - pub fn eval(&self, other: &Self, f: F) -> Result - where - T: scale::Decode + 'static, - F: FnOnce(&T, &T) -> R, - { - Self::check_matching_types(self, other)?; - let decoded_self = self.decode::()?; - let decoded_other = other.decode::()?; - Ok(f(&decoded_self, &decoded_other)) - } - - /// Evaluates the given closure on the given typed decoded instances - /// and writes back the result into the typed encoded instance. - pub fn eval_mut(&mut self, other: &Self, f: F) -> Result - where - T: scale::Decode + scale::Encode + 'static, - F: FnOnce(&mut T, &T) -> R, - { - Self::check_matching_types(self, other)?; - let mut decoded_self = self.decode::()?; - let decoded_other = other.decode::()?; - let result = f(&mut decoded_self, &decoded_other); - self.encoded.clear(); - scale::Encode::encode_to(&decoded_self, &mut self.encoded); - Ok(result) - } - - /// Returns `true` if both instances are of type `T` and are equal. - /// - /// # Note - /// - /// The equality check is performed on decoded instances. - pub fn eq(&self, other: &Self) -> Result - where - T: PartialEq + scale::Decode + 'static, - { - self.eval::(other, |lhs, rhs| core::cmp::PartialEq::eq(lhs, rhs)) - } - - /// Returns order relation if both instances are of type `T`. - /// - /// # Note - /// - /// The order relation is performed on the decoded instances. - pub fn cmp(&self, other: &Self) -> Result - where - T: PartialOrd + Ord + scale::Decode + 'static, - { - self.eval::(other, |lhs, rhs| core::cmp::Ord::cmp(lhs, rhs)) - } - - /// Computes the hash of the decoded typed instance if types match. - pub fn hash(&self, state: &mut H) -> Result<()> - where - T: scale::Decode + Hash + 'static, - H: Hasher, - { - self.decode::()?.hash(state); - Ok(()) - } -} - -impl PartialEq for TypedEncoded { - fn eq(&self, other: &Self) -> bool { - self.type_id == other.type_id && self.encoded == other.encoded - } -} - -impl Eq for TypedEncoded {} - -impl PartialOrd for TypedEncoded { - fn partial_cmp(&self, other: &Self) -> Option { - Some(Ord::cmp(self, other)) - } -} - -impl Ord for TypedEncoded { - fn cmp(&self, other: &Self) -> Ordering { - self.encoded.cmp(&other.encoded) - } -} diff --git a/crates/env/src/engine/off_chain/types.rs b/crates/env/src/engine/off_chain/types.rs index 430fb0a0d54..cd9e8644b6b 100644 --- a/crates/env/src/engine/off_chain/types.rs +++ b/crates/env/src/engine/off_chain/types.rs @@ -12,43 +12,57 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Emulated environmental types of the test environment. -//! -//! Due to technical constraints it is not possible to define the test -//! environment instance thread-locally and generically over the actual -//! environmental types. -//! -//! For that reason we store the type information of all the environmental -//! types at runtime to at least have some kind of type safety upon access. -//! This is done via the `TypedEncoded` abstraction that stores the -//! SCALE encoded bytes and also has a runtime type information marker -//! assigned upon initialization to check whether accesses to it are -//! type safe. +//! Contains the necessary conversions from `ink_engine` types to types +//! of this crate. -use super::TypedEncoded; +use super::{ + test_api::EmittedEvent, + AccountError, + Error, + OffChainError, +}; -/// Type markers used in conjunction with `TypedEncoded`. -#[rustfmt::skip] -mod type_marker { - /// Type marker representing an environmental `AccountId`. - #[derive(Debug, Clone)] pub enum AccountId {} - /// Type marker representing an environmental `Balance`. - #[derive(Debug, Clone)] pub enum Balance {} - /// Type marker representing an environmental `Hash`. - #[derive(Debug, Clone)] pub enum Hash {} - /// Type marker representing an environmental `Timestamp`. - #[derive(Debug, Clone)] pub enum OffTimestamp {} - /// Type marker representing an environmental `BlockNumber`. - #[derive(Debug, Clone)] pub enum BlockNumber {} +impl From for EmittedEvent { + fn from(evt: ink_engine::test_api::EmittedEvent) -> Self { + EmittedEvent { + topics: evt.topics, + data: evt.data, + } + } } -/// Off-chain environment account ID type. -pub type OffAccountId = TypedEncoded; -/// Off-chain environment balance type. -pub type OffBalance = TypedEncoded; -/// Off-chain environment hash type. -pub type OffHash = TypedEncoded; -/// Off-chain environment timestamp type. -pub type OffTimestamp = TypedEncoded; -/// Off-chain environment block number type. -pub type OffBlockNumber = TypedEncoded; +impl From for Error { + fn from(err: ink_engine::Error) -> Self { + let e = match err { + ink_engine::Error::Account(acc) => OffChainError::Account(acc.into()), + ink_engine::Error::UninitializedBlocks => OffChainError::UninitializedBlocks, + ink_engine::Error::UninitializedExecutionContext => { + OffChainError::UninitializedExecutionContext + } + ink_engine::Error::UnregisteredChainExtension => { + OffChainError::UnregisteredChainExtension + } + }; + Error::OffChain(e) + } +} + +impl From for AccountError { + fn from(err: ink_engine::AccountError) -> Self { + match err { + ink_engine::AccountError::Decoding(e) => AccountError::Decoding(e), + ink_engine::AccountError::UnexpectedUserAccount => { + AccountError::UnexpectedUserAccount + } + ink_engine::AccountError::NoAccountForId(acc) => { + AccountError::NoAccountForId(acc) + } + } + } +} + +impl From for Error { + fn from(account_error: ink_engine::AccountError) -> Self { + Error::OffChain(OffChainError::Account(account_error.into())) + } +} diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index bd732048f20..23d80569cd3 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -46,10 +46,3 @@ std = [ "scale/std", ] show-codegen-docs = [] - -# Due to https://github.com/rust-lang/cargo/issues/6915 features that affect a dev-dependency -# currently can't be enabled by a parent crate, hence `["ink_env/ink-experimental-engine"]` does -# not work. -# After we switch to the new off-chain environment with https://github.com/paritytech/ink/issues/565 -# we can remove the feature altogether. -ink-experimental-engine = [] diff --git a/crates/lang/tests/unique_topics.rs b/crates/lang/tests/unique_topics.rs index 9fcf5985e69..3eae5731298 100644 --- a/crates/lang/tests/unique_topics.rs +++ b/crates/lang/tests/unique_topics.rs @@ -59,19 +59,13 @@ mod my_contract { } } - #[cfg(not(feature = "ink-experimental-engine"))] #[cfg(test)] mod tests { use super::*; use ink_env::test::EmittedEvent; use ink_lang as ink; - // The following test unfortunately has to be ignored until we make - // `ink-experimental-engine` the default with https://github.com/paritytech/ink/issues/565. - // See the issue for details. - #[ignore] #[ink::test] - #[cfg(feature = "ink-experimental-engine")] fn event_must_have_unique_topics() { // given let my_contract = MyContract::new(); @@ -91,26 +85,6 @@ mod my_contract { assert!(!has_duplicates(&mut encoded_topics)); } - #[ink::test] - fn event_must_have_unique_topics() { - // given - let my_contract = MyContract::new(); - - // when - MyContract::emit_my_event(&my_contract); - - // then - // all topics must be unique - let emitted_events = - ink_env::test::recorded_events().collect::>(); - let mut encoded_topics: std::vec::Vec<&[u8]> = emitted_events[0] - .topics - .iter() - .map(|topic| topic.encoded_bytes().expect("encoded bytes must exist")) - .collect(); - assert!(!has_duplicates(&mut encoded_topics)); - } - /// Finds duplicates in a given vector. /// /// This function has complexity of `O(n * log n)` and no additional memory diff --git a/crates/storage/Cargo.toml b/crates/storage/Cargo.toml index 57f72621510..5d40bc4d1ff 100644 --- a/crates/storage/Cargo.toml +++ b/crates/storage/Cargo.toml @@ -46,4 +46,3 @@ std = [ "scale-info/std", ] ink-fuzz-tests = ["std"] -ink-experimental-engine = ["ink_env/ink-experimental-engine"] diff --git a/crates/storage/src/collections/binary_heap/tests.rs b/crates/storage/src/collections/binary_heap/tests.rs index c245f64d886..c38444a8a79 100644 --- a/crates/storage/src/collections/binary_heap/tests.rs +++ b/crates/storage/src/collections/binary_heap/tests.rs @@ -236,77 +236,6 @@ fn spread_layout_clear_works() { .unwrap() } -#[test] -#[should_panic(expected = "encountered empty storage cell")] -#[cfg(not(feature = "ink-experimental-engine"))] -fn drop_works() { - ink_env::test::run_test::(|_| { - let root_key = Key::from([0x42; 32]); - - // if the setup panics it should not cause the test to pass - let setup_result = std::panic::catch_unwind(|| { - let heap = heap_from_slice(&[23, 25, 65]); - SpreadLayout::push_spread(&heap, &mut KeyPtr::from(root_key)); - - let _ = as SpreadLayout>::pull_spread(&mut KeyPtr::from( - root_key, - )); - // heap is dropped which should clear the cells - }); - - assert!(setup_result.is_ok(), "setup should not panic"); - - let contract_id = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - let used_cells = ink_env::test::count_used_storage_cells::< - ink_env::DefaultEnvironment, - >(&contract_id) - .expect("Used cells must be returned"); - assert_eq!(used_cells, 0); - - let _ = - as SpreadLayout>::pull_spread(&mut KeyPtr::from(root_key)); - Ok(()) - }) - .unwrap() -} - -#[test] -#[should_panic(expected = "encountered empty storage cell")] -#[cfg(feature = "ink-experimental-engine")] -fn drop_works() { - ink_env::test::run_test::(|_| { - let root_key = Key::from([0x42; 32]); - - // if the setup panics it should not cause the test to pass - let setup_result = std::panic::catch_unwind(|| { - let heap = heap_from_slice(&[23, 25, 65]); - SpreadLayout::push_spread(&heap, &mut KeyPtr::from(root_key)); - - let _ = as SpreadLayout>::pull_spread(&mut KeyPtr::from( - root_key, - )); - // heap is dropped which should clear the cells - }); - - assert!(setup_result.is_ok(), "setup should not panic"); - - let contract_id = ink_env::test::callee::(); - let used_cells = ink_env::test::count_used_storage_cells::< - ink_env::DefaultEnvironment, - >(&contract_id) - .expect("Used cells must be returned"); - assert_eq!(used_cells, 0); - - let _ = - as SpreadLayout>::pull_spread(&mut KeyPtr::from(root_key)); - Ok(()) - }) - .unwrap() -} - #[test] fn clear_works_on_filled_heap() { let mut heap = heap_from_slice(&[b'a', b'b', b'c', b'd']); @@ -321,68 +250,6 @@ fn clear_works_on_empty_heap() { assert!(heap.is_empty()); } -#[cfg(not(feature = "ink-experimental-engine"))] -fn check_complexity_read_writes( - heap_size: u32, - heap_op: F, - expected_net_reads: usize, - expected_net_writes: usize, -) -> ink_env::Result<()> -where - F: FnOnce(&mut BinaryHeap), -{ - ink_env::test::run_test::(|_| { - let heap1 = heap_of_size(heap_size); - let root_key = Key::from([0x42; 32]); - SpreadLayout::push_spread(&heap1, &mut KeyPtr::from(root_key)); - - let contract_account = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - - let mut lazy_heap = - as SpreadLayout>::pull_spread(&mut KeyPtr::from(root_key)); - - let (base_reads, base_writes) = ink_env::test::get_contract_storage_rw::< - ink_env::DefaultEnvironment, - >(&contract_account)?; - - // elements.len + vec.len - const CONST_WRITES: u32 = 2; - assert_eq!( - (base_reads as u32, base_writes as u32), - (0, CONST_WRITES + get_count_cells(heap_size)) - ); - - heap_op(&mut lazy_heap); - - // write back to storage so we can see how many writes required - SpreadLayout::push_spread(&lazy_heap, &mut KeyPtr::from(root_key)); - - let (reads, writes) = ink_env::test::get_contract_storage_rw::< - ink_env::DefaultEnvironment, - >(&contract_account)?; - - let net_reads = reads - base_reads; - let net_writes = writes - base_writes; - - assert_eq!( - net_reads, expected_net_reads, - "size {}: storage reads", - heap_size - ); - assert_eq!( - net_writes, expected_net_writes, - "size {}: storage writes", - heap_size - ); - - Ok(()) - }) -} - -#[cfg(feature = "ink-experimental-engine")] fn check_complexity_read_writes( heap_size: u32, heap_op: F, diff --git a/crates/storage/src/collections/hashmap/tests.rs b/crates/storage/src/collections/hashmap/tests.rs index ada69c81ef9..10eb7876641 100644 --- a/crates/storage/src/collections/hashmap/tests.rs +++ b/crates/storage/src/collections/hashmap/tests.rs @@ -324,74 +324,6 @@ fn spread_layout_clear_works() { } #[test] -#[cfg(not(feature = "ink-experimental-engine"))] -fn storage_is_cleared_completely_after_pull_lazy() { - ink_env::test::run_test::(|_| { - // given - let root_key = Key::from([0x42; 32]); - let lazy_hmap = Lazy::new(filled_hmap()); - SpreadLayout::push_spread(&lazy_hmap, &mut KeyPtr::from(root_key)); - let pulled_hmap = > as SpreadLayout>::pull_spread( - &mut KeyPtr::from(root_key), - ); - - // when - SpreadLayout::clear_spread(&pulled_hmap, &mut KeyPtr::from(root_key)); - - // then - let contract_id = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - let used_cells = ink_env::test::count_used_storage_cells::< - ink_env::DefaultEnvironment, - >(&contract_id) - .expect("used cells must be returned"); - assert_eq!(used_cells, 0); - - Ok(()) - }) - .unwrap() -} - -#[test] -#[should_panic(expected = "storage entry was empty")] -#[cfg(not(feature = "ink-experimental-engine"))] -fn drop_works() { - ink_env::test::run_test::(|_| { - let root_key = Key::from([0x42; 32]); - - // if the setup panics it should not cause the test to pass - let setup_result = std::panic::catch_unwind(|| { - let hmap = filled_hmap(); - SpreadLayout::push_spread(&hmap, &mut KeyPtr::from(root_key)); - let _ = as SpreadLayout>::pull_spread( - &mut KeyPtr::from(root_key), - ); - // hmap is dropped which should clear the cells - }); - assert!(setup_result.is_ok(), "setup should not panic"); - - let contract_id = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - let used_cells = ink_env::test::count_used_storage_cells::< - ink_env::DefaultEnvironment, - >(&contract_id) - .expect("used cells must be returned"); - assert_eq!(used_cells, 0); - - let _ = as SpreadLayout>::pull_spread( - &mut KeyPtr::from(root_key), - ); - Ok(()) - }) - .unwrap() -} - -#[test] -#[cfg(feature = "ink-experimental-engine")] fn storage_is_cleared_completely_after_pull_lazy() { ink_env::test::run_test::(|_| { // given @@ -420,7 +352,6 @@ fn storage_is_cleared_completely_after_pull_lazy() { #[test] #[should_panic(expected = "storage entry was empty")] -#[cfg(feature = "ink-experimental-engine")] fn drop_works() { ink_env::test::run_test::(|_| { let root_key = Key::from([0x42; 32]); diff --git a/crates/storage/src/collections/smallvec/tests.rs b/crates/storage/src/collections/smallvec/tests.rs index ef748614cac..0bf5f096ac1 100644 --- a/crates/storage/src/collections/smallvec/tests.rs +++ b/crates/storage/src/collections/smallvec/tests.rs @@ -398,73 +398,6 @@ fn spread_layout_clear_works() { } #[test] -#[cfg(not(feature = "ink-experimental-engine"))] -fn storage_is_cleared_completely_after_pull_lazy() { - ink_env::test::run_test::(|_| { - // given - let root_key = Key::from([0x42; 32]); - let lazy_vec = Lazy::new(vec_from_slice(&[b'a', b'b', b'c', b'd'])); - SpreadLayout::push_spread(&lazy_vec, &mut KeyPtr::from(root_key)); - let pulled_vec = > as SpreadLayout>::pull_spread( - &mut KeyPtr::from(root_key), - ); - - // when - SpreadLayout::clear_spread(&pulled_vec, &mut KeyPtr::from(root_key)); - - // then - let contract_id = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - let used_cells = ink_env::test::count_used_storage_cells::< - ink_env::DefaultEnvironment, - >(&contract_id) - .expect("used cells must be returned"); - assert_eq!(used_cells, 0); - - Ok(()) - }) - .unwrap() -} - -#[test] -#[should_panic(expected = "encountered empty storage cell")] -#[cfg(not(feature = "ink-experimental-engine"))] -fn drop_works() { - ink_env::test::run_test::(|_| { - let root_key = Key::from([0x42; 32]); - - // if the setup panics it should not cause the test to pass - let setup_result = std::panic::catch_unwind(|| { - let vec = vec_from_slice(&[b'a', b'b', b'c', b'd']); - SpreadLayout::push_spread(&vec, &mut KeyPtr::from(root_key)); - let _ = as SpreadLayout>::pull_spread(&mut KeyPtr::from( - root_key, - )); - // vec is dropped which should clear the cells - }); - assert!(setup_result.is_ok(), "setup should not panic"); - - let contract_id = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - let used_cells = ink_env::test::count_used_storage_cells::< - ink_env::DefaultEnvironment, - >(&contract_id) - .expect("used cells must be returned"); - assert_eq!(used_cells, 0); - - let _ = - as SpreadLayout>::pull_spread(&mut KeyPtr::from(root_key)); - Ok(()) - }) - .unwrap() -} - -#[test] -#[cfg(feature = "ink-experimental-engine")] fn storage_is_cleared_completely_after_pull_lazy() { ink_env::test::run_test::(|_| { // given @@ -493,7 +426,6 @@ fn storage_is_cleared_completely_after_pull_lazy() { #[test] #[should_panic(expected = "encountered empty storage cell")] -#[cfg(feature = "ink-experimental-engine")] fn drop_works() { ink_env::test::run_test::(|_| { let root_key = Key::from([0x42; 32]); diff --git a/crates/storage/src/collections/stash/tests.rs b/crates/storage/src/collections/stash/tests.rs index 8d722d0d3df..1b165ea7d70 100644 --- a/crates/storage/src/collections/stash/tests.rs +++ b/crates/storage/src/collections/stash/tests.rs @@ -754,73 +754,6 @@ fn spread_layout_clear_works() { } #[test] -#[cfg(not(feature = "ink-experimental-engine"))] -fn storage_is_cleared_completely_after_pull_lazy() { - ink_env::test::run_test::(|_| { - // given - let root_key = Key::from([0x42; 32]); - let lazy_stash = Lazy::new(create_holey_stash()); - SpreadLayout::push_spread(&lazy_stash, &mut KeyPtr::from(root_key)); - let pulled_stash = > as SpreadLayout>::pull_spread( - &mut KeyPtr::from(root_key), - ); - - // when - SpreadLayout::clear_spread(&pulled_stash, &mut KeyPtr::from(root_key)); - - // then - let contract_id = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - let storage_used = ink_env::test::count_used_storage_cells::< - ink_env::DefaultEnvironment, - >(&contract_id) - .expect("used cells must be returned"); - assert_eq!(storage_used, 0); - - Ok(()) - }) - .unwrap() -} - -#[test] -#[should_panic(expected = "storage entry was empty")] -#[cfg(not(feature = "ink-experimental-engine"))] -fn drop_works() { - ink_env::test::run_test::(|_| { - let root_key = Key::from([0x42; 32]); - - // if the setup panics it should not cause the test to pass - let setup_result = std::panic::catch_unwind(|| { - let stash = create_holey_stash(); - SpreadLayout::push_spread(&stash, &mut KeyPtr::from(root_key)); - let _ = as SpreadLayout>::pull_spread(&mut KeyPtr::from( - root_key, - )); - // stash is dropped which should clear the cells - }); - assert!(setup_result.is_ok(), "setup should not panic"); - - let contract_id = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - let used_cells = ink_env::test::count_used_storage_cells::< - ink_env::DefaultEnvironment, - >(&contract_id) - .expect("used cells must be returned"); - assert_eq!(used_cells, 0); - - let _ = - as SpreadLayout>::pull_spread(&mut KeyPtr::from(root_key)); - Ok(()) - }) - .unwrap() -} - -#[test] -#[cfg(feature = "ink-experimental-engine")] fn storage_is_cleared_completely_after_pull_lazy() { ink_env::test::run_test::(|_| { // given @@ -849,7 +782,6 @@ fn storage_is_cleared_completely_after_pull_lazy() { #[test] #[should_panic(expected = "storage entry was empty")] -#[cfg(feature = "ink-experimental-engine")] fn drop_works() { ink_env::test::run_test::(|_| { let root_key = Key::from([0x42; 32]); diff --git a/crates/storage/src/collections/vec/tests.rs b/crates/storage/src/collections/vec/tests.rs index 743a8c93baa..be16972adae 100644 --- a/crates/storage/src/collections/vec/tests.rs +++ b/crates/storage/src/collections/vec/tests.rs @@ -499,78 +499,6 @@ fn test_binary_search_implementation_details() { #[test] #[should_panic(expected = "encountered empty storage cell")] -#[cfg(not(feature = "ink-experimental-engine"))] -fn storage_is_cleared_completely_after_pull_lazy() { - ink_env::test::run_test::(|_| { - // given - let root_key = Key::from([0x42; 32]); - let mut lazy_vec: Lazy> = Lazy::new(StorageVec::new()); - lazy_vec.push(13u32); - lazy_vec.push(13u32); - SpreadLayout::push_spread(&lazy_vec, &mut KeyPtr::from(root_key)); - let pulled_vec = > as SpreadLayout>::pull_spread( - &mut KeyPtr::from(root_key), - ); - - // when - SpreadLayout::clear_spread(&pulled_vec, &mut KeyPtr::from(root_key)); - - // then - let contract_id = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - let used_cells = ink_env::test::count_used_storage_cells::< - ink_env::DefaultEnvironment, - >(&contract_id) - .expect("used cells must be returned"); - assert_eq!(used_cells, 0); - let _ = - *> as SpreadLayout>::pull_spread(&mut KeyPtr::from(root_key)); - - Ok(()) - }) - .unwrap() -} - -#[test] -#[should_panic(expected = "encountered empty storage cell")] -#[cfg(not(feature = "ink-experimental-engine"))] -fn drop_works() { - ink_env::test::run_test::(|_| { - let root_key = Key::from([0x42; 32]); - - // if the setup panics it should not cause the test to pass - let setup_result = std::panic::catch_unwind(|| { - let vec = vec_from_slice(&[b'a', b'b', b'c', b'd']); - SpreadLayout::push_spread(&vec, &mut KeyPtr::from(root_key)); - let _ = as SpreadLayout>::pull_spread(&mut KeyPtr::from( - root_key, - )); - // vec is dropped which should clear the cells - }); - assert!(setup_result.is_ok(), "setup should not panic"); - - let contract_id = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - let used_cells = ink_env::test::count_used_storage_cells::< - ink_env::DefaultEnvironment, - >(&contract_id) - .expect("used cells must be returned"); - assert_eq!(used_cells, 0); - - let _ = - as SpreadLayout>::pull_spread(&mut KeyPtr::from(root_key)); - Ok(()) - }) - .unwrap() -} - -#[test] -#[should_panic(expected = "encountered empty storage cell")] -#[cfg(feature = "ink-experimental-engine")] fn storage_is_cleared_completely_after_pull_lazy() { ink_env::test::run_test::(|_| { // given @@ -603,7 +531,6 @@ fn storage_is_cleared_completely_after_pull_lazy() { #[test] #[should_panic(expected = "encountered empty storage cell")] -#[cfg(feature = "ink-experimental-engine")] fn drop_works() { ink_env::test::run_test::(|_| { let root_key = Key::from([0x42; 32]); diff --git a/crates/storage/src/lazy/lazy_cell.rs b/crates/storage/src/lazy/lazy_cell.rs index f8d7e0f11c7..a4eb48cd123 100644 --- a/crates/storage/src/lazy/lazy_cell.rs +++ b/crates/storage/src/lazy/lazy_cell.rs @@ -720,112 +720,6 @@ mod tests { #[test] #[should_panic(expected = "encountered empty storage cell")] - #[cfg(not(feature = "ink-experimental-engine"))] - fn nested_lazies_are_cleared_completely_after_pull() { - ink_env::test::run_test::(|_| { - // given - let root_key = Key::from([0x42; 32]); - let nested_lazy: Lazy> = Lazy::new(Lazy::new(13u32)); - SpreadLayout::push_spread(&nested_lazy, &mut KeyPtr::from(root_key)); - let pulled_lazy = > as SpreadLayout>::pull_spread( - &mut KeyPtr::from(root_key), - ); - - // when - SpreadLayout::clear_spread(&pulled_lazy, &mut KeyPtr::from(root_key)); - - // then - let contract_id = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - let used_cells = ink_env::test::count_used_storage_cells::< - ink_env::DefaultEnvironment, - >(&contract_id) - .expect("used cells must be returned"); - assert_eq!(used_cells, 0); - let _ = *> as SpreadLayout>::pull_spread(&mut KeyPtr::from( - root_key, - )); - Ok(()) - }) - .unwrap() - } - - #[test] - #[should_panic(expected = "encountered empty storage cell")] - #[cfg(not(feature = "ink-experimental-engine"))] - fn lazy_drop_works() { - ink_env::test::run_test::(|_| { - // given - let root_key = Key::from([0x42; 32]); - - // when - let setup_result = std::panic::catch_unwind(|| { - let lazy: Lazy = Lazy::new(13u32); - SpreadLayout::push_spread(&lazy, &mut KeyPtr::from(root_key)); - let _pulled_lazy = - as SpreadLayout>::pull_spread(&mut KeyPtr::from(root_key)); - // lazy is dropped which should clear the cells - }); - assert!(setup_result.is_ok(), "setup should not panic"); - - // then - let contract_id = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - let used_cells = ink_env::test::count_used_storage_cells::< - ink_env::DefaultEnvironment, - >(&contract_id) - .expect("used cells must be returned"); - assert_eq!(used_cells, 0); - let _ = - * as SpreadLayout>::pull_spread(&mut KeyPtr::from(root_key)); - Ok(()) - }) - .unwrap() - } - - #[test] - #[should_panic(expected = "encountered empty storage cell")] - #[cfg(not(feature = "ink-experimental-engine"))] - fn lazy_drop_works_with_greater_footprint() { - ink_env::test::run_test::(|_| { - // given - let root_key = Key::from([0x42; 32]); - - // when - let setup_result = std::panic::catch_unwind(|| { - let lazy: Lazy<[u32; 5]> = Lazy::new([13, 14, 15, 16, 17]); - SpreadLayout::push_spread(&lazy, &mut KeyPtr::from(root_key)); - let _pulled_lazy = as SpreadLayout>::pull_spread( - &mut KeyPtr::from(root_key), - ); - // lazy is dropped which should clear the cells - }); - assert!(setup_result.is_ok(), "setup should not panic"); - - // then - let contract_id = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - let used_cells = ink_env::test::count_used_storage_cells::< - ink_env::DefaultEnvironment, - >(&contract_id) - .expect("used cells must be returned"); - assert_eq!(used_cells, 0); - let _ = - * as SpreadLayout>::pull_spread(&mut KeyPtr::from(root_key)); - Ok(()) - }) - .unwrap() - } - - #[test] - #[should_panic(expected = "encountered empty storage cell")] - #[cfg(feature = "ink-experimental-engine")] fn nested_lazies_are_cleared_completely_after_pull() { ink_env::test::run_test::(|_| { // given @@ -856,7 +750,6 @@ mod tests { #[test] #[should_panic(expected = "encountered empty storage cell")] - #[cfg(feature = "ink-experimental-engine")] fn lazy_drop_works() { ink_env::test::run_test::(|_| { // given @@ -888,7 +781,6 @@ mod tests { #[test] #[should_panic(expected = "encountered empty storage cell")] - #[cfg(feature = "ink-experimental-engine")] fn lazy_drop_works_with_greater_footprint() { ink_env::test::run_test::(|_| { // given diff --git a/crates/storage/src/test_utils.rs b/crates/storage/src/test_utils.rs index a40a0215263..348a38a5faf 100644 --- a/crates/storage/src/test_utils.rs +++ b/crates/storage/src/test_utils.rs @@ -202,25 +202,7 @@ macro_rules! fuzz_storage { } /// Asserts that the storage is empty, without any leftovers. -#[cfg(all( - test, - feature = "ink-fuzz-tests", - not(feature = "ink-experimental-engine") -))] -pub fn assert_storage_clean() { - let contract_id = - ink_env::test::get_current_contract_account_id::() - .expect("contract id must exist"); - let used_cells = - ink_env::test::count_used_storage_cells::( - &contract_id, - ) - .expect("used cells must be returned"); - assert_eq!(used_cells, 0); -} - -/// Asserts that the storage is empty, without any leftovers. -#[cfg(all(test, feature = "ink-fuzz-tests", feature = "ink-experimental-engine"))] +#[cfg(all(test, feature = "ink-fuzz-tests"))] pub fn assert_storage_clean() { let contract_id = ink_env::test::callee::(); let used_cells = diff --git a/examples/contract-terminate/Cargo.toml b/examples/contract-terminate/Cargo.toml index a6bd6cc2141..e84457dd4fc 100644 --- a/examples/contract-terminate/Cargo.toml +++ b/examples/contract-terminate/Cargo.toml @@ -31,4 +31,3 @@ std = [ "scale-info/std", ] ink-as-dependency = [] -ink-experimental-engine = ["ink_env/ink-experimental-engine"] diff --git a/examples/contract-terminate/lib.rs b/examples/contract-terminate/lib.rs index 18e0171ca80..707aa4dcff1 100644 --- a/examples/contract-terminate/lib.rs +++ b/examples/contract-terminate/lib.rs @@ -26,69 +26,9 @@ pub mod just_terminates { } } - #[cfg(not(feature = "ink-experimental-engine"))] #[cfg(test)] mod tests { use super::*; - - use ink_env::{ - call, - test, - }; - use ink_lang as ink; - - #[ink::test] - fn terminating_works() { - // given - let accounts = default_accounts(); - let contract_id = ink_env::test::get_current_contract_account_id::< - ink_env::DefaultEnvironment, - >() - .expect("Cannot get contract id"); - set_sender(accounts.alice); - set_balance(contract_id, 100); - let mut contract = JustTerminate::new(); - - // when - let should_terminate = move || contract.terminate_me(); - - // then - ink_env::test::assert_contract_termination::( - should_terminate, - accounts.alice, - 100, - ); - } - - fn default_accounts( - ) -> ink_env::test::DefaultAccounts { - ink_env::test::default_accounts::() - .expect("Off-chain environment should have been initialized already") - } - - fn set_sender(sender: AccountId) { - let callee = ink_env::account_id::(); - test::push_execution_context::( - sender, - callee, - 1000000, - 1000000, - test::CallData::new(call::Selector::new([0x00; 4])), // dummy - ); - } - - fn set_balance(account_id: AccountId, balance: Balance) { - ink_env::test::set_account_balance::( - account_id, balance, - ) - .expect("Cannot set account balance"); - } - } - - #[cfg(feature = "ink-experimental-engine")] - #[cfg(test)] - mod tests_experimental_engine { - use super::*; use ink_lang as ink; #[ink::test] diff --git a/examples/contract-transfer/Cargo.toml b/examples/contract-transfer/Cargo.toml index a6300dae372..ae7d74c3ecd 100644 --- a/examples/contract-transfer/Cargo.toml +++ b/examples/contract-transfer/Cargo.toml @@ -32,4 +32,3 @@ std = [ "scale-info/std", ] ink-as-dependency = [] -ink-experimental-engine = ["ink_env/ink-experimental-engine"] diff --git a/examples/contract-transfer/lib.rs b/examples/contract-transfer/lib.rs index 8c8b9e0fbab..089622c45a2 100644 --- a/examples/contract-transfer/lib.rs +++ b/examples/contract-transfer/lib.rs @@ -62,159 +62,9 @@ pub mod give_me { } } - #[cfg(not(feature = "ink-experimental-engine"))] #[cfg(test)] mod tests { use super::*; - - use ink_env::{ - call, - test, - }; - use ink_lang as ink; - - #[ink::test] - fn transfer_works() { - // given - let contract_balance = 100; - let accounts = default_accounts(); - let mut give_me = create_contract(contract_balance); - - // when - set_sender(accounts.eve); - set_balance(accounts.eve, 0); - give_me.give_me(80); - - // then - assert_eq!(get_balance(accounts.eve), 80); - } - - #[ink::test] - #[should_panic(expected = "insufficient funds!")] - fn transfer_fails_insufficient_funds() { - // given - let contract_balance = 100; - let accounts = default_accounts(); - let mut give_me = create_contract(contract_balance); - - // when - set_sender(accounts.eve); - give_me.give_me(120); - - // then - // `give_me` must already have panicked here - } - - #[ink::test] - fn test_transferred_value() { - // given - let accounts = default_accounts(); - let give_me = create_contract(100); - - // when - set_sender(accounts.eve); - let mut data = ink_env::test::CallData::new(ink_env::call::Selector::new([ - 0xCA, 0xFE, 0xBA, 0xBE, - ])); - data.push_arg(&accounts.eve); - let mock_transferred_value = 10; - - // Push the new execution context which sets Eve as caller and - // the `mock_transferred_value` as the value which the contract - // will see as transferred to it. - ink_env::test::push_execution_context::( - accounts.eve, - contract_id(), - 1000000, - mock_transferred_value, - data, - ); - - // then - // there must be no panic - give_me.was_it_ten(); - } - - #[ink::test] - #[should_panic(expected = "payment was not ten")] - fn test_transferred_value_must_fail() { - // given - let accounts = default_accounts(); - let give_me = create_contract(100); - - // when - set_sender(accounts.eve); - let mut data = ink_env::test::CallData::new(ink_env::call::Selector::new([ - 0xCA, 0xFE, 0xBA, 0xBE, - ])); - data.push_arg(&accounts.eve); - let mock_transferred_value = 13; - - // Push the new execution context which sets Eve as caller and - // the `mock_transferred_value` as the value which the contract - // will see as transferred to it. - ink_env::test::push_execution_context::( - accounts.eve, - contract_id(), - 1000000, - mock_transferred_value, - data, - ); - - // then - give_me.was_it_ten(); - } - - /// Creates a new instance of `GiveMe` with `initial_balance`. - /// - /// Returns the `contract_instance`. - fn create_contract(initial_balance: Balance) -> GiveMe { - let accounts = default_accounts(); - set_sender(accounts.alice); - set_balance(contract_id(), initial_balance); - GiveMe::new() - } - - fn contract_id() -> AccountId { - ink_env::test::get_current_contract_account_id::( - ) - .expect("Cannot get contract id") - } - - fn set_sender(sender: AccountId) { - let callee = ink_env::account_id::(); - test::push_execution_context::( - sender, - callee, - 1000000, - 1000000, - test::CallData::new(call::Selector::new([0x00; 4])), // dummy - ); - } - - fn default_accounts( - ) -> ink_env::test::DefaultAccounts { - ink_env::test::default_accounts::() - .expect("Off-chain environment should have been initialized already") - } - - fn set_balance(account_id: AccountId, balance: Balance) { - ink_env::test::set_account_balance::( - account_id, balance, - ) - .expect("Cannot set account balance"); - } - - fn get_balance(account_id: AccountId) -> Balance { - ink_env::test::get_account_balance::(account_id) - .expect("Cannot set account balance") - } - } - - #[cfg(feature = "ink-experimental-engine")] - #[cfg(test)] - mod tests_experimental_engine { - use super::*; use ink_lang as ink; #[ink::test] diff --git a/examples/erc1155/Cargo.toml b/examples/erc1155/Cargo.toml index ec29904b510..81703e052d1 100644 --- a/examples/erc1155/Cargo.toml +++ b/examples/erc1155/Cargo.toml @@ -33,4 +33,3 @@ std = [ "scale-info/std", ] ink-as-dependency = [] -ink-experimental-engine = ["ink_env/ink-experimental-engine"] diff --git a/examples/erc1155/lib.rs b/examples/erc1155/lib.rs index 4404ec4de15..60a643eb32f 100644 --- a/examples/erc1155/lib.rs +++ b/examples/erc1155/lib.rs @@ -625,32 +625,12 @@ mod erc1155 { use ink_lang as ink; - #[cfg(feature = "ink-experimental-engine")] fn set_sender(sender: AccountId) { ink_env::test::set_caller::(sender); } - #[cfg(not(feature = "ink-experimental-engine"))] - fn set_sender(sender: AccountId) { - const WALLET: [u8; 32] = [7; 32]; - ink_env::test::push_execution_context::( - sender, - WALLET.into(), - 1000000, - 1000000, - ink_env::test::CallData::new(ink_env::call::Selector::new([0x00; 4])), /* dummy */ - ); - } - - #[cfg(feature = "ink-experimental-engine")] - fn default_accounts() -> ink_env::test::DefaultAccounts { - ink_env::test::default_accounts::() - } - - #[cfg(not(feature = "ink-experimental-engine"))] fn default_accounts() -> ink_env::test::DefaultAccounts { ink_env::test::default_accounts::() - .expect("off-chain environment should have been initialized already") } fn alice() -> AccountId { diff --git a/examples/erc20/Cargo.toml b/examples/erc20/Cargo.toml index 7234ac890e0..9f291316e2c 100644 --- a/examples/erc20/Cargo.toml +++ b/examples/erc20/Cargo.toml @@ -31,4 +31,3 @@ std = [ "scale-info/std", ] ink-as-dependency = [] -ink-experimental-engine = ["ink_env/ink-experimental-engine"] diff --git a/examples/erc20/lib.rs b/examples/erc20/lib.rs index 088f8bfcc0b..683c4a2f914 100644 --- a/examples/erc20/lib.rs +++ b/examples/erc20/lib.rs @@ -222,325 +222,8 @@ mod erc20 { } } - /// Unit tests. - #[cfg(not(feature = "ink-experimental-engine"))] #[cfg(test)] mod tests { - /// Imports all the definitions from the outer scope so we can use them here. - use super::*; - - type Event = ::Type; - - use ink_lang as ink; - - fn assert_transfer_event( - event: &ink_env::test::EmittedEvent, - expected_from: Option, - expected_to: Option, - expected_value: Balance, - ) { - let decoded_event = ::decode(&mut &event.data[..]) - .expect("encountered invalid contract event data buffer"); - if let Event::Transfer(Transfer { from, to, value }) = decoded_event { - assert_eq!(from, expected_from, "encountered invalid Transfer.from"); - assert_eq!(to, expected_to, "encountered invalid Transfer.to"); - assert_eq!(value, expected_value, "encountered invalid Trasfer.value"); - } else { - panic!("encountered unexpected event kind: expected a Transfer event") - } - let expected_topics = vec![ - encoded_into_hash(&PrefixedValue { - value: b"Erc20::Transfer", - prefix: b"", - }), - encoded_into_hash(&PrefixedValue { - prefix: b"Erc20::Transfer::from", - value: &expected_from, - }), - encoded_into_hash(&PrefixedValue { - prefix: b"Erc20::Transfer::to", - value: &expected_to, - }), - encoded_into_hash(&PrefixedValue { - prefix: b"Erc20::Transfer::value", - value: &expected_value, - }), - ]; - for (n, (actual_topic, expected_topic)) in - event.topics.iter().zip(expected_topics).enumerate() - { - let topic = actual_topic - .decode::() - .expect("encountered invalid topic encoding"); - assert_eq!(topic, expected_topic, "encountered invalid topic at {}", n); - } - } - - /// The default constructor does its job. - #[ink::test] - fn new_works() { - // Constructor works. - let _erc20 = Erc20::new(100); - - // Transfer event triggered during initial construction. - let emitted_events = ink_env::test::recorded_events().collect::>(); - assert_eq!(1, emitted_events.len()); - - assert_transfer_event( - &emitted_events[0], - None, - Some(AccountId::from([0x01; 32])), - 100, - ); - } - - /// The total supply was applied. - #[ink::test] - fn total_supply_works() { - // Constructor works. - let erc20 = Erc20::new(100); - // Transfer event triggered during initial construction. - let emitted_events = ink_env::test::recorded_events().collect::>(); - assert_transfer_event( - &emitted_events[0], - None, - Some(AccountId::from([0x01; 32])), - 100, - ); - // Get the token total supply. - assert_eq!(erc20.total_supply(), 100); - } - - /// Get the actual balance of an account. - #[ink::test] - fn balance_of_works() { - // Constructor works - let erc20 = Erc20::new(100); - // Transfer event triggered during initial construction - let emitted_events = ink_env::test::recorded_events().collect::>(); - assert_transfer_event( - &emitted_events[0], - None, - Some(AccountId::from([0x01; 32])), - 100, - ); - let accounts = - ink_env::test::default_accounts::() - .expect("Cannot get accounts"); - // Alice owns all the tokens on contract instantiation - assert_eq!(erc20.balance_of(accounts.alice), 100); - // Bob does not owns tokens - assert_eq!(erc20.balance_of(accounts.bob), 0); - } - - #[ink::test] - fn transfer_works() { - // Constructor works. - let mut erc20 = Erc20::new(100); - // Transfer event triggered during initial construction. - let accounts = - ink_env::test::default_accounts::() - .expect("Cannot get accounts"); - - assert_eq!(erc20.balance_of(accounts.bob), 0); - // Alice transfers 10 tokens to Bob. - assert_eq!(erc20.transfer(accounts.bob, 10), Ok(())); - // Bob owns 10 tokens. - assert_eq!(erc20.balance_of(accounts.bob), 10); - - let emitted_events = ink_env::test::recorded_events().collect::>(); - assert_eq!(emitted_events.len(), 2); - // Check first transfer event related to ERC-20 instantiation. - assert_transfer_event( - &emitted_events[0], - None, - Some(AccountId::from([0x01; 32])), - 100, - ); - // Check the second transfer event relating to the actual trasfer. - assert_transfer_event( - &emitted_events[1], - Some(AccountId::from([0x01; 32])), - Some(AccountId::from([0x02; 32])), - 10, - ); - } - - #[ink::test] - fn invalid_transfer_should_fail() { - // Constructor works. - let mut erc20 = Erc20::new(100); - let accounts = - ink_env::test::default_accounts::() - .expect("Cannot get accounts"); - - assert_eq!(erc20.balance_of(accounts.bob), 0); - // Get contract address. - let callee = ink_env::account_id::(); - // Create call - let mut data = - ink_env::test::CallData::new(ink_env::call::Selector::new([0x00; 4])); // balance_of - data.push_arg(&accounts.bob); - // Push the new execution context to set Bob as caller - ink_env::test::push_execution_context::( - accounts.bob, - callee, - 1000000, - 1000000, - data, - ); - - // Bob fails to transfers 10 tokens to Eve. - assert_eq!( - erc20.transfer(accounts.eve, 10), - Err(Error::InsufficientBalance) - ); - // Alice owns all the tokens. - assert_eq!(erc20.balance_of(accounts.alice), 100); - assert_eq!(erc20.balance_of(accounts.bob), 0); - assert_eq!(erc20.balance_of(accounts.eve), 0); - - // Transfer event triggered during initial construction. - let emitted_events = ink_env::test::recorded_events().collect::>(); - assert_eq!(emitted_events.len(), 1); - assert_transfer_event( - &emitted_events[0], - None, - Some(AccountId::from([0x01; 32])), - 100, - ); - } - - #[ink::test] - fn transfer_from_works() { - // Constructor works. - let mut erc20 = Erc20::new(100); - // Transfer event triggered during initial construction. - let accounts = - ink_env::test::default_accounts::() - .expect("Cannot get accounts"); - - // Bob fails to transfer tokens owned by Alice. - assert_eq!( - erc20.transfer_from(accounts.alice, accounts.eve, 10), - Err(Error::InsufficientAllowance) - ); - // Alice approves Bob for token transfers on her behalf. - assert_eq!(erc20.approve(accounts.bob, 10), Ok(())); - - // The approve event takes place. - assert_eq!(ink_env::test::recorded_events().count(), 2); - - // Get contract address. - let callee = ink_env::account_id::(); - // Create call. - let mut data = - ink_env::test::CallData::new(ink_env::call::Selector::new([0x00; 4])); // balance_of - data.push_arg(&accounts.bob); - // Push the new execution context to set Bob as caller. - ink_env::test::push_execution_context::( - accounts.bob, - callee, - 1000000, - 1000000, - data, - ); - - // Bob transfers tokens from Alice to Eve. - assert_eq!( - erc20.transfer_from(accounts.alice, accounts.eve, 10), - Ok(()) - ); - // Eve owns tokens. - assert_eq!(erc20.balance_of(accounts.eve), 10); - - // Check all transfer events that happened during the previous calls: - let emitted_events = ink_env::test::recorded_events().collect::>(); - assert_eq!(emitted_events.len(), 3); - assert_transfer_event( - &emitted_events[0], - None, - Some(AccountId::from([0x01; 32])), - 100, - ); - // The second event `emitted_events[1]` is an Approve event that we skip checking. - assert_transfer_event( - &emitted_events[2], - Some(AccountId::from([0x01; 32])), - Some(AccountId::from([0x05; 32])), - 10, - ); - } - - #[ink::test] - fn allowance_must_not_change_on_failed_transfer() { - let mut erc20 = Erc20::new(100); - let accounts = - ink_env::test::default_accounts::() - .expect("Cannot get accounts"); - - // Alice approves Bob for token transfers on her behalf. - let alice_balance = erc20.balance_of(accounts.alice); - let initial_allowance = alice_balance + 2; - assert_eq!(erc20.approve(accounts.bob, initial_allowance), Ok(())); - - // Get contract address. - let callee = ink_env::account_id::(); - // Create call. - let mut data = - ink_env::test::CallData::new(ink_env::call::Selector::new([0x00; 4])); // balance_of - data.push_arg(&accounts.bob); - // Push the new execution context to set Bob as caller. - ink_env::test::push_execution_context::( - accounts.bob, - callee, - 1000000, - 1000000, - data, - ); - - // Bob tries to transfer tokens from Alice to Eve. - let emitted_events_before = ink_env::test::recorded_events(); - assert_eq!( - erc20.transfer_from(accounts.alice, accounts.eve, alice_balance + 1), - Err(Error::InsufficientBalance) - ); - // Allowance must have stayed the same - assert_eq!( - erc20.allowance(accounts.alice, accounts.bob), - initial_allowance - ); - // No more events must have been emitted - let emitted_events_after = ink_env::test::recorded_events(); - assert_eq!(emitted_events_before.count(), emitted_events_after.count()); - } - } - - /// For calculating the event topic hash. - struct PrefixedValue<'a, 'b, T> { - pub prefix: &'a [u8], - pub value: &'b T, - } - - impl scale::Encode for PrefixedValue<'_, '_, X> - where - X: scale::Encode, - { - #[inline] - fn size_hint(&self) -> usize { - self.prefix.size_hint() + self.value.size_hint() - } - - #[inline] - fn encode_to(&self, dest: &mut T) { - self.prefix.encode_to(dest); - self.value.encode_to(dest); - } - } - - #[cfg(feature = "ink-experimental-engine")] - #[cfg(test)] - mod tests_experimental_engine { use super::*; use ink_env::Clear;