diff --git a/Cargo.toml b/Cargo.toml index a00d3feb174..5781351fc51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,7 @@ members = [ "abi", "core", - "lang", "lang2", - "model", "prelude", "utils", ] diff --git a/LICENSE_TEMPLATE b/LICENSE_TEMPLATE index 07627650b1d..a346cb8dcfa 100644 --- a/LICENSE_TEMPLATE +++ b/LICENSE_TEMPLATE @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright {20\d{2}}-{20\d{2}} 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. diff --git a/core/Cargo.toml b/core/Cargo.toml index 027992c873d..62d6dac2f07 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -23,9 +23,16 @@ ink_prelude = { path = "../prelude/", default-features = false } scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive", "full"] } type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true } -derive_more = { version = "0.99.2", default-features = false, features = ["from"] } +derive_more = { version = "0.99.2", default-features = false, features = ["from", "display"] } smallvec = { version = "1.0", default-features = false, features = ["union"] } cfg-if = "0.1" +num-traits = { version = "0.2.1", default-features = false, feature = ["i128"] } + +# Only used in the off-chain environment. +# +# Sadly couldn't be marked as dev-dependency. +# Never use this crate outside of the off-chain environment! +rand = { version = "0.7", default-features = false, features = ["alloc"] } [features] default = ["test-env"] @@ -38,6 +45,8 @@ std = [ "ink_prelude/std", "scale/std", "type-metadata/std", + "rand/std", + "num-traits/std", ] ink-generate-abi = [ "ink_abi", diff --git a/core/src/env/api.rs b/core/src/env/api.rs deleted file mode 100644 index 377bb78bd40..00000000000 --- a/core/src/env/api.rs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2018-2019 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 scale::{ - Decode, - Encode, -}; - -use super::ContractEnvStorage; -use crate::{ - env::{ - traits::{ - Env, - EnvTypes, - }, - CallError, - CreateError, - EnvStorage as _, - }, - storage::Key, -}; -use ink_prelude::vec::Vec; - -/// Stores the given value under the specified key in the contract storage. -/// -/// # Safety -/// -/// This operation is unsafe because it does not check for key integrity. -/// Users can compare this operation with a raw pointer dereferencing in Rust. -pub unsafe fn store(key: Key, value: &[u8]) { - ContractEnvStorage::store(key, value) -} - -/// Clears the data stored at the given key from the contract storage. -/// -/// # Safety -/// -/// This operation is unsafe because it does not check for key integrity. -/// Users can compare this operation with a raw pointer dereferencing in Rust. -pub unsafe fn clear(key: Key) { - ContractEnvStorage::clear(key) -} - -/// Loads the data stored at the given key from the contract storage. -/// -/// # Safety -/// -/// This operation is unsafe because it does not check for key integrity. -/// Users can compare this operation with a raw pointer dereferencing in Rust. -pub unsafe fn load(key: Key) -> Option> { - ContractEnvStorage::load(key) -} - -/// Returns the given data back to the caller. -/// -/// # Note -/// -/// This operation must be the last operation performed by a called -/// smart contract before it returns the execution back to its caller. -pub fn return_data(data: T) -where - T: Encode, - E: Env, -{ - E::return_data(&data.encode()[..]) -} - -/// Dispatches a Call into the runtime, for invoking other substrate -/// modules. Dispatched only after successful contract execution. -/// -/// The encoded Call MUST be decodable by the target substrate runtime. -/// If decoding fails, then the smart contract execution will fail. -pub fn dispatch_call(call: C) -where - T: Env, - C: Into<::Call>, -{ - T::dispatch_raw_call(&call.into().encode()[..]) -} - -/// Invokes a remote smart contract. -/// -/// Does not expect to receive return data back. -/// Use this whenever you call a remote smart contract that returns nothing back. -pub fn call_invoke( - callee: T::AccountId, - gas: u64, - value: T::Balance, - input_data: &[u8], -) -> Result<(), CallError> -where - T: Env, -{ - T::call_invoke(callee, gas, value, input_data) -} - -/// Evaluates a remote smart contract. -/// -/// Expects to receive return data back. -/// Use this whenever calling a remote smart contract that returns a value. -pub fn call_evaluate( - callee: T::AccountId, - gas: u64, - value: T::Balance, - input_data: &[u8], -) -> Result -where - T: Env, - R: Decode, -{ - T::call_evaluate(callee, gas, value, input_data) -} - -/// Instantiates a new smart contract. -/// -/// Upon success returns the account ID of the newly created smart contract. -pub fn create( - code_hash: T::Hash, - gas_limit: u64, - value: T::Balance, - input_data: &[u8], -) -> Result -where - T: Env, -{ - T::create(code_hash, gas_limit, value, input_data) -} diff --git a/core/src/env/calls.rs b/core/src/env/calls.rs deleted file mode 100644 index ba42cd33c89..00000000000 --- a/core/src/env/calls.rs +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2018-2019 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 core::marker::PhantomData; - -use scale::Decode; - -use crate::env::{ - self, - CallError, - CreateError, - Env, - EnvTypes, -}; -use ink_prelude::{ - vec, - vec::Vec, -}; - -/// Consists of the input data to a call. -/// The first four bytes are the function selector and the rest are SCALE encoded inputs. -struct CallAbi { - /// Already encoded function selector and inputs. - raw: Vec, -} - -impl CallAbi { - /// Creates new call ABI data for the given selector. - pub fn new(selector: [u8; 4]) -> Self { - Self { - raw: vec![selector[0], selector[1], selector[2], selector[3]], - } - } - - /// Pushes the given argument onto the call ABI data in encoded form. - pub fn push_arg(self, arg: &A) -> Self - where - A: scale::Encode, - { - let mut this = self; - this.raw.extend(&arg.encode()); - this - } - - /// Returns the underlying byte representation. - pub fn to_bytes(&self) -> &[u8] { - &self.raw - } -} - -/// Represents a return type. -/// -/// Used as a marker type to differentiate at compile-time between invoke and evaluate. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct ReturnType(PhantomData); - -/// Builds up contract instantiations. -pub struct CreateBuilder -where - E: EnvTypes, -{ - /// The code hash of the created contract. - code_hash: E::Hash, - /// The maximum gas costs allowed for the instantiation. - gas_limit: u64, - /// The transferred value for the newly created contract. - value: E::Balance, - /// The input data for the instantation. - raw_input: Vec, - /// The type of the instantiated contract. - contract_marker: PhantomData C>, -} - -impl CreateBuilder -where - E: EnvTypes, - E::Balance: Default, -{ - /// Creates a new create builder to guide instantiation of a smart contract. - pub fn new(code_hash: E::Hash) -> Self { - Self { - code_hash, - gas_limit: 0, - value: Default::default(), - raw_input: Vec::new(), - contract_marker: Default::default(), - } - } -} - -/// Builds up a call. -pub struct CallBuilder -where - E: EnvTypes, -{ - /// The account ID of the to-be-called smart contract. - account_id: E::AccountId, - /// The maximum gas costs allowed for the call. - gas_limit: u64, - /// The transferred value for the call. - value: E::Balance, - /// The expected return type. - return_type: PhantomData>, - /// The already encoded call ABI data. - raw_input: CallAbi, -} - -impl CreateBuilder -where - E: EnvTypes, -{ - /// Sets the maximumly allowed gas costs for the call. - pub fn gas_limit(self, gas_limit: u64) -> Self { - let mut this = self; - this.gas_limit = gas_limit; - this - } - - /// Sets the value transferred upon the execution of the call. - pub fn value(self, value: E::Balance) -> Self { - let mut this = self; - this.value = value; - this - } - - /// Pushes an argument to the inputs of the call. - pub fn push_arg(self, arg: &A) -> Self - where - A: scale::Encode, - { - let mut this = self; - this.raw_input.extend(&arg.encode()); - this - } -} - -/// Needed because of conflicting implementations of From for T -/// resulting of generated `ink_lang` code. -pub trait FromAccountId -where - E: Env, -{ - fn from_account_id(account_id: ::AccountId) -> Self; -} - -impl CreateBuilder -where - E: Env, - C: FromAccountId, -{ - /// Runs the process to create and instantiate a new smart contract. - /// Returns the account ID of the newly created smart contract. - pub fn create(self) -> Result { - env::create::(self.code_hash, self.gas_limit, self.value, &self.raw_input) - .map(FromAccountId::from_account_id) - } -} - -impl CallBuilder> -where - E: EnvTypes, - E::Balance: Default, -{ - /// Instantiates an evaluatable (returns data) remote smart contract call. - pub fn eval(account_id: E::AccountId, selector: [u8; 4]) -> Self { - Self { - account_id, - gas_limit: 0, - value: E::Balance::default(), - return_type: PhantomData, - raw_input: CallAbi::new(selector), - } - } -} - -impl CallBuilder -where - E: EnvTypes, - E::Balance: Default, -{ - /// Instantiates a non-evaluatable (returns no data) remote smart contract call. - pub fn invoke(account_id: E::AccountId, selector: [u8; 4]) -> Self { - Self { - account_id, - gas_limit: 0, - value: E::Balance::default(), - return_type: PhantomData, - raw_input: CallAbi::new(selector), - } - } -} - -impl CallBuilder -where - E: EnvTypes, -{ - /// Sets the maximumly allowed gas costs for the call. - pub fn gas_limit(self, gas_limit: u64) -> Self { - let mut this = self; - this.gas_limit = gas_limit; - this - } - - /// Sets the value transferred upon the execution of the call. - pub fn value(self, value: E::Balance) -> Self { - let mut this = self; - this.value = value; - this - } - - /// Pushes an argument to the inputs of the call. - pub fn push_arg(self, arg: &A) -> Self - where - A: scale::Encode, - { - let mut this = self; - this.raw_input = this.raw_input.push_arg(arg); - this - } -} - -impl CallBuilder> -where - E: Env, - R: Decode, -{ - /// Fires the call to the remote smart contract. - /// Returns the returned data back to the caller. - pub fn fire(self) -> Result { - env::call_evaluate::( - self.account_id, - self.gas_limit, - self.value, - self.raw_input.to_bytes(), - ) - } -} - -impl CallBuilder -where - E: Env, -{ - /// Fires the call to the remote smart contract. - pub fn fire(self) -> Result<(), CallError> { - env::call_invoke::( - self.account_id, - self.gas_limit, - self.value, - self.raw_input.to_bytes(), - ) - } -} diff --git a/core/src/env/mod.rs b/core/src/env/mod.rs deleted file mode 100644 index fa38e6592ed..00000000000 --- a/core/src/env/mod.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2018-2019 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. - -//! Contract environments. -//! -//! A contract is able to operate on different environments. -//! -//! Currently the SRML environment operating directly on the -//! substrate runtime module library (SRML) and the test -//! environment for testing and inspecting contracts are -//! provided. -//! -//! By default the SRML environment is used. -//! To enable the test environment the `test-env` crate feature -//! has to be enabled. - -mod api; -mod calls; -mod srml; -mod traits; - -#[cfg(feature = "test-env")] -pub mod test; - -#[cfg(feature = "test-env")] -mod test_env; - -pub use api::*; -pub use traits::*; - -pub use self::{ - calls::{ - CallBuilder, - CreateBuilder, - FromAccountId, - ReturnType, - }, - srml::DefaultSrmlTypes, - traits::{ - CallError, - CreateError, - }, -}; - -/// The storage environment implementation that is currently being used. -/// -/// This may be either -/// - `SrmlEnvStorage` for real contract storage -/// manipulation that may happen on-chain. -/// - `TestEnvStorage` for emulating a contract environment -/// that can be inspected by the user and used -/// for testing contracts off-chain. -#[cfg(not(feature = "test-env"))] -pub(self) type ContractEnvStorage = self::srml::SrmlEnvStorage; - -/// The storage environment implementation for the test environment. -#[cfg(feature = "test-env")] -pub(self) type ContractEnvStorage = self::test_env::TestEnvStorage; - -/// The contract environment implementation that is currently being used -/// -/// Generic over user supplied EnvTypes for different runtimes -#[cfg(not(feature = "test-env"))] -pub type ContractEnv = self::srml::SrmlEnv; - -/// The contract environment implementation for the test environment -#[cfg(feature = "test-env")] -pub type ContractEnv = self::test_env::TestEnv; diff --git a/core/src/env/srml/mod.rs b/core/src/env/srml/mod.rs deleted file mode 100644 index 66c58b21aca..00000000000 --- a/core/src/env/srml/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018-2019 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. - -#[cfg(not(feature = "test-env"))] -mod srml_only; - -mod types; - -pub use self::types::DefaultSrmlTypes; - -#[cfg(not(feature = "test-env"))] -pub use self::srml_only::{ - sys, - SrmlEnv, - SrmlEnvStorage, -}; diff --git a/core/src/env/srml/srml_only/impls.rs b/core/src/env/srml/srml_only/impls.rs deleted file mode 100644 index 38253cb7653..00000000000 --- a/core/src/env/srml/srml_only/impls.rs +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2018-2019 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 core::marker::PhantomData; - -use scale::{ - Decode, - Encode, -}; - -use crate::{ - env::{ - srml::sys, - CallError, - CreateError, - Env, - EnvStorage, - EnvTypes, - }, - storage::Key, -}; -use ink_prelude::vec::Vec; - -/// Load the contents of the scratch buffer -fn read_scratch_buffer() -> Vec { - let size = unsafe { sys::ext_scratch_size() }; - let mut value = Vec::new(); - if size > 0 { - value.resize(size as usize, 0); - unsafe { - sys::ext_scratch_read(value.as_mut_ptr() as u32, 0, size); - } - } - value -} - -/// Writes the contents of `data` into the scratch buffer. -fn write_scratch_buffer(data: &[u8]) { - unsafe { - sys::ext_scratch_write(data.as_ptr() as u32, data.len() as u32); - } -} - -/// The SRML contract environment storage -pub enum SrmlEnvStorage {} - -impl EnvStorage for SrmlEnvStorage { - /// Stores the given bytes under the given key. - unsafe fn store(key: Key, value: &[u8]) { - sys::ext_set_storage( - key.as_bytes().as_ptr() as u32, - 1, - value.as_ptr() as u32, - value.len() as u32, - ); - } - - /// Clears the value stored under the given key. - unsafe fn clear(key: Key) { - sys::ext_set_storage(key.as_bytes().as_ptr() as u32, 0, 0, 0) - } - - /// Loads the value stored at the given key if any. - unsafe fn load(key: Key) -> Option> { - const SUCCESS: u32 = 0; - if sys::ext_get_storage(key.as_bytes().as_ptr() as u32) == SUCCESS { - return Some(read_scratch_buffer()) - } - None - } -} - -/// The SRML contracts environment. -pub struct SrmlEnv -where - T: EnvTypes, -{ - marker: PhantomData T>, -} - -impl EnvTypes for SrmlEnv -where - T: EnvTypes, -{ - type AccountId = ::AccountId; - type Balance = ::Balance; - type Hash = ::Hash; - type Moment = ::Moment; - type BlockNumber = ::BlockNumber; - type Call = ::Call; -} - -macro_rules! impl_getters_for_srml_env { - ( $( ($name:ident, $ext_name:ident, $ret_type:ty) ),* ) => { - $( - fn $name() -> $ret_type { - unsafe { sys::$ext_name() }; - Decode::decode(&mut &read_scratch_buffer()[..]) - .expect(concat!( - stringify!($name), " expects to receive a correctly sized buffer" - )) - } - )* - } -} - -impl SrmlEnv -where - T: EnvTypes, -{ - fn call( - callee: ::AccountId, - gas: u64, - value: ::Balance, - input_data: &[u8], - ) -> u32 { - let callee = callee.encode(); - let value = value.encode(); - unsafe { - sys::ext_call( - callee.as_ptr() as u32, - callee.len() as u32, - gas, - value.as_ptr() as u32, - value.len() as u32, - input_data.as_ptr() as u32, - input_data.len() as u32, - ) - } - } -} - -impl Env for SrmlEnv -where - T: EnvTypes, -{ - fn input() -> Vec { - read_scratch_buffer() - } - - impl_getters_for_srml_env!( - (address, ext_address, ::AccountId), - (balance, ext_balance, ::Balance), - (caller, ext_caller, ::AccountId), - (random_seed, ext_random_seed, ::Hash), - (now, ext_now, ::Moment), - ( - block_number, - ext_block_number, - ::BlockNumber - ), - (gas_price, ext_gas_price, ::Balance), - (gas_left, ext_gas_left, ::Balance), - ( - value_transferred, - ext_value_transferred, - ::Balance - ) - ); - - fn return_data(data: &[u8]) { - write_scratch_buffer(data) - } - - fn println(content: &str) { - unsafe { sys::ext_println(content.as_ptr() as u32, content.len() as u32) } - } - - fn deposit_raw_event(topics: &[::Hash], data: &[u8]) { - unsafe { - sys::ext_deposit_event( - topics.as_ptr() as u32, - topics.len() as u32, - data.as_ptr() as u32, - data.len() as u32, - ) - } - } - - fn dispatch_raw_call(data: &[u8]) { - unsafe { sys::ext_dispatch_call(data.as_ptr() as u32, data.len() as u32) } - } - - fn call_invoke( - callee: ::AccountId, - gas: u64, - value: ::Balance, - input_data: &[u8], - ) -> Result<(), CallError> { - let result = Self::call(callee, gas, value, input_data); - if result != 0 { - return Err(CallError) - } - Ok(()) - } - - fn call_evaluate( - callee: ::AccountId, - gas: u64, - value: ::Balance, - input_data: &[u8], - ) -> Result { - let result = Self::call(callee, gas, value, input_data); - if result != 0 { - return Err(CallError) - } - U::decode(&mut &read_scratch_buffer()[..]).map_err(|_| CallError) - } - - fn create( - code_hash: ::Hash, - gas_limit: u64, - value: ::Balance, - input_data: &[u8], - ) -> Result<::AccountId, CreateError> { - let result = { - let code_hash = code_hash.encode(); - let value = value.encode(); - unsafe { - sys::ext_create( - code_hash.as_ptr() as u32, - code_hash.len() as u32, - gas_limit, - value.as_ptr() as u32, - value.len() as u32, - input_data.as_ptr() as u32, - input_data.len() as u32, - ) - } - }; - if result != 0 { - return Err(CreateError) - } - ::AccountId::decode(&mut &read_scratch_buffer()[..]) - .map_err(|_| CreateError) - } -} diff --git a/core/src/env/srml/srml_only/mod.rs b/core/src/env/srml/srml_only/mod.rs deleted file mode 100644 index 407a257d201..00000000000 --- a/core/src/env/srml/srml_only/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018-2019 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 sys; - -pub use self::impls::{ - SrmlEnv, - SrmlEnvStorage, -}; diff --git a/core/src/env/srml/srml_only/sys.rs b/core/src/env/srml/srml_only/sys.rs deleted file mode 100644 index 7b564f9ca87..00000000000 --- a/core/src/env/srml/srml_only/sys.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2018-2019 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. - -//! External C API to communicate with substrate contracts runtime module. -//! -//! Refer to substrate SRML contract module for more documentation. - -extern "C" { - /// Creates a new smart contract account. - pub fn ext_create( - init_code_ptr: u32, - init_code_len: u32, - gas: u64, - value_ptr: u32, - value_len: u32, - input_data_ptr: u32, - input_data_len: u32, - ) -> u32; - - /// Calls a remote smart contract. - /// - /// Eventually returned data is put into the scratch buffer. - pub fn ext_call( - callee_ptr: u32, - callee_len: u32, - gas: u64, - value_ptr: u32, - value_len: u32, - input_data_ptr: u32, - input_data_len: u32, - ) -> u32; - - /// Tells the execution environment to load the raw byte - /// representation of the caller into the scratch buffer. - pub fn ext_caller(); - - /// Prints the contents of `str_ptr` to the console. - /// - /// # Note - /// - /// This must only be used in `--dev` chain environments! - pub fn ext_println(str_ptr: u32, str_len: u32); - - /// Deposits raw event data through the Contracts module. - pub fn ext_deposit_event( - topics_ptr: u32, - topics_len: u32, - data_ptr: u32, - data_len: u32, - ); - - /// Writes the contents of the buffer at `value_ptr` into the - /// storage slot associated with the given key or clears the - /// associated slot if `value_non_null` is `0`. - pub fn ext_set_storage( - key_ptr: u32, - value_non_null: u32, - value_ptr: u32, - value_len: u32, - ); - - /// Dispatches a Call into the runtime, for invocation of substrate modules - /// - /// Call data is written to the scratch buffer, and it MUST be decodable into the host chain - /// runtime `Call` type. - pub fn ext_dispatch_call(call_ptr: u32, call_len: u32); - - /// Tells the execution environment to load the contents - /// stored at the given key into the scratch buffer. - pub fn ext_get_storage(key_ptr: u32) -> u32; - - /// Returns the length in bytes of the scratch buffer. - pub fn ext_scratch_size() -> u32; - - /// Reads the contents of the scratch buffer at the host site starting at `offset` and writes them to the - /// buffer starting at `dst_ptr` with length `len` on the smart contract site. - pub fn ext_scratch_read(dst_ptr: u32, offset: u32, len: u32); - - /// Writes the contents of the given data buffer into the scratch buffer on the host side. - pub fn ext_scratch_write(src_ptr: u32, len: u32); - - /// Stores the address of the current contract into the scratch buffer. - pub fn ext_address(); - - /// Stores the balance of the current account into the scratch buffer. - /// - /// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten. - pub fn ext_balance(); - - /// Stores the gas price for the current transaction into the scratch buffer. - /// - /// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten. - pub fn ext_gas_price(); - - /// Stores the amount of gas left into the scratch buffer. - /// - /// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten. - pub fn ext_gas_left(); - - /// Stores the value transferred along with this call or as endowment into the scratch buffer. - /// - /// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten. - pub fn ext_value_transferred(); - - /// Load the latest block RNG seed into the scratch buffer. - pub fn ext_random_seed(); - - /// Load the latest block timestamp into the scratch buffer. - pub fn ext_now(); - - /// Load the latest block number into the scratch buffer. - pub fn ext_block_number(); -} diff --git a/core/src/env/srml/types.rs b/core/src/env/srml/types.rs deleted file mode 100644 index 2ecc5cd2283..00000000000 --- a/core/src/env/srml/types.rs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2018-2019 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::{ - env::EnvTypes, - ink_core, - storage::Flush, -}; -use core::{ - array::TryFromSliceError, - convert::TryFrom, -}; -use scale::{ - Decode, - Encode, -}; - -#[cfg(feature = "ink-generate-abi")] -use type_metadata::Metadata; - -/// The SRML fundamental types. -#[cfg_attr(feature = "test-env", derive(Debug, Clone, PartialEq, Eq))] -pub enum DefaultSrmlTypes {} - -/// Empty enum for default Call type, so it cannot be constructed. -/// For calling into the runtime, a user defined Call type required. -/// See https://github.com/paritytech/ink-types-node-runtime. -/// -/// # Note -/// -/// Some traits are only implemented to satisfy the constraints of the test -/// environment, in order to keep the code size small. -#[cfg_attr(feature = "test-env", derive(Debug, Clone, PartialEq, Eq))] -pub enum Call {} -impl scale::Encode for Call {} - -/// This implementation is only to satisfy the Decode constraint in the -/// test environment. Since Call cannot be constructed then just return -/// None, but this should never be called. -#[cfg(feature = "test-env")] -impl scale::Decode for Call { - fn decode(_value: &mut I) -> Result { - Err("Call cannot be instantiated".into()) - } -} - -impl EnvTypes for DefaultSrmlTypes { - type AccountId = AccountId; - type Balance = Balance; - type Hash = Hash; - type Moment = Moment; - type BlockNumber = BlockNumber; - type Call = Call; -} - -/// The default SRML address type. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encode, Decode, Flush)] -#[cfg_attr(feature = "ink-generate-abi", derive(Metadata))] -pub struct AccountId([u8; 32]); - -impl From<[u8; 32]> for AccountId { - fn from(address: [u8; 32]) -> AccountId { - AccountId(address) - } -} - -impl<'a> TryFrom<&'a [u8]> for AccountId { - type Error = TryFromSliceError; - - fn try_from(bytes: &'a [u8]) -> Result { - let address = <[u8; 32]>::try_from(bytes)?; - Ok(AccountId(address)) - } -} - -/// The default SRML balance type. -pub type Balance = u128; - -/// The default SRML hash type. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encode, Decode, Flush)] -#[cfg_attr(feature = "ink-generate-abi", derive(Metadata))] -pub struct Hash([u8; 32]); - -impl Default for Hash { - fn default() -> Self { - Hash(Default::default()) - } -} - -impl From<[u8; 32]> for Hash { - fn from(hash: [u8; 32]) -> Hash { - Hash(hash) - } -} - -impl<'a> TryFrom<&'a [u8]> for Hash { - type Error = TryFromSliceError; - - fn try_from(bytes: &'a [u8]) -> Result { - let hash = <[u8; 32]>::try_from(bytes)?; - Ok(Hash(hash)) - } -} - -/// The default SRML moment type. -pub type Moment = u64; - -/// The default SRML blocknumber type. -pub type BlockNumber = u64; diff --git a/core/src/env/test.rs b/core/src/env/test.rs deleted file mode 100644 index 3248720c28d..00000000000 --- a/core/src/env/test.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2018-2019 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. - -//! Public api to interact with the special testing environment. - -use crate::env::{ - traits::EnvTypes, - ContractEnv, - ContractEnvStorage, -}; - -/// Returns the total number of reads to all storage entries. -pub fn total_reads() -> u64 { - ContractEnvStorage::total_reads() -} - -/// Returns the total number of writes to all storage entries. -pub fn total_writes() -> u64 { - ContractEnvStorage::total_writes() -} - -/// Sets the caller for the next calls to the given address. -pub fn set_caller(address: T::AccountId) { - ContractEnv::::set_caller(address) -} - -/// Sets the timestamp for the next contract invocation. -pub fn set_now(timestamp: T::Moment) { - ContractEnv::::set_now(timestamp) -} - -/// Sets the current block number for the next contract invocation. -pub fn set_block_number(block_number: T::BlockNumber) { - ContractEnv::::set_block_number(block_number) -} - -/// Sets the contract balance for the next contract invocation. -pub fn set_balance(balance: T::Balance) { - ContractEnv::::set_balance(balance) -} - -/// Returns an iterator over the uninterpreted bytes of all past emitted events. -pub fn emitted_events() -> impl Iterator> { - ContractEnv::::emitted_events() -} diff --git a/core/src/env/test_env.rs b/core/src/env/test_env.rs deleted file mode 100644 index d5e4bdfe264..00000000000 --- a/core/src/env/test_env.rs +++ /dev/null @@ -1,812 +0,0 @@ -// Copyright 2018-2019 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 core::cell::{ - Cell, - RefCell, -}; -use std::marker::PhantomData; - -use scale::{ - Decode, - Encode, -}; - -use super::*; -use crate::{ - env::{ - CallError, - EnvTypes, - }, - storage::Key, -}; -use ink_prelude::collections::hash_map::{ - Entry, - HashMap, -}; - -/// A wrapper for the generic bytearray used for data in contract events. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct EventData { - topics: Vec>, - data: Vec, -} - -/// Raw recorded data of smart contract creates and instantiations. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct RawCreateData { - code_hash: Vec, - gas_limit: u64, - value: Vec, - input_data: Vec, -} - -/// Decoded recorded data of smart contract creates and instantiations. -pub struct CreateData -where - E: EnvTypes, -{ - pub code_hash: E::Hash, - pub gas_limit: u64, - pub value: E::Balance, - pub input_data: Vec, -} - -impl From for CreateData -where - E: EnvTypes, -{ - fn from(raw: RawCreateData) -> Self { - Self { - code_hash: Decode::decode(&mut &raw.code_hash[..]) - .expect("encountered invalid encoded code hash"), - gas_limit: raw.gas_limit, - value: Decode::decode(&mut &raw.value[..]) - .expect("encountered invalid encoded value"), - input_data: raw.input_data, - } - } -} - -impl EventData { - /// Returns the uninterpreted bytes of the emitted event. - fn data_as_bytes(&self) -> &[u8] { - self.data.as_slice() - } -} - -/// Emulates the data given to remote smart contract call instructions. -pub struct RawCallData { - pub callee: Vec, - pub gas: u64, - pub value: Vec, - pub input_data: Vec, -} - -/// Decoded call data of recorded external calls. -pub struct CallData -where - E: EnvTypes, -{ - pub callee: E::AccountId, - pub gas: u64, - pub value: E::Balance, - pub input_data: Vec, -} - -/// An entry in the storage of the test environment. -/// -/// # Note -/// -/// Additionally to its data it also stores the total -/// number of reads and writes done to this entry. -pub struct StorageEntry { - /// The actual data that is stored in this storage entry. - data: Vec, - /// The number of reads to this storage entry. - reads: Cell, - /// The number of writes to this storage entry. - writes: u64, -} - -impl StorageEntry { - /// Creates a new storage entry for the given data. - pub fn new(data: Vec) -> Self { - Self { - data, - reads: Cell::new(0), - writes: 0, - } - } - - /// Increases the read counter by one. - fn inc_reads(&self) { - self.reads.set(self.reads.get() + 1); - } - - /// Increases the write counter by one. - fn inc_writes(&mut self) { - self.writes += 1; - } - - /// Returns the number of reads for this storage entry. - pub fn reads(&self) -> u64 { - self.reads.get() - } - - /// Returns the number of writes to this storage entry. - pub fn writes(&self) -> u64 { - self.writes - } - - /// Returns the data stored in this storage entry. - /// - /// # Note - /// - /// Also bumps the read counter. - pub fn read(&self) -> Vec { - self.inc_reads(); - self.data.clone() - } - - /// Writes the given data to this storage entry. - /// - /// # Note - /// - /// Also bumps the write counter. - pub fn write(&mut self, new_data: Vec) { - self.inc_writes(); - self.data = new_data; - } -} - -/// The data underlying to a test environment. -pub struct TestEnvData { - /// The storage entries. - storage: HashMap, - /// The address of the contract. - /// - /// # Note - /// - /// The current address can be adjusted by `TestEnvData::set_address`. - address: Vec, - /// The balance of the contract. - /// - /// # Note - /// - /// The current balance can be adjusted by `TestEnvData::set_balance`. - balance: Vec, - /// The caller address for the next contract invocation. - /// - /// # Note - /// - /// The current caller can be adjusted by `TestEnvData::set_caller`. - caller: Vec, - /// The input data for the next contract invocation. - /// - /// # Note - /// - /// The current input can be adjusted by `TestEnvData::set_input`. - input: Vec, - /// The random seed for the next contract invocation. - /// - /// # Note - /// - /// The current random seed can be adjusted by `TestEnvData::set_random_seed`. - random_seed: Vec, - /// The timestamp for the next contract invocation. - /// - /// # Note - /// - /// The current timestamp can be adjusted by `TestEnvData::set_now`. - now: Vec, - /// The current block number for the next contract invocation. - /// - /// # Note - /// - /// The current current block number can be adjusted by `TestEnvData::set_block_number`. - block_number: Vec, - /// The total number of reads from the storage. - total_reads: Cell, - /// The total number of writes to the storage. - total_writes: u64, - /// Deposited events of the contract invocation. - events: Vec, - /// Calls dispatched to the runtime - dispatched_calls: Vec>, - /// The current gas price. - gas_price: Vec, - /// The remaining gas. - gas_left: Vec, - /// The total transferred value. - value_transferred: Vec, - /// The recorded external calls. - calls: Vec, - /// The expected return data of the next external call. - call_return: Vec, - /// Returned data. - return_data: Vec, - /// Recorded smart contract instantiations. - creates: Vec, - /// The address of the next instantiated smart contract. - next_create_address: Vec, -} - -impl Default for TestEnvData { - fn default() -> Self { - Self { - address: Vec::new(), - storage: HashMap::new(), - balance: Vec::new(), - caller: Vec::new(), - input: Vec::new(), - random_seed: Vec::new(), - now: Vec::new(), - block_number: Vec::new(), - total_reads: Cell::new(0), - total_writes: 0, - events: Vec::new(), - gas_price: Vec::new(), - gas_left: Vec::new(), - value_transferred: Vec::new(), - dispatched_calls: Vec::new(), - calls: Vec::new(), - call_return: Vec::new(), - return_data: Vec::new(), - creates: Vec::new(), - next_create_address: Vec::new(), - } - } -} - -impl TestEnvData { - /// Resets `self` as if no contract execution happened so far. - pub fn reset(&mut self) { - self.address.clear(); - self.balance.clear(); - self.storage.clear(); - self.caller.clear(); - self.input.clear(); - self.random_seed.clear(); - self.now.clear(); - self.block_number.clear(); - self.total_reads.set(0); - self.total_writes = 0; - self.events.clear(); - self.dispatched_calls.clear(); - self.calls.clear(); - self.call_return.clear(); - self.return_data.clear(); - self.creates.clear(); - self.next_create_address.clear(); - } - - /// Increments the total number of reads from the storage. - fn inc_total_reads(&self) { - self.total_reads.set(self.total_reads.get() + 1) - } - - /// Increments the total number of writes to the storage. - fn inc_total_writes(&mut self) { - self.total_writes += 1 - } - - /// Returns the total number of reads from the storage. - pub fn total_reads(&self) -> u64 { - self.total_reads.get() - } - - /// Returns the total number of writes to the storage. - pub fn total_writes(&self) -> u64 { - self.total_writes - } - - /// Returns the number of reads from the entry associated by the given key if any. - pub fn reads_for(&self, key: Key) -> Option { - self.storage.get(&key).map(|loaded| loaded.reads()) - } - - /// Returns the number of writes to the entry associated by the given key if any. - pub fn writes_for(&self, key: Key) -> Option { - self.storage.get(&key).map(|loaded| loaded.writes()) - } - - /// Sets the contract address for the next contract invocation. - pub fn set_address(&mut self, new_address: Vec) { - self.address = new_address; - } - - /// Sets the contract balance for the next contract invocation. - pub fn set_balance(&mut self, new_balance: Vec) { - self.balance = new_balance; - } - - /// Sets the caller address for the next contract invocation. - pub fn set_caller(&mut self, new_caller: Vec) { - self.caller = new_caller; - } - - /// Sets the input data for the next contract invocation. - pub fn set_input(&mut self, input_bytes: Vec) { - self.input = input_bytes; - } - - /// Appends new event data to the end of the bytearray. - pub fn add_event(&mut self, topics: &[Vec], event_data: &[u8]) { - let new_event = EventData { - topics: topics.to_vec(), - data: event_data.to_vec(), - }; - self.events.push(new_event); - } - - /// Appends a dispatched call to the runtime - pub fn add_dispatched_call(&mut self, call: &[u8]) { - self.dispatched_calls.push(call.to_vec()); - } - - /// Sets the random seed for the next contract invocation. - pub fn set_random_seed(&mut self, random_seed_hash: Vec) { - self.random_seed = random_seed_hash.to_vec(); - } - - /// Sets the timestamp for the next contract invocation. - pub fn set_now(&mut self, timestamp: Vec) { - self.now = timestamp; - } - - /// Sets the current block number for the next contract invocation. - pub fn set_block_number(&mut self, block_number: Vec) { - self.block_number = block_number; - } - - /// Returns an iterator over all emitted events. - pub fn emitted_events(&self) -> impl DoubleEndedIterator { - self.events - .iter() - .map(|event_data| event_data.data_as_bytes()) - } - - /// Returns an iterator over all dispatched calls - pub fn dispatched_calls(&self) -> impl DoubleEndedIterator { - self.dispatched_calls.iter().map(Vec::as_slice) - } - - /// Records a new external call. - pub fn add_call(&mut self, callee: &[u8], gas: u64, value: &[u8], input_data: &[u8]) { - let new_call = RawCallData { - callee: callee.to_vec(), - gas, - value: value.to_vec(), - input_data: input_data.to_vec(), - }; - self.calls.push(new_call); - } - - /// Returns an iterator over all recorded external calls. - pub fn external_calls(&self) -> impl DoubleEndedIterator { - self.calls.iter() - } - - /// Set the expected return data of the next external call. - pub fn set_return_data(&mut self, return_data: &[u8]) { - self.return_data = return_data.to_vec(); - } - - /// Returns the latest returned data. - pub fn returned_data(&self) -> &[u8] { - &self.return_data - } - - /// Records a new smart contract instantiation. - pub fn add_create( - &mut self, - code_hash: &[u8], - gas_limit: u64, - value: &[u8], - input_data: &[u8], - ) { - let new_create = RawCreateData { - code_hash: code_hash.to_vec(), - gas_limit, - value: value.to_vec(), - input_data: input_data.to_vec(), - }; - self.creates.push(new_create); - } - - /// Returns an iterator over all recorded smart contract instantiations. - pub fn creates(&self) -> impl DoubleEndedIterator { - self.creates.iter() - } - - /// Sets the address of the next instantiated smart contract. - pub fn set_next_create_address(&mut self, account_id: &[u8]) { - self.next_create_address = account_id.to_vec(); - } -} - -impl TestEnvData { - pub fn address(&self) -> Vec { - self.address.clone() - } - - pub fn balance(&self) -> Vec { - self.balance.clone() - } - - pub fn caller(&self) -> Vec { - self.caller.clone() - } - - pub fn store(&mut self, key: Key, value: &[u8]) { - self.inc_total_writes(); - match self.storage.entry(key) { - Entry::Occupied(mut occupied) => occupied.get_mut().write(value.to_vec()), - Entry::Vacant(vacant) => { - vacant.insert(StorageEntry::new(value.to_vec())); - } - } - } - - pub fn clear(&mut self, key: Key) { - // Storage clears count as storage write. - self.inc_total_writes(); - self.storage.remove(&key); - } - - pub fn load(&self, key: Key) -> Option> { - self.inc_total_reads(); - self.storage.get(&key).map(|loaded| loaded.read()) - } - - pub fn input(&self) -> Vec { - self.input.clone() - } - - pub fn random_seed(&self) -> Vec { - self.random_seed.clone() - } - - pub fn now(&self) -> Vec { - self.now.clone() - } - - pub fn block_number(&self) -> Vec { - self.block_number.clone() - } - - pub fn gas_price(&self) -> Vec { - self.gas_price.clone() - } - - pub fn gas_left(&self) -> Vec { - self.gas_left.clone() - } - - pub fn value_transferred(&self) -> Vec { - self.value_transferred.clone() - } - - pub fn return_data(&mut self, data: &[u8]) { - self.return_data = data.to_vec(); - } - - pub fn println(&self, content: &str) { - println!("{}", content) - } - - pub fn deposit_raw_event(&mut self, topics: &[Vec], data: &[u8]) { - self.add_event(topics, data); - } - - pub fn dispatch_call(&mut self, call: &[u8]) { - self.add_dispatched_call(call); - } - - pub fn call( - &mut self, - callee: &[u8], - gas: u64, - value: &[u8], - input_data: &[u8], - ) -> Vec { - self.add_call(callee, gas, value, input_data); - self.call_return.clone() - } - - pub fn create( - &mut self, - code_hash: &[u8], - gas_limit: u64, - value: &[u8], - input_data: &[u8], - ) -> Vec { - self.add_create(code_hash, gas_limit, value, input_data); - self.next_create_address.clone() - } -} - -thread_local! { - /// The test environment data. - /// - /// This needs to be thread local since tests are run - /// in parallel by default which may lead to data races otherwise. - pub static TEST_ENV_DATA: RefCell = { - RefCell::new(TestEnvData::default()) - }; -} - -/// Test environment for testing SRML contract off-chain. -pub struct TestEnv { - marker: PhantomData T>, -} - -macro_rules! impl_env_setters_for_test_env { - ( $( ($fn_name:ident, $name:ident, $ty:ty) ),* ) => { - $( - pub fn $fn_name($name: $ty) { - TEST_ENV_DATA.with(|test_env| test_env.borrow_mut().$fn_name($name.encode())) - } - )* - } -} - -impl TestEnv -where - T: EnvTypes, -{ - /// Resets the test environment as if no contract execution happened so far. - pub fn reset() { - TEST_ENV_DATA.with(|test_env| test_env.borrow_mut().reset()) - } - - /// Returns the number of reads from the entry associated by the given key if any. - pub fn reads_for(key: Key) -> Option { - TEST_ENV_DATA.with(|test_env| test_env.borrow().reads_for(key)) - } - - /// Returns the number of writes to the entry associated by the given key if any. - pub fn writes_for(key: Key) -> Option { - TEST_ENV_DATA.with(|test_env| test_env.borrow().writes_for(key)) - } - - /// Returns the latest returned data. - pub fn returned_data() -> Vec { - TEST_ENV_DATA.with(|test_env| test_env.borrow().returned_data().to_vec()) - } - - /// Sets the input data for the next contract invocation. - pub fn set_input(input_bytes: &[u8]) { - TEST_ENV_DATA - .with(|test_env| test_env.borrow_mut().set_input(input_bytes.to_vec())) - } - - /// Sets the expected return data for the next external call. - pub fn set_return_data(expected_return_data: &[u8]) { - TEST_ENV_DATA - .with(|test_env| test_env.borrow_mut().set_return_data(expected_return_data)) - } - - /// Sets the address of the next instantiated smart contract. - pub fn set_next_create_address(account_id: T::AccountId) { - TEST_ENV_DATA.with(|test_env| { - test_env - .borrow_mut() - .set_next_create_address(&account_id.encode()) - }) - } - - impl_env_setters_for_test_env!( - (set_address, address, T::AccountId), - (set_balance, balance, T::Balance), - (set_caller, caller, T::AccountId), - (set_random_seed, random_seed, T::Hash), - (set_now, now, T::Moment), - (set_block_number, block_number, T::BlockNumber) - ); - - /// Returns an iterator over all emitted events. - pub fn emitted_events() -> impl DoubleEndedIterator> { - TEST_ENV_DATA.with(|test_env| { - test_env - .borrow() - .emitted_events() - .map(|event_bytes| event_bytes.to_vec()) - .collect::>() - .into_iter() - }) - } - - /// Returns an iterator over all emitted events. - pub fn external_calls() -> impl DoubleEndedIterator> { - TEST_ENV_DATA.with(|test_env| { - test_env - .borrow() - .external_calls() - .map(|raw_call_data| { - CallData { - callee: Decode::decode(&mut &raw_call_data.callee[..]) - .expect("invalid encoded callee"), - gas: raw_call_data.gas, - value: Decode::decode(&mut &raw_call_data.value[..]) - .expect("invalid encoded value"), - input_data: raw_call_data.input_data.clone(), - } - }) - .collect::>() - .into_iter() - }) - } - - /// Returns an iterator over all recorded smart contract instantiations. - pub fn creates() -> impl DoubleEndedIterator> { - TEST_ENV_DATA.with(|test_env| { - test_env - .borrow() - .creates() - .cloned() - .map(Into::into) - .collect::>() - .into_iter() - }) - } - - /// Returns an iterator over all dispatched calls. - pub fn dispatched_calls() -> impl DoubleEndedIterator { - TEST_ENV_DATA.with(|test_env| { - test_env - .borrow() - .dispatched_calls() - .map(|call| Decode::decode(&mut &call[..]).expect("Valid encoded Call")) - .collect::>() - .into_iter() - }) - } -} - -macro_rules! impl_env_getters_for_test_env { - ( $( ($fn_name:ident, $ret_name:ty) ),* ) => { - $( - fn $fn_name() -> $ret_name { - TEST_ENV_DATA.with(|test_env| Decode::decode(&mut &test_env.borrow().$fn_name()[..]) - .expect("environment instances are assumed to be correctly encoded")) - } - )* - } -} - -impl EnvTypes for TestEnv -where - T: EnvTypes, -{ - type AccountId = ::AccountId; - type Balance = ::Balance; - type Hash = ::Hash; - type Moment = ::Moment; - type BlockNumber = ::BlockNumber; - type Call = ::Call; -} - -impl Env for TestEnv -where - T: EnvTypes, -{ - impl_env_getters_for_test_env!( - (address, T::AccountId), - (balance, T::Balance), - (caller, T::AccountId), - (input, Vec), - (random_seed, T::Hash), - (now, T::Moment), - (block_number, T::BlockNumber), - (gas_price, T::Balance), - (gas_left, T::Balance), - (value_transferred, T::Balance) - ); - - fn return_data(data: &[u8]) { - TEST_ENV_DATA.with(|test_env| test_env.borrow_mut().return_data(data)) - } - - fn println(content: &str) { - TEST_ENV_DATA.with(|test_env| test_env.borrow().println(content)) - } - - fn deposit_raw_event(topics: &[T::Hash], data: &[u8]) { - TEST_ENV_DATA.with(|test_env| { - let topics = topics.iter().map(Encode::encode).collect::>(); - test_env.borrow_mut().deposit_raw_event(&topics, data) - }) - } - - fn dispatch_raw_call(data: &[u8]) { - TEST_ENV_DATA.with(|test_env| test_env.borrow_mut().dispatch_call(data)) - } - - fn call_invoke( - callee: T::AccountId, - gas: u64, - value: T::Balance, - input_data: &[u8], - ) -> Result<(), CallError> { - let callee = &(callee.encode())[..]; - let value = &(value.encode())[..]; - let _return_data = TEST_ENV_DATA - .with(|test_env| test_env.borrow_mut().call(callee, gas, value, input_data)); - Ok(()) - } - - fn call_evaluate( - callee: T::AccountId, - gas: u64, - value: T::Balance, - input_data: &[u8], - ) -> Result { - let callee = &(callee.encode())[..]; - let value = &(value.encode())[..]; - TEST_ENV_DATA.with(|test_env| { - Decode::decode( - &mut &(test_env.borrow_mut().call(callee, gas, value, input_data))[..], - ) - .map_err(|_| CallError) - }) - } - - fn create( - code_hash: T::Hash, - gas_limit: u64, - value: T::Balance, - input_data: &[u8], - ) -> Result { - let code_hash = &(code_hash.encode())[..]; - let value = &(value.encode())[..]; - TEST_ENV_DATA.with(|test_env| { - Decode::decode( - &mut &(test_env - .borrow_mut() - .create(code_hash, gas_limit, value, input_data))[..], - ) - .map_err(|_| CreateError) - }) - } -} - -pub enum TestEnvStorage {} - -impl EnvStorage for TestEnvStorage { - unsafe fn store(key: Key, value: &[u8]) { - TEST_ENV_DATA.with(|test_env| test_env.borrow_mut().store(key, value)) - } - - unsafe fn clear(key: Key) { - TEST_ENV_DATA.with(|test_env| test_env.borrow_mut().clear(key)) - } - - unsafe fn load(key: Key) -> Option> { - TEST_ENV_DATA.with(|test_env| test_env.borrow().load(key)) - } -} - -impl TestEnvStorage { - /// Returns the total number of reads from the storage. - pub fn total_reads() -> u64 { - TEST_ENV_DATA.with(|test_env| test_env.borrow().total_reads()) - } - - /// Returns the total number of writes to the storage. - pub fn total_writes() -> u64 { - TEST_ENV_DATA.with(|test_env| test_env.borrow().total_writes()) - } -} diff --git a/core/src/env/traits.rs b/core/src/env/traits.rs deleted file mode 100644 index aa26e35026c..00000000000 --- a/core/src/env/traits.rs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2018-2019 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 scale::{ - Codec, - Decode, -}; - -use crate::storage::Key; -use ink_prelude::vec::Vec; - -/// Error encountered by calling a remote contract. -/// -/// # Note -/// -/// This is currently just a placeholder for potential future error codes. -#[derive(Debug, Copy, Clone)] -pub struct CallError; - -/// Error encountered upon creating and instantiation a new smart contract. -/// -/// # Note -/// -/// This is currently just a placeholder for potential future error codes. -#[derive(Debug, Copy, Clone)] -pub struct CreateError; - -#[cfg(not(feature = "test-env"))] -/// The environmental types usable by contracts defined with ink!. -pub trait EnvTypes { - /// The type of an address. - type AccountId: Codec + Clone + PartialEq + Eq; - /// The type of balances. - type Balance: Codec + Clone + PartialEq + Eq; - /// The type of hash. - type Hash: Codec + Clone + PartialEq + Eq; - /// The type of timestamps. - type Moment: Codec + Clone + PartialEq + Eq; - /// The type of block number. - type BlockNumber: Codec + Clone + PartialEq + Eq; - /// The type of a call into the runtime - type Call: scale::Encode; -} - -#[cfg(feature = "test-env")] -/// The environmental types usable by contracts defined with ink!. -/// For the test environment extra trait bounds are required for using the types in unit tests. -pub trait EnvTypes { - /// The type of an address. - type AccountId: Codec + Clone + PartialEq + Eq + core::fmt::Debug; - /// The type of balances. - type Balance: Codec + Clone + PartialEq + Eq + core::fmt::Debug; - /// The type of hash. - type Hash: Codec + Clone + PartialEq + Eq + core::fmt::Debug; - /// The type of timestamps. - type Moment: Codec + Clone + PartialEq + Eq + core::fmt::Debug; - /// The type of block number. - type BlockNumber: Codec + Clone + PartialEq + Eq + core::fmt::Debug; - /// The type of a call into the runtime. - /// Requires Decode for inspecting raw dispatched calls in the test environment. - type Call: Codec + Clone + PartialEq + Eq + core::fmt::Debug; -} - -/// Types implementing this can act as contract storage. -pub trait EnvStorage { - /// Stores the given value under the given key. - /// - /// # Safety - /// - /// This operation is unsafe because it does not check for key integrity. - /// Users can compare this operation with a raw pointer dereferencing in Rust. - unsafe fn store(key: Key, value: &[u8]); - - /// Clears the value stored under the given key. - /// - /// # Safety - /// - /// This operation is unsafe because it does not check for key integrity. - /// Users can compare this operation with a raw pointer dereferencing in Rust. - unsafe fn clear(key: Key); - - /// Loads data stored under the given key. - /// - /// # Safety - /// - /// This operation is unsafe because it does not check for key integrity. - /// Users can compare this operation with a raw pointer dereferencing in Rust. - unsafe fn load(key: Key) -> Option>; -} - -/// The environment API usable by contracts defined with pDSL. -pub trait Env: EnvTypes { - /// Returns the chain address of the contract. - fn address() -> ::AccountId; - - /// Returns the chain balance of the contract. - fn balance() -> ::Balance; - - /// Returns the chain address of the caller. - fn caller() -> ::AccountId; - - /// Loads input data for contract execution. - fn input() -> Vec; - - /// Get the random seed from the latest block. - fn random_seed() -> ::Hash; - - /// Get the timestamp of the latest block. - fn now() -> ::Moment; - - /// Get the block number of the latest block. - fn block_number() -> ::BlockNumber; - - /// Returns the current gas price. - fn gas_price() -> ::Balance; - - /// Returns the gas left for this contract execution. - fn gas_left() -> ::Balance; - - /// Returns the amount of value that has been transferred. - fn value_transferred() -> ::Balance; - - /// Returns from the contract execution with the given value. - /// - /// # Safety - /// - /// The external callers rely on the correct type of the encoded - /// returned value. This API is unsafe because it does not provide - /// guarantees on its own to always encode the expected type. - fn return_data(data: &[u8]); - - /// Prints the given content to Substrate output. - /// - /// # Note - /// - /// Usable only in development (`--dev`) chains. - fn println(content: &str); - - /// Deposits raw event data through Contracts module. - fn deposit_raw_event(topics: &[::Hash], data: &[u8]); - - /// Dispatches a call into the Runtime. - fn dispatch_raw_call(data: &[u8]); - - /// Calls a remote smart contract without returning data - fn call_invoke( - callee: ::AccountId, - gas: u64, - value: ::Balance, - input_data: &[u8], - ) -> Result<(), CallError>; - - /// Calls a remote smart contract and return encoded data - fn call_evaluate( - callee: ::AccountId, - gas: u64, - value: ::Balance, - input_data: &[u8], - ) -> Result; - - /// Creates and instantiates a new smart contract. - fn create( - code_hash: ::Hash, - gas_limit: u64, - value: ::Balance, - input_data: &[u8], - ) -> Result<::AccountId, CreateError>; -} diff --git a/core/src/env2/buffer_arena.rs b/core/src/env2/buffer_arena.rs deleted file mode 100644 index 1aeff4ae01e..00000000000 --- a/core/src/env2/buffer_arena.rs +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright 2018-2019 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. - -//! This file defines the global buffer arena that is accessible globally -//! and acts as a cache for allocated heap memory to avoid heap memory -//! throttling resulting from heap usage for intermediate computations. -//! -//! Exactly this happens a lot in the boundaries between SRML contracts -//! and ink! since encoding and decoding of SCALE values has to be done -//! in such an intermediate buffer. -//! -//! Users and systems are advised to share a common set of allocated buffers -//! provided by the global buffer arena. - -use crate::env2::utils::{ - EnlargeTo, - Reset, -}; -use cfg_if::cfg_if; -use core::cell::{ - Cell, - RefCell, -}; -use ink_prelude::vec::Vec; - -/// The maximum amount of used byte buffers at the same time. -/// -/// Since the whole point behind this byte buffer arena is to cache -/// allocated heap memory the number of concurrent byte buffers that are -/// in use at the same time should be kept small. -const IN_USE_LIMIT: usize = 1000; - -cfg_if! { - if #[cfg(feature = "std")] { - thread_local! { - /// Global buffer arena that provides shared recycled buffers to avoid - /// constantly allocating and deallocating heap memory during contract execution. - /// - /// # Note - /// - /// This is mainly useful to interact with the environment because - /// it requires intermediate buffers for its encoding and decoding. - /// - /// # API - /// - /// - Can be accessed through [`std::thread::LocalKey::with`]. - /// - Provides recycled buffers through the `get_buffer` call. - pub static BUFFER_ARENA: BufferArena = BufferArena::new(); - } - } else { - /// Global buffer arena that provides shared recycled buffers to avoid - /// constantly allocating and deallocating heap memory during contract execution. - /// - /// # Note - /// - /// This is mainly useful to interact with the environment because - /// it requires intermediate buffers for its encoding and decoding. - /// - /// # API - /// - /// - Can be accessed through [`std::thread::LocalKey::with`]. - /// - Provides recycled buffers through the `get_buffer` call. - pub static BUFFER_ARENA: GlobalBufferArena = GlobalBufferArena::new(BufferArena::new()); - - /// Wrapper around `BufferArena` to provide similar interface - /// as `std::thread::LocalKey` provided by `thread_local` does. - /// - /// Also acts as safety guard to prevent references to `BufferRef` - /// escape the closure using the [`GlobalBufferArena::with`] API. - pub struct GlobalBufferArena { - /// The wrapped buffer arena. - arena: BufferArena, - } - - /// CRITICAL NOTE - /// ============= - /// - /// The wrapped `BufferArena` type itself is __NOT__ `Sync` since it is using - /// `Cell` and `RefCell` internally instead of the thread-safe alternatives. - /// However, since Wasm smart contracts are guaranteed to operate single - /// threaded we can allow for this unsafe `Sync` implementation to allow - /// for having the global static `BUFFER_ARENA` variable and as long as we - /// are only operating single threaded this shouldn't be unsafe. - unsafe impl Sync for GlobalBufferArena {} - - impl GlobalBufferArena { - /// Creates a new `GlobalBufferArena` from the given `BufferArena`. - pub const fn new(arena: BufferArena) -> Self { - Self { arena } - } - - /// Runs the given closure for the wrapped `BufferArena`. - /// - /// This way no references may escape the closure. - pub fn with(&self, f: F) - where - F: FnOnce(&BufferArena), - { - f(&self.arena) - } - } - } -} - -/// A byte buffer arena that acts as a cache for allocated heap memory. -pub struct BufferArena { - /// The currently available byte buffers. - free: RefCell>, - /// Counts the buffers that are currently in use at the same time. - /// - /// # Note - /// - /// This value is purely used as diagnostic measures to provide - /// smart contract writers with feedback if their implementation - /// is abusing the buffer arena. - /// We might want to turn these checks off for Wasm compilation. - in_use: Cell, -} - -impl BufferArena { - /// Returns a new empty buffer arena. - /// - /// Since this acts as cache we only require one instance of this type - /// that we use as `thread_local` global which is safe since - /// Wasm smart contracts are guaranteed to run in a single thread. - pub(self) const fn new() -> Self { - Self { - free: RefCell::new(Vec::new()), - in_use: Cell::new(0), - } - } - - /// Returns a fresh buffer that can be used for intermediate computations. - /// - /// Buffers returned through this API implement all the necessary traits - /// required to use them as environment buffer. - /// - /// - [`Reset`]: Allows resetting the buffer. This clears all the elements, - /// however, it retains the memory allocation. - /// - [`EnlargeTo`]: Safely enlarges the buffer to the required minimum size - /// if it isn't already large enough. - /// - [`core::convert::AsRef`]`<[u8]>`: Returns a shared view into the byte buffer. - /// - [`core::convert::AsMut`]`<[u8]>`: Returns an exclusive view into the byte buffer. - pub fn get_buffer(&self) -> BufferRef { - self.in_use.set(self.in_use() + 1); - if self.in_use() > IN_USE_LIMIT { - panic!("too many concurrent byte buffers") - } - self.free - .borrow_mut() - .pop() - .unwrap_or_else(Buffer::new) - .into_ref() - } - - /// Returns the buffer to the arena. - /// - /// This is only called from the `Drop` implementation of `BufferRef` - /// to return the wrapped buffer back to the global buffer arena instance. - pub(self) fn return_buffer(&self, buffer: Buffer) { - self.in_use.set(self.in_use() - 1); - self.free.borrow_mut().push(buffer) - } - - /// Returns the number of buffers that are currently in use at the same time. - pub fn in_use(&self) -> usize { - self.in_use.get() - } - - /// Returns the number of buffers that are not in use. - pub fn free(&self) -> usize { - self.free.borrow().len() - } - - /// Returns the current number of cached buffers. - pub fn allocated(&self) -> usize { - self.in_use() + self.free() - } -} - -/// A byte buffer. -/// -/// This is a thin wrapper around a byte vector providing only -/// the minimal interface to be operable as environmental intermediate -/// buffer. -pub struct Buffer { - /// The warpper internal raw byte buffer. - buffer: Vec, -} - -impl<'a> Buffer -where - Self: 'a, -{ - /// Returns a new empty byte buffer. - pub(self) const fn new() -> Self { - Self { buffer: Vec::new() } - } - - /// Wraps `self` in a buffer reference. - pub(self) fn into_ref(self) -> BufferRef<'a> { - BufferRef { - buffer: self, - lt: core::marker::PhantomData, - } - } -} - -impl Reset for Buffer { - fn reset(&mut self) { - Reset::reset(&mut self.buffer) - } -} - -impl EnlargeTo for Buffer { - fn enlarge_to(&mut self, new_size: usize) { - EnlargeTo::enlarge_to(&mut self.buffer, new_size) - } -} - -impl core::convert::AsRef<[u8]> for Buffer { - fn as_ref(&self) -> &[u8] { - &self.buffer - } -} - -impl core::convert::AsMut<[u8]> for Buffer { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.buffer - } -} - -/// A buffer reference providing lifetime constrained access -/// to the wrapper byte buffer. -/// -/// Buffer references are created by the only global buffer arena -/// instance and make sure that their wrapper buffer is returned to -/// the arena upon `Drop`. -pub struct BufferRef<'a> { - /// The wrapped byte buffer. - buffer: Buffer, - /// The emulated lifetime. - lt: core::marker::PhantomData &'a ()>, -} - -impl BufferRef<'_> { - /// Takes the buffer out of the buffer reference - /// leaving behind an empty byte buffer without - /// associated heap allocation. - /// - /// Also resets the byte buffer. - fn take_buffer(&mut self) -> Buffer { - Reset::reset(&mut self.buffer); - core::mem::replace(&mut self.buffer, Buffer::new()) - } -} - -impl Reset for BufferRef<'_> { - fn reset(&mut self) { - Reset::reset(&mut self.buffer) - } -} - -impl EnlargeTo for BufferRef<'_> { - fn enlarge_to(&mut self, new_size: usize) { - EnlargeTo::enlarge_to(&mut self.buffer, new_size) - } -} - -impl core::convert::AsRef<[u8]> for BufferRef<'_> { - fn as_ref(&self) -> &[u8] { - core::convert::AsRef::<[u8]>::as_ref(&self.buffer) - } -} - -impl core::convert::AsMut<[u8]> for BufferRef<'_> { - fn as_mut(&mut self) -> &mut [u8] { - core::convert::AsMut::<[u8]>::as_mut(&mut self.buffer) - } -} - -impl Drop for BufferRef<'_> { - fn drop(&mut self) { - // Returns the byte buffer back to the global buffer arena. - BUFFER_ARENA.with(|arena| arena.return_buffer(self.take_buffer())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - macro_rules! assert_arena { - ( - $arena:ident, - in_use: $expected_in_use:literal, - free: $expected_free:literal, - allocated: $expected_allocated:literal - ) => {{ - assert_eq!( - $arena.in_use(), - $expected_in_use, - "number of buffers in use doesn't match expected" - ); - assert_eq!( - $arena.free(), - $expected_free, - "number of free buffers doens't match expected" - ); - assert_eq!( - $arena.allocated(), - $expected_allocated, - "number of allocated buffers doesn't match expected" - ); - }}; - } - - #[test] - fn it_works() { - BUFFER_ARENA.with(|arena| { - assert_arena!(arena, in_use: 0, free: 0, allocated: 0); - // Allocate a single buffer for a short time. - { - let _b = arena.get_buffer(); - assert_arena!(arena, in_use: 1, free: 0, allocated: 1); - } - // We should now have a single allocated buffer - // but none in use. - assert_arena!(arena, in_use: 0, free: 1, allocated: 1); - // Allocate a single buffer again so that we see - // it is being reused. - { - let _b = arena.get_buffer(); - assert_arena!(arena, in_use: 1, free: 0, allocated: 1); - } - assert_arena!(arena, in_use: 0, free: 1, allocated: 1); - // Now we allocate 3 buffers in their own scope - // and check the `in_use` and `allocated`. - { - let _b0 = arena.get_buffer(); - { - let _b1 = arena.get_buffer(); - { - // At this point we should have 3 buffers - // allocated and in use. - let _b2 = arena.get_buffer(); - assert_arena!(arena, in_use: 3, free: 0, allocated: 3); - } - assert_arena!(arena, in_use: 2, free: 1, allocated: 3); - } - assert_arena!(arena, in_use: 1, free: 2, allocated: 3); - } - // At this point we dropped all 3 buffers again - // so none is in use but we still have 3 allocated - // buffers. - assert_arena!(arena, in_use: 0, free: 3, allocated: 3); - }); - } -} diff --git a/core/src/env2/dyn_env.rs b/core/src/env2/dyn_env.rs deleted file mode 100644 index 1f42d919000..00000000000 --- a/core/src/env2/dyn_env.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2018-2019 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 core::ops::{ - Deref, - DerefMut, -}; - -use crate::{ - env2::{ - AccessEnv, - EnvAccess, - EnvAccessMut, - }, - storage::{ - alloc::{ - Allocate, - AllocateUsing, - DynAlloc, - Initialize, - }, - Flush, - }, -}; - -/// A wrapper around `EnvAccess` or `EnvAccessMut` that adds a dynamic storage allocator. -#[cfg_attr(feature = "ink-generate-abi", derive(type_metadata::Metadata))] -#[derive(Debug)] -pub struct DynEnv { - /// The wrapped environment. - env: E, - /// The dynamic storage allocator. - alloc: DynAlloc, -} - -#[cfg(feature = "ink-generate-abi")] -impl ink_abi::HasLayout for DynEnv -where - E: type_metadata::Metadata + 'static, -{ - fn layout(&self) -> ink_abi::StorageLayout { - use type_metadata::Metadata as _; - ink_abi::LayoutStruct::new( - Self::meta_type(), - vec![ink_abi::LayoutField::new("alloc", self.alloc.layout())], - ) - .into() - } -} - -impl DynEnv { - #[inline] - pub fn env(&self) -> &E { - &self.env - } - - #[inline] - pub fn env_mut(&mut self) -> &mut E { - &mut self.env - } -} - -impl<'a, E> AccessEnv for &'a DynEnv> { - type Target = core::cell::RefMut<'a, EnvAccessMut>; - - #[inline] - fn env(self) -> Self::Target { - (&self.env).env() - } -} - -impl<'a, E> AccessEnv for &'a mut DynEnv> { - type Target = &'a mut EnvAccessMut; - - #[inline] - fn env(self) -> Self::Target { - (&mut self.env).env() - } -} - -impl Deref for DynEnv { - type Target = E; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.env - } -} - -impl DerefMut for DynEnv { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.env - } -} - -impl Flush for DynEnv { - #[inline] - fn flush(&mut self) { - self.alloc.flush() - } -} - -impl AllocateUsing for DynEnv -where - E: Default, -{ - #[inline] - unsafe fn allocate_using(alloc: &mut A) -> Self - where - A: Allocate, - { - Self { - env: Default::default(), - alloc: AllocateUsing::allocate_using(alloc), - } - } -} - -impl Initialize for DynEnv { - type Args = (); - - #[inline] - fn default_value() -> Option { - Some(()) - } - - #[inline] - fn initialize(&mut self, _args: Self::Args) { - self.alloc.initialize(()); - } -} diff --git a/core/src/env2/env_access/immutable.rs b/core/src/env2/env_access/immutable.rs deleted file mode 100644 index 8d45eda30dc..00000000000 --- a/core/src/env2/env_access/immutable.rs +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2018-2019 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 core::cell::RefCell; - -use crate::{ - env2::{ - call::{ - CallData, - CallParams, - CreateParams, - ReturnType, - }, - env_access::EmitEvent as _, - AccessEnv, - Env, - EnvAccessMut, - Result, - Topics, - }, - storage::{ - alloc::{ - Allocate, - AllocateUsing, - Initialize, - }, - Flush, - Key, - }, -}; - -/// A `&self` accessor to `EnvAccessMut`. -/// -/// This allows ink! `&self` messages to make use of the environment efficiently -/// while also maintaining access invariants through runtime checks. -/// A wrapper arround `EnvAccessMut` allowing for `&self` accesses to make it -/// usable in `&self` ink! messages. -/// -/// # Note -/// -/// Using `EnvAccessMut` is preferable since it performs these access checks at -/// compile-time. -pub struct EnvAccess { - /// Allows accessing the inner environment by `&self` instead of `&mut self`. - /// - /// This is important to make `DynEnv` work also in conjunction with `&self` messages. - pub(crate) access: RefCell>, -} - -impl core::fmt::Debug for EnvAccess { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_struct("EnvAccess").finish() - } -} - -#[cfg(feature = "ink-generate-abi")] -impl type_metadata::HasTypeId for EnvAccess -where - E: type_metadata::Metadata, -{ - fn type_id() -> type_metadata::TypeId { - type_metadata::TypeIdCustom::new( - "EnvAccess", - type_metadata::Namespace::from_module_path(module_path!()) - .expect("namespace from module path cannot fail"), - vec![E::meta_type()], - ) - .into() - } -} - -#[cfg(feature = "ink-generate-abi")] -impl type_metadata::HasTypeDef for EnvAccess -where - crate::env2::EnvAccessMut: type_metadata::Metadata, -{ - fn type_def() -> type_metadata::TypeDef { - type_metadata::TypeDefStruct::new(vec![type_metadata::NamedField::new( - "access", - as type_metadata::Metadata>::meta_type(), - )]) - .into() - } -} - -impl<'a, E> AccessEnv for &'a EnvAccess { - type Target = core::cell::RefMut<'a, EnvAccessMut>; - - #[inline] - fn env(self) -> Self::Target { - self.access.borrow_mut() - } -} - -impl<'a, E> AccessEnv for &'a mut EnvAccess { - type Target = &'a mut EnvAccessMut; - - #[inline] - fn env(self) -> Self::Target { - self.access.get_mut() - } -} - -impl AllocateUsing for EnvAccess { - #[inline] - unsafe fn allocate_using(_alloc: &mut A) -> Self - where - A: Allocate, - { - Self::default() - } -} - -impl Flush for EnvAccess {} - -impl Initialize for EnvAccess { - type Args = (); - - #[inline(always)] - fn initialize(&mut self, _args: Self::Args) {} -} - -impl Default for EnvAccess { - fn default() -> Self { - Self { - access: RefCell::new(Default::default()), - } - } -} - -impl From> for EnvAccess { - fn from(env_access_mut: EnvAccessMut) -> Self { - Self { - access: RefCell::new(env_access_mut), - } - } -} - -macro_rules! impl_forward_for { - ( - $( #[$meta:meta] )* - fn $fn_name:ident $( < $($gen_arg:ident),* > )? ( &self $(, $arg_name:ident : $arg_ty:ty )* ) - $( - where - $( - $bound_ident:ident : $bound_ty:path - ),* - )? - ; - - $($tt:tt)* - ) => { - impl_forward_for!( - $( #[$meta] )* - fn $fn_name $( < $($gen_arg),* > )? ( &self $(, $arg_name : $arg_ty )* ) -> () - $( - where - $( - $bound_ident : $bound_ty - ),* - )? - ; - - $($tt)* - ); - }; - ( - $( #[$meta:meta] )* - fn $fn_name:ident $( < $($gen_arg:ident),* > )? ( &self $(, $arg_name:ident : $arg_ty:ty )* ) -> $ret:ty - $( - where - $( - $bound_ident:ident : $bound_ty:path - ),* - )? - ; - - $($tt:tt)* - ) => { - $( #[$meta] )* - pub fn $fn_name $( < $($gen_arg),* > )? (&self $(, $arg_name : $arg_ty)* ) -> $ret - $( - where - $( - $bound_ident : $bound_ty - ),* - )? - { - self.access.borrow_mut().$fn_name( $($arg_name),* ) - } - - impl_forward_for!($($tt)*); - }; - () => {}; -} - -impl EnvAccess -where - T: Env, -{ - impl_forward_for! { - /// Returns the address of the caller of the executed contract. - fn caller(&self) -> T::AccountId; - /// Returns the transferred balance for the contract execution. - fn transferred_balance(&self) -> T::Balance; - /// Returns the current price for gas. - fn gas_price(&self) -> T::Balance; - /// Returns the amount of gas left for the contract execution. - fn gas_left(&self) -> T::Balance; - /// Returns the current block time in milliseconds. - fn now_in_ms(&self) -> T::Moment; - /// Returns the address of the executed contract. - fn address(&self) -> T::AccountId; - /// Returns the balance of the executed contract. - fn balance(&self) -> T::Balance; - /// Returns the current rent allowance for the executed contract. - fn rent_allowance(&self) -> T::Balance; - /// Returns the current block number. - fn block_number(&self) -> T::BlockNumber; - /// Returns the minimum balance of the executed contract. - fn minimum_balance(&self) -> T::Balance; - - /// Sets the rent allowance of the executed contract to the new value. - fn set_rent_allowance(&self, new_value: T::Balance); - - /// Writes the value to the contract storage under the given key. - fn set_contract_storage(&self, key: Key, value: &V) - where - V: scale::Encode; - - /// Returns the value stored under the given key in the contract's storage. - /// - /// # Errors - /// - /// - If the key's entry is empty - /// - If the decoding of the typed value failed - fn get_contract_storage(&self, key: Key) -> Result - where - R: scale::Decode; - - /// Clears the contract's storage key entry. - fn clear_contract_storage(&self, key: Key); - - /// Invokes a contract message. - /// - /// # Errors - /// - /// If the called contract has trapped. - fn invoke_contract(&self, call_data: &CallParams) -> Result<()>; - - /// Evaluates a contract message and returns its result. - /// - /// # Errors - /// - /// - If the called contract traps. - /// - If the account ID is invalid. - /// - If given too little endowment. - /// - If arguments passed to the called contract are invalid. - /// - If the called contract runs out of gas. - fn eval_contract(&self, call_data: &CallParams>) -> Result - where - R: scale::Decode; - - /// Instantiates another contract. - /// - /// # Errors - /// - /// - If the instantiation process traps. - /// - If the code hash is invalid. - /// - If given too little endowment. - /// - If the instantiation process runs out of gas. - fn create_contract(&self, params: &CreateParams) -> Result; - - /// Emits an event with the given event data. - fn emit_event(&self, event: Event) - where - Event: Topics, - Event: scale::Encode; - - /// Returns the input to the executed contract. - /// - /// # Note - /// - /// - The input is the 4-bytes selector followed by the arguments - /// of the called function in their SCALE encoded representation. - /// - This property must be received as the first action an executed - /// contract to its environment and can only be queried once. - /// The environment access asserts this guarantee. - fn input(&self) -> CallData; - - /// Returns the value back to the caller of the executed contract. - /// - /// # Note - /// - /// The setting of this property must be the last interaction between - /// the executed contract and its environment. - /// The environment access asserts this guarantee. - fn output(&self, return_value: &R) - where - R: scale::Encode; - - /// Returns a random hash. - /// - /// # Note - /// - /// The subject buffer can be used to further randomize the hash. - fn random(&self, subject: &[u8]) -> T::Hash; - - /// Prints the given contents to the environmental log. - fn println(&self, content: &str); - - /// Returns the value from the *runtime* storage at the position of the key. - /// - /// # Errors - /// - /// - If the key's entry is empty - /// - If the decoding of the typed value failed - fn get_runtime_storage(&self, key: &[u8]) -> Result - where - R: scale::Decode; - } -} diff --git a/core/src/env2/env_access/mod.rs b/core/src/env2/env_access/mod.rs deleted file mode 100644 index b3c673c2e7b..00000000000 --- a/core/src/env2/env_access/mod.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018-2019 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 immutable; -mod mutable; - -pub use self::{ - immutable::EnvAccess, - mutable::{ - EmitEvent, - EnvAccessMut, - }, -}; - -/// Allows to access the environment from `&EnvAccess` and `&mut EnvAccess` -/// respectively with different degree of efficiency. -pub trait AccessEnv { - /// The environment definition. - type Target; - - /// Access the environment. - fn env(self) -> Self::Target; -} diff --git a/core/src/env2/env_access/mutable.rs b/core/src/env2/env_access/mutable.rs deleted file mode 100644 index cdee00312cf..00000000000 --- a/core/src/env2/env_access/mutable.rs +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright 2018-2019 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 core::marker::PhantomData; - -use crate::{ - env2::{ - call::{ - CallData, - CallParams, - CreateParams, - ReturnType, - }, - property, - Env, - EnvTypes, - GetProperty, - Result, - SetProperty, - Topics, - }, - storage::{ - alloc::{ - Allocate, - AllocateUsing, - Initialize, - }, - Flush, - Key, - }, -}; -use ink_prelude::vec::Vec; - -#[cfg_attr(feature = "ink-generate-abi", derive(type_metadata::Metadata))] -#[derive(Debug)] -/// A wrapper around environments to make accessing them more efficient. -pub struct EnvAccessMut { - /// The wrapped environment to access. - env: PhantomData, - /// A buffer to make environment accesses - /// more efficient by avoiding allocations. - buffer: Vec, - /// False as long as there has been no interaction between - /// the executed contract and the environment. - /// - /// This flag is used to check at runtime if the environment - /// is used correctly in respect to accessing its input. - has_interacted: bool, - /// True as long as the return value has not yet been set. - /// - /// This flag is used to check at runtime if the environment - /// is used correctly in respect to returning its value. - has_returned_value: bool, -} - -impl AllocateUsing for EnvAccessMut { - #[inline] - unsafe fn allocate_using(_alloc: &mut A) -> Self - where - A: Allocate, - { - Self::default() - } -} - -impl Flush for EnvAccessMut {} - -impl Initialize for EnvAccessMut { - type Args = (); - - #[inline(always)] - fn initialize(&mut self, _args: Self::Args) {} -} - -impl Default for EnvAccessMut { - fn default() -> Self { - Self { - env: Default::default(), - buffer: Default::default(), - has_interacted: false, - has_returned_value: false, - } - } -} - -impl EnvTypes for EnvAccessMut -where - T: EnvTypes, -{ - /// The type of an address. - type AccountId = T::AccountId; - /// The type of balances. - type Balance = T::Balance; - /// The type of hash. - type Hash = T::Hash; - /// The type of timestamps. - type Moment = T::Moment; - /// The type of block number. - type BlockNumber = T::BlockNumber; - /// The type of a call into the runtime - type Call = T::Call; -} - -macro_rules! impl_get_property_for { - ( - $( #[$meta:meta] )* - fn $fn_name:ident< $prop_name:ident >() -> $ret:ty; $($tt:tt)* - ) => { - $( #[$meta] )* - pub fn $fn_name(&mut self) -> $ret { - self.assert_not_yet_returned(); - self.set_has_interacted(); - >>::get_property(&mut self.buffer) - } - - impl_get_property_for!($($tt)*); - }; - () => {} -} - -/// Allow emitting generic events. -pub trait EmitEvent -where - T: Env, -{ - /// Emits an event with the given event data. - fn emit_event(&mut self, event: Event) - where - Event: Topics + scale::Encode; -} - -impl EmitEvent for EnvAccessMut -where - T: Env, -{ - /// Emits an event with the given event data. - fn emit_event(&mut self, event: Event) - where - Event: Topics + scale::Encode, - { - T::emit_event(&mut self.buffer, event) - } -} - -impl EnvAccessMut -where - T: Env, -{ - /// Asserts that no value has been returned yet by the contract execution. - fn assert_not_yet_returned(&self) { - assert!(!self.has_returned_value) - } - - /// Sets the flag for recording interaction between executed contract - /// and environment to `true`. - fn set_has_interacted(&mut self) { - self.has_interacted = true; - } - - impl_get_property_for! { - /// Returns the address of the caller of the executed contract. - fn caller() -> T::AccountId; - /// Returns the transferred balance for the contract execution. - fn transferred_balance() -> T::Balance; - /// Returns the current price for gas. - fn gas_price() -> T::Balance; - /// Returns the amount of gas left for the contract execution. - fn gas_left() -> T::Balance; - /// Returns the current block time in milliseconds. - fn now_in_ms() -> T::Moment; - /// Returns the address of the executed contract. - fn address
() -> T::AccountId; - /// Returns the balance of the executed contract. - fn balance() -> T::Balance; - /// Returns the current rent allowance for the executed contract. - fn rent_allowance() -> T::Balance; - /// Returns the current block number. - fn block_number() -> T::BlockNumber; - /// Returns the minimum balance of the executed contract. - fn minimum_balance() -> T::Balance; - } - - /// Sets the rent allowance of the executed contract to the new value. - pub fn set_rent_allowance(&mut self, new_value: T::Balance) { - self.assert_not_yet_returned(); - self.set_has_interacted(); - >>::set_property( - &mut self.buffer, - &new_value, - ) - } - - /// Writes the value to the contract storage under the given key. - pub fn set_contract_storage(&mut self, key: Key, value: &V) - where - V: scale::Encode, - { - T::set_contract_storage(&mut self.buffer, key, value) - } - - /// Returns the value stored under the given key in the contract's storage. - /// - /// # Errors - /// - /// - If the key's entry is empty - /// - If the decoding of the typed value failed - pub fn get_contract_storage(&mut self, key: Key) -> Result - where - R: scale::Decode, - { - T::get_contract_storage(&mut self.buffer, key) - } - - /// Clears the contract's storage key entry. - pub fn clear_contract_storage(&mut self, key: Key) { - T::clear_contract_storage(key) - } - - /// Invokes a contract message. - /// - /// # Errors - /// - /// If the called contract has trapped. - pub fn invoke_contract(&mut self, call_data: &CallParams) -> Result<()> { - T::invoke_contract(&mut self.buffer, call_data) - } - - /// Evaluates a contract message and returns its result. - /// - /// # Errors - /// - /// - If the called contract traps. - /// - If the account ID is invalid. - /// - If given too few endowment. - /// - If arguments passed to the called contract are invalid. - /// - If the called contract runs out of gas. - pub fn eval_contract( - &mut self, - call_data: &CallParams>, - ) -> Result - where - R: scale::Decode, - { - T::eval_contract(&mut self.buffer, call_data) - } - - /// Instantiates another contract. - /// - /// # Errors - /// - /// - If the instantiation process traps. - /// - If the code hash is invalid. - /// - If given too few endowment. - /// - If the instantiation process runs out of gas. - pub fn create_contract( - &mut self, - params: &CreateParams, - ) -> Result { - T::create_contract(&mut self.buffer, params) - } - - /// Returns the input to the executed contract. - /// - /// # Note - /// - /// - The input is the 4-bytes selector followed by the arguments - /// of the called function in their SCALE encoded representation. - /// - This property must be received as the first action an executed - /// contract to its environment and can only be queried once. - /// The environment access asserts this guarantee. - pub fn input(&mut self) -> CallData { - assert!(!self.has_interacted); - self.assert_not_yet_returned(); - self.set_has_interacted(); - >>::get_property(&mut self.buffer) - } - - /// Returns the value back to the caller of the executed contract. - /// - /// # Note - /// - /// The setting of this property must be the last interaction between - /// the executed contract and its environment. - /// The environment access asserts this guarantee. - pub fn output(&mut self, return_value: &R) - where - R: scale::Encode, - { - self.assert_not_yet_returned(); - self.set_has_interacted(); - self.has_returned_value = true; - T::output(&mut self.buffer, &return_value); - } - - /// Returns a random hash. - /// - /// # Note - /// - /// The subject buffer can be used to further randomize the hash. - pub fn random(&mut self, subject: &[u8]) -> T::Hash { - self.assert_not_yet_returned(); - self.set_has_interacted(); - T::random(&mut self.buffer, subject) - } - - /// Prints the given contents to the environmental log. - pub fn println(&mut self, content: &str) { - T::println(content) - } - - /// Returns the value from the *runtime* storage at the position of the key. - /// - /// # Errors - /// - /// - If the key's entry is empty - /// - If the decoding of the typed value failed - pub fn get_runtime_storage(&mut self, key: &[u8]) -> Result - where - R: scale::Decode, - { - T::get_runtime_storage(&mut self.buffer, key) - } -} diff --git a/core/src/env2/error.rs b/core/src/env2/error.rs deleted file mode 100644 index db405b564b4..00000000000 --- a/core/src/env2/error.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018-2019 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. - -//! Error definitions specific to environment accesses. - -use derive_more::From; - -/// Error encountered by calling a remote contract. -/// -/// # Note -/// -/// This is currently just a placeholder for potential future error codes. -#[derive(Debug, Copy, Clone)] -pub struct CallError; - -/// Error encountered upon creating and instantiation a new smart contract. -/// -/// # Note -/// -/// This is currently just a placeholder for potential future error codes. -#[derive(Debug, Copy, Clone)] -pub struct CreateError; - -/// Errors that can be encountered while accessing the contract's environment. -#[derive(Debug, Clone, From)] -pub enum Error { - Call(CallError), - Create(CreateError), - Codec(scale::Error), - InvalidStorageKey, - InvalidStorageRead, - InvalidContractCall, - InvalidContractCallReturn, - InvalidContractInstantiation, - InvalidContractInstantiationReturn, - InvalidRandomSeed, -} - -/// The environmental error type. -pub type Result = core::result::Result; diff --git a/core/src/env2/mod.rs b/core/src/env2/mod.rs deleted file mode 100644 index a5892df924b..00000000000 --- a/core/src/env2/mod.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2018-2019 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. - -//! Environment definitions and access. -//! -//! Provides 5 different preferred ways to access the underlying environment. -//! -//! | Access Type | Optimized Access | Restricted Access | Checked Access | Dynamic Allocator | -//! |---|---|---|---|---| -//! | `SrmlEnv` or `TestEnv` | No | - | No | No | -//! | `EnvAccess` | Yes | `&self` | Yes (@rt) | No | -//! | `EnvAccessMut` | Yes | `&mut self` | Yes (@ct) | No | -//! | `DynEnv` | Yes | `&self` | Yes (@rt) | Yes | -//! | `DynEnv` | Yes | `&mut self` | Yes (@ct) | Yes | -//! -//! * - @rt: reads "at runtime" -//! * - @ct: reads "at compiletime" -//! -//! # Explanations -//! -//! - **Optimized Access:** Tries to reuse buffers and minimize allocations. -//! - **Restricted Access:** Restricts usage for certain message types, e.g. only for `&mut self` messages. -//! - **Checked Access:** Checks certain accesses to the environment for obvious failures. -//! - **Dynamic Allocator:** Additionally provides an out-of-box dynamic allocator and an interface to -//! allocate and instantiate dynamic storage objects. -//! -//! # Note -//! -//! - If your contract uses dynamic allocations prefer using `DynEnvAccess` or `DynEnvAccessMut`. -//! - For `&self` messages prefer using `EnvAccess` or `DynEnvAccess`. -//! - For `&mut self` messages prefer using `EnvAccessMut` or `DynEnvAccessMut`. -//! - Direct access to `SrmlEnv` or `TestEnv` is always the least optimal solution and generally not preferred. - -mod buffer_arena; -pub mod call; -mod dyn_env; -mod env_access; -mod error; -pub mod property; -mod traits; -pub mod types; -pub mod utils; - -use cfg_if::cfg_if; - -/// Error definitions specific to environment accesses. -pub mod errors { - pub use super::error::{ - CallError, - CreateError, - }; -} - -cfg_if! { - if #[cfg(feature = "test-env")] { - pub mod test; - /// The currently chosen environmental implementation. - /// - /// When compiling for Wasm and Substrate this refers to `SrmlEnv` and - /// when compiling for off-chain testing this refer to `TestEnv`. - /// - /// This configuration compiled for off-chain testing. - pub type EnvImpl = self::test::TestEnv; - } else { - mod srml; - pub use self::srml::{ - RetCode, - }; - /// The currently chosen environmental implementation. - /// - /// When compiling for Wasm and Substrate this refers to `SrmlEnv` and - /// when compiling for off-chain testing this refer to `TestEnv`. - /// - /// This configuration compiled as Wasm for Substrate. - pub type EnvImpl = self::srml::SrmlEnv; - } -} - -#[cfg(not(feature = "std"))] -pub use self::buffer_arena::GlobalBufferArena; - -pub use self::{ - buffer_arena::{ - BufferArena, - BufferRef, - BUFFER_ARENA, - }, - dyn_env::DynEnv, - env_access::{ - AccessEnv, - EmitEvent, - EnvAccess, - EnvAccessMut, - }, - error::{ - Error, - Result, - }, - traits::{ - Env, - EnvTypes, - GetProperty, - SetProperty, - Topics, - }, - types::DefaultSrmlTypes, -}; diff --git a/core/src/env2/property.rs b/core/src/env2/property.rs deleted file mode 100644 index 036249429e7..00000000000 --- a/core/src/env2/property.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2018-2019 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. - -//! Smart contract properties. -//! -//! Allow to query and mutate certain properties of a smart contract. - -use core::marker::PhantomData; - -use crate::env2::{ - call::CallData, - EnvTypes, -}; - -pub(crate) mod private { - /// A seal to prevent outside crates to implement new properties. - pub trait PropertySeal {} - /// A seal to prevent outside crates to change the read flags of existing properties. - pub trait ReadPropertySeal {} - /// A seal to prevent outside crates to change the write flags of existing properties. - pub trait WritePropertySeal {} -} - -/// A property. -/// -/// # Note -/// -/// Properties are information a contract can query or mutate. -/// Properties can either be read, write or both. -pub trait Property: private::PropertySeal {} -/// A read property can be read from a contract. -/// -/// # Examples -/// -/// The `AccountId` property can be read from a contract. -pub trait ReadProperty: Property + private::ReadPropertySeal { - type In: scale::Decode + ?Sized; -} -/// A write property can be mutated by a contract. -/// -/// # Examples -/// -/// The `MinimumDeposit` can be mutated by a contract. -pub trait WriteProperty: Property + private::WritePropertySeal { - type Out: scale::Encode + ?Sized; -} - -macro_rules! impl_property_for { - ( - $( #[$meta:meta] )* - struct $name:ident { read: Some<$in:ty>, write: Some<$out:ty> }, $($tt:tt)* - ) => { - $( #[$meta] )* - pub struct $name { marker: PhantomData E>, } - impl $crate::env2::property::Property for $name {} - impl $crate::env2::property::private::PropertySeal for $name {} - impl $crate::env2::property::ReadProperty for $name - where - E: EnvTypes, - { - type In = $in; - } - impl $crate::env2::property::private::ReadPropertySeal for $name {} - impl $crate::env2::property::WriteProperty for $name - where - E: EnvTypes, - { - type Out = $out; - } - impl $crate::env2::property::private::WritePropertySeal for $name {} - - impl_property_for! { $($tt)* } - }; - ( - $( #[$meta:meta] )* - struct $name:ident { read: Some<$in:ty>, write: None }, $($tt:tt)* - ) => { - $( #[$meta] )* - pub struct $name { marker: PhantomData E>, } - impl $crate::env2::property::Property for $name {} - impl $crate::env2::property::private::PropertySeal for $name {} - impl $crate::env2::property::ReadProperty for $name - where - E: EnvTypes, - { - type In = $in; - } - impl $crate::env2::property::private::ReadPropertySeal for $name {} - - impl_property_for! { $($tt)* } - }; - ( - $( #[$meta:meta] )* - struct $name:ident { read: None, write: Some<$out:ty> }, $($tt:tt)* - ) => { - $( #[$meta] )* - pub struct $name { marker: PhantomData E>, } - impl $crate::env2::property::Property for $name {} - impl $crate::env2::property::private::PropertySeal for $name {} - impl $crate::env2::property::WriteProperty for $name - where - E: EnvTypes, - { - type Out = $out; - } - impl $crate::env2::property::private::WritePropertySeal for $name {} - - impl_property_for! { $($tt)* } - }; - () => {}; -} - -impl_property_for! { - /// The caller of an executed contract. - struct Caller { read: Some, write: None }, - /// The transferred balance for the contract execution. - struct TransferredBalance { read: Some, write: None }, - /// The current gas price. - struct GasPrice { read: Some, write: None }, - /// The amount of gas left for the current contract execution. - struct GasLeft { read: Some, write: None }, - /// The block time in milli seconds. - struct NowInMs { read: Some, write: None }, - /// The account ID of the executed contract. - struct Address { read: Some, write: None }, - /// The balance of the executed contract. - struct Balance { read: Some, write: None }, - /// The rent allowance of the executed contract. - struct RentAllowance { read: Some, write: Some }, - /// The current block number. - struct BlockNumber { read: Some, write: None }, - /// The minimum possible balance for a contract. - struct MinimumBalance { read: Some, write: None }, - /// The input data for this contract execution. - struct Input { read: Some, write: None }, -} diff --git a/core/src/env2/srml/impls.rs b/core/src/env2/srml/impls.rs deleted file mode 100644 index 8e73b2758cf..00000000000 --- a/core/src/env2/srml/impls.rs +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright 2018-2019 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 core::marker::PhantomData; - -use scale::{ - Decode, - Encode, -}; - -use crate::{ - env2::{ - call::{ - CallParams, - CreateParams, - ReturnType, - }, - property, - srml::ext, - utils::{ - EnlargeTo, - Reset, - }, - Env, - EnvTypes, - Error, - GetProperty, - Result, - SetProperty, - Topics, - }, - storage::Key, -}; - -/// The SRML contract environment. -/// -/// # Dev Notes -/// -/// The implementation of environmental access routines have to be as efficient -/// as possible to not waste gas by any means. This has the effect that implementation -/// of some environmental access routines defined here are suboptimal in respect -/// to readability. -/// -/// Any avoidable inefficiency is regarded by us as a bug. -/// If you spot such an inefficiency please report an issue at: -/// https://github.com/paritytech/ink/issues. -pub struct SrmlEnv { - marker: PhantomData T>, -} - -#[cfg(feature = "ink-generate-abi")] -impl type_metadata::HasTypeId for SrmlEnv -where - E: type_metadata::Metadata, -{ - fn type_id() -> type_metadata::TypeId { - type_metadata::TypeIdCustom::new( - "SrmlEnv", - type_metadata::Namespace::from_module_path(module_path!()) - .expect("namespace from module path cannot fail"), - vec![E::meta_type()], - ) - .into() - } -} - -#[cfg(feature = "ink-generate-abi")] -impl type_metadata::HasTypeDef for SrmlEnv { - fn type_def() -> type_metadata::TypeDef { - type_metadata::TypeDefStruct::new(vec![]).into() - } -} - -impl EnvTypes for SrmlEnv -where - T: EnvTypes, -{ - /// The type of an address. - type AccountId = T::AccountId; - /// The type of balances. - type Balance = T::Balance; - /// The type of hash. - type Hash = T::Hash; - /// The type of timestamps. - type Moment = T::Moment; - /// The type of block number. - type BlockNumber = T::BlockNumber; - /// The type of a call into the runtime - type Call = T::Call; -} - -impl SrmlEnv -where - T: EnvTypes, -{ - fn get_property_impl(buffer: &mut I, ext_fn: fn()) -> P::In - where - P: property::ReadProperty, - I: AsMut<[u8]> + EnlargeTo, - { - ext_fn(); - let req_len = ext::scratch_size(); - buffer.enlarge_to(req_len); - ext::scratch_read(&mut buffer.as_mut()[0..req_len], 0); - Decode::decode(&mut &buffer.as_mut()[0..req_len]) - .expect("failed at decoding the property") - } -} - -macro_rules! impl_get_property_for { - ( $( ($name:ident use $sys_fn:expr), )* ) => { - $( - impl GetProperty> for SrmlEnv - where - T: EnvTypes, - { - fn get_property( - buffer: &mut I, - ) -> as property::ReadProperty>::In - where - I: AsMut<[u8]> + EnlargeTo, - { - Self::get_property_impl::, _>(buffer, $sys_fn) - } - } - )* - } -} - -impl_get_property_for! { - (Caller use ext::caller), - (TransferredBalance use ext::value_transferred), - (GasPrice use ext::gas_price), - (GasLeft use ext::gas_left), - (NowInMs use ext::now), - (Address use ext::address), - (Balance use ext::balance), - (RentAllowance use ext::rent_allowance), - (BlockNumber use ext::block_number), - (MinimumBalance use ext::minimum_balance), -} - -impl GetProperty> for SrmlEnv -where - T: EnvTypes, -{ - fn get_property( - buffer: &mut I, - ) -> as property::ReadProperty>::In - where - I: AsMut<[u8]> + EnlargeTo, - { - Self::get_property_impl::, _>(buffer, || ()) - } -} - -impl SetProperty> for SrmlEnv -where - T: EnvTypes, -{ - fn set_property( - buffer: &mut O, - value: & as property::WriteProperty>::Out, - ) where - O: scale::Output + AsRef<[u8]> + Reset, - { - buffer.reset(); - value.encode_to(buffer); - ext::set_rent_allowance(buffer.as_ref()); - } -} - -impl SrmlEnv -where - T: EnvTypes, -{ - /// Invokes the cross-contract call from the given parameters. - /// - /// Uses the given buffer as cache for intermediate data. - fn invoke_contract_impl( - buffer: &mut O, - call_data: &CallParams, - ) -> Result<()> - where - O: scale::Output + AsRef<[u8]> + Reset, - { - // First we reset the buffer to start from a clean slate. - buffer.reset(); - // Now we encode `call_data`, `endowment` and `input_data` - // each after one another into our buffer and remember their - // boundaries using the guards. - call_data.callee().encode_to(buffer); - let callee_guard = buffer.as_ref().len(); - call_data.endowment().encode_to(buffer); - let endowment_guard = buffer.as_ref().len(); - call_data.input_data().encode_to(buffer); - // We now use the guards in order to split the buffer into - // some read-only slices that each store their respective - // encoded value and call the actual routine. - let callee = &buffer.as_ref()[0..callee_guard]; - let endowment = &buffer.as_ref()[callee_guard..endowment_guard]; - let gas_limit = call_data.gas_limit(); - let call_data = &buffer.as_ref()[endowment_guard..]; - // Do the actual contract call. - let ret = ext::call(callee, gas_limit, endowment, call_data); - if !ret.is_success() { - // Maybe the called contract trapped. - return Err(Error::InvalidContractCall) - } - Ok(()) - } -} - -impl Env for SrmlEnv -where - T: EnvTypes, -{ - fn get_contract_storage(buffer: &mut I, key: Key) -> Result - where - I: AsMut<[u8]> + EnlargeTo, - R: scale::Decode, - { - let ret = ext::get_storage(key.as_bytes()); - if !ret.is_success() { - return Err(Error::InvalidStorageKey) - } - let req_len = ext::scratch_size(); - buffer.enlarge_to(req_len); - let ret = ext::scratch_read(&mut buffer.as_mut()[0..req_len], 0); - if !ret.is_success() { - return Err(Error::InvalidStorageRead) - } - Decode::decode(&mut &buffer.as_mut()[0..req_len]).map_err(Into::into) - } - - fn set_contract_storage(buffer: &mut O, key: Key, val: &V) - where - O: scale::Output + AsRef<[u8]> + Reset, - V: scale::Encode, - { - buffer.reset(); - val.encode_to(buffer); - ext::set_storage(key.as_bytes(), Some(buffer.as_ref())); - } - - fn clear_contract_storage(key: Key) { - ext::set_storage(key.as_bytes(), None); - } - - fn invoke_contract(buffer: &mut O, call_data: &CallParams) -> Result<()> - where - O: scale::Output + AsRef<[u8]> + Reset, - { - Self::invoke_contract_impl(buffer, call_data) - } - - fn eval_contract( - buffer: &mut IO, - call_data: &CallParams>, - ) -> Result - where - IO: scale::Output + AsRef<[u8]> + AsMut<[u8]> + EnlargeTo + Reset, - R: scale::Decode, - { - Self::invoke_contract_impl(buffer, call_data)?; - // At this point our call was successful and we can now fetch - // the returned data and decode it for the result value. - let req_len = ext::scratch_size(); - buffer.enlarge_to(req_len); - let ret = ext::scratch_read(&mut buffer.as_mut()[0..req_len], 0); - if !ret.is_success() { - return Err(Error::InvalidContractCallReturn) - } - Decode::decode(&mut &buffer.as_ref()[0..req_len]).map_err(Into::into) - } - - fn create_contract( - buffer: &mut IO, - create_data: &CreateParams, - ) -> Result - where - IO: scale::Output + AsRef<[u8]> + AsMut<[u8]> + EnlargeTo + Reset, - { - // First we reset the buffer to start from a clean slate. - buffer.reset(); - // Now we encode `code_hash`, `endowment` and `input_data` - // each after one another into our buffer and remember their - // boundaries using the guards. - create_data.code_hash().encode_to(buffer); - let code_hash_guard = buffer.as_ref().len(); - create_data.endowment().encode_to(buffer); - let endowment_guard = buffer.as_ref().len(); - create_data.input_data().encode_to(buffer); - // We now use the guards in order to split the buffer into - // some read-only slices that each store their respective - // encoded value and call the actual routine. - let code_hash = &buffer.as_ref()[0..code_hash_guard]; - let endowment = &buffer.as_ref()[code_hash_guard..endowment_guard]; - let gas_limit = create_data.gas_limit(); - let call_data = &buffer.as_ref()[endowment_guard..]; - // Do the actual contract instantiation. - let ret = ext::create(code_hash, gas_limit, endowment, call_data); - if !ret.is_success() { - // Maybe the called contract trapped. - return Err(Error::InvalidContractInstantiation) - } - // At this point our contract instantiation was successful - // and we can now fetch the returned data and decode it for - // the result value. - let req_len = ext::scratch_size(); - buffer.enlarge_to(req_len); - let ret = ext::scratch_read(&mut buffer.as_mut()[0..req_len], 0); - if !ret.is_success() { - return Err(Error::InvalidContractInstantiationReturn) - } - Decode::decode(&mut &buffer.as_ref()[0..req_len]).map_err(Into::into) - } - - fn emit_event(buffer: &mut O, event: Event) - where - O: scale::Output + AsRef<[u8]> + Reset, - Event: Topics + scale::Encode, - { - // First we reset the buffer to start from a clean slate. - buffer.reset(); - // Now we encode `topics` and the raw encoded `data` - // each after one another into our buffer and remember their - // boundaries using guards respectively. - event.topics().encode_to(buffer); - let topics_guard = buffer.as_ref().len(); - event.encode_to(buffer); - // We now use the guards in order to split the buffer into - // some read-only slices that each store their respective - // encoded value and call the actual routine. - let topics = &buffer.as_ref()[0..topics_guard]; - let data = &buffer.as_ref()[topics_guard..]; - // Do the actual depositing of the event. - ext::deposit_event(topics, data); - } - - fn invoke_runtime(buffer: &mut O, call_data: &::Call) - where - O: scale::Output + AsRef<[u8]> + Reset, - { - buffer.reset(); - call_data.encode_to(buffer); - ext::dispatch_call(buffer.as_ref()); - } - - fn restore_to( - buffer: &mut O, - dest: Self::AccountId, - code_hash: Self::Hash, - rent_allowance: Self::Balance, - filtered_keys: &[Key], - ) where - O: scale::Output + AsRef<[u8]> + Reset, - { - // First we reset the buffer to start from a clean slate. - buffer.reset(); - // Now we encode `dest`, `code_hash` and `rent_allowance` - // each after one another into our buffer and remember their - // boundaries using guards respectively. - dest.encode_to(buffer); - let dest_guard = buffer.as_ref().len(); - code_hash.encode_to(buffer); - let code_hash_guard = buffer.as_ref().len(); - rent_allowance.encode_to(buffer); - // We now use the guards in order to split the buffer into - // some read-only slices that each store their respective - // encoded value and call the actual routine. - let dest = &buffer.as_ref()[0..dest_guard]; - let code_hash = &buffer.as_ref()[dest_guard..code_hash_guard]; - let rent_allowance = &buffer.as_ref()[code_hash_guard..]; - // Perform the actual restoration process. - ext::restore_to(dest, code_hash, rent_allowance, filtered_keys); - } - - fn output(buffer: &mut O, return_value: &R) - where - O: scale::Output + AsRef<[u8]> + Reset, - R: scale::Encode, - { - buffer.reset(); - return_value.encode_to(buffer); - ext::scratch_write(buffer.as_ref()); - } - - fn random(buffer: &mut I, subject: &[u8]) -> Self::Hash - where - I: AsMut<[u8]> + EnlargeTo, - { - ext::random_seed(subject); - let req_len = ext::scratch_size(); - buffer.enlarge_to(req_len); - ext::scratch_read(&mut buffer.as_mut()[0..req_len], 0); - Decode::decode(&mut &buffer.as_mut()[0..req_len]) - .expect("failed at decoding value returned by `ext_random_seed`") - } - - fn println(content: &str) { - ext::println(content) - } - - fn get_runtime_storage(buffer: &mut I, key: &[u8]) -> Result - where - I: AsMut<[u8]> + EnlargeTo, - R: scale::Decode, - { - let ret = ext::get_runtime_storage(key); - if !ret.is_success() { - return Err(Error::InvalidStorageKey) - } - let req_len = ext::scratch_size(); - buffer.enlarge_to(req_len); - let ret = ext::scratch_read(&mut buffer.as_mut()[0..req_len], 0); - if !ret.is_success() { - return Err(Error::InvalidStorageRead) - } - Decode::decode(&mut &buffer.as_mut()[0..req_len]).map_err(Into::into) - } -} diff --git a/core/src/env2/srml/mod.rs b/core/src/env2/srml/mod.rs deleted file mode 100644 index 69ed44777d0..00000000000 --- a/core/src/env2/srml/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018-2019 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. - -pub mod ext; -mod impls; -mod ret_code; - -pub use self::{ - impls::SrmlEnv, - ret_code::RetCode, -}; diff --git a/core/src/env2/srml/ret_code.rs b/core/src/env2/srml/ret_code.rs deleted file mode 100644 index 622c4d840b4..00000000000 --- a/core/src/env2/srml/ret_code.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2018-2019 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 derive_more::From; - -/// A return code which is the result of an external SRML call. -#[derive(Debug, Copy, Clone, PartialEq, Eq, From)] -pub struct RetCode { - code: u32, -} - -impl RetCode { - /// Creates a `success` indicating return code. - pub fn success() -> Self { - Self { code: 0 } - } - - /// Returns `true` if `self` is success. - pub fn is_success(self) -> bool { - self.code == 0 - } - - /// Returns the `u32` representation of `self`. - pub fn to_u32(self) -> u32 { - self.code - } -} diff --git a/core/src/env2/test/accessor.rs b/core/src/env2/test/accessor.rs deleted file mode 100644 index 2e9d867fa7d..00000000000 --- a/core/src/env2/test/accessor.rs +++ /dev/null @@ -1,528 +0,0 @@ -// Copyright 2018-2019 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. - -//! # For Developers -//! -//! For the implementation of the `Env` trait and all of its sub traits -//! we can ignore the `buffer` parameters that we use extensively in the -//! `SrmlEnv` implementation since here we already have our buffers in the -//! contract's memory. - -use core::{ - cell::{ - Ref, - RefCell, - RefMut, - }, - marker::PhantomData, -}; - -use crate::{ - env2::{ - call::{ - CallParams, - CreateParams, - ReturnType, - }, - error::CallError, - property, - test::{ - typed_encoded::AlreadyInitialized, - Account, - AccountKind, - CallContractRecord, - ContractAccount, - CreateContractRecord, - EmitEventRecord, - InvokeRuntimeRecord, - Record, - RestoreContractRecord, - TestEnvInstance, - TypedEncoded, - }, - utils::{ - EnlargeTo, - Reset, - }, - Env, - EnvTypes, - Error, - GetProperty, - Result, - SetProperty, - Topics, - }, - storage::Key, -}; -use scale::Encode; - -thread_local! { - /// The single thread-local test environment instance. - /// - /// # Note - /// - /// This needs to be thread local since tests are run - /// in parallel by default which may lead to data races otherwise. - pub static INSTANCE: RefCell = { - RefCell::new(TestEnvInstance::default()) - }; -} - -/// Accessor to the test environment instance. -/// -/// This acts as the real test environment to the outside. -pub struct TestEnv { - /// Needed to trick Rust into allowing `T`. - marker: PhantomData T>, -} - -#[cfg(feature = "ink-generate-abi")] -impl type_metadata::HasTypeId for TestEnv -where - E: type_metadata::Metadata, -{ - fn type_id() -> type_metadata::TypeId { - type_metadata::TypeIdCustom::new( - "TestEnv", - type_metadata::Namespace::from_module_path(module_path!()) - .expect("namespace from module path cannot fail"), - vec![E::meta_type()], - ) - .into() - } -} - -#[cfg(feature = "ink-generate-abi")] -impl type_metadata::HasTypeDef for TestEnv { - fn type_def() -> type_metadata::TypeDef { - type_metadata::TypeDefStruct::new(vec![]).into() - } -} - -impl TestEnv -where - T: EnvTypes, - // The below constraints are satisfied for the default SRLM type configuration. - ::AccountId: From<[u8; 32]>, - ::Balance: From, - ::BlockNumber: From, - ::Moment: From, -{ - /// Tries to initialize the test environment with proper state. - /// - /// The test environment must only be used after it has been initialized. - /// Otherwise accessing its state will certainly crash the execution due - /// to type mismatches. - /// - /// # Errors - /// - /// If the test environment has already been initialized. - pub fn try_initialize() -> core::result::Result<(), AlreadyInitialized> { - INSTANCE.with(|instance| instance.borrow_mut().try_initialize::()) - } -} - -impl EnvTypes for TestEnv -where - T: EnvTypes, -{ - /// The type of an address. - type AccountId = T::AccountId; - /// The type of balances. - type Balance = T::Balance; - /// The type of hash. - type Hash = T::Hash; - /// The type of timestamps. - type Moment = T::Moment; - /// The type of block number. - type BlockNumber = T::BlockNumber; - /// The type of a call into the runtime - type Call = T::Call; -} - -impl SetProperty> for TestEnv -where - T: EnvTypes, -{ - fn set_property( - _buffer: &mut O, - value: & as property::WriteProperty>::Out, - ) where - O: scale::Output + AsRef<[u8]> + Reset, - { - INSTANCE.with(|instance| { - let mut account = RefMut::map(instance.borrow_mut(), |instance| { - let account_id = &instance.exec_context.callee; - instance - .accounts - .get_mut(account_id) - .expect("callee is required to be in the accounts DB") - }); - account.rent_allowance.assign(value); - }) - } -} - -macro_rules! impl_get_property_for { - ( $prop_name:ident => |$name:ident| $body:tt $($rest:tt)* ) => { - impl GetProperty> for TestEnv - where - T: EnvTypes, - { - fn get_property( - _buffer: &mut I, - ) -> as property::ReadProperty>::In - where - I: AsMut<[u8]> + EnlargeTo, - { - INSTANCE.with(|$name| $body) - } - } - - impl_get_property_for!($($rest)*); - }; - () => {}; -} - -impl_get_property_for! { - Input => |instance| { - instance.borrow().exec_context.call_data.clone() - } - - Caller => |instance| { - instance.borrow().exec_context.caller.to_origin() - } - - TransferredBalance => |instance| { - instance.borrow().exec_context.transferred_balance.to_origin() - } - - GasPrice => |instance| { - instance.borrow().state.gas_price.to_origin() - } - - GasLeft => |instance| { - instance.borrow().exec_context.gas_left.to_origin() - } - - NowInMs => |instance| { - instance.borrow().block.now_in_ms.to_origin() - } - - Address => |instance| { - instance.borrow().exec_context.callee.to_origin() - } - - Balance => |instance| { - let account = Ref::map(instance.borrow(), |instance| { - let account_id = &instance.exec_context.callee; - instance.accounts - .get(account_id) - .expect("callee is required to be in the accounts DB") - }); - account.balance.to_origin() - } - - RentAllowance => |instance| { - let account = Ref::map(instance.borrow(), |instance| { - let account_id = &instance.exec_context.callee; - instance.accounts - .get(account_id) - .expect("callee is required to be in the accounts DB") - }); - account.rent_allowance.to_origin() - } - - BlockNumber => |instance| { - instance.borrow().block.number.to_origin() - } - - MinimumBalance => |instance| { - instance.borrow().state.minimum_balance.to_origin() - } -} - -impl Env for TestEnv -where - T: EnvTypes, - ::Hash: From<[u8; 32]>, - ::AccountId: From<[u8; 32]>, -{ - fn get_contract_storage(_buffer: &mut I, key: Key) -> Result - where - I: AsMut<[u8]> + EnlargeTo, - R: scale::Decode, - { - INSTANCE.with(|instance| { - let storage = Ref::map(instance.borrow(), |instance| { - let account_id = &instance.exec_context.callee; - &instance - .accounts - .get(account_id) - .expect("callee is required to be in the accounts DB") - .contract() - .expect("callee must refer to a contract account") - .storage - }); - let encoded = storage - .read(key) - .map(|entry| entry.data()) - .ok_or(Error::InvalidStorageRead)?; - Ok(scale::Decode::decode(&mut &encoded[..]) - .map_err(|_| Error::InvalidStorageRead)?) - }) - } - - fn set_contract_storage(buffer: &mut O, key: Key, value: &V) - where - O: scale::Output + AsRef<[u8]> + Reset, - V: scale::Encode, - { - INSTANCE.with(|instance| { - let mut storage = RefMut::map(instance.borrow_mut(), |instance| { - let account_id = &instance.exec_context.callee; - &mut instance - .accounts - .get_mut(account_id) - .expect("callee is required to be in the accounts DB") - .contract_mut() - .expect("callee must refer to a contract account") - .storage - }); - buffer.reset(); - value.encode_to(buffer); - storage.write(key, buffer.as_ref()); - }) - } - - fn clear_contract_storage(key: Key) { - INSTANCE.with(|instance| { - let mut storage = RefMut::map(instance.borrow_mut(), |instance| { - let account_id = &instance.exec_context.callee; - &mut instance - .accounts - .get_mut(account_id) - .expect("callee is required to be in the accounts DB") - .contract_mut() - .expect("callee must refer to a contract account") - .storage - }); - storage.clear(key); - }) - } - - fn invoke_contract(_buffer: &mut O, call_data: &CallParams) -> Result<()> - where - O: scale::Output + AsRef<[u8]> + Reset, - { - // With the off-chain test environment we have no means to invoke - // a remote contract on the chain since there is no chain. - // What we do instead is to log the call and do nothing. - // The codegen of ink! shall instead call the contract directly - // and log a call through an invokation of this API. - INSTANCE.with(|instance| { - instance - .borrow_mut() - .records - .push(Record::from(CallContractRecord::new(call_data))); - Ok(()) - }) - } - - fn eval_contract( - _buffer: &mut IO, - call_data: &CallParams>, - ) -> Result - where - IO: scale::Output + AsRef<[u8]> + AsMut<[u8]> + EnlargeTo + Reset, - R: scale::Decode, - { - // With the off-chain test environment we have no means to invoke - // a remote contract on the chain since there is no chain. - // What we do instead is to log the call and do nothing. - // The codegen of ink! shall instead call the contract directly - // and log a call through an invokation of this API. - // - // # Note - // - // For the sake of simplicity we will return an error here since - // we cannot generically construct an `R` out of thin air for the - // return type. The codegen of ink! will have to handle this case. - INSTANCE.with(|instance| { - instance - .borrow_mut() - .records - .push(Record::from(CallContractRecord::new(call_data))); - Err(Error::Call(CallError)) - }) - } - - fn create_contract( - _buffer: &mut IO, - create_data: &CreateParams, - ) -> Result - where - IO: scale::Output + AsRef<[u8]> + AsMut<[u8]> + EnlargeTo + Reset, - { - // With the off-chain test environment we have no means to instantiate - // a remote contract on the chain since there is no chain. - // - // Instead we register a new contract account into the emulated accounts - // data base. This is not equivalent to instantiation of a new contract. - // However, this allows to query certain stats about the newly created contract. - INSTANCE.with(|instance| { - // Record the contract instantiation. - instance - .borrow_mut() - .records - .push(Record::from(CreateContractRecord::new(create_data))); - // Actual instantiation of a contract. - let (typed_encoded, account_id) = - instance.borrow_mut().account_id_gen.next::(); - instance.borrow_mut().accounts.insert( - typed_encoded, - Account { - balance: TypedEncoded::from_origin(&0), - rent_allowance: TypedEncoded::from_origin(&0), - kind: AccountKind::Contract(ContractAccount::new( - TypedEncoded::from_origin(create_data.code_hash()), - )), - }, - ); - - Ok(account_id) - }) - } - - fn emit_event(_buffer: &mut O, event: Event) - where - O: scale::Output + AsRef<[u8]> + Reset, - Event: Topics + scale::Encode, - { - // With the off-chain test environment we have no means - // to emit an event on the chain since there is no chain. - // What we do instead is to log the call and do nothing. - INSTANCE.with(|instance| { - instance - .borrow_mut() - .records - .push(Record::from(EmitEventRecord::new(event))); - }) - } - - fn invoke_runtime(_buffer: &mut O, call_data: &::Call) - where - O: scale::Output + AsRef<[u8]> + Reset, - { - // With the off-chain test environment we have no means - // to emit an event on the chain since there is no chain. - // What we do instead is to log the call and do nothing. - // - // Since runtime invokations are async fire-and-forget a - // contract cannot check for it being run anyways. - INSTANCE.with(|instance| { - instance - .borrow_mut() - .records - .push(Record::from(InvokeRuntimeRecord::new(call_data.encode()))); - }) - } - - fn restore_to( - _buffer: &mut O, - dest: Self::AccountId, - code_hash: Self::Hash, - rent_allowance: Self::Balance, - filtered_keys: &[Key], - ) where - O: scale::Output + AsRef<[u8]> + Reset, - { - // With the off-chain test environment we have no means - // to restore another contract on the chain since there is no chain. - // What we do instead is to log the restoration and do nothing. - INSTANCE.with(|instance| { - instance.borrow_mut().records.push(Record::from( - RestoreContractRecord::new::( - &dest, - &code_hash, - &rent_allowance, - filtered_keys, - ), - )); - }) - } - - /// Sets the output of the contract within the test environment. - /// - /// # Panics - /// - /// If this function is called multiple times from within a contract. - /// Setting output multiple times is not allows in any environment - /// so this acts as another safety guard. - fn output(_buffer: &mut O, return_value: &R) - where - O: scale::Output + AsRef<[u8]> + Reset, - R: scale::Encode, - { - // With the off-chain test environment we have no means to - // return a value from a contract since there are no other contracts - // on the chain since there is no chain (I am not even joking ...). - // - // What we do instead is to log the encoded value to make it possible - // to query for it through the test environment after the successful call. - INSTANCE.with(|instance| { - if instance.borrow().exec_context.output.is_some() { - panic!( - "cannot set contract output multiple times within the same execution" - ) - } - instance.borrow_mut().exec_context.output = Some(return_value.encode()); - }) - } - - fn random(_buffer: &mut I, subject: &[u8]) -> Self::Hash - where - I: AsMut<[u8]> + EnlargeTo, - { - // We return a randomized value as best effort. - // This won't have the same guarantees as the `random_seed` functionality - // provided by Substrate. - // Instead we are going to return a unique randomized `Hash` in - // dependence of the given `subject` buffer. - // Given the same `subject` buffer we also return the same `Hash`. - ink_utils::hash::keccak256(subject).into() - } - - fn println(content: &str) { - println!("{}", content) - } - - fn get_runtime_storage(_buffer: &mut I, key: &[u8]) -> Result - where - I: AsMut<[u8]> + EnlargeTo, - R: scale::Decode, - { - INSTANCE.with(|instance| { - let storage = &instance.borrow().state.storage; - // Raw runtime storage keys can be of any length, so hash to fit 32 byte Key size - let key = Key(ink_utils::hash::keccak256(key)); - let encoded = storage - .read(key) - .map(|entry| entry.data()) - .ok_or(Error::InvalidStorageKey)?; - Ok(scale::Decode::decode(&mut &encoded[..]) - .map_err(|_| Error::InvalidStorageRead)?) - }) - } -} diff --git a/core/src/env2/test/account.rs b/core/src/env2/test/account.rs deleted file mode 100644 index d59ca75ee28..00000000000 --- a/core/src/env2/test/account.rs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2018-2019 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. - -//! The accounts database stores all user- and contract accounts -//! of the emulated block chain. -//! -//! It can also be used to store new accounts and query information -//! about existing ones. -//! -//! The executed smart contract as well as the default caller will -//! always have predefined accounts. - -use core::borrow::Borrow; - -use crate::env2::test::{ - storage::Storage, - types::*, - TypedEncoded, -}; -use ink_prelude::collections::btree_map::BTreeMap; - -/// The on-chain registered accounts. -/// -/// An account can be either a user account or a contract account. -#[derive(Debug, Clone)] -pub struct AccountsDb { - /// All on-chain registered accounts. - accounts: BTreeMap, -} - -impl AccountsDb { - /// Returns the number of accounts in the database. - pub fn len(&self) -> usize { - self.accounts.len() - } - - /// Returns `true` if the number of accounts in the database is 0. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Inserts a new account to the data base. - pub fn insert(&mut self, account_id: AccountId, account: Account) { - self.accounts.insert(account_id, account); - } - - /// Returns the account associated with the given ID and otherwise returns `None`. - pub fn get(&self, account_id: &Q) -> Option<&Account> - where - AccountId: Borrow, - Q: Ord, - { - self.accounts.get(account_id) - } - - /// Returns the account associated with the given ID and otherwise returns `None`. - pub fn get_mut(&mut self, account_id: &Q) -> Option<&mut Account> - where - AccountId: Borrow, - Q: Ord, - { - self.accounts.get_mut(account_id) - } -} - -impl Default for AccountsDb { - fn default() -> Self { - Self { - accounts: BTreeMap::new(), - } - } -} - -/// An on-chain registered account. -#[derive(Debug, Clone)] -pub struct Account { - /// The current balance of the account. - pub balance: Balance, - /// The rent allowance. - /// - /// This is not only valid for contract accounts per se. - pub rent_allowance: Balance, - /// The kind of the account and associated data. - pub kind: AccountKind, -} - -impl Account { - /// Returns `true` if `self` is a user account. - pub fn is_user(&self) -> bool { - if let AccountKind::User(_) = &self.kind { - return true - } - false - } - - /// Returns the user account if `self` is a user account and otherwise return `None`. - pub fn user(&self) -> Option<&UserAccount> { - if let AccountKind::User(user_account) = &self.kind { - return Some(user_account) - } - None - } - - /// Returns the user account if `self` is a user account and otherwise return `None`. - pub fn user_mut(&mut self) -> Option<&mut UserAccount> { - if let AccountKind::User(user_account) = &mut self.kind { - return Some(user_account) - } - None - } - - /// Returns `true` if `self` is a contract account. - pub fn is_contract(&self) -> bool { - if let AccountKind::Contract(_) = &self.kind { - return true - } - false - } - - /// Returns the user account if `self` is a user account and otherwise return `None`. - pub fn contract(&self) -> Option<&ContractAccount> { - if let AccountKind::Contract(contract_account) = &self.kind { - return Some(contract_account) - } - None - } - - /// Returns the user account if `self` is a user account and otherwise return `None`. - pub fn contract_mut(&mut self) -> Option<&mut ContractAccount> { - if let AccountKind::Contract(contract_account) = &mut self.kind { - return Some(contract_account) - } - None - } -} - -/// The kind of an account. -/// -/// An account can be either a user account or a contract account. -#[derive(Debug, Clone)] -pub enum AccountKind { - /// A user account. - User(UserAccount), - /// A contract account. - Contract(ContractAccount), -} - -/// Specific state of user accounts. -#[derive(Debug, Clone)] -pub struct UserAccount { - /// The users display name. - pub display_name: String, -} - -impl UserAccount { - /// Creates a new user account. - pub fn new(name: S) -> Self - where - S: Into, - { - Self { - display_name: name.into(), - } - } -} - -/// Specific state of contract accounts. -#[derive(Debug, Clone)] -pub struct ContractAccount { - /// The associated code hash. - pub code_hash: Hash, - /// The contract's unique storage. - pub storage: Storage, - /// The minimum balance allowed for the contract. - pub minimum_balance: Balance, -} - -impl ContractAccount { - /// Creates a new contract account. - pub fn new(code_hash: Hash) -> Self { - Self { - code_hash, - storage: Storage::default(), - minimum_balance: TypedEncoded::from_origin(&0), - } - } -} diff --git a/core/src/env2/test/instance.rs b/core/src/env2/test/instance.rs deleted file mode 100644 index 9679edbb18b..00000000000 --- a/core/src/env2/test/instance.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2018-2019 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::{ - byte_utils, - env2::{ - call::{ - CallData, - Selector, - }, - test::{ - storage::Storage, - types::*, - AccountsDb, - AlreadyInitialized, - Record, - TypedEncoded, - }, - EnvTypes, - }, -}; - -/// The instance of the test environment. -/// -/// # Single Instance -/// -/// This is basically the database of the actual test environment. -/// We need exactly one single instance of this type which the actual -/// `TestEnv` is going to access through `thread_local` storage. -/// Since `thread_local` storage doesn't allow for generics `TestEnvInstance` -/// needs to be `EnvTypes` agnostic. -/// -/// # Type Safety -/// -/// To counter the lost type safety of being `EnvTypes` agnostic -/// `TestEnvInstance` uses the `TypedEncoded` abstraction where possible -/// since it provides a small type-safe runtime-checked wrapper -/// arround the state. -/// -/// # Default -/// -/// The `thread_local` storage is using the `Default` implementation -/// of `TestEnvInstance` in order to initialize it thread locally. -/// However, since we are using `TypedEncoded` we need a separate initialization -/// routine to actually initialize those for their guarantees around type safe accesses. -/// To initialize `TestEnvInstance` type-safely `TestEnv` is using its `initialize_using` -/// routine which has certain constraints to the actual environmental types. -#[derive(Debug, Default)] -pub struct TestEnvInstance { - /// The accounts registered on the chain. - pub accounts: AccountsDb, - /// The emulated chain state. - pub state: ChainState, - /// The current and latest block. - pub block: Block, - /// The current contract execution context. - pub exec_context: ExecutionContext, - /// Account ID generator. - pub account_id_gen: AccountIdGen, - /// Records of certain events and environmental interactions. - pub records: Vec, -} - -impl TestEnvInstance { - /// 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) -> Result<(), AlreadyInitialized> - where - E: EnvTypes, - ::AccountId: From<[u8; 32]>, - ::Balance: From, - ::BlockNumber: From, - ::Moment: From, - { - self.state.try_initialize::( - ::Balance::from(1), - ::Balance::from(0), - )?; - self.block.try_initialize::( - ::BlockNumber::from(0), - ::Moment::from(0), - )?; - self.exec_context.try_initialize::( - ::AccountId::from([0x00; 32]), - ::AccountId::from([0x01; 32]), - ::Balance::from(1000), - ::Balance::from(500_000), - None, - )?; - Ok(()) - } -} - -/// The emulated chain state. -/// -/// This stores general information about the chain. -#[derive(Debug, Clone, Default)] -pub struct ChainState { - /// The emulated chain storage. - pub storage: Storage, - /// The current gas price. - pub gas_price: Balance, - /// The existential deposit. - pub minimum_balance: Balance, -} - -impl ChainState { - /// 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, - gas_price: ::Balance, - minimum_balance: ::Balance, - ) -> Result<(), AlreadyInitialized> - where - E: EnvTypes, - ::Balance: From, - { - self.gas_price = TypedEncoded::from_origin(&gas_price); - self.minimum_balance = TypedEncoded::from_origin(&minimum_balance); - Ok(()) - } -} - -/// A block within the emulated chain. -/// -/// This stores information associated to blocks. -#[derive(Debug, Clone, Default)] -pub struct Block { - /// The number of the block. - pub number: BlockNumber, - /// The blocktime in milliseconds. - pub now_in_ms: Moment, -} - -impl Block { - /// 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, - number: ::BlockNumber, - now_in_ms: ::Moment, - ) -> Result<(), AlreadyInitialized> - where - E: EnvTypes, - { - self.number = TypedEncoded::from_origin(&number); - self.now_in_ms = TypedEncoded::from_origin(&now_in_ms); - Ok(()) - } -} - -/// An execution context is opened whenever a contract is being called or instantiated. -#[derive(Debug, Clone)] -pub struct ExecutionContext { - /// The caller of the execution. - pub caller: AccountId, - /// The address of the called contract. - pub callee: AccountId, - /// The endowment for the call. - pub transferred_balance: Balance, - /// The amount of gas left for further execution. - pub gas_left: Balance, - /// The limit of gas usage. - /// - /// There might be no limit thus `gas_left` is the actual limit then. - pub gas_limit: Option, - /// The raw call data for the contract execution. - pub call_data: CallData, - /// The output of the contract if any. - /// - /// Since this can be an arbitrary type we need to store it - /// as its most general form: raw bytes. - pub output: Option>, -} - -impl ExecutionContext { - /// 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, - caller: ::AccountId, - callee: ::AccountId, - transferred_balance: ::Balance, - gas_left: ::Balance, - gas_limit: Option<::Balance>, - ) -> Result<(), AlreadyInitialized> - where - E: EnvTypes, - { - self.caller = TypedEncoded::from_origin(&caller); - self.callee = TypedEncoded::from_origin(&callee); - self.transferred_balance = TypedEncoded::from_origin(&transferred_balance); - self.gas_left = TypedEncoded::from_origin(&gas_left); - self.gas_limit = gas_limit.map(|gas_limit| TypedEncoded::from_origin(&gas_limit)); - Ok(()) - } -} - -impl Default for ExecutionContext { - fn default() -> Self { - Self { - caller: Default::default(), - callee: Default::default(), - transferred_balance: Default::default(), - gas_left: Default::default(), - gas_limit: Default::default(), - call_data: CallData::new(Selector::from([0x00; 4])), - output: None, - } - } -} - -/// Allocates new account IDs. -/// -/// This is used whenever a new account or contract -/// is created on the emulated chain. -#[derive(Debug, Clone, Default)] -pub struct AccountIdGen { - /// The current account ID. - current: [u8; 32], -} - -impl AccountIdGen { - /// Returns the next account ID. - pub fn next(&mut self) -> (AccountId, ::AccountId) - where - E: EnvTypes, - ::AccountId: From<[u8; 32]>, - { - byte_utils::bytes_add_bytes(&mut self.current, &[0x01]); - let account_id: ::AccountId = self.current.into(); - let typed_encoded = TypedEncoded::from_origin(&account_id); - (typed_encoded, account_id) - } -} diff --git a/core/src/env2/test/record.rs b/core/src/env2/test/record.rs deleted file mode 100644 index 61d0dec82e3..00000000000 --- a/core/src/env2/test/record.rs +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2018-2019 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. - -//! Records are log entries for important events that were happening -//! on the emulated block chain. -//! -//! Records are a critical component for the emulated test environment -//! since certain operations are not possible to be emulated in its -//! current implementation, e.g. contract execution or proper -//! runtime on-chain behaviour since it's off-chain. -//! -//! For this records are stored instead of performing certain operations -//! that the user can query after or during the emulated contract execution. - -use derive_more::From; - -use crate::{ - env2::{ - call::{ - CallData, - CallParams, - CreateParams, - }, - test::{ - types::*, - TypedEncoded, - }, - EnvTypes, - Topics, - }, - storage::Key, -}; -use ink_prelude::vec::Vec; - -/// A record of an event happening on the off-chain test environment. -/// -/// This is useful for inspection of a contract execution. -#[derive(Debug, From)] -pub enum Record { - /// Calls (invoke or evaluate) of contracts. - Call(CallContractRecord), - /// Instantiations of a contracts. - Create(CreateContractRecord), - /// Emitted events. - EmitEvent(EmitEventRecord), - /// Invokation of the runtime. - InvokeRuntime(InvokeRuntimeRecord), - /// Restoration of a contract. - RestoreContract(RestoreContractRecord), -} - -impl Record { - /// Returns the contract call record if `self` is one and otherwise `None`. - pub fn contract_call(&self) -> Option<&CallContractRecord> { - match self { - Record::Call(call_record) => Some(call_record), - _ => None, - } - } - - /// Returns the contract instantiation record if `self` is one and otherwise `None`. - pub fn contract_instantiation(&self) -> Option<&CreateContractRecord> { - match self { - Record::Create(create_record) => Some(create_record), - _ => None, - } - } - - /// Returns the emitted event record if `self` is one and otherwise `None`. - pub fn emitted_event(&self) -> Option<&EmitEventRecord> { - match self { - Record::EmitEvent(emitted_event) => Some(emitted_event), - _ => None, - } - } - - /// Returns the runtime invokation record if `self` is one and otherwise `None`. - pub fn runtime_invokation(&self) -> Option<&InvokeRuntimeRecord> { - match self { - Record::InvokeRuntime(runtime_invokation) => Some(runtime_invokation), - _ => None, - } - } -} - -/// A contract call record. -/// -/// # Note -/// -/// This can be either an invokation (no return value) or an -/// evaluation (with return value) of a contract call. -#[derive(Debug)] -pub struct CallContractRecord { - /// Recorded code hash for the created contract. - pub callee: AccountId, - /// Recorded gas limit for the contract creation. - pub gas_limit: u64, - /// Recorded endowment. - pub endowment: Balance, - /// Recorded input data for contract creation. - pub input_data: CallData, -} - -impl CallContractRecord { - /// Creates a new record for a contract call. - pub fn new<'a, E, R>(call_params: &'a CallParams) -> Self - where - E: EnvTypes, - { - Self { - callee: TypedEncoded::from_origin(call_params.callee()), - gas_limit: call_params.gas_limit(), - endowment: TypedEncoded::from_origin(call_params.endowment()), - input_data: call_params.input_data().clone(), - } - } -} - -/// A contract instantitation record. -#[derive(Debug)] -pub struct CreateContractRecord { - /// Recorded code hash for the created contract. - pub code_hash: Hash, - /// Recorded gas limit for the contract creation. - pub gas_limit: u64, - /// Recorded endowment. - pub endowment: Balance, - /// Recorded input data for contract creation. - pub input_data: CallData, -} - -impl CreateContractRecord { - /// Creates a new record for a contract instantiation. - pub fn new<'a, E, C>(create_params: &'a CreateParams) -> Self - where - E: EnvTypes, - { - Self { - code_hash: TypedEncoded::from_origin(create_params.code_hash()), - gas_limit: create_params.gas_limit(), - endowment: TypedEncoded::from_origin(create_params.endowment()), - input_data: create_params.input_data().clone(), - } - } -} - -/// Record for an emitted event. -#[derive(Debug)] -pub struct EmitEventRecord { - /// Recorded topics of the emitted event. - pub topics: Vec, - /// Recorded encoding of the emitted event. - pub data: Vec, -} - -impl EmitEventRecord { - /// Creates a new record for an emitted event. - pub fn new(event: Event) -> Self - where - Env: EnvTypes, - Event: Topics + scale::Encode, - { - Self { - topics: event - .topics() - .iter() - .map(|topic| TypedEncoded::from_origin(topic)) - .collect::>(), - data: event.encode(), - } - } -} - -/// Record of a runtime invokation. -#[derive(Debug)] -pub struct InvokeRuntimeRecord { - /// Since we have to be agnostic over runtimes we cannot - /// be more precise here than use the completely generic - /// encoded raw bytes of the runtime call. - pub encoded: Vec, -} - -impl InvokeRuntimeRecord { - /// Creates a new record for a runtime invokation. - pub fn new(data: V) -> Self - where - V: Into>, - { - Self { - encoded: data.into(), - } - } -} - -/// Record of a contract restoration. -#[derive(Debug)] -pub struct RestoreContractRecord { - /// The destination account ID. - pub dest: AccountId, - /// The original code hash of the contract. - pub code_hash: Hash, - /// The initial rent allowance for the restored contract. - pub rent_allowance: Balance, - /// The filtered keys for the restoration process. - pub filtered_keys: Vec, -} - -impl RestoreContractRecord { - /// Creates a new record for a contract restoration. - pub fn new( - dest: &::AccountId, - code_hash: &::Hash, - rent_allowance: &::Balance, - filtered_keys: &[Key], - ) -> Self - where - E: EnvTypes, - { - Self { - dest: TypedEncoded::from_origin(dest), - code_hash: TypedEncoded::from_origin(code_hash), - rent_allowance: TypedEncoded::from_origin(rent_allowance), - filtered_keys: filtered_keys.to_vec(), - } - } -} diff --git a/core/src/env2/test/storage.rs b/core/src/env2/test/storage.rs deleted file mode 100644 index b8a8c4464f6..00000000000 --- a/core/src/env2/test/storage.rs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2018-2019 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. - -//! The storage emulates the chain's storage as well as the unique -//! storage of the executed contract. -//! -//! It is a map from `Key` (32-bytes) to a generic `Vec`. -//! This is pretty close to the actual on-chain storage in Substrate. - -use core::cell::Cell; - -use crate::storage::Key; -use ink_prelude::collections::btree_map::{ - self, - BTreeMap, -}; - -/// An entry in the storage of the test environment. -/// -/// # Note -/// -/// Additionally to its data it also stores the total -/// number of reads and writes done to this entry. -#[derive(Debug, Clone)] -pub struct Entry { - /// The actual data that is stored in this storage entry. - data: Vec, - /// The number of reads to this storage entry. - reads: Cell, - /// The number of writes to this storage entry. - writes: usize, -} - -impl Entry { - /// Creates a new storage entry for the given data. - pub fn new(data: V) -> Self - where - V: Into>, - { - Self { - data: data.into(), - reads: Cell::new(0), - writes: 0, - } - } - - /// Increases the read counter by one. - fn inc_reads(&self) { - self.reads.set(self.reads.get() + 1); - } - - /// Increases the write counter by one. - fn inc_writes(&mut self) { - self.writes += 1; - } - - /// Returns the number of reads for this storage entry. - pub fn reads(&self) -> usize { - self.reads.get() - } - - /// Returns the number of writes to this storage entry. - pub fn writes(&self) -> usize { - self.writes - } - - /// Returns the data stored in this storage entry. - /// - /// # Note - /// - /// Also bumps the read counter. - pub fn data(&self) -> &[u8] { - self.inc_reads(); - &self.data - } - - /// Overwrites the data of this entry. - /// - /// # Note - /// - /// Also bumps the write counter. - pub fn overwrite(&mut self, new_data: V) - where - V: Into>, - { - self.inc_writes(); - self.data = new_data.into(); - } -} - -/// The storage of a smart contract. -/// -/// This is a mapping from `Key` to entries -/// that just store the raw byte representation -/// of whatever they are storing. -/// -/// Also every entry and the storage itself are -/// storing counts of read and write accesses for -/// further introspection of storage access. -#[derive(Debug, Clone)] -pub struct Storage { - /// All storage entries. - entries: BTreeMap, - /// The total number of reads from the storage. - total_reads: Cell, - /// The total number of writes to the storage. - total_writes: usize, -} - -impl Default for Storage { - fn default() -> Self { - Self { - entries: BTreeMap::new(), - total_reads: Cell::new(0), - total_writes: 0, - } - } -} - -impl Storage { - /// Writes the given value to the storage under the key. - /// - /// # Note - /// - /// Overwrites if there was already a value stored under the key. - pub fn write(&mut self, key: Key, value: V) - where - V: Into>, - { - self.inc_total_writes(); - match self.entries.entry(key) { - btree_map::Entry::Occupied(mut occupied) => { - occupied.get_mut().overwrite(value); - } - btree_map::Entry::Vacant(vacant) => { - vacant.insert(Entry::new(value)); - } - } - } - - /// Clears the entry under the given key. - pub fn clear(&mut self, key: Key) { - self.inc_total_writes(); - self.entries.remove(&key); - } - - /// Reads the data from the storage. - /// - /// # Note - /// - /// This lso resets the read & write counters for - /// the same entry in the current implementation. - pub fn read(&self, key: Key) -> Option<&Entry> { - self.inc_total_reads(); - self.entries.get(&key) - } - - /// Increases the total reads counter by one. - fn inc_total_reads(&self) { - self.total_reads.set(self.total_reads.get() + 1); - } - - /// Increases the total writes counter by one. - fn inc_total_writes(&mut self) { - self.total_writes += 1; - } - - /// Returns the number of reads for this storage entry. - pub fn reads(&self) -> usize { - self.total_reads.get() - } - - /// Returns the number of writes to this storage entry. - pub fn writes(&self) -> usize { - self.total_writes - } -} diff --git a/core/src/env2/test/typed_encoded.rs b/core/src/env2/test/typed_encoded.rs deleted file mode 100644 index e7fdfa4ef53..00000000000 --- a/core/src/env2/test/typed_encoded.rs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2018-2019 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 core::{ - any::TypeId, - cmp::Ordering, - hash::{ - Hash, - Hasher, - }, - marker::PhantomData, -}; - -/// A wrapper around an encoded entity that only allows type safe accesses. -/// -/// # Note -/// -/// Checks are implemented at runtime. -#[derive(Debug)] -pub struct TypedEncoded { - /// The bytes of the encoded representation of the type. - encoded: Vec, - /// The unique identifier of the encoded type. - type_id: TypeId, - /// Classification marker. - /// - /// # Note - /// - /// - This shouldn't 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 `TestEnvInstance` might use one abstract marker for every - /// of the fundamental SRML 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 M>, -} - -impl PartialEq for TypedEncoded { - fn eq(&self, other: &Self) -> bool { - if self.type_id == other.type_id - && self.encoded.as_slice() == other.encoded.as_slice() - { - return true - } - false - } -} - -impl Eq for TypedEncoded {} - -impl PartialOrd for TypedEncoded { - fn partial_cmp(&self, other: &Self) -> Option { - if self.type_id != other.type_id { - return None - } - self.as_bytes().partial_cmp(other.as_bytes()) - } -} - -impl Ord for TypedEncoded { - fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(other) - .expect("expect to have same `type_id`") - } -} - -impl Clone for TypedEncoded { - fn clone(&self) -> Self { - Self { - encoded: self.encoded.clone(), - type_id: self.type_id, - marker: Default::default(), - } - } -} - -impl Hash for TypedEncoded { - fn hash(&self, state: &mut H) - where - H: Hasher, - { - self.encoded.hash(state); - self.type_id.hash(state); - } -} - -/// Marker that indicates untypedness for an instance of `TypedEncoded`. -/// -/// # Note -/// -/// - We abuse this to initialize instances of `TypedEncoded` where concrete -/// types for them are not yet known. -/// - This allows for a special case where even instances with differing marker -/// types can interoperate as long as at least one of them has `Uninitialized` as marker, -/// although this is type unsafe since we cannot guarantee that the encoding matches -/// the type. -enum Uninitialized {} - -impl Default for TypedEncoded { - /// Creates an uninitialized instance. - /// - /// # Note - /// - /// This instance can be initialized with a proper value at a later point - /// using a call to `TypedEncoded::try_initialize`. - fn default() -> Self { - Self { - encoded: Vec::new(), - type_id: TypeId::of::(), - marker: Default::default(), - } - } -} - -/// Encountered when trying to initialize an already initialized `TypedEncoded`. -#[derive(Debug, PartialEq, Eq)] -pub struct AlreadyInitialized; - -impl TypedEncoded { - /// 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: &V) -> Result<(), AlreadyInitialized> - where - V: scale::Encode + 'static, - { - if self.type_id != TypeId::of::() { - return Err(AlreadyInitialized) - } - self.encoded = value.encode(); - self.type_id = TypeId::of::(); - Ok(()) - } -} - -impl TypedEncoded { - /// Returns the encoded bytes. - fn as_bytes(&self) -> &[u8] { - &self.encoded - } -} - -/// Encountered when trying to decode a `TypedEncoded` as an invalid type. -#[derive(Debug, PartialEq, Eq)] -pub struct UnmatchingType; - -impl TypedEncoded { - /// Converts back into the original typed origin. - /// - /// # Errors - /// - /// If the given type doesn't match the origin's real type. - pub fn try_to_origin(&self) -> Result - where - T: scale::Decode + 'static, - { - if self.type_id != TypeId::of::() { - return Err(UnmatchingType) - } - let decoded = T::decode(&mut self.as_bytes()) - .expect("we should have received an instance of this by encoding; qed"); - Ok(decoded) - } - - /// Converts back into the original typed origin. - /// - /// # Panics - /// - /// If the given type doesn't match the origin's real type. - pub fn to_origin(&self) -> T - where - T: scale::Decode + 'static, - { - self.try_to_origin() - .expect("encountered invalid origin type") - } -} - -impl TypedEncoded { - /// Converts the original typed entity into its encoded representation. - pub fn from_origin(value: &T) -> Self - where - T: scale::Encode + 'static, - { - Self { - encoded: value.encode(), - type_id: TypeId::of::(), - marker: Default::default(), - } - } - - /// Tries to assign a new value to `self`. - /// - /// # Errors - /// - /// If the types of the current and new value do not match. - pub fn try_assign(&mut self, new_value: &T) -> Result<(), UnmatchingType> - where - T: scale::Encode + 'static, - { - if self.type_id != TypeId::of::() { - return Err(UnmatchingType) - } - self.encoded.clear(); - new_value.encode_to(&mut self.encoded); - Ok(()) - } - - /// Assigns a new value to `self`. - /// - /// # Panics - /// - /// If the types of the current and new value do not match. - pub fn assign(&mut self, new_value: &T) - where - T: scale::Encode + 'static, - { - self.try_assign(new_value) - .expect("encountered invalid assignment type") - } -} diff --git a/core/src/env2/traits.rs b/core/src/env2/traits.rs deleted file mode 100644 index a737b957b67..00000000000 --- a/core/src/env2/traits.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2018-2019 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 scale::Codec; - -use crate::{ - env2::{ - call::{ - CallParams, - CreateParams, - ReturnType, - }, - property, - utils::{ - EnlargeTo, - Reset, - }, - Result, - }, - storage::Key, -}; - -/// The environmental types usable by contracts defined with ink!. -pub trait EnvTypes { - /// The type of an address. - type AccountId: 'static + Codec + Clone + PartialEq + Eq; - /// The type of balances. - type Balance: 'static + Codec + Clone + PartialEq + Eq; - /// The type of hash. - type Hash: 'static + Codec + Clone + PartialEq + Eq; - /// The type of timestamps. - type Moment: 'static + Codec + Clone + PartialEq + Eq; - /// The type of block number. - type BlockNumber: 'static + Codec + Clone + PartialEq + Eq; - /// The type of a call into the runtime - type Call: 'static + scale::Encode; -} - -/// Allows reading contract properties. -pub trait GetProperty

-where - P: property::ReadProperty, -{ - /// Gets the property. - /// - /// Uses `buffer` for intermediate computation. - fn get_property(buffer: &mut I) -> P::In - where - I: AsMut<[u8]> + EnlargeTo; -} - -/// Allows mutating contract properties. -pub trait SetProperty

-where - P: property::WriteProperty, -{ - /// Sets the property. - /// - /// Uses `buffer` for intermediate computation. - fn set_property(buffer: &mut O, encoded: &P::Out) - where - O: scale::Output + AsRef<[u8]> + Reset; -} - -/// The interface that ink! environments have to implement. -pub trait Env: - EnvTypes - + Sized - + GetProperty> - + GetProperty> - + GetProperty> - + GetProperty> - + GetProperty> - + GetProperty> - + GetProperty> - + GetProperty> - + SetProperty> - + GetProperty> - + GetProperty> - + GetProperty> -{ - /// Returns the value at the contract storage at the position of the key. - /// - /// # Errors - /// - /// - If `key` associates no elements. - /// - If the element at `key` could not be decoded into `T`. - fn get_contract_storage(buffer: &mut I, key: Key) -> Result - where - I: AsMut<[u8]> + EnlargeTo, - T: scale::Decode; - - /// Sets the value at the key to the given encoded value. - fn set_contract_storage(buffer: &mut O, key: Key, val: &T) - where - O: scale::Output + AsRef<[u8]> + Reset, - T: scale::Encode; - - /// Clears the value at the key position. - fn clear_contract_storage(key: Key); - - /// Invokes a contract call with the given call data. - /// - /// # Note - /// - /// Invocations fire and forget and thus won't return a value back. - fn invoke_contract(buffer: &mut O, call_data: &CallParams) -> Result<()> - where - O: scale::Output + AsRef<[u8]> + Reset; - - /// Evaluates a contract call with the given call data. - /// - /// # Note - /// - /// Evaluations return a return value back to the caller. - fn eval_contract( - buffer: &mut IO, - call_data: &CallParams>, - ) -> Result - where - IO: scale::Output + AsRef<[u8]> + AsMut<[u8]> + EnlargeTo + Reset, - R: scale::Decode; - - /// Instantiates a contract from the given create data and returns its account ID. - fn create_contract( - buffer: &mut IO, - create_data: &CreateParams, - ) -> Result - where - IO: scale::Output + AsRef<[u8]> + AsMut<[u8]> + EnlargeTo + Reset; - - /// Emits an event with the given event data. - fn emit_event(buffer: &mut O, event_data: Event) - where - O: scale::Output + AsRef<[u8]> + Reset, - Event: Topics + scale::Encode; - - /// Invokes a runtime dispatchable function with the given call data. - fn invoke_runtime(buffer: &mut O, call_data: &::Call) - where - O: scale::Output + AsRef<[u8]> + Reset; - - /// Restores the contract to the given address. - /// - /// The `filtered_keys` indicate keys to ignore for evaluating - /// the equality of the restoration storage. These keys might be required - /// by the restoring contract but not by the restored contract. - fn restore_to( - buffer: &mut O, - dest: Self::AccountId, - code_hash: Self::Hash, - rent_allowance: Self::Balance, - filtered_keys: &[Key], - ) where - O: scale::Output + AsRef<[u8]> + Reset; - - /// Returns the given value back to the caller of the executed contract. - fn output(buffer: &mut O, return_value: &R) - where - O: scale::Output + AsRef<[u8]> + Reset, - R: scale::Encode; - - /// Returns a random hash given the subject. - fn random(buffer: &mut I, subject: &[u8]) -> Self::Hash - where - I: AsMut<[u8]> + EnlargeTo; - - /// Prints the contents as a single line. - /// - /// # Note - /// - /// This is a pure debug utility and should not be used in production. - /// In fact production chains will generally reject contracts upon deploy - /// that make use of this functionality. - fn println(content: &str); - - /// Returns the value from the *runtime* storage at the position of the key. - /// - /// # Errors - /// - /// - If `key` associates no elements. - /// - If the element at `key` could not be decoded into `T`. - fn get_runtime_storage(buffer: &mut I, key: &[u8]) -> Result - where - I: AsMut<[u8]> + EnlargeTo, - T: scale::Decode; -} - -/// Implemented by event types to communicate their topic hashes. -pub trait Topics -where - E: EnvTypes, -{ - /// Returns the topic hashes of `self`. - fn topics(&self) -> &'static [::Hash]; -} diff --git a/core/src/env2/types.rs b/core/src/env2/types.rs deleted file mode 100644 index b21205e02ea..00000000000 --- a/core/src/env2/types.rs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2018-2019 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. - -//! Types for the default SRML environment. -//! -//! These are simple mirrored types from the default SRML configuration. -//! Their interfaces and functionality might not be complete. -//! -//! Users are required to provide their own type definitions and `EnvTypes` -//! implementations in order to write ink! contracts for other chain configurations. - -use core::{ - array::TryFromSliceError, - convert::TryFrom, -}; - -use derive_more::From; -use scale::{ - Decode, - Encode, -}; -#[cfg(feature = "ink-generate-abi")] -use type_metadata::Metadata; - -use crate::{ - env2::EnvTypes, - storage::Flush, -}; -use ink_prelude::vec::Vec; - -/// The fundamental types of the SRML default configuration. -#[cfg_attr(feature = "test-env", derive(Debug, Clone, PartialEq, Eq))] -#[cfg_attr(feature = "ink-generate-abi", derive(type_metadata::Metadata))] -pub enum DefaultSrmlTypes {} - -impl EnvTypes for DefaultSrmlTypes { - type AccountId = AccountId; - type Balance = Balance; - type Hash = Hash; - type Moment = Moment; - type BlockNumber = BlockNumber; - type Call = Call; -} - -/// The default SRML balance type. -pub type Balance = u128; - -/// The default SRML moment type. -pub type Moment = u64; - -/// The default SRML blocknumber type. -pub type BlockNumber = u64; - -/// Empty enum for default Call type, so it cannot be constructed. -/// For calling into the runtime, a user defined Call type required. -/// See https://github.com/paritytech/ink-types-node-runtime. -/// -/// # Note -/// -/// Some traits are only implemented to satisfy the constraints of the test -/// environment, in order to keep the code size small. - -/// This call type guarantees to never be constructed. -/// -/// This has the effect that users of the default SRML types are -/// not able to call back into the runtime. -/// This operation is generally unsupported because of the currently -/// implied additional overhead. -/// -/// # Note -/// -/// A user defined `Call` type is required for calling into the runtime. -/// For more info visit: https://github.com/paritytech/ink-types-node-runtime -#[cfg_attr(feature = "test-env", derive(Debug, Clone, PartialEq, Eq))] -pub enum Call {} - -/// The implementation enforces at runtime that `Encode` is not called -/// for the default SRML `Call` type but for performance reasons this check -/// is removed for the on-chain (release mode) version. -impl Encode for Call { - fn encode(&self) -> Vec { - debug_assert!(false, "cannot encode default SRML `Call` type"); - Vec::new() - } -} - -/// This implementation is only to satisfy the Decode constraint in the -/// test environment. Since Call cannot be constructed then just return -/// None, but this should never be called. -#[cfg(feature = "test-env")] -impl scale::Decode for Call { - fn decode(_value: &mut I) -> Result { - Err("The default SRML `Call` type cannot be used for runtime calls".into()) - } -} - -/// The default SRML `AccountId` type. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encode, Decode, From, Default)] -#[cfg_attr(feature = "ink-generate-abi", derive(Metadata))] -pub struct AccountId([u8; 32]); - -impl<'a> TryFrom<&'a [u8]> for AccountId { - type Error = TryFromSliceError; - - fn try_from(bytes: &'a [u8]) -> Result { - let address = <[u8; 32]>::try_from(bytes)?; - Ok(Self(address)) - } -} - -impl Flush for AccountId {} - -/// The default SRML `Hash` type. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encode, Decode, From, Default)] -#[cfg_attr(feature = "ink-generate-abi", derive(Metadata))] -pub struct Hash([u8; 32]); - -impl<'a> TryFrom<&'a [u8]> for Hash { - type Error = TryFromSliceError; - - fn try_from(bytes: &'a [u8]) -> Result { - let address = <[u8; 32]>::try_from(bytes)?; - Ok(Self(address)) - } -} - -impl Flush for Hash {} diff --git a/core/src/env2/utils.rs b/core/src/env2/utils.rs deleted file mode 100644 index c0d8ee0b4cd..00000000000 --- a/core/src/env2/utils.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2018-2019 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. - -//! Utility definitions used for environmental access. - -use ink_prelude::vec::Vec; -use smallvec::{ - Array, - SmallVec, -}; - -/// Buffers that allow to reset themselves. -/// -/// # Note -/// -/// Reset buffers are guaranteed to have a `len` of 0. -pub trait Reset { - /// Resets the buffer. - fn reset(&mut self); -} - -impl Reset for Vec { - fn reset(&mut self) { - self.clear() - } -} - -impl Reset for SmallVec -where - T: Array, -{ - fn reset(&mut self) { - self.clear() - } -} - -/// Buffers that allow to enlarge themselves to the specified minimum length. -pub trait EnlargeTo { - /// Enlarge `self` to fit at least `new_size` elements. - /// - /// # Note - /// - /// This should be implemented as a no-op if `self` is already big enough. - fn enlarge_to(&mut self, new_size: usize); -} - -impl EnlargeTo for Vec -where - T: Default + Clone, -{ - fn enlarge_to(&mut self, new_size: usize) { - if self.len() < new_size { - self.resize(new_size, Default::default()) - } - } -} - -impl EnlargeTo for SmallVec -where - T: Array, - ::Item: Default + Clone, -{ - fn enlarge_to(&mut self, new_size: usize) { - if self.len() < new_size { - self.resize(new_size, Default::default()) - } - } -} diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs new file mode 100644 index 00000000000..91df7498694 --- /dev/null +++ b/core/src/env3/api.rs @@ -0,0 +1,448 @@ +// Copyright 2019-2020 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. + +//! The public raw interface towards the host Wasm engine. + +use crate::{ + env3::{ + backend::{ + Env, + TypedEnv, + }, + call::{ + CallData, + CallParams, + InstantiateParams, + ReturnType, + }, + engine::{ + EnvInstance, + OnInstance, + }, + EnvTypes, + Result, + Topics, + }, + storage::Key, +}; + +/// Returns the address of the caller of the executed contract. +/// +/// # Errors +/// +/// If the returned caller cannot be properly decoded. +pub fn caller() -> Result +where + T: EnvTypes, +{ + ::on_instance(|instance| TypedEnv::caller::(instance)) +} + +/// Returns the transferred balance for the contract execution. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn transferred_balance() -> Result +where + T: EnvTypes, +{ + ::on_instance(|instance| { + TypedEnv::transferred_balance::(instance) + }) +} + +/// Returns the current price for gas. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn gas_price() -> Result +where + T: EnvTypes, +{ + ::on_instance(|instance| { + TypedEnv::gas_price::(instance) + }) +} + +/// Returns the amount of gas left for the contract execution. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn gas_left() -> Result +where + T: EnvTypes, +{ + ::on_instance(|instance| TypedEnv::gas_left::(instance)) +} + +/// Returns the current block time in milliseconds. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn block_timestamp() -> Result +where + T: EnvTypes, +{ + ::on_instance(|instance| { + TypedEnv::block_timestamp::(instance) + }) +} + +/// Returns the address of the executed contract. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn address() -> Result +where + T: EnvTypes, +{ + ::on_instance(|instance| TypedEnv::address::(instance)) +} + +/// Returns the balance of the executed contract. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn balance() -> Result +where + T: EnvTypes, +{ + ::on_instance(|instance| TypedEnv::balance::(instance)) +} + +/// Returns the current rent allowance for the executed contract. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn rent_allowance() -> Result +where + T: EnvTypes, +{ + ::on_instance(|instance| { + TypedEnv::rent_allowance::(instance) + }) +} + +/// Returns the current block number. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn block_number() -> Result +where + T: EnvTypes, +{ + ::on_instance(|instance| { + TypedEnv::block_number::(instance) + }) +} + +/// Returns the minimum balance for the contracts chain. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn minimum_balance() -> Result +where + T: EnvTypes, +{ + ::on_instance(|instance| { + TypedEnv::minimum_balance::(instance) + }) +} + +/// Returns the tombstone deposit for the contracts chain. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn tombstone_deposit() -> Result +where + T: EnvTypes, +{ + ::on_instance(|instance| { + TypedEnv::tombstone_deposit::(instance) + }) +} + +/// Emits an event with the given event data. +pub fn emit_event(event: Event) +where + T: EnvTypes, + Event: Topics + scale::Encode, +{ + ::on_instance(|instance| { + TypedEnv::emit_event::(instance, event) + }) +} + +/// Sets the rent allowance of the executed contract to the new value. +pub fn set_rent_allowance(new_value: T::Balance) +where + T: EnvTypes, +{ + ::on_instance(|instance| { + TypedEnv::set_rent_allowance::(instance, new_value) + }) +} + +/// Writes the value to the contract storage under the given key. +pub fn set_contract_storage(key: Key, value: &V) +where + V: scale::Encode, +{ + ::on_instance(|instance| { + Env::set_contract_storage::(instance, key, value) + }) +} + +/// Returns the value stored under the given key in the contract's storage. +/// +/// # Errors +/// +/// - If the key's entry is empty +/// - If the decoding of the typed value failed +pub fn get_contract_storage(key: Key) -> Result +where + R: scale::Decode, +{ + ::on_instance(|instance| { + Env::get_contract_storage::(instance, key) + }) +} + +/// Clears the contract's storage key entry. +pub fn clear_contract_storage(key: Key) { + ::on_instance(|instance| { + Env::clear_contract_storage(instance, key) + }) +} + +/// Invokes a call to the runtime. +/// +/// # Note +/// +/// The call is not guaranteed to execute immediately but might be deferred +/// to the end of the contract execution. +/// +/// # Errors +/// +/// - If the called runtime function does not exist. +pub fn invoke_runtime(params: &T::Call) -> Result<()> +where + T: EnvTypes, +{ + ::on_instance(|instance| { + TypedEnv::invoke_runtime::(instance, params) + }) +} + +/// Invokes a contract message. +/// +/// # Errors +/// +/// - If the called contract does not exist. +/// - If the called contract is a tombstone. +/// - If arguments passed to the called contract message are invalid. +/// - If the called contract execution has trapped. +/// - If the called contract ran out of gas upon execution. +pub fn invoke_contract(params: &CallParams) -> Result<()> +where + T: EnvTypes, +{ + ::on_instance(|instance| { + TypedEnv::invoke_contract::(instance, params) + }) +} + +/// Evaluates a contract message and returns its result. +/// +/// # Errors +/// +/// - If the called contract does not exist. +/// - If the called contract is a tombstone. +/// - If arguments passed to the called contract message are invalid. +/// - If the called contract execution has trapped. +/// - If the called contract ran out of gas upon execution. +/// - If the returned value failed to decode properly. +pub fn eval_contract(params: &CallParams>) -> Result +where + T: EnvTypes, + R: scale::Decode, +{ + ::on_instance(|instance| { + TypedEnv::eval_contract::(instance, params) + }) +} + +/// Instantiates another contract. +/// +/// # Errors +/// +/// - If the code hash is invalid. +/// - If the arguments passed to the instantiation process are invalid. +/// - If the instantiation process traps. +/// - If the instantiation process runs out of gas. +/// - If given too few endowment. +/// - If the returned account ID failed to decode properly. +pub fn create_contract(params: &InstantiateParams) -> Result +where + T: EnvTypes, +{ + ::on_instance(|instance| { + TypedEnv::create_contract::(instance, params) + }) +} + +/// Restores a smart contract in tombstone state. +/// +/// # Params +/// +/// - `account_id`: Account ID of the to-be-restored contract. +/// - `code_hash`: Code hash of the to-be-restored contract. +/// - `rent_allowance`: Rent allowance of the restored contract +/// upon successful restoration. +/// - `filtered_keys`: Storage keys that will be removed for the tombstone hash +/// match calculation that decide whether the original contract +/// storage and the storage of the restorer contract are equal. +/// +/// # Usage +/// +/// A smart contract that has too few funds to pay for its storage fees +/// can eventually be evicted. An evicted smart contract `C` leaves behind +/// a tombstone associated with a hash that has been computed partially +/// by its storage contents. +/// +/// To restore contract `C` back to a fully working contract the normal +/// process is to write another contract `C2` with the only purpose to +/// eventually have the absolutely same contract storage as `C` did when +/// it was evicted. +/// For that purpose `C2` can use other storage keys that have not been in +/// use by contract `C`. +/// Once `C2` contract storage matches the storage of `C` when it was evicted +/// `C2` can invoke this method in order to initiate restoration of `C`. +/// A tombstone hash is calculated for `C2` and if it matches the tombstone +/// hash of `C` the restoration is going to be successful. +/// The `filtered_keys` argument can be used to ignore the extraneous keys +/// used by `C2` but not used by `C`. +/// +/// The process of such a smart contract restoration can generally be very expensive. +/// +/// # Note +/// +/// - The `filtered_keys` can be used to ignore certain storage regions +/// in the restorer contract to not influence the hash calculations. +/// - Does *not* perform restoration right away but defers it to the end of +/// the contract execution. +/// - Restoration is cancelled if there is no tombstone in the destination +/// address or if the hashes don't match. No changes are made in this case. +pub fn restore_contract( + account_id: T::AccountId, + code_hash: T::Hash, + rent_allowance: T::Balance, + filtered_keys: &[Key], +) where + T: EnvTypes, +{ + ::on_instance(|instance| { + TypedEnv::restore_contract::( + instance, + account_id, + code_hash, + rent_allowance, + filtered_keys, + ) + }) +} + +/// Returns the input to the executed contract. +/// +/// # Note +/// +/// - The input is the 4-bytes selector followed by the arguments +/// of the called function in their SCALE encoded representation. +/// - This property must be received as the first action an executed +/// contract to its environment and can only be queried once. +/// The environment access asserts this guarantee. +/// +/// # Errors +/// +/// - If the call to `input` is not the first call to the environment. +/// - If the input failed to decode into call data. +/// - This happens only if the host runtime provides less than 4 bytes for +/// the function selector upon this query. +pub fn input() -> Result { + ::on_instance(|instance| Env::input(instance)) +} + +/// Returns the value back to the caller of the executed contract. +/// +/// # Note +/// +/// This call must be the last call to the contract +/// environment for every contract execution. +pub fn output(return_value: &R) +where + R: scale::Encode, +{ + ::on_instance(|instance| { + Env::output::(instance, return_value) + }) +} + +/// Returns a random hash. +/// +/// # Note +/// +/// - The subject buffer can be used to further randomize the hash. +/// - Within the same execution returns the same random hash for the same subject. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn random(subject: &[u8]) -> Result +where + T: EnvTypes, +{ + ::on_instance(|instance| { + TypedEnv::random::(instance, subject) + }) +} + +/// Prints the given contents to the environmental log. +pub fn println(content: &str) { + ::on_instance(|instance| Env::println(instance, content)) +} + +/// Returns the value from the *runtime* storage at the position of the key if any. +/// +/// # Errors +/// +/// - If the key's entry is empty +/// - If the decoding of the typed value failed +pub fn get_runtime_storage(runtime_key: &[u8]) -> Option> +where + R: scale::Decode, +{ + ::on_instance(|instance| { + Env::get_runtime_storage::(instance, runtime_key) + }) +} diff --git a/core/src/env3/backend.rs b/core/src/env3/backend.rs new file mode 100644 index 00000000000..c9fd77a4940 --- /dev/null +++ b/core/src/env3/backend.rs @@ -0,0 +1,237 @@ +// Copyright 2019-2020 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::{ + env3::{ + call::{ + CallData, + CallParams, + InstantiateParams, + ReturnType, + }, + EnvTypes, + Result, + Topics, + }, + storage::Key, +}; + +/// Environmental contract functionality that does not require `EnvTypes`. +pub trait Env { + /// Writes the value to the contract storage under the given key. + fn set_contract_storage(&mut self, key: Key, value: &V) + where + V: scale::Encode; + + /// Returns the value stored under the given key in the contract's storage. + /// + /// # Errors + /// + /// - If the key's entry is empty + /// - If the decoding of the typed value failed + fn get_contract_storage(&mut self, key: Key) -> Result + where + R: scale::Decode; + + /// Clears the contract's storage key entry. + fn clear_contract_storage(&mut self, key: Key); + + /// Returns the value from the *runtime* storage at the position of the key. + /// + /// # Errors + /// + /// - If the key's entry is empty + /// - If the decoding of the typed value failed + fn get_runtime_storage(&mut self, runtime_key: &[u8]) -> Option> + where + R: scale::Decode; + + /// Returns the input to the executed contract. + /// + /// # Note + /// + /// - The input is the 4-bytes selector followed by the arguments + /// of the called function in their SCALE encoded representation. + /// - This property must be received as the first action an executed + /// contract to its environment and can only be queried once. + /// The environment access asserts this guarantee. + fn input(&mut self) -> Result; + + /// Returns the value back to the caller of the executed contract. + /// + /// # Note + /// + /// The setting of this property must be the last interaction between + /// the executed contract and its environment. + /// The environment access asserts this guarantee. + fn output(&mut self, return_value: &R) + where + R: scale::Encode; + + /// Prints the given contents to the console log. + fn println(&mut self, content: &str); +} + +/// Environmental contract functionality. +pub trait TypedEnv: Env { + /// Returns the address of the caller of the executed contract. + fn caller(&mut self) -> Result; + + /// Returns the transferred balance for the contract execution. + fn transferred_balance(&mut self) -> Result; + + /// Returns the current price for gas. + fn gas_price(&mut self) -> Result; + + /// Returns the amount of gas left for the contract execution. + fn gas_left(&mut self) -> Result; + + /// Returns the timestamp of the current block. + fn block_timestamp(&mut self) -> Result; + + /// Returns the address of the executed contract. + fn address(&mut self) -> Result; + + /// Returns the balance of the executed contract. + fn balance(&mut self) -> Result; + + /// Returns the current rent allowance for the executed contract. + fn rent_allowance(&mut self) -> Result; + + /// Returns the current block number. + fn block_number(&mut self) -> Result; + + /// Returns the minimum balance of the contracts chain. + fn minimum_balance(&mut self) -> Result; + + /// Returns the tombstone deposit of the contract chain. + fn tombstone_deposit(&mut self) -> Result; + + /// Emits an event with the given event data. + fn emit_event(&mut self, event: Event) + where + T: EnvTypes, + Event: Topics + scale::Encode; + + /// Sets the rent allowance of the executed contract to the new value. + fn set_rent_allowance(&mut self, new_value: T::Balance) + where + T: EnvTypes; + + /// Invokes a call of the runtime. + fn invoke_runtime(&mut self, call: &T::Call) -> Result<()> + where + T: EnvTypes; + + /// Invokes a contract message. + /// + /// # Errors + /// + /// If the called contract has trapped. + fn invoke_contract(&mut self, call_data: &CallParams) -> Result<()> + where + T: EnvTypes; + + /// Evaluates a contract message and returns its result. + /// + /// # Errors + /// + /// - If the called contract traps. + /// - If the account ID is invalid. + /// - If given too few endowment. + /// - If arguments passed to the called contract are invalid. + /// - If the called contract runs out of gas. + fn eval_contract( + &mut self, + call_data: &CallParams>, + ) -> Result + where + T: EnvTypes, + R: scale::Decode; + + /// Instantiates another contract. + /// + /// # Errors + /// + /// - If the instantiation process traps. + /// - If the code hash is invalid. + /// - If given too few endowment. + /// - If the instantiation process runs out of gas. + fn create_contract( + &mut self, + params: &InstantiateParams, + ) -> Result + where + T: EnvTypes; + + /// Restores a smart contract tombstone. + /// + /// # Params + /// + /// - `account_id`: Encoded bytes of the `AccountId` of the to-be-restored contract. + /// - `code_hash`: Encoded code hash of the to-be-restored contract. + /// - `rent_allowance`: The encoded rent allowance of the restored contract + /// upon successful restoration. + /// - `filtered_keys`: Storage keys that will be ignored for the tombstone hash + /// match calculation that decide whether the original contract + /// storage and the storage of the restorer contract is equal. + /// + /// # Usage + /// + /// A smart contract that has too few funds to pay for its storage fees + /// can eventually be evicted. An evicted smart contract `C` leaves behind + /// a tombstone associated with a hash that has been computed partially + /// by its storage contents. + /// + /// To restore contract `C` back to a fully working contract the normal + /// process is to write another contract `C2` with the only purpose to + /// eventually have the absolutely same contract storage as `C` did when + /// it was evicted. + /// For that purpose `C2` can use other storage keys that have not been in + /// use by contract `C`. + /// Once `C2` contract storage matches the storage of `C` when it was evicted + /// `C2` can invoke this method in order to initiate restoration of `C`. + /// A tombstone hash is calculated for `C2` and if it matches the tombstone + /// hash of `C` the restoration is going to be successful. + /// The `filtered_keys` argument can be used to ignore the extraneous keys + /// used by `C2` but not used by `C`. + /// + /// The process of such a smart contract restoration can generally be very expensive. + /// + /// # Note + /// + /// - The `filtered_keys` can be used to ignore certain storage regions + /// in the restorer contract to not influence the hash calculations. + /// - Does *not* perform restoration right away but defers it to the end of + /// the contract execution. + /// - Restoration is cancelled if there is no tombstone in the destination + /// address or if the hashes don't match. No changes are made in this case. + fn restore_contract( + &mut self, + account_id: T::AccountId, + code_hash: T::Hash, + rent_allowance: T::Balance, + filtered_keys: &[Key], + ) where + T: EnvTypes; + + /// Returns a random hash. + /// + /// # Note + /// + /// The subject buffer can be used to further randomize the hash. + fn random(&mut self, subject: &[u8]) -> Result + where + T: EnvTypes; +} diff --git a/core/src/env2/call/builder.rs b/core/src/env3/call/builder.rs similarity index 69% rename from core/src/env2/call/builder.rs rename to core/src/env3/call/builder.rs index f4ba0453f50..229291f753f 100644 --- a/core/src/env2/call/builder.rs +++ b/core/src/env3/call/builder.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2019-2020 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. @@ -14,18 +14,15 @@ use core::marker::PhantomData; -use crate::env2::{ +use crate::env3::{ call::{ state, CallData, Selector, }, - errors::CallError, - Env, - EnvAccessMut, EnvTypes, + Result, }; -use ink_prelude::vec::Vec; /// Represents a return type. /// @@ -43,7 +40,7 @@ where /// The maximum gas costs allowed for the call. gas_limit: u64, /// The transferred value for the call. - value: E::Balance, + transferred_value: E::Balance, /// The expected return type. return_type: PhantomData>, /// The already encoded call data respecting the ABI. @@ -74,9 +71,9 @@ where pub fn gas_limit(&self) -> u64 { self.gas_limit } - /// The endowment for the instantiated contract. - pub fn endowment(&self) -> &E::Balance { - &self.value + /// The transferred value for the called contract. + pub fn transferred_value(&self) -> &E::Balance { + &self.transferred_value } /// The raw encoded input data. @@ -95,7 +92,7 @@ where Self { callee, gas_limit: 0, - value: E::Balance::default(), + transferred_value: E::Balance::default(), return_type: PhantomData, call_data: CallData::new(selector), } @@ -137,8 +134,8 @@ where } /// Sets the value transferred upon the execution of the call. - pub fn value(mut self, value: E::Balance) -> Self { - self.params.value = value; + pub fn transferred_value(mut self, value: E::Balance) -> Self { + self.params.transferred_value = value; self } } @@ -167,61 +164,25 @@ where impl CallBuilder, Seal> where - E: Env, + E: EnvTypes, R: scale::Decode, { /// Fires the call to the remote smart contract. /// Returns the returned data back to the caller. - /// - /// # Note - /// - /// Prefer using the [`CallBuilder::fire_using`] method whenever possible - /// since it is more efficient. - pub fn fire(self) -> Result - where - R: scale::Decode, - { - E::eval_contract(&mut Vec::new(), &self.params).map_err(|_| CallError) - } - - /// Fires the call to the smart contract and returns - /// the return value of the call back to the caller. - /// - /// # Note - /// - /// Uses the provided environmental access in order to - /// dispatch the call which is more efficient than the - /// [`CallBuilder::fire`] method. - pub fn fire_using(self, env: &mut EnvAccessMut) -> Result + pub fn fire(self) -> Result where R: scale::Decode, { - env.eval_contract(&self.params).map_err(|_| CallError) + crate::env3::eval_contract(&self.params) } } impl CallBuilder where - E: Env, + E: EnvTypes, { /// Fires the cross-call to the smart contract. - /// - /// # Note - /// - /// Prefer using the [`CallBuilder::fire_using`] method whenever possible - /// since it is more efficient. - pub fn fire(self) -> Result<(), CallError> { - E::invoke_contract(&mut Vec::new(), &self.params).map_err(|_| CallError) - } - - /// Fires the cross-call to the smart contract. - /// - /// # Note - /// - /// Uses the provided environmental access in order to - /// dispatch the call which is more efficient than the - /// [`CallBuilder::fire`] method. - pub fn fire_using(self, env: &mut EnvAccessMut) -> Result<(), CallError> { - env.invoke_contract(&self.params).map_err(|_| CallError) + pub fn fire(self) -> Result<()> { + crate::env3::invoke_contract(&self.params) } } diff --git a/core/src/env2/call/create.rs b/core/src/env3/call/instantiate.rs similarity index 59% rename from core/src/env2/call/create.rs rename to core/src/env3/call/instantiate.rs index 4857c1a5889..48c8bb354e8 100644 --- a/core/src/env2/call/create.rs +++ b/core/src/env3/call/instantiate.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2019-2020 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. @@ -14,20 +14,17 @@ use core::marker::PhantomData; -use crate::env2::{ +use crate::env3::{ call::{ CallData, Selector, }, - errors::CreateError, - Env, - EnvAccessMut, EnvTypes, + Result, }; -use ink_prelude::vec::Vec; pub mod state { - pub use crate::env2::call::state::{ + pub use crate::env3::call::state::{ Sealed, Unsealed, }; @@ -46,25 +43,25 @@ pub mod state { /// /// This is needed because of conflicting implementations of `From for T` /// in the generated code of `ink_lang`. -pub trait FromAccountId +pub trait FromAccountId where - E: EnvTypes, + T: EnvTypes, { /// Creates the contract instance from the account ID of the already instantiated contract. - fn from_account_id(account_id: ::AccountId) -> Self; + fn from_account_id(account_id: ::AccountId) -> Self; } /// Builds up contract instantiations. -pub struct CreateParams +pub struct InstantiateParams where - E: EnvTypes, + T: EnvTypes, { /// The code hash of the created contract. - code_hash: E::Hash, + code_hash: T::Hash, /// The maximum gas costs allowed for the instantiation. gas_limit: u64, - /// The transferred value for the newly created contract. - value: E::Balance, + /// The endowment for the instantiated contract. + endowment: T::Balance, /// The input data for the instantation. call_data: CallData, /// The type of the instantiated contract. @@ -72,22 +69,22 @@ where } /// Builds up contract instantiations. -pub struct CreateBuilder +pub struct InstantiateBuilder where - E: EnvTypes, + T: EnvTypes, { /// The parameters of the cross-contract instantiation. - params: CreateParams, + params: InstantiateParams, /// Seal state. state: PhantomData (Seal, CodeHash)>, } -impl CreateParams +impl InstantiateParams where - E: EnvTypes, + T: EnvTypes, { /// The code hash of the contract. - pub fn code_hash(&self) -> &E::Hash { + pub fn code_hash(&self) -> &T::Hash { &self.code_hash } @@ -96,8 +93,8 @@ where self.gas_limit } /// The endowment for the instantiated contract. - pub fn endowment(&self) -> &E::Balance { - &self.value + pub fn endowment(&self) -> &T::Balance { + &self.endowment } /// The raw encoded input data. @@ -106,18 +103,18 @@ where } } -impl CreateParams +impl InstantiateParams where - E: EnvTypes, - E::Hash: Default, - E::Balance: Default, + T: EnvTypes, + T::Hash: Default, + T::Balance: Default, { /// Creates the default set of initial create parameters. fn new(selector: Selector) -> Self { Self { code_hash: Default::default(), gas_limit: 0, - value: Default::default(), + endowment: Default::default(), call_data: CallData::new(selector), contract_marker: Default::default(), } @@ -126,51 +123,51 @@ where /// Creates a new create builder without setting any presets. pub fn build( selector: Selector, - ) -> CreateBuilder { - CreateBuilder { - params: CreateParams::new(selector), + ) -> InstantiateBuilder { + InstantiateBuilder { + params: InstantiateParams::new(selector), state: Default::default(), } } } -impl CreateBuilder +impl InstantiateBuilder where - E: EnvTypes, + T: EnvTypes, { - /// Sets the maximumly allowed gas costs for the call. + /// Sets the maximum allowed gas costs for the call. pub fn gas_limit(mut self, gas_limit: u64) -> Self { self.params.gas_limit = gas_limit; self } /// Sets the value transferred upon the execution of the call. - pub fn value(mut self, value: E::Balance) -> Self { - self.params.value = value; + pub fn endowment(mut self, value: T::Balance) -> Self { + self.params.endowment = value; self } } -impl CreateBuilder +impl InstantiateBuilder where - E: EnvTypes, + T: EnvTypes, { /// Using the given code hash. pub fn using_code( mut self, - code_hash: E::Hash, - ) -> CreateBuilder { + code_hash: T::Hash, + ) -> InstantiateBuilder { self.params.code_hash = code_hash; - CreateBuilder { + InstantiateBuilder { params: self.params, state: Default::default(), } } } -impl CreateBuilder +impl InstantiateBuilder where - E: EnvTypes, + T: EnvTypes, { /// Pushes an argument to the inputs of the call. pub fn push_arg(mut self, arg: &A) -> Self @@ -182,42 +179,21 @@ where } /// Seals the create builder to prevent further arguments. - pub fn seal(self) -> CreateBuilder { - CreateBuilder { + pub fn seal(self) -> InstantiateBuilder { + InstantiateBuilder { params: self.params, state: Default::default(), } } } -impl CreateBuilder +impl InstantiateBuilder where - E: Env, - C: FromAccountId, + T: EnvTypes, + C: FromAccountId, { /// Instantiates the contract and returns its account ID back to the caller. - /// - /// # Note - /// - /// Prefer using [`CreateBuilder::create_using`] whenever possible because it is more efficient. - pub fn create(self) -> Result { - E::create_contract(&mut Vec::new(), &self.params) - .map(FromAccountId::from_account_id) - .map_err(|_| CreateError) - } - - /// Instantiates the contract and returns its account ID back to the caller. - /// - /// # Note - /// - /// Prefer using this over [`CreateBuilder::create`] whenever possible because it is more efficient. - pub fn create_using( - self, - env_access: &mut EnvAccessMut, - ) -> Result { - env_access - .create_contract(&self.params) - .map(FromAccountId::from_account_id) - .map_err(|_| CreateError) + pub fn instantiate(self) -> Result { + crate::env3::create_contract(&self.params).map(FromAccountId::from_account_id) } } diff --git a/core/src/env2/call/mod.rs b/core/src/env3/call/mod.rs similarity index 77% rename from core/src/env2/call/mod.rs rename to core/src/env3/call/mod.rs index 3f23749ce0c..5cb26e30040 100644 --- a/core/src/env2/call/mod.rs +++ b/core/src/env3/call/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2019-2020 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. @@ -12,13 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Utilities to call or instantiate contracts on the chain. + mod builder; -mod create; +mod instantiate; mod utils; pub mod state { - pub use crate::env2::call::{ - create::state::{ + pub use crate::env3::call::{ + instantiate::state::{ CodeHashAssigned, CodeHashUnassigned, }, @@ -35,9 +37,9 @@ pub use self::{ CallParams, ReturnType, }, - create::{ - CreateBuilder, - CreateParams, + instantiate::{ + InstantiateBuilder, + InstantiateParams, FromAccountId, }, utils::{ diff --git a/core/src/env2/call/utils.rs b/core/src/env3/call/utils.rs similarity index 97% rename from core/src/env2/call/utils.rs rename to core/src/env3/call/utils.rs index edcf8344b90..d7d00fdf415 100644 --- a/core/src/env2/call/utils.rs +++ b/core/src/env3/call/utils.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2019-2020 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. @@ -55,7 +55,7 @@ impl Selector { } /// Creates a selector directly from 4 bytes. - pub const fn from_bytes(bytes: [u8; 4]) -> Self { + pub const fn new(bytes: [u8; 4]) -> Self { Self { bytes } } diff --git a/core/src/env3/engine/mod.rs b/core/src/env3/engine/mod.rs new file mode 100644 index 00000000000..43fe53cfa86 --- /dev/null +++ b/core/src/env3/engine/mod.rs @@ -0,0 +1,43 @@ +// Copyright 2019-2020 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::env3::backend::{ + Env, + TypedEnv, +}; +use cfg_if::cfg_if; + +pub trait OnInstance: Env + TypedEnv { + fn on_instance(f: F) -> R + where + F: FnOnce(&mut Self) -> R; +} + +cfg_if! { + if #[cfg(all(not(feature = "std"), target_arch = "wasm32"))] { + mod on_chain; + pub use self::on_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/core/src/env3/engine/off_chain/db/accounts.rs b/core/src/env3/engine/off_chain/db/accounts.rs new file mode 100644 index 00000000000..9ace7ac1954 --- /dev/null +++ b/core/src/env3/engine/off_chain/db/accounts.rs @@ -0,0 +1,328 @@ +// Copyright 2019-2020 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::{ + env3::{ + EnvError, + EnvTypes, + }, + storage::Key, +}; +use core::cell::Cell; +use derive_more::From; +use ink_prelude::collections::BTreeMap; + +/// 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 EnvError { + fn from(account_error: AccountError) -> Self { + EnvError::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: EnvTypes, + { + 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(), + } + } + + /// Returns the account for the given account ID if any. + pub fn get_account(&self, at: &T::AccountId) -> Option<&Account> + where + T: EnvTypes, + { + 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: EnvTypes, + { + 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: EnvTypes, + { + 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, + rent_allowance: T::Balance, + ) where + T: EnvTypes, + { + self.accounts.insert( + OffAccountId::new(&account_id), + Account { + balance: OffBalance::new(&initial_balance), + kind: AccountKind::Contract(ContractAccount::new::(rent_allowance)), + }, + ); + } +} + +/// 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: EnvTypes, + { + 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: EnvTypes, + { + 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), + } + } + + /// Returns the rent allowance of the contract account or an error. + pub fn rent_allowance(&self) -> Result + where + T: EnvTypes, + { + self.contract_or_err() + .and_then(|contract| contract.rent_allowance.decode().map_err(Into::into)) + } + + /// Sets the rent allowance for the contract account or returns an error. + pub fn set_rent_allowance(&mut self, new_rent_allowance: T::Balance) -> Result<()> + where + T: EnvTypes, + { + self.contract_or_err_mut().and_then(|contract| { + contract + .rent_allowance + .assign(&new_rent_allowance) + .map_err(Into::into) + }) + } + + /// 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)) + } + + /// Clears the contract storage at 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()) + } +} + +/// 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's rent allowance. + rent_allowance: OffBalance, + /// The contract storage. + pub storage: ContractStorage, +} + +impl ContractAccount { + /// Creates a new contract account with the given initial rent allowance. + pub fn new(rent_allowance: T::Balance) -> Self + where + T: EnvTypes, + { + Self { + rent_allowance: OffBalance::new(&rent_allowance), + 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() + } +} + +/// 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); + } +} diff --git a/core/src/env3/engine/off_chain/db/block.rs b/core/src/env3/engine/off_chain/db/block.rs new file mode 100644 index 00000000000..96741c2886c --- /dev/null +++ b/core/src/env3/engine/off_chain/db/block.rs @@ -0,0 +1,113 @@ +// Copyright 2019-2020 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::env3::EnvTypes; + +/// An emulated block in the chain. +pub struct Block { + /// The current block number. + number: OffBlockNumber, + /// The time stamp of the block. + time_stamp: OffTimeStamp, + /// The randomization entropy for a block. + /// + /// # Note + /// + /// - Can optionally be set for more control via + /// [`crate::env3::set_block_randomization_hash`]. + entropy: OffHash, +} + +impl Block { + /// Creates a new block for the given number and time stamp. + pub fn new(number: T::BlockNumber, time_stamp: T::TimeStamp) -> Self + where + T: EnvTypes, + { + use crate::env3::Clear; + use rand::Rng as _; + let mut entropy = ::Hash::clear(); + rand::thread_rng().fill(entropy.as_mut()); + Self { + number: TypedEncoded::new(&number), + time_stamp: TypedEncoded::new(&time_stamp), + entropy: TypedEncoded::new(&entropy), + } + } + + /// Returns the block number. + pub fn number(&self) -> Result + where + T: EnvTypes, + { + self.number.decode().map_err(Into::into) + } + + /// Returns the time stamp of the block. + pub fn time_stamp(&self) -> Result + where + T: EnvTypes, + { + self.time_stamp.decode().map_err(Into::into) + } + + /// Sets the entropy of this block to the given entropy. + /// + /// # Note + /// + /// This is mainly used to control what [`crate::env3::random`] returns + /// in the off-chain environment. + pub fn set_entropy(&mut self, new_entropy: T::Hash) -> Result<()> + where + T: EnvTypes, + { + self.entropy.assign(&new_entropy).map_err(Into::into) + } + + /// Returns a randomized hash. + /// + /// # Note + /// + /// - This is the off-chain environment implementation of + /// [`crate::env3::random`]. It provides the same behaviour 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 + /// testability 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: EnvTypes, + { + 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/core/src/env3/engine/off_chain/db/chain_spec.rs b/core/src/env3/engine/off_chain/db/chain_spec.rs new file mode 100644 index 00000000000..0256d19c1ae --- /dev/null +++ b/core/src/env3/engine/off_chain/db/chain_spec.rs @@ -0,0 +1,93 @@ +// Copyright 2019-2020 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::env3::EnvTypes; + +/// The chain specification. +pub struct ChainSpec { + /// The current gas price. + gas_price: OffBalance, + /// The minimum value an account of the chain may have. + minimum_balance: OffBalance, + /// The tombstone deposit. + tombstone_deposit: OffBalance, + /// The targeted block time. + block_time: OffTimeStamp, +} + +impl ChainSpec { + /// Creates a new uninitialized chain specification. + pub fn uninitialized() -> Self { + Self { + gas_price: OffBalance::uninitialized(), + minimum_balance: OffBalance::uninitialized(), + tombstone_deposit: OffBalance::uninitialized(), + block_time: OffTimeStamp::uninitialized(), + } + } + + /// Default initialization for the off-chain specification. + pub fn initialize_as_default(&mut self) -> crate::env3::Result<()> + where + T: EnvTypes, + ::AccountId: From<[u8; 32]>, + { + self.gas_price + .try_initialize::(&T::Balance::from(100))?; + self.minimum_balance + .try_initialize::(&T::Balance::from(42))?; + self.tombstone_deposit + .try_initialize::(&T::Balance::from(16))?; + self.block_time + .try_initialize::(&T::TimeStamp::from(5))?; + Ok(()) + } + + /// Returns the gas price for the chain. + pub fn gas_price(&self) -> Result + where + T: EnvTypes, + { + self.gas_price.decode().map_err(Into::into) + } + + /// Returns the minimum balance for an account on the chain. + pub fn minimum_balance(&self) -> Result + where + T: EnvTypes, + { + self.minimum_balance.decode().map_err(Into::into) + } + + /// Returns the tombstone deposit for the chain. + pub fn tombstone_deposit(&self) -> Result + where + T: EnvTypes, + { + self.tombstone_deposit.decode().map_err(Into::into) + } + + /// Returns the targeted block time for the chain. + pub fn block_time(&self) -> Result + where + T: EnvTypes, + { + self.block_time.decode().map_err(Into::into) + } +} diff --git a/core/src/env3/engine/off_chain/db/console.rs b/core/src/env3/engine/off_chain/db/console.rs new file mode 100644 index 00000000000..f19f2c9e147 --- /dev/null +++ b/core/src/env3/engine/off_chain/db/console.rs @@ -0,0 +1,76 @@ +// Copyright 2019-2020 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 console used to print console contents and store them. +pub struct Console { + /// The buffer to store the already pasted contents. + past_prints: Vec, +} + +impl Console { + /// Creates a new empty console. + pub fn new() -> Self { + Self { + past_prints: Vec::new(), + } + } + + /// Prints the contents to the actual console and stores them. + pub fn println(&mut self, contents: &str) { + self.past_prints.push(contents.to_string()); + println!("{}", contents); + } + + /// Returns an iterator over the past console prints. + pub fn past_prints(&self) -> PastPrints { + PastPrints::new(self) + } +} + +/// Iterator over the past prints to the console. +pub struct PastPrints<'a> { + /// Iterator over the past printlns. + iter: core::slice::Iter<'a, String>, +} + +impl<'a> PastPrints<'a> { + /// Creates a new iterator over the past console prints. + fn new(console: &'a Console) -> Self { + Self { + iter: console.past_prints.iter(), + } + } +} + +impl<'a> Iterator for PastPrints<'a> { + type Item = &'a str; + + fn next(&mut self) -> Option { + self.iter.next().map(AsRef::as_ref) + } +} + +impl<'a> ExactSizeIterator for PastPrints<'a> { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl<'a> DoubleEndedIterator for PastPrints<'a> { + fn next_back(&mut self) -> Option { + self.iter.next_back().map(AsRef::as_ref) + } +} diff --git a/core/src/env3/engine/off_chain/db/events.rs b/core/src/env3/engine/off_chain/db/events.rs new file mode 100644 index 00000000000..76710006c6b --- /dev/null +++ b/core/src/env3/engine/off_chain/db/events.rs @@ -0,0 +1,74 @@ +// Copyright 2019-2020 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::env3::{ + EnvTypes, + 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: EnvTypes, + E: Topics + scale::Encode, + { + Self { + topics: emitted_event + .topics() + .iter() + .map(|hash| OffHash::new(hash)) + .collect::>(), + 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(), + } + } + + /// Records a new emitted event. + pub fn record(&mut self, new_event: E) + where + T: EnvTypes, + 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/core/src/env3/engine/off_chain/db/exec_context.rs b/core/src/env3/engine/off_chain/db/exec_context.rs new file mode 100644 index 00000000000..347caeb177f --- /dev/null +++ b/core/src/env3/engine/off_chain/db/exec_context.rs @@ -0,0 +1,233 @@ +// Copyright 2019-2020 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, + }, + OffAccountId, + OffBalance, +}; +use crate::env3::{ + call::CallData, + EnvTypes, +}; +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: OffBalance, + /// 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: EnvTypes, + { + ExecContextBuilder::new() + } + + /// Returns the caller. + pub fn caller(&self) -> Result + where + T: EnvTypes, + { + self.caller.decode().map_err(Into::into) + } + + /// Returns the callee. + pub fn callee(&self) -> Result + where + T: EnvTypes, + { + self.callee.decode().map_err(Into::into) + } + + /// Returns the transferred value. + pub fn transferred_value(&self) -> Result + where + T: EnvTypes, + { + self.transferred_value.decode().map_err(Into::into) + } + + /// Returns the gas. + pub fn gas(&self) -> Result + where + T: EnvTypes, + { + self.gas.decode().map_err(Into::into) + } + + /// 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: EnvTypes, +{ + /// 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: EnvTypes, +{ + /// 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 { + if self.caller.is_some() { + panic!("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 { + if self.callee.is_some() { + panic!("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: T::Balance) -> Self { + if self.gas.is_some() { + panic!("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 { + if self.transferred_value.is_some() { + panic!("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 { + if self.call_data.is_some() { + panic!("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: TypedEncoded::new(&gas), + call_data: self.call_data.unwrap(), + output: None, + } + } +} diff --git a/core/src/env2/test/mod.rs b/core/src/env3/engine/off_chain/db/mod.rs similarity index 54% rename from core/src/env2/test/mod.rs rename to core/src/env3/engine/off_chain/db/mod.rs index fa8da23f650..b497df30b88 100644 --- a/core/src/env2/test/mod.rs +++ b/core/src/env3/engine/off_chain/db/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2019-2020 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. @@ -12,36 +12,38 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Test environment for off-chain testing and utilities. +mod accounts; +mod block; +mod chain_spec; +mod console; +mod events; +mod exec_context; -mod accessor; -pub mod account; -mod instance; -pub mod record; -pub mod storage; -mod typed_encoded; -pub mod types; - -use self::{ - account::{ +pub use self::{ + accounts::{ Account, + AccountError, AccountKind, AccountsDb, ContractAccount, + ContractStorage, }, - instance::TestEnvInstance, - record::{ - CallContractRecord, - CreateContractRecord, - EmitEventRecord, - InvokeRuntimeRecord, - Record, - RestoreContractRecord, + block::Block, + chain_spec::ChainSpec, + console::{ + Console, + PastPrints, }, - typed_encoded::{ - AlreadyInitialized, - TypedEncoded, + events::{ + EmittedEvent, + EmittedEventsRecorder, }, + exec_context::ExecContext, +}; +use super::{ + OffAccountId, + OffBalance, + OffBlockNumber, + OffHash, + OffTimeStamp, }; - -pub use self::accessor::TestEnv; diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs new file mode 100644 index 00000000000..8e564822ad9 --- /dev/null +++ b/core/src/env3/engine/off_chain/impls.rs @@ -0,0 +1,275 @@ +// Copyright 2019-2020 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::{ + Account, + EnvInstance, +}; +use crate::{ + env3::{ + call::{ + CallData, + CallParams, + InstantiateParams, + ReturnType, + }, + Env, + EnvError, + EnvTypes, + Result, + Topics, + TypedEnv, + }, + storage::Key, +}; + +impl EnvInstance { + /// Returns the callee account. + fn callee_account(&self) -> &Account { + let callee = self + .exec_context() + .expect("uninitialized execution 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 execution context") + .callee + .clone(); + self.accounts + .get_account_off_mut(&callee) + .expect("callee account does not exist") + } +} + +impl Env for EnvInstance { + fn set_contract_storage(&mut self, key: Key, value: &V) + where + V: scale::Encode, + { + self.callee_account_mut() + .set_storage(key, value) + .expect("callee account is not a smart contract"); + } + + fn get_contract_storage(&mut self, key: Key) -> Result + where + R: scale::Decode, + { + self.callee_account() + .get_storage::(key)? + .ok_or_else(|| EnvError::MissingContractStorageEntry) + } + + fn clear_contract_storage(&mut self, key: Key) { + self.callee_account_mut() + .clear_storage(key) + .expect("callee account is not a smart contract"); + } + + fn get_runtime_storage(&mut self, runtime_key: &[u8]) -> Option> + where + R: scale::Decode, + { + self.runtime_storage.load::(runtime_key) + } + + fn input(&mut self) -> Result { + self.exec_context() + .map(|exec_ctx| &exec_ctx.call_data) + .map(Clone::clone) + .map_err(|_| scale::Error::from("could not decode input call data")) + .map_err(Into::into) + } + + fn output(&mut self, return_value: &R) + where + R: scale::Encode, + { + let ctx = self + .exec_context_mut() + .expect("uninitialized execution context"); + ctx.output = Some(return_value.encode()); + } + + fn println(&mut self, content: &str) { + self.console.println(content) + } +} + +impl TypedEnv for EnvInstance { + fn caller(&mut self) -> Result { + self.exec_context() + .expect("uninitialized execution context") + .caller::() + .map_err(|_| scale::Error::from("could not decode caller")) + .map_err(Into::into) + } + + fn transferred_balance(&mut self) -> Result { + self.exec_context() + .expect("uninitialized execution context") + .transferred_value::() + .map_err(|_| scale::Error::from("could not decode transferred balance")) + .map_err(Into::into) + } + + fn gas_price(&mut self) -> Result { + self.chain_spec + .gas_price::() + .map_err(|_| scale::Error::from("could not decode gas price")) + .map_err(Into::into) + } + + fn gas_left(&mut self) -> Result { + self.exec_context() + .expect("uninitialized execution context") + .gas::() + .map_err(|_| scale::Error::from("could not decode gas left")) + .map_err(Into::into) + } + + fn block_timestamp(&mut self) -> Result { + self.current_block() + .expect("uninitialized execution context") + .time_stamp::() + .map_err(|_| scale::Error::from("could not decode block time")) + .map_err(Into::into) + } + + fn address(&mut self) -> Result { + self.exec_context() + .expect("uninitialized execution context") + .callee::() + .map_err(|_| scale::Error::from("could not decode callee")) + .map_err(Into::into) + } + + fn balance(&mut self) -> Result { + self.callee_account() + .balance::() + .map_err(|_| scale::Error::from("could not decode callee balance")) + .map_err(Into::into) + } + + fn rent_allowance(&mut self) -> Result { + self.callee_account() + .rent_allowance::() + .map_err(|_| scale::Error::from("could not decode callee rent allowance")) + .map_err(Into::into) + } + + fn block_number(&mut self) -> Result { + self.current_block() + .expect("uninitialized execution context") + .number::() + .map_err(|_| scale::Error::from("could not decode block number")) + .map_err(Into::into) + } + + fn minimum_balance(&mut self) -> Result { + self.chain_spec + .minimum_balance::() + .map_err(|_| scale::Error::from("could not decode minimum balance")) + .map_err(Into::into) + } + + fn tombstone_deposit(&mut self) -> Result { + self.chain_spec + .tombstone_deposit::() + .map_err(|_| scale::Error::from("could not decode tombstone deposit")) + .map_err(Into::into) + } + + fn emit_event(&mut self, new_event: Event) + where + T: EnvTypes, + Event: Topics + scale::Encode, + { + self.emitted_events.record::(new_event) + } + + fn set_rent_allowance(&mut self, new_rent_allowance: T::Balance) + where + T: EnvTypes, + { + self.callee_account_mut() + .set_rent_allowance::(new_rent_allowance) + .expect("could not encode rent allowance") + } + + fn invoke_contract(&mut self, _call_params: &CallParams) -> Result<()> + where + T: EnvTypes, + { + unimplemented!("off-chain environment does not support contract invokation") + } + + fn invoke_runtime(&mut self, params: &T::Call) -> Result<()> + where + T: EnvTypes, + { + self.runtime_call_handler.invoke::(params) + } + + fn eval_contract( + &mut self, + _call_params: &CallParams>, + ) -> Result + where + T: EnvTypes, + R: scale::Decode, + { + unimplemented!("off-chain environment does not support contract evaluation") + } + + fn create_contract( + &mut self, + _params: &InstantiateParams, + ) -> Result + where + T: EnvTypes, + { + unimplemented!("off-chain environment does not support contract instantiation") + } + + fn restore_contract( + &mut self, + _account_id: T::AccountId, + _code_hash: T::Hash, + _rent_allowance: T::Balance, + _filtered_keys: &[Key], + ) where + T: EnvTypes, + { + unimplemented!("off-chain environment does not support contract restoration") + } + + fn random(&mut self, subject: &[u8]) -> Result + where + T: EnvTypes, + { + self.current_block() + .expect("uninitialized execution context") + .random::(subject) + .map_err(Into::into) + } +} diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs new file mode 100644 index 00000000000..a5608e19677 --- /dev/null +++ b/core/src/env3/engine/off_chain/mod.rs @@ -0,0 +1,235 @@ +// Copyright 2019-2020 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 db; +mod impls; +mod runtime_calls; +mod runtime_storage; +pub mod test_api; +mod typed_encoded; +mod types; + +use self::{ + db::{ + Account, + AccountsDb, + Block, + ChainSpec, + Console, + EmittedEvent, + EmittedEventsRecorder, + ExecContext, + }, + runtime_calls::RuntimeCallHandler, + runtime_storage::RuntimeStorage, + typed_encoded::TypedEncoded, + types::{ + OffAccountId, + OffBalance, + OffBlockNumber, + OffCall, + OffHash, + OffTimeStamp, + }, +}; +pub use self::{ + db::{ + AccountError, + PastPrints, + }, + typed_encoded::TypedEncodedError, +}; +use super::OnInstance; +use crate::env3::EnvTypes; +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)] + UnregisteredRuntimeCallHandler, +} + +pub type Result = core::result::Result; + +/// 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 console to print debug contents. + console: Console, + /// The emulated runtime storage. + runtime_storage: RuntimeStorage, + /// The runtime calls handler. + runtime_call_handler: RuntimeCallHandler, + /// Emitted events recorder. + emitted_events: EmittedEventsRecorder, +} + +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(), + console: Console::new(), + runtime_storage: RuntimeStorage::new(), + runtime_call_handler: RuntimeCallHandler::new(), + emitted_events: EmittedEventsRecorder::new(), + } + } + + /// 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::env3::Result<()> + where + T: EnvTypes, + ::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(2)), + ); + // Bob has half the balance that alice got. + self.accounts.add_user_account::( + default_accounts.bob, + T::Balance::max_value().div(T::Balance::from(4)), + ); + // 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(0), + T::TimeStamp::from(0), + )); + // 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(0), + T::Balance::from(20), + ); + // Initialize the execution context for the first contract execution. + use crate::env3::call::{ + CallData, + Selector, + }; + self.exec_context.push( + ExecContext::build::() + .caller(default_accounts.alice) + .callee(contract_account_id) + .gas(T::Balance::from(500_000)) + .transferred_value(T::Balance::from(500)) + .call_data(CallData::new(Selector::from_str("call"))) + .finish(), + ); + Ok(()) + } + + /// Advances the chain by a single block. + pub fn advance_block(&mut self) -> crate::env3::Result<()> + where + T: EnvTypes, + { + let new_block_number = T::BlockNumber::from(self.blocks.len() as u32); + let new_time_stamp = self.current_block()?.time_stamp::()? + + self.chain_spec.block_time::()?; + self.blocks + .push(Block::new::(new_block_number, new_time_stamp)); + 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_else(|| OffChainError::UninitializedBlocks) + } +} + +impl OnInstance for EnvInstance { + fn on_instance(f: F) -> R + where + F: FnOnce(&mut Self) -> R, + { + thread_local!( + static INSTANCE: RefCell = RefCell::new( + EnvInstance::uninitialized() + ) + ); + INSTANCE.with(|instance| f(&mut instance.borrow_mut())) + } +} diff --git a/core/src/env3/engine/off_chain/runtime_calls.rs b/core/src/env3/engine/off_chain/runtime_calls.rs new file mode 100644 index 00000000000..23a0fcf4ef3 --- /dev/null +++ b/core/src/env3/engine/off_chain/runtime_calls.rs @@ -0,0 +1,66 @@ +// Copyright 2019-2020 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::{ + OffCall, + OffChainError, +}; +use crate::env3::{ + EnvTypes, + Result, +}; + +/// Runtime call handler. +/// +/// More generically a mapping from bytes to bytes. +pub struct RuntimeCallHandler { + /// The currently registered runtime call handler. + registered: Option>, +} + +impl RuntimeCallHandler { + /// Creates a new runtime call handler. + /// + /// Initialized without any handler. + pub fn new() -> Self { + Self { registered: None } + } + + /// Register a runtime call handler. + pub fn register(&mut self, mut f: F) + where + T: EnvTypes, + F: FnMut(::Call) + 'static, + { + self.registered = Some(Box::new(move |call: OffCall| { + f(call + .decode::<::Call>() + .expect("could not decode call")) + })); + } + + /// Invokes the runtime with the given parameters. + pub fn invoke(&mut self, params: &T::Call) -> Result<()> + where + T: EnvTypes, + { + match &mut self.registered { + Some(ref mut handler) => { + handler(OffCall::new(params)); + Ok(()) + } + None => Err(OffChainError::UnregisteredRuntimeCallHandler.into()), + } + } +} diff --git a/core/src/env3/engine/off_chain/runtime_storage.rs b/core/src/env3/engine/off_chain/runtime_storage.rs new file mode 100644 index 00000000000..8d2ee56e234 --- /dev/null +++ b/core/src/env3/engine/off_chain/runtime_storage.rs @@ -0,0 +1,54 @@ +// Copyright 2019-2020 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::env3::Result; +use ink_prelude::{ + collections::btree_map::BTreeMap, + vec::Vec, +}; + +/// Runtime storage. +/// +/// More generically a mapping from bytes to bytes. +pub struct RuntimeStorage { + /// The underlying storage mapping. + entries: BTreeMap, Vec>, +} + +impl RuntimeStorage { + /// Creates a new runtime storage. + pub fn new() -> Self { + Self { + entries: BTreeMap::new(), + } + } + + /// Stores the value under the given key. + pub fn store(&mut self, key: Vec, value: T) + where + T: scale::Encode, + { + self.entries.insert(key, value.encode()); + } + + /// Loads the value under the given key if any. + pub fn load(&self, key: &[u8]) -> Option> + where + T: scale::Decode, + { + self.entries.get(key).map(|encoded| { + ::decode(&mut &encoded[..]).map_err(Into::into) + }) + } +} diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs new file mode 100644 index 00000000000..3f7a98a6413 --- /dev/null +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -0,0 +1,328 @@ +// Copyright 2019-2020 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::{ + db::ExecContext, + AccountError, + EmittedEvent, + EnvInstance, + OnInstance, +}; +use crate::env3::{ + call::CallData, + EnvTypes, + Result, +}; +use ink_prelude::string::String; + +/// 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: T::Balance, + endowment: T::Balance, + call_data: CallData, +) where + T: EnvTypes, +{ + ::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(); + }) +} + +/// 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, +) -> Result<()> +where + T: EnvTypes, +{ + ::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)) + }) +} + +/// 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: EnvTypes, +{ + ::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.balance::().map_err(Into::into)) + }) +} + +/// Sets the rent allowance of the contract account to the given rent allowance. +/// +/// # Errors +/// +/// - If `account` does not exist. +/// - If the underlying `account` type does not match. +/// - If the underlying `new_rent_allowance` type does not match. +pub fn set_contract_rent_allowance( + account_id: T::AccountId, + new_rent_allowance: T::Balance, +) -> Result<()> +where + T: EnvTypes, +{ + ::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_rent_allowance::(new_rent_allowance) + .map_err(Into::into) + }) + }) +} + +/// Returns the rent allowance of the contract account. +/// +/// # Errors +/// +/// - If `account` does not exist. +/// - If the underlying `account` type does not match. +/// - If the returned rent allowance cannot be properly decoded. +pub fn get_contract_rent_allowance(account_id: T::AccountId) -> Result +where + T: EnvTypes, +{ + ::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.rent_allowance::().map_err(Into::into)) + }) +} + +/// Sets the runtime storage to value for the given key. +pub fn set_runtime_storage(key: &[u8], value: T) +where + T: scale::Encode, +{ + ::on_instance(|instance| { + instance.runtime_storage.store(key.to_vec(), value) + }) +} + +/// Sets the call handler for runtime calls. +pub fn set_runtime_call_handler(f: F) +where + T: EnvTypes, + F: FnMut(::Call) + 'static, +{ + ::on_instance(|instance| { + instance.runtime_call_handler.register::(f) + }) +} + +/// Set the entropy hash of the current block. +/// +/// # Note +/// +/// This allows to control what [`crate::env3::random`] returns. +pub fn set_block_entropy(entropy: T::Hash) -> Result<()> +where + T: EnvTypes, +{ + ::on_instance(|instance| { + instance.current_block_mut()?.set_entropy::(entropy) + }) + .map_err(Into::into) +} + +/// Returns the contents of the past performed environmental `println` in order. +pub fn recorded_printlns() -> 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 + .console + .past_prints() + .map(ToOwned::to_owned) + .collect::>() + .into_iter() + }) +} + +/// Returns the recorded emitted events in order. +pub fn recorded_events() -> impl Iterator { + ::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() + }) +} + +/// Advances the chain by a single block. +pub fn advance_block() -> Result<()> +where + T: EnvTypes, +{ + ::on_instance(|instance| instance.advance_block::()) +} + +/// The default accounts. +pub struct DefaultAccounts +where + T: EnvTypes, +{ + pub alice: T::AccountId, + pub bob: T::AccountId, + pub charlie: T::AccountId, + pub django: T::AccountId, + pub eve: T::AccountId, + pub frank: T::AccountId, +} + +/// Returns the default accounts for testing purposes: +/// Alice, Bob, Charlie, Django, Eve and Frank. +pub fn default_accounts() -> Result> +where + T: EnvTypes, + ::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]), + }) +} + +/// Initializes the whole off-chain environment. +/// +/// # Note +/// +/// - Initializes the off-chain environment with default values that fit most +/// uses cases. +/// - The off-chain environment _must_ be initialized before use. +pub fn initialize_as_default() -> Result<()> +where + T: EnvTypes, + ::AccountId: From<[u8; 32]>, +{ + ::on_instance(|instance| { + instance.initialize_as_default::() + }) +} + +/// Runs the given closure test function with the default configuartion +/// for the off-chain environment. +pub fn run_test(f: F) -> Result<()> +where + T: EnvTypes, + F: FnOnce(DefaultAccounts) -> Result<()>, + ::AccountId: From<[u8; 32]>, +{ + initialize_as_default::()?; + let default_accounts = default_accounts::()?; + f(default_accounts) +} + +/// 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)> +where + T: EnvTypes, +{ + ::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)) + }) +} diff --git a/core/src/env3/engine/off_chain/typed_encoded.rs b/core/src/env3/engine/off_chain/typed_encoded.rs new file mode 100644 index 00000000000..ac8451aebae --- /dev/null +++ b/core/src/env3/engine/off_chain/typed_encoded.rs @@ -0,0 +1,329 @@ +// Copyright 2019-2020 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::env3::EnvError; +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 isn't possible to decide about the used types + /// given by `EnvTypes` at initialization. + type_id: Option, + /// Classification marker. + /// + /// # Note + /// + /// - This shouldn't 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 EnvError { + fn from(typed_encoded_error: TypedEncodedError) -> Self { + EnvError::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 ininitialized 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 clousure 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 clousure 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/core/src/env2/test/types.rs b/core/src/env3/engine/off_chain/types.rs similarity index 59% rename from core/src/env2/test/types.rs rename to core/src/env3/engine/off_chain/types.rs index 397b22a9ee8..32b0d75e695 100644 --- a/core/src/env2/test/types.rs +++ b/core/src/env3/engine/off_chain/types.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2019-2020 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. @@ -25,34 +25,34 @@ //! assigned upon initialization to check whether accesses to it are //! type safe. -use crate::env2::test::TypedEncoded; +use super::TypedEncoded; /// Type markers used in conjunction with `TypedEncoded`. #[rustfmt::skip] mod type_marker { /// Type marker representing an environmental `AccountId`. - #[derive(Debug)] pub enum AccountId {} + #[derive(Debug, Clone)] pub enum AccountId {} /// Type marker representing an environmental `Balance`. - #[derive(Debug)] pub enum Balance {} + #[derive(Debug, Clone)] pub enum Balance {} /// Type marker representing an environmental `Hash`. - #[derive(Debug)] pub enum Hash {} - /// Type marker representing an environmental `Moment`. - #[derive(Debug)] pub enum Moment {} + #[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)] pub enum BlockNumber {} + #[derive(Debug, Clone)] pub enum BlockNumber {} /// Type marker representing an environmental `Call`. - #[derive(Debug)] pub enum Call {} + #[derive(Debug, Clone)] pub enum Call {} } -/// Environmental account ID type. -pub type AccountId = TypedEncoded; -/// Environmental balance type. -pub type Balance = TypedEncoded; -/// Environmental hash type. -pub type Hash = TypedEncoded; -/// Environmental moment (block time) type. -pub type Moment = TypedEncoded; -/// Environmental block number type. -pub type BlockNumber = TypedEncoded; -/// Environmental call (runtime dispatch) type. -pub type Call = TypedEncoded; +/// 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 moment (block time) type. +pub type OffTimeStamp = TypedEncoded; +/// Off-chain environment block number type. +pub type OffBlockNumber = TypedEncoded; +/// Off-chain environment call (runtime dispatch) type. +pub type OffCall = TypedEncoded; diff --git a/core/src/env3/engine/on_chain/buffer.rs b/core/src/env3/engine/on_chain/buffer.rs new file mode 100644 index 00000000000..8deb5ccc68b --- /dev/null +++ b/core/src/env3/engine/on_chain/buffer.rs @@ -0,0 +1,91 @@ +// Copyright 2019-2020 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. + +/// A static buffer with 16kB of capacity. +pub struct StaticBuffer { + /// The static buffer with a total capacity of 16kB. + buffer: [u8; Self::CAPACITY], + /// The number of elements currently in use by the buffer + /// counting from the start. + len: usize, +} + +impl StaticBuffer { + /// The capacity of the static buffer. + const CAPACITY: usize = 1 << 14; // 16kB + + /// Creates a new static buffer. + pub const fn new() -> Self { + Self { + buffer: [0; Self::CAPACITY], + len: 0, + } + } + + /// Returns the current length of the static buffer. + pub fn len(&self) -> usize { + self.len + } + + /// Resizes the static buffer to the given length. + /// + /// # Panics + /// + /// Panics for lengths greater than its capacity. + pub fn resize(&mut self, new_len: usize) { + if new_len > Self::CAPACITY { + panic!("static buffer overflowed") + } + self.len = new_len; + } + + /// Resets the length of the buffer to 0. + pub fn clear(&mut self) { + self.len = 0; + } +} + +impl scale::Output for StaticBuffer { + fn write(&mut self, bytes: &[u8]) { + if self.len + bytes.len() > Self::CAPACITY { + panic!("static buffer overflowed") + } + let start = self.len; + let len_bytes = bytes.len(); + self.buffer[start..(start + len_bytes)].copy_from_slice(bytes); + self.len += len_bytes; + } + + fn push_byte(&mut self, byte: u8) { + if self.len == Self::CAPACITY { + panic!("static buffer overflowed") + } + self.buffer[self.len] = byte; + self.len += 1; + } +} + +impl> core::ops::Index for StaticBuffer { + type Output = I::Output; + + fn index(&self, index: I) -> &Self::Output { + core::ops::Index::index(&self.buffer[..self.len], index) + } +} + +impl> core::ops::IndexMut for StaticBuffer { + fn index_mut(&mut self, index: I) -> &mut Self::Output { + core::ops::IndexMut::index_mut(&mut self.buffer[..self.len], index) + } +} diff --git a/core/src/env2/srml/ext.rs b/core/src/env3/engine/on_chain/ext.rs similarity index 65% rename from core/src/env2/srml/ext.rs rename to core/src/env3/engine/on_chain/ext.rs index a6fc5b7727c..f9b49804db6 100644 --- a/core/src/env2/srml/ext.rs +++ b/core/src/env3/engine/on_chain/ext.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2019-2020 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. @@ -17,17 +17,98 @@ //! Refer to substrate SRML contract module for more documentation. use crate::{ - env2::srml::RetCode, + env3::{ + EnvError, + Result, + }, storage::Key, }; +/// Returned by the host environment if a contract call trapped. +const TRAP_RETURN_CODE: u32 = 0x0100; + +mod sys { + extern "C" { + pub fn ext_instantiate( + init_code_ptr: u32, + init_code_len: u32, + gas: u64, + value_ptr: u32, + value_len: u32, + input_data_ptr: u32, + input_data_len: u32, + ) -> u32; + + pub fn ext_call( + callee_ptr: u32, + callee_len: u32, + gas: u64, + value_ptr: u32, + value_len: u32, + input_data_ptr: u32, + input_data_len: u32, + ) -> u32; + + pub fn ext_deposit_event( + topics_ptr: u32, + topics_len: u32, + data_ptr: u32, + data_len: u32, + ); + + pub fn ext_set_storage( + key_ptr: u32, + value_non_null: u32, + value_ptr: u32, + value_len: u32, + ); + pub fn ext_get_storage(key_ptr: u32) -> u32; + + pub fn ext_get_runtime_storage(key_ptr: u32, key_len: u32) -> u32; + + pub fn ext_restore_to( + dest_ptr: u32, + dest_len: u32, + code_hash_ptr: u32, + code_hash_len: u32, + rent_allowance_ptr: u32, + rent_allowance_len: u32, + delta_ptr: u32, + delta_count: u32, + ); + + pub fn ext_dispatch_call(call_ptr: u32, call_len: u32); + + pub fn ext_scratch_size() -> u32; + pub fn ext_scratch_read(dst_ptr: u32, offset: u32, len: u32); + pub fn ext_scratch_write(src_ptr: u32, len: u32); + + pub fn ext_caller(); + pub fn ext_block_number(); + pub fn ext_address(); + pub fn ext_balance(); + pub fn ext_gas_price(); + pub fn ext_gas_left(); + pub fn ext_value_transferred(); + pub fn ext_now(); + pub fn ext_rent_allowance(); + pub fn ext_minimum_balance(); + pub fn ext_tombstone_deposit(); + + pub fn ext_set_rent_allowance(value_ptr: u32, value_len: u32); + + pub fn ext_random_seed(subject_ptr: u32, subject_len: u32); + pub fn ext_println(str_ptr: u32, str_len: u32); + } +} + pub fn create( code_hash: &[u8], gas_limit: u64, value: &[u8], create_data: &[u8], -) -> RetCode { - unsafe { +) -> Result<()> { + let ret_code = unsafe { sys::ext_instantiate( code_hash.as_ptr() as u32, code_hash.len() as u32, @@ -37,12 +118,17 @@ pub fn create( create_data.as_ptr() as u32, create_data.len() as u32, ) + }; + match ret_code { + 0 => Ok(()), + c if c == TRAP_RETURN_CODE => Err(EnvError::ContractInstantiationTrapped), + err if err <= 0xFF => Err(EnvError::ContractInstantiationFailState(err as u8)), + _unknown => panic!("encountered unknown error code upon contract call"), } - .into() } -pub fn call(callee: &[u8], gas_limit: u64, value: &[u8], call_data: &[u8]) -> RetCode { - unsafe { +pub fn call(callee: &[u8], gas_limit: u64, value: &[u8], call_data: &[u8]) -> Result<()> { + let ret_code = unsafe { sys::ext_call( callee.as_ptr() as u32, callee.len() as u32, @@ -52,8 +138,13 @@ pub fn call(callee: &[u8], gas_limit: u64, value: &[u8], call_data: &[u8]) -> Re call_data.as_ptr() as u32, call_data.len() as u32, ) + }; + match ret_code { + 0 => Ok(()), + c if c == TRAP_RETURN_CODE => Err(EnvError::ContractInstantiationTrapped), + err if err <= 0xFF => Err(EnvError::ContractInstantiationFailState(err as u8)), + _unknown => panic!("encountered unknown error code upon contract call"), } - .into() } pub fn deposit_event(topics: &[u8], data: &[u8]) { @@ -67,38 +158,65 @@ pub fn deposit_event(topics: &[u8], data: &[u8]) { } } -pub fn set_storage(key: &[u8], value: Option<&[u8]>) { - match value { - Some(value) => unsafe { - sys::ext_set_storage( - key.as_ptr() as u32, - 1, - value.as_ptr() as u32, - value.len() as u32, - ) - }, - None => unsafe { sys::ext_set_storage(key.as_ptr() as u32, 0, 0, 0) }, +pub fn set_storage(key: &[u8], encoded_value: &[u8]) { + unsafe { + sys::ext_set_storage( + key.as_ptr() as u32, + 1, + encoded_value.as_ptr() as u32, + encoded_value.len() as u32, + ) } } -pub fn get_storage(key: &[u8]) -> RetCode { - unsafe { sys::ext_get_storage(key.as_ptr() as u32) }.into() +pub fn clear_storage(key: &[u8]) { + unsafe { sys::ext_set_storage(key.as_ptr() as u32, 0, 0, 0) } } -pub fn get_runtime_storage(key: &[u8]) -> RetCode { - unsafe { sys::ext_get_runtime_storage(key.as_ptr() as u32, key.len() as u32) }.into() +pub fn get_storage(key: &[u8]) -> Result<()> { + let ret_code = unsafe { sys::ext_get_storage(key.as_ptr() as u32) }; + match ret_code { + 0 => Ok(()), + 1 => Err(EnvError::MissingContractStorageEntry), + _unknown => panic!("encountered unexpected return code"), + } +} + +pub fn get_runtime_storage(runtime_key: &[u8]) -> Result<()> { + let ret_code = unsafe { + sys::ext_get_runtime_storage( + runtime_key.as_ptr() as u32, + runtime_key.len() as u32, + ) + }; + match ret_code { + 0 => Ok(()), + 1 => Err(EnvError::MissingRuntimeStorageEntry), + _unknown => panic!("encountered unsupported return code"), + } } +/// Restores a tombstone to the original smart contract. +/// +/// # Params +/// +/// - `account_id`: Encoded bytes of the `AccountId` of the to-be-restored contract. +/// - `code_hash`: Encoded code hash of the to-be-restored contract. +/// - `rent_allowance`: The encoded rent allowance of the restored contract +/// upon successful restoration. +/// - `filtered_keys`: Storage keys that will be ignored for the tombstone hash +/// match calculation that decide whether the original contract +/// storage and the storage of the restorer contract is equal. pub fn restore_to( - dest: &[u8], + account_id: &[u8], code_hash: &[u8], rent_allowance: &[u8], filtered_keys: &[Key], ) { unsafe { sys::ext_restore_to( - dest.as_ptr() as u32, - dest.len() as u32, + account_id.as_ptr() as u32, + account_id.len() as u32, code_hash.as_ptr() as u32, code_hash.len() as u32, rent_allowance.as_ptr() as u32, @@ -117,14 +235,12 @@ pub fn scratch_size() -> usize { (unsafe { sys::ext_scratch_size() }) as usize } -pub fn scratch_read(dest: &mut [u8], offset: u32) -> RetCode { - unsafe { sys::ext_scratch_read(dest.as_mut_ptr() as u32, offset, dest.len() as u32) }; - RetCode::success() +pub fn scratch_read(dest: &mut [u8], offset: u32) { + unsafe { sys::ext_scratch_read(dest.as_mut_ptr() as u32, offset, dest.len() as u32) } } -pub fn scratch_write(src: &[u8]) -> RetCode { - unsafe { sys::ext_scratch_write(src.as_ptr() as u32, src.len() as u32) }; - RetCode::success() +pub fn scratch_write(src: &[u8]) { + unsafe { sys::ext_scratch_write(src.as_ptr() as u32, src.len() as u32) } } macro_rules! impl_ext_wrapper_for { @@ -149,11 +265,11 @@ impl_ext_wrapper_for! { (now => ext_now), (rent_allowance => ext_rent_allowance), (minimum_balance => ext_minimum_balance), + (tombstone_deposit => ext_tombstone_deposit), } -pub fn set_rent_allowance(value: &[u8]) -> RetCode { - unsafe { sys::ext_set_rent_allowance(value.as_ptr() as u32, value.len() as u32) }; - RetCode::success() +pub fn set_rent_allowance(value: &[u8]) { + unsafe { sys::ext_set_rent_allowance(value.as_ptr() as u32, value.len() as u32) } } pub fn random_seed(subject: &[u8]) { @@ -164,77 +280,3 @@ pub fn println(content: &str) { let bytes = content.as_bytes(); unsafe { sys::ext_println(bytes.as_ptr() as u32, bytes.len() as u32) } } - -mod sys { - extern "C" { - pub fn ext_instantiate( - init_code_ptr: u32, - init_code_len: u32, - gas: u64, - value_ptr: u32, - value_len: u32, - input_data_ptr: u32, - input_data_len: u32, - ) -> u32; - - pub fn ext_call( - callee_ptr: u32, - callee_len: u32, - gas: u64, - value_ptr: u32, - value_len: u32, - input_data_ptr: u32, - input_data_len: u32, - ) -> u32; - - pub fn ext_deposit_event( - topics_ptr: u32, - topics_len: u32, - data_ptr: u32, - data_len: u32, - ); - - pub fn ext_set_storage( - key_ptr: u32, - value_non_null: u32, - value_ptr: u32, - value_len: u32, - ); - pub fn ext_get_storage(key_ptr: u32) -> u32; - - pub fn ext_get_runtime_storage(key_ptr: u32, key_len: u32) -> u32; - - pub fn ext_restore_to( - dest_ptr: u32, - dest_len: u32, - code_hash_ptr: u32, - code_hash_len: u32, - rent_allowance_ptr: u32, - rent_allowance_len: u32, - delta_ptr: u32, - delta_count: u32, - ); - - pub fn ext_dispatch_call(call_ptr: u32, call_len: u32); - - pub fn ext_scratch_size() -> u32; - pub fn ext_scratch_read(dst_ptr: u32, offset: u32, len: u32); - pub fn ext_scratch_write(src_ptr: u32, len: u32); - - pub fn ext_caller(); - pub fn ext_block_number(); - pub fn ext_address(); - pub fn ext_balance(); - pub fn ext_gas_price(); - pub fn ext_gas_left(); - pub fn ext_value_transferred(); - pub fn ext_now(); - pub fn ext_rent_allowance(); - pub fn ext_minimum_balance(); - - pub fn ext_set_rent_allowance(value_ptr: u32, value_len: u32); - - pub fn ext_random_seed(subject_ptr: u32, subject_len: u32); - pub fn ext_println(str_ptr: u32, str_len: u32); - } -} diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs new file mode 100644 index 00000000000..4cf9321779c --- /dev/null +++ b/core/src/env3/engine/on_chain/impls.rs @@ -0,0 +1,334 @@ +// Copyright 2019-2020 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::{ + ext, + EnvInstance, +}; +use crate::{ + env3::{ + call::{ + CallData, + CallParams, + InstantiateParams, + ReturnType, + }, + Env, + EnvTypes, + Result, + Topics, + TypedEnv, + }, + storage::Key, +}; + +impl EnvInstance { + /// Empties the contract-side scratch buffer. + /// + /// # Note + /// + /// This is useful to perform before invoking a series of + /// [`WasmEnv::append_encode_into_buffer`]. + fn reset_buffer(&mut self) { + self.buffer.clear(); + } + + /// Resizes the amount of used bytes of the internal buffer. + fn resize_buffer(&mut self, new_len: usize) { + self.buffer.resize(new_len); + } + + /// Reads the current scratch buffer into the contract-side buffer. + /// + /// Returns the amount of bytes read. + fn read_scratch_buffer(&mut self) -> usize { + let req_len = ext::scratch_size(); + self.resize_buffer(req_len); + ext::scratch_read(&mut self.buffer[0..req_len], 0); + req_len + } + + /// Reads from the scratch buffer and directly decodes into a value of `T`. + /// + /// # Errors + /// + /// If the decoding into a value of `T` failed. + fn decode_scratch_buffer(&mut self) -> Result + where + T: scale::Decode, + { + let req_len = self.read_scratch_buffer(); + scale::Decode::decode(&mut &self.buffer[0..req_len]).map_err(Into::into) + } + + /// Encodes the value into the contract-side scratch buffer. + fn encode_into_buffer(&mut self, value: T) + where + T: scale::Encode, + { + self.reset_buffer(); + scale::Encode::encode_to(&value, &mut self.buffer); + } + + /// Appends the encoded value into the contract-side scratch buffer + /// and returns the byte ranges into the encoded region. + fn append_encode_into_buffer(&mut self, value: T) -> core::ops::Range + where + T: scale::Encode, + { + let start = self.buffer.len(); + scale::Encode::encode_to(&value, &mut self.buffer); + let end = self.buffer.len(); + core::ops::Range { start, end } + } + + /// Returns the contract property value. + fn get_property(&mut self, ext_fn: fn()) -> Result + where + T: scale::Decode, + { + ext_fn(); + self.decode_scratch_buffer().map_err(Into::into) + } + + /// Reusable implementation for invoking another contract message. + fn invoke_contract_impl( + &mut self, + call_params: &CallParams, + ) -> Result<()> + where + T: EnvTypes, + { + // Reset the contract-side buffer to append onto clean slate. + self.reset_buffer(); + // Append the encoded `call_data`, `endowment` and `call_data` + // in order and remember their encoded regions within the buffer. + let callee = self.append_encode_into_buffer(call_params.callee()); + let endowment = self.append_encode_into_buffer(call_params.transferred_value()); + let call_data = self.append_encode_into_buffer(call_params.input_data()); + // Resolve the encoded regions into actual byte slices. + let callee = &self.buffer[callee]; + let endowment = &self.buffer[endowment]; + let call_data = &self.buffer[call_data]; + // Perform the actual contract call. + ext::call(callee, call_params.gas_limit(), endowment, call_data) + } +} + +impl Env for EnvInstance { + fn set_contract_storage(&mut self, key: Key, value: &V) + where + V: scale::Encode, + { + self.encode_into_buffer(value); + ext::set_storage(key.as_bytes(), &self.buffer[..]); + } + + fn get_contract_storage(&mut self, key: Key) -> Result + where + R: scale::Decode, + { + ext::get_storage(key.as_bytes())?; + self.decode_scratch_buffer().map_err(Into::into) + } + + fn clear_contract_storage(&mut self, key: Key) { + ext::clear_storage(key.as_bytes()) + } + + fn get_runtime_storage(&mut self, runtime_key: &[u8]) -> Option> + where + R: scale::Decode, + { + if ext::get_runtime_storage(runtime_key).is_err() { + return None + } + Some(self.decode_scratch_buffer().map_err(Into::into)) + } + + fn input(&mut self) -> Result { + self.get_property::(|| ()) + } + + fn output(&mut self, return_value: &R) + where + R: scale::Encode, + { + self.encode_into_buffer(return_value); + ext::scratch_write(&self.buffer[..]); + } + + fn println(&mut self, content: &str) { + ext::println(content) + } +} + +impl TypedEnv for EnvInstance { + fn caller(&mut self) -> Result { + self.get_property::(ext::caller) + } + + fn transferred_balance(&mut self) -> Result { + self.get_property::(ext::value_transferred) + } + + fn gas_price(&mut self) -> Result { + self.get_property::(ext::gas_price) + } + + fn gas_left(&mut self) -> Result { + self.get_property::(ext::gas_left) + } + + fn block_timestamp(&mut self) -> Result { + self.get_property::(ext::now) + } + + fn address(&mut self) -> Result { + self.get_property::(ext::address) + } + + fn balance(&mut self) -> Result { + self.get_property::(ext::balance) + } + + fn rent_allowance(&mut self) -> Result { + self.get_property::(ext::rent_allowance) + } + + fn block_number(&mut self) -> Result { + self.get_property::(ext::block_number) + } + + fn minimum_balance(&mut self) -> Result { + self.get_property::(ext::minimum_balance) + } + + fn tombstone_deposit(&mut self) -> Result { + self.get_property::(ext::tombstone_deposit) + } + + fn emit_event(&mut self, event: Event) + where + T: EnvTypes, + Event: Topics + scale::Encode, + { + // Reset the contract-side buffer to append onto clean slate. + self.reset_buffer(); + // Append the encoded `topics` and the raw encoded `data` + // in order and remember their encoded regions within the buffer. + let topics = self.append_encode_into_buffer(event.topics()); + let data = self.append_encode_into_buffer(event); + // Resolve the encoded regions into actual byte slices. + let topics = &self.buffer[topics]; + let data = &self.buffer[data]; + // Do the actual depositing of the event. + ext::deposit_event(topics, data); + } + + fn set_rent_allowance(&mut self, new_value: T::Balance) + where + T: EnvTypes, + { + self.encode_into_buffer(&new_value); + ext::set_rent_allowance(&self.buffer[..]) + } + + fn invoke_runtime(&mut self, call: &T::Call) -> Result<()> + where + T: EnvTypes, + { + self.encode_into_buffer(call); + ext::dispatch_call(&self.buffer[..]); + Ok(()) + } + + fn invoke_contract(&mut self, call_params: &CallParams) -> Result<()> + where + T: EnvTypes, + { + self.invoke_contract_impl(call_params) + } + + fn eval_contract( + &mut self, + call_params: &CallParams>, + ) -> Result + where + T: EnvTypes, + R: scale::Decode, + { + self.invoke_contract_impl(call_params)?; + self.decode_scratch_buffer().map_err(Into::into) + } + + fn create_contract( + &mut self, + params: &InstantiateParams, + ) -> Result + where + T: EnvTypes, + { + // Reset the contract-side buffer to append onto clean slate. + self.reset_buffer(); + // Append the encoded `code_hash`, `endowment` and `create_data` + // in order and remember their encoded regions within the buffer. + let code_hash = self.append_encode_into_buffer(params.code_hash()); + let endowment = self.append_encode_into_buffer(params.endowment()); + let create_data = self.append_encode_into_buffer(params.input_data()); + // Resolve the encoded regions into actual byte slices. + let code_hash = &self.buffer[code_hash]; + let endowment = &self.buffer[endowment]; + let create_data = &self.buffer[create_data]; + // Do the actual contract instantiation. + ext::create(code_hash, params.gas_limit(), endowment, create_data)?; + // At this point our contract instantiation was successful + // and we can now fetch the returned data and decode it for + // the result value. + self.decode_scratch_buffer().map_err(Into::into) + } + + fn restore_contract( + &mut self, + account_id: T::AccountId, + code_hash: T::Hash, + rent_allowance: T::Balance, + filtered_keys: &[Key], + ) where + T: EnvTypes, + { + // Reset the contract-side buffer to append onto clean slate. + self.reset_buffer(); + // Append the encoded `account_id`, `code_hash` and `rent_allowance` + // and `filtered_keys` in order and remember their encoded regions + // within the buffer. + let account_id = self.append_encode_into_buffer(account_id); + let code_hash = self.append_encode_into_buffer(code_hash); + let rent_allowance = self.append_encode_into_buffer(rent_allowance); + // Resolve the encoded regions into actual byte slices. + let account_id = &self.buffer[account_id]; + let code_hash = &self.buffer[code_hash]; + let rent_allowance = &self.buffer[rent_allowance]; + // Perform the actual contract restoration. + ext::restore_to(account_id, code_hash, rent_allowance, filtered_keys); + } + + fn random(&mut self, subject: &[u8]) -> Result + where + T: EnvTypes, + { + ext::random_seed(subject); + self.decode_scratch_buffer().map_err(Into::into) + } +} diff --git a/core/src/env3/engine/on_chain/mod.rs b/core/src/env3/engine/on_chain/mod.rs new file mode 100644 index 00000000000..c97706c2858 --- /dev/null +++ b/core/src/env3/engine/on_chain/mod.rs @@ -0,0 +1,47 @@ +// Copyright 2019-2020 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 buffer; +mod ext; +mod impls; + +use super::OnInstance; + +use self::buffer::StaticBuffer; + +/// The on-chain environment. +pub struct EnvInstance { + /// Encode & decode buffer with static size of 16kB. + /// + /// If operations require more than that they will fail. + /// This limit was chosen after benchmarking Substrate storage + /// storage and load performance and was found to be a sweet spot. + /// + /// Please note that this is still an implementation detail and + /// might change. Users should generally avoid storing too big values + /// into single storage entries. + buffer: StaticBuffer, +} + +impl OnInstance for EnvInstance { + fn on_instance(f: F) -> R + where + F: FnOnce(&mut Self) -> R, + { + static mut INSTANCE: EnvInstance = EnvInstance { + buffer: StaticBuffer::new(), + }; + f(unsafe { &mut INSTANCE }) + } +} diff --git a/core/src/env3/error.rs b/core/src/env3/error.rs new file mode 100644 index 00000000000..daa841da458 --- /dev/null +++ b/core/src/env3/error.rs @@ -0,0 +1,45 @@ +// Copyright 2019-2020 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 derive_more::From; + +#[cfg(any(feature = "std", test, doc))] +use crate::env3::engine::off_chain::OffChainError; + +/// Errors that can be encountered upon environmental interaction. +#[derive(Debug, From, PartialEq, Eq)] +pub enum EnvError { + /// Error upon decoding an encoded value. + Decode(scale::Error), + /// An error that can only occure in the off-chain environment. + #[cfg(any(feature = "std", test, doc))] + OffChain(OffChainError), + /// The call to another contract has trapped. + ContractCallTrapped, + /// A called contract returned a custom error code. + #[from(ignore)] + ContractCallFailState(u8), + /// The instantiation of another contract has trapped. + ContractInstantiationTrapped, + /// The instantiated contract returned a custom error code. + #[from(ignore)] + ContractInstantiationFailState(u8), + /// The queried runtime storage entry is missing. + MissingRuntimeStorageEntry, + /// The queried contract storage entry is missing. + MissingContractStorageEntry, +} + +/// A result of environmental operations. +pub type Result = core::result::Result; diff --git a/core/src/env3/mod.rs b/core/src/env3/mod.rs new file mode 100644 index 00000000000..38ee67dbf61 --- /dev/null +++ b/core/src/env3/mod.rs @@ -0,0 +1,49 @@ +// Copyright 2019-2020 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. + +//! Environmental interface. (version 3) +//! +//! This is the interface with which a smart contract is able to communicate +//! with the outside world through its sandbox boundaries. + +mod api; +mod backend; +pub mod call; +mod engine; +mod error; +mod types; + +#[cfg(any(feature = "std", test, doc))] +#[doc(inline)] +pub use self::engine::off_chain::test_api as test; + +use self::backend::{ + Env, + TypedEnv, +}; +pub use self::{ + api::*, + error::{ + EnvError, + Result, + }, + types::{ + AccountId, + Clear, + DefaultEnvTypes, + EnvTypes, + Hash, + Topics, + }, +}; diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs new file mode 100644 index 00000000000..51765477dc6 --- /dev/null +++ b/core/src/env3/types.rs @@ -0,0 +1,336 @@ +// Copyright 2019-2020 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. + +//! Types for the default environment. +//! +//! These are simple mirrored types from the default SRML configuration. +//! Their interfaces and functionality might not be complete. +//! +//! Users are required to provide their own type definitions and `EnvTypes` +//! implementations in order to write ink! contracts for other chain configurations. + +use crate::storage::Flush; +use core::{ + array::TryFromSliceError, + convert::TryFrom, +}; +use derive_more::From; +use ink_prelude::vec::Vec; +use scale::{ + Decode, + Encode, +}; +#[cfg(feature = "ink-generate-abi")] +use type_metadata::Metadata; + +use core::ops::{ + Add, + AddAssign, + Div, + DivAssign, + Mul, + MulAssign, + Sub, + SubAssign, +}; +use num_traits::{ + Bounded, + One, + Zero, +}; + +/// Types that allow for simple arithmetic operations. +/// +/// Subset of all trait bounds copied over from what Substrate defines +/// for its `SimpleArithmetic` types. We can extend this in the future +/// if needed. +pub trait SimpleArithmetic: + Sized + + From + + Bounded + + Ord + + PartialOrd + + Zero + + One + + Bounded + + Add + + AddAssign + + Sub + + SubAssign + + Mul + + MulAssign + + Div + + DivAssign +// Further trait bounds from the original SimpleArithmetic trait +// that we could use to extend ink!'s SimpleArithmetic trait. +// +// From + +// From + +// From + +// TryFrom + +// TryFrom + +// TryFrom + +// TryInto + +// TryInto + +// TryInto + +// TryInto + +// TryInto + +// TryInto + +// UniqueSaturatedInto + +// UniqueSaturatedInto + +// UniqueSaturatedInto + +// UniqueSaturatedInto + +// UniqueSaturatedInto + +// UniqueSaturatedFrom + +// UniqueSaturatedFrom + +// Shl + +// Shr + +// CheckedAdd + +// CheckedSub + +// CheckedMul + +// CheckedDiv + +// CheckedShl + +// CheckedShr + +// IntegerSquareRoot + +// Saturating + +{ +} + +impl SimpleArithmetic for T where + T: Sized + + From + + Bounded + + Ord + + PartialOrd + + Zero + + One + + Add + + AddAssign + + Sub + + SubAssign + + Mul + + MulAssign + + Div + + DivAssign +{ +} + +/// The environmental types usable by contracts defined with ink!. +pub trait EnvTypes { + /// The type of an address. + type AccountId: 'static + scale::Codec + Clone + PartialEq + Eq + Ord; + /// The type of balances. + type Balance: 'static + + scale::Codec + + Copy + + Clone + + PartialEq + + Eq + + SimpleArithmetic; + /// The type of hash. + type Hash: 'static + + scale::Codec + + Copy + + Clone + + Clear + + PartialEq + + Eq + + Ord + + AsRef<[u8]> + + AsMut<[u8]>; + /// The type of timestamps. + type TimeStamp: 'static + + scale::Codec + + Copy + + Clone + + PartialEq + + Eq + + SimpleArithmetic; + /// The type of block number. + type BlockNumber: 'static + + scale::Codec + + Copy + + Clone + + PartialEq + + Eq + + SimpleArithmetic; + /// The type of a call into the runtime + type Call: 'static + scale::Codec; +} + +/// Implemented by event types to communicate their topic hashes. +pub trait Topics +where + T: EnvTypes, +{ + /// Returns the topic hashes of `self`. + fn topics(&self) -> &'static [::Hash]; +} + +/// The fundamental types of the default configuration. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ink-generate-abi", derive(Metadata))] +pub enum DefaultEnvTypes {} + +impl EnvTypes for DefaultEnvTypes { + type AccountId = AccountId; + type Balance = Balance; + type Hash = Hash; + type TimeStamp = TimeStamp; + type BlockNumber = BlockNumber; + type Call = Call; +} + +/// The default balance type. +pub type Balance = u128; + +/// The default time stamp type. +pub type TimeStamp = u64; + +/// The default block number type. +pub type BlockNumber = u64; + +/// This call type guarantees to never be constructed. +/// +/// This has the effect that users of the default SRML types are +/// not able to call back into the runtime. +/// This operation is generally unsupported because of the currently +/// implied additional overhead. +/// +/// # Note +/// +/// A user defined `Call` type is required for calling into the runtime. +/// For more info visit: https://github.com/paritytech/ink-types-node-runtime +#[derive(Debug)] +pub enum Call {} + +impl Encode for Call { + fn encode(&self) -> Vec { + // The implementation enforces at runtime that `Encode` is not called + // for the default SRML `Call` type but for performance reasons this check + // is removed for the on-chain (release mode) version. + debug_assert!(false, "cannot encode default SRML `Call` type"); + Vec::new() + } +} + +impl scale::Decode for Call { + fn decode(_value: &mut I) -> Result { + // This implementation is only to satisfy the Decode constraint in the + // test environment. Since Call cannot be constructed then just return + // None, but this should never be called. + Err("The default SRML `Call` type cannot be used for runtime calls".into()) + } +} + +/// The default environment `AccountId` type. +/// +/// # Note +/// +/// This is a mirror of the `AccountId` type used in the default configuration +/// of PALLET contracts. +#[derive( + Debug, + Copy, + Clone, + PartialEq, + Eq, + Ord, + PartialOrd, + Hash, + Encode, + Decode, + From, + Default, +)] +#[cfg_attr(feature = "ink-generate-abi", derive(Metadata))] +pub struct AccountId([u8; 32]); + +impl<'a> TryFrom<&'a [u8]> for AccountId { + type Error = TryFromSliceError; + + fn try_from(bytes: &'a [u8]) -> Result { + let address = <[u8; 32]>::try_from(bytes)?; + Ok(Self(address)) + } +} + +impl Flush for AccountId {} + +/// The default environment `Hash` type. +/// +/// # Note +/// +/// This is a mirror of the `Hash` type used in the default configuration +/// of PALLET contracts. +#[derive( + Debug, + Copy, + Clone, + PartialEq, + Eq, + Ord, + PartialOrd, + Hash, + Encode, + Decode, + From, + Default, +)] +#[cfg_attr(feature = "ink-generate-abi", derive(Metadata))] +pub struct Hash([u8; 32]); + +impl<'a> TryFrom<&'a [u8]> for Hash { + type Error = TryFromSliceError; + + fn try_from(bytes: &'a [u8]) -> Result { + let address = <[u8; 32]>::try_from(bytes)?; + Ok(Self(address)) + } +} + +impl AsRef<[u8]> for Hash { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl AsMut<[u8]> for Hash { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0[..] + } +} + +/// The equivalent of `Zero` for hashes. +/// +/// A hash that consists only of 0 bits is clear. +pub trait Clear { + /// Returns `true` if the hash is clear. + fn is_clear(&self) -> bool; + + /// Returns a clear hash. + fn clear() -> Self; +} + +impl Clear for Hash { + fn is_clear(&self) -> bool { + self.as_ref().iter().all(|&byte| byte == 0x00) + } + + fn clear() -> Self { + Self([0x00; 32]) + } +} + +impl Flush for Hash {} diff --git a/core/src/lib.rs b/core/src/lib.rs index d685d5ca80b..13198cd8f48 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -50,8 +50,7 @@ extern crate ink_alloc; mod test_utils; mod byte_utils; -pub mod env; -pub mod env2; +pub mod env3; pub mod storage; // Needed for derive macros of `core/derive` sub crate. diff --git a/core/src/storage/cell/mod.rs b/core/src/storage/cell/mod.rs index 48e99de1890..47d66c9e904 100644 --- a/core/src/storage/cell/mod.rs +++ b/core/src/storage/cell/mod.rs @@ -14,12 +14,10 @@ //! Provides low-level primitive cell types. -mod raw_cell; mod sync_cell; mod typed_cell; pub use self::{ - raw_cell::RawCell, sync_cell::SyncCell, typed_cell::TypedCell, }; diff --git a/core/src/storage/cell/raw_cell.rs b/core/src/storage/cell/raw_cell.rs deleted file mode 100644 index 0fd548487b9..00000000000 --- a/core/src/storage/cell/raw_cell.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2018-2019 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::{ - env, - storage::{ - alloc::{ - Allocate, - AllocateUsing, - }, - Key, - NonCloneMarker, - }, -}; -use ink_prelude::vec::Vec; -use scale::{ - Decode, - Encode, -}; - -/// A raw cell. -/// -/// Provides uninterpreted and unformatted access to the associated contract storage slot. -/// -/// # Guarantees -/// -/// - `Owned` -/// -/// Read more about kinds of guarantees and their effect [here](../index.html#guarantees). -#[derive(Debug, PartialEq, Eq, Hash, Encode, Decode)] -pub struct RawCell { - /// The key to the associated contract storage slot. - key: Key, - /// Marker that prevents this type from being `Copy` or `Clone` by accident. - non_clone: NonCloneMarker<()>, -} - -impl AllocateUsing for RawCell { - #[inline] - unsafe fn allocate_using(alloc: &mut A) -> Self - where - A: Allocate, - { - Self { - key: alloc.alloc(1), - non_clone: NonCloneMarker::default(), - } - } -} - -impl RawCell { - /// Loads the bytes stored in the cell if not empty. - pub fn load(&self) -> Option> { - unsafe { env::load(self.key) } - } - - /// Stores the given bytes into the cell. - pub fn store(&mut self, bytes: &[u8]) { - unsafe { env::store(self.key, bytes) } - } - - /// Removes the bytes stored in the cell. - pub fn clear(&mut self) { - unsafe { env::clear(self.key) } - } - - /// Returns the associated, internal raw key. - pub fn raw_key(&self) -> Key { - self.key - } -} - -#[cfg(all(test, feature = "test-env"))] -mod tests { - use super::*; - - use crate::{ - storage::alloc::{ - AllocateUsing, - BumpAlloc, - }, - test_utils::run_test, - }; - - fn instantiate() -> RawCell { - unsafe { - let mut alloc = BumpAlloc::from_raw_parts(Key([0x0; 32])); - RawCell::allocate_using(&mut alloc) - } - } - - #[test] - fn simple() { - run_test(|| { - let mut cell = instantiate(); - assert_eq!(cell.load(), None); - cell.store(b"Hello, World!"); - assert_eq!(cell.load(), Some(b"Hello, World!".to_vec())); - cell.clear(); - assert_eq!(cell.load(), None); - }) - } - - #[test] - fn count_reads() { - run_test(|| { - let cell = instantiate(); - assert_eq!(env::test::total_reads(), 0); - cell.load(); - assert_eq!(env::test::total_reads(), 1); - cell.load(); - cell.load(); - assert_eq!(env::test::total_reads(), 3); - }) - } - - #[test] - fn count_writes() { - run_test(|| { - let mut cell = instantiate(); - assert_eq!(env::test::total_writes(), 0); - cell.store(b"a"); - assert_eq!(env::test::total_writes(), 1); - cell.store(b"b"); - cell.store(b"c"); - assert_eq!(env::test::total_writes(), 3); - }) - } -} diff --git a/core/src/storage/cell/sync_cell.rs b/core/src/storage/cell/sync_cell.rs index 640cfe688fb..a02d4303ac0 100644 --- a/core/src/storage/cell/sync_cell.rs +++ b/core/src/storage/cell/sync_cell.rs @@ -375,7 +375,7 @@ impl SyncCell { /// Returns the associated, internal raw key. pub fn raw_key(&self) -> Key { - self.cell.raw_key() + self.cell.key() } } @@ -437,14 +437,13 @@ where #[cfg(all(test, feature = "test-env"))] mod tests { use super::*; - use crate::env; - use crate::{ + env3 as env, + env3::Result, storage::{ alloc::BumpAlloc, Key, }, - test_utils::run_test, }; fn dummy_cell() -> SyncCell { @@ -455,8 +454,8 @@ mod tests { } #[test] - fn simple() { - run_test(|| { + fn simple() -> Result<()> { + env::test::run_test::(|_| { let mut cell = dummy_cell(); assert_eq!(cell.get(), None); cell.set(5); @@ -465,119 +464,187 @@ mod tests { assert_eq!(cell.get(), Some(&15)); cell.clear(); assert_eq!(cell.get(), None); + Ok(()) }) } #[test] - fn multi_session_simulation() { - let mut cell1 = dummy_cell(); - cell1.set(42); - assert_eq!(cell1.get(), Some(&42)); - // Using same key as `cell1` - // -> overlapping access but different caches - // Cache has not yet been synced: - assert_eq!(dummy_cell().get(), None); - // Sync cache now! - cell1.flush(); - // Using same key as `cell1` - // -> overlapping access but different caches - // Cache has been flushed before: - assert_eq!(dummy_cell().get(), Some(&42)); + fn multi_session_simulation() -> Result<()> { + env::test::run_test::(|_| { + let mut cell1 = dummy_cell(); + cell1.set(42); + assert_eq!(cell1.get(), Some(&42)); + // Using same key as `cell1` + // -> overlapping access but different caches + // Cache has not yet been synced: + assert_eq!(dummy_cell().get(), None); + // Sync cache now! + cell1.flush(); + // Using same key as `cell1` + // -> overlapping access but different caches + // Cache has been flushed before: + assert_eq!(dummy_cell().get(), Some(&42)); + Ok(()) + }) } #[test] - fn count_rw_get() { - // Repetitions performed. - const N: u32 = 5; - - let mut cell = dummy_cell(); - - // Asserts initial reads and writes are zero. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); + fn count_rw_get() -> Result<()> { + env::test::run_test::(|_| { + // Repetitions performed. + const N: u32 = 5; - // Repeated reads on the same cell. - for _i in 0..N { - cell.get(); - assert_eq!(env::test::total_reads(), 1); - assert_eq!(env::test::total_writes(), 0); - } + let mut cell = dummy_cell(); + let contract_account_id = env::address::()?; + + // Asserts initial reads and writes are zero. + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (0, 0) + ); + + // Repeated reads on the same cell. + for _i in 0..N { + cell.get(); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (1, 0) + ); + } - // Flush the cell and assert reads and writes. - cell.flush(); - assert_eq!(env::test::total_reads(), 1); - assert_eq!(env::test::total_writes(), 0); + // Flush the cell and assert reads and writes. + cell.flush(); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (1, 0) + ); + Ok(()) + }) } #[test] - fn count_rw_get_mut() { - // Repetitions performed. - const N: u32 = 5; - - let mut cell = dummy_cell(); + fn count_rw_get_mut() -> Result<()> { + env::test::run_test::(|_| { + // Repetitions performed. + const N: u32 = 5; - // Asserts initial reads and writes are zero. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); - - // Repeated mutable reads on the same cell. - for _i in 0..N { - cell.get_mut(); - assert_eq!(env::test::total_reads(), 1); - assert_eq!(env::test::total_writes(), 0); - } + let mut cell = dummy_cell(); + let contract_account_id = env::address::()?; + + // Asserts initial reads and writes are zero. + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (0, 0) + ); + + // Repeated mutable reads on the same cell. + for _i in 0..N { + cell.get_mut(); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (1, 0) + ); + } - // Flush the cell and assert reads and writes. - cell.flush(); - assert_eq!(env::test::total_reads(), 1); - assert_eq!(env::test::total_writes(), 1); + // Flush the cell and assert reads and writes. + cell.flush(); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (1, 1) + ); + Ok(()) + }) } #[test] - fn count_rw_set() { - // Repetitions performed. - const N: u32 = 5; + fn count_rw_set() -> Result<()> { + env::test::run_test::(|_| { + // Repetitions performed. + const N: u32 = 5; - let mut cell = dummy_cell(); - - // Asserts initial reads and writes are zero. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); - - // Repeated writes to the same cell. - for _i in 0..N { - cell.set(42); - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); - } + let mut cell = dummy_cell(); + let contract_account_id = env::address::()?; + + // Asserts initial reads and writes are zero. + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (0, 0) + ); + + // Repeated writes to the same cell. + for _i in 0..N { + cell.set(42); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (0, 0) + ); + } - // Flush the cell and assert reads and writes. - cell.flush(); - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 1); + // Flush the cell and assert reads and writes. + cell.flush(); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (0, 1) + ); + Ok(()) + }) } #[test] - fn count_rw_clear() { - // Repetitions performed. - const N: u32 = 5; - - let mut cell = dummy_cell(); + fn count_rw_clear() -> Result<()> { + env::test::run_test::(|_| { + // Repetitions performed. + const N: u32 = 5; - // Asserts initial reads and writes are zero. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); - - // Repeated writes to the same cell. - for _i in 0..N { - cell.clear(); - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); - } + let mut cell = dummy_cell(); + let contract_account_id = env::address::()?; + + // Asserts initial reads and writes are zero. + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (0, 0) + ); + + // Repeated writes to the same cell. + for _i in 0..N { + cell.clear(); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (0, 0) + ); + } - // Flush the cell and assert reads and writes. - cell.flush(); - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 1); + // Flush the cell and assert reads and writes. + cell.flush(); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (0, 1) + ); + Ok(()) + }) } } diff --git a/core/src/storage/cell/typed_cell.rs b/core/src/storage/cell/typed_cell.rs index 20d7c40025e..fbf0d729013 100644 --- a/core/src/storage/cell/typed_cell.rs +++ b/core/src/storage/cell/typed_cell.rs @@ -12,15 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::storage::{ - alloc::{ - Allocate, - AllocateUsing, +use crate::{ + env3 as env, + storage::{ + alloc::{ + Allocate, + AllocateUsing, + }, + Key, }, - cell::RawCell, - Key, - NonCloneMarker, }; +use core::marker::PhantomData; /// A typed cell. /// @@ -32,29 +34,12 @@ use crate::storage::{ /// - `Typed` /// /// Read more about kinds of guarantees and their effect [here](../index.html#guarantees). -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(Debug, PartialEq, Eq, Hash, scale::Encode, scale::Decode)] pub struct TypedCell { - /// The associated raw cell. - cell: RawCell, - /// Marker that prevents this type from being `Copy` or `Clone` by accident. - non_clone: NonCloneMarker, -} - -impl scale::Encode for TypedCell { - fn encode_to(&self, dest: &mut W) { - self.cell.encode_to(dest) - } -} - -impl scale::Decode for TypedCell { - fn decode(input: &mut I) -> Result { - RawCell::decode(input).map(|raw_cell| { - Self { - cell: raw_cell, - non_clone: NonCloneMarker::default(), - } - }) - } + /// The associated storage key. + key: Key, + /// Marker to trick the Rust compiler. + marker: PhantomData T>, } impl AllocateUsing for TypedCell { @@ -64,8 +49,8 @@ impl AllocateUsing for TypedCell { A: Allocate, { Self { - cell: RawCell::allocate_using(alloc), - non_clone: Default::default(), + key: alloc.alloc(1), + marker: Default::default(), } } } @@ -73,12 +58,12 @@ impl AllocateUsing for TypedCell { impl TypedCell { /// Removes the value stored in the cell. pub fn clear(&mut self) { - self.cell.clear() + env::clear_contract_storage(self.key); } /// Returns the associated, internal raw key. - pub fn raw_key(&self) -> Key { - self.cell.raw_key() + pub fn key(&self) -> Key { + self.key } } @@ -88,12 +73,7 @@ where { /// Loads the value stored in the cell if any. pub fn load(&self) -> Option { - self.cell.load().map(|bytes| { - T::decode(&mut &bytes[..]).expect( - "[ink_core::TypedCell::load] Error: \ - failed upon decoding", - ) - }) + env::get_contract_storage::(self.key).ok() } } @@ -102,25 +82,21 @@ where T: scale::Encode, { /// Stores the value into the cell. - pub fn store(&mut self, val: &T) { - self.cell.store(&T::encode(&val)) + pub fn store(&mut self, new_value: &T) { + env::set_contract_storage::(self.key, &new_value) } } #[cfg(all(test, feature = "test-env"))] mod tests { use super::*; - use crate::{ - env, - storage::Key, - }; use crate::{ + env3::Result, storage::alloc::{ AllocateUsing, BumpAlloc, }, - test_utils::run_test, }; fn dummy_cell() -> TypedCell { @@ -131,40 +107,75 @@ mod tests { } #[test] - fn simple() { - run_test(|| { + fn simple() -> Result<()> { + env::test::run_test::(|_| { let mut cell = dummy_cell(); assert_eq!(cell.load(), None); cell.store(&5); assert_eq!(cell.load(), Some(5)); cell.clear(); assert_eq!(cell.load(), None); + Ok(()) }) } #[test] - fn count_reads() { - run_test(|| { + fn count_reads() -> Result<()> { + env::test::run_test::(|_| { let cell = dummy_cell(); - assert_eq!(env::test::total_reads(), 0); + let contract_account_id = env::address::()?; + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (0, 0) + ); cell.load(); - assert_eq!(env::test::total_reads(), 1); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (1, 0) + ); cell.load(); cell.load(); - assert_eq!(env::test::total_reads(), 3); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (3, 0) + ); + Ok(()) }) } #[test] - fn count_writes() { - run_test(|| { + fn count_writes() -> Result<()> { + env::test::run_test::(|_| { let mut cell = dummy_cell(); - assert_eq!(env::test::total_writes(), 0); + let contract_account_id = env::address::()?; + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (0, 0) + ); cell.store(&1); - assert_eq!(env::test::total_writes(), 1); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (0, 1) + ); cell.store(&2); cell.store(&3); - assert_eq!(env::test::total_writes(), 3); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (0, 3) + ); + Ok(()) }) } } diff --git a/core/src/storage/chunk/mod.rs b/core/src/storage/chunk/mod.rs index 5b5374dc9c3..856cc2d0bf2 100644 --- a/core/src/storage/chunk/mod.rs +++ b/core/src/storage/chunk/mod.rs @@ -14,14 +14,10 @@ //! Provides low-level primitives to operate on chunks of cells. -mod raw_chunk; mod sync_chunk; mod typed_chunk; -pub(crate) use self::raw_chunk::RawChunkCell; - pub use self::{ - raw_chunk::RawChunk, sync_chunk::SyncChunk, typed_chunk::TypedChunk, }; diff --git a/core/src/storage/chunk/raw_chunk.rs b/core/src/storage/chunk/raw_chunk.rs deleted file mode 100644 index cbafe82ca38..00000000000 --- a/core/src/storage/chunk/raw_chunk.rs +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2018-2019 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::{ - env, - storage::{ - alloc::{ - Allocate, - AllocateUsing, - }, - Key, - NonCloneMarker, - }, -}; -use ink_prelude::vec::Vec; - -/// A chunk of raw cells. -/// -/// Provides uninterpreted and unformatted access with offset -/// to the associated contract storage slot. -/// -/// # Guarantees -/// -/// - `Owned` -/// -/// Read more about kinds of guarantees and their effect [here](../index.html#guarantees). -#[derive(Debug, PartialEq, Eq)] -pub struct RawChunk { - /// The key to the associated contract storage slot. - key: Key, - /// Marker that prevents this type from being `Copy` or `Clone` by accident. - non_clone: NonCloneMarker<()>, -} - -/// A single cell within a chunk of raw cells. -#[derive(Debug, PartialEq, Eq)] -pub struct RawChunkCell<'a> { - /// The key to the corresponding cell within the raw chunk. - key: Key, - /// Marker that prevents this type from being `Copy` or `Clone` by accident. - non_clone: NonCloneMarker<&'a mut ()>, -} - -impl RawChunkCell<'_> { - /// Creates a new raw chunk cell from the given key. - /// - /// # Safety - /// - /// This is unsafe since it doesn't check aliasing of cells. - pub(self) unsafe fn new_unchecked(key: Key) -> Self { - Self { - key, - non_clone: NonCloneMarker::default(), - } - } - - /// Store the bytes into the cell. - pub fn store(&mut self, bytes: &[u8]) { - unsafe { env::store(self.key, bytes) } - } - - /// Remove the bytes stored in the cell. - pub fn clear(&mut self) { - unsafe { env::clear(self.key) } - } -} - -impl scale::Encode for RawChunk { - fn encode_to(&self, dest: &mut W) { - self.key.encode_to(dest) - } -} - -impl scale::Decode for RawChunk { - fn decode(input: &mut I) -> Result { - Key::decode(input).map(|key| unsafe { Self::new_unchecked(key) }) - } -} - -impl AllocateUsing for RawChunk { - unsafe fn allocate_using(alloc: &mut A) -> Self - where - A: Allocate, - { - Self::new_unchecked(alloc.alloc(u32::max_value().into())) - } -} - -impl RawChunk { - /// Creates a new raw cell chunk for the given key and capacity. - /// - /// # Safety - /// - /// This is unsafe because ... - /// - ... it does not check if the associated - /// contract storage does not alias with other accesses. - /// - ... it does not check if given capacity is non zero. - unsafe fn new_unchecked(key: Key) -> Self { - Self { - key, - non_clone: Default::default(), - } - } - - /// Returns the underlying key to the cells. - /// - /// # Note - /// - /// This is a low-level utility getter and should - /// normally not be required by users. - pub fn cells_key(&self) -> Key { - self.key - } - - /// Returns a key for the `n`-th cell if within bounds. - /// - /// # Error - /// - /// Returns an error if `n` is not within bounds. - fn offset_key(&self, n: u32) -> Key { - self.key + n - } - - /// Returns an accessor to the `n`-th cell. - pub(crate) fn cell_at(&mut self, n: u32) -> RawChunkCell { - unsafe { RawChunkCell::new_unchecked(self.offset_key(n)) } - } - - /// Loads the bytes stored in the `n`-th cell. - pub fn load(&self, n: u32) -> Option> { - unsafe { env::load(self.key + n) } - } - - /// Stores the given bytes into the `n`-th cell. - pub fn store(&mut self, n: u32, bytes: &[u8]) { - self.cell_at(n).store(bytes) - } - - /// Removes the bytes stored in the `n`-th cell. - pub fn clear(&mut self, n: u32) { - self.cell_at(n).clear() - } -} - -#[cfg(all(test, feature = "test-env"))] -mod tests { - use super::*; - - use crate::test_utils::run_test; - - #[test] - fn simple() { - run_test(|| { - const TEST_LEN: u32 = 5; - const WORD_SIZE: usize = 4; - - let mut chunk = unsafe { RawChunk::new_unchecked(Key([0x42; 32])) }; - - // Invariants after initialization - for i in 0..TEST_LEN { - assert_eq!(chunk.load(i), None); - } - - // Store some elements - for i in 0..TEST_LEN { - chunk.store(i, &[i as u8; WORD_SIZE]); - assert_eq!(chunk.load(i), Some(vec![i as u8; WORD_SIZE])); - } - - // Clear all elements. - for i in 0..TEST_LEN { - chunk.clear(i); - assert_eq!(chunk.load(i), None); - } - }) - } - - #[test] - fn count_reads_writes() { - run_test(|| { - const TEST_LEN: u32 = 5; - const WORD_SIZE: usize = 4; - - let mut chunk = unsafe { RawChunk::new_unchecked(Key([0x42; 32])) }; - - // Reads and writes after init. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); - - // Loading from all cells. - for i in 0..TEST_LEN { - chunk.load(i); - assert_eq!(env::test::total_reads(), i as u64 + 1); - assert_eq!(env::test::total_writes(), 0); - } - assert_eq!(env::test::total_reads(), TEST_LEN as u64); - assert_eq!(env::test::total_writes(), 0); - - // Writing to all cells. - for i in 0..TEST_LEN { - chunk.store(i, &[i as u8; WORD_SIZE]); - assert_eq!(env::test::total_reads(), TEST_LEN as u64); - assert_eq!(env::test::total_writes(), i as u64 + 1); - } - assert_eq!(env::test::total_reads(), TEST_LEN as u64); - assert_eq!(env::test::total_writes(), TEST_LEN as u64); - - // Loading multiple times from a single cell. - const LOAD_REPEATS: usize = 3; - for n in 0..LOAD_REPEATS { - chunk.load(0); - assert_eq!(env::test::total_reads(), TEST_LEN as u64 + n as u64 + 1); - assert_eq!(env::test::total_writes(), TEST_LEN as u64); - } - assert_eq!( - env::test::total_reads(), - TEST_LEN as u64 + LOAD_REPEATS as u64 - ); - assert_eq!(env::test::total_writes(), TEST_LEN as u64); - - // Storing multiple times to a single cell. - const STORE_REPEATS: usize = 3; - for n in 0..STORE_REPEATS { - chunk.store(0, b"test"); - assert_eq!( - env::test::total_reads(), - TEST_LEN as u64 + LOAD_REPEATS as u64 - ); - assert_eq!(env::test::total_writes(), TEST_LEN as u64 + n as u64 + 1); - } - assert_eq!( - env::test::total_reads(), - TEST_LEN as u64 + LOAD_REPEATS as u64 - ); - assert_eq!( - env::test::total_writes(), - TEST_LEN as u64 + STORE_REPEATS as u64 - ); - }) - } -} diff --git a/core/src/storage/chunk/sync_chunk/chunk.rs b/core/src/storage/chunk/sync_chunk/chunk.rs index eb54b19c3b3..3234c07933d 100644 --- a/core/src/storage/chunk/sync_chunk/chunk.rs +++ b/core/src/storage/chunk/sync_chunk/chunk.rs @@ -139,7 +139,7 @@ impl SyncChunk { /// This is a low-level utility getter and should /// normally not be required by users. pub fn cells_key(&self) -> Key { - self.chunk.cells_key() + self.chunk.key() } } diff --git a/core/src/storage/chunk/sync_chunk/tests.rs b/core/src/storage/chunk/sync_chunk/tests.rs index c2684f5f30c..920852f0383 100644 --- a/core/src/storage/chunk/sync_chunk/tests.rs +++ b/core/src/storage/chunk/sync_chunk/tests.rs @@ -14,7 +14,8 @@ use super::*; use crate::{ - env, + env3 as env, + env3::Result, storage::{ alloc::{ AllocateUsing, @@ -23,7 +24,6 @@ use crate::{ Flush, Key, }, - test_utils::run_test, }; fn dummy_chunk() -> SyncChunk { @@ -34,8 +34,8 @@ fn dummy_chunk() -> SyncChunk { } #[test] -fn simple() { - run_test(|| { +fn simple() -> Result<()> { + env::test::run_test::(|_| { const TEST_LEN: u32 = 5; let mut chunk = dummy_chunk(); @@ -56,12 +56,13 @@ fn simple() { chunk.clear(i); assert_eq!(chunk.get(i), None); } + Ok(()) }) } #[test] -fn take_put() { - run_test(|| { +fn take_put() -> Result<()> { + env::test::run_test::(|_| { let mut chunk = dummy_chunk(); // Take empty cell yields `None` @@ -70,12 +71,13 @@ fn take_put() { assert_eq!(chunk.put(5, 42), None); // Taking now should yield the inserted value assert_eq!(chunk.take(5), Some(42)); + Ok(()) }) } #[test] -fn replace() { - run_test(|| { +fn replace() -> Result<()> { + env::test::run_test::(|_| { let mut chunk = dummy_chunk(); // Replace some with none. @@ -86,12 +88,13 @@ fn replace() { // After clearing it will be none again. chunk.clear(0); assert_eq!(chunk.put(0, 42), None); + Ok(()) }) } #[test] -fn take() { - run_test(|| { +fn take() -> Result<()> { + env::test::run_test::(|_| { let mut chunk = dummy_chunk(); // Remove at none. @@ -109,227 +112,203 @@ fn take() { assert_eq!(chunk.take(0), Some(1337)); // After take returns none again. assert_eq!(chunk.get(0), None); + Ok(()) }) } -#[test] -fn count_rw_get() { - // How many times we read or write from or to cells. - const N: u32 = 5; +/// Returns the current number of total contract storage reads and writes. +fn get_contract_storage_rw() -> (usize, usize) { + let contract_account_id = env::address::().unwrap(); + env::test::get_contract_storage_rw::(&contract_account_id) + .unwrap() +} - let mut chunk = dummy_chunk(); +#[test] +fn count_rw_get() -> Result<()> { + env::test::run_test::(|_| { + // How many times we read or write from or to cells. + const N: u32 = 5; + let mut chunk = dummy_chunk(); - // Assert clean read writes. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); + // Assert clean read writes. + assert_eq!(get_contract_storage_rw(), (0, 0)); - // Loading from all cells. - for i in 0..N { - #[allow(unused_must_use)] - { - chunk.get(i); + // Loading from all cells. + for i in 0..N { + let _ = chunk.get(i); + assert_eq!(get_contract_storage_rw(), (i as usize + 1, 0)); } - assert_eq!(env::test::total_reads(), i as u64 + 1); - assert_eq!(env::test::total_writes(), 0); - } - assert_eq!(env::test::total_reads(), N as u64); - assert_eq!(env::test::total_writes(), 0); + assert_eq!(get_contract_storage_rw(), (N as usize, 0)); - // Flush and check reads and writes. - chunk.flush(); - assert_eq!(env::test::total_reads(), N as u64); - assert_eq!(env::test::total_writes(), 0); + // Flush and check reads and writes. + chunk.flush(); + assert_eq!(get_contract_storage_rw(), (N as usize, 0)); + Ok(()) + }) } #[test] -fn count_rw_get_repeat() { - // How many times we repeat to read from the same cell. - const N: u32 = 5; - - let mut chunk = dummy_chunk(); +fn count_rw_get_repeat() -> Result<()> { + env::test::run_test::(|_| { + // How many times we repeat to read from the same cell. + const N: u32 = 5; + let mut chunk = dummy_chunk(); - // Assert clean read writes. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); + // Assert clean read writes. + assert_eq!(get_contract_storage_rw(), (0, 0)); - // Loading from all cells. - for _i in 0..N { - #[allow(unused_must_use)] - { - chunk.get(0); + // Loading from all cells. + for _i in 0..N { + let _ = chunk.get(0); + assert_eq!(get_contract_storage_rw(), (1, 0)); } - assert_eq!(env::test::total_reads(), 1); - assert_eq!(env::test::total_writes(), 0); - } - assert_eq!(env::test::total_reads(), 1); - assert_eq!(env::test::total_writes(), 0); + assert_eq!(get_contract_storage_rw(), (1, 0)); - // Flush and check reads and writes. - chunk.flush(); - assert_eq!(env::test::total_reads(), 1); - assert_eq!(env::test::total_writes(), 0); + // Flush and check reads and writes. + chunk.flush(); + assert_eq!(get_contract_storage_rw(), (1, 0)); + Ok(()) + }) } #[test] -fn count_rw_set() { - // How many times we read or write from or to cells. - const N: u32 = 5; - - let mut chunk = dummy_chunk(); +fn count_rw_set() -> Result<()> { + env::test::run_test::(|_| { + // How many times we read or write from or to cells. + const N: u32 = 5; + let mut chunk = dummy_chunk(); - // Assert clean read writes. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); + // Assert clean read writes. + assert_eq!(get_contract_storage_rw(), (0, 0)); - // Writing to all cells. - for i in 0..N { - chunk.set(i, 42); - } - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); + // Writing to all cells. + for i in 0..N { + chunk.set(i, 42); + } + assert_eq!(get_contract_storage_rw(), (0, 0)); - // Flush and check reads and writes. - chunk.flush(); - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), N as u64); + // Flush and check reads and writes. + chunk.flush(); + assert_eq!(get_contract_storage_rw(), (0, N as usize)); + Ok(()) + }) } #[test] -fn count_rw_set_repeat() { - // How many times we write to the same cell. - const N: u32 = 5; - - let mut chunk = dummy_chunk(); +fn count_rw_set_repeat() -> Result<()> { + env::test::run_test::(|_| { + // How many times we write to the same cell. + const N: u32 = 5; + let mut chunk = dummy_chunk(); - // Assert clean read writes. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); + // Assert clean read writes. + assert_eq!(get_contract_storage_rw(), (0, 0)); - // Writing to all cells. - for _i in 0..N { - chunk.set(0, 42); - } - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); + // Writing to all cells. + for _i in 0..N { + chunk.set(0, 42); + } + assert_eq!(get_contract_storage_rw(), (0, 0)); - // Flush and check reads and writes. - chunk.flush(); - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 1); + // Flush and check reads and writes. + chunk.flush(); + assert_eq!(get_contract_storage_rw(), (0, 1)); + Ok(()) + }) } #[test] -fn count_rw_put() { - // How many times we read or write from or to cells. - const N: u32 = 5; - - let mut chunk = dummy_chunk(); +fn count_rw_put() -> Result<()> { + env::test::run_test::(|_| { + // How many times we read or write from or to cells. + const N: u32 = 5; + let mut chunk = dummy_chunk(); - // Assert clean read writes. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); + // Assert clean read writes. + assert_eq!(get_contract_storage_rw(), (0, 0)); - // Writing to all cells. - for i in 0..N { - #[allow(unused_must_use)] - { - chunk.put(i, 42); + // Writing to all cells. + for i in 0..N { + let _ = chunk.put(i, 42); + assert_eq!(get_contract_storage_rw(), (i as usize + 1, 0)); } - assert_eq!(env::test::total_reads(), i as u64 + 1); - assert_eq!(env::test::total_writes(), 0); - } - assert_eq!(env::test::total_reads(), N as u64); - assert_eq!(env::test::total_writes(), 0); + assert_eq!(get_contract_storage_rw(), (N as usize, 0)); - // Flush and check reads and writes. - chunk.flush(); - assert_eq!(env::test::total_reads(), N as u64); - assert_eq!(env::test::total_writes(), N as u64); + // Flush and check reads and writes. + chunk.flush(); + assert_eq!(get_contract_storage_rw(), (N as usize, N as usize)); + Ok(()) + }) } #[test] -fn count_rw_put_repeat() { - // How many times we put into the same cell. - const N: u32 = 5; - - let mut chunk = dummy_chunk(); +fn count_rw_put_repeat() -> Result<()> { + env::test::run_test::(|_| { + // How many times we put into the same cell. + const N: u32 = 5; + let mut chunk = dummy_chunk(); - // Assert clean read writes. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); + // Assert clean read writes. + assert_eq!(get_contract_storage_rw(), (0, 0)); - // Writing to all cells. - for _i in 0..N { - #[allow(unused_must_use)] - { - chunk.put(0, 42); + // Writing to all cells. + for _i in 0..N { + let _ = chunk.put(0, 42); + assert_eq!(get_contract_storage_rw(), (1, 0)); } - assert_eq!(env::test::total_reads(), 1); - assert_eq!(env::test::total_writes(), 0); - } - assert_eq!(env::test::total_reads(), 1); - assert_eq!(env::test::total_writes(), 0); + assert_eq!(get_contract_storage_rw(), (1, 0)); - // Flush and check reads and writes. - chunk.flush(); - assert_eq!(env::test::total_reads(), 1); - assert_eq!(env::test::total_writes(), 1); + // Flush and check reads and writes. + chunk.flush(); + assert_eq!(get_contract_storage_rw(), (1, 1)); + Ok(()) + }) } #[test] -fn count_rw_take() { - // How many times we take from cells. - const N: u32 = 5; - - let mut chunk = dummy_chunk(); +fn count_rw_take() -> Result<()> { + env::test::run_test::(|_| { + // How many times we take from cells. + const N: u32 = 5; + let mut chunk = dummy_chunk(); - // Assert clean read writes. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); + // Assert clean read writes. + assert_eq!(get_contract_storage_rw(), (0, 0)); - // Writing to all cells. - for i in 0..N { - #[allow(unused_must_use)] - { - chunk.take(i); + // Writing to all cells. + for i in 0..N { + let _ = chunk.take(i); + assert_eq!(get_contract_storage_rw(), (i as usize + 1, 0)); } - assert_eq!(env::test::total_reads(), i as u64 + 1); - assert_eq!(env::test::total_writes(), 0); - } - assert_eq!(env::test::total_reads(), N as u64); - assert_eq!(env::test::total_writes(), 0); + assert_eq!(get_contract_storage_rw(), (N as usize, 0)); - // Flush and check reads and writes. - chunk.flush(); - assert_eq!(env::test::total_reads(), N as u64); - assert_eq!(env::test::total_writes(), N as u64); + // Flush and check reads and writes. + chunk.flush(); + assert_eq!(get_contract_storage_rw(), (N as usize, N as usize)); + Ok(()) + }) } #[test] -fn count_rw_take_repeat() { - // How many times we take from the same cell. - const N: u32 = 5; - - let mut chunk = dummy_chunk(); +fn count_rw_take_repeat() -> Result<()> { + env::test::run_test::(|_| { + // How many times we take from the same cell. + const N: u32 = 5; + let mut chunk = dummy_chunk(); - // Assert clean read writes. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); + // Assert clean read writes. + assert_eq!(get_contract_storage_rw(), (0, 0)); - // Writing to all cells. - for _i in 0..N { - #[allow(unused_must_use)] - { - chunk.take(0); + // Writing to all cells. + for _i in 0..N { + let _ = chunk.take(0); + assert_eq!(get_contract_storage_rw(), (1, 0)); } - assert_eq!(env::test::total_reads(), 1); - assert_eq!(env::test::total_writes(), 0); - } - assert_eq!(env::test::total_reads(), 1); - assert_eq!(env::test::total_writes(), 0); + assert_eq!(get_contract_storage_rw(), (1, 0)); - // Flush and check reads and writes. - chunk.flush(); - assert_eq!(env::test::total_reads(), 1); - assert_eq!(env::test::total_writes(), 1); + // Flush and check reads and writes. + chunk.flush(); + assert_eq!(get_contract_storage_rw(), (1, 1)); + Ok(()) + }) } diff --git a/core/src/storage/chunk/typed_chunk.rs b/core/src/storage/chunk/typed_chunk.rs index 5014e788fc5..04f15c7ec35 100644 --- a/core/src/storage/chunk/typed_chunk.rs +++ b/core/src/storage/chunk/typed_chunk.rs @@ -12,18 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::storage::{ - alloc::{ - Allocate, - AllocateUsing, +use crate::{ + env3 as env, + storage::{ + alloc::{ + Allocate, + AllocateUsing, + }, + Key, }, - chunk::{ - RawChunk, - RawChunkCell, - }, - Key, - NonCloneMarker, }; +use core::marker::PhantomData; /// A chunk of typed cells. /// @@ -37,98 +36,118 @@ use crate::storage::{ /// Read more about kinds of guarantees and their effect [here](../index.html#guarantees). #[derive(Debug, PartialEq, Eq)] pub struct TypedChunk { - /// The underlying chunk of cells. - chunk: RawChunk, - /// Marker that prevents this type from being `Copy` or `Clone` by accident. - non_clone: NonCloneMarker, + /// The underlying key into the contract storage. + key: Key, + /// Marker to trick the Rust compiler into thinking that we actually make use of `T`. + marker: PhantomData T>, } /// A single cell within a chunk of typed cells. -#[derive(Debug, PartialEq, Eq)] -pub struct TypedChunkCell<'a, T> { - /// The underlying cell within the chunk of cells. - cell: RawChunkCell<'a>, - /// Marker that prevents this type from being `Copy` or `Clone` by accident. - non_clone: NonCloneMarker, +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub struct TypedChunkCell<'a, T, M> { + /// The underlying key that points to the typed cell. + key: Key, + /// Marker to trick the Rust compiler into thinking that we actually + /// make use of `T` and `'a`. + marker: PhantomData &'a (T, M)>, } -impl AllocateUsing for TypedChunk { - unsafe fn allocate_using(alloc: &mut A) -> Self - where - A: Allocate, - { +/// Markers for a shared referenced typed chunk cell. +pub enum SharedTypedChunkCell {} +/// Markers for a exclusive referenced typed chunk cell. +pub enum ExclusiveTypedChunkCell {} + +impl<'a, T> TypedChunkCell<'a, T, SharedTypedChunkCell> { + /// Creates a new shared typed chunk cell from the given key. + fn shared(key: Key) -> Self { Self { - chunk: RawChunk::allocate_using(alloc), - non_clone: Default::default(), + key, + marker: Default::default(), } } } -impl<'a, T> TypedChunkCell<'a, T> { - /// Creates a new raw chunk cell from the given key. - /// - /// # Safety - /// - /// This is unsafe since it doesn't check aliasing of cells. - pub(self) unsafe fn new_unchecked(cell: RawChunkCell<'a>) -> Self { +impl<'a, T> TypedChunkCell<'a, T, ExclusiveTypedChunkCell> { + /// Creates a new exclusive typed chunk cell from the given key. + fn exclusive(key: Key) -> Self { Self { - cell, - non_clone: NonCloneMarker::default(), + key, + marker: Default::default(), } } /// Removes the value stored in this cell. - pub fn clear(&mut self) { - self.cell.clear() + pub fn clear(self) { + env::clear_contract_storage(self.key) } } -impl<'a, T> TypedChunkCell<'a, T> +impl<'a, T> TypedChunkCell<'a, T, ExclusiveTypedChunkCell> where T: scale::Encode, { - /// Stores the value into the cell. - pub fn store(&mut self, val: &T) { - self.cell.store(&T::encode(val)) + /// Stores the value from the cell into the contract storage. + pub fn store(self, new_value: &T) { + env::set_contract_storage(self.key, new_value) + } +} + +impl<'a, T, M> TypedChunkCell<'a, T, M> +where + T: scale::Decode, +{ + /// Loads the value from the storage into the cell. + pub fn load(self) -> Option { + env::get_contract_storage(self.key).ok() + } +} + +impl AllocateUsing for TypedChunk { + unsafe fn allocate_using(alloc: &mut A) -> Self + where + A: Allocate, + { + Self { + key: alloc.alloc(u32::max_value().into()), + marker: Default::default(), + } } } impl scale::Encode for TypedChunk { fn encode_to(&self, dest: &mut W) { - self.chunk.encode_to(dest) + self.key.encode_to(dest) } } impl scale::Decode for TypedChunk { fn decode(input: &mut I) -> Result { - RawChunk::decode(input).map(|raw_chunk| { - Self { - chunk: raw_chunk, - non_clone: NonCloneMarker::default(), - } + Ok(Self { + key: Key::decode(input)?, + marker: Default::default(), }) } } impl TypedChunk { - /// Returns the underlying key to the cells. - /// - /// # Note - /// - /// This is a low-level utility getter and should - /// normally not be required by users. - pub fn cells_key(&self) -> Key { - self.chunk.cells_key() + /// Returns the underlying key. + pub fn key(&self) -> Key { + self.key + } + + /// Returns a shared accessor the cell at the given index. + fn cell_at(&self, index: u32) -> TypedChunkCell { + TypedChunkCell::shared(self.key + index) } - /// Returns an accessor to the `n`-th cell. - pub(crate) fn cell_at(&mut self, n: u32) -> TypedChunkCell { - unsafe { TypedChunkCell::new_unchecked(self.chunk.cell_at(n)) } + /// Returns an exclusive accessor the cell at the given index. + fn cell_at_mut(&mut self, index: u32) -> TypedChunkCell { + TypedChunkCell::exclusive(self.key + index) } /// Removes the value stored in the `n`-th cell. - pub fn clear(&mut self, n: u32) { - self.cell_at(n).clear() + pub fn clear(&mut self, index: u32) { + self.cell_at_mut(index).clear() } } @@ -136,20 +155,13 @@ impl TypedChunk where T: scale::Decode, { - /// Loads the value stored in the `n`-th cell if any. + /// Loads the value stored in the storage at the given index if any. /// /// # Panics /// /// If decoding of the loaded bytes fails. - pub fn load(&self, n: u32) -> Option { - self.chunk.load(n).map(|loaded| { - T::decode(&mut &loaded[..]) - // Maybe we should return an error instead of panicking. - .expect( - "[ink_core::TypedChunkCell::load] Error: \ - failed upon decoding" - ) - }) + pub fn load(&self, index: u32) -> Option { + self.cell_at(index).load() } } @@ -157,31 +169,33 @@ impl TypedChunk where T: scale::Encode, { - /// Stores the value into the `n`-th cell. - pub fn store(&mut self, n: u32, val: &T) { - self.cell_at(n).store(val) + /// Stores the value into the cell at the given index. + pub fn store(&mut self, index: u32, new_value: &T) { + self.cell_at_mut(index).store(new_value) } } #[cfg(all(test, feature = "test-env"))] mod tests { use super::*; - use crate::{ - env, - test_utils::run_test, + env3 as env, + env3::Result, }; + fn create_typed_chunk() -> TypedChunk { + unsafe { + let mut alloc = + crate::storage::alloc::BumpAlloc::from_raw_parts(Key([0x0; 32])); + TypedChunk::allocate_using(&mut alloc) + } + } + #[test] - fn simple() { - run_test(|| { + fn simple() -> Result<()> { + env::test::run_test::(|_| { const TEST_LEN: u32 = 5; - - let mut chunk = unsafe { - let mut alloc = - crate::storage::alloc::BumpAlloc::from_raw_parts(Key([0x0; 32])); - TypedChunk::allocate_using(&mut alloc) - }; + let mut chunk = create_typed_chunk(); // Invariants after initialization for i in 0..TEST_LEN { @@ -199,73 +213,77 @@ mod tests { chunk.clear(i); assert_eq!(chunk.load(i), None); } + Ok(()) }) } + /// Returns the current number of total contract storage reads and writes. + fn get_contract_storage_rw() -> (usize, usize) { + let contract_account_id = env::address::().unwrap(); + env::test::get_contract_storage_rw::(&contract_account_id) + .unwrap() + } + #[test] - fn count_reads_writes() { - run_test(|| { + fn count_reads_writes() -> Result<()> { + env::test::run_test::(|_| { const TEST_LEN: u32 = 5; - - let mut chunk = unsafe { - let mut alloc = - crate::storage::alloc::BumpAlloc::from_raw_parts(Key([0x0; 32])); - TypedChunk::allocate_using(&mut alloc) - }; + let mut chunk = create_typed_chunk(); // Reads and writes after init. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); + assert_eq!(get_contract_storage_rw(), (0, 0),); // Loading from all cells. for i in 0..TEST_LEN { chunk.load(i); - assert_eq!(env::test::total_reads(), i as u64 + 1); - assert_eq!(env::test::total_writes(), 0); + assert_eq!(get_contract_storage_rw(), (i as usize + 1, 0)); } - assert_eq!(env::test::total_reads(), TEST_LEN as u64); - assert_eq!(env::test::total_writes(), 0); + assert_eq!(get_contract_storage_rw(), (TEST_LEN as usize, 0)); // Writing to all cells. for i in 0..TEST_LEN { chunk.store(i, &i); - assert_eq!(env::test::total_reads(), TEST_LEN as u64); - assert_eq!(env::test::total_writes(), i as u64 + 1); + assert_eq!( + get_contract_storage_rw(), + (TEST_LEN as usize, i as usize + 1) + ); } - assert_eq!(env::test::total_reads(), TEST_LEN as u64); - assert_eq!(env::test::total_writes(), TEST_LEN as u64); + assert_eq!( + get_contract_storage_rw(), + (TEST_LEN as usize, TEST_LEN as usize) + ); // Loading multiple times from a single cell. const LOAD_REPEATS: usize = 3; for n in 0..LOAD_REPEATS { chunk.load(0); - assert_eq!(env::test::total_reads(), TEST_LEN as u64 + n as u64 + 1); - assert_eq!(env::test::total_writes(), TEST_LEN as u64); + assert_eq!( + get_contract_storage_rw(), + (TEST_LEN as usize + n + 1, TEST_LEN as usize,) + ); } assert_eq!( - env::test::total_reads(), - TEST_LEN as u64 + LOAD_REPEATS as u64 + get_contract_storage_rw(), + (TEST_LEN as usize + LOAD_REPEATS, TEST_LEN as usize,) ); - assert_eq!(env::test::total_writes(), TEST_LEN as u64); // Storing multiple times to a single cell. const STORE_REPEATS: usize = 3; for n in 0..STORE_REPEATS { chunk.store(0, &10); assert_eq!( - env::test::total_reads(), - TEST_LEN as u64 + LOAD_REPEATS as u64 + get_contract_storage_rw(), + (TEST_LEN as usize + LOAD_REPEATS, TEST_LEN as usize + n + 1,) ); - assert_eq!(env::test::total_writes(), TEST_LEN as u64 + n as u64 + 1); } assert_eq!( - env::test::total_reads(), - TEST_LEN as u64 + LOAD_REPEATS as u64 - ); - assert_eq!( - env::test::total_writes(), - TEST_LEN as u64 + STORE_REPEATS as u64 + get_contract_storage_rw(), + ( + TEST_LEN as usize + LOAD_REPEATS, + TEST_LEN as usize + STORE_REPEATS, + ) ); + Ok(()) }) } } diff --git a/core/src/storage/collections/binary_heap/tests.rs b/core/src/storage/collections/binary_heap/tests.rs index b2e290e7d38..3206fcc4d59 100644 --- a/core/src/storage/collections/binary_heap/tests.rs +++ b/core/src/storage/collections/binary_heap/tests.rs @@ -24,6 +24,8 @@ use scale::{ }; use crate::{ + env3 as env, + env3::Result, storage::{ alloc::{ AllocateUsing, @@ -33,7 +35,6 @@ use crate::{ BinaryHeap, Key, }, - test_utils::run_test, }; fn empty_heap() -> BinaryHeap { @@ -80,8 +81,8 @@ fn assert_push_equals_sorted_pop( } #[test] -fn new_unchecked() { - run_test(|| { +fn new_unchecked() -> Result<()> { + env::test::run_test::(|_| { // given let heap = empty_heap(); @@ -89,12 +90,13 @@ fn new_unchecked() { assert_eq!(heap.len(), 0); assert!(heap.is_empty()); assert_eq!(heap.iter().next(), None); + Ok(()) }) } #[test] -fn push_on_empty_heap() { - run_test(|| { +fn push_on_empty_heap() -> Result<()> { + env::test::run_test::(|_| { // given let mut heap = empty_heap(); assert_eq!(heap.pop(), None); @@ -105,12 +107,13 @@ fn push_on_empty_heap() { // then assert_eq!(heap.len(), 1); assert_eq!(heap.pop(), Some(42)); + Ok(()) }) } #[test] -fn push_duplicates_max() { - run_test(|| { +fn push_duplicates_max() -> Result<()> { + env::test::run_test::(|_| { // given let mut heap = empty_heap(); @@ -125,12 +128,13 @@ fn push_duplicates_max() { assert_eq!(heap.pop(), Some(20)); assert_eq!(heap.pop(), Some(10)); assert_eq!(heap.pop(), Some(10)); + Ok(()) }) } #[test] -fn peek() { - run_test(|| { +fn peek() -> Result<()> { + env::test::run_test::(|_| { // given let mut heap = empty_heap(); assert_eq!(heap.peek(), None); @@ -140,12 +144,13 @@ fn peek() { // then assert_eq!(heap.peek(), Some(&42)); + Ok(()) }) } #[test] -fn peek_mut() { - run_test(|| { +fn peek_mut() -> Result<()> { + env::test::run_test::(|_| { // given let mut heap = empty_heap(); heap.push(42); @@ -157,12 +162,13 @@ fn peek_mut() { // then assert_eq!(heap.peek(), Some(&1337)); + Ok(()) }) } #[test] -fn pop_empty_and_refill() { - run_test(|| { +fn pop_empty_and_refill() -> Result<()> { + env::test::run_test::(|_| { // given let mut heap = filled_heap(); for _ in 0..heap.len() { @@ -176,12 +182,13 @@ fn pop_empty_and_refill() { // then assert_eq!(heap.pop(), Some(123)); assert_eq!(heap.len(), 0); + Ok(()) }) } #[test] -fn take_empty() { - run_test(|| { +fn take_empty() -> Result<()> { + env::test::run_test::(|_| { // given let mut heap = empty_heap(); @@ -189,12 +196,13 @@ fn take_empty() { assert_eq!(heap.pop(), None); assert_eq!(heap.peek(), None); assert_eq!(heap.peek_mut(), None); + Ok(()) }) } #[test] -fn push_negative_positive_range_min() { - run_test(|| { +fn push_negative_positive_range_min() -> Result<()> { + env::test::run_test::(|_| { // given let mut heap = empty_heap(); @@ -208,12 +216,13 @@ fn push_negative_positive_range_min() { assert_eq!(heap.pop(), Some(1)); assert_eq!(heap.pop(), Some(0)); assert_eq!(heap.pop(), Some(-1)); + Ok(()) }) } #[test] -fn push_negative_positive_range_max() { - run_test(|| { +fn push_negative_positive_range_max() -> Result<()> { + env::test::run_test::(|_| { // given let mut heap = empty_heap(); @@ -227,12 +236,13 @@ fn push_negative_positive_range_max() { assert_eq!(heap.pop(), Some(1)); assert_eq!(heap.pop(), Some(0)); assert_eq!(heap.pop(), Some(-1)); + Ok(()) }) } #[test] -fn iter() { - run_test(|| { +fn iter() -> Result<()> { + env::test::run_test::(|_| { // given let heap = filled_heap(); @@ -246,12 +256,13 @@ fn iter() { assert_eq!(iter.next(), Some((2, &42))); assert_eq!(iter.next(), Some((3, &5))); assert_eq!(iter.next(), None); + Ok(()) }) } #[test] -fn iter_back() { - run_test(|| { +fn iter_back() -> Result<()> { + env::test::run_test::(|_| { // given let heap = filled_heap(); @@ -264,12 +275,13 @@ fn iter_back() { assert_eq!(iter.next_back(), Some((1, &77))); assert_eq!(iter.next_back(), Some((0, &1337))); assert_eq!(iter.next_back(), None); + Ok(()) }) } #[test] -fn iter_size_hint() { - run_test(|| { +fn iter_size_hint() -> Result<()> { + env::test::run_test::(|_| { // given let heap = filled_heap(); @@ -280,24 +292,27 @@ fn iter_size_hint() { // then iter.next(); assert_eq!(iter.size_hint(), (3, Some(3))); + Ok(()) }) } #[test] -fn unordered_push_results_in_ordered_pop() { - run_test(|| { +fn unordered_push_results_in_ordered_pop() -> Result<()> { + env::test::run_test::(|_| { let mut heap = empty_heap(); let vec = vec![5, 42, 1337, 77, -1, 0, 9999, 3, 65, 90, 1_000_000, -32]; assert_push_equals_sorted_pop(&mut heap, vec); + Ok(()) }) } #[test] -fn max_heap_with_multiple_levels() { - run_test(|| { +fn max_heap_with_multiple_levels() -> Result<()> { + env::test::run_test::(|_| { let mut heap = empty_heap(); let vec = vec![100, 10, 20, 30, 7, 8, 9, 17, 18, 29, 27, 28, 30]; assert_push_equals_sorted_pop(&mut heap, vec); + Ok(()) }) } @@ -308,8 +323,8 @@ fn max_heap_with_multiple_levels() { struct V(u32); #[test] -fn min_heap_with_multiple_levels() { - run_test(|| { +fn min_heap_with_multiple_levels() -> Result<()> { + env::test::run_test::(|_| { let mut heap: BinaryHeap = unsafe { let mut alloc = BumpAlloc::from_raw_parts(Key([0x0; 32])); BinaryHeap::allocate_using(&mut alloc).initialize_into(()) @@ -330,5 +345,6 @@ fn min_heap_with_multiple_levels() { V(30), ]; assert_push_equals_sorted_pop(&mut heap, vec); + Ok(()) }) } diff --git a/core/src/storage/collections/hash_map/tests.rs b/core/src/storage/collections/hash_map/tests.rs index 9c9595bd2ba..6d55b212c9e 100644 --- a/core/src/storage/collections/hash_map/tests.rs +++ b/core/src/storage/collections/hash_map/tests.rs @@ -13,6 +13,8 @@ // limitations under the License. use crate::{ + env3 as env, + env3::Result, storage::{ self, alloc::{ @@ -22,7 +24,6 @@ use crate::{ }, Key, }, - test_utils::run_test, }; fn new_empty() -> storage::HashMap { @@ -33,17 +34,18 @@ fn new_empty() -> storage::HashMap { } #[test] -fn new_unchecked() { - run_test(|| { +fn new_unchecked() -> Result<()> { + env::test::run_test::(|_| { let map = new_empty::(); assert_eq!(map.len(), 0); assert_eq!(map.is_empty(), true); + Ok(()) }) } #[test] -fn get() { - run_test(|| { +fn get() -> Result<()> { + env::test::run_test::(|_| { let mut map = new_empty::(); // Inserts some elements assert_eq!(map.insert("Black".into(), "White".into()), None); @@ -52,12 +54,13 @@ fn get() { assert_eq!(map.get("Black"), Some(&"White".into())); assert_eq!(map.get("Up"), Some(&"Down".into())); assert_eq!(map.get("Forward"), None); + Ok(()) }) } #[test] -fn index() { - run_test(|| { +fn index() -> Result<()> { + env::test::run_test::(|_| { let mut map = new_empty::(); // Inserts some elements assert_eq!(map.insert("Black".into(), "White".into()), None); @@ -65,32 +68,38 @@ fn index() { // Check if get returns the right answer assert_eq!(map["Black"], "White"); assert_eq!(map["Up"], "Down"); + Ok(()) }) } #[test] -fn index_repeat() { - run_test(|| { +fn index_repeat() -> Result<()> { + env::test::run_test::(|_| { let mut map = new_empty::(); // Inserts some elements assert_eq!(map.insert("Something".into(), "There it is!".into()), None); // Check if get returns the right answer repeatedly assert_eq!(map["Something"], "There it is!"); assert_eq!(map["Something"], "There it is!"); + Ok(()) }) } #[test] #[should_panic] fn index_fail0() { - let map = new_empty::(); - // This will just fail and panic - let _ = &map["Won't catch this!"]; + env::test::run_test::(|_| { + let map = new_empty::(); + // This will just fail and panic + let _ = &map["Won't catch this!"]; + Ok(()) + }) + .unwrap() } #[test] -fn insert() { - run_test(|| { +fn insert() -> Result<()> { + env::test::run_test::(|_| { let mut map = new_empty::(); assert_eq!(map.len(), 0); // Insert empty @@ -106,12 +115,13 @@ fn insert() { assert_eq!(map.len(), 1); // Should return the new value assert_eq!(map["1"], ", World!"); + Ok(()) }) } #[test] -fn contains() { - run_test(|| { +fn contains() -> Result<()> { + env::test::run_test::(|_| { let mut map = new_empty::(); // Inserts some elements assert_eq!(map.insert("x".into(), "Anton".into()), None); @@ -122,12 +132,13 @@ fn contains() { // Check contains `false` assert_eq!(map.contains_key("Anton"), false); assert_eq!(map.contains_key(""), false); + Ok(()) }) } #[test] -fn remove() { - run_test(|| { +fn remove() -> Result<()> { + env::test::run_test::(|_| { let mut map = new_empty::(); // Inserts some elements assert_eq!(map.insert("Dog".into(), "Animal".into()), None); @@ -144,12 +155,13 @@ fn remove() { assert_eq!(map.len(), 1); assert_eq!(map.remove("Robin"), Some("Bird".into())); assert_eq!(map.len(), 0); + Ok(()) }) } #[test] -fn mutate_with() { - run_test(|| { +fn mutate_with() -> Result<()> { + env::test::run_test::(|_| { let mut map = new_empty::(); // Inserts some elements assert_eq!(map.insert("Dog Breed".into(), "Akita".into()), None); @@ -173,5 +185,6 @@ fn mutate_with() { map.mutate_with("Bird Breed", |breed| *breed = "Parrot".into()), None ); + Ok(()) }) } diff --git a/core/src/storage/collections/stash/tests.rs b/core/src/storage/collections/stash/tests.rs index 0b054ef8995..58ed684b49a 100644 --- a/core/src/storage/collections/stash/tests.rs +++ b/core/src/storage/collections/stash/tests.rs @@ -13,6 +13,8 @@ // limitations under the License. use crate::{ + env3 as env, + env3::Result, storage::{ alloc::{ AllocateUsing, @@ -22,7 +24,6 @@ use crate::{ Key, Stash, }, - test_utils::run_test, }; fn empty_stash() -> Stash { @@ -43,30 +44,32 @@ fn filled_stash() -> Stash { } #[test] -fn new_unchecked() { - run_test(|| { +fn new_unchecked() -> Result<()> { + env::test::run_test::(|_| { let stash = empty_stash(); // Initial invariant. assert_eq!(stash.len(), 0); assert!(stash.is_empty()); assert_eq!(stash.iter().next(), None); + Ok(()) }) } #[test] -fn put_empty() { - run_test(|| { +fn put_empty() -> Result<()> { + env::test::run_test::(|_| { let mut stash = empty_stash(); // Before and after first put. assert_eq!(stash.get(0), None); assert_eq!(stash.put(42), 0); assert_eq!(stash.get(0), Some(&42)); + Ok(()) }) } #[test] -fn put_filled() { - run_test(|| { +fn put_filled() -> Result<()> { + env::test::run_test::(|_| { let mut stash = filled_stash(); // Before and next put. assert_eq!(stash.get(0), Some(&5)); @@ -79,21 +82,23 @@ fn put_filled() { assert_eq!(stash.put(123), 4); assert_eq!(stash.get(4), Some(&123)); assert_eq!(stash.len(), 5); + Ok(()) }) } #[test] -fn take_empty() { - run_test(|| { +fn take_empty() -> Result<()> { + env::test::run_test::(|_| { let mut stash = empty_stash(); assert_eq!(stash.take(0), None); assert_eq!(stash.take(1000), None); + Ok(()) }) } #[test] -fn take_filled() { - run_test(|| { +fn take_filled() -> Result<()> { + env::test::run_test::(|_| { let mut stash = filled_stash(); // Take and check len assert_eq!(stash.len(), 4); @@ -107,12 +112,13 @@ fn take_filled() { assert_eq!(stash.len(), 0); assert_eq!(stash.take(4), None); assert_eq!(stash.len(), 0); + Ok(()) }) } #[test] -fn put_take() { - run_test(|| { +fn put_take() -> Result<()> { + env::test::run_test::(|_| { let mut stash = filled_stash(); // Take and put "randomly" // @@ -198,12 +204,13 @@ fn put_take() { // Vacant | | 3 | | 5 | | | // |------------------------------------------ // next_vacant = 1 + Ok(()) }) } #[test] -fn iter() { - run_test(|| { +fn iter() -> Result<()> { + env::test::run_test::(|_| { let stash = filled_stash(); let mut iter = stash.iter(); assert_eq!(iter.next(), Some((0, &5))); @@ -211,6 +218,7 @@ fn iter() { assert_eq!(iter.next(), Some((2, &1337))); assert_eq!(iter.next(), Some((3, &77))); assert_eq!(iter.next(), None); + Ok(()) }) } @@ -223,20 +231,21 @@ fn holey_stash() -> Stash { } #[test] -fn iter_holey() { - run_test(|| { +fn iter_holey() -> Result<()> { + env::test::run_test::(|_| { let stash = holey_stash(); let mut iter = stash.iter(); assert_eq!(iter.next(), Some((0, &5))); assert_eq!(iter.next(), Some((2, &1337))); assert_eq!(iter.next(), Some((4, &123))); assert_eq!(iter.next(), None); + Ok(()) }) } #[test] -fn iter_back() { - run_test(|| { +fn iter_back() -> Result<()> { + env::test::run_test::(|_| { let stash = filled_stash(); let mut iter = stash.iter(); assert_eq!(iter.next_back(), Some((3, &77))); @@ -244,28 +253,31 @@ fn iter_back() { assert_eq!(iter.next_back(), Some((1, &42))); assert_eq!(iter.next_back(), Some((0, &5))); assert_eq!(iter.next_back(), None); + Ok(()) }) } #[test] -fn iter_back_holey() { - run_test(|| { +fn iter_back_holey() -> Result<()> { + env::test::run_test::(|_| { let stash = holey_stash(); let mut iter = stash.iter(); assert_eq!(iter.next_back(), Some((4, &123))); assert_eq!(iter.next_back(), Some((2, &1337))); assert_eq!(iter.next_back(), Some((0, &5))); assert_eq!(iter.next_back(), None); + Ok(()) }) } #[test] -fn iter_size_hint() { - run_test(|| { +fn iter_size_hint() -> Result<()> { + env::test::run_test::(|_| { let stash = filled_stash(); let mut iter = stash.iter(); assert_eq!(iter.size_hint(), (4, Some(4))); iter.next(); assert_eq!(iter.size_hint(), (3, Some(3))); + Ok(()) }) } diff --git a/core/src/storage/key.rs b/core/src/storage/key.rs index a32a39ba292..3b0791dbafd 100644 --- a/core/src/storage/key.rs +++ b/core/src/storage/key.rs @@ -230,66 +230,65 @@ mod tests { use super::*; use crate::{ - env, + env3 as env, + env3::{ + EnvError, + Result, + }, test_utils::run_test, }; #[test] - fn store_load_clear() { - run_test(|| { + fn store_load_clear() -> Result<()> { + env::test::run_test::(|_| { let key = Key([0x42; 32]); - assert_eq!(unsafe { env::load(key) }, None); - unsafe { - env::store(key, &[0x5]); - } - assert_eq!(unsafe { env::load(key) }, Some(vec![0x5])); - unsafe { - env::clear(key); - } - assert_eq!(unsafe { env::load(key) }, None); + assert_eq!( + env::get_contract_storage::<()>(key), + Err(EnvError::MissingContractStorageEntry), + ); + env::set_contract_storage(key, &[0x05_u8; 5]); + assert_eq!(env::get_contract_storage::<[i8; 5]>(key), Ok([0x05; 5]),); + env::clear_contract_storage(key); + assert_eq!( + env::get_contract_storage::<[u8; 5]>(key), + Err(EnvError::MissingContractStorageEntry), + ); + Ok(()) }) } #[test] - fn key_add() { - run_test(|| { + fn key_add() -> Result<()> { + env::test::run_test::(|_| { let key00 = Key([0x0; 32]); - let key05 = key00 + 5_u32; // -> 5 + let key05 = key00 + 05_u32; // -> 5 let key10 = key00 + 10_u32; // -> 10 | same as key55 - let key55 = key05 + 5_u32; // -> 5 + 5 = 10 | same as key10 - unsafe { - env::store(key55, &[42]); - } - assert_eq!(unsafe { env::load(key10) }, Some(vec![42])); - unsafe { - env::store(key10, &[13, 37]); - } - assert_eq!(unsafe { env::load(key55) }, Some(vec![13, 37])); + let key55 = key05 + 05_u32; // -> 5 + 5 = 10 | same as key10 + env::set_contract_storage(key55, &42); + assert_eq!(env::get_contract_storage::(key10), Ok(42)); + env::set_contract_storage(key10, &1337); + assert_eq!(env::get_contract_storage::(key55), Ok(1337)); + Ok(()) }) } #[test] - fn key_add_sub() { - run_test(|| { + fn key_add_sub() -> Result<()> { + env::test::run_test::(|_| { let key0a = Key([0x0; 32]); - unsafe { - env::store(key0a, &[0x01]); - } let key1a = key0a + 1337_u32; - unsafe { - env::store(key1a, &[0x02]); - } let key2a = key0a + 42_u32; - unsafe { - env::store(key2a, &[0x03]); - } let key3a = key0a + 52_u32; let key2b = key3a - 10_u32; - assert_eq!(unsafe { env::load(key2b) }, Some(vec![0x03])); let key1b = key2b - 42_u32; - assert_eq!(unsafe { env::load(key1b) }, Some(vec![0x01])); - let key0b = key1b + 2000_u32 - 663_u32; - assert_eq!(unsafe { env::load(key0b) }, Some(vec![0x02])); + let key0b = key1b + 2000_u32 - 663_u32; // same as key1a + env::set_contract_storage(key0a, &1); + env::set_contract_storage(key1a, &2); + env::set_contract_storage(key2a, &3); + assert_eq!(env::get_contract_storage::(key2b), Ok(3)); + assert_eq!(env::get_contract_storage::(key1b), Ok(1)); + assert_eq!(env::get_contract_storage::(key0b), Ok(2)); + Ok(()) }) } diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 6cddb160cda..92300955990 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -55,7 +55,6 @@ //! | Primitive | Owned | Typed | Opt. Reads | Mutable | Safe Load | //! |:-----------:|:-----:|:-----:|:----------:|:-------:|:---------:| //! | `Key` | No | No | No | No | No | -//! | `RawCell` | Yes | No | No | No | No | //! | `TypedCell` | Yes | Yes | No | No | No | //! | `SyncCell` | Yes | Yes | Yes | Yes | No | //! @@ -67,13 +66,11 @@ //! //! ### Kinds //! -//! - `RawChunk` //! - `TypedChunk` //! - `SyncChunk` pub mod alloc; mod key; -mod non_clone; pub mod cell; pub mod chunk; @@ -81,8 +78,6 @@ mod collections; mod flush; mod value; -use self::non_clone::NonCloneMarker; - pub use self::{ collections::{ binary_heap::{ diff --git a/core/src/storage/non_clone.rs b/core/src/storage/non_clone.rs deleted file mode 100644 index a3c06d81d46..00000000000 --- a/core/src/storage/non_clone.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018-2019 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 core::marker::PhantomData; - -use scale::{ - Decode, - Encode, -}; - -/// Marks types as non-`Copy` and non-`Clone`. -/// -/// # Note -/// -/// - This marker type is zero-sized for all `T`. -/// - This serves as documentation for crate maintainers -/// as well as utility to enforce this behaviour for types -/// that are using it. -/// - Especially for `Cell` types it is important to make them -/// non-`Copy` and non-`Clone` since that would violate their -/// ownership guarantees over their contract storage slot. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)] -pub struct NonCloneMarker(PhantomData); - -impl Default for NonCloneMarker { - fn default() -> Self { - Self(PhantomData) - } -} diff --git a/examples/core/erc20/.cargo/config b/examples/core/erc20/.cargo/config deleted file mode 100644 index 7d5b52df77d..00000000000 --- a/examples/core/erc20/.cargo/config +++ /dev/null @@ -1,4 +0,0 @@ -[target.wasm32-unknown-unknown] -rustflags = [ - "-C", "link-args=-z stack-size=65536 --import-memory" -] diff --git a/examples/core/erc20/Cargo.toml b/examples/core/erc20/Cargo.toml deleted file mode 100644 index e1bb66cbec9..00000000000 --- a/examples/core/erc20/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "erc20" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -ink_core = { path = "../../../core", default-features = false } -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] } - -[lib] -name = "erc20" -crate-type = ["cdylib"] - -[features] -default = ["std"] -std = [ - "ink_core/std", - "scale/std", -] -test-env = [ - "std", - "ink_core/test-env", -] - -[profile.release] -panic = "abort" -lto = true -opt-level = "z" -overflow-checks = true diff --git a/examples/core/erc20/src/lib.rs b/examples/core/erc20/src/lib.rs deleted file mode 100644 index 3e9b5ff37bb..00000000000 --- a/examples/core/erc20/src/lib.rs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2018-2019 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. - -#![cfg_attr(not(all(test, feature = "test-env")), no_std)] - -use scale::{ - Decode, - Encode, -}; -use ink_core::{ - env::{ - self, - ContractEnv, - DefaultSrmlTypes, - EnvTypes, - Env as _, - }, - storage::{ - self, - alloc::{ - AllocateUsing, - Allocate, - BumpAlloc, - Initialize, - }, - Flush, - Key, - }, -}; - -type AccountId = as EnvTypes>::AccountId; -type Balance = as EnvTypes>::Balance; - -/// The storage data that is hold by the ERC-20 token. -#[derive(Debug, Encode, Decode)] -pub struct Erc20Token { - /// All peeps done by all users. - balances: storage::HashMap, - /// Balances that are spendable by non-owners. - /// - /// # Note - /// - /// Mapping: (from, to) -> allowed - allowances: storage::HashMap<(AccountId, AccountId), Balance>, - /// The total supply. - total_supply: storage::Value, -} - -impl Erc20Token { - /// Returns the total number of tokens in existence. - pub fn total_supply(&self) -> Balance { - *self.total_supply - } - - /// Returns the balance of the given address. - pub fn balance_of(&self, owner: AccountId) -> Balance { - *self.balances.get(&owner).unwrap_or(&0) - } - - /// Returns the amount of tokens that an owner allowed to a spender. - pub fn allowance(&self, owner: AccountId, spender: AccountId) -> Balance { - *self.allowances.get(&(owner, spender)).unwrap_or(&0) - } - - /// Transfers token from the sender to the `to` address. - pub fn transfer(&mut self, to: AccountId, value: Balance) -> bool { - self.transfer_impl(ContractEnv::::caller(), to, value); - true - } - - /// Approve the passed address to spend the specified amount of tokens - /// on the behalf of the message's sender. - /// - /// # Note - /// - /// Beware that changing an allowance with this method afterwards brings - /// the risk that someone may use both, the old and the new allowance, - /// by unfortunate transaction ordering. - /// One possible solution to mitigate this race condition is to first reduce - /// the spender's allowance to 0 and set the desired value afterwards: - /// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - pub fn approve(&mut self, spender: AccountId, value: Balance) -> bool { - let owner = ContractEnv::::caller(); - self.allowances.insert((owner, spender), value); - // emit event (not ready yet) - true - } - - /// Transfer tokens from one address to another. - /// - /// Note that while this function emits an approval event, - /// this is not required as per the specification, - /// and other compliant implementations may not emit the event. - pub fn transfer_from(&mut self, from: AccountId, to: AccountId, value: Balance) -> bool { - self.allowances[&(from, to)] -= value; - self.transfer_impl(from, to, value); - // emit approval(from, to, value) (not yet ready) - true - } - - /// Transfers token from a specified address to another address. - fn transfer_impl(&mut self, from: AccountId, to: AccountId, value: Balance) { - self.balances[&from] -= value; - self.balances[&to] += value; - // emit transfer(from, to, value) (not ready yet) - } - - // fn mint_for(&mut self, receiver: AccountId, value: Balance) { - // self.balances[&receiver] += value; - // self.total_supply += value; - // // emit transfer(0, receiver, value) (not ready yet) - // } -} - -// BELOW THIS EVERYTHING WILL EVENTUALLY BE GENERATED BY THE eDSL - -impl AllocateUsing for Erc20Token { - unsafe fn allocate_using(alloc: &mut A) -> Self - where - A: Allocate, - { - Self { - balances: storage::HashMap::allocate_using(alloc), - allowances: storage::HashMap::allocate_using(alloc), - total_supply: storage::Value::allocate_using(alloc), - } - } -} - -impl Initialize for Erc20Token { - type Args = (); - - fn initialize(&mut self, _params: Self::Args) { - // self.mint_for(alice_address(), 10_000); - // self.mint_for(bob_address(), 500); - } -} - -impl Flush for Erc20Token { - fn flush(&mut self) { - self.balances.flush(); - self.allowances.flush(); - self.total_supply.flush(); - } -} - -/// Erc20Token API. -#[derive(Encode, Decode)] -enum Action { - TotalSupply, // -> Balance - BalanceOf { - owner: AccountId, - }, // -> Balance - Allowance { - owner: AccountId, - spender: AccountId, - }, // -> Balance - Transfer { - to: AccountId, - value: Balance, - }, // -> bool - Approve { - spender: AccountId, - value: Balance, - }, // -> bool - TransferFrom { - from: AccountId, - to: AccountId, - value: Balance, - }, // -> bool -} - -fn ret(val: T) -> ! -where - T: scale::Encode, -{ - unsafe { env::r#return::>(val) } -} - -fn instantiate() -> Erc20Token { - unsafe { - let mut alloc = BumpAlloc::from_raw_parts(Key([0x0; 32])); - Erc20Token::allocate_using(&mut alloc) - } -} - -#[no_mangle] -pub extern "C" fn deploy() { - instantiate().initialize_into(()).flush() -} - -fn decode_params() -> Action { - let input = ContractEnv::::input(); - Action::decode(&mut &input[..]).unwrap() -} - -#[no_mangle] -pub extern "C" fn call() { - let mut erc20token = instantiate(); - match decode_params() { - Action::TotalSupply => { - let ret_val = erc20token.total_supply(); - erc20token.flush(); - ret(ret_val); - } - Action::BalanceOf { owner } => { - let ret_val = erc20token.balance_of(owner); - erc20token.flush(); - ret(ret_val); - } - Action::Allowance { owner, spender } => { - let ret_val = erc20token.allowance(owner, spender); - erc20token.flush(); - ret(ret_val); - } - Action::Transfer { to, value } => { - let ret_val = erc20token.transfer(to, value); - erc20token.flush(); - ret(ret_val); - } - Action::Approve { spender, value } => { - let ret_val = erc20token.approve(spender, value); - erc20token.flush(); - ret(ret_val); - } - Action::TransferFrom { from, to, value } => { - let ret_val = erc20token.transfer_from(from, to, value); - erc20token.flush(); - ret(ret_val); - } - } -} diff --git a/examples/lang/delegator/.cargo/config b/examples/lang/delegator/.cargo/config deleted file mode 100644 index 9ed784e6e2d..00000000000 --- a/examples/lang/delegator/.cargo/config +++ /dev/null @@ -1,4 +0,0 @@ -[target.wasm32-unknown-unknown] -rustflags = [ - "-C", "link-args=-z stack-size=65536 --import-memory" -] \ No newline at end of file diff --git a/examples/lang/delegator/.gitignore b/examples/lang/delegator/.gitignore deleted file mode 100644 index bf910de10af..00000000000 --- a/examples/lang/delegator/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Ignore build artifacts from the local tests sub-crate. -/target/ - -# Ignore backup files creates by cargo fmt. -**/*.rs.bk - -# Remove Cargo.lock when creating an executable, leave it for libraries -# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock -Cargo.lock \ No newline at end of file diff --git a/examples/lang/delegator/.ink/abi_gen/Cargo.toml b/examples/lang/delegator/.ink/abi_gen/Cargo.toml deleted file mode 100644 index 46d9eb4d2dd..00000000000 --- a/examples/lang/delegator/.ink/abi_gen/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "abi-gen" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" -publish = false - -[[bin]] -name = "abi-gen" -path = "main.rs" - -[dependencies] -contract = { path = "../..", package = "delegator", features = ["ink-generate-abi"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" diff --git a/examples/lang/delegator/.ink/abi_gen/main.rs b/examples/lang/delegator/.ink/abi_gen/main.rs deleted file mode 100644 index 885d6954cc3..00000000000 --- a/examples/lang/delegator/.ink/abi_gen/main.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() -> Result<(), std::io::Error> { - let abi = contract::ink_generate_abi(); - let contents = serde_json::to_string_pretty(&abi)?; - std::fs::create_dir("target").ok(); - std::fs::write("target/metadata.json", contents)?; - Ok(()) -} diff --git a/examples/lang/delegator/Cargo.toml b/examples/lang/delegator/Cargo.toml deleted file mode 100644 index a3790b2ff38..00000000000 --- a/examples/lang/delegator/Cargo.toml +++ /dev/null @@ -1,83 +0,0 @@ -[package] -name = "delegator" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -ink_abi = { path = "../../../abi", default-features = false, features = ["derive"], optional = true } -ink_core = { path = "../../../core", default-features = false } -ink_model = { path = "../../../model", default-features = false } -ink_lang = { path = "../../../lang", default-features = false } -ink_prelude = { path = "../../../prelude", default-features = false } - -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] } -type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true } - -adder = { path = "adder", default-features = false, features = ["ink-as-dependency"] } -subber = { path = "subber", default-features = false, features = ["ink-as-dependency"] } -accumulator = { path = "accumulator", default-features = false, features = ["ink-as-dependency"] } - -[lib] -name = "delegator" -crate-type = [ - # Used for normal contract Wasm blobs. - "cdylib", - # Used for ABI generation. - "rlib", -] - -[features] -default = ["test-env"] -std = [ - "ink_abi/std", - "ink_core/std", - "ink_model/std", - "ink_lang/std", - "ink_prelude/std", - "scale/std", - "type-metadata/std", - - "adder/std", - "subber/std", - "accumulator/std", -] -test-env = [ - "std", - "ink_core/test-env", - "ink_model/test-env", - "ink_lang/test-env", - "type-metadata/std", - - "adder/test-env", - "subber/test-env", - "accumulator/test-env", -] -ink-generate-abi = [ - "std", - "ink_abi", - "type-metadata", - "ink_core/ink-generate-abi", - - "adder/ink-generate-abi", - "subber/ink-generate-abi", - "accumulator/ink-generate-abi", -] -ink-as-dependency = [] - -[profile.release] -panic = "abort" -lto = true -opt-level = "z" -overflow-checks = true - -[workspace] -members = [ - ".ink/abi_gen", - "accumulator", - "adder", - "subber", -] -exclude = [ - ".ink" -] diff --git a/examples/lang/delegator/README.md b/examples/lang/delegator/README.md deleted file mode 100644 index 841399aa699..00000000000 --- a/examples/lang/delegator/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Delegator Smart Contract - -The delegator smart contract is our showcase for executing other smart contracts on-chain. - -It consists in total of 4 different smart contract: - -- Delegator (root): Delegates calls either to the Adder or Subber smart contract -- Adder: Increases a value in the Accumulator smart contract -- Subber: Decreases a value in the Accumulator smart contract -- Accumulator: Owns a simple `i32` value that can be incremented or decremented - -In order to test this bundle of smart contracts you need to do the following: - -1. Compile all dependencies of the Delegator smart contract using the `./build-all.sh` script. - As usual you will receive their respective Wasm blobs in their respective `target` folders. -1. Go to the delegator directory (root of the example) and run `cargo contract generate-metadata` - in order to generate the ABI file for the Delegator smart contract. - Note: You won't need an ABI file for the other smart contracts since we won't operate on them - using the Polkadot UI. -1. Put the Wasm blobs of Accumulator, Adder, Subber and the Delegator on the chain via `put_code` command. - While doing so note down their respective code hashes that you can inspect by extracting this information - out from the signalling events upon putting the code on the chain. -1. Instantiate the Delegator smart contract given all of the code hashes and a starting value. - The Delegator smart contract will take over the work of instantiating the other smart contracts for you. -1. Now you are able to run the operations provided by the Delegator smart contract. - Namely `delegate` to delegate the call to either the Adder or the Subber to either increase or decrease - the value stored in the Accumulator smart contract respectively and `switch` to switch the currently - delegated-to smart contract. - The initial delegated-to smart contract is the Adder. diff --git a/examples/lang/delegator/accumulator/Cargo.toml b/examples/lang/delegator/accumulator/Cargo.toml deleted file mode 100644 index d156598f483..00000000000 --- a/examples/lang/delegator/accumulator/Cargo.toml +++ /dev/null @@ -1,51 +0,0 @@ -[package] -name = "accumulator" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -ink_abi = { path = "../../../../abi", default-features = false, features = ["derive"], optional = true } -ink_core = { path = "../../../../core", default-features = false } -ink_model = { path = "../../../../model", default-features = false } -ink_lang = { path = "../../../../lang", default-features = false } -ink_prelude = { path = "../../../../prelude", default-features = false } - -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] } -type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true } - -[lib] -name = "accumulator" -crate-type = [ - # Used for normal contract Wasm blobs. - "cdylib", - # Used for ABI generation. - "rlib", -] - -[features] -default = ["test-env"] -std = [ - "ink_abi/std", - "ink_core/std", - "ink_model/std", - "ink_lang/std", - "ink_prelude/std", - "scale/std", - "type-metadata/std", -] -test-env = [ - "std", - "ink_core/test-env", - "ink_model/test-env", - "ink_lang/test-env", - "type-metadata/std", -] -ink-generate-abi = [ - "std", - "ink_abi", - "type-metadata", - "ink_core/ink-generate-abi", - "ink_lang/ink-generate-abi", -] -ink-as-dependency = [] diff --git a/examples/lang/delegator/accumulator/src/lib.rs b/examples/lang/delegator/accumulator/src/lib.rs deleted file mode 100644 index a40628ba062..00000000000 --- a/examples/lang/delegator/accumulator/src/lib.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018-2019 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. - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_core::storage; -use ink_lang::contract; -use ink_prelude::format; - -contract! { - #![env = ink_core::env::DefaultSrmlTypes] - - /// Holds a simple i32 value that can be incremented and decremented. - struct Accumulator { - /// The current value. - value: storage::Value, - } - - impl Deploy for Accumulator { - /// Initializes the value to the initial value. - fn deploy(&mut self, init_value: i32) { - self.value.set(init_value) - } - } - - impl Accumulator { - /// Mutates the internal value. - pub(external) fn inc(&mut self, by: i32) { - self.value += by; - } - - /// Returns the current state. - pub(external) fn get(&self) -> i32 { - *self.value - } - } -} diff --git a/examples/lang/delegator/adder/Cargo.toml b/examples/lang/delegator/adder/Cargo.toml deleted file mode 100644 index e559ea3658e..00000000000 --- a/examples/lang/delegator/adder/Cargo.toml +++ /dev/null @@ -1,56 +0,0 @@ -[package] -name = "adder" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -ink_abi = { path = "../../../../abi", default-features = false, features = ["derive"], optional = true } -ink_core = { path = "../../../../core", default-features = false } -ink_model = { path = "../../../../model", default-features = false } -ink_lang = { path = "../../../../lang", default-features = false } -ink_prelude = { path = "../../../../prelude", default-features = false } - -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] } -type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true } - -accumulator = { path = "../accumulator", default-features = false, features = ["ink-as-dependency"] } - -[lib] -name = "adder" -crate-type = [ - # Used for normal contract Wasm blobs. - "cdylib", - # Used for ABI generation. - "rlib", -] - -[features] -default = ["test-env"] -std = [ - "ink_abi/std", - "ink_core/std", - "ink_model/std", - "ink_lang/std", - "ink_prelude/std", - "scale/std", - "type-metadata/std", - - "accumulator/std", -] -test-env = [ - "std", - "ink_core/test-env", - "ink_model/test-env", - "ink_lang/test-env", -] -ink-generate-abi = [ - "std", - "ink_abi", - "type-metadata", - "ink_core/ink-generate-abi", - "ink_lang/ink-generate-abi", - - "accumulator/ink-generate-abi", -] -ink-as-dependency = [] diff --git a/examples/lang/delegator/adder/src/lib.rs b/examples/lang/delegator/adder/src/lib.rs deleted file mode 100644 index 75121fb6a1a..00000000000 --- a/examples/lang/delegator/adder/src/lib.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018-2019 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. - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_core::storage; -use ink_lang::contract; -use ink_prelude::format; - -contract! { - #![env = ink_core::env::DefaultSrmlTypes] - - /// Increments the accumulator's value. - struct Adder { - /// The accumulator to store values. - accumulator: storage::Value, - } - - impl Deploy for Adder { - fn deploy(&mut self, accumulator: AccountId) { - self.accumulator.set(accumulator::Accumulator::from_account_id(accumulator)); - } - } - - impl Adder { - /// Flips the current state of our smart contract. - pub(external) fn inc(&mut self, by: i32) { - self.accumulator.inc(by); - } - } -} diff --git a/examples/lang/delegator/build-all.sh b/examples/lang/delegator/build-all.sh deleted file mode 100755 index f2db0121e01..00000000000 --- a/examples/lang/delegator/build-all.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -pushd accumulator && cargo contract build && popd && -pushd adder && cargo contract build && popd && -pushd subber && cargo contract build && popd && -cargo contract build diff --git a/examples/lang/delegator/src/lib.rs b/examples/lang/delegator/src/lib.rs deleted file mode 100644 index 2c252aa818c..00000000000 --- a/examples/lang/delegator/src/lib.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2018-2019 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. - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_core::storage; -use ink_lang::contract; -use ink_prelude::format; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, scale::Encode, scale::Decode)] -#[cfg_attr(feature = "ink-generate-abi", derive(type_metadata::Metadata))] -pub enum Which { - Adder, - Subber, -} - -impl ink_core::storage::Flush for Which {} - -contract! { - #![env = ink_core::env::DefaultSrmlTypes] - - /// Delegates calls to an adder or subber contract to mutate - /// a value in an accumulator contract. - /// - /// In order to deploy the delegator smart contract we first - /// have to manually put the code of the accumulator, adder - /// and subber smart contracts, receive their code hashes from - /// the signalled events and put their code hash into our - /// delegator smart contract. - struct Delegator { - /// Says which of adder or subber is currently in use. - which: storage::Value, - /// The accumulator smart contract. - accumulator: storage::Value, - /// The adder smart contract. - adder: storage::Value, - /// The subber smart contract. - subber: storage::Value, - } - - impl Deploy for Delegator { - /// Initializes the value to the initial value. - fn deploy( - &mut self, - init_value: i32, - accumulator_code_hash: Hash, - adder_code_hash: Hash, - subber_code_hash: Hash, - ) { - self.which.set(Which::Adder); - let total_balance = env.balance(); - let accumulator = accumulator::Accumulator::new(accumulator_code_hash, init_value) - .value(total_balance / 4) - .create() - .expect("failed at instantiating the accumulator contract"); - self.accumulator.set(accumulator.clone()); - self.adder.set( - adder::Adder::new(adder_code_hash, accumulator.account_id()) - .value(total_balance / 4) - .create() - .expect("failed at instantiating the adder contract") - ); - self.subber.set( - subber::Subber::new(subber_code_hash, accumulator.account_id()) - .value(total_balance / 4) - .create() - .expect("failed at instantiating the subber contract") - ); - } - } - - impl Delegator { - /// Delegates the call. - pub(external) fn delegate(&mut self, by: i32) { - match &*self.which { - Which::Adder => self.adder.inc(by), - Which::Subber => self.subber.dec(by), - } - } - - /// Switches the delegator. - pub(external) fn switch(&mut self) { - match *self.which { - Which::Adder => { - *self.which = Which::Subber; - } - Which::Subber => { - *self.which = Which::Adder; - } - } - } - } -} diff --git a/examples/lang/delegator/subber/Cargo.toml b/examples/lang/delegator/subber/Cargo.toml deleted file mode 100644 index 3e1215479ec..00000000000 --- a/examples/lang/delegator/subber/Cargo.toml +++ /dev/null @@ -1,53 +0,0 @@ -[package] -name = "subber" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -ink_abi = { path = "../../../../abi", default-features = false, features = ["derive"], optional = true } -ink_core = { path = "../../../../core", default-features = false } -ink_model = { path = "../../../../model", default-features = false } -ink_lang = { path = "../../../../lang", default-features = false } -ink_prelude = { path = "../../../../prelude", default-features = false } - -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] } -type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true } - -accumulator = { path = "../accumulator", default-features = false, features = ["ink-as-dependency"] } - -[lib] -name = "subber" -crate-type = [ - # Used for normal contract Wasm blobs. - "cdylib", - # Used for ABI generation. - "rlib", -] - -[features] -default = ["test-env"] -std = [ - "ink_abi/std", - "ink_core/std", - "ink_model/std", - "ink_lang/std", - "ink_prelude/std", - "scale/std", - "type-metadata/std", -] -test-env = [ - "std", - "ink_core/test-env", - "ink_model/test-env", - "ink_lang/test-env", - "type-metadata/std", -] -ink-generate-abi = [ - "std", - "ink_abi", - "type-metadata", - "ink_core/ink-generate-abi", - "ink_lang/ink-generate-abi", -] -ink-as-dependency = [] diff --git a/examples/lang/delegator/subber/src/lib.rs b/examples/lang/delegator/subber/src/lib.rs deleted file mode 100644 index 0aced333637..00000000000 --- a/examples/lang/delegator/subber/src/lib.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2018-2019 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. - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_core::storage; -use ink_lang::contract; -use ink_prelude::format; - -contract! { - #![env = ink_core::env::DefaultSrmlTypes] - - /// Decrements the accumulator's value. - struct Subber { - /// The accumulator to store values. - accumulator: storage::Value, - } - - impl Deploy for Subber { - fn deploy(&mut self, accumulator: AccountId) { - self.accumulator.set(accumulator::Accumulator::from_account_id(accumulator)); - } - } - - impl Subber { - pub(external) fn dec(&mut self, by: i32) { - self.accumulator.inc(-by); - } - } -} diff --git a/examples/lang/erc20/.cargo/config b/examples/lang/erc20/.cargo/config deleted file mode 100644 index 7d5b52df77d..00000000000 --- a/examples/lang/erc20/.cargo/config +++ /dev/null @@ -1,4 +0,0 @@ -[target.wasm32-unknown-unknown] -rustflags = [ - "-C", "link-args=-z stack-size=65536 --import-memory" -] diff --git a/examples/lang/erc20/.ink/abi_gen/Cargo.toml b/examples/lang/erc20/.ink/abi_gen/Cargo.toml deleted file mode 100644 index 0e566c9df5f..00000000000 --- a/examples/lang/erc20/.ink/abi_gen/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "abi-gen" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" -publish = false - -[[bin]] -name = "abi-gen" -path = "main.rs" - -[dependencies] -contract = { path = "../..", package = "erc20", features = ["ink-generate-abi"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" diff --git a/examples/lang/erc20/.ink/abi_gen/main.rs b/examples/lang/erc20/.ink/abi_gen/main.rs deleted file mode 100644 index 885d6954cc3..00000000000 --- a/examples/lang/erc20/.ink/abi_gen/main.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() -> Result<(), std::io::Error> { - let abi = contract::ink_generate_abi(); - let contents = serde_json::to_string_pretty(&abi)?; - std::fs::create_dir("target").ok(); - std::fs::write("target/metadata.json", contents)?; - Ok(()) -} diff --git a/examples/lang/erc20/Cargo.toml b/examples/lang/erc20/Cargo.toml deleted file mode 100644 index 2311a01b760..00000000000 --- a/examples/lang/erc20/Cargo.toml +++ /dev/null @@ -1,64 +0,0 @@ -[package] -name = "erc20" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -ink_abi = { path = "../../../abi", default-features = false, features = ["derive"], optional = true } -ink_core = { path = "../../../core", default-features = false } -ink_model = { path = "../../../model", default-features = false } -ink_lang = { path = "../../../lang", default-features = false } -ink_prelude = { path = "../../../prelude", default-features = false } - -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] } -type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true } - -[lib] -name = "erc20" -crate-type = [ - # Used for normal contract Wasm blobs. - "cdylib", - # Used for ABI generation. - "rlib", -] - -[features] -default = ["test-env"] -std = [ - "ink_abi/std", - "ink_core/std", - "ink_model/std", - "ink_lang/std", - "ink_prelude/std", - "type-metadata/std", - "scale/std", -] -test-env = [ - "std", - "ink_core/test-env", - "ink_model/test-env", - "ink_lang/test-env", -] -ink-generate-abi = [ - "std", - "ink_abi", - "type-metadata", - "ink_core/ink-generate-abi", - "ink_lang/ink-generate-abi", -] -ink-as-dependency = [] - -[profile.release] -panic = "abort" -lto = true -opt-level = "z" -overflow-checks = true - -[workspace] -members = [ - ".ink/abi_gen" -] -exclude = [ - ".ink" -] diff --git a/examples/lang/erc20/src/lib.rs b/examples/lang/erc20/src/lib.rs deleted file mode 100644 index a10495c2033..00000000000 --- a/examples/lang/erc20/src/lib.rs +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2018-2019 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. - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_core::{ - env::DefaultSrmlTypes, - storage, -}; -use ink_lang::contract; -use ink_model::EnvHandler; -use ink_prelude::format; - -contract! { - #![env = DefaultSrmlTypes] - - // Event deposited when a token transfer occurs - event Transfer { - from: Option, - to: Option, - value: Balance, - } - - // Event deposited when an approval occurs - event Approval { - owner: AccountId, - spender: AccountId, - value: Balance, - } - - /// The storage items for a typical ERC20 token implementation. - struct Erc20 { - /// The total supply. - total_supply: storage::Value, - /// The balance of each user. - balances: storage::HashMap, - /// Balances that are spendable by non-owners: (owner, spender) -> allowed - allowances: storage::HashMap<(AccountId, AccountId), Balance>, - } - - impl Deploy for Erc20 { - fn deploy(&mut self, init_value: Balance) { - self.total_supply.set(init_value); - self.balances.insert(env.caller(), init_value); - env.emit(Transfer { - from: None, - to: Some(env.caller()), - value: init_value - }); - } - } - - impl Erc20 { - /// Returns the total number of tokens in existence. - pub(external) fn total_supply(&self) -> Balance { - let total_supply = *self.total_supply; - env.println(&format!("Erc20::total_supply = {:?}", total_supply)); - total_supply - } - - /// Returns the balance of the given AccountId. - pub(external) fn balance_of(&self, owner: AccountId) -> Balance { - let balance = self.balance_of_or_zero(&owner); - env.println(&format!("Erc20::balance_of(owner = {:?}) = {:?}", owner, balance)); - balance - } - - /// Returns the amount of tokens that an owner allowed to a spender. - pub(external) fn allowance(&self, owner: AccountId, spender: AccountId) -> Balance { - let allowance = self.allowance_or_zero(&owner, &spender); - env.println(&format!( - "Erc20::allowance(owner = {:?}, spender = {:?}) = {:?}", - owner, spender, allowance - )); - allowance - } - - /// Transfers token from the sender to the `to` AccountId. - pub(external) fn transfer(&mut self, to: AccountId, value: Balance) -> bool { - self.transfer_impl(env, env.caller(), to, value) - } - - /// Approve the passed AccountId to spend the specified amount of tokens - /// on the behalf of the message's sender. - pub(external) fn approve(&mut self, spender: AccountId, value: Balance) -> bool { - let owner = env.caller(); - self.allowances.insert((owner, spender), value); - env.emit(Approval { - owner: owner, - spender: spender, - value: value - }); - true - } - - /// Transfer tokens from one AccountId to another. - pub(external) fn transfer_from(&mut self, from: AccountId, to: AccountId, value: Balance) -> bool { - let allowance = self.allowance_or_zero(&from, &env.caller()); - if allowance < value { - return false - } - self.allowances.insert((from, env.caller()), allowance - value); - self.transfer_impl(env, from, to, value) - } - } - - impl Erc20 { - /// Returns the balance of the AccountId or 0 if there is no balance. - fn balance_of_or_zero(&self, of: &AccountId) -> Balance { - *self.balances.get(of).unwrap_or(&0) - } - - /// Returns the allowance or 0 of there is no allowance. - fn allowance_or_zero(&self, owner: &AccountId, spender: &AccountId) -> Balance { - *self.allowances.get(&(*owner, *spender)).unwrap_or(&0) - } - - /// Transfers token from a specified AccountId to another AccountId. - fn transfer_impl(&mut self, env: &mut EnvHandler>, from: AccountId, to: AccountId, value: Balance) -> bool { - let balance_from = self.balance_of_or_zero(&from); - if balance_from < value { - return false - } - self.balances.insert(from, balance_from - value); - let balance_to = self.balance_of_or_zero(&to); - self.balances.insert(to, balance_to + value); - env.emit(Transfer { - from: Some(from), - to: Some(to), - value: value - }); - true - } - } -} - -#[cfg(all(test, feature = "test-env"))] -mod tests { - use ink_core::env; - - use super::*; - - type Types = ink_core::env::DefaultSrmlTypes; - - #[test] - fn deployment_works() { - let alice = AccountId::from([0x0; 32]); - env::test::set_caller::(alice); - - // Deploy the contract with some `init_value` - let erc20 = Erc20::deploy_mock(1234); - // Check that the `total_supply` is `init_value` - assert_eq!(erc20.total_supply(), 1234); - // Check that `balance_of` Alice is `init_value` - assert_eq!(erc20.balance_of(alice), 1234); - } - - #[test] - fn transfer_works() { - let alice = AccountId::from([0x0; 32]); - let bob = AccountId::from([0x1; 32]); - - env::test::set_caller::(alice); - // Deploy the contract with some `init_value` - let mut erc20 = Erc20::deploy_mock(1234); - // Alice does not have enough funds for this - assert_eq!(erc20.transfer(bob, 4321), false); - // Alice can do this though - assert_eq!(erc20.transfer(bob, 234), true); - // Check Alice and Bob have the expected balance - assert_eq!(erc20.balance_of(alice), 1000); - assert_eq!(erc20.balance_of(bob), 234); - } - - #[test] - fn allowance_works() { - let alice = AccountId::from([0x0; 32]); - let bob = AccountId::from([0x1; 32]); - let charlie = AccountId::from([0x2; 32]); - - env::test::set_caller::(alice); - // Deploy the contract with some `init_value` - let mut erc20 = Erc20::deploy_mock(1234); - // Bob does not have an allowance from Alice's balance - assert_eq!(erc20.allowance(alice, bob), 0); - // Thus, Bob cannot transfer out of Alice's account - env::test::set_caller::(bob); - assert_eq!(erc20.transfer_from(alice, bob, 1), false); - // Alice can approve bob for some of her funds - env::test::set_caller::(alice); - assert_eq!(erc20.approve(bob, 20), true); - // And the allowance reflects that correctly - assert_eq!(erc20.allowance(alice, bob), 20); - // Charlie cannot send on behalf of Bob - env::test::set_caller::(charlie); - assert_eq!(erc20.transfer_from(alice, bob, 10), false); - // Bob cannot transfer more than he is allowed - env::test::set_caller::(bob); - assert_eq!(erc20.transfer_from(alice, charlie, 25), false); - // A smaller amount should work though - assert_eq!(erc20.transfer_from(alice, charlie, 10), true); - // Check that the allowance is updated - assert_eq!(erc20.allowance(alice, bob), 10); - // and the balance transferred to the right person - assert_eq!(erc20.balance_of(charlie), 10); - } - - #[test] - fn events_work() { - let alice = AccountId::from([0x0; 32]); - let bob = AccountId::from([0x1; 32]); - - // No events to start - env::test::set_caller::(alice); - assert_eq!(env::test::emitted_events::().count(), 0); - // Event should be emitted for initial minting - let mut erc20 = Erc20::deploy_mock(1234); - assert_eq!(env::test::emitted_events::().count(), 1); - // Event should be emitted for approvals - assert_eq!(erc20.approve(bob, 20), true); - assert_eq!(env::test::emitted_events::().count(), 2); - // Event should be emitted for transfers - assert_eq!(erc20.transfer(bob, 10), true); - assert_eq!(env::test::emitted_events::().count(), 3); - } -} diff --git a/examples/lang/erc721/.cargo/config b/examples/lang/erc721/.cargo/config deleted file mode 100644 index 7d5b52df77d..00000000000 --- a/examples/lang/erc721/.cargo/config +++ /dev/null @@ -1,4 +0,0 @@ -[target.wasm32-unknown-unknown] -rustflags = [ - "-C", "link-args=-z stack-size=65536 --import-memory" -] diff --git a/examples/lang/erc721/.ink/abi_gen/Cargo.toml b/examples/lang/erc721/.ink/abi_gen/Cargo.toml deleted file mode 100644 index 38f6556b1eb..00000000000 --- a/examples/lang/erc721/.ink/abi_gen/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "abi-gen" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" -publish = false - -[[bin]] -name = "abi-gen" -path = "main.rs" - -[dependencies] -contract = { path = "../..", package = "erc721", features = ["ink-generate-abi"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" diff --git a/examples/lang/erc721/.ink/abi_gen/main.rs b/examples/lang/erc721/.ink/abi_gen/main.rs deleted file mode 100644 index 885d6954cc3..00000000000 --- a/examples/lang/erc721/.ink/abi_gen/main.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() -> Result<(), std::io::Error> { - let abi = contract::ink_generate_abi(); - let contents = serde_json::to_string_pretty(&abi)?; - std::fs::create_dir("target").ok(); - std::fs::write("target/metadata.json", contents)?; - Ok(()) -} diff --git a/examples/lang/erc721/Cargo.toml b/examples/lang/erc721/Cargo.toml deleted file mode 100644 index e56359ae5d4..00000000000 --- a/examples/lang/erc721/Cargo.toml +++ /dev/null @@ -1,64 +0,0 @@ -[package] -name = "erc721" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -ink_abi = { path = "../../../abi", default-features = false, features = ["derive"], optional = true } -ink_core = { path = "../../../core", default-features = false } -ink_model = { path = "../../../model", default-features = false } -ink_lang = { path = "../../../lang", default-features = false } -ink_prelude = { path = "../../../prelude", default-features = false } - -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] } -type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true } - -[lib] -name = "erc721" -crate-type = [ - # Used for normal contract Wasm blobs. - "cdylib", - # Used for ABI generation. - "rlib", -] - -[features] -default = ["test-env"] -std = [ - "ink_abi/std", - "ink_core/std", - "ink_model/std", - "ink_lang/std", - "ink_prelude/std", - "type-metadata/std", - "scale/std", -] -test-env = [ - "std", - "ink_core/test-env", - "ink_model/test-env", - "ink_lang/test-env", -] -ink-generate-abi = [ - "std", - "ink_abi", - "type-metadata", - "ink_core/ink-generate-abi", - "ink_lang/ink-generate-abi", -] -ink-as-dependency = [] - -[profile.release] -panic = "abort" -lto = true -opt-level = "z" -overflow-checks = true - -[workspace] -members = [ - ".ink/abi_gen" -] -exclude = [ - ".ink" -] diff --git a/examples/lang/erc721/src/lib.rs b/examples/lang/erc721/src/lib.rs deleted file mode 100644 index d3a698a316e..00000000000 --- a/examples/lang/erc721/src/lib.rs +++ /dev/null @@ -1,526 +0,0 @@ -// Copyright 2018-2019 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. - -#![cfg_attr(not(feature = "std"), no_std)] - -use core::result::Result; -use ink_core::{ - env::DefaultSrmlTypes, - storage, -}; -use ink_lang::contract; -use ink_model; -use ink_prelude::format; -use scale::{ - Decode, - Encode, -}; - -pub type EnvHandler = ink_model::EnvHandler>; -pub type TokenId = u32; -pub type Counter = u32; - -#[cfg(feature = "ink-generate-abi")] -use type_metadata::Metadata; - -#[derive(Encode, Decode, Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "ink-generate-abi", derive(Metadata))] -pub enum Error { - NotOwner, - NotApproved, - TokenExists, - TokenNotFound, - CanNotInsert, - CanNotRemove, - CanNotGetCounter, - AccountZeroNotAllowed, -} - -impl Error { - fn to_u32(self) -> u32 { - self as u32 - } -} - -contract! { - #![env = DefaultSrmlTypes] - - /// Event deposited when a token transfer occurs - event Transfer { - from: AccountId, - to: AccountId, - id: TokenId, - } - - /// Event deposited when an approval occurs - event Approval { - owner: AccountId, - to: AccountId, - id: TokenId, - } - - /// The storage items for a typical ERC721 token implementation. - struct Erc721 { - token_owner: storage::HashMap, - token_approvals: storage::HashMap, - owned_tokens_count: storage::HashMap, - } - - impl Deploy for Erc721 { - fn deploy(&mut self) {} - } - - impl Erc721 { - /// External wrapper for `fn balance_of()`. - pub(external) fn get_balance(&self, owner: AccountId) -> u32 { - let balance = self.balance_of(&owner); - env.println(&format!("Erc721::balance_of(owner = {:?}) = {:?}", owner, balance)); - balance - } - - /// External wrapper for `fn owner_of()`. - pub(external) fn get_owner(&self, id: u32) -> AccountId { - let owner = self.owner_of(&id); - env.println(&format!("Erc721::owner_of(token = {:?}) = {:?}", id, owner)); - owner - } - - /// External wrapper for `fn mint()`. - pub(external) fn mint_token(&mut self, to: AccountId, id: u32) -> Result<(), u32> { - self.mint(env, &to, &id)?; - env.println(&format!("Erc721::minted(token = {:?}) = {:?}", id, to)); - Ok(()) - } - - /// External wrapper for `fn transfer_from()`. - pub(external) fn transfer_token(&mut self, from: AccountId, to: AccountId, id: u32) -> Result<(), u32> { - self.transfer_from(env, &from, &to, &id)?; - env.println(&format!("Erc721::transferred(token = {:?}) = {:?}", id, to)); - Ok(()) - } - - /// External wrapper for `fn burn()`. - pub(external) fn burn_token(&mut self, from: AccountId, id: u32) -> Result<(), u32> { - self.burn(env, &from, &id)?; - env.println(&format!("Erc721::burned(token = {:?}) = {:?}", id, from)); - Ok(()) - } - - /// External wrapper for `fn approve()`. - pub(external) fn approve_transfer(&mut self, to: AccountId, id: u32) -> Result<(), u32> { - self.approve(env, &to, &id)?; - env.println(&format!("Erc721::approved(token = {:?}) = {:?}", id, to)); - Ok(()) - } - } - - impl Erc721 { - /// Transfers token `id` `from` the sender to the `to` AccountId. - fn transfer_from(&mut self, env: &EnvHandler, from: &AccountId, to: &AccountId, id: &TokenId) -> Result<(), u32> { - let caller = env.caller(); - if !self.exists(id) { - return Err(Error::TokenNotFound.to_u32()) - }; - if !self.approved_or_owner(&caller, id){ - return Err(Error::NotApproved.to_u32()) - }; - - self.clear_approval(id)?; - self.remove_token_from(from, id)?; - self.add_token_to(to, id)?; - env.emit(Transfer { - from: *from, - to: *to, - id: *id, - }); - Ok(()) - } - - /// Removes existing approval from token `id`. - fn clear_approval(&mut self, id: &TokenId) -> Result<(), u32> { - if !self.token_approvals.contains_key(id) { - return Ok(()) - }; - - match self.token_approvals.remove(id) { - Some(_res) => Ok(()), - None => Err(Error::CanNotRemove.to_u32()) - } - } - - /// Removes token `id` from the owner. - fn remove_token_from(&mut self, from: &AccountId, id: &TokenId) -> Result<(), u32> { - if !self.exists(id) { - return Err(Error::TokenNotFound.to_u32()); - }; - - self.decrease_counter_of(from)?; - self.token_owner - .remove(id) - .ok_or(Error::CanNotRemove.to_u32())?; - Ok(()) - } - - /// Adds the token `id` to the `to` AccountID. - fn add_token_to(&mut self, to: &AccountId, id: &TokenId) -> Result<(), u32> { - if self.exists(id) { - return Err(Error::TokenExists.to_u32()) - }; - if *to == AccountId::from([0x0; 32]) { - return Err(Error::AccountZeroNotAllowed.to_u32()) - }; - - self.increase_counter_of(to)?; - if !self.token_owner.insert(*id, *to).is_none() { - return Err(Error::CanNotInsert.to_u32()) - } - Ok(()) - } - - /// Approve the passed AccountId to transfer the specified token - /// on behalf of the message's sender. - fn approve(&mut self, env: &EnvHandler, to: &AccountId, id: &TokenId) -> Result<(), u32> { - let caller = env.caller(); - if self.owner_of(id) != caller { - return Err(Error::NotOwner.to_u32()) - }; - if *to == AccountId::from([0x0; 32]) { - return Err(Error::AccountZeroNotAllowed.to_u32()) - }; - - if !self.token_approvals.insert(*id, *to).is_none() { - return Err(Error::CanNotInsert.to_u32()) - }; - env.emit(Approval { - owner: caller, - to: *to, - id: *id, - }); - Ok(()) - } - - /// Creates a unique token `id` assigned to the `to` AccountId. - fn mint(&mut self, env: &EnvHandler, to: &AccountId, id: &TokenId) -> Result<(), u32> { - if *to == AccountId::from([0x0; 32]) { - return Err(Error::AccountZeroNotAllowed.to_u32()) - }; - if self.exists(id) { - return Err(Error::TokenExists.to_u32()) - } - - if !self.token_owner.insert(*id, *to).is_none() { - return Err(Error::CanNotInsert.to_u32()) - } - self.increase_counter_of(to)?; - env.emit(Transfer { - from: AccountId::from([0x0; 32]), - to: *to, - id: *id, - }); - Ok(()) - } - - /// Destroys an existing token `id` owned by the caller. - fn burn(&mut self, env: &EnvHandler, from: &AccountId, id: &TokenId)-> Result<(), u32> { - let caller = env.caller(); - if !self.exists(id) { - return Err(Error::TokenNotFound.to_u32()) - }; - if self.owner_of(id) != caller && *from != AccountId::from([0x0; 32]) { - return Err(Error::NotOwner.to_u32()) - }; - - self.clear_approval(id)?; - self.decrease_counter_of(from)?; - self.token_owner - .remove(id) - .ok_or(Error::CanNotRemove.to_u32())?; - env.emit(Transfer { - from: *from, - to: AccountId::from([0x0; 32]), - id: *id, - }); - Ok(()) - } - - /// Increase token counter from the `of` AccountId. - fn increase_counter_of(&mut self, of: &AccountId) -> Result<(), u32> { - if self.balance_of(of) > 0 { - let count = self.owned_tokens_count - .get_mut(of) - .ok_or(Error::CanNotGetCounter.to_u32())?; - *count += 1; - return Ok(()) - } else { - match self.owned_tokens_count.insert(*of, 1) { - Some(_) => Err(Error::CanNotInsert.to_u32()), - None => Ok(()), - } - } - } - - /// Decrease token counter from the `of` AccountId. - fn decrease_counter_of(&mut self, of: &AccountId) -> Result<(), u32> { - let count = self.owned_tokens_count - .get_mut(of) - .ok_or(Error::CanNotGetCounter.to_u32())?; - *count -= 1; - Ok(()) - } - - /// Returns the total number of tokens from an account. - fn balance_of(&self, of: &AccountId) -> u32 { - *self.owned_tokens_count.get(of).unwrap_or(&0u32) - } - - /// Returns the owner of a token or AccountId 0x0 if it does not exists. - fn owner_of(&self, id: &TokenId) -> AccountId { - *self.token_owner.get(id).unwrap_or(&AccountId::from([0x0; 32])) - } - - /// Returns the approved AccountId froma token `id` - /// or AccountId 0x0 if it does not exists. - fn approved_for(&self, id: &TokenId) -> AccountId { - *self.token_approvals.get(id).unwrap_or(&AccountId::from([0x0; 32])) - } - - /// Returns true if the AccountId `from` is the owner of token `id` - /// or it has been approved on behalf of the token `id` owner. - fn approved_or_owner(&self, from: &AccountId, id: &TokenId) -> bool { - *from != AccountId::from([0x0; 32]) && - (*from == self.owner_of(id) || *from == self.approved_for(id)) - } - - /// Returns true if token `id` exists or false if it does not. - fn exists(&self, id: &TokenId) -> bool { - self.token_owner.get(id).is_some() && self.token_owner.contains_key(id) - } - } -} - -#[cfg(all(test, feature = "test-env"))] -mod tests { - use super::*; - use ink_core::env; - type Types = ink_core::env::DefaultSrmlTypes; - type Erc721Test = test::TestableErc721; - - /// Generate testing accounts. - fn generate_accounts(length: u8) -> Option> { - if length > 0 { - let mut accounts: Vec = vec![AccountId::from([0x0; 32]); 1]; - for n in 1..=length { - accounts.push(AccountId::from([n; 32])); - } - Some(accounts) - } else { - None - } - } - - /// Deploys an ERC721 test contract. - fn initialize_erc721(from: AccountId) -> Erc721Test { - env::test::set_caller::(from); - Erc721::deploy_mock() - } - - #[test] - fn deployment_works() { - let accounts = generate_accounts(3).unwrap(); - let erc721 = initialize_erc721(accounts[0]); - // AccountId 0 can not owns tokens. - assert_eq!(erc721.get_balance(accounts[0]), 0); - // Token 0 does not exists. - assert_eq!(erc721.get_owner(0), accounts[0]); - } - - #[test] - fn mint_works() { - let accounts = generate_accounts(2).unwrap(); - let mut erc721 = initialize_erc721(accounts[0]); - // Token 1 does not exists. - assert_eq!(erc721.get_owner(1), accounts[0]); - // AccountId 1 does not owns tokens. - assert_eq!(erc721.get_balance(accounts[1]), 0); - // Create token Id 1. - assert_eq!(erc721.mint_token(accounts[1], 1), Ok(())); - // AccountId 1 owns 1 token. - assert_eq!(erc721.get_balance(accounts[1]), 1); - // AccountId 1 owns token Id 1. - assert_eq!(erc721.get_owner(1), accounts[1]); - // Create token Id 2 owned by AccountId 1. - assert_eq!(erc721.mint_token(accounts[1], 2), Ok(())); - // AccountId 1 owns 2 tokens. - assert_eq!(erc721.get_balance(accounts[1]), 2); - // Token Id 2 is owned by AccountId 1. - assert_eq!(erc721.get_owner(2), accounts[1]); - // Create token Id 3. - assert_eq!(erc721.mint_token(accounts[2], 3), Ok(())); - // Account Id 2 owns 1 token. - assert_eq!(erc721.get_balance(accounts[2]), 1); - // Token Id 3 is owned by AccountId 2. - assert_eq!(erc721.get_owner(3), accounts[2]); - } - - #[test] - fn mint_existing_should_fail() { - let accounts = generate_accounts(2).unwrap(); - let mut erc721 = initialize_erc721(accounts[0]); - // Create token Id 1. - assert_eq!(erc721.mint_token(accounts[1], 1), Ok(())); - // AccountId 1 owns 1 token. - assert_eq!(erc721.get_balance(accounts[1]), 1); - // AccountId 1 owns token Id 1. - assert_eq!(erc721.get_owner(1), accounts[1]); - // Cannot create token Id if it exists. - // AccountId 2 cannot own token Id 1. - assert_eq!( - erc721.mint_token(accounts[2], 1), - Err(Error::TokenExists.to_u32()) - ); - // AccountId 2 does not owns tokens. - assert_eq!(erc721.get_balance(accounts[2]), 0); - // AccountId 1 owns token Id 1. - assert_eq!(erc721.get_owner(1), accounts[1]); - } - - #[test] - fn transfer_works() { - let accounts = generate_accounts(2).unwrap(); - let mut erc721 = initialize_erc721(accounts[0]); - // Create token Id 1. - assert_eq!(erc721.mint_token(accounts[1], 1), Ok(())); - // AccountId 1 owns 1 token. - assert_eq!(erc721.get_balance(accounts[1]), 1); - // AccountId 1 owns token Id 1. - assert_eq!(erc721.get_owner(1), accounts[1]); - // Change transaction caller to AccountId 1. - env::test::set_caller::(accounts[1]); - // Transfer token Id 1 from AccountId 1 to AccountId 2. - assert_eq!(erc721.transfer_token(accounts[1], accounts[2], 1), Ok(())); - // AccountId 1 does not owns tokens. - assert_eq!(erc721.get_balance(accounts[1]), 0); - // AccountId 2 owns 1 token. - assert_eq!(erc721.get_balance(accounts[2]), 1); - // AccountId 2 owns token Id 1. - assert_eq!(erc721.get_owner(1), accounts[2]); - } - - #[test] - fn invalid_transfer_should_fail() { - let accounts = generate_accounts(3).unwrap(); - let mut erc721 = initialize_erc721(accounts[0]); - // Transfer token fails if it does not exists. - assert_eq!( - erc721.transfer_token(accounts[1], accounts[2], 2), - Err(Error::TokenNotFound.to_u32()) - ); - // Token Id 2 does not exists. - assert_eq!(erc721.get_owner(2), accounts[0]); - // Create token Id 2. - assert_eq!(erc721.mint_token(accounts[1], 2), Ok(())); - // AccountId 1 owns 1 token. - assert_eq!(erc721.get_balance(accounts[1]), 1); - // Token Id 2 is owned by AccountId 1. - assert_eq!(erc721.get_owner(2), accounts[1]); - // Change transaction caller to AccountId 2. - env::test::set_caller::(accounts[2]); - // AccountId 2 cannot transfer not owned tokens. - assert_eq!( - erc721.transfer_token(accounts[1], accounts[2], 2), - Err(Error::NotApproved.to_u32()) - ); - } - - #[test] - fn burn_works() { - let accounts = generate_accounts(3).unwrap(); - let mut erc721 = initialize_erc721(accounts[0]); - // Create token Id 1. - assert_eq!(erc721.mint_token(accounts[1], 1), Ok(())); - // AccountId 1 owns 1 token. - assert_eq!(erc721.get_balance(accounts[1]), 1); - // AccountId 1 owns token Id 1. - assert_eq!(erc721.get_owner(1), accounts[1]); - // Change transaction caller to AccountId 1. - env::test::set_caller::(accounts[1]); - // Transfer Token Id 1 from AccountId 1 to AccountId 2. - assert_eq!(erc721.transfer_token(accounts[1], accounts[2], 1), Ok(())); - // AccountId 1 does not owns tokens. - assert_eq!(erc721.get_balance(accounts[1]), 0); - // AccountId 2 owns token 1. - assert_eq!(erc721.get_balance(accounts[2]), 1); - // Token Id 1 is owned by AccountId 2. - assert_eq!(erc721.get_owner(1), accounts[2]); - // Change transaction caller to AccountId 2. - env::test::set_caller::(accounts[2]); - // Destroy token Id 1. - assert_eq!(erc721.burn_token(accounts[2], 1), Ok(())); - // AccountId 2 does not owns tokens. - assert_eq!(erc721.get_balance(accounts[2]), 0); - } - - #[test] - fn approved_transfer_works() { - let accounts = generate_accounts(3).unwrap(); - let mut erc721 = initialize_erc721(accounts[0]); - // Create token Id 1. - assert_eq!(erc721.mint_token(accounts[1], 1), Ok(())); - // Token Id 1 is owned by AccountId 1. - assert_eq!(erc721.get_owner(1), accounts[1]); - // Change transaction caller to AccountId 1. - env::test::set_caller::(accounts[1]); - // Approve token Id 1 transfer for AccountId 2 on behalf of the owner. - assert_eq!(erc721.approve_transfer(accounts[2], 1), Ok(())); - // Change transaction caller to AccountId 2. - env::test::set_caller::(accounts[2]); - // Transfer token Id 1 from AccountId 1 to AccountId 3. - assert_eq!(erc721.transfer_token(accounts[1], accounts[3], 1), Ok(())); - // TokenId 3 is owned by AccountId 3. - assert_eq!(erc721.get_owner(1), accounts[3]); - // AccountId 1 does not owns tokens. - assert_eq!(erc721.get_balance(accounts[1]), 0); - // AccountId 2 does not owns tokens. - assert_eq!(erc721.get_balance(accounts[2]), 0); - // AccountId 3 owns 1 token. - assert_eq!(erc721.get_balance(accounts[3]), 1); - } - - #[test] - fn not_approved_transfer_should_fail() { - let accounts = generate_accounts(3).unwrap(); - let mut erc721 = initialize_erc721(accounts[0]); - // Create token Id 1. - assert_eq!(erc721.mint_token(accounts[1], 1), Ok(())); - // AccountId 1 owns 1 token. - assert_eq!(erc721.get_balance(accounts[1]), 1); - // AccountId 2 does not owns tokens. - assert_eq!(erc721.get_balance(accounts[2]), 0); - // AccountId 3 does not owns tokens. - assert_eq!(erc721.get_balance(accounts[3]), 0); - // Change transaction caller to AccountId 2. - env::test::set_caller::(accounts[2]); - // AccountId 3 is not approved by AccountId 1. - assert_eq!( - erc721.transfer_token(accounts[1], accounts[3], 1), - Err(Error::NotApproved.to_u32()) - ); - // AccountId 1 owns 1 token. - assert_eq!(erc721.get_balance(accounts[1]), 1); - // AccountId 2 does not owns tokens. - assert_eq!(erc721.get_balance(accounts[2]), 0); - // AccountId 3 does not owns tokens. - assert_eq!(erc721.get_balance(accounts[3]), 0); - } -} diff --git a/examples/lang/events/.cargo/config b/examples/lang/events/.cargo/config deleted file mode 100644 index 9ed784e6e2d..00000000000 --- a/examples/lang/events/.cargo/config +++ /dev/null @@ -1,4 +0,0 @@ -[target.wasm32-unknown-unknown] -rustflags = [ - "-C", "link-args=-z stack-size=65536 --import-memory" -] \ No newline at end of file diff --git a/examples/lang/events/.gitignore b/examples/lang/events/.gitignore deleted file mode 100644 index bf910de10af..00000000000 --- a/examples/lang/events/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Ignore build artifacts from the local tests sub-crate. -/target/ - -# Ignore backup files creates by cargo fmt. -**/*.rs.bk - -# Remove Cargo.lock when creating an executable, leave it for libraries -# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock -Cargo.lock \ No newline at end of file diff --git a/examples/lang/events/.ink/abi_gen/Cargo.toml b/examples/lang/events/.ink/abi_gen/Cargo.toml deleted file mode 100644 index 6c513fd467b..00000000000 --- a/examples/lang/events/.ink/abi_gen/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "abi-gen" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" -publish = false - -[[bin]] -name = "abi-gen" -path = "main.rs" - -[dependencies] -contract = { path = "../..", package = "events", features = ["ink-generate-abi"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" diff --git a/examples/lang/events/.ink/abi_gen/main.rs b/examples/lang/events/.ink/abi_gen/main.rs deleted file mode 100644 index 87a58e3d07a..00000000000 --- a/examples/lang/events/.ink/abi_gen/main.rs +++ /dev/null @@ -1,8 +0,0 @@ - -fn main() -> Result<(), std::io::Error> { - let abi = contract::ink_generate_abi(); - let contents = serde_json::to_string_pretty(&abi)?; - std::fs::create_dir("target").ok(); - std::fs::write("target/metadata.json", contents)?; - Ok(()) -} diff --git a/examples/lang/events/Cargo.toml b/examples/lang/events/Cargo.toml deleted file mode 100644 index 827886c2921..00000000000 --- a/examples/lang/events/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -[package] -name = "events" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -ink_abi = { path = "../../../abi", default-features = false, features = ["derive"], optional = true } -ink_core = { path = "../../../core", default-features = false } -ink_model = { path = "../../../model", default-features = false } -ink_lang = { path = "../../../lang", default-features = false } - -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] } -type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true } - -[lib] -name = "events" -crate-type = [ - # Used for normal contract Wasm blobs. - "cdylib", - # Used for ABI generation. - "rlib", -] - -[features] -default = ["test-env"] -std = [ - "ink_abi/std", - "ink_core/std", - "ink_model/std", - "ink_lang/std", - "scale/std", - "type-metadata/std", -] -test-env = [ - "std", - "ink_core/test-env", - "ink_model/test-env", - "ink_lang/test-env", -] -ink-generate-abi = [ - "std", - "ink_abi", - "type-metadata", - "ink_core/ink-generate-abi", - "ink_lang/ink-generate-abi", -] -ink-as-dependency = [] - -[profile.release] -panic = "abort" -lto = true -opt-level = "z" -overflow-checks = true - -[workspace] -members = [ - ".ink/abi_gen" -] -exclude = [ - ".ink" -] diff --git a/examples/lang/events/src/lib.rs b/examples/lang/events/src/lib.rs deleted file mode 100644 index 2edef1257aa..00000000000 --- a/examples/lang/events/src/lib.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2018-2019 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. - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_core::storage; -use ink_core::env::{ - ContractEnv, - DefaultSrmlTypes, -}; -use ink_lang::contract; - -contract! { - #![env = DefaultSrmlTypes] - - /// Tests emitting of custom defined events. - struct CallCounter { - /// A simple counter for the calls. - count: storage::Value, - } - - impl Deploy for CallCounter { - fn deploy(&mut self) { - self.count.set(0) - } - } - - event IncCalled { current: u32 } - event DecCalled { current: u32 } - - impl CallCounter { - /// Increments the internal counter. - /// - /// # Note - /// - /// Also emits an event. - pub(external) fn inc(&mut self) { - self.count += 1; - env.emit(IncCalled { current: *self.count }); - } - - /// Decrements the internal counter. - /// - /// # Note - /// - /// Also emits an event. - pub(external) fn dec(&mut self) { - self.dec_internal(env); - } - } - - impl CallCounter { - fn dec_internal(&mut self, env: &mut ink_model::EnvHandler>) { - self.count -= 1; - env.emit(DecCalled { current: *self.count }); - } - } -} - -#[cfg(all(test, feature = "test-env"))] -mod tests { - use ink_core::env; - - use super::*; - - #[test] - fn it_works() { - let mut contract = CallCounter::deploy_mock(); - assert_eq!(env::test::emitted_events::().count(), 0); - contract.inc(); - assert_eq!(env::test::emitted_events::().count(), 1); - contract.dec(); - assert_eq!(env::test::emitted_events::().count(), 2); - } -} diff --git a/examples/lang/flipper/.cargo/config b/examples/lang/flipper/.cargo/config deleted file mode 100644 index 9ed784e6e2d..00000000000 --- a/examples/lang/flipper/.cargo/config +++ /dev/null @@ -1,4 +0,0 @@ -[target.wasm32-unknown-unknown] -rustflags = [ - "-C", "link-args=-z stack-size=65536 --import-memory" -] \ No newline at end of file diff --git a/examples/lang/flipper/.gitignore b/examples/lang/flipper/.gitignore deleted file mode 100644 index bf910de10af..00000000000 --- a/examples/lang/flipper/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Ignore build artifacts from the local tests sub-crate. -/target/ - -# Ignore backup files creates by cargo fmt. -**/*.rs.bk - -# Remove Cargo.lock when creating an executable, leave it for libraries -# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock -Cargo.lock \ No newline at end of file diff --git a/examples/lang/flipper/.ink/abi_gen/Cargo.toml b/examples/lang/flipper/.ink/abi_gen/Cargo.toml deleted file mode 100644 index 6d66e0a1fc6..00000000000 --- a/examples/lang/flipper/.ink/abi_gen/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "abi-gen" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" -publish = false - -[[bin]] -name = "abi-gen" -path = "main.rs" - -[dependencies] -contract = { path = "../..", package = "flipper", features = ["ink-generate-abi"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" diff --git a/examples/lang/flipper/.ink/abi_gen/main.rs b/examples/lang/flipper/.ink/abi_gen/main.rs deleted file mode 100644 index 87a58e3d07a..00000000000 --- a/examples/lang/flipper/.ink/abi_gen/main.rs +++ /dev/null @@ -1,8 +0,0 @@ - -fn main() -> Result<(), std::io::Error> { - let abi = contract::ink_generate_abi(); - let contents = serde_json::to_string_pretty(&abi)?; - std::fs::create_dir("target").ok(); - std::fs::write("target/metadata.json", contents)?; - Ok(()) -} diff --git a/examples/lang/flipper/Cargo.toml b/examples/lang/flipper/Cargo.toml deleted file mode 100644 index 56250fb9050..00000000000 --- a/examples/lang/flipper/Cargo.toml +++ /dev/null @@ -1,64 +0,0 @@ -[package] -name = "flipper" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -ink_abi = { path = "../../../abi", default-features = false, features = ["derive"], optional = true } -ink_core = { path = "../../../core", default-features = false } -ink_model = { path = "../../../model", default-features = false } -ink_lang = { path = "../../../lang", default-features = false } -ink_prelude = { path = "../../../prelude", default-features = false } - -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] } -type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true } - -[lib] -name = "flipper" -crate-type = [ - # Used for normal contract Wasm blobs. - "cdylib", - # Used for ABI generation. - "rlib", -] - -[features] -default = ["test-env"] -std = [ - "ink_abi/std", - "ink_core/std", - "ink_model/std", - "ink_lang/std", - "ink_prelude/std", - "scale/std", - "type-metadata/std", -] -test-env = [ - "std", - "ink_core/test-env", - "ink_model/test-env", - "ink_lang/test-env", -] -ink-generate-abi = [ - "std", - "ink_abi", - "type-metadata", - "ink_core/ink-generate-abi", - "ink_lang/ink-generate-abi", -] -ink-as-dependency = [] - -[profile.release] -panic = "abort" -lto = true -opt-level = "z" -overflow-checks = true - -[workspace] -members = [ - ".ink/abi_gen" -] -exclude = [ - ".ink" -] diff --git a/examples/lang/flipper/src/lib.rs b/examples/lang/flipper/src/lib.rs deleted file mode 100644 index e03172695d8..00000000000 --- a/examples/lang/flipper/src/lib.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2018-2019 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. - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_core::{ - env::DefaultSrmlTypes, - storage, -}; -use ink_lang::contract; -use ink_prelude::format; - -contract! { - #![env = DefaultSrmlTypes] - - /// This simple dummy contract has a `bool` value that can - /// alter between `true` and `false` using the `flip` message. - /// Users can retrieve its current state using the `get` message. - struct Flipper { - /// The current state of our flag. - value: storage::Value, - } - - impl Deploy for Flipper { - /// Initializes our state to `false` upon deploying our smart contract. - fn deploy(&mut self) { - self.value.set(false) - } - } - - impl Flipper { - /// Flips the current state of our smart contract. - pub(external) fn flip(&mut self) { - *self.value = !*self.value; - } - - /// Returns the current state. - pub(external) fn get(&self) -> bool { - env.println(&format!("Flipper Value: {:?}", *self.value)); - *self.value - } - } -} - -#[cfg(test)] -mod tests { - use super::Flipper; - - #[test] - fn it_works() { - let mut flipper = Flipper::deploy_mock(); - assert_eq!(flipper.get(), false); - flipper.flip(); - assert_eq!(flipper.get(), true); - } -} diff --git a/examples/lang/incrementer/.cargo/config b/examples/lang/incrementer/.cargo/config deleted file mode 100644 index 9ed784e6e2d..00000000000 --- a/examples/lang/incrementer/.cargo/config +++ /dev/null @@ -1,4 +0,0 @@ -[target.wasm32-unknown-unknown] -rustflags = [ - "-C", "link-args=-z stack-size=65536 --import-memory" -] \ No newline at end of file diff --git a/examples/lang/incrementer/.gitignore b/examples/lang/incrementer/.gitignore deleted file mode 100644 index bf910de10af..00000000000 --- a/examples/lang/incrementer/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Ignore build artifacts from the local tests sub-crate. -/target/ - -# Ignore backup files creates by cargo fmt. -**/*.rs.bk - -# Remove Cargo.lock when creating an executable, leave it for libraries -# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock -Cargo.lock \ No newline at end of file diff --git a/examples/lang/incrementer/.ink/abi_gen/Cargo.toml b/examples/lang/incrementer/.ink/abi_gen/Cargo.toml deleted file mode 100644 index be1383b11b5..00000000000 --- a/examples/lang/incrementer/.ink/abi_gen/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "abi-gen" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" -publish = false - -[[bin]] -name = "abi-gen" -path = "main.rs" - -[dependencies] -contract = { path = "../..", package = "incrementer", features = ["ink-generate-abi"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" diff --git a/examples/lang/incrementer/.ink/abi_gen/main.rs b/examples/lang/incrementer/.ink/abi_gen/main.rs deleted file mode 100644 index 87a58e3d07a..00000000000 --- a/examples/lang/incrementer/.ink/abi_gen/main.rs +++ /dev/null @@ -1,8 +0,0 @@ - -fn main() -> Result<(), std::io::Error> { - let abi = contract::ink_generate_abi(); - let contents = serde_json::to_string_pretty(&abi)?; - std::fs::create_dir("target").ok(); - std::fs::write("target/metadata.json", contents)?; - Ok(()) -} diff --git a/examples/lang/incrementer/Cargo.toml b/examples/lang/incrementer/Cargo.toml deleted file mode 100644 index 28098e1cab1..00000000000 --- a/examples/lang/incrementer/Cargo.toml +++ /dev/null @@ -1,65 +0,0 @@ -[package] -name = "incrementer" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -ink_abi = { path = "../../../abi", default-features = false, features = ["derive"], optional = true } -ink_core = { path = "../../../core", default-features = false } -ink_model = { path = "../../../model", default-features = false } -ink_lang = { path = "../../../lang", default-features = false } -ink_prelude = { path = "../../../prelude", default-features = false } - -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] } -type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true } - -[lib] -name = "incrementer" -crate-type = [ - # Used for normal contract Wasm blobs. - "cdylib", - # Used for ABI generation. - "rlib", -] - -[features] -default = ["test-env"] -std = [ - "ink_abi/std", - "ink_core/std", - "ink_model/std", - "ink_lang/std", - "ink_prelude/std", - "scale/std", - "type-metadata/std", -] -test-env = [ - "std", - "ink_core/test-env", - "ink_model/test-env", - "ink_lang/test-env", - "type-metadata/std", -] -ink-generate-abi = [ - "std", - "ink_abi", - "type-metadata", - "ink_core/ink-generate-abi", - "ink_lang/ink-generate-abi", -] -ink-as-dependency = [] - -[profile.release] -panic = "abort" -lto = true -opt-level = "z" -overflow-checks = true - -[workspace] -members = [ - ".ink/abi_gen" -] -exclude = [ - ".ink" -] diff --git a/examples/lang/incrementer/src/lib.rs b/examples/lang/incrementer/src/lib.rs deleted file mode 100644 index d09c6591710..00000000000 --- a/examples/lang/incrementer/src/lib.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2018-2019 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. - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_core::storage; -use ink_lang::contract; -use ink_prelude::format; - -contract! { - #![env = ink_core::env::DefaultSrmlTypes] - - /// A simple incrementer contract that can only increment, - /// compare and return its internal value. - struct Incrementer { - /// The current value. - value: storage::Value, - } - - impl Deploy for Incrementer { - /// Initializes the value to the initial value. - fn deploy(&mut self, init_value: u32) { - self.value.set(init_value) - } - } - - impl Incrementer { - /// Flips the current state of our smart contract. - pub(external) fn inc(&mut self, by: u32) { - env.println(&format!("Incrementer::inc by = {:?}", by)); - self.value += by; - } - - /// Returns the current state. - pub(external) fn get(&self) -> u32 { - env.println(&format!("Incrementer::get = {:?}", *self.value)); - *self.value - } - - /// Returns `true` if the internal value is greater than or equal to the provided value. - pub(external) fn compare(&self, with: u32) -> bool { - env.println(&format!("Incrementer::compare self.value >= with = {:?}", *self.value >= with)); - *self.value >= with - } - } -} - -#[cfg(test)] -mod tests { - use super::Incrementer; - - #[test] - fn it_works() { - let mut incrementer = Incrementer::deploy_mock(5); - assert_eq!(incrementer.get(), 5); - incrementer.inc(42); - assert_eq!(incrementer.get(), 47); - incrementer.inc(0); - assert_eq!(incrementer.get(), 47); - } -} diff --git a/examples/lang/shared_vec/.cargo/config b/examples/lang/shared_vec/.cargo/config deleted file mode 100644 index 9ed784e6e2d..00000000000 --- a/examples/lang/shared_vec/.cargo/config +++ /dev/null @@ -1,4 +0,0 @@ -[target.wasm32-unknown-unknown] -rustflags = [ - "-C", "link-args=-z stack-size=65536 --import-memory" -] \ No newline at end of file diff --git a/examples/lang/shared_vec/.gitignore b/examples/lang/shared_vec/.gitignore deleted file mode 100644 index bf910de10af..00000000000 --- a/examples/lang/shared_vec/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Ignore build artifacts from the local tests sub-crate. -/target/ - -# Ignore backup files creates by cargo fmt. -**/*.rs.bk - -# Remove Cargo.lock when creating an executable, leave it for libraries -# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock -Cargo.lock \ No newline at end of file diff --git a/examples/lang/shared_vec/.ink/abi_gen/Cargo.toml b/examples/lang/shared_vec/.ink/abi_gen/Cargo.toml deleted file mode 100644 index 5964dfa9560..00000000000 --- a/examples/lang/shared_vec/.ink/abi_gen/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "abi-gen" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" -publish = false - -[[bin]] -name = "abi-gen" -path = "main.rs" - -[dependencies] -contract = { path = "../..", package = "shared_vec", features = ["ink-generate-abi"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" diff --git a/examples/lang/shared_vec/.ink/abi_gen/main.rs b/examples/lang/shared_vec/.ink/abi_gen/main.rs deleted file mode 100644 index 87a58e3d07a..00000000000 --- a/examples/lang/shared_vec/.ink/abi_gen/main.rs +++ /dev/null @@ -1,8 +0,0 @@ - -fn main() -> Result<(), std::io::Error> { - let abi = contract::ink_generate_abi(); - let contents = serde_json::to_string_pretty(&abi)?; - std::fs::create_dir("target").ok(); - std::fs::write("target/metadata.json", contents)?; - Ok(()) -} diff --git a/examples/lang/shared_vec/Cargo.toml b/examples/lang/shared_vec/Cargo.toml deleted file mode 100644 index 580d4ace689..00000000000 --- a/examples/lang/shared_vec/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -[package] -name = "shared_vec" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -ink_abi = { path = "../../../abi", default-features = false, features = ["derive"], optional = true } -ink_core = { path = "../../../core", default-features = false } -ink_model = { path = "../../../model", default-features = false } -ink_lang = { path = "../../../lang", default-features = false } - -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] } -type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true } - -[lib] -name = "shared_vec" -crate-type = [ - # Used for normal contract Wasm blobs. - "cdylib", - # Used for ABI generation. - "rlib", -] - -[features] -default = ["test-env"] -std = [ - "ink_abi/std", - "ink_core/std", - "ink_model/std", - "ink_lang/std", - "scale/std", - "type-metadata/std", -] -test-env = [ - "std", - "ink_core/test-env", - "ink_model/test-env", - "ink_lang/test-env", -] -ink-generate-abi = [ - "std", - "ink_abi", - "type-metadata", - "ink_core/ink-generate-abi", - "ink_lang/ink-generate-abi", -] -ink-as-dependency = [] - -[profile.release] -panic = "abort" -lto = true -opt-level = "z" -overflow-checks = true - -[workspace] -members = [ - ".ink/abi_gen" -] -exclude = [ - ".ink" -] diff --git a/examples/lang/shared_vec/src/lib.rs b/examples/lang/shared_vec/src/lib.rs deleted file mode 100644 index 41f4f8d8304..00000000000 --- a/examples/lang/shared_vec/src/lib.rs +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2018-2019 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. - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_core::{storage, storage::Flush}; -use ink_lang::contract; -use scale::{Encode, Decode}; - -#[cfg(feature = "ink-generate-abi")] -use type_metadata::Metadata; - -/// Access rights to the shared vector. -#[derive(Encode, Decode)] -#[cfg_attr(feature = "ink-generate-abi", derive(Metadata))] -pub struct Access { - pub begin: Option, - pub end: Option, -} - -impl Flush for Access {} - -impl Access { - /// Creates unlimited access rights. - pub fn unlimited() -> Self { - Access { begin: None, end: None } - } - - /// Returns `true` if the access is unlimited. - pub fn is_unlimited(&self) -> bool { - self.begin.is_none() && self.end.is_none() - } - - /// Creates new limited access rights. - pub fn new(begin: B, end: E) -> Self - where - B: Into>, - E: Into>, - { - let begin = begin.into(); - let end = end.into(); - assert!(begin <= end); - Access { begin, end } - } - - /// Returns `true` if the given index is within access rights. - pub fn contains(&self, index: u32) -> bool { - let begin = self.begin.unwrap_or(0); - let end = self.end.unwrap_or(core::u32::MAX); - begin <= index && index <= end - } -} - -pub type ErrNo = u32; -pub type Result = core::result::Result; - -const ACCESS_NOT_ALLOWED: ErrNo = 1; -const NOT_REGISTERED: ErrNo = 2; -const OUT_OF_BOUNDS: ErrNo = 3; - -contract! { - #![env = ink_core::env::DefaultSrmlTypes] - - /// A shared vector that is accessiable to a subset of allowed mutators. - struct SharedVec { - /// The allowed mutators. - /// - /// They can operate on a range within the shared vector. - /// The range is defined by `(start,end)` where `start` and `end` - /// refer to the zero-starting index. A value of `None` means - /// that there is no lower or upper bound. - mutators: storage::HashMap, - /// The shared vector. - vec: storage::Vec, - } - - /// Fires whenever a new mutator is registered - /// or when a mutators access rights are changed. - event Register { - /// The mutator. - mutator: AccountId, - /// The begin access index. - begin: Option, - /// The end access index. - end: Option, - } - - /// Fires whenever a mutator pushes the vector successfully. - event Push { - /// The mutator. - mutator: AccountId, - /// The pushed value. - value: i32, - } - - /// Fires whenever a mutator changes the vector. - event Mutate { - /// The index where the change happened. - at: u32, - /// The new value. - value: i32, - } - - impl Deploy for SharedVec { - /// Initializes the value to the initial value. - fn deploy(&mut self) { - self.mutators.insert(env.caller(), Access::unlimited()); - } - } - - impl SharedVec { - /// Returns the users access if registered or an appropriate error. - fn validate_access(&self, mutator: AccountId) -> Result<&Access, u32> { - if let Some(access) = self.mutators.get(&mutator) { - Ok(access) - } else { - Err(NOT_REGISTERED) - } - } - - /// Pushes a new value to the shared vector. - /// - /// # Errors - /// - /// - If the caller does not have unlimited access rights. - pub(external) fn push(&mut self, value: i32) -> Result<(), u32> { - let access = self.validate_access(env.caller())?; - if !access.is_unlimited() { - return Err(ACCESS_NOT_ALLOWED) - } - self.vec.push(value); - env.emit(Push { mutator: env.caller(), value }); - Ok(()) - } - - /// Registers a new user with the given access rights. - /// - /// Can also be used to change access rights of an already existing user. - /// - /// # Errors - /// - /// - If the caller does not have unlimited access rights. - pub(external) fn register( - &mut self, - mutator: AccountId, - begin: Option, - end: Option - ) -> Result<(), u32> { - let access = self.validate_access(env.caller())?; - if !access.is_unlimited() { - return Err(ACCESS_NOT_ALLOWED) - } - self.mutators.insert(mutator, Access::new(begin, end)); - env.emit(Register { mutator, begin, end }); - Ok(()) - } - - /// Sets the value at the given position to the given value. - /// - /// Returns the previous value. - /// - /// # Errors - /// - /// - If the given position is out of bounds. - /// - If the caller does not have the required access rights. - pub(external) fn set(&mut self, at: u32, to: i32) -> Result { - let access = self.validate_access(env.caller())?; - if !access.contains(at) { - return Err(ACCESS_NOT_ALLOWED) - } - let res = self.vec - .replace(at, move || to) - .ok_or(OUT_OF_BOUNDS)?; - env.emit(Mutate { at, value: to }); - Ok(res) - } - - /// Returns the value of the shared vector at the given position - /// or `None` if the access is out of bounds. - pub(external) fn get(&self, at: u32) -> Option { - self.vec.get(at).cloned() - } - - /// Returns the length of the shared vector. - pub(external) fn len(&self) -> u32 { - self.vec.len() - } - } -} - -#[cfg(test)] -mod tests { - use std::convert::TryFrom; - - use ink_core::env; - use ink_core::env::DefaultSrmlTypes; - - use super::*; - - #[test] - fn it_works() { - let alice = AccountId::try_from([0x0; 32]).unwrap(); - let bob = AccountId::try_from([0x1; 32]).unwrap(); - let charly = AccountId::try_from([0x2; 32]).unwrap(); - - env::test::set_caller::(alice); - - let mut shared_vec = SharedVec::deploy_mock(); - assert_eq!(shared_vec.len(), 0); - assert_eq!(shared_vec.push(5), Ok(())); - assert_eq!(shared_vec.len(), 1); - assert_eq!(shared_vec.register(bob, Some(0), Some(1)), Ok(())); - assert_eq!(shared_vec.push(42), Ok(())); - assert_eq!(shared_vec.push(1337), Ok(())); - assert_eq!(shared_vec.push(77), Ok(())); - assert_eq!(shared_vec.len(), 4); - assert_eq!(shared_vec.set(1, 1000), Ok(42)); - assert_eq!(shared_vec.set(2, 2000), Ok(1337)); - assert_eq!(shared_vec.set(5, 3000), Err(OUT_OF_BOUNDS)); - assert_eq!(shared_vec.get(0), Some(5)); - assert_eq!(shared_vec.get(1), Some(1000)); - assert_eq!(shared_vec.get(2), Some(2000)); - assert_eq!(shared_vec.get(3), Some(77)); - - env::test::set_caller::(bob); - - assert_eq!(shared_vec.set(1, 999), Ok(1000)); - assert_eq!(shared_vec.set(2, 1999), Err(ACCESS_NOT_ALLOWED)); - assert_eq!(shared_vec.set(5, 3000), Err(ACCESS_NOT_ALLOWED)); - assert_eq!(shared_vec.register(charly, Some(0), Some(2)), Err(ACCESS_NOT_ALLOWED)); - assert_eq!(shared_vec.get(0), Some(5)); - assert_eq!(shared_vec.get(1), Some(999)); - assert_eq!(shared_vec.get(2), Some(2000)); - assert_eq!(shared_vec.get(3), Some(77)); - - env::test::set_caller::(charly); - - assert_eq!(shared_vec.set(1, 888), Err(NOT_REGISTERED)); - assert_eq!(shared_vec.set(5, 3000), Err(NOT_REGISTERED)); - assert_eq!(shared_vec.register(charly, Some(1), Some(1)), Err(NOT_REGISTERED)); - assert_eq!(shared_vec.get(0), Some(5)); - assert_eq!(shared_vec.get(1), Some(999)); - assert_eq!(shared_vec.get(2), Some(2000)); - assert_eq!(shared_vec.get(3), Some(77)); - } -} diff --git a/examples/lang2/delegator/adder/lib.rs b/examples/lang2/delegator/adder/lib.rs index 9bbb6a410da..3e4689c34c6 100644 --- a/examples/lang2/delegator/adder/lib.rs +++ b/examples/lang2/delegator/adder/lib.rs @@ -15,14 +15,14 @@ #![cfg_attr(not(feature = "std"), no_std)] #![feature(proc_macro_hygiene)] -#[cfg(not(feature = "ink-as-dependency"))] -use ink_core::storage; - -use accumulator::Accumulator; use ink_lang2 as ink; #[ink::contract(version = "0.1.0")] mod adder { + #[cfg(not(feature = "ink-as-dependency"))] + use ink_core::storage; + use accumulator::Accumulator; + /// Increments the underlying accumulator's value. #[ink(storage)] struct Adder { diff --git a/examples/lang2/delegator/lib.rs b/examples/lang2/delegator/lib.rs index c42c01ce0b6..5dbb57a2c12 100644 --- a/examples/lang2/delegator/lib.rs +++ b/examples/lang2/delegator/lib.rs @@ -15,18 +15,18 @@ #![cfg_attr(not(feature = "std"), no_std)] #![feature(proc_macro_hygiene)] -use ink_core::storage::{ - self, - Flush, -}; use ink_lang2 as ink; -use accumulator::Accumulator; -use adder::Adder; -use subber::Subber; - #[ink::contract(version = "0.1.0")] mod delegator { + use accumulator::Accumulator; + use adder::Adder; + use ink_core::storage::{ + self, + Flush, + }; + use subber::Subber; + /// Specifies the state of the delegator. /// /// In `Adder` state the delegator will delegate to the `Adder` contract @@ -73,19 +73,19 @@ mod delegator { self.which.set(Which::Adder); let total_balance = self.env().balance(); let accumulator = Accumulator::new(init_value) - .value(total_balance / 4) + .endowment(total_balance / 4) .using_code(accumulator_code_hash) - .create_using(self.env()) + .instantiate() .expect("failed at instantiating the `Accumulator` contract"); let adder = Adder::new(accumulator.clone()) - .value(total_balance / 4) + .endowment(total_balance / 4) .using_code(adder_code_hash) - .create_using(self.env()) + .instantiate() .expect("failed at instantiating the `Adder` contract"); let subber = Subber::new(accumulator.clone()) - .value(total_balance / 4) + .endowment(total_balance / 4) .using_code(subber_code_hash) - .create_using(self.env()) + .instantiate() .expect("failed at instantiating the `Subber` contract"); self.accumulator.set(accumulator); self.adder.set(adder); diff --git a/examples/lang2/delegator/subber/lib.rs b/examples/lang2/delegator/subber/lib.rs index 358d71856a4..9d24d7e63da 100644 --- a/examples/lang2/delegator/subber/lib.rs +++ b/examples/lang2/delegator/subber/lib.rs @@ -15,14 +15,14 @@ #![feature(proc_macro_hygiene)] #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(not(feature = "ink-as-dependency"))] -use ink_core::storage; - -use accumulator::Accumulator; use ink_lang2 as ink; #[ink::contract(version = "0.1.0")] mod subber { + #[cfg(not(feature = "ink-as-dependency"))] + use ink_core::storage; + use accumulator::Accumulator; + /// Decreases the underlying accumulator's value. #[ink(storage)] struct Subber { diff --git a/examples/model/erc20/.cargo/config b/examples/model/erc20/.cargo/config deleted file mode 100644 index 7d5b52df77d..00000000000 --- a/examples/model/erc20/.cargo/config +++ /dev/null @@ -1,4 +0,0 @@ -[target.wasm32-unknown-unknown] -rustflags = [ - "-C", "link-args=-z stack-size=65536 --import-memory" -] diff --git a/examples/model/erc20/Cargo.toml b/examples/model/erc20/Cargo.toml deleted file mode 100644 index a41b96f3258..00000000000 --- a/examples/model/erc20/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "erc20" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -ink_core = { path = "../../../core" } -ink_model = { path = "../../../model" } -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] } - -[lib] -name = "erc20" -crate-type = ["cdylib"] - -[features] -default = ["std"] -std = [ - "ink_core/std", - "ink_model/std", - "scale/std", -] -test-env = [ - "std", - "ink_core/test-env", - "ink_model/test-env", -] - -[profile.release] -panic = "abort" -lto = true -opt-level = "z" -overflow-checks = true diff --git a/examples/model/erc20/src/lib.rs b/examples/model/erc20/src/lib.rs deleted file mode 100644 index 0b73ee0e643..00000000000 --- a/examples/model/erc20/src/lib.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2018-2019 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. - -#![no_std] - -use ink_core::{ - env::{ - ContractEnv, - EnvTypes, - DefaultSrmlTypes, - }, - storage, -}; -use ink_model::{ - messages, - state, - Contract, - ContractDecl, -}; - -type AccountId = as EnvTypes>::AccountId; -type Balance = as EnvTypes>::Balance; - -state! { - /// A simple implementation of a rudimentary Erc20 token contract. - struct Erc20Token { - /// The balance for an address. - balances: storage::HashMap, - /// The total supply. - total: storage::Value - } -} - -messages! { - /// Returns the total supply. - 0 => TotalSupply() -> Balance; - /// Returns the balance of the given address. - 1 => BalanceOf(owner: AccountId) -> Balance; - /// Transfers balance from the caller to the given address. - /// - /// Returns `true` if the transfer was successful. - 2 => Transfer(to: AccountId, amount: Balance) -> bool; -} - -#[rustfmt::skip] -fn instantiate() -> impl Contract { - ContractDecl::using::>() - .on_deploy(|env, init_supply| { - let caller = env.caller(); - env.state.balances[&caller] = init_supply; - env.state.total.set(init_supply); - }) - .on_msg::(|env, _| { - *env.state.total.get() - }) - .on_msg::(|env, owner| { - env.state.balances[&owner] - }) - .on_msg_mut::(|env, (to, amount)| { - let from = env.caller(); - let balance_from = env.state.balances[&from]; - let balance_to = env.state.balances[&to]; - - if balance_from >= amount { - env.state.balances[&from] = balance_from - amount; - env.state.balances[&to] = balance_to + amount; - return true - } - - false - }) - .instantiate() -} - -#[no_mangle] -fn deploy() { - instantiate().deploy() -} - -#[no_mangle] -fn call() { - instantiate().dispatch() -} diff --git a/lang/Cargo.toml b/lang/Cargo.toml deleted file mode 100644 index f05713e6000..00000000000 --- a/lang/Cargo.toml +++ /dev/null @@ -1,55 +0,0 @@ -[package] -name = "ink_lang" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -license = "APACHE-2.0" -readme = "README.md" -repository = "https://github.com/paritytech/ink" -documentation = "https://substrate.dev/substrate-contracts-workshop/#/" -homepage = "https://www.parity.io/" -description = "[ink!] Rust based eDSL for writing smart contracts for Substrate" -keywords = ["wasm", "parity", "webassembly", "blockchain", "edsl"] -categories = ["no-std", "embedded"] -include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] - -[dependencies] -ink_utils = { path = "../utils/" } -ink_model = { path = "../model/", default-features = false } -ink_prelude = { path = "../prelude/", default-features = false } -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] } - -quote = "1.0" -syn = { version = "1.0", features = ["parsing", "full", "extra-traits"] } -proc-macro2 = "1.0" -heck = "0.3" -itertools = { version = "0.8", default-features = false } -either = { version = "1.5", default-features = false } -serde = { version = "1.0", default-features = false, features = ["derive"] } -serde_json = "1.0" - -[dev-dependencies] -pretty_assertions = "0.6.1" - -[lib] -name = "ink_lang" -proc-macro = true - -[features] -default = ["test-env"] -test-env = [ - "std", - "ink_model/test-env", -] -std = [ - "ink_model/std", - "ink_prelude/std", - "scale/std", - "itertools/use_std", - "either/use_std", - "serde/std", -] -ink-generate-abi = [ - "std", -] diff --git a/lang/LICENSE b/lang/LICENSE deleted file mode 120000 index ea5b60640b0..00000000000 --- a/lang/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE \ No newline at end of file diff --git a/lang/README.md b/lang/README.md deleted file mode 120000 index 32d46ee883b..00000000000 --- a/lang/README.md +++ /dev/null @@ -1 +0,0 @@ -../README.md \ No newline at end of file diff --git a/lang/src/ast.rs b/lang/src/ast.rs deleted file mode 100644 index c51544a0cbd..00000000000 --- a/lang/src/ast.rs +++ /dev/null @@ -1,397 +0,0 @@ -// Copyright 2018-2019 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 proc_macro2::{ - Ident, - TokenStream as TokenStream2, -}; -use syn::{ - punctuated::Punctuated, - token, - ReturnType, - Token, -}; - -use crate::parser::keywords; - -#[derive(Debug)] -pub struct Contract { - pub items: Vec, -} - -impl Contract { - pub fn env_metas<'a>(&'a self) -> impl Iterator + 'a { - self.items.iter().filter_map(|item| { - match *item { - Item::EnvMeta(ref t) => Some(t), - _ => None, - } - }) - } - - pub fn states<'a>(&'a self) -> impl Iterator + 'a { - self.items.iter().filter_map(|item| { - match *item { - Item::State(ref c) => Some(c), - _ => None, - } - }) - } - - pub fn deploy_impl_blocks<'a>( - &'a self, - ) -> impl Iterator + 'a { - self.items.iter().filter_map(|item| { - match *item { - Item::DeployImpl(ref d) => Some(d), - _ => None, - } - }) - } - - pub fn impl_blocks<'a>(&'a self) -> impl Iterator + 'a { - self.items.iter().filter_map(|item| { - match *item { - Item::Impl(ref i) => Some(i), - _ => None, - } - }) - } - - pub fn events<'a>(&'a self) -> impl Iterator + 'a { - self.items.iter().filter_map(|item| { - match *item { - Item::Event(ref event) => Some(event), - _ => None, - } - }) - } -} - -#[allow(clippy::large_enum_variant)] -#[derive(Debug)] -pub enum Item { - EnvMeta(ItemEnvMeta), - State(ItemState), - DeployImpl(ItemDeployImpl), - Impl(ItemImpl), - Event(ItemEvent), -} - -#[derive(Debug, Clone)] -pub struct ItemEnvMeta { - pub env_types_metas: Vec, -} - -#[derive(Debug, Clone)] -pub struct ItemEnvTypesMeta { - pub ident: Ident, - pub eq_token: Token![=], - pub ty: syn::Type, -} - -/// An event declaration. -/// -/// # Example -/// -/// This mirrors the syntax for: `event Foo { bar: Bar };` -#[derive(Debug, PartialEq, Eq, Hash)] -pub struct ItemEvent { - pub attrs: Vec, - pub event_tok: crate::parser::keywords::event, - pub ident: Ident, - pub brace_tok: token::Brace, - pub args: Punctuated, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct EventArg { - pub attrs: Vec, - pub ident: Ident, - pub colon_tok: Token![:], - pub ty: syn::Type, -} - -impl EventArg { - /// Returns `true` if the event argument is indexed. - pub fn is_indexed(&self) -> bool { - self.attrs.iter().any(|attr| { - attr.style == syn::AttrStyle::Outer - && attr.path.is_ident("indexed") - && attr.tokens.is_empty() - }) - } -} - -#[derive(Debug)] -pub struct ItemState { - pub attrs: Vec, - pub struct_tok: token::Struct, - pub ident: Ident, - pub fields: syn::FieldsNamed, -} - -#[derive(Debug)] -pub struct ItemDeployImpl { - pub attrs: Vec, - pub impl_tok: Token![impl], - pub deploy_tok: keywords::Deploy, - pub for_tok: Token![for], - pub self_ty: Ident, - pub brace_tok: token::Brace, - pub item: DeployItemMethod, -} - -#[derive(Debug)] -pub struct DeployItemMethod { - pub attrs: Vec, - pub deploy_tok: keywords::deploy, - pub sig: Signature, - pub block: syn::Block, -} - -#[derive(Debug)] -pub struct ItemImpl { - pub attrs: Vec, - pub impl_tok: token::Impl, - pub self_ty: Ident, - pub brace_tok: token::Brace, - pub items: Vec, -} - -#[derive(Debug)] -pub struct ItemImplMethod { - pub attrs: Vec, - pub vis: MethodVisibility, - pub sig: Signature, - pub block: syn::Block, -} - -#[derive(Debug, Clone)] -pub enum MethodVisibility { - External(ExternalVisibility), - Inherited, -} - -impl MethodVisibility { - /// Returns `true` if this is an external visibility. - /// - /// # Note - /// - /// The `pub(external)` visibility is only used for contract messages. - pub fn is_external(&self) -> bool { - match self { - MethodVisibility::External(_) => true, - _ => false, - } - } -} - -#[derive(Debug, Clone)] -pub struct ExternalVisibility { - pub pub_tok: token::Pub, - pub paren_tok: token::Paren, - pub external_tok: keywords::external, -} - -#[derive(Debug, Clone)] -pub struct Signature { - pub ident: Ident, - pub fn_tok: token::Fn, - pub generics: syn::Generics, - pub paren_tok: token::Paren, - pub inputs: Punctuated, - pub output: ReturnType, -} - -pub struct FnInputs { - punct: Punctuated, -} - -impl FnInputs { - pub fn to_actual_params(&self) -> Punctuated { - let mut params: Punctuated = Default::default(); - for pat_ty in self.punct.iter().filter_map(|fn_arg| { - if let FnArg::Typed(pat_ty) = fn_arg { - Some(pat_ty) - } else { - None - } - }) { - params.push((*pat_ty.pat).clone()) - } - params - } -} - -impl quote::ToTokens for FnInputs { - fn to_tokens(&self, tokens: &mut TokenStream2) { - self.punct.to_tokens(tokens) - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum FnDeclKind { - SelfRef, - SelfRefMut, - SelfVal, - Static, -} - -impl Signature { - pub fn kind(&self) -> FnDeclKind { - match self.inputs.iter().next().unwrap() { - FnArg::Receiver(syn::Receiver { - mutability: Some(_), - reference: Some(_), - .. - }) => FnDeclKind::SelfRefMut, - FnArg::Receiver(syn::Receiver { - mutability: None, - reference: Some(_), - .. - }) => FnDeclKind::SelfRef, - FnArg::Receiver(syn::Receiver { - mutability: None, - reference: None, - .. - }) - | FnArg::Receiver(syn::Receiver { - mutability: Some(_), - reference: None, - .. - }) => FnDeclKind::SelfVal, - _ => FnDeclKind::Static, - } - } - - pub fn is_self_ref(&self) -> bool { - if let FnDeclKind::SelfRef | FnDeclKind::SelfRefMut = self.kind() { - return true - } - false - } - - pub fn inputs(&self) -> FnInputs { - assert!(self.is_self_ref()); - FnInputs { - punct: self.inputs.clone(), - } - } - - pub fn inputs_without_self(&self) -> FnInputs { - assert!(self.is_self_ref()); - let mut inputs_without_self: Punctuated = Default::default(); - for input in self.inputs.iter().skip(1) { - inputs_without_self.push(input.clone()) - } - FnInputs { - punct: inputs_without_self, - } - } - - pub fn inputs_with_env(&self, env_handler: &syn::Type) -> FnInputs { - assert!(self.is_self_ref()); - let mut inputs_with_env: Punctuated = Default::default(); - let mut inputs_iter = self.inputs.iter(); - let self_arg = inputs_iter.next().unwrap(); - inputs_with_env.push_value(self_arg.clone()); - inputs_with_env.push_punct(Default::default()); - let custom_pat_ty: PatType = if self.kind() == FnDeclKind::SelfRefMut { - syn::parse_quote! { env: &mut #env_handler } - } else { - syn::parse_quote! { env: &#env_handler } - }; - inputs_with_env.push(FnArg::Typed(custom_pat_ty.into_pat_type())); - for input in inputs_iter { - inputs_with_env.push(input.clone()) - } - FnInputs { - punct: inputs_with_env, - } - } -} - -#[allow(clippy::large_enum_variant)] -#[derive(Debug, Clone)] -pub enum FnArg { - Receiver(syn::Receiver), - Typed(syn::PatType), -} - -impl FnArg { - /// Returns the ident if available. - pub fn ident(&self) -> Option { - match self { - FnArg::Receiver(_) => None, - FnArg::Typed(pat_ty) => { - match &*pat_ty.pat { - syn::Pat::Ident(pat_ident) => Some(pat_ident.ident.clone()), - _ => None, - } - } - } - } - - /// Returns `true` if the fn argument is accepted by pattern and type. - pub fn is_typed(&self) -> Option<&syn::PatType> { - match self { - FnArg::Typed(pat_ty) => Some(pat_ty), - _ => None, - } - } -} - -impl quote::ToTokens for FnArg { - fn to_tokens(&self, tokens: &mut TokenStream2) { - match self { - FnArg::Receiver(receiver) => receiver.to_tokens(tokens), - FnArg::Typed(pat_ty) => pat_ty.to_tokens(tokens), - } - } -} - -#[derive(Debug)] -pub struct PatType { - pub attrs: Vec, - pub pat: Box, - pub colon_token: Token![:], - pub ty: Box, -} - -impl syn::parse::Parse for PatType { - fn parse(input: syn::parse::ParseStream<'_>) -> syn::parse::Result { - let attrs = syn::Attribute::parse_outer(input)?; - let pat = input.parse()?; - let colon_token = input.parse()?; - let ty = input.parse()?; - Ok(Self { - attrs, - pat, - colon_token, - ty, - }) - } -} - -impl PatType { - pub fn into_pat_type(self) -> syn::PatType { - syn::PatType { - attrs: self.attrs, - pat: self.pat, - colon_token: self.colon_token, - ty: self.ty, - } - } -} diff --git a/lang/src/contract.rs b/lang/src/contract.rs deleted file mode 100644 index df52bcfcfae..00000000000 --- a/lang/src/contract.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018-2019 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 proc_macro2::TokenStream as TokenStream2; -use syn::Result; - -#[cfg(feature = "ink-generate-abi")] -use crate::old_abi; -use crate::{ - gen, - hir, - parser, -}; - -pub fn generate(input: TokenStream2) -> TokenStream2 { - match generate_or_err(input) { - Ok(tokens) => tokens, - Err(err) => err.to_compile_error(), - } -} - -pub fn generate_or_err(input: TokenStream2) -> Result { - let ast_contract = parser::parse_contract(input)?; - let hir_contract = hir::Contract::from_ast(&ast_contract)?; - #[cfg(feature = "ink-generate-abi")] - old_abi::generate_old_abi(&hir_contract)?; - let tokens = gen::generate_code(&hir_contract); - Ok(tokens) -} diff --git a/lang/src/error.rs b/lang/src/error.rs deleted file mode 100644 index 33434046bd2..00000000000 --- a/lang/src/error.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018-2019 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. - -macro_rules! bail { - ($($args:tt)*) => { - return Err(format_err!($($args)*).into()) - } -} - -macro_rules! format_err { - ($tokens:expr, $($msg:tt)*) => { - match &$tokens { - t => { - syn::parse::Error::new_spanned(t, format_args!($($msg)*)) - } - } - } -} diff --git a/lang/src/gen/abi.rs b/lang/src/gen/abi.rs deleted file mode 100644 index 0faf1061a72..00000000000 --- a/lang/src/gen/abi.rs +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2018-2019 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. - -//! Code generation for smart contract ABI and metadata generation. -//! -//! This two-steps process is required because Rust macros (and thus `ink_lang`) -//! are not able to access type information or anything that is related to that. - -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -use crate::{ - ast, - gen::selector_to_expr, - hir, -}; - -/// Trims a doc string obtained from an attribute token stream into the actual doc string. -/// -/// Practically speaking this method removes the trailing start `" = \""` and end `\"` -/// of documentation strings coming from Syn attribute token streams. -fn trim_doc_string(attr: &syn::Attribute) -> String { - attr.tokens - .to_string() - .trim_start_matches('=') - .trim_start() - .trim_start_matches('r') - .trim_start_matches('\"') - .trim_end_matches('\"') - .trim() - .into() -} - -pub fn generate_code(tokens: &mut TokenStream2, contract: &hir::Contract) { - let abi_mod_body = generate_abi_mod_body(contract); - tokens.extend(abi_mod_body); -} - -fn generate_abi_mod_body(contract: &hir::Contract) -> TokenStream2 { - let ink_generate_abi_contract = generate_abi_contract(contract); - let ink_generate_abi_layout = generate_abi_layout(contract); - - quote! { - #[cfg(not(feature = "ink-as-dependency"))] - #[cfg(feature = "ink-generate-abi")] - pub fn ink_generate_abi() -> ink_abi::InkProject { - let contract = { - #ink_generate_abi_contract - }; - let layout = { - #ink_generate_abi_layout - }; - ink_abi::InkProject::new(layout, contract) - } - } -} - -fn generate_abi_constructor(contract: &hir::Contract) -> TokenStream2 { - let constructor = &contract.on_deploy; - - let args = constructor - .sig - .inputs - .iter() - .filter_map(ast::FnArg::is_typed) - .map(|capt| { - let name = match &*capt.pat { - syn::Pat::Ident(pat_ident) => { - if pat_ident.by_ref.is_none() - && pat_ident.mutability.is_none() - && pat_ident.subpat.is_none() - { - pat_ident.ident.to_string() - } else { - unreachable!("encountered invalid deploy argument") - } - } - syn::Pat::Path(pat_path) => { - if pat_path.qself.is_none() - && pat_path.path.leading_colon.is_none() - && pat_path.path.segments.len() == 1 - && pat_path.path.segments[0].arguments.is_empty() - { - pat_path.path.segments[0].ident.to_string() - } else { - unreachable!("invalid arg name encountered") - } - } - _ => { - unreachable!( - "encountered invalid argument syntax: the only allowed is `ident : type`", - ) - } - }; - let ty = &capt.ty; - let type_spec = generate_type_spec_code(ty); - quote! { - ink_abi::MessageParamSpec::new(#name) - .of_type(#type_spec) - .done() - } - }); - let docs = constructor.docs().map(trim_doc_string); - - quote! { - ink_abi::ConstructorSpec::new("on_deploy") - .selector([0u8; 4]) - .args(vec![ - #(#args ,)* - ]) - .docs(vec![ - #(#docs ,)* - ]) - .done() - } -} - -fn generate_abi_messages<'a>( - contract: &'a hir::Contract, -) -> impl Iterator + 'a { - contract.messages.iter().map(|message| { - let selector = selector_to_expr(message.selector()); - let is_mut = message.is_mut(); - let docs = message.docs().map(trim_doc_string); - let name = message.sig.ident.to_string(); - let inputs = message - .sig - .inputs - .iter() - .filter_map(ast::FnArg::is_typed) - .map(|capt| { - let name: String = match &*capt.pat { - syn::Pat::Ident(pat_ident) => { - if pat_ident.by_ref.is_none() - && pat_ident.mutability.is_none() - && pat_ident.subpat.is_none() - { - pat_ident.ident.to_string() - } else { - unreachable!("encountered invalid deploy argument") - } - } - syn::Pat::Path(pat_path) => { - if pat_path.qself.is_none() - && pat_path.path.leading_colon.is_none() - && pat_path.path.segments.len() == 1 - && pat_path.path.segments[0].arguments.is_empty() - { - pat_path.path.segments[0].ident.to_string() - } else { - unreachable!("invalid arg name encountered") - } - } - _ => unreachable!("encountered invalid argument syntax: the only allowed is `ident : type`"), - }; - let ty = &capt.ty; - let type_spec = generate_type_spec_code(ty); - quote! { - ink_abi::MessageParamSpec::new(#name) - .of_type(#type_spec) - .done() - } - }); - let ret_type = match &message.sig.output { - syn::ReturnType::Default => { - quote! { - ink_abi::ReturnTypeSpec::new(None) - } - } - syn::ReturnType::Type(_, ty) => { - let type_spec = generate_type_spec_code(&*ty); - quote! { - ink_abi::ReturnTypeSpec::new(#type_spec) - } - } - }; - quote! { - ink_abi::MessageSpec::new(#name) - .selector(#selector) - .mutates(#is_mut) - .args(vec![ - #(#inputs ,)* - ]) - .docs(vec![ - #(#docs ,)* - ]) - .returns( - #ret_type - ) - .done() - } - }) -} - -fn generate_type_spec_code(ty: &syn::Type) -> TokenStream2 { - fn without_display_name(ty: &syn::Type) -> TokenStream2 { - quote! { ink_abi::TypeSpec::new::<#ty>() } - } - if let syn::Type::Path(type_path) = ty { - if type_path.qself.is_some() { - return without_display_name(ty) - } - let path = &type_path.path; - if path.segments.is_empty() { - return without_display_name(ty) - } - let segs = path - .segments - .iter() - .map(|seg| seg.ident.to_string()) - .collect::>(); - return quote! { - ink_abi::TypeSpec::with_name_segs::<#ty, _>(vec![#(#segs),*].into_iter().map(AsRef::as_ref)) - } - } - without_display_name(ty) -} - -fn generate_abi_events<'a>( - contract: &'a hir::Contract, -) -> impl Iterator + 'a { - contract.events.iter().map(|event| { - let name = &event.ident; - let args = event.args.iter().map(|event_arg| { - let name = &event_arg.ident; - let indexed = event_arg.is_indexed(); - let ty = &event_arg.ty; - let type_spec = generate_type_spec_code(ty); - quote! { - ink_abi::EventParamSpec::new(stringify!(#name)) - .of_type(#type_spec) - .indexed(#indexed) - .done() - } - }); - let docs = event.docs().map(trim_doc_string); - quote! { - ink_abi::EventSpec::new(stringify!(#name)) - .args(vec![ - #(#args ,)* - ]) - .docs(vec![ - #(#docs ,)* - ]) - .done() - } - }) -} - -fn generate_abi_contract(contract: &hir::Contract) -> TokenStream2 { - let contract_name = &contract.name; - let contract_name_lit = contract_name.to_string(); - - // We currently do not have a way to specify docs for whole contracts. - // For this we could either take the docs of the contract state struct - // or allow for inner-attribute doc style within the `contract!` macro call. - let docs = quote! {}; - - let constructor = generate_abi_constructor(contract); - let messages = generate_abi_messages(contract); - let events = generate_abi_events(contract); - - quote! { - ink_abi::ContractSpec::new(#contract_name_lit) - .constructors(vec![ - #constructor - ]) - .messages(vec![ - #(#messages ,)* - ]) - .events(vec![ - #(#events ,)* - ]) - .docs(vec![ - #docs - ]) - .done() - } -} - -fn generate_abi_layout(contract: &hir::Contract) -> TokenStream2 { - let contract_name = &contract.name; - quote! { - unsafe { - use ink_core::storage::alloc::AllocateUsing as _; - use ink_abi::HasLayout as _; - #contract_name::allocate_using( - &mut ink_core::storage::alloc::BumpAlloc::from_raw_parts(ink_core::storage::Key([0x0; 32])) - ).layout() - } - } -} diff --git a/lang/src/gen/as_dependency.rs b/lang/src/gen/as_dependency.rs deleted file mode 100644 index d8ab6e996d1..00000000000 --- a/lang/src/gen/as_dependency.rs +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright 2018-2019 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. - -//! Code generation for smart contracts when they are compiled as a dependency of another smart contract -//! using the generally available `ink-as-dependency` crate feature. -//! -//! The code generated by this generally conflicts with other generated code since an ink! contract -//! that is compiled as dependency no longer requires any infrastructure to dispatch calls or instantiations. -//! However, it requires special treatment for all public messages since their bodies are completely -//! replaced by direct forwards to the remote call infrastructure going through SRML contracts. - -use proc_macro2::TokenStream as TokenStream2; -use quote::{ - quote, - quote_spanned, -}; - -use crate::{ - ast, - gen::selector_to_expr, - hir, -}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum Mutability { - Immutable, - Mutable, -} - -pub fn generate_code(tokens: &mut TokenStream2, contract: &hir::Contract) { - let messages = generate_messages_as_dependency(contract); - let call_enhancer_messages = - generate_call_enhancer_messages(contract, Mutability::Immutable); - let call_enhancer_mut_messages = - generate_call_enhancer_messages(contract, Mutability::Mutable); - let state = generate_state_as_dependency(contract); - let contract_ident = &contract.name; - - tokens.extend(quote! { - #[cfg(feature = "ink-as-dependency")] - mod as_dependency { - use super::*; - - #state - - impl #contract_ident { - #(#messages)* - } - - impl<'a> CallEnhancer<'a> { - #(#call_enhancer_messages)* - } - - impl<'a> CallEnhancerMut<'a> { - #(#call_enhancer_mut_messages)* - } - } - - #[cfg(feature = "ink-as-dependency")] - pub use as_dependency::{ - #contract_ident, - CallEnhancer, - CallEnhancerMut, - }; - }); -} - -fn generate_state_as_dependency(contract: &hir::Contract) -> TokenStream2 { - let name = &contract.name; - let attrs = &contract.state.attrs; - let create = generate_create(contract); - quote! { - #( #attrs )* - #[derive(Clone, scale::Encode, scale::Decode)] - #[cfg_attr(feature = "ink-generate-abi", derive(type_metadata::Metadata))] - pub struct #name { - account_id: AccountId, - } - - impl ink_core::storage::Flush for #name {} - - /// Allows to enhance calls to `&self` contract messages. - pub struct CallEnhancer<'a> { - contract: &'a #name, - } - - /// Allows to enhance calls to `&mut self` contract messages. - pub struct CallEnhancerMut<'a> { - contract: &'a mut #name, - } - - impl ink_core::env::FromAccountId for #name { - fn from_account_id(account_id: AccountId) -> Self { - Self { account_id } - } - } - - impl #name { - #create - - /// Returns the internal account ID of the contract. - pub fn account_id(&self) -> AccountId { - self.account_id - } - - /// Allows to enhance calls to `&self` contract messages. - pub fn call(&self) -> CallEnhancer { - CallEnhancer { contract: self } - } - - /// Allows to enhance calls to `&mut self` contract messages. - pub fn call_mut(&mut self) -> CallEnhancerMut { - CallEnhancerMut { contract: self } - } - } - } -} - -fn generate_create(contract: &hir::Contract) -> TokenStream2 { - let deploy_handler = &contract.on_deploy; - let attrs = &deploy_handler.attrs; - let args = deploy_handler.sig.inputs.iter().skip(1); - let inputs = deploy_handler - .sig - .inputs - .iter() - .filter_map(ast::FnArg::ident) - .map(|ident| quote! { #ident }); - quote_spanned! { deploy_handler.sig.fn_tok.span => - #(#attrs)* - pub fn new( - code_hash: Hash, - #(#args ,)* - ) -> ink_core::env::CreateBuilder { - ink_core::env::CreateBuilder::::new(code_hash) - #( - .push_arg(&#inputs) - )* - } - } -} - -fn generate_messages_as_dependency<'a>( - contract: &'a hir::Contract, -) -> impl Iterator + 'a { - let contract_ident = &contract.name; - contract.messages.iter().map(move |message| { - let ident = &message.sig.ident; - let attrs = &message.attrs; - let args = message.sig.inputs.iter().skip(1); - let (self_arg, call_fn) = if message.is_mut() { - (quote! { &mut self }, quote! { call_mut() }) - } else { - (quote! { &self }, quote! { call() }) - }; - let inputs = message - .sig - .inputs - .iter() - .filter_map(ast::FnArg::ident) - .map(|ident| quote! { #ident }); - let output = &message.sig.output; - let (_impl_generics, type_generics, where_clause) = - message.sig.generics.split_for_impl(); - match output { - syn::ReturnType::Default => { - quote_spanned! { ident.span() => - #(#attrs)* - pub fn #ident #type_generics ( - #self_arg , - #(#args ,)* - ) #where_clause { - self.#call_fn.#ident( #(#inputs ,)* ) - .fire() - .expect(concat!( - "invocation of ", - stringify!(#contract_ident), "::", stringify!(#ident), - " message was invalid")) - } - } - } - syn::ReturnType::Type(_, ty) => { - let ty = &*ty; - quote_spanned! { ident.span() => - #(#attrs)* - pub fn #ident #type_generics ( - #self_arg , - #(#args ,)* - ) -> #ty #where_clause { - self.#call_fn.#ident( #(#inputs ,)* ) - .fire() - .expect(concat!( - "evaluation of ", - stringify!(#contract_ident), "::", stringify!(#ident), - " message was invalid")) - } - } - } - } - }) -} - -fn generate_call_enhancer_messages<'a>( - contract: &'a hir::Contract, - mutability: Mutability, -) -> impl Iterator + 'a { - contract.messages - .iter() - .filter(move |message| { - if mutability == Mutability::Mutable { - message.is_mut() - } else { - !message.is_mut() - } - }) - .map(|message| { - let ident = &message.sig.ident; - let attrs = &message.attrs; - let args = message.sig.inputs.iter().skip(1); - let inputs = message.sig.inputs - .iter() - .filter_map(ast::FnArg::ident) - .map(|ident| quote! { #ident }); - let output = &message.sig.output; - let (_impl_generics, type_generics, where_clause) = message.sig.generics.split_for_impl(); - let selector = selector_to_expr(message.selector()); - match output { - syn::ReturnType::Default => quote_spanned! { ident.span() => - #(#attrs)* - pub fn #ident #type_generics ( - self, - #(#args ,)* - ) -> ink_core::env::CallBuilder #where_clause { - ink_core::env::CallBuilder::::invoke(self.contract.account_id.clone(), #selector) - #( - .push_arg(&#inputs) - )* - } - }, - syn::ReturnType::Type(_, ty) => { - let ty = &*ty; - quote_spanned! { ident.span() => - #(#attrs)* - pub fn #ident #type_generics ( - self, - #(#args ,)* - ) -> ink_core::env::CallBuilder> #where_clause { - ink_core::env::CallBuilder::eval(self.contract.account_id.clone(), #selector) - #( - .push_arg(&#inputs) - )* - } - } - } - } - }) -} diff --git a/lang/src/gen/build.rs b/lang/src/gen/build.rs deleted file mode 100644 index b7779d1b236..00000000000 --- a/lang/src/gen/build.rs +++ /dev/null @@ -1,518 +0,0 @@ -// Copyright 2018-2019 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. - -//! Code generation for normal Wasm smart contract builds. -//! -//! Generated code that conflicts with specialized `test` or `doc` -//! code needs to be guarded by `#[cfg(..)]`. - -use std::iter; - -use proc_macro2::{ - Ident, - TokenStream as TokenStream2, -}; -use quote::{ - quote, - ToTokens, -}; -use syn::{ - punctuated::Punctuated, - Token, -}; - -use crate::{ - ast, - gen::selector_to_expr, - hir, -}; - -pub fn generate_code(tokens: &mut TokenStream2, contract: &hir::Contract) { - let env_items = { - let mut result = quote! {}; - let tokens = &mut result; - codegen_for_contract_env(tokens, contract); - result - }; - let mod_body = { - let mut result = quote! {}; - let tokens = &mut result; - // codegen_for_contract_env(tokens, contract); - codegen_for_state(tokens, contract); - codegen_for_messages(tokens, contract); - codegen_for_message_impls(tokens, contract); - codegen_for_method_impls(tokens, contract); - codegen_for_instantiate(tokens, contract); - codegen_for_entry_points(tokens, contract); - codegen_for_event_mod(tokens, contract); - result - }; - tokens.extend(quote! { - #env_items - - #[cfg(not(feature = "ink-as-dependency"))] - mod normal { - use super::*; - #mod_body - } - #[cfg(not(feature = "ink-as-dependency"))] - use normal::*; - - #[cfg(not(feature = "ink-as-dependency"))] - use ink_core::env::FromAccountId as _; - }); -} - -fn codegen_for_contract_env(tokens: &mut TokenStream2, contract: &hir::Contract) { - let env_types = &contract.env_types_type; - tokens.extend(quote! { - mod types { - use super::*; - use ink_core::env::{ContractEnv, EnvTypes}; - - pub type AccountId = <#env_types as EnvTypes>::AccountId; - pub type Balance = <#env_types as EnvTypes>::Balance; - pub type Hash = <#env_types as EnvTypes>::Hash; - pub type Moment = <#env_types as EnvTypes>::Moment; - pub type BlockNumber = <#env_types as EnvTypes>::BlockNumber; - } - - type Env = ink_core::env::ContractEnv<#env_types>; - use types::{ - AccountId, - Balance, - Hash, - Moment, - BlockNumber, - }; - }) -} - -fn codegen_for_event_mod(tokens: &mut TokenStream2, contract: &hir::Contract) { - if contract.events.is_empty() { - // Do nothing if the user specified no events - return - } - let use_event_body = { - let mut content = quote! {}; - for event in contract.events.iter() { - let ident = &event.ident; - content.extend(quote! { - #ident, - }) - } - content - }; - let mod_event_body = { - let mut content = quote! {}; - codegen_for_event_private_mod(&mut content, contract); - codegen_for_events(&mut content, contract); - codegen_for_event_emit_trait(&mut content, contract); - content - }; - tokens.extend(quote! { - mod events { - use super::*; - - #mod_event_body - } - - use events::{ - EmitEventExt as _, - #use_event_body - }; - }) -} - -fn codegen_for_event_private_mod(tokens: &mut TokenStream2, contract: &hir::Contract) { - let event_enum_mod_body = { - let mut content = quote! {}; - for event in contract.events.iter() { - let name = &event.ident; - content.extend(quote! { - #name(#name), - }) - } - content - }; - tokens.extend(quote! { - mod private { - use super::*; - - #[doc(hidden)] - #[derive(scale::Encode, scale::Decode)] - pub enum Event { - #event_enum_mod_body - } - - /// Used to seal the emit trait. - pub trait Sealed {} - } - }) -} - -impl quote::ToTokens for hir::Event { - fn to_tokens(&self, tokens: &mut TokenStream2) { - for attr in &self.attrs { - attr.to_tokens(tokens) - } - ::default().to_tokens(tokens); - ::default().to_tokens(tokens); - self.ident.to_tokens(tokens); - syn::token::Brace::default().surround(tokens, |inner| { - for arg in self.args.iter() { - for attr in &arg.attrs { - attr.to_tokens(inner) - } - ::default().to_tokens(inner); - arg.to_tokens(inner); - ::default().to_tokens(inner); - } - }); - } -} - -impl quote::ToTokens for ast::EventArg { - fn to_tokens(&self, tokens: &mut TokenStream2) { - self.ident.to_tokens(tokens); - self.colon_tok.to_tokens(tokens); - self.ty.to_tokens(tokens); - } -} - -fn codegen_for_events(tokens: &mut TokenStream2, contract: &hir::Contract) { - for event in contract.events.iter() { - let ident = &event.ident; - - tokens.extend(quote! { - #[derive(scale::Encode, scale::Decode)] - #event - - impl From<#ident> for private::Event { - fn from(event: #ident) -> Self { - private::Event::#ident(event) - } - } - }) - } -} - -fn codegen_for_event_emit_trait(tokens: &mut TokenStream2, contract: &hir::Contract) { - let env_types = &contract.env_types_type; - tokens.extend(quote! { - pub trait EmitEventExt: private::Sealed { - /// Emits the given event. - fn emit(&self, event: E) - where - E: Into, - { - use scale::Encode as _; - as ink_core::env::Env>::deposit_raw_event( - &[], event.into().encode().as_slice() - ) - } - } - - impl EmitEventExt for ink_model::EnvHandler> {} - impl private::Sealed for ink_model::EnvHandler> {} - }) -} - -fn codegen_for_entry_points(tokens: &mut TokenStream2, contract: &hir::Contract) { - let state_name = &contract.name; - - tokens.extend(quote! { - #[cfg(not(test))] - #[no_mangle] - fn deploy() -> u32 { - #state_name::instantiate().deploy().to_u32() - } - - #[cfg(not(test))] - #[no_mangle] - fn call() -> u32 { - #state_name::instantiate().dispatch().to_u32() - } - }) -} - -fn codegen_for_instantiate(tokens: &mut TokenStream2, contract: &hir::Contract) { - let state_name = &contract.name; - let env_types = &contract.env_types_type; - - let deploy_handler_toks = { - let deploy_fn_args = { - let mut deploy_fn_args: Punctuated = Punctuated::new(); - for input in contract.on_deploy.sig.inputs.iter().skip(1) { - deploy_fn_args.push(input.clone()) - } - deploy_fn_args - }; - let deploy_call_args = { - let mut deploy_call_args: Punctuated = Punctuated::new(); - for pat_ty in deploy_fn_args.iter().filter_map(|fn_arg| { - if let ast::FnArg::Typed(pat_ty) = fn_arg { - Some(pat_ty) - } else { - None - } - }) { - deploy_call_args.push((*pat_ty.pat).clone()) - } - deploy_call_args - }; - let deploy_fn_args_toks = match deploy_fn_args.iter().count() { - 0 => quote! {()}, - 1 => deploy_fn_args.into_token_stream(), - _ => { - let mut toks = quote! {}; - syn::token::Paren::default().surround(&mut toks, |surrounded_toks| { - deploy_call_args.to_tokens(surrounded_toks) - }); - toks - } - }; - quote! { - .on_deploy(|env, #deploy_fn_args_toks| { - let (handler, state) = env.split_mut(); - state.deploy(handler, #deploy_call_args) - }) - } - }; - - let messages_toks = { - let mut content = quote! {}; - for message in &contract.messages { - let msg_ident = &message.sig.ident; - - use heck::CamelCase as _; - let camelcase_msg_ident = Ident::new( - &message.sig.ident.to_string().to_camel_case(), - message.sig.ident.span(), - ); - - let msg_fn_args = { - let mut msg_fn_args: Punctuated = - Default::default(); - for input in message.sig.inputs.iter().skip(1) { - msg_fn_args.push(input.clone()) - } - msg_fn_args - }; - - let msg_call_args = { - let mut msg_call_args: Punctuated = - Default::default(); - for pat_ty in msg_fn_args.iter().filter_map(|fn_arg| { - if let ast::FnArg::Typed(pat_ty) = fn_arg { - Some(pat_ty) - } else { - None - } - }) { - msg_call_args.push((*pat_ty.pat).clone()) - } - msg_call_args - }; - - let msg_fn_args_toks = match msg_fn_args.iter().count() { - 0 => quote! {_}, - 1 => msg_fn_args.into_token_stream(), - _ => { - let mut toks = quote! {}; - syn::token::Paren::default().surround(&mut toks, |surrounded_toks| { - msg_call_args.to_tokens(surrounded_toks) - }); - toks - } - }; - - let msg_toks = if message.is_mut() { - quote! { - .on_msg_mut::< msg::#camelcase_msg_ident >(|env, #msg_fn_args_toks| { - let (handler, state) = env.split_mut(); - state. #msg_ident (handler, #msg_call_args) - }) - } - } else { - quote! { - .on_msg::< msg::#camelcase_msg_ident >(|env, #msg_fn_args_toks| { - let (handler, state) = env.split(); - state. #msg_ident (handler, #msg_call_args) - }) - } - }; - msg_toks.to_tokens(&mut content) - } - content - }; - - tokens.extend(quote! { - use ink_model::Contract as _; - - #[cfg(not(test))] - impl #state_name { - pub(crate) fn instantiate() -> impl ink_model::Contract { - ink_model::ContractDecl::using::>() - #deploy_handler_toks - #messages_toks - .instantiate() - } - } - }) -} - -fn codegen_for_message_impls(tokens: &mut TokenStream2, contract: &hir::Contract) { - let state_name = &contract.name; - let env_types = &contract.env_types_type; - let env_handler: syn::Type = syn::parse_quote! { - ink_model::EnvHandler> - }; - let message_impls = { - let mut content = quote! {}; - for message in iter::once(&contract.on_deploy.clone().into_message()) - .chain(contract.messages.iter()) - { - for attr in &message.attrs { - attr.to_tokens(&mut content) - } - ::default().to_tokens(&mut content); - let sig = &message.sig; - sig.fn_tok.to_tokens(&mut content); - message.sig.ident.to_tokens(&mut content); - sig.paren_tok.surround(&mut content, |inner_toks| { - let inputs_with_env = sig.inputs_with_env(&env_handler); - inputs_with_env.to_tokens(inner_toks); - }); - sig.output.to_tokens(&mut content); - message.block.to_tokens(&mut content); - } - content - }; - tokens.extend(quote! { - impl #state_name { - #message_impls - } - }); -} - -fn codegen_for_method_impls(tokens: &mut TokenStream2, contract: &hir::Contract) { - let state_name = &contract.name; - let methods_impls = { - let mut content = quote! {}; - for method in contract.methods.iter() { - for attr in &method.attrs { - attr.to_tokens(&mut content) - } - let sig = &method.sig; - sig.fn_tok.to_tokens(&mut content); - method.sig.ident.to_tokens(&mut content); - let generics = &sig.generics; - generics.lt_token.to_tokens(&mut content); - generics.params.to_tokens(&mut content); - generics.gt_token.to_tokens(&mut content); - sig.paren_tok.surround(&mut content, |inner_toks| { - sig.inputs.to_tokens(inner_toks); - }); - sig.output.to_tokens(&mut content); - generics.where_clause.to_tokens(&mut content); - method.block.to_tokens(&mut content); - } - content - }; - if contract.methods.iter().count() > 0 { - tokens.extend(quote! { - impl #state_name { - #methods_impls - } - }) - } -} - -fn codegen_for_state(tokens: &mut TokenStream2, contract: &hir::Contract) { - let state_attrs_toks = { - let mut content = quote! {}; - for attr in &contract.state.attrs { - attr.to_tokens(&mut content) - } - content - }; - let struct_fields_toks = &contract.state.fields; - let name = &contract.name; - tokens.extend(quote! { - ink_model::state! { - #state_attrs_toks - #[cfg_attr( - feature = "ink-generate-abi", - derive( - type_metadata::Metadata, - ink_abi::HasLayout, - ) - )] - pub struct #name - #struct_fields_toks - } - }); -} - -fn codegen_for_messages(tokens: &mut TokenStream2, contract: &hir::Contract) { - let messages_content = { - let mut content = quote! {}; - for message in contract.messages.iter() { - for attr in &message.attrs { - attr.to_tokens(&mut content) - } - let msg_id = selector_to_expr(message.selector()); - msg_id.to_tokens(&mut content); - ]>::default().to_tokens(&mut content); - use heck::CamelCase as _; - let camel_case_ident = Ident::new( - &message.sig.ident.to_string().to_camel_case(), - message.sig.ident.span(), - ); - camel_case_ident.to_tokens(&mut content); - let sig = &message.sig; - sig.paren_tok.surround(&mut content, |inner_toks| { - let args_without_self = { - let mut args_without_self: Punctuated = - Punctuated::new(); - for fn_arg in sig - .inputs.iter() - // Performing `skip(1)` here works because we already asserted - // that all messages have to start with either `&self` or `&mut self`. - .skip(1) - { - args_without_self.push(fn_arg.clone()) - } - args_without_self - }; - args_without_self.to_tokens(inner_toks) - }); - sig.output.to_tokens(&mut content); - ::default().to_tokens(&mut content); - } - content - }; - tokens.extend(quote! { - mod msg { - use super::*; - // Apparently this `use` is required even though it should not be. - // -> Further investigations needed! - use ink_model::messages; - ink_model::messages! { - #messages_content - } - } - }) -} diff --git a/lang/src/gen/doc.rs b/lang/src/gen/doc.rs deleted file mode 100644 index b9bd23ab1d0..00000000000 --- a/lang/src/gen/doc.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018-2019 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. - -//! Code generation for documentation generation of Wasm smart contracts. -//! -//! We use the special `#[cfg(rustdoc)]` that is set when `rustdoc` is -//! compiling a crate in order to generate special code that is only used -//! for documentation purposes. - -use proc_macro2::TokenStream as TokenStream2; - -use crate::hir; - -pub fn generate_code(_tokens: &mut TokenStream2, _contract: &hir::Contract) {} diff --git a/lang/src/gen/mod.rs b/lang/src/gen/mod.rs deleted file mode 100644 index cd0b97bd931..00000000000 --- a/lang/src/gen/mod.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2018-2019 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 abi; -mod as_dependency; -mod build; -mod doc; -mod test; - -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use syn::{ - parse_str, - Expr, -}; - -use crate::hir; - -/// Generates code for the given contract. -/// -/// # Note -/// -/// This generates code for normal Wasm smart contract builds as -/// well as code for specialized `test` and `doc` targets. -pub fn generate_code(contract: &hir::Contract) -> TokenStream2 { - let mut tokens = quote! {}; - build::generate_code(&mut tokens, contract); - doc::generate_code(&mut tokens, contract); - test::generate_code(&mut tokens, contract); - abi::generate_code(&mut tokens, contract); - as_dependency::generate_code(&mut tokens, contract); - tokens -} - -/// The function is required because syn doesn't provide a -/// `ToTokens` implementation for `[u8; 4]`. -fn selector_to_expr(selector: [u8; 4]) -> Expr { - let selector = format!( - "[{}, {}, {}, {}]", - selector[0], selector[1], selector[2], selector[3] - ); - parse_str::(&selector).expect("failed to parse selector") -} diff --git a/lang/src/gen/test.rs b/lang/src/gen/test.rs deleted file mode 100644 index e4b2b554509..00000000000 --- a/lang/src/gen/test.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2018-2019 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. - -//! Code generation for test generation of Wasm smart contracts. -//! -//! Test code is generated under the `#[cfg(test)]` compile flag. - -use proc_macro2::TokenStream as TokenStream2; -use quote::{ - quote, - ToTokens, -}; -use syn::{ - self, - punctuated::Punctuated, - Token, -}; - -use crate::{ - ast, - hir, - ident_ext::IdentExt, -}; - -pub fn generate_code(tokens: &mut TokenStream2, contract: &hir::Contract) { - let test_mod_body = generate_test_mod_body(contract); - - tokens.extend(quote! { - #[cfg(test)] - mod test { - use super::*; - - #test_mod_body - } - }) -} - -fn generate_test_mod_body(contract: &hir::Contract) -> TokenStream2 { - let mut tokens = quote! {}; - generate_test_struct(&mut tokens, contract); - generate_test_deploy(&mut tokens, contract); - generate_test_allocate_deploy_block(&mut tokens, contract); - generate_test_methods(&mut tokens, contract); - tokens -} - -/// For a contract returns its testable type name. -/// -/// # Example -/// -/// For a contract called `Flipper` this returns `TestableFlipper`. -fn testable_contract_name(contract: &hir::Contract) -> proc_macro2::Ident { - proc_macro2::Ident::from_str(["Testable", &contract.name.to_string()].concat()) -} - -fn generate_test_struct(tokens: &mut TokenStream2, contract: &hir::Contract) { - let contract_name = &contract.name; - let env_types = &contract.env_types_type; - let testable_name = testable_contract_name(contract); - tokens.extend(quote! { - pub struct #testable_name { - env: ink_model::ExecutionEnv<#contract_name, ink_core::env::ContractEnv<#env_types>>, - } - }) -} - -fn generate_test_deploy(tokens: &mut TokenStream2, contract: &hir::Contract) { - let contract_name = &contract.name; - let testable_name = testable_contract_name(contract); - let deploy_fn_toks = { - let mut content = quote! {}; - ::default().to_tokens(&mut content); - ::default().to_tokens(&mut content); - syn::Ident::from_str("deploy_mock").to_tokens(&mut content); - syn::token::Paren::default().surround(&mut content, |inner| { - contract - .on_deploy - .sig - .inputs_without_self() - .to_tokens(inner) - }); - ]>::default().to_tokens(&mut content); - testable_name.to_tokens(&mut content); - syn::token::Brace::default().surround(&mut content, |inner| { - let inputs = { - let mut inputs: Punctuated = Default::default(); - for input in &contract.on_deploy.sig.inputs { - if let ast::FnArg::Typed(pat_ty) = input { - inputs.push((*pat_ty.pat).clone()) - } - } - inputs - }; - inner.extend(quote! { - let mut mock = #testable_name::allocate(); - mock.deploy(#inputs); - mock - }) - }); - content - }; - tokens.extend(quote! { - impl #contract_name { - /// Returns a testable version of the contract. - #deploy_fn_toks - } - }) -} - -fn generate_test_allocate_deploy_block( - tokens: &mut TokenStream2, - contract: &hir::Contract, -) { - let testable_name = testable_contract_name(contract); - let mut impl_body = quote! {}; - generate_test_allocate_fn(&mut impl_body, contract); - generate_test_deploy_fn(&mut impl_body, contract); - tokens.extend(quote! { - impl #testable_name { - #impl_body - } - }) -} - -fn generate_test_allocate_fn(tokens: &mut TokenStream2, _contract: &hir::Contract) { - tokens.extend(quote! { - /// Allocates the testable contract storage. - fn allocate() -> Self { - use ink_core::storage::{ - Key, - alloc::{ - AllocateUsing as _, - Initialize as _, - BumpAlloc, - }, - }; - Self { - env: unsafe { - let mut alloc = BumpAlloc::from_raw_parts(Key([0x0; 32])); - ink_model::ExecutionEnv::allocate_using(&mut alloc).initialize_into(()) - } - } - } - }) -} - -fn generate_test_deploy_fn(tokens: &mut TokenStream2, contract: &hir::Contract) { - let log_params = contract.on_deploy.sig.inputs_without_self(); - let act_params = log_params.to_actual_params(); - tokens.extend(quote! { - /// Deploys the testable contract by initializing it with the given values. - fn deploy(&mut self, #log_params) { - let (handler, state) = self.env.split_mut(); - state.deploy(handler, #act_params) - } - }) -} - -fn generate_test_methods(tokens: &mut TokenStream2, contract: &hir::Contract) { - let impl_for = testable_contract_name(contract); - let mut impl_body = quote! {}; - generate_test_method_fns(&mut impl_body, contract); - tokens.extend(quote! { - impl #impl_for { - #impl_body - } - }) -} - -fn generate_test_method_fns(tokens: &mut TokenStream2, contract: &hir::Contract) { - for msg in &contract.messages { - generate_test_method_fn(tokens, msg); - } -} - -fn generate_test_method_fn(tokens: &mut TokenStream2, msg: &hir::Message) { - ::default().to_tokens(tokens); - ::default().to_tokens(tokens); - msg.sig.ident.to_tokens(tokens); - syn::token::Paren::default() - .surround(tokens, |inner| msg.sig.inputs().to_tokens(inner)); - msg.sig.output.to_tokens(tokens); - syn::token::Brace::default().surround(tokens, |inner| { - let params = msg.sig.inputs_without_self().to_actual_params(); - let name = &msg.sig.ident; - let split_impl = if msg.is_mut() { - quote! { self.env.split_mut() } - } else { - quote! { self.env.split() } - }; - inner.extend(quote! { - let (handler, state) = #split_impl; - state.#name(handler, #params) - }) - }); -} diff --git a/lang/src/hir.rs b/lang/src/hir.rs deleted file mode 100644 index 831aa161327..00000000000 --- a/lang/src/hir.rs +++ /dev/null @@ -1,578 +0,0 @@ -// Copyright 2018-2019 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 proc_macro2::{ - Ident, - Span, -}; -use syn::{ - self, - punctuated::Punctuated, - Result, - Token, - Type, -}; - -use crate::ast; - -/// A smart contract. -#[derive(Debug)] -pub struct Contract { - /// The name of the smart contract. - pub name: Ident, - /// The type of the environment types - pub env_types_type: Type, - /// The storage state fields. - pub state: State, - /// The deploy handler of the smart contract. - pub on_deploy: DeployHandler, - /// The messages of the smart contract. - pub messages: Vec, - /// Methods of the smart contract. - pub methods: Vec, - /// Events of the smart contract. - pub events: Vec, -} - -/// An event definition. -#[derive(Debug)] -pub struct Event { - pub attrs: Vec, - pub ident: Ident, - pub args: Punctuated, -} - -/// Returns an iterator over all doc attributes. -pub fn filter_doc_attributes( - attrs: &[syn::Attribute], -) -> impl Iterator { - attrs - .iter() - .filter(|attr| attr.style == syn::AttrStyle::Outer && attr.path.is_ident("doc")) -} - -impl Event { - /// Returns all doc attributes of the message. - pub fn docs(&self) -> impl Iterator { - filter_doc_attributes(&self.attrs) - } -} - -impl Contract { - /// Extracts the type for environment types from the contract items - /// and performs some integrity checks on it. - /// - /// # Errors - /// - /// - If no type for environment types has been found. - /// - If more than one type for environment types has been found. - fn extract_env_types(contract: &ast::Contract) -> Result { - let env_types = contract - .env_metas() - .flat_map(|meta| meta.env_types_metas.clone()) - .map(|meta| { - if meta.ident == "env" { - Ok(meta.ty) - } else { - Err(syn::Error::new( - Span::call_site(), - format!("unknown env attribute '{}'", meta.ident), - )) - } - }) - .collect::>>()?; - if env_types.is_empty() { - return Err(syn::Error::new( - Span::call_site(), - "couldn't find an `#![env = ]` attribute", - )) - } - if env_types.len() > 1 { - return Err(syn::Error::new( - Span::call_site(), - format!( - "requires exactly one `#![env = ]` attribute; found {:?}", - env_types.len() - ), - )) - } - Ok(env_types[0].clone()) - } - - /// Extracts all events from the contract. - /// - /// Performs some semantic checks on them as a whole. - /// - /// # Errors - /// - /// - If there are multiple events with the same names. - /// - If an event has the same name as the contract - fn extract_events( - contract_ident: &Ident, - contract: &ast::Contract, - ) -> Result> { - let events = contract.events().collect::>(); - let mut unique_events = std::collections::HashSet::<&ast::ItemEvent>::new(); - for event in &events { - if &event.ident == contract_ident { - bail!( - event.ident, - "cannot declare an event with the same name as the contract", - ); - } - if !unique_events.contains(event) { - unique_events.insert(event); - } else { - bail!( - event.ident, - "cannot declare multiple events with the same name", - ); - } - } - let mut ret = unique_events - .iter() - .map(|event| { - Event { - attrs: event.attrs.clone(), - ident: event.ident.clone(), - args: event.args.clone(), - } - }) - .collect::>(); - ret.sort_by(|e1, e2| e1.ident.partial_cmp(&e2.ident).unwrap()); - Ok(ret) - } - - /// Extracts the contract state from the contract items - /// and performs some integrity checks on it. - /// - /// # Errors - /// - /// - If no contract state has been found. - /// - If more than one contract state has been found. - fn extract_state(contract: &ast::Contract) -> Result<(&Ident, State)> { - let states = contract.states().collect::>(); - if states.is_empty() { - return Err(syn::Error::new( - Span::call_site(), - "couldn't find a contract state `struct`", - )) - } - if states.len() > 1 { - return Err(syn::Error::new( - Span::call_site(), - format!( - "requires exactly one contract state `struct`; found {:?}", - states.len() - ), - )) - } - let state = states[0]; - Ok((&state.ident, State::from(state))) - } - - fn unpack_impl_blocks( - contract_ident: &Ident, - contract: &ast::Contract, - ) -> Result<(Vec, Vec)> { - let impl_blocks = contract.impl_blocks().collect::>(); - if impl_blocks.is_empty() { - return Err(syn::Error::new( - Span::call_site(), - "requires at least one contract impl block `struct`; found none", - )) - } - for impl_block in impl_blocks.iter() { - if impl_block.self_ty != *contract_ident { - bail!( - impl_block.self_ty, - "contract impl blocks must implement for the contract type: {}", - contract_ident - ); - } - } - use itertools::Itertools as _; - let (messages, methods): (Vec<_>, Vec<_>) = impl_blocks - .into_iter() - .flat_map(|impl_block| impl_block.items.iter()) - .partition_map(|msg_or_method| { - use either::Either; - if msg_or_method.vis.is_external() { - Either::Left(Message::from(msg_or_method)) - } else { - Either::Right(Method::from(msg_or_method)) - } - }); - for method in methods.iter() { - if method.sig.ident == "deploy" { - bail!( - method.sig.ident, - "contract methods must not be named `deploy`", - ); - } - } - for msg in messages.iter() { - if msg.sig.ident == "deploy" { - bail!( - msg.sig.ident, - "contract messages must not be named `deploy`", - ); - } - let inputs = &msg.sig.inputs; - { - match inputs.first() { - None => { - bail!( - msg.sig.ident, - "contract messages must operate on `&self` or `&mut self`", - ); - } - Some(self_ty) => { - match self_ty { - ast::FnArg::Receiver(syn::Receiver { - mutability: None, - reference: Some(_), - .. - }) - | ast::FnArg::Receiver(syn::Receiver { - mutability: Some(_), - reference: Some(_), - .. - }) => (), - _ => { - bail!( - self_ty, - "contract messages must operate on `&self` or `&mut self`", - ); - } - } - } - } - } - for fn_arg in inputs.iter().skip(1) { - if let ast::FnArg::Typed(pat_ty) = fn_arg { - if let syn::Pat::Ident(pat_ident) = &*pat_ty.pat { - if pat_ident.ident == "env" { - bail!( - pat_ident.ident, - "contract messages must not contain an argument called `env`", - ); - } - } - } - } - if msg.sig.generics != Default::default() { - bail!(msg.sig.generics, "contract messages must not be generic"); - } - } - Ok((messages, methods)) - } - - /// Extracts the deploy handler for the given contract identifier - /// out of the given AST based smart contract token tree. - /// - /// # Errors - /// - /// - If there is no deploy handler. - /// - If there is more than one deploy handler for the same contract. - /// - If there is a deploy handler for a contract that does not exist. - fn extract_deploy_handler( - contract_ident: &Ident, - contract: &ast::Contract, - ) -> Result { - let mut deploy_impl_blocks: Vec<&ast::ItemDeployImpl> = - contract.deploy_impl_blocks().collect(); - if deploy_impl_blocks.is_empty() { - bail!( - contract_ident, - "couldn't find a contract deploy implementation; requires exactly one", - ); - } - deploy_impl_blocks.retain(|block| block.self_ty == *contract_ident); - if deploy_impl_blocks.is_empty() { - bail!( - contract_ident, - "couldn't find a contract deploy implementation: `impl Deploy for {} {{ ... }}`", - contract_ident, - ); - } - if deploy_impl_blocks.len() >= 2 { - bail!( - contract_ident, - "found more than one contract deploy implementation for {}", - contract_ident - ); - } - let deploy_impl_block = deploy_impl_blocks[0]; - - let sig = &deploy_impl_block.item.sig; - let self_ty = sig.inputs.first().unwrap(); - match self_ty { - ast::FnArg::Receiver(syn::Receiver { - mutability: Some(_), - reference: Some(_), - .. - }) => (), - _ => { - bail!( - self_ty, - "the deploy implementation must operate on `&mut self`", - ); - } - } - - for fn_arg in sig.inputs.iter().skip(1) { - if let ast::FnArg::Typed(pat_typed) = fn_arg { - if let syn::Pat::Ident(pat_ident) = &*pat_typed.pat { - if pat_ident.ident == "env" { - bail!( - pat_ident.ident, - "the deploy implementation must not contain an argument named `env`", - ); - } - } - } - } - if sig.generics != Default::default() { - bail!( - sig.generics, - "the deploy implementation must not be generic", - ); - } - if sig.output != syn::ReturnType::Default { - bail!( - sig.output, - "the deploy implementation must not have a return type", - ); - } - - Ok(DeployHandler { - attrs: deploy_impl_block.item.attrs.clone(), - sig: deploy_impl_block.item.sig.clone(), - block: deploy_impl_block.item.block.clone(), - }) - } - - /// Creates a high-level representation contract from the given AST-level contract. - /// - /// # Errors - /// - /// If any invariant of a contract is invalidated. - pub fn from_ast(contract: &ast::Contract) -> Result { - let env_types_type = Self::extract_env_types(contract)?; - let (ident, state) = Self::extract_state(contract)?; - let deploy_handler = Self::extract_deploy_handler(ident, contract)?; - let (messages, methods) = Self::unpack_impl_blocks(ident, contract)?; - let events = Self::extract_events(ident, contract)?; - Ok(Self { - name: ident.clone(), - env_types_type, - state, - on_deploy: deploy_handler, - messages, - methods, - events, - }) - } -} - -#[derive(Debug)] -pub struct State { - /// The attributes. - /// - /// # Note - /// - /// Also used for documentation. - pub attrs: Vec, - /// The state fields. - /// - /// # Note - /// - /// These are the fields that are going to - /// be stored in the contract storage. - pub fields: syn::FieldsNamed, -} - -impl From<&ast::ItemState> for State { - fn from(state: &ast::ItemState) -> Self { - Self { - attrs: state.attrs.clone(), - fields: state.fields.clone(), - } - } -} - -/// The deploy handler of a smart contract. -/// -/// # Note -/// -/// This is what is getting called upon deploying a smart contract. -/// Normally this is used to initialize storage values. -#[derive(Debug, Clone)] -pub struct DeployHandler { - /// The attributes. - /// - /// # Note - /// - /// Also used for documentation. - pub attrs: Vec, - /// The function signature. - pub sig: ast::Signature, - /// The actual implementation. - pub block: syn::Block, -} - -impl DeployHandler { - /// Returns all doc attributes of the message. - pub fn docs(&self) -> impl Iterator { - filter_doc_attributes(&self.attrs) - } - - /// Converts this on-deploy handler into its corresponding message. - pub fn into_message(self) -> Message { - Message { - attrs: self.attrs, - sig: self.sig, - block: self.block, - } - } -} - -impl From for DeployHandler { - fn from(msg: Message) -> Self { - Self { - attrs: msg.attrs, - sig: msg.sig, - block: msg.block, - } - } -} - -/// A message that is handled by the smart contract. -/// -/// # Note -/// -/// Messages of a smart contract are only callable externally. -/// They are used to communicate with other smart contracts. -#[derive(Debug)] -pub struct Message { - /// The attributes. - /// - /// # Note - /// - /// Also used for documentation. - pub attrs: Vec, - /// The message signature. - /// - /// # Note - /// - /// This also holds the name of the message. - pub sig: ast::Signature, - /// The actual implementation. - pub block: syn::Block, -} - -impl Message { - /// Returns all doc attributes of the message. - pub fn docs(&self) -> impl Iterator { - filter_doc_attributes(&self.attrs) - } - - /// Returns `true` if the message potentially mutates its state. - pub fn is_mut(&self) -> bool { - let self_arg = self - .sig - .inputs - .iter() - .next() - .expect("messages must always have at least `&mut self` as parameter"); - match self_arg { - ast::FnArg::Receiver(syn::Receiver { - reference, - mutability, - .. - }) => reference.is_some() && mutability.is_some(), - _ => panic!(), - } - } - - /// Returns the message selector for this message. - pub fn selector(&self) -> [u8; 4] { - raw_message_selector(self.sig.ident.to_string().as_str()) - } -} - -fn raw_message_selector(name: &str) -> [u8; 4] { - let keccak = ink_utils::hash::keccak256(name.as_bytes()); - [keccak[3], keccak[2], keccak[1], keccak[0]] -} - -impl From<&ast::ItemImplMethod> for Message { - fn from(impl_method: &ast::ItemImplMethod) -> Self { - Self { - attrs: impl_method.attrs.clone(), - sig: impl_method.sig.clone(), - block: impl_method.block.clone(), - } - } -} - -/// A method defined on the smart contract. -#[derive(Debug)] -pub struct Method { - /// The attributes. - /// - /// # Note - /// - /// Also used for documentation. - pub attrs: Vec, - /// The method visibility. - /// - /// # Note - /// - /// Currently only inherent visibility (private) is - /// available for methods. - pub vis: ast::MethodVisibility, - /// The method signature. - /// - /// # Note - /// - /// This also holds the name of the method. - pub sig: ast::Signature, - /// The actual implementation. - pub block: syn::Block, -} - -impl From<&ast::ItemImplMethod> for Method { - fn from(impl_method: &ast::ItemImplMethod) -> Self { - Self { - attrs: impl_method.attrs.clone(), - sig: impl_method.sig.clone(), - vis: impl_method.vis.clone(), - block: impl_method.block.clone(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn message_selectors() { - assert_eq!(raw_message_selector("inc"), [15, 89, 208, 231]); - assert_eq!(raw_message_selector("get"), [254, 74, 68, 37]); - assert_eq!(raw_message_selector("compare"), [21, 176, 197, 12]); - } -} diff --git a/lang/src/ident_ext.rs b/lang/src/ident_ext.rs deleted file mode 100644 index 9b19b0be99b..00000000000 --- a/lang/src/ident_ext.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018-2019 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 proc_macro2::{ - Ident, - Span, -}; - -/// Utilities for operating on `Ident` instances. -pub trait IdentExt { - /// Creates a new Ident from the given `str`. - fn from_str>(s: T) -> Ident { - Ident::new(s.as_ref(), Span::call_site()) - } -} - -impl IdentExt for Ident {} diff --git a/lang/src/lib.rs b/lang/src/lib.rs deleted file mode 100644 index 7011de06037..00000000000 --- a/lang/src/lib.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018-2019 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. - -#![recursion_limit = "256"] - -extern crate proc_macro; - -#[macro_use] -mod error; - -mod ast; -mod gen; -mod hir; -mod ident_ext; -mod parser; - -#[cfg(feature = "ink-generate-abi")] -mod old_abi; - -#[cfg(test)] -mod tests; - -mod contract; - -use proc_macro::TokenStream; - -#[proc_macro] -pub fn contract(input: TokenStream) -> TokenStream { - contract::generate(input.into()).into() -} - -#[cfg(test)] -pub use contract::generate_or_err; diff --git a/lang/src/old_abi.rs b/lang/src/old_abi.rs deleted file mode 100644 index 923cf022291..00000000000 --- a/lang/src/old_abi.rs +++ /dev/null @@ -1,794 +0,0 @@ -// Copyright 2018-2019 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. - -//! The old ink! ABI format generation routine. -//! -//! This is in place as long as the new ABI format is not well supported -//! in all important areas of use. -//! Remove this as soon as the new ABI format is stable enough. -//! Instead of using the old crate feature the old ABI generation is using -//! the newly introduced `ink-generate-abi` crate feature. - -use std::convert::TryFrom; - -use serde::{ - Deserialize, - Serialize, -}; -use syn::Result; - -use crate::{ - ast, - hir, -}; - -/// Describes a message parameter or return type. -#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] -#[serde(untagged)] -pub enum TypeDescription { - /// The `bool` primitive type. - Primitive(PrimitiveTypeDescription), - /// The tuple type - Tuple(TupleTypeDescription), - /// The fixed size array type - Array(ArrayTypeDescription), - /// A concrete `Option` type. - Option(OptionTypeDescription), - /// A concrete `Result` type. - Result(ResultTypeDescription), - /// A concrete `Vec` type. - Vec(VecTypeDescription), -} - -/// Describes an option param or return type. -#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] -pub enum OptionTypeDescription { - #[serde(rename = "Option")] - Single { - /// The generic type param. - #[serde(rename = "T")] - inner: Box, - }, -} - -impl TryFrom<&syn::TypePath> for OptionTypeDescription { - type Error = syn::Error; - - fn try_from(type_path: &syn::TypePath) -> Result { - if type_path.qself.is_some() || type_path.path.leading_colon.is_some() { - bail!(type_path, "`Option` cannot be qualified or start with `::`"); - } - if type_path.path.segments.len() != 1 { - bail!(type_path, "too many path segments for an `Option` type"); - } - let seg = &type_path.path.segments[0]; - if seg.ident != "Option" { - bail!(type_path, "invalid ident for `Option` type"); - } - match &seg.arguments { - syn::PathArguments::AngleBracketed(generic_args) => { - if generic_args.args.len() != 1 { - bail!(generic_args, "too many generic args for `Option` type"); - } - match &generic_args.args[0] { - syn::GenericArgument::Type(ty) => { - Ok(OptionTypeDescription::Single { - inner: Box::new(TypeDescription::try_from(ty)?), - }) - } - invalid => bail!(invalid, "invalid generic type args for `Option`"), - } - } - invalid => bail!(invalid, "invalid type arguments for `Option`"), - } - } -} - -/// Describes a `Vec` param or return type. -/// -/// # Note -/// -/// With `Vec` we refer to `memory::Vec` here. -#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] -pub enum VecTypeDescription { - #[serde(rename = "Vec")] - Single { - /// The generic type param. - #[serde(rename = "T")] - elem_type: Box, - }, -} - -impl TryFrom<&syn::TypePath> for VecTypeDescription { - type Error = syn::Error; - - fn try_from(type_path: &syn::TypePath) -> Result { - if type_path.qself.is_some() || type_path.path.leading_colon.is_some() { - bail!(type_path, "`Vec` cannot be qualified or start with `::`"); - } - if type_path.path.segments.len() != 1 { - bail!(type_path, "too many path segments for an `Vec` type"); - } - let seg = &type_path.path.segments[0]; - if seg.ident != "Vec" { - bail!(type_path, "invalid ident for `Vec` type"); - } - match &seg.arguments { - syn::PathArguments::AngleBracketed(generic_args) => { - if generic_args.args.len() != 1 { - bail!(generic_args, "too many generic args for `Vec` type"); - } - match &generic_args.args[0] { - syn::GenericArgument::Type(ty) => { - Ok(VecTypeDescription::Single { - elem_type: Box::new(TypeDescription::try_from(ty)?), - }) - } - invalid => bail!(invalid, "invalid generic type args for `Vec`"), - } - } - invalid => bail!(invalid, "invalid type arguments for `Vec`"), - } - } -} - -/// Describes a result param or return type. -#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] -pub enum ResultTypeDescription { - #[serde(rename = "Result")] - Single { - /// The `Ok`-type. - #[serde(rename = "T")] - ok_type: Box, - /// The `Err`-type. - #[serde(rename = "E")] - err_type: Box, - }, -} - -impl TryFrom<&syn::TypePath> for ResultTypeDescription { - type Error = syn::Error; - - fn try_from(type_path: &syn::TypePath) -> Result { - if type_path.qself.is_some() || type_path.path.leading_colon.is_some() { - bail!(type_path, "`Result` cannot be qualified or start with `::`"); - } - if type_path.path.segments.len() != 1 { - bail!(type_path, "too many path segments for an `Result` type"); - } - let seg = &type_path.path.segments[0]; - if seg.ident != "Result" { - bail!(type_path, "invalid ident for `Result` type"); - } - match &seg.arguments { - syn::PathArguments::AngleBracketed(generic_args) => { - if generic_args.args.len() != 2 { - bail!( - generic_args, - "`Result` type requires 2 generic type arguments", - ); - } - let ok_type = match &generic_args.args[0] { - syn::GenericArgument::Type(ty) => TypeDescription::try_from(ty), - invalid => bail!(invalid, "invalid generic type args for `Result`"), - }?; - let err_type = match &generic_args.args[1] { - syn::GenericArgument::Type(ty) => TypeDescription::try_from(ty), - invalid => bail!(invalid, "invalid generic type args for `Result`"), - }?; - Ok(ResultTypeDescription::Single { - ok_type: Box::new(ok_type), - err_type: Box::new(err_type), - }) - } - invalid => bail!(invalid, "invalid type arguments for `Result`"), - } - } -} - -impl TryFrom<&syn::Type> for TypeDescription { - type Error = syn::Error; - - fn try_from(ty: &syn::Type) -> Result { - match ty { - syn::Type::Tuple(tuple) => { - TupleTypeDescription::try_from(tuple).map(TypeDescription::Tuple) - } - syn::Type::Array(array) => { - ArrayTypeDescription::try_from(array).map(TypeDescription::Array) - } - syn::Type::Path(path) => { - if path.path.segments.len() != 1 || path.path.leading_colon.is_some() { - bail!(path, "invalid self qualifier or leading `::` for type"); - } - let ident = &path.path.segments[0].ident; - match ident.to_string().as_str() { - "Option" => { - OptionTypeDescription::try_from(path).map(TypeDescription::Option) - } - "Result" => { - ResultTypeDescription::try_from(path).map(TypeDescription::Result) - } - "Vec" => VecTypeDescription::try_from(path).map(TypeDescription::Vec), - _ => { - PrimitiveTypeDescription::try_from(path) - .map(TypeDescription::Primitive) - } - } - } - invalid => bail!(invalid, "invalid or unsupported type"), - } - } -} - -#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] -pub enum PrimitiveTypeDescription { - /// The `bool` primitive type. - #[serde(rename = "bool")] - Bool, - /// The `u8` primitive unsigned integer. - #[serde(rename = "u8")] - U8, - /// The `u16` primitive unsigned integer. - #[serde(rename = "u16")] - U16, - /// The `u32` primitive unsigned integer. - #[serde(rename = "u32")] - U32, - /// The `u64` primitive unsigned integer. - #[serde(rename = "u64")] - U64, - /// The `u128` primitive unsigned integer. - #[serde(rename = "u128")] - U128, - /// The `i8` primitive signed integer. - #[serde(rename = "i8")] - I8, - /// The `i16` primitive signed integer. - #[serde(rename = "i16")] - I16, - /// The `i32` primitive signed integer. - #[serde(rename = "i32")] - I32, - /// The `i64` primitive signed integer. - #[serde(rename = "i64")] - I64, - /// The `i128` primitive signed integer. - #[serde(rename = "i128")] - I128, - /// The SRML address type. - AccountId, - /// The SRML balance type. - Balance, - /// The SRML hash type. - Hash, - /// The SRML moment type. - Moment, - /// The SRML block number type. - BlockNumber, -} - -impl TryFrom<&syn::TypePath> for PrimitiveTypeDescription { - type Error = syn::Error; - - fn try_from(ty: &syn::TypePath) -> Result { - use quote::ToTokens; - - match ty.into_token_stream().to_string().as_str() { - "bool" => Ok(PrimitiveTypeDescription::Bool), - "u8" => Ok(PrimitiveTypeDescription::U8), - "u16" => Ok(PrimitiveTypeDescription::U16), - "u32" => Ok(PrimitiveTypeDescription::U32), - "u64" => Ok(PrimitiveTypeDescription::U64), - "u128" => Ok(PrimitiveTypeDescription::U128), - "i8" => Ok(PrimitiveTypeDescription::I8), - "i16" => Ok(PrimitiveTypeDescription::I16), - "i32" => Ok(PrimitiveTypeDescription::I32), - "i64" => Ok(PrimitiveTypeDescription::I64), - "i128" => Ok(PrimitiveTypeDescription::I128), - "AccountId" => Ok(PrimitiveTypeDescription::AccountId), - "Balance" => Ok(PrimitiveTypeDescription::Balance), - "Hash" => Ok(PrimitiveTypeDescription::Hash), - "Moment" => Ok(PrimitiveTypeDescription::Moment), - "BlockNumber" => Ok(PrimitiveTypeDescription::BlockNumber), - unsupported => { - bail!( - ty, - "{} is unsupported as message interface type", - unsupported - ); - } - } - } -} - -/// Describes a tuple type -#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] -#[serde(transparent)] -pub struct TupleTypeDescription { - elems: Vec, -} - -impl TryFrom<&syn::TypeTuple> for TupleTypeDescription { - type Error = syn::Error; - - fn try_from(arg: &syn::TypeTuple) -> Result { - let elems = arg - .elems - .iter() - .map(TypeDescription::try_from) - .collect::>()?; - Ok(TupleTypeDescription { elems }) - } -} - -/// Describes a fixed size array type -#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] -pub enum ArrayTypeDescription { - #[serde(rename = "[T;n]")] - FixedLength { - #[serde(rename = "T")] - inner: Box, - #[serde(rename = "n")] - arity: u32, - }, -} - -impl TryFrom<&syn::TypeArray> for ArrayTypeDescription { - type Error = syn::Error; - - fn try_from(arg: &syn::TypeArray) -> Result { - let ty = TypeDescription::try_from(&*arg.elem)?; - if let syn::Expr::Lit(syn::ExprLit { - lit: syn::Lit::Int(ref int_lit), - .. - }) = arg.len - { - Ok(ArrayTypeDescription::FixedLength { - inner: Box::new(ty), - arity: int_lit.base10_parse::()?, - }) - } else { - bail!(arg.len, "invalid array length expression"); - } - } -} - -/// Describes a pair of parameter name and type. -#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct ParamDescription { - /// The name of the parameter. - name: String, - /// The type of the parameter. - #[serde(rename = "type")] - ty: TypeDescription, -} - -impl TryFrom<&syn::PatType> for ParamDescription { - type Error = syn::Error; - - fn try_from(arg: &syn::PatType) -> Result { - let name = match &*arg.pat { - syn::Pat::Ident(ident) => ident.ident.to_string(), - _ => { - bail!(arg.pat, "unsupported type pattern, currently only identifiers like `foo` are supported"); - } - }; - Ok(Self { - name, - ty: TypeDescription::try_from(&*arg.ty)?, - }) - } -} - -/// Describes the deploy handler of a contract. -#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct DeployDescription { - /// The parameters of the deploy handler. - args: Vec, -} - -impl TryFrom<&hir::DeployHandler> for DeployDescription { - type Error = syn::Error; - - fn try_from(deploy_handler: &hir::DeployHandler) -> Result { - let args = deploy_handler - .sig - .inputs - .iter() - .filter_map(|arg| { - match arg { - ast::FnArg::Typed(pat_ty) => { - let description = ParamDescription::try_from(pat_ty); - Some(description) - } - _ => None, - } - }) - .collect::>>()?; - Ok(Self { args }) - } -} - -/// Describes the return type of a contract message. -#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] -#[serde(transparent)] -pub struct ReturnTypeDescription { - #[serde(rename = "type")] - opt_type: Option, -} - -impl ReturnTypeDescription { - /// Creates a new return type description from the given optional type. - pub fn new(opt_type: T) -> Self - where - T: Into>, - { - Self { - opt_type: opt_type.into(), - } - } -} - -impl TryFrom<&syn::ReturnType> for ReturnTypeDescription { - type Error = syn::Error; - - fn try_from(ret_ty: &syn::ReturnType) -> Result { - match ret_ty { - syn::ReturnType::Default => Ok(ReturnTypeDescription::new(None)), - syn::ReturnType::Type(_, ty) => { - Ok(ReturnTypeDescription::new(Some(TypeDescription::try_from( - &**ty, - )?))) - } - } - } -} - -/// Describes a contract message. -#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct MessageDescription { - /// The name of the message. - name: String, - /// The selector hash of the message. - selector: u64, - /// If the message is allowed to mutate the contract state. - mutates: bool, - /// The parameters of the message. - args: Vec, - /// The return type of the message. - return_type: ReturnTypeDescription, -} - -impl TryFrom<&hir::Message> for MessageDescription { - type Error = syn::Error; - - fn try_from(message: &hir::Message) -> Result { - let s = message.selector(); - let selector = u32::from_le_bytes([s[3], s[2], s[1], s[0]]).into(); - Ok(Self { - name: message.sig.ident.to_string(), - selector, - mutates: message.is_mut(), - args: { - message - .sig - .inputs - .iter() - .filter_map(|arg| { - match arg { - ast::FnArg::Typed(pat_ty) => { - Some(ParamDescription::try_from(pat_ty)) - } - _ => None, - } - }) - .collect::>>()? - }, - return_type: ReturnTypeDescription::try_from(&message.sig.output)?, - }) - } -} - -/// Describes a contract. -#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct ContractDescription { - /// The name of the contract. - name: String, - /// The deploy handler of the contract. - deploy: DeployDescription, - /// The external messages of the contract. - messages: Vec, -} - -impl TryFrom<&hir::Contract> for ContractDescription { - type Error = syn::Error; - - fn try_from(contract: &hir::Contract) -> Result { - Ok(ContractDescription { - name: contract.name.to_string(), - deploy: DeployDescription::try_from(&contract.on_deploy)?, - messages: { - contract - .messages - .iter() - .map(MessageDescription::try_from) - .collect::>>()? - }, - }) - } -} - -/// Writes a JSON API description into the `target/` folder. -pub fn generate_old_abi(contract: &hir::Contract) -> Result<()> { - let description = ContractDescription::try_from(contract)?; - let contents = serde_json::to_string_pretty(&description) - .expect("Failed at generating JSON API description as JSON"); - let path_buf = String::from("target/old_abi.json"); - std::fs::create_dir("target").unwrap_or(()); - std::fs::write(path_buf, contents) - .expect("Failed at writing JSON API descrition to file"); - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::{ - PrimitiveTypeDescription::*, - TypeDescription::*, - *, - }; - use syn::parse_quote; - - fn assert_eq_type_description(ty: syn::Type, expected: TypeDescription) { - let actual = TypeDescription::try_from(&ty).unwrap(); - assert_eq!(expected, actual); - } - - fn assert_json_roundtrip(ty: syn::Type, json: &str) { - let td = TypeDescription::try_from(&ty).unwrap(); - let actual_json = serde_json::to_string(&td).unwrap(); - assert_eq!(json, actual_json); - let deserialized: TypeDescription = serde_json::de::from_str(json).unwrap(); - assert_eq!(td, deserialized); - } - - #[test] - fn primitives() { - assert_eq_type_description( - parse_quote!(u16), - Primitive(PrimitiveTypeDescription::U16), - ); - assert_eq_type_description( - parse_quote!(bool), - Primitive(PrimitiveTypeDescription::Bool), - ); - assert_eq_type_description( - parse_quote!(i64), - Primitive(PrimitiveTypeDescription::I64), - ); - assert_eq_type_description( - parse_quote!(AccountId), - Primitive(PrimitiveTypeDescription::AccountId), - ); - assert_eq_type_description( - parse_quote!(Moment), - Primitive(PrimitiveTypeDescription::Moment), - ); - assert_eq_type_description( - parse_quote!(BlockNumber), - Primitive(PrimitiveTypeDescription::BlockNumber), - ); - } - - #[test] - fn tuple_basic() { - assert_eq_type_description( - parse_quote!((bool, i32)), - Tuple(TupleTypeDescription { - elems: vec![Primitive(Bool), Primitive(I32)], - }), - ) - } - - #[test] - fn tuple_nested() { - assert_eq_type_description( - parse_quote!((u32, (bool, i32))), - Tuple(TupleTypeDescription { - elems: vec![ - Primitive(U32), - Tuple(TupleTypeDescription { - elems: vec![Primitive(Bool), Primitive(I32)], - }), - ], - }), - ) - } - - #[test] - fn tuple_of_arrays() { - assert_eq_type_description( - parse_quote!(([i32; 2], [u32; 2])), - Tuple(TupleTypeDescription { - elems: vec![ - Array(ArrayTypeDescription::FixedLength { - inner: Box::new(Primitive(I32)), - arity: 2, - }), - Array(ArrayTypeDescription::FixedLength { - inner: Box::new(Primitive(U32)), - arity: 2, - }), - ], - }), - ) - } - - #[test] - fn array_basic() { - assert_eq_type_description( - parse_quote!([u32; 5]), - Array(ArrayTypeDescription::FixedLength { - inner: Box::new(Primitive(U32)), - arity: 5, - }), - ) - } - - #[test] - fn array_nested() { - assert_eq_type_description( - parse_quote!([[u32; 5]; 3]), - Array(ArrayTypeDescription::FixedLength { - inner: Box::new(Array(ArrayTypeDescription::FixedLength { - inner: Box::new(Primitive(U32)), - arity: 5, - })), - arity: 3, - }), - ) - } - - #[test] - fn array_of_tuples() { - assert_eq_type_description( - parse_quote!([(bool, u32); 5]), - Array(ArrayTypeDescription::FixedLength { - inner: Box::new(Tuple(TupleTypeDescription { - elems: vec![Primitive(Bool), Primitive(U32)], - })), - arity: 5, - }), - ) - } - - #[test] - fn tuple_json() { - assert_json_roundtrip(parse_quote!((u64, i32)), r#"["u64","i32"]"#) - } - - #[test] - fn array_json() { - assert_json_roundtrip(parse_quote!([u32; 5]), r#"{"[T;n]":{"T":"u32","n":5}}"#) - } - - fn expect_failure(input: syn::Type, expected_err: &str) { - let res = TypeDescription::try_from(&input).map_err(|err| format!("{}", err)); - assert_eq!(res, Err(String::from(expected_err))); - } - - #[test] - fn option_json_failure() { - expect_failure( - parse_quote!(::Option), - "invalid self qualifier or leading `::` for type", - ); - expect_failure( - parse_quote!(::Option), - "invalid self qualifier or leading `::` for type", - ); - expect_failure( - parse_quote!(Option), - "too many generic args for `Option` type", - ); - expect_failure( - parse_quote!(Option<'a>), - "invalid generic type args for `Option`", - ); - } - - #[test] - fn option_json_success() { - assert_json_roundtrip(parse_quote!(Option), r#"{"Option":{"T":"i32"}}"#); - assert_json_roundtrip( - parse_quote!(Option<(bool, i32)>), - r#"{"Option":{"T":["bool","i32"]}}"#, - ); - assert_json_roundtrip( - parse_quote!(Option>), - r#"{"Option":{"T":{"Option":{"T":"i32"}}}}"#, - ); - } - - #[test] - fn vec_json_failure() { - expect_failure( - parse_quote!(::Vec), - "invalid self qualifier or leading `::` for type", - ); - expect_failure( - parse_quote!(::Vec), - "invalid self qualifier or leading `::` for type", - ); - expect_failure( - parse_quote!(Vec), - "too many generic args for `Vec` type", - ); - expect_failure(parse_quote!(Vec<'a>), "invalid generic type args for `Vec`"); - } - - #[test] - fn vec_json_success() { - assert_json_roundtrip(parse_quote!(Vec), r#"{"Vec":{"T":"i32"}}"#); - assert_json_roundtrip( - parse_quote!(Vec<(bool, i32)>), - r#"{"Vec":{"T":["bool","i32"]}}"#, - ); - assert_json_roundtrip( - parse_quote!(Vec>), - r#"{"Vec":{"T":{"Vec":{"T":"i32"}}}}"#, - ); - } - - #[test] - fn result_json_failure() { - expect_failure( - parse_quote!(::Result), - "invalid self qualifier or leading `::` for type", - ); - expect_failure( - parse_quote!(::Result), - "invalid self qualifier or leading `::` for type", - ); - expect_failure( - parse_quote!(Result), - "`Result` type requires 2 generic type arguments", - ); - expect_failure( - parse_quote!(Result), - "`Result` type requires 2 generic type arguments", - ); - expect_failure( - parse_quote!(Result<'a, bool>), - "invalid generic type args for `Result`", - ); - } - - #[test] - fn result_json_success() { - assert_json_roundtrip( - parse_quote!(Result), - r#"{"Result":{"T":"bool","E":"i32"}}"#, - ); - assert_json_roundtrip( - parse_quote!(Result<(bool, i32), [u8; 8]>), - r#"{"Result":{"T":["bool","i32"],"E":{"[T;n]":{"T":"u8","n":8}}}}"#, - ); - assert_json_roundtrip( - parse_quote!(Result, Result>), - r#"{"Result":{"T":{"Result":{"T":"u8","E":"i8"}},"E":{"Result":{"T":"u16","E":"i16"}}}}"#, - ); - } -} diff --git a/lang/src/parser.rs b/lang/src/parser.rs deleted file mode 100644 index ac20dcc996e..00000000000 --- a/lang/src/parser.rs +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright 2018-2019 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 proc_macro2::{ - Ident, - Span, - TokenStream as TokenStream2, -}; -use syn::{ - parse::{ - Parse, - ParseStream, - Result, - }, - Token, -}; - -use crate::ast; - -pub mod keywords { - use syn::custom_keyword; - - custom_keyword!(Deploy); - custom_keyword!(deploy); - custom_keyword!(external); - custom_keyword!(event); -} - -pub fn parse_contract(token_stream: TokenStream2) -> Result { - syn::parse2(token_stream).map_err(Into::into) -} - -impl Parse for ast::Contract { - fn parse(input: ParseStream<'_>) -> Result { - Ok(ast::Contract { - items: ast::Item::parse_outer(input)?, - }) - } -} - -impl ast::Item { - fn parse_outer(input: ParseStream<'_>) -> Result> { - let mut res = Vec::new(); - while !input.is_empty() { - res.push(input.parse()?); - } - Ok(res) - } -} - -impl Parse for ast::Item { - fn parse(input: ParseStream<'_>) -> Result { - let inner_attrs: ast::ItemEnvMeta = input.parse()?; - if !inner_attrs.env_types_metas.is_empty() { - return Ok(ast::Item::EnvMeta(inner_attrs)) - } - let attrs_outer = syn::Attribute::parse_outer(input)?; - let lookahead = input.lookahead1(); - if lookahead.peek(Token![struct]) { - input.parse().map(|mut state: ast::ItemState| { - state.attrs = attrs_outer; - ast::Item::State(state) - }) - } else if lookahead.peek(Token![impl]) { - if input.peek2(keywords::Deploy) { - input.parse().map(|mut deploy_impl: ast::ItemDeployImpl| { - deploy_impl.attrs = attrs_outer; - ast::Item::DeployImpl(deploy_impl) - }) - } else { - input.parse().map(|mut impl_block: ast::ItemImpl| { - impl_block.attrs = attrs_outer; - ast::Item::Impl(impl_block) - }) - } - } else if lookahead.peek(keywords::event) { - input.parse().map(|mut event: ast::ItemEvent| { - event.attrs = attrs_outer; - ast::Item::Event(event) - }) - } else { - Err(lookahead.error()) - } - } -} - -impl Parse for ast::ItemEnvMeta { - fn parse(input: ParseStream<'_>) -> Result { - let attrs = input.call(syn::Attribute::parse_inner)?; - let env_types = attrs - .iter() - .map(ast::ItemEnvTypesMeta::parse_from_attr) - .collect::>>()?; - Ok(Self { - env_types_metas: env_types, - }) - } -} - -impl ast::ItemEnvTypesMeta { - fn parse_from_attr(attr: &syn::Attribute) -> Result { - let first_segment = attr - .path - .segments - .pairs() - .next() - .expect("paths have at least one segment") - .into_tuple(); - if let Some(colon) = first_segment.1 { - return Err(syn::Error::new(colon.spans[0], "expected meta value")) - } - let ident = first_segment.0.ident.clone(); - let parser = |input: ParseStream<'_>| { - let eq_token = input.parse()?; - let ty = input.parse()?; - Ok(Self { - ident, - eq_token, - ty, - }) - }; - syn::parse::Parser::parse2(parser, attr.tokens.clone()) - } -} - -impl Parse for ast::ItemState { - fn parse(input: ParseStream<'_>) -> Result { - let struct_tok = input.parse()?; - let ident = input.parse()?; - let fields = input.parse()?; - Ok(Self { - attrs: vec![], - struct_tok, - ident, - fields, - }) - } -} - -impl Parse for ast::ItemDeployImpl { - fn parse(input: ParseStream<'_>) -> Result { - let impl_tok = input.parse()?; - let deploy_tok = input.parse()?; - let for_tok = input.parse()?; - let self_ty = input.parse()?; - let content; - let (brace_tok, inner_attrs, deploy_fn_impl) = { - let brace_tok = syn::braced!(content in input); - let inner_attrs = content.call(syn::Attribute::parse_inner)?; - let deploy_fn_impl = content.parse()?; - (brace_tok, inner_attrs, deploy_fn_impl) - }; - Ok(Self { - attrs: inner_attrs, - impl_tok, - deploy_tok, - for_tok, - self_ty, - brace_tok, - item: deploy_fn_impl, - }) - } -} - -impl Parse for ast::DeployItemMethod { - fn parse(input: ParseStream<'_>) -> Result { - let attrs = syn::Attribute::parse_outer(input)?; - let fn_tok = input.parse()?; - let deploy_tok = input.parse()?; - let (paren_tok, inputs) = { - let content; - let paren_tok = syn::parenthesized!(content in input); - let inputs = content.parse_terminated(ast::FnArg::parse)?; - (paren_tok, inputs) - }; - let output = input.parse()?; - let block = input.parse()?; - Ok(Self { - attrs, - deploy_tok, - sig: ast::Signature { - ident: Ident::new("deploy", Span::call_site()), - fn_tok, - paren_tok, - inputs, - output, - generics: Default::default(), - }, - block, - }) - } -} - -impl Parse for ast::ItemImpl { - fn parse(input: ParseStream<'_>) -> Result { - let impl_tok = input.parse()?; - let self_ty = input.parse()?; - let (brace_tok, inner_attrs, items) = { - let content; - let brace_tok = syn::braced!(content in input); - let inner_attrs = content.call(syn::Attribute::parse_inner)?; - - let mut items = Vec::new(); - while !content.is_empty() { - items.push(content.parse()?); - } - (brace_tok, inner_attrs, items) - }; - Ok(Self { - attrs: inner_attrs, - impl_tok, - self_ty, - brace_tok, - items, - }) - } -} - -impl Parse for ast::ItemImplMethod { - fn parse(input: ParseStream<'_>) -> Result { - let attrs = syn::Attribute::parse_outer(input)?; - let vis = input.parse()?; - let fn_tok = input.parse()?; - let ident = input.parse()?; - let generics: syn::Generics = input.parse()?; - let (paren_tok, inputs) = { - let content; - let paren_tok = syn::parenthesized!(content in input); - let inputs = content.parse_terminated(ast::FnArg::parse)?; - (paren_tok, inputs) - }; - let output = input.parse()?; - let where_clause: Option = input.parse()?; - let block = input.parse()?; - - Ok(Self { - attrs, - vis, - sig: ast::Signature { - ident, - fn_tok, - paren_tok, - inputs, - output, - generics: syn::Generics { - where_clause, - ..generics - }, - }, - block, - }) - } -} - -impl Parse for ast::MethodVisibility { - fn parse(input: ParseStream<'_>) -> Result { - if input.peek(Token![pub]) { - Ok(ast::MethodVisibility::External(input.parse()?)) - } else { - Ok(ast::MethodVisibility::Inherited) - } - } -} - -impl Parse for ast::ExternalVisibility { - fn parse(input: ParseStream<'_>) -> Result { - let pub_tok = input.parse::()?; - let content; - let paren_tok = syn::parenthesized!(content in input); - let external_tok = content.parse()?; - Ok(ast::ExternalVisibility { - pub_tok, - paren_tok, - external_tok, - }) - } -} - -impl Parse for ast::FnArg { - fn parse(input: ParseStream<'_>) -> Result { - if input.peek(Token![&]) { - let ahead = input.fork(); - if ahead.call(ast::FnArg::receiver).is_ok() && !ahead.peek(Token![:]) { - return input.call(ast::FnArg::receiver).map(ast::FnArg::Receiver) - } - } - - if input.peek(Token![mut]) || input.peek(Token![self]) { - let ahead = input.fork(); - if ahead.call(ast::FnArg::receiver).is_ok() && !ahead.peek(Token![:]) { - return input.call(ast::FnArg::receiver).map(ast::FnArg::Receiver) - } - } - - let ahead = input.fork(); - let err = match ahead.call(ast::FnArg::pat_typed) { - Ok(_) => return input.call(ast::FnArg::pat_typed).map(ast::FnArg::Typed), - Err(err) => err, - }; - - Err(err) - } -} - -impl ast::FnArg { - fn receiver(input: ParseStream<'_>) -> Result { - Ok(input.parse()?) - } - - fn pat_typed(input: ParseStream<'_>) -> Result { - Ok(syn::PatType { - attrs: syn::Attribute::parse_outer(input)?, - pat: input.parse()?, - colon_token: input.parse()?, - ty: input.parse()?, - }) - } -} - -impl Parse for ast::ItemEvent { - fn parse(input: ParseStream<'_>) -> Result { - let event_tok = input.parse()?; - let ident = input.parse()?; - let (brace_tok, args) = { - let content; - let brace_tok = syn::braced!(content in input); - let inputs = content.parse_terminated(ast::EventArg::parse)?; - (brace_tok, inputs) - }; - Ok(Self { - attrs: vec![], - event_tok, - ident, - brace_tok, - args, - }) - } -} - -impl Parse for ast::EventArg { - fn parse(input: ParseStream<'_>) -> Result { - let attrs = syn::Attribute::parse_outer(input)?; - let ident = input.parse()?; - let colon_tok = input.parse()?; - let ty = input.parse()?; - Ok(Self { - attrs, - ident, - colon_tok, - ty, - }) - } -} diff --git a/lang/src/tests/events.rs b/lang/src/tests/events.rs deleted file mode 100644 index 2c3a0d7b5b5..00000000000 --- a/lang/src/tests/events.rs +++ /dev/null @@ -1,514 +0,0 @@ -// Copyright 2018-2019 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::*; - -#[test] -fn contract_compiles() { - assert_eq_tokenstreams( - quote! { - #![env = DefaultSrmlTypes] - - /// Tests emitting of custom defined events. - struct CallCounter { - /// A simple counter for the calls. - count: storage::Value, - } - - impl Deploy for CallCounter { - fn deploy(&mut self) { - } - } - - /// Fires when the value is incremented. - event IncCalled { - /// The current value. - current: u32 - } - - /// Fires when the value is decremented. - event DecCalled { - /// The current value. - current: u32 - } - - impl CallCounter { - /// Increments the internal counter. - /// - /// # Note - /// - /// Also emits an event. - pub(external) fn inc(&mut self) { - self.value += 1; - env.emit(IncCalled { current: *self.value }); - } - - /// Decrements the internal counter. - /// - /// # Note - /// - /// Also emits an event. - pub(external) fn dec(&mut self) { - self.value -= 1; - env.emit(DecCalled { current: *self.value }); - } - } - }, - quote! { - mod types { - use super::*; - use ink_core::env::{ContractEnv, EnvTypes}; - - pub type AccountId = ::AccountId; - pub type Balance = ::Balance; - pub type Hash = ::Hash; - pub type Moment = ::Moment; - pub type BlockNumber = ::BlockNumber; - } - - type Env = ink_core::env::ContractEnv; - use types::{ - AccountId, - Balance, - Hash, - Moment, - BlockNumber, - }; - - #[cfg(not(feature = "ink-as-dependency"))] - mod normal { - use super::*; - - ink_model::state! { - /// Tests emitting of custom defined events. - #[cfg_attr( - feature = "ink-generate-abi", - derive(type_metadata::Metadata, ink_abi::HasLayout,) - )] - pub struct CallCounter { - /// A simple counter for the calls. - count: storage::Value, - } - } - - - mod msg { - use super::*; - use ink_model::messages; - - ink_model::messages! { - /// Increments the internal counter. - /// - /// # Note - /// - /// Also emits an event. - [15, 89, 208, 231] => Inc(); - /// Decrements the internal counter. - /// - /// # Note - /// - /// Also emits an event. - [105, 169, 85, 123] => Dec(); - } - } - - impl CallCounter { - pub fn deploy(&mut self, env: &mut ink_model::EnvHandler >) {} - - /// Increments the internal counter. - /// - /// # Note - /// - /// Also emits an event. - pub fn inc(&mut self, env: &mut ink_model::EnvHandler >) { - self.value += 1; - env.emit(IncCalled { current: *self.value }); - } - - /// Decrements the internal counter. - /// - /// # Note - /// - /// Also emits an event. - pub fn dec(&mut self, env: &mut ink_model::EnvHandler >) { - self.value -= 1; - env.emit(DecCalled { current: *self.value }); - } - } - - use ink_model::Contract as _; - - #[cfg(not(test))] - impl CallCounter { - pub(crate) fn instantiate() -> impl ink_model::Contract { - ink_model::ContractDecl::using::>() - .on_deploy(|env, ()| { - let (handler, state) = env.split_mut(); - state.deploy(handler,) - }) - .on_msg_mut::(|env, _| { - let (handler, state) = env.split_mut(); - state.inc(handler,) - }) - .on_msg_mut::(|env, _| { - let (handler, state) = env.split_mut(); - state.dec(handler,) - }) - .instantiate() - } - } - - #[cfg(not(test))] #[no_mangle] fn deploy() -> u32 { CallCounter::instantiate().deploy().to_u32() } - #[cfg(not(test))] #[no_mangle] fn call() -> u32 { CallCounter::instantiate().dispatch().to_u32() } - - mod events { - use super::*; - - mod private { - use super::*; - - #[doc(hidden)] - #[derive(scale::Encode, scale::Decode)] - pub enum Event { - DecCalled(DecCalled), - IncCalled(IncCalled), - } - - /// Used to seal the emit trait. - pub trait Sealed { } - } - - #[derive(scale::Encode, scale::Decode)] - /// Fires when the value is decremented. - pub struct DecCalled { - /// The current value. - pub current: u32, - } - - impl From for private::Event { - fn from(event: DecCalled) -> Self { - private::Event::DecCalled(event) - } - } - - #[derive(scale::Encode, scale::Decode)] - /// Fires when the value is incremented. - pub struct IncCalled { - /// The current value. - pub current: u32, - } - - impl From for private::Event { - fn from(event: IncCalled) -> Self { - private::Event::IncCalled(event) - } - } - - pub trait EmitEventExt: private::Sealed { - /// Emits the given event. - fn emit(&self, event: E) - where - E: Into, - { - use scale::Encode as _; - as ink_core::env::Env>::deposit_raw_event( - &[], event.into().encode().as_slice() - ) - } - } - - impl EmitEventExt for ink_model::EnvHandler> { } - impl private::Sealed for ink_model::EnvHandler> { } - } - - use events::{ - EmitEventExt as _, - DecCalled, - IncCalled, - }; - } - - #[cfg(not(feature = "ink-as-dependency"))] - use normal::*; - - #[cfg(not(feature = "ink-as-dependency"))] - use ink_core::env::FromAccountId as _; - - #[cfg(test)] - mod test { - use super::*; - - pub struct TestableCallCounter { - env: ink_model::ExecutionEnv>, - } - - impl CallCounter { - /// Returns a testable version of the contract. - pub fn deploy_mock() -> TestableCallCounter { - let mut mock = TestableCallCounter::allocate(); - mock.deploy(); - mock - } - } - - impl TestableCallCounter { - /// Allocates the testable contract storage. - fn allocate() -> Self { - use ink_core::storage::{ - Key, - alloc::{ - AllocateUsing as _, - Initialize as _, - BumpAlloc, - }, - }; - Self { - env: unsafe { - let mut alloc = BumpAlloc::from_raw_parts(Key([0x0; 32])); - ink_model::ExecutionEnv::allocate_using(&mut alloc).initialize_into(()) - } - } - } - - /// Deploys the testable contract by initializing it with the given values. - fn deploy(&mut self,) { - let (handler, state) = self.env.split_mut(); - state.deploy(handler,) - } - } - - impl TestableCallCounter { - pub fn inc(&mut self) { - let (handler, state) = self.env.split_mut(); - state.inc(handler,) - } - - pub fn dec(&mut self) { - let (handler, state) = self.env.split_mut(); - state.dec(handler,) - } - } - } - - #[cfg(not(feature = "ink-as-dependency"))] - #[cfg(feature = "ink-generate-abi")] - pub fn ink_generate_abi() -> ink_abi::InkProject { - let contract = { - ink_abi::ContractSpec::new("CallCounter") - .constructors(vec![ - ink_abi::ConstructorSpec::new("on_deploy") - .selector([0u8; 4]) - .args(vec![]) - .docs(vec![]) - .done() - ]) - .messages(vec![ - ink_abi::MessageSpec::new("inc") - .selector([15, 89, 208, 231]) - .mutates(true) - .args(vec![]) - .docs(vec![ - "Increments the internal counter.", - "", - "# Note", - "", - "Also emits an event.", - ]) - .returns( - ink_abi::ReturnTypeSpec::new(None) - ) - .done(), - ink_abi::MessageSpec::new("dec") - .selector([105, 169, 85, 123]) - .mutates(true) - .args(vec![]) - .docs(vec![ - "Decrements the internal counter.", - "", - "# Note", - "", - "Also emits an event.", - ]) - .returns(ink_abi::ReturnTypeSpec::new(None)) - .done(), - ]) - .events(vec![ - ink_abi::EventSpec::new(stringify!(DecCalled)) - .args(vec![ - ink_abi::EventParamSpec::new(stringify!(current)) - .of_type( - ink_abi::TypeSpec::with_name_segs::( - vec!["u32"].into_iter().map(AsRef::as_ref) - ) - ) - .indexed(false) - .done(), - ]) - .docs(vec![ - "Fires when the value is decremented.", - ]) - .done(), - ink_abi::EventSpec::new(stringify!(IncCalled)) - .args(vec![ - ink_abi::EventParamSpec::new(stringify!(current)) - .of_type( - ink_abi::TypeSpec::with_name_segs::( - vec!["u32"].into_iter().map(AsRef::as_ref) - ) - ) - .indexed(false) - .done(), - ]) - .docs(vec![ - "Fires when the value is incremented.", - ]) - .done(), - ]) - .docs(vec![]) - .done() - }; - let layout = { - unsafe { - use ink_core::storage::alloc::AllocateUsing as _; - use ink_abi::HasLayout as _; - CallCounter::allocate_using( - &mut ink_core::storage::alloc::BumpAlloc::from_raw_parts( - ink_core::storage::Key([0x0; 32]))) - .layout() - } - }; - ink_abi::InkProject::new(layout, contract) - } - - #[cfg(feature = "ink-as-dependency")] - mod as_dependency { - use super::*; - - /// Tests emitting of custom defined events. - #[derive(Clone, scale::Encode, scale::Decode)] - #[cfg_attr(feature = "ink-generate-abi", derive(type_metadata::Metadata))] - pub struct CallCounter { - account_id: AccountId, - } - - impl ink_core::storage::Flush for CallCounter {} - - /// Allows to enhance calls to `&self` contract messages. - pub struct CallEnhancer<'a> { - contract: &'a CallCounter, - } - - /// Allows to enhance calls to `&mut self` contract messages. - pub struct CallEnhancerMut<'a> { - contract: &'a mut CallCounter, - } - - impl ink_core::env::FromAccountId for CallCounter { - fn from_account_id(account_id: AccountId) -> Self { - Self { account_id } - } - } - - impl CallCounter { - pub fn new(code_hash: Hash,) -> ink_core::env::CreateBuilder { - ink_core::env::CreateBuilder::::new(code_hash) - } - /// Returns the internal account ID of the contract. - pub fn account_id(&self) -> AccountId { - self.account_id - } - /// Allows to enhance calls to `&self` contract messages. - pub fn call(&self) -> CallEnhancer { - CallEnhancer { contract : self } - } - /// Allows to enhance calls to `&mut self` contract messages. - pub fn call_mut(&mut self) -> CallEnhancerMut { - CallEnhancerMut { contract : self } - } - } - - impl CallCounter { - /// Increments the internal counter. - /// - /// # Note - /// - /// Also emits an event. - pub fn inc(&mut self,) { - self - .call_mut() - .inc() - .fire() - .expect( - concat!( - "invocation of ", - stringify!(CallCounter), - "::", - stringify!(inc), - " message was invalid" - ) - ) - } - - /// Decrements the internal counter. - /// - /// # Note - /// - /// Also emits an event. - pub fn dec(&mut self,) { - self - .call_mut() - .dec() - .fire() - .expect( - concat!( - "invocation of ", - stringify!(CallCounter), - "::", - stringify!(dec), - " message was invalid" - ) - ) - } - } - - impl<'a> CallEnhancer<'a> {} - - impl<'a> CallEnhancerMut<'a> { - /// Increments the internal counter. - /// - /// # Note - /// - /// Also emits an event. - pub fn inc(self,) -> ink_core::env::CallBuilder { - ink_core::env::CallBuilder::::invoke( - self.contract.account_id.clone(), [15, 89, 208, 231]) - } - - /// Decrements the internal counter. - /// - /// # Note - /// - /// Also emits an event. - pub fn dec(self,) -> ink_core::env::CallBuilder { - ink_core::env::CallBuilder::::invoke( - self.contract.account_id.clone(), [105, 169, 85, 123]) - } - } - } - - #[cfg(feature = "ink-as-dependency")] - pub use as_dependency::{CallCounter, CallEnhancer, CallEnhancerMut,}; - }, - ) -} diff --git a/lang/src/tests/flipper.rs b/lang/src/tests/flipper.rs deleted file mode 100644 index d4f2e89a791..00000000000 --- a/lang/src/tests/flipper.rs +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2018-2019 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::*; - -#[test] -fn contract_compiles() { - assert_eq_tokenstreams( - quote! { - #![env = DefaultSrmlTypes] - - /// A simple contract that has a boolean value that can be flipped and be returned. - struct Flipper { - /// The internal value. - value: storage::Value, - } - - impl Deploy for Flipper { - /// The internal boolean is initialized with `true`. - fn deploy(&mut self) { - self.value.set(true) - } - } - - impl Flipper { - /// Flips the internal boolean. - pub(external) fn flip(&mut self) { - self.value = !(*self.value) - } - - /// Returns the internal boolean. - pub(external) fn get(&self) -> bool { - *self.value - } - } - }, - quote! { - mod types { - use super::*; - use ink_core::env::{ContractEnv, EnvTypes}; - - pub type AccountId = ::AccountId; - pub type Balance = ::Balance; - pub type Hash = ::Hash; - pub type Moment = ::Moment; - pub type BlockNumber = ::BlockNumber; - } - - type Env = ink_core::env::ContractEnv; - use types::{ - AccountId, - Balance, - Hash, - Moment, - BlockNumber, - }; - - #[cfg(not(feature = "ink-as-dependency"))] - mod normal { - use super::*; - - ink_model::state! { - /// A simple contract that has a boolean value that can be flipped and be returned. - #[cfg_attr( - feature = "ink-generate-abi", - derive(type_metadata::Metadata, ink_abi::HasLayout,) - )] - pub struct Flipper { - /// The internal value. - value: storage::Value, - } - } - - mod msg { - use super::*; - use ink_model::messages; - - ink_model::messages! { - /// Flips the internal boolean. - [57, 219, 151, 140] => Flip(); - /// Returns the internal boolean. - [254, 74, 68, 37] => Get() -> bool; - } - } - - impl Flipper { - /// The internal boolean is initialized with `true`. - pub fn deploy(&mut self, env: &mut ink_model::EnvHandler >) { - self.value.set(true) - } - - /// Flips the internal boolean. - pub fn flip(&mut self, env: &mut ink_model::EnvHandler >) { - self.value = !(*self.value) - } - - /// Returns the internal boolean. - pub fn get(&self, env: &ink_model::EnvHandler >) -> bool { - *self.value - } - } - - use ink_model::Contract as _; - - #[cfg(not(test))] - impl Flipper { - pub(crate) fn instantiate() -> impl ink_model::Contract { - ink_model::ContractDecl::using::>() - .on_deploy(|env, ()| { - let (handler, state) = env.split_mut(); - state.deploy(handler,) - }) - .on_msg_mut::(|env, _| { - let (handler, state) = env.split_mut(); - state.flip(handler,) - }) - .on_msg::(|env, _| { - let (handler, state) = env.split(); - state.get(handler,) - }) - .instantiate() - } - } - - #[cfg(not(test))] #[no_mangle] fn deploy() -> u32 { Flipper::instantiate().deploy().to_u32() } - #[cfg(not(test))] #[no_mangle] fn call() -> u32 { Flipper::instantiate().dispatch().to_u32() } - } - - #[cfg(not(feature = "ink-as-dependency"))] - use normal::*; - - #[cfg(not(feature = "ink-as-dependency"))] - use ink_core::env::FromAccountId as _; - - #[cfg(test)] - mod test { - use super::*; - - pub struct TestableFlipper { - env: ink_model::ExecutionEnv>, - } - - impl Flipper { - /// Returns a testable version of the contract. - pub fn deploy_mock() -> TestableFlipper { - let mut mock = TestableFlipper::allocate(); - mock.deploy(); - mock - } - } - - impl TestableFlipper { - /// Allocates the testable contract storage. - fn allocate() -> Self { - use ink_core::storage::{ - Key, - alloc::{ - AllocateUsing as _, - Initialize as _, - BumpAlloc, - }, - }; - Self { - env: unsafe { - let mut alloc = BumpAlloc::from_raw_parts(Key([0x0; 32])); - ink_model::ExecutionEnv::allocate_using(&mut alloc).initialize_into(()) - } - } - } - - /// Deploys the testable contract by initializing it with the given values. - fn deploy(&mut self,) { - let (handler, state) = self.env.split_mut(); - state.deploy(handler,) - } - } - - impl TestableFlipper { - pub fn flip(&mut self) { - let (handler, state) = self.env.split_mut(); - state.flip(handler,) - } - - pub fn get(&self) -> bool { - let (handler, state) = self.env.split(); - state.get(handler,) - } - } - } - - #[cfg(not(feature = "ink-as-dependency"))] - #[cfg(feature = "ink-generate-abi")] - pub fn ink_generate_abi() -> ink_abi::InkProject{ - let contract = { - ink_abi::ContractSpec::new("Flipper") - .constructors(vec![ - ink_abi::ConstructorSpec::new("on_deploy") - .selector([0u8; 4]) - .args(vec![]) - .docs(vec![ - "The internal boolean is initialized with `true`.", - ]) - .done() - ]) - .messages(vec![ - ink_abi::MessageSpec::new("flip") - .selector([57, 219, 151, 140]) - .mutates(true) - .args(vec![]) - .docs(vec!["Flips the internal boolean.",]) - .returns(ink_abi::ReturnTypeSpec::new(None)) - .done(), - ink_abi::MessageSpec::new("get") - .selector([254, 74, 68, 37]) - .mutates(false) - .args(vec![]) - .docs(vec!["Returns the internal boolean.",]) - .returns( - ink_abi::ReturnTypeSpec::new( - ink_abi::TypeSpec::with_name_segs::( - vec!["bool"].into_iter().map(AsRef::as_ref) - ) - ) - ) - .done(), - ]) - .events(vec![]) - .docs(vec![]) - .done() - }; - let layout = { - unsafe { - use ink_core::storage::alloc::AllocateUsing as _; - use ink_abi::HasLayout as _; - Flipper::allocate_using( - &mut ink_core::storage::alloc::BumpAlloc::from_raw_parts( - ink_core::storage::Key([0x0; 32]) - ) - ).layout() - } - }; - ink_abi::InkProject::new(layout, contract) - } - - #[cfg(feature = "ink-as-dependency")] - mod as_dependency { - use super::*; - - /// A simple contract that has a boolean value that can be flipped and be returned. - #[derive(Clone, scale::Encode, scale::Decode)] - #[cfg_attr(feature = "ink-generate-abi", derive(type_metadata::Metadata))] - pub struct Flipper { - account_id: AccountId, - } - - impl ink_core::storage::Flush for Flipper {} - - /// Allows to enhance calls to `&self` contract messages. - pub struct CallEnhancer<'a> { - contract: &'a Flipper, - } - - /// Allows to enhance calls to `&mut self` contract messages. - pub struct CallEnhancerMut<'a> { - contract: &'a mut Flipper, - } - - impl ink_core::env::FromAccountId for Flipper { - fn from_account_id(account_id: AccountId) -> Self { - Self { account_id } - } - } - - impl Flipper { - /// The internal boolean is initialized with `true`. - pub fn new(code_hash: Hash,) -> ink_core::env::CreateBuilder { - ink_core::env::CreateBuilder::::new(code_hash) - } - /// Returns the internal account ID of the contract. - pub fn account_id(&self) -> AccountId { - self.account_id - } - /// Allows to enhance calls to `&self` contract messages. - pub fn call(&self) -> CallEnhancer { - CallEnhancer { contract : self } - } - /// Allows to enhance calls to `&mut self` contract messages. - pub fn call_mut(&mut self) -> CallEnhancerMut { - CallEnhancerMut { contract : self } - } - } - - impl Flipper { - /// Flips the internal boolean. - pub fn flip(&mut self,) { - self - .call_mut() - .flip() - .fire() - .expect( - concat!( - "invocation of ", - stringify!(Flipper), - "::", - stringify!(flip), - " message was invalid" - ) - ) - } - - /// Returns the internal boolean. - pub fn get(&self,) -> bool { - self - .call() - .get() - .fire() - .expect( - concat!( - "evaluation of ", - stringify!(Flipper), - "::", - stringify!(get), - " message was invalid" - ) - ) - } - } - - impl<'a> CallEnhancer<'a> { - /// Returns the internal boolean. - pub fn get(self,) -> ink_core::env::CallBuilder> { - ink_core::env::CallBuilder::eval( - self.contract.account_id.clone(), [254, 74, 68, 37] - ) - } - } - - impl<'a> CallEnhancerMut<'a> { - /// Flips the internal boolean. - pub fn flip(self,) -> ink_core::env::CallBuilder { - ink_core::env::CallBuilder::::invoke( - self.contract.account_id.clone(), [57, 219, 151, 140]) - } - } - } - - #[cfg(feature = "ink-as-dependency")] - pub use as_dependency::{Flipper, CallEnhancer, CallEnhancerMut,}; - }, - ) -} diff --git a/lang/src/tests/incrementer.rs b/lang/src/tests/incrementer.rs deleted file mode 100644 index a65b6d4702c..00000000000 --- a/lang/src/tests/incrementer.rs +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright 2018-2019 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::*; - -#[test] -fn contract_compiles() { - assert_eq_tokenstreams( - quote! { - #![env = DefaultSrmlTypes] - - /// A simple contract that has a value that can be - /// incremented, returned and compared. - struct Incrementer { - /// The internal value. - value: storage::Value, - } - - impl Deploy for Incrementer { - /// Automatically called when the contract is deployed. - fn deploy(&mut self, init_value: u32) { - self.value.set(init_value) - } - } - - impl Incrementer { - /// Increments the internal counter. - pub(external) fn inc(&mut self, by: u32) { - self.value += by - } - - /// Returns the internal counter. - pub(external) fn get(&self) -> u32 { - *self.value - } - - /// Returns `true` if `x` is greater than the internal value. - pub(external) fn compare(&self, x: u32) -> bool { - x > *self.value - } - } - }, - quote! { - mod types { - use super::*; - use ink_core::env::{ContractEnv, EnvTypes}; - - pub type AccountId = ::AccountId; - pub type Balance = ::Balance; - pub type Hash = ::Hash; - pub type Moment = ::Moment; - pub type BlockNumber = ::BlockNumber; - } - - type Env = ink_core::env::ContractEnv; - use types::{ - AccountId, - Balance, - Hash, - Moment, - BlockNumber, - }; - - #[cfg(not(feature = "ink-as-dependency"))] - mod normal { - use super::*; - - ink_model::state! { - /// A simple contract that has a value that can be - /// incremented, returned and compared. - #[cfg_attr( - feature = "ink-generate-abi", - derive(type_metadata::Metadata, ink_abi::HasLayout,) - )] - pub struct Incrementer { - /// The internal value. - value: storage::Value, - } - } - - mod msg { - use super::*; - use ink_model::messages; - - ink_model::messages! { - /// Increments the internal counter. - [15, 89, 208, 231] => Inc(by: u32); - /// Returns the internal counter. - [254, 74, 68, 37] => Get() -> u32; - /// Returns `true` if `x` is greater than the internal value. - [21, 176, 197, 12] => Compare(x: u32) -> bool; - } - } - - impl Incrementer { - /// Automatically called when the contract is deployed. - pub fn deploy(&mut self, env: &mut ink_model::EnvHandler >, init_value: u32) { - self.value.set(init_value) - } - - /// Increments the internal counter. - pub fn inc(&mut self, env: &mut ink_model::EnvHandler >, by: u32) { - self.value += by - } - - /// Returns the internal counter. - pub fn get(&self, env: &ink_model::EnvHandler >) -> u32 { - *self.value - } - - /// Returns `true` if `x` is greater than the internal value. - pub fn compare(&self, env: &ink_model::EnvHandler >, x: u32) -> bool { - x > *self.value - } - } - - use ink_model::Contract as _; - - #[cfg(not(test))] - impl Incrementer { - pub(crate) fn instantiate() -> impl ink_model::Contract { - ink_model::ContractDecl::using::>() - .on_deploy(|env, init_value: u32| { - let (handler, state) = env.split_mut(); - state.deploy(handler, init_value) - }) - .on_msg_mut::(|env, by: u32| { - let (handler, state) = env.split_mut(); - state.inc(handler, by) - }) - .on_msg::(|env, _| { - let (handler, state) = env.split(); - state.get(handler,) - }) - .on_msg::(|env, x: u32| { - let (handler, state) = env.split(); - state.compare(handler, x) - }) - .instantiate() - } - } - - #[cfg(not(test))] #[no_mangle] fn deploy() -> u32 { Incrementer::instantiate().deploy().to_u32() } - #[cfg(not(test))] #[no_mangle] fn call() -> u32 { Incrementer::instantiate().dispatch().to_u32() } - } - - #[cfg(not(feature = "ink-as-dependency"))] - use normal::*; - - #[cfg(not(feature = "ink-as-dependency"))] - use ink_core::env::FromAccountId as _; - - #[cfg(test)] - mod test { - use super::*; - - pub struct TestableIncrementer { - env: ink_model::ExecutionEnv>, - } - - impl Incrementer { - /// Returns a testable version of the contract. - pub fn deploy_mock(init_value: u32) -> TestableIncrementer { - let mut mock = TestableIncrementer::allocate(); - mock.deploy(init_value); - mock - } - } - - impl TestableIncrementer { - /// Allocates the testable contract storage. - fn allocate() -> Self { - use ink_core::storage::{ - Key, - alloc::{ - AllocateUsing as _, - Initialize as _, - BumpAlloc, - }, - }; - Self { - env: unsafe { - let mut alloc = BumpAlloc::from_raw_parts(Key([0x0; 32])); - ink_model::ExecutionEnv::allocate_using(&mut alloc).initialize_into(()) - } - } - } - - /// Deploys the testable contract by initializing it with the given values. - fn deploy(&mut self , init_value: u32) { - let (handler, state) = self.env.split_mut(); - state.deploy(handler, init_value) - } - } - - impl TestableIncrementer { - pub fn inc(& mut self, by: u32) { - let (handler, state) = self.env.split_mut(); - state.inc(handler, by) - } - - pub fn get(&self) -> u32 { - let (handler, state) = self.env.split(); - state.get(handler,) - } - - pub fn compare(&self, x: u32) -> bool { - let (handler, state) = self.env.split(); - state.compare(handler, x) - } - } - } - - #[cfg(not(feature = "ink-as-dependency"))] - #[cfg(feature = "ink-generate-abi")] - pub fn ink_generate_abi() -> ink_abi::InkProject { - let contract = { - ink_abi::ContractSpec::new("Incrementer") - .constructors(vec![ - ink_abi::ConstructorSpec::new("on_deploy") - .selector([0u8; 4]) - .args(vec![ - ink_abi::MessageParamSpec::new("init_value") - .of_type( - ink_abi::TypeSpec::with_name_segs::( - vec!["u32"].into_iter().map(AsRef::as_ref) - ) - ) - .done(), - ]) - .docs(vec![ - "Automatically called when the contract is deployed.", - ]) - .done() - ]) - .messages(vec![ - ink_abi::MessageSpec::new("inc") - .selector([15, 89, 208, 231]) - .mutates(true) - .args(vec![ - ink_abi::MessageParamSpec::new("by") - .of_type( - ink_abi::TypeSpec::with_name_segs::( - vec!["u32"].into_iter().map(AsRef::as_ref) - ) - ) - .done(), - ]) - .docs(vec![ - "Increments the internal counter.", - ]) - .returns( - ink_abi::ReturnTypeSpec::new(None) - ) - .done(), - ink_abi::MessageSpec::new("get") - .selector([254, 74, 68, 37]) - .mutates(false) - .args(vec![]) - .docs(vec![ - "Returns the internal counter.", - ]) - .returns( - ink_abi::ReturnTypeSpec::new( - ink_abi::TypeSpec::with_name_segs::( - vec!["u32"].into_iter().map(AsRef::as_ref) - ) - ) - ) - .done(), - ink_abi::MessageSpec::new("compare") - .selector([21, 176, 197, 12]) - .mutates(false) - .args(vec![ - ink_abi::MessageParamSpec::new("x") - .of_type( - ink_abi::TypeSpec::with_name_segs::( - vec!["u32"].into_iter().map(AsRef::as_ref) - ) - ) - .done(), - ]) - .docs(vec![ - "Returns `true` if `x` is greater than the internal value.", - ]) - .returns( - ink_abi::ReturnTypeSpec::new( - ink_abi::TypeSpec::with_name_segs::( - vec!["bool"].into_iter().map(AsRef::as_ref) - ) - ) - ) - .done(), - ]) - .events(vec![]) - .docs(vec![]) - .done() - }; - let layout = { - unsafe { - use ink_core::storage::alloc::AllocateUsing as _; - use ink_abi::HasLayout as _; - Incrementer::allocate_using( - &mut ink_core::storage::alloc::BumpAlloc::from_raw_parts( - ink_core::storage::Key([0x0; 32]) - ) - ).layout() - } - }; - ink_abi::InkProject::new(layout, contract) - } - - #[cfg(feature = "ink-as-dependency")] - mod as_dependency { - use super::*; - - /// A simple contract that has a value that can be - /// incremented, returned and compared. - #[derive(Clone, scale::Encode, scale::Decode)] - #[cfg_attr(feature = "ink-generate-abi", derive(type_metadata::Metadata))] - pub struct Incrementer { - account_id: AccountId, - } - - impl ink_core::storage::Flush for Incrementer {} - - /// Allows to enhance calls to `&self` contract messages. - pub struct CallEnhancer<'a> { - contract: &'a Incrementer, - } - - /// Allows to enhance calls to `&mut self` contract messages. - pub struct CallEnhancerMut<'a> { - contract: &'a mut Incrementer, - } - - impl ink_core::env::FromAccountId for Incrementer { - fn from_account_id(account_id: AccountId) -> Self { - Self { account_id } - } - } - - impl Incrementer { - /// Automatically called when the contract is deployed. - pub fn new(code_hash: Hash, init_value: u32,) -> ink_core::env::CreateBuilder { - ink_core::env::CreateBuilder::::new(code_hash) - .push_arg(&init_value) - } - /// Returns the internal account ID of the contract. - pub fn account_id(&self) -> AccountId { - self.account_id - } - /// Allows to enhance calls to `&self` contract messages. - pub fn call(&self) -> CallEnhancer { - CallEnhancer { contract : self } - } - /// Allows to enhance calls to `&mut self` contract messages. - pub fn call_mut(&mut self) -> CallEnhancerMut { - CallEnhancerMut { contract : self } - } - } - - impl Incrementer { - /// Increments the internal counter. - pub fn inc(&mut self, by: u32,) { - self - .call_mut() - .inc(by,) - .fire() - .expect( - concat!( - "invocation of ", - stringify!(Incrementer), - "::", - stringify!(inc), - " message was invalid" - ) - ) - } - - /// Returns the internal counter. - pub fn get(&self,) -> u32 { - self - .call() - .get() - .fire() - .expect( - concat!( - "evaluation of ", - stringify!(Incrementer), - "::", - stringify!(get), - " message was invalid" - ) - ) - } - - /// Returns `true` if `x` is greater than the internal value. - pub fn compare(&self, x: u32,) -> bool { - self - .call() - .compare(x,) - .fire() - .expect( - concat!( - "evaluation of ", - stringify!(Incrementer), - "::", - stringify!(compare), - " message was invalid" - ) - ) - } - } - - impl<'a> CallEnhancer<'a> { - /// Returns the internal counter. - pub fn get(self,) -> ink_core::env::CallBuilder> { - ink_core::env::CallBuilder::eval( - self.contract.account_id.clone(), [254, 74, 68, 37] - ) - } - - /// Returns `true` if `x` is greater than the internal value. - pub fn compare(self, x: u32,) -> ink_core::env::CallBuilder> { - ink_core::env::CallBuilder::eval( - self - .contract - .account_id - .clone(), - [21, 176, 197, 12] - ).push_arg(&x) - } - } - - impl<'a> CallEnhancerMut<'a> { - /// Increments the internal counter. - pub fn inc(self, by: u32,) -> ink_core::env::CallBuilder { - ink_core::env::CallBuilder::::invoke( - self.contract.account_id.clone(), [15, 89, 208, 231] - ).push_arg(&by) - } - } - } - - #[cfg(feature = "ink-as-dependency")] - pub use as_dependency::{Incrementer, CallEnhancer, CallEnhancerMut,}; - }, - ) -} diff --git a/lang/src/tests/mod.rs b/lang/src/tests/mod.rs deleted file mode 100644 index 75fc1971e56..00000000000 --- a/lang/src/tests/mod.rs +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright 2018-2019 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 events; -mod flipper; -mod incrementer; -mod noop; -mod utils; - -pub(crate) use crate::generate_or_err; -pub(crate) use quote::quote; -pub(crate) use utils::{ - assert_eq_tokenstreams, - assert_failure, -}; - -#[test] -fn empty_contract_input() { - assert!(generate_or_err(quote! {}).is_err()); -} - -#[test] -fn missing_env_types_meta() { - assert_failure( - quote! { - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&mut self) {} - } - impl TestContract {} - }, - "couldn\'t find an `#![env = ]` attribute", - ) -} - -#[test] -fn multiple_env_types_meta() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&mut self) {} - } - impl TestContract {} - }, - "requires exactly one `#![env = ]` attribute; found 2", - ) -} - -#[test] -fn env_types_meta_wrong_attr_name() { - assert_failure( - quote! { - #![not_env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&mut self) {} - } - impl TestContract {} - }, - "unknown env attribute \'not_env\'", - ) -} - -#[test] -fn using_self_val_in_message() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&mut self) {} - } - impl TestContract { - pub(external) fn with_self_value(self) {} - } - }, - "contract messages must operate on `&self` or `&mut self`", - ) -} - -#[test] -fn using_non_self_in_message() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&mut self) {} - } - impl TestContract { - pub(external) fn with_self_value(not_self: u32) {} - } - }, - "contract messages must operate on `&self` or `&mut self`", - ) -} - -#[test] -fn using_empty_message_args() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&mut self) {} - } - impl TestContract { - pub(external) fn with_self_value() {} - } - }, - "contract messages must operate on `&self` or `&mut self`", - ) -} - -#[test] -fn using_self_val_in_deploy() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(self) {} - } - impl TestContract {} - }, - "the deploy implementation must operate on `&mut self`", - ) -} - -#[test] -fn using_self_ref_in_deploy() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&self) {} - } - impl TestContract {} - }, - "the deploy implementation must operate on `&mut self`", - ) -} - -#[test] -fn missing_state_in_contract() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - impl Deploy for TestContract { - fn deploy(self) {} - } - impl TestContract {} - }, - "couldn't find a contract state `struct`", - ) -} - -#[test] -fn missing_deploy_impl_block() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl TestContract {} - }, - "couldn't find a contract deploy implementation; requires exactly one", - ) -} - -#[test] -fn env_as_deploy_handler_arg() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&mut self, env: u32) {} - } - }, - "the deploy implementation must not contain an argument named `env`", - ) -} - -#[test] -fn generic_deploy_handler() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&mut self, generic_param: T) {} - } - }, - "expected parentheses", // The check for this is built into the parser. - ) -} - -#[test] -fn deploy_handler_with_return_type() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&mut self) -> u32 {} - } - }, - "the deploy implementation must not have a return type", - ) -} - -#[test] -fn env_as_message_arg() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&mut self) {} - } - impl TestContract { - pub(external) fn test_message(&self, env: u32) {} - } - }, - "contract messages must not contain an argument called `env`", - ) -} - -#[test] -fn message_called_deploy() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&mut self) {} - } - impl TestContract { - pub(external) fn deploy(&mut self) {} - } - }, - "contract messages must not be named `deploy`", - ) -} - -#[test] -fn method_called_deploy() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&mut self) {} - } - impl TestContract { - fn deploy(&mut self) {} - } - }, - "contract methods must not be named `deploy`", - ) -} - -#[test] -fn multiple_states() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract1 {} - struct TestContract2 {} - impl Deploy for TestContract1 { - fn deploy(&mut self) {} - } - impl Deploy for TestContract2 { - fn deploy(&mut self) {} - } - }, - "requires exactly one contract state `struct`; found 2", - ) -} - -#[test] -fn multiple_deploy_handlers() { - assert_failure( - quote! { - #![env = ink_core::env::DefaultSrmlTypes] - struct TestContract {} - impl Deploy for TestContract { - fn deploy(&mut self) {} - } - impl Deploy for TestContract { - fn deploy(&mut self) {} - } - }, - "found more than one contract deploy implementation for TestContract", - ) -} diff --git a/lang/src/tests/noop.rs b/lang/src/tests/noop.rs deleted file mode 100644 index 73fae58b3b9..00000000000 --- a/lang/src/tests/noop.rs +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2018-2019 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::*; - -#[test] -fn contract_compiles() { - assert_eq_tokenstreams( - quote! { - #![env = DefaultSrmlTypes] - - /// The contract that does nothing. - /// - /// # Note - /// - /// Can be deployed, cannot be called. - struct Noop {} - - impl Deploy for Noop { - /// Does nothing to initialize itself. - fn deploy(&mut self) {} - } - - /// Provides no way to call it as extrinsic. - impl Noop {} - }, - quote! { - mod types { - use super::*; - use ink_core::env::{ContractEnv, EnvTypes}; - - pub type AccountId = ::AccountId; - pub type Balance = ::Balance; - pub type Hash = ::Hash; - pub type Moment = ::Moment; - pub type BlockNumber = ::BlockNumber; - } - - type Env = ink_core::env::ContractEnv; - use types::{ - AccountId, - Balance, - Hash, - Moment, - BlockNumber, - }; - - #[cfg(not(feature = "ink-as-dependency"))] - mod normal { - use super::*; - - ink_model::state! { - /// The contract that does nothing. - /// - /// # Note - /// - /// Can be deployed, cannot be called. - #[cfg_attr( - feature = "ink-generate-abi", - derive(type_metadata::Metadata, ink_abi::HasLayout,) - )] - pub struct Noop {} - } - - mod msg { - use super::*; - use ink_model::messages; - - ink_model::messages! {} - } - - impl Noop { - /// Does nothing to initialize itself. - pub fn deploy(&mut self, env: &mut ink_model::EnvHandler >) { } - } - - use ink_model::Contract as _; - - #[cfg(not(test))] - impl Noop { - pub(crate) fn instantiate() -> impl ink_model::Contract { - ink_model::ContractDecl::using::>() - .on_deploy(|env, ()| { - let (handler, state) = env.split_mut(); - state.deploy(handler,) - }) - .instantiate() - } - } - - #[cfg(not(test))] #[no_mangle] fn deploy() -> u32 { Noop::instantiate().deploy().to_u32() } - #[cfg(not(test))] #[no_mangle] fn call() -> u32 { Noop::instantiate().dispatch().to_u32() } - } - - #[cfg(not(feature = "ink-as-dependency"))] - use normal::*; - - #[cfg(not(feature = "ink-as-dependency"))] - use ink_core::env::FromAccountId as _; - - #[cfg(test)] - mod test { - use super::*; - - pub struct TestableNoop { - env: ink_model::ExecutionEnv>, - } - - impl Noop { - /// Returns a testable version of the contract. - pub fn deploy_mock() -> TestableNoop { - let mut mock = TestableNoop::allocate(); - mock.deploy(); - mock - } - } - - impl TestableNoop { - /// Allocates the testable contract storage. - fn allocate() -> Self { - use ink_core::storage::{ - Key, - alloc::{ - AllocateUsing as _, - Initialize as _, - BumpAlloc, - }, - }; - Self { - env: unsafe { - let mut alloc = BumpAlloc::from_raw_parts(Key([0x0; 32])); - ink_model::ExecutionEnv::allocate_using(&mut alloc).initialize_into(()) - } - } - } - - /// Deploys the testable contract by initializing it with the given values. - fn deploy(&mut self,) { - let (handler, state) = self.env.split_mut(); - state.deploy(handler,) - } - } - - impl TestableNoop { } - } - - #[cfg(not(feature = "ink-as-dependency"))] - #[cfg(feature = "ink-generate-abi")] - pub fn ink_generate_abi() -> ink_abi::InkProject { - let contract = { - ink_abi::ContractSpec::new("Noop") - .constructors(vec![ - ink_abi::ConstructorSpec::new("on_deploy") - .selector([0u8; 4]) - .args(vec![]) - .docs(vec![ - "Does nothing to initialize itself.", - ]) - .done() - ]) - .messages(vec![]) - .events(vec![]) - .docs(vec![]) - .done() - }; - let layout = { - unsafe { - use ink_core::storage::alloc::AllocateUsing as _; - use ink_abi::HasLayout as _; - Noop::allocate_using( - &mut ink_core::storage::alloc::BumpAlloc::from_raw_parts( - ink_core::storage::Key( - [0x0; 32] - ) - )) - .layout() - } - }; - ink_abi::InkProject::new(layout, contract) - } - - #[cfg(feature = "ink-as-dependency")] - mod as_dependency { - use super::*; - - /// The contract that does nothing. - /// - /// # Note - /// - /// Can be deployed, cannot be called. - #[derive(Clone, scale::Encode, scale::Decode)] - #[cfg_attr(feature = "ink-generate-abi", derive(type_metadata::Metadata))] - pub struct Noop { - account_id: AccountId, - } - - impl ink_core::storage::Flush for Noop {} - - /// Allows to enhance calls to `&self` contract messages. - pub struct CallEnhancer<'a> { - contract: &'a Noop, - } - - /// Allows to enhance calls to `&mut self` contract messages. - pub struct CallEnhancerMut<'a> { - contract: &'a mut Noop, - } - - impl ink_core::env::FromAccountId for Noop { - fn from_account_id(account_id: AccountId) -> Self { - Self { account_id } - } - } - - impl Noop { - /// Does nothing to initialize itself. - pub fn new(code_hash: Hash,) -> ink_core::env::CreateBuilder { - ink_core::env::CreateBuilder::::new(code_hash) - } - /// Returns the internal account ID of the contract. - pub fn account_id(&self) -> AccountId { - self.account_id - } - /// Allows to enhance calls to `&self` contract messages. - pub fn call(&self) -> CallEnhancer { - CallEnhancer { contract : self } - } - /// Allows to enhance calls to `&mut self` contract messages. - pub fn call_mut(&mut self) -> CallEnhancerMut { - CallEnhancerMut { contract : self } - } - } - - impl Noop { } - - impl<'a> CallEnhancer<'a> { } - impl<'a> CallEnhancerMut<'a> { } - } - - #[cfg(feature = "ink-as-dependency")] - pub use as_dependency::{Noop, CallEnhancer, CallEnhancerMut,}; - }, - ) -} diff --git a/lang/src/tests/utils.rs b/lang/src/tests/utils.rs deleted file mode 100644 index f11a0784ff6..00000000000 --- a/lang/src/tests/utils.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2018-2019 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 pretty_assertions::assert_eq; - -use crate::generate_or_err; -use proc_macro2::TokenStream as TokenStream2; - -pub fn assert_eq_tokenstreams(input: TokenStream2, expected: TokenStream2) { - let result = generate_or_err(input) - .map(|result| result.to_string()) - .map_err(|err| err.to_string()); - let expected = Ok(expected.to_string()); - assert_eq!(result, expected,) -} - -pub fn assert_failure(input: TokenStream2, err_str: &'static str) { - assert_eq!( - generate_or_err(input) - .map(|result| result.to_string()) - .map_err(|err| err.to_string()), - Err(err_str.to_string()) - ) -} diff --git a/lang2/macro/src/codegen/contract.rs b/lang2/macro/src/codegen/contract.rs index ab2daf18cc2..4972e657397 100644 --- a/lang2/macro/src/codegen/contract.rs +++ b/lang2/macro/src/codegen/contract.rs @@ -71,8 +71,6 @@ impl GenerateCode for ContractModule<'_> { quote! { mod #ident { - use super::*; - #env_types // Private struct and other type definitions. @@ -89,11 +87,11 @@ impl GenerateCode for ContractModule<'_> { } #[cfg(all(test, feature = "test-env"))] - pub type #storage_ident = self::__ink_private::TestableStorageAndEnv; + pub type #storage_ident = self::__ink_private::TestableStorage; #[cfg(not(all(test, feature = "test-env")))] #conflic_depedency_cfg - pub type #storage_ident = self::__ink_private::StorageAndEnv; + pub type #storage_ident = self::__ink_private::Storage; #[cfg(feature = "ink-as-dependency")] pub type #storage_ident = self::__ink_private::StorageAsDependency; diff --git a/lang2/macro/src/codegen/cross_calling.rs b/lang2/macro/src/codegen/cross_calling.rs index 8de969884dd..50d26beb79c 100644 --- a/lang2/macro/src/codegen/cross_calling.rs +++ b/lang2/macro/src/codegen/cross_calling.rs @@ -179,14 +179,14 @@ impl CrossCalling<'_> { } } - impl ink_core::env2::call::FromAccountId for StorageAsDependency { + impl ink_core::env3::call::FromAccountId for StorageAsDependency { #[inline] fn from_account_id(account_id: AccountId) -> Self { Self { account_id } } } - impl ink_lang2::ToAccountId for StorageAsDependency { + impl ink_lang2::ToAccountId for StorageAsDependency { #[inline] fn to_account_id(&self) -> AccountId { self.account_id @@ -208,21 +208,23 @@ impl CrossCalling<'_> { let attrs = utils::filter_non_ink_attributes(&function.attrs); let fn_args = function.sig.inputs(); let arg_idents = function.sig.inputs().map(|fn_arg| &fn_arg.ident); - let selector = function.selector().expect("constructors always have selectors"); + let selector = function + .selector() + .expect("constructors always have selectors"); let selector_bytes = selector.as_bytes(); quote_spanned!(span=> #( #attrs )* pub fn #ident( #( #fn_args ),* - ) -> ink_core::env2::call::CreateBuilder< - Env, + ) -> ink_core::env3::call::CreateBuilder< + EnvTypes, Self, - ink_core::env2::call::state::Sealed, - ink_core::env2::call::state::CodeHashUnassigned, + ink_core::env3::call::state::Sealed, + ink_core::env3::call::state::CodeHashUnassigned, > { - ink_core::env2::call::CreateParams::::build( - ink_core::env2::call::Selector::from_bytes([#( #selector_bytes ),*]) + ink_core::env3::call::CreateParams::::build( + ink_core::env3::call::Selector::new([#( #selector_bytes ),*]) ) #( .push_arg(&#arg_idents) @@ -329,7 +331,7 @@ impl CrossCalling<'_> { syn::ReturnType::Type(_, ty) => Some((&**ty).clone()), }; let ret_ty_sig = if ret_ty.is_some() { - quote! { ink_core::env2::call::ReturnType<#ret_ty> } + quote! { ink_core::env3::call::ReturnType<#ret_ty> } } else { quote! { () } }; @@ -349,12 +351,12 @@ impl CrossCalling<'_> { pub fn #ident( self, #( #fn_args ),* - ) -> ink_core::env2::call::CallBuilder< - Env, #ret_ty_sig, ink_core::env2::call::state::Sealed + ) -> ink_core::env3::call::CallBuilder< + EnvTypes, #ret_ty_sig, ink_core::env3::call::state::Sealed > { - ink_core::env2::call::CallParams::::#instantiate_fn( + ink_core::env3::call::CallParams::::#instantiate_fn( ink_lang2::ToAccountId::to_account_id(self.contract), - ink_core::env2::call::Selector::from_bytes([ #( #selector_bytes ),* ]), + ink_core::env3::call::Selector::new([ #( #selector_bytes ),* ]), ) #( .push_arg(&#arg_idents) diff --git a/lang2/macro/src/codegen/dispatch.rs b/lang2/macro/src/codegen/dispatch.rs index c8eb16fb2ee..47a548bb660 100644 --- a/lang2/macro/src/codegen/dispatch.rs +++ b/lang2/macro/src/codegen/dispatch.rs @@ -113,7 +113,7 @@ impl Dispatch<'_> { ); let fn_selector = quote_spanned!(span => impl ink_lang2::FnSelector for #namespace<[(); #selector_id]> { - const SELECTOR: ink_core::env2::call::Selector = ink_core::env2::call::Selector::from_bytes([ + const SELECTOR: ink_core::env3::call::Selector = ink_core::env3::call::Selector::new([ #( #selector_bytes ),* ]); } @@ -223,17 +223,17 @@ impl Dispatch<'_> { .map(|fun| self.generate_dispatch_using_mode_fragment(fun)); quote! { - impl ink_lang2::DispatchUsingMode for StorageAndEnv { + impl ink_lang2::DispatchUsingMode for Storage { #[allow(unused_parens)] fn dispatch_using_mode( mode: ink_lang2::DispatchMode ) -> core::result::Result<(), ink_lang2::DispatchError> { - ink_lang2::Contract::with_storage::() + ink_lang2::Contract::with_storage::() #( #fragments )* .done() - .dispatch_using_mode(mode) + .dispatch_using_mode::(mode) } } } @@ -245,7 +245,7 @@ impl Dispatch<'_> { #[no_mangle] fn deploy() -> u32 { ink_lang2::DispatchRetCode::from( - ::dispatch_using_mode( + ::dispatch_using_mode( ink_lang2::DispatchMode::Instantiate, ), ) @@ -256,7 +256,7 @@ impl Dispatch<'_> { #[no_mangle] fn call() -> u32 { ink_lang2::DispatchRetCode::from( - ::dispatch_using_mode( + ::dispatch_using_mode( ink_lang2::DispatchMode::Call, ), ) diff --git a/lang2/macro/src/codegen/env_types.rs b/lang2/macro/src/codegen/env_types.rs index db8d3969134..1f459341bb2 100644 --- a/lang2/macro/src/codegen/env_types.rs +++ b/lang2/macro/src/codegen/env_types.rs @@ -33,13 +33,13 @@ impl GenerateCode for EnvTypes<'_> { let env_types = &self.contract.meta_info.env_types.ty; quote! { - type Env = ink_core::env2::EnvImpl<#env_types>; + type EnvTypes = #env_types; - type AccountId = <#env_types as ink_core::env2::EnvTypes>::AccountId; - type Balance = <#env_types as ink_core::env2::EnvTypes>::Balance; - type Hash = <#env_types as ink_core::env2::EnvTypes>::Hash; - type Moment = <#env_types as ink_core::env2::EnvTypes>::Moment; - type BlockNumber = <#env_types as ink_core::env2::EnvTypes>::BlockNumber; + type AccountId = <#env_types as ink_core::env3::EnvTypes>::AccountId; + type Balance = <#env_types as ink_core::env3::EnvTypes>::Balance; + type Hash = <#env_types as ink_core::env3::EnvTypes>::Hash; + type TimeStamp = <#env_types as ink_core::env3::EnvTypes>::TimeStamp; + type BlockNumber = <#env_types as ink_core::env3::EnvTypes>::BlockNumber; } } } @@ -55,11 +55,11 @@ impl GenerateCode for EnvTypesImports<'_> { fn generate_code(&self) -> TokenStream2 { quote! { use super::{ - Env, + EnvTypes, AccountId, Balance, Hash, - Moment, + TimeStamp, BlockNumber, }; } diff --git a/lang2/macro/src/codegen/events.rs b/lang2/macro/src/codegen/events.rs index 7a2554f5c3a..2f896902fa4 100644 --- a/lang2/macro/src/codegen/events.rs +++ b/lang2/macro/src/codegen/events.rs @@ -95,12 +95,12 @@ impl EventHelpers<'_> { E: Into; } - impl<'a> EmitEvent for &'a mut ink_core::env2::EnvAccessMut { + impl<'a> EmitEvent for ink_lang2::EnvAccess<'a, EnvTypes> { fn emit_event(self, event: E) where E: Into, { - ink_core::env2::EmitEvent::emit_event(self, event.into()) + ink_lang2::EnvAccess::::emit_event(self, event.into()) } } } @@ -128,7 +128,7 @@ impl EventHelpers<'_> { } )* - impl ink_core::env2::Topics for Event { + impl ink_core::env3::Topics for Event { fn topics(&self) -> &'static [Hash] { match self { #( @@ -146,7 +146,7 @@ impl EventHelpers<'_> { let ident = &item_event.ident; quote_spanned!(span => - impl ink_core::env2::Topics for #ident { + impl ink_core::env3::Topics for #ident { fn topics(&self) -> &'static [Hash] { &[] } diff --git a/lang2/macro/src/codegen/storage.rs b/lang2/macro/src/codegen/storage.rs index 42a9da6f1a4..a9e49d72c97 100644 --- a/lang2/macro/src/codegen/storage.rs +++ b/lang2/macro/src/codegen/storage.rs @@ -49,13 +49,10 @@ impl GenerateCode for Storage<'_> { let storage_span = self.contract.storage.span(); let conflic_depedency_cfg = self.generate_code_using::(); - let aliases = self.generate_aliases(); let trait_impls = self.generate_trait_impls_for_storage(); let access_env_impls = self.generate_access_env_trait_impls(); let message_impls = self.generate_message_impls(); let storage_struct = self.generate_storage_struct(); - let storage_and_env_wrapper = self.generate_storage_and_env_wrapper(); - let layout_impls = self.generate_has_layout(); let use_emit_event = if !self.contract.events.is_empty() { // Required to allow for `self.env().emit_event(..)` in messages and constructors. @@ -70,22 +67,19 @@ impl GenerateCode for Storage<'_> { mod __ink_storage { use super::*; - #aliases #access_env_impls #trait_impls #storage_struct - #storage_and_env_wrapper - #layout_impls } #conflic_depedency_cfg - pub use __ink_storage::StorageAndEnv; + pub use __ink_storage::Storage; #conflic_depedency_cfg const _: () = { // Used to make `self.env()` available in message code. #[allow(unused_imports)] - use ink_core::env2::AccessEnv as _; + use ink_lang2::Env as _; #use_emit_event #message_impls @@ -96,54 +90,14 @@ impl GenerateCode for Storage<'_> { impl Storage<'_> { fn generate_access_env_trait_impls(&self) -> TokenStream2 { - let access_env_impls = if self.contract.meta_info.is_dynamic_allocation_enabled() - { - quote! { - impl ink_lang2::AccessEnv for StorageAndEnv { - fn access_env(&mut self) -> &mut ink_core::env2::EnvAccess { - self.__env.env_mut() - } - } - } - } else { - quote! { - impl ink_lang2::AccessEnv for StorageAndEnv { - fn access_env(&mut self) -> &mut ink_core::env2::EnvAccess { - &mut self.__env - } - } - } - }; quote! { - #access_env_impls + impl<'a> ink_lang2::Env for &'a Storage { + type EnvAccess = ink_lang2::EnvAccess<'a, EnvTypes>; - impl<'a> ink_core::env2::AccessEnv for &'a StorageAndEnv { - type Target = <&'a UsedEnv as ink_core::env2::AccessEnv>::Target; - - fn env(self) -> Self::Target { - ink_core::env2::AccessEnv::env(&self.__env) + fn env(self) -> Self::EnvAccess { + Default::default() } } - - impl<'a> ink_core::env2::AccessEnv for &'a mut StorageAndEnv { - type Target = <&'a mut UsedEnv as ink_core::env2::AccessEnv>::Target; - - fn env(self) -> Self::Target { - ink_core::env2::AccessEnv::env(&mut self.__env) - } - } - } - } - - fn generate_aliases(&self) -> TokenStream2 { - if self.contract.meta_info.is_dynamic_allocation_enabled() { - quote! { - pub type UsedEnv = ink_core::env2::DynEnv>; - } - } else { - quote! { - pub type UsedEnv = ink_core::env2::EnvAccess; - } } } @@ -195,98 +149,6 @@ impl Storage<'_> { } } - fn generate_has_layout(&self) -> TokenStream2 { - let env_layout = if self.contract.meta_info.is_dynamic_allocation_enabled() { - quote! { ink_abi::LayoutField::new("env", self.__env.layout()), } - } else { - quote! {} - }; - quote! { - #[cfg(feature = "ink-generate-abi")] - impl ink_abi::HasLayout for StorageAndEnv { - fn layout(&self) -> ink_abi::StorageLayout { - use type_metadata::Metadata as _; - ink_abi::LayoutStruct::new( - Self::meta_type(), - vec![ - ink_abi::LayoutField::new("storage", self.__storage.layout()), - #env_layout - ], - ) - .into() - } - } - - } - } - - fn generate_storage_and_env_wrapper(&self) -> TokenStream2 { - let attrs = utils::filter_non_ink_attributes(&self.contract.storage.attrs); - - quote! { - #(#attrs)* - #[cfg_attr( - feature = "ink-generate-abi", - derive(type_metadata::Metadata) - )] - #[cfg_attr(any(test, feature = "test-env"), derive(Debug))] - pub struct StorageAndEnv { - __storage: Storage, - __env: UsedEnv, - } - - impl core::ops::Deref for StorageAndEnv { - type Target = Storage; - - fn deref(&self) -> &Self::Target { - &self.__storage - } - } - - impl core::ops::DerefMut for StorageAndEnv { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.__storage - } - } - - impl ink_core::storage::alloc::AllocateUsing for StorageAndEnv { - unsafe fn allocate_using(alloc: &mut A) -> Self - where - A: ink_core::storage::alloc::Allocate, - { - Self { - __storage: ink_core::storage::alloc::AllocateUsing::allocate_using(alloc), - __env: ink_core::storage::alloc::AllocateUsing::allocate_using(alloc), - } - } - } - - impl ink_core::storage::Flush for StorageAndEnv { - fn flush(&mut self) { - ink_core::storage::Flush::flush(&mut self.__storage); - ink_core::storage::Flush::flush(&mut self.__env); - } - } - - - - impl ink_core::storage::alloc::Initialize for StorageAndEnv { - type Args = (); - - fn default_value() -> Option { - Some(()) - } - - fn initialize(&mut self, _args: Self::Args) { - ink_core::storage::alloc::Initialize::try_default_initialize(&mut self.__storage); - ink_core::storage::alloc::Initialize::try_default_initialize(&mut self.__env); - } - } - - impl ink_lang2::Storage for StorageAndEnv {} - } - } - /// Generates the storage struct definition. fn generate_storage_struct(&self) -> TokenStream2 { let storage = &self.contract.storage; @@ -308,6 +170,8 @@ impl Storage<'_> { #[cfg_attr(any(test, feature = "test-env"), derive(Debug))] pub struct Storage #fields + + impl ink_lang2::Storage for Storage {} ) } @@ -347,7 +211,7 @@ impl Storage<'_> { .map(|fun| self.generate_message(fun)); quote_spanned!( span => #[cfg_attr(feature = "cargo-clippy", allow(clippy::new_ret_no_self))] - impl StorageAndEnv { + impl Storage { #( #fns )* diff --git a/lang2/macro/src/codegen/testable.rs b/lang2/macro/src/codegen/testable.rs index 69e8cda60c6..eb9e928cad0 100644 --- a/lang2/macro/src/codegen/testable.rs +++ b/lang2/macro/src/codegen/testable.rs @@ -46,14 +46,14 @@ impl GenerateCode for TestWrapper<'_> { quote! { #[cfg(all(test, feature = "test-env"))] - pub use self::__ink_testable::TestableStorageAndEnv; + pub use self::__ink_testable::TestableStorage; #[cfg(all(test, feature = "test-env"))] mod __ink_testable { use super::*; - impl ink_lang2::InstantiateTestable for StorageAndEnv { - type Wrapped = TestableStorageAndEnv; + impl ink_lang2::InstantiateTestable for Storage { + type Wrapped = TestableStorage; fn instantiate() -> Self::Wrapped { let mut contract: Self = unsafe { @@ -65,8 +65,8 @@ impl GenerateCode for TestWrapper<'_> { &mut alloc, ) }; - ink_core::env2::test::TestEnv::::try_initialize() - .expect("encountered already initialized test environment"); + ink_core::env3::test::initialize_as_default::() + .expect("encountered already initialized off-chain environment"); ink_core::storage::alloc::Initialize::try_default_initialize( &mut contract, ); @@ -74,14 +74,14 @@ impl GenerateCode for TestWrapper<'_> { } } - pub use self::__ink_private::TestableStorageAndEnv; + pub use self::__ink_private::TestableStorage; mod __ink_private { use super::*; #testable_storage_and_env } - impl TestableStorageAndEnv { + impl TestableStorage { #( #wrapped_constructors )* @@ -106,8 +106,8 @@ impl TestWrapper<'_> { quote_spanned!(span=> pub fn #ident( #(#fn_args),* - ) -> ::Wrapped { - let mut contract = ::instantiate(); + ) -> ::Wrapped { + let mut contract = ::instantiate(); contract.#ident( #( #arg_idents @@ -125,25 +125,25 @@ impl TestWrapper<'_> { quote! { #( #attrs )* #[derive(Debug)] - pub struct TestableStorageAndEnv { - contract: StorageAndEnv, + pub struct TestableStorage { + contract: Storage, } - impl From for TestableStorageAndEnv { - fn from(contract: StorageAndEnv) -> Self { + impl From for TestableStorage { + fn from(contract: Storage) -> Self { Self { contract } } } - impl core::ops::Deref for TestableStorageAndEnv { - type Target = StorageAndEnv; + impl core::ops::Deref for TestableStorage { + type Target = Storage; fn deref(&self) -> &Self::Target { &self.contract } } - impl core::ops::DerefMut for TestableStorageAndEnv { + impl core::ops::DerefMut for TestableStorage { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.contract } diff --git a/lang2/macro/src/ir/data.rs b/lang2/macro/src/ir/data.rs index 0fba84102e6..496a9f0b249 100644 --- a/lang2/macro/src/ir/data.rs +++ b/lang2/macro/src/ir/data.rs @@ -69,6 +69,7 @@ pub struct MetaInfo { impl MetaInfo { /// Returns `true` if the user enabled dynamic storage allocation. + #[allow(unused)] // We might need this again in the future! If not, remove. pub fn is_dynamic_allocation_enabled(&self) -> bool { self.dynamic_allocations_enabled } @@ -88,7 +89,7 @@ pub struct MetaTypes { impl Default for MetaTypes { fn default() -> Self { Self { - ty: syn::parse_quote! { ink_core::env2::DefaultSrmlTypes }, + ty: syn::parse_quote! { ink_core::env3::DefaultEnvTypes }, } } } diff --git a/lang2/macro/src/ir/params.rs b/lang2/macro/src/ir/params.rs index 20f0ef9122e..15353cde2e4 100644 --- a/lang2/macro/src/ir/params.rs +++ b/lang2/macro/src/ir/params.rs @@ -41,7 +41,7 @@ use crate::ir::MetaVersion; /// # Example /// /// ```no_compile -/// #[ink::contract(env = DefaultSrmlTypes, version = 0.1.0)] +/// #[ink::contract(env = DefaultEnvTypes, version = 0.1.0)] /// ``` pub struct Params { /// The delimited meta information parameters. @@ -71,7 +71,7 @@ impl Spanned for Params { /// /// ```no_compile /// #[ink::contract( -/// env = DefaultSrmlTypes, // The used chain types. +/// env = DefaultEnvTypes, // The used chain types. /// version = 0.1.0, // The used ink! version. /// )] /// mod my_contract { ... } @@ -84,7 +84,7 @@ impl Spanned for Params { #[derive(Debug, Clone, From)] #[allow(clippy::large_enum_variant)] // We should benchmark this somehow. pub enum MetaParam { - /// Environmental types definition: `#[ink(env = DefaultSrmlTypes)]` + /// Environmental types definition: `#[ink(env = DefaultEnvTypes)]` Types(ParamTypes), /// Information about the ink! version: `#[ink(version = x.y.z)]` Version(ParamVersion), @@ -103,7 +103,7 @@ impl MetaParam { /// /// # Examples /// - /// - for `types = DefaultSrmlTypes` this is `types` + /// - for `types = DefaultEnvTypes` this is `types` /// - for `version = [0, 1, 0]` this is `version` pub fn ident(&self) -> &Ident { match self { @@ -168,7 +168,7 @@ impl ParamDynamicAllocations { } } -/// The environment types definition: `#[ink(env = DefaultSrmlTypes)]` +/// The environment types definition: `#[ink(env = DefaultEnvTypes)]` #[derive(Debug, Clone)] pub struct ParamTypes { /// The `env` identifier. diff --git a/lang2/macro/src/ir/tests.rs b/lang2/macro/src/ir/tests.rs index 1fa3073a7aa..c99a063271b 100644 --- a/lang2/macro/src/ir/tests.rs +++ b/lang2/macro/src/ir/tests.rs @@ -40,6 +40,6 @@ fn parse_meta_event() { #[test] fn parse_params() { let _input: Params = syn::parse_quote! { - env = DefaultSrmlTypes, version = "0.1.0" + env = DefaultEnvTypes, version = "0.1.0" }; } diff --git a/lang2/macro/tests/compile_tests.rs b/lang2/macro/tests/compile_tests.rs index f1edfbdcc81..4d2349ae723 100644 --- a/lang2/macro/tests/compile_tests.rs +++ b/lang2/macro/tests/compile_tests.rs @@ -27,7 +27,6 @@ fn compile_tests() { t.compile_fail("tests/ui/fail/03-invalid-version.rs"); t.compile_fail("tests/ui/fail/04-missing-message.rs"); t.compile_fail("tests/ui/fail/05-forbidden-idents.rs"); - t.compile_fail("tests/ui/fail/06-access-generated-fields.rs"); t.compile_fail("tests/ui/fail/07-constructor-missing-self.rs"); t.compile_fail("tests/ui/fail/08-constructor-self-ref.rs"); t.compile_fail("tests/ui/fail/09-constructor-self-val.rs"); diff --git a/lang2/macro/tests/ui/fail/06-access-generated-fields.rs b/lang2/macro/tests/ui/fail/06-access-generated-fields.rs deleted file mode 100644 index 719362f84db..00000000000 --- a/lang2/macro/tests/ui/fail/06-access-generated-fields.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![feature(proc_macro_hygiene)] - -use ink_lang2 as ink; - -#[ink::contract(version = "0.1.0")] -mod noop { - #[ink(storage)] - struct Noop {} - - impl Noop { - #[ink(constructor)] - fn new(&mut self) { - let _ = &self.__env; - } - - #[ink(message)] - fn noop(&self) { - let _ = &self.__storage; - } - } -} - -fn main() {} diff --git a/lang2/macro/tests/ui/fail/06-access-generated-fields.stderr b/lang2/macro/tests/ui/fail/06-access-generated-fields.stderr deleted file mode 100644 index 32f83545df6..00000000000 --- a/lang2/macro/tests/ui/fail/06-access-generated-fields.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0616]: field `__env` of struct `noop::__ink_private::__ink_storage::StorageAndEnv` is private - --> $DIR/06-access-generated-fields.rs:13:22 - | -13 | let _ = &self.__env; - | ^^^^^^^^^^ - -error[E0616]: field `__storage` of struct `noop::__ink_private::__ink_storage::StorageAndEnv` is private - --> $DIR/06-access-generated-fields.rs:18:22 - | -18 | let _ = &self.__storage; - | ^^^^^^^^^^^^^^ diff --git a/lang2/macro/tests/ui/pass/02-flipper-contract.rs b/lang2/macro/tests/ui/pass/02-flipper-contract.rs index 8466a037571..a4dc0914267 100644 --- a/lang2/macro/tests/ui/pass/02-flipper-contract.rs +++ b/lang2/macro/tests/ui/pass/02-flipper-contract.rs @@ -1,10 +1,11 @@ #![feature(proc_macro_hygiene)] -use ink_core::storage; use ink_lang2 as ink; #[ink::contract(version = "0.1.0")] mod flipper { + use ink_core::storage; + #[ink(storage)] struct Flipper { value: storage::Value, diff --git a/lang2/macro/tests/ui/pass/03-incrementer-contract.rs b/lang2/macro/tests/ui/pass/03-incrementer-contract.rs index c09bd45685d..4455b5118f1 100644 --- a/lang2/macro/tests/ui/pass/03-incrementer-contract.rs +++ b/lang2/macro/tests/ui/pass/03-incrementer-contract.rs @@ -1,10 +1,11 @@ #![feature(proc_macro_hygiene)] -use ink_core::storage; use ink_lang2 as ink; #[ink::contract(version = "0.1.0")] mod incrementer { + use ink_core::storage; + #[ink(storage)] struct Incrementer { value: storage::Value, diff --git a/lang2/macro/tests/ui/pass/04-erc20-contract.rs b/lang2/macro/tests/ui/pass/04-erc20-contract.rs index 7b4867806c4..81b5d4178b7 100644 --- a/lang2/macro/tests/ui/pass/04-erc20-contract.rs +++ b/lang2/macro/tests/ui/pass/04-erc20-contract.rs @@ -1,10 +1,11 @@ #![feature(proc_macro_hygiene)] -use ink_core::storage; use ink_lang2 as ink; #[ink::contract(version = "0.1.0")] mod erc20 { + use ink_core::storage; + #[ink(storage)] struct Erc20 { total_supply: storage::Value, diff --git a/lang2/macro/tests/ui/pass/05-erc721-contract.rs b/lang2/macro/tests/ui/pass/05-erc721-contract.rs index 8e2dcafcfd2..4736192aaf0 100644 --- a/lang2/macro/tests/ui/pass/05-erc721-contract.rs +++ b/lang2/macro/tests/ui/pass/05-erc721-contract.rs @@ -1,10 +1,11 @@ #![feature(proc_macro_hygiene)] -use ink_core::storage; use ink_lang2 as ink; #[ink::contract(version = "0.1.0")] mod erc721 { + use ink_core::storage; + /// A token ID. pub type TokenId = u32; diff --git a/lang2/macro/tests/ui/pass/07-flipper-as-dependency.rs b/lang2/macro/tests/ui/pass/07-flipper-as-dependency.rs index f0f3a431f30..922fc95ee75 100644 --- a/lang2/macro/tests/ui/pass/07-flipper-as-dependency.rs +++ b/lang2/macro/tests/ui/pass/07-flipper-as-dependency.rs @@ -1,6 +1,5 @@ #![feature(proc_macro_hygiene)] -use ink_core::storage; use ink_lang2 as ink; #[ink::contract( @@ -8,6 +7,8 @@ use ink_lang2 as ink; compile_as_dependency = true, )] mod flipper { + use ink_core::storage; + #[ink(storage)] struct Flipper { value: storage::Value, diff --git a/lang2/src/contract.rs b/lang2/src/contract.rs index 14913fc6fdb..8fba2525f11 100644 --- a/lang2/src/contract.rs +++ b/lang2/src/contract.rs @@ -12,13 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use core::{ - marker::PhantomData, - mem::ManuallyDrop, -}; - use crate::{ - AccessEnv, Dispatch, DispatchError, DispatchList, @@ -31,6 +25,11 @@ use crate::{ FnOutput, PushDispatcher, }; +use core::{ + marker::PhantomData, + mem::ManuallyDrop, +}; +use ink_core::env3::EnvTypes; /// The contract definition. pub struct Contract { @@ -198,25 +197,28 @@ where DispatchList: Dispatch, DispatchList: Dispatch, { - pub fn dispatch_using_mode( + pub fn dispatch_using_mode( mut self, mode: DispatchMode, ) -> Result<(), DispatchError> where - Storage: AccessEnv, - Env: ink_core::env2::Env, + T: EnvTypes, { // Initialize storage if we instantiate the contract. if mode == DispatchMode::Instantiate { self.storage.try_default_initialize(); } // Dispatch using the contract execution input. - let call_data = self.storage.access_env().input(); + let call_data = + ink_core::env3::input().map_err(|_| DispatchError::CouldNotReadInput)?; match mode { DispatchMode::Instantiate => { - self.constructors.dispatch(&mut self.storage, &call_data) + self.constructors + .dispatch::(&mut self.storage, &call_data) + } + DispatchMode::Call => { + self.messages.dispatch::(&mut self.storage, &call_data) } - DispatchMode::Call => self.messages.dispatch(&mut self.storage, &call_data), } } } diff --git a/lang2/src/cross_calling.rs b/lang2/src/cross_calling.rs index 7579c5eb0c5..4ce5903e517 100644 --- a/lang2/src/cross_calling.rs +++ b/lang2/src/cross_calling.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use ink_core::env2::EnvTypes; +use ink_core::env3::EnvTypes; /// Implemented by contracts that are compiled as dependencies. /// @@ -41,10 +41,10 @@ pub trait ForwardCallMut { /// Implemented by contracts that are compiled as dependencies. /// /// Allows them to return their underlying account identifier. -pub trait ToAccountId +pub trait ToAccountId where - Env: EnvTypes, + T: EnvTypes, { /// Returns the underlying account identifier of the instantiated contract. - fn to_account_id(&self) -> ::AccountId; + fn to_account_id(&self) -> ::AccountId; } diff --git a/lang2/src/dispatcher.rs b/lang2/src/dispatcher.rs index 697e1e74442..2843bdbdd7c 100644 --- a/lang2/src/dispatcher.rs +++ b/lang2/src/dispatcher.rs @@ -12,24 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -use core::any::TypeId; - -use ink_core::{ - env2::call::{ - CallData, - Selector, - }, - storage::Flush, -}; - use crate::{ - AccessEnv, DispatchError, FnInput, FnOutput, FnSelector, Message, }; +use core::any::TypeId; +use ink_core::{ + env3::{ + call::{ + CallData, + Selector, + }, + EnvTypes, + }, + storage::Flush, +}; /// Results of message handling operations. pub type Result = core::result::Result; @@ -37,10 +37,9 @@ pub type Result = core::result::Result; /// Types implementing this trait can handle contract calls. pub trait Dispatch { /// Dispatches the call and returns the call result. - fn dispatch(&self, storage: &mut S, input: &CallData) -> Result<()> + fn dispatch(&self, storage: &mut S, input: &CallData) -> Result<()> where - S: AccessEnv, - Env: ink_core::env2::Env; + T: EnvTypes; } /// A dispatcher that shall never dispatch. @@ -53,10 +52,9 @@ pub trait Dispatch { pub struct UnreachableDispatcher; impl Dispatch for UnreachableDispatcher { - fn dispatch(&self, _storage: &mut S, _data: &CallData) -> Result<()> + fn dispatch(&self, _storage: &mut S, _data: &CallData) -> Result<()> where - S: AccessEnv, - Env: ink_core::env2::Env, + T: EnvTypes, { Err(DispatchError::UnknownSelector) } @@ -110,15 +108,14 @@ where D: Dispatch + FnSelector, Rest: Dispatch, { - fn dispatch(&self, storage: &mut S, data: &CallData) -> Result<()> + fn dispatch(&self, storage: &mut S, data: &CallData) -> Result<()> where - S: AccessEnv, - Env: ink_core::env2::Env, + T: EnvTypes, { if ::SELECTOR == data.selector() { - self.dispatcher.dispatch(storage, data) + self.dispatcher.dispatch::(storage, data) } else { - self.rest.dispatch(storage, data) + self.rest.dispatch::(storage, data) } } } @@ -207,17 +204,16 @@ macro_rules! impl_dispatcher_for { ::Output: scale::Encode, S: Flush, { - fn dispatch(&self, storage: &mut S, data: &CallData) -> Result<()> + fn dispatch(&self, storage: &mut S, data: &CallData) -> Result<()> where - S: AccessEnv, - Env: ink_core::env2::Env, + T: EnvTypes, { use scale::Decode as _; let args = ::Input::decode(&mut &data.params()[..]) .map_err(|_| DispatchError::InvalidParameters)?; let result = self.eval(storage, args); if TypeId::of::<::Output>() != TypeId::of::<()>() { - AccessEnv::access_env(storage).output(&result) + ink_core::env3::output::<::Output>(&result) } if ::IS_MUT { // Flush the storage since the message might have mutated it. diff --git a/lang2/src/env_access.rs b/lang2/src/env_access.rs new file mode 100644 index 00000000000..cb408e0eb19 --- /dev/null +++ b/lang2/src/env_access.rs @@ -0,0 +1,341 @@ +// Copyright 2018-2019 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 core::marker::PhantomData; +use ink_core::{ + env3 as env, + env3::{ + call::{ + CallParams, + InstantiateParams, + ReturnType, + }, + EnvTypes, + Result, + Topics, + }, + storage::Key, +}; + +/// Allows to directly access the environment mutably. +/// +/// # Note +/// +/// This is generally implemented for storage structs that include +/// their environment in order to allow the different dispatch functions +/// to use it for returning the contract's output. +pub trait Env { + /// The environmental types. + type EnvAccess; + + /// Accesses the environment with predefined environmental types. + fn env(self) -> Self::EnvAccess; +} + +/// A typed accessor to the environment. +/// +/// This allows ink! messages to make use of the environment efficiently +/// and user friendly while also maintaining access invariants. +pub struct EnvAccess<'a, T> { + /// Tricks the Rust compiler into thinking that we use `T`. + marker: PhantomData &'a T>, +} + +impl<'a, T> Default for EnvAccess<'a, T> { + fn default() -> Self { + Self { + marker: Default::default(), + } + } +} + +impl<'a, E> core::fmt::Debug for EnvAccess<'a, E> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct("EnvAccess").finish() + } +} + +impl<'a, T> EnvAccess<'a, T> +where + T: EnvTypes, +{ + /// Returns the address of the caller of the executed contract. + /// + /// # Panics + /// + /// If the returned caller cannot be properly decoded. + pub fn caller(self) -> T::AccountId { + env::caller::().expect("couldn't decode caller") + } + + /// Returns the transferred balance for the contract execution. + /// + /// # Panics + /// + /// If the returned value cannot be properly decoded. + pub fn transferred_balance(self) -> T::Balance { + env::transferred_balance::().expect("couldn't decode transferred balance") + } + + /// Returns the current price for gas. + /// + /// # Panics + /// + /// If the returned value cannot be properly decoded. + pub fn gas_price(self) -> T::Balance { + env::gas_price::().expect("couldn't decode gas price") + } + + /// Returns the amount of gas left for the contract execution. + /// + /// # Panics + /// + /// If the returned value cannot be properly decoded. + pub fn gas_left(self) -> T::Balance { + env::gas_left::().expect("couldn't decode gas left") + } + + /// Returns the current block time in milliseconds. + /// + /// # Panics + /// + /// If the returned value cannot be properly decoded. + pub fn now_in_ms(self) -> T::TimeStamp { + env::now_in_ms::().expect("couldn't decode block time stamp") + } + + /// Returns the address of the executed contract. + /// + /// # Panics + /// + /// If the returned value cannot be properly decoded. + pub fn address(self) -> T::AccountId { + env::address::().expect("couldn't decode contract address") + } + + /// Returns the balance of the executed contract. + /// + /// # Panics + /// + /// If the returned value cannot be properly decoded. + pub fn balance(self) -> T::Balance { + env::balance::().expect("couldn't decode contract balance") + } + + /// Returns the current rent allowance for the executed contract. + /// + /// # Panics + /// + /// If the returned value cannot be properly decoded. + pub fn rent_allowance(self) -> T::Balance { + env::rent_allowance::().expect("couldn't decode contract rent allowance") + } + + /// Returns the current block number. + /// + /// # Panics + /// + /// If the returned value cannot be properly decoded. + pub fn block_number(self) -> T::BlockNumber { + env::block_number::().expect("couldn't decode block number") + } + + /// Returns the minimum balance for the contracts chain. + /// + /// # Panics + /// + /// If the returned value cannot be properly decoded. + pub fn minimum_balance(self) -> T::Balance { + env::minimum_balance::().expect("couldn't decode minimum account balance") + } + + /// Returns the tombstone deposit for the contracts chain. + /// + /// # Panics + /// + /// If the returned value cannot be properly decoded. + pub fn tombstone_deposit(self) -> T::Balance { + env::tombstone_deposit::().expect("couldn't decode tombstone deposits") + } + + /// Emits an event with the given event data. + pub fn emit_event(self, event: Event) + where + Event: Topics + scale::Encode, + { + env::emit_event::(event) + } + + /// Sets the rent allowance of the executed contract to the new value. + pub fn set_rent_allowance(self, new_value: T::Balance) { + env::set_rent_allowance::(new_value) + } + + /// Invokes a call to the runtime. + /// + /// # Note + /// + /// The call is not guaranteed to execute immediately but might be deferred + /// to the end of the contract execution. + /// + /// # Errors + /// + /// - If the called runtime function does not exist. + pub fn invoke_runtime(self, params: &T::Call) -> Result<()> { + env::invoke_runtime::(params) + } + + /// Invokes a contract message. + /// + /// # Note + /// + /// This is a low level way to invoke another smart contract. + /// Prefer to use the ink! guided and type safe approach to using this. + /// + /// # Errors + /// + /// - If the called contract does not exist. + /// - If the called contract is a tombstone. + /// - If arguments passed to the called contract message are invalid. + /// - If the called contract execution has trapped. + /// - If the called contract ran out of gas upon execution. + /// - If given too few endowment. + pub fn invoke_contract(self, params: &CallParams) -> Result<()> { + env::invoke_contract::(params) + } + + /// Evaluates a contract message and returns its result. + /// + /// # Note + /// + /// This is a low level way to evaluate another smart contract. + /// Prefer to use the ink! guided and type safe approach to using this. + /// + /// # Errors + /// + /// - If the called contract does not exist. + /// - If the called contract is a tombstone. + /// - If arguments passed to the called contract message are invalid. + /// - If the called contract execution has trapped. + /// - If the called contract ran out of gas upon execution. + /// - If given too few endowment. + /// - If the returned value failed to decode properly. + pub fn eval_contract(self, params: &CallParams>) -> Result + where + R: scale::Decode, + { + env::eval_contract::(params) + } + + /// Instantiates another contract. + /// + /// # Note + /// + /// This is a low level way to instantiate another smart contract. + /// Prefer to use the ink! guided and type safe approach to using this. + /// + /// # Errors + /// + /// - If the code hash is invalid. + /// - If the arguments passed to the instantiation process are invalid. + /// - If the instantiation process traps. + /// - If the instantiation process runs out of gas. + /// - If given too few endowment. + /// - If the returned account ID failed to decode properly. + pub fn create_contract(self, params: &InstantiateParams) -> Result { + env::create_contract::(params) + } + + /// Restores a smart contract in tombstone state. + /// + /// # Params + /// + /// - `account_id`: Account ID of the to-be-restored contract. + /// - `code_hash`: Code hash of the to-be-restored contract. + /// - `rent_allowance`: Rent allowance of the restored contract + /// upon successful restoration. + /// - `filtered_keys`: Storage keys that will be removed for the tombstone hash + /// match calculation that decide whether the original contract + /// storage and the storage of the restorer contract is equal. + /// + /// # Usage + /// + /// A smart contract that has too few funds to pay for its storage fees + /// can eventually be evicted. An evicted smart contract `C` leaves behind + /// a tombstone associated with a hash that has been computed partially + /// by its storage contents. + /// + /// To restore contract `C` back to a fully working contract the normal + /// process is to write another contract `C2` with the only purpose to + /// eventually have the absolutely same contract storage as `C` did when + /// it was evicted. + /// For that purpose `C2` can use other storage keys that have not been in + /// use by contract `C`. + /// Once `C2` contract storage matches the storage of `C` when it was evicted + /// `C2` can invoke this method in order to initiate restoration of `C`. + /// A tombstone hash is calculated for `C2` and if it matches the tombstone + /// hash of `C` the restoration is going to be successful. + /// The `filtered_keys` argument can be used to ignore the extraneous keys + /// used by `C2` but not used by `C`. + /// + /// The process of such a smart contract restoration can generally be very expensive. + /// + /// # Note + /// + /// - The `filtered_keys` can be used to ignore certain storage regions + /// in the restorer contract to not influence the hash calculations. + /// - Does *not* perform restoration right away but defers it to the end of + /// the contract execution. + /// - Restoration is cancelled if there is no tombstone in the destination + /// address or if the hashes don't match. No changes are made in this case. + pub fn restore_contract( + self, + account_id: T::AccountId, + code_hash: T::Hash, + rent_allowance: T::Balance, + filtered_keys: &[Key], + ) { + env::restore_contract::(account_id, code_hash, rent_allowance, filtered_keys) + } + + /// Returns a random hash. + /// + /// # Note + /// + /// - The subject buffer can be used to further randomize the hash. + /// - Within the same execution returns the same random hash for the same subject. + /// + /// # Panics + /// + /// If the returned value cannot be properly decoded. + pub fn random(self, subject: &[u8]) -> T::Hash + where + T: EnvTypes, + { + env::random::(subject).expect("couldn't decode randomized hash") + } + + /// Returns the value from the *runtime* storage at the position of the key if any. + /// + /// # Errors + /// + /// - If the key's entry is empty + /// - If the decoding of the typed value failed + pub fn get_runtime_storage(self, runtime_key: &[u8]) -> Option> + where + R: scale::Decode, + { + env::get_runtime_storage::(runtime_key) + } +} diff --git a/lang2/src/error.rs b/lang2/src/error.rs index 09e468d8841..e36058348e2 100644 --- a/lang2/src/error.rs +++ b/lang2/src/error.rs @@ -25,20 +25,15 @@ pub enum DispatchError { InvalidParameters, InvalidInstantiateParameters, InvalidCallParameters, + + CouldNotReadInput, } impl DispatchError { /// Converts `self` into an associated `u32` that SRML contracts can handle. #[inline] pub fn to_u32(self) -> u32 { - match self { - DispatchError::UnknownSelector => 0x01, - DispatchError::UnknownInstantiateSelector => 0x02, - DispatchError::UnknownCallSelector => 0x03, - DispatchError::InvalidParameters => 0x04, - DispatchError::InvalidInstantiateParameters => 0x05, - DispatchError::InvalidCallParameters => 0x06, - } + DispatchRetCode::from(self).to_u32() } } @@ -74,6 +69,7 @@ impl From for DispatchRetCode { DispatchError::InvalidParameters => Self(0x04), DispatchError::InvalidInstantiateParameters => Self(0x05), DispatchError::InvalidCallParameters => Self(0x06), + DispatchError::CouldNotReadInput => Self(0x07), } } } diff --git a/lang2/src/lib.rs b/lang2/src/lib.rs index c48fec21231..5f32e402da1 100644 --- a/lang2/src/lib.rs +++ b/lang2/src/lib.rs @@ -24,6 +24,7 @@ mod abi; mod contract; mod cross_calling; mod dispatcher; +mod env_access; mod error; mod testable; mod traits; @@ -56,6 +57,10 @@ pub use self::{ PushDispatcher, UnreachableDispatcher, }, + env_access::{ + Env, + EnvAccess, + }, error::{ DispatchError, DispatchResult, @@ -63,7 +68,6 @@ pub use self::{ }, testable::InstantiateTestable, traits::{ - AccessEnv, FnInput, FnOutput, FnSelector, diff --git a/lang2/src/traits.rs b/lang2/src/traits.rs index 54d7882ba7b..3ab3ed41c9a 100644 --- a/lang2/src/traits.rs +++ b/lang2/src/traits.rs @@ -13,10 +13,7 @@ // limitations under the License. use ink_core::{ - env2::{ - call::Selector, - EnvAccess, - }, + env3::call::Selector, storage::{ alloc::{ AllocateUsing, @@ -49,17 +46,5 @@ pub trait Message: FnInput + FnOutput + FnSelector { const IS_MUT: bool; } -/// Allows to directly access the environment mutably. -/// -/// # Note -/// -/// This is generally implemented for storage structs that include -/// their environment in order to allow the different dispatch functions -/// to use it for returning the contract's output. -pub trait AccessEnv { - /// Returns a mutable access to the environment. - fn access_env(&mut self) -> &mut EnvAccess; -} - /// Types implementing this trait are storage structs. pub trait Storage: AllocateUsing + Initialize + Flush {} diff --git a/model/Cargo.toml b/model/Cargo.toml deleted file mode 100644 index 4a29b1cf258..00000000000 --- a/model/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "ink_model" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" -license = "APACHE-2.0" -readme = "README.md" -repository = "https://github.com/paritytech/ink" -documentation = "https://substrate.dev/substrate-contracts-workshop/#/" -homepage = "https://www.parity.io/" -description = "[ink!] Rust based eDSL for writing smart contracts for Substrate" -keywords = ["wasm", "parity", "webassembly", "blockchain", "edsl"] -categories = ["no-std", "embedded"] -include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] - -[dependencies] -ink_core = { path = "../core", default-features = false } -ink_prelude = { path = "../prelude/", default-features = false } -scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive", "full"] } - -[features] -default = ["test-env"] -test-env = [ - "std", - "ink_core/test-env", -] -std = [ - "ink_core/std", - "ink_prelude/std", - "scale/std", -] diff --git a/model/README.md b/model/README.md deleted file mode 100644 index ff962291ff2..00000000000 --- a/model/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# pDSL Model - -Medium-level abstractions to write and represent Wasm smart contracts. - -Greatly inspired by https://github.com/paritytech/fleetwood. - -This is currently in a very rough research shape. -Do not use it and don't expect anything to work at the moment. diff --git a/model/src/contract.rs b/model/src/contract.rs deleted file mode 100644 index be35355caff..00000000000 --- a/model/src/contract.rs +++ /dev/null @@ -1,536 +0,0 @@ -// Copyright 2018-2019 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 core::marker::PhantomData; - -use ink_core::env; -use ink_prelude::vec::Vec; - -use crate::{ - exec_env::ExecutionEnv, - msg::Message, - msg_handler::{ - CallData, - MessageHandler, - MessageHandlerMut, - RawMessageHandler, - RawMessageHandlerMut, - UnreachableMessageHandler, - }, - state::ContractState, -}; - -/// A marker struct to tell that the deploy handler requires no arguments. -#[derive(Copy, Clone)] -pub struct NoDeployArgs; - -/// A handler specific to deploying a smart contract. -/// -/// # Note -/// -/// This is normally mainly used to correctly initialize -/// a smart contracts state. -pub struct DeployHandler { - /// The arguments that deploy expects. - /// - /// This tricks Rust into thinking that this owns the state type. - /// However, it is just a marker which allows the contract declaration - /// to be a zero-sized-type (ZST). - args: PhantomData, - /// The actual deployment function. - deploy_fn: fn(&mut ExecutionEnv, Args), -} - -impl DeployHandler { - /// Returns a deploy handler that does nothing. - const fn init() -> Self { - Self { - args: PhantomData, - deploy_fn: move |_env, _| {}, - } - } -} - -impl DeployHandler { - /// Returns a new deploy handler for the given closure. - const fn new(raw_handler: fn(&mut ExecutionEnv, Args)) -> Self { - Self { - args: PhantomData, - deploy_fn: raw_handler, - } - } -} - -impl Copy for DeployHandler {} - -impl Clone for DeployHandler { - fn clone(&self) -> Self { - Self { - args: self.args, - deploy_fn: self.deploy_fn, - } - } -} - -/// A contract declaration. -/// -/// Uses the builder pattern in order to represent a contract -/// based on compile-time construction. -/// -/// Can be used to actually instantiate a contract during run-time -/// in order to dispatch a contract call or deploy state. -pub struct ContractDecl { - /// The type of the contract state. - /// - /// This tricks Rust into thinking that this owns the state type. - /// However, it is just a marker which allows the contract declaration - /// to be a zero-sized-type (ZST). - state: PhantomData, - - deployer: DeployHandler, - /// The compile-time chain of message handlers. - /// - /// # Note - /// - /// They are represented by a recursive tuple chain and start with - /// a simple `UnreachableMessageHandler` node. For every further - /// registered message handler this tuple is extended recursively. - /// - /// ## Example - /// - /// ```no_compile - /// UnreachableMessageHandler // Upon initialization. - /// (Foo, UnreachableMessageHandler) // After adding message handler Foo. - /// (Bar, (Foo, UnreachableMessageHandler)) // After adding message handler Bar. - /// ``` - /// - /// Note that every pair of message handlers is also a message handler. - handlers: HandlerChain, -} - -impl Clone - for ContractDecl -where - HandlerChain: Clone, -{ - fn clone(&self) -> Self { - Self { - state: self.state, - deployer: self.deployer, - handlers: self.handlers.clone(), - } - } -} - -impl Copy - for ContractDecl -where - HandlerChain: Copy, -{ -} - -/// An empty contract state. -#[derive(Copy, Clone)] -pub struct EmptyContractState; - -/// An empty env. -#[derive(Copy, Clone)] -pub struct EmptyEnv; - -impl ContractDecl { - /// Creates a new contract declaration with the given name. - pub const fn using( - ) -> ContractDecl { - ContractDecl { - state: PhantomData, - deployer: DeployHandler::init(), - handlers: UnreachableMessageHandler, - } - } -} - -impl ContractDecl { - /// Registers the given deployment procedure for the contract. - /// - /// # Note - /// - /// This is used to initialize the contract state upon deployment. - pub const fn on_deploy( - self, - handler: fn(&mut ExecutionEnv, Args), - ) -> ContractDecl - where - Args: scale::Decode, - { - ContractDecl { - state: self.state, - deployer: DeployHandler::new(handler), - handlers: self.handlers, - } - } -} - -impl - ContractDecl -where - Self: Copy, // Required in order to make this compile-time computable. -{ - /// Convenience method to append another message handler. - const fn append_msg_handler( - self, - handler: MsgHandler, - ) -> ContractDecl { - ContractDecl { - state: PhantomData, - deployer: self.deployer, - handlers: (handler, self.handlers), - } - } - - /// Registers a read-only message handler. - /// - /// # Note - /// - /// Read-only message handlers do not mutate contract state. - #[allow(clippy::type_complexity)] - pub const fn on_msg( - self, - handler: RawMessageHandler, - ) -> ContractDecl< - State, - Env, - DeployArgs, - (MessageHandler, HandlerChain), - > - where - Msg: Message, - State: ContractState, - Env: env::Env, - { - self.append_msg_handler(MessageHandler::from_raw(handler)) - } - - /// Registers a mutable message handler. - /// - /// # Note - /// - /// Mutable message handlers may mutate contract state. - #[allow(clippy::type_complexity)] - pub const fn on_msg_mut( - self, - handler: RawMessageHandlerMut, - ) -> ContractDecl< - State, - Env, - DeployArgs, - (MessageHandlerMut, HandlerChain), - > - where - Msg: Message, - State: ContractState, - Env: env::Env, - { - self.append_msg_handler(MessageHandlerMut::from_raw(handler)) - } -} - -impl - ContractDecl -where - // Self: Copy, // Required in order to make this compile-time computable. - State: ContractState, -{ - /// Creates an instance of the contract declaration. - /// - /// This associates the state with the contract storage - /// and defines its layout. - pub fn instantiate(self) -> ContractInstance { - use ink_core::storage::{ - alloc::{ - AllocateUsing, - BumpAlloc, - }, - Key, - }; - let env = unsafe { - // Note that it is totally fine here to start with a key - // offset of `0x0` as long as we only consider having one - // contract instance per execution. Otherwise their - // associated storage could overlap. - // - // This can later be solved by having an implementation for - // `AllocateUsing` for `ContractDecl` to actually instantiate - // them using an already existing allocator. Note that then - // all contracts always have to be allocated in the same - // order which could be achieved by simply putting all contracts - // into a contract struct that itself implements `AllocateUsing`. - let mut alloc = BumpAlloc::from_raw_parts(Key([0x0; 32])); - AllocateUsing::allocate_using(&mut alloc) - }; - ContractInstance { - env, - deployer: self.deployer, - handlers: self.handlers, - } - } -} - -/// A return status code for `deploy` and `dispatch` calls back to the SRML contracts module. -/// -/// # Note -/// -/// The `call` and `create` SRML contracts interfacing -/// instructions both return a `u32`, however, only the least-significant -/// 8 bits can be non-zero. -/// For a start we only allow `0` and `255` as return codes. -/// -/// Zero (`0`) represents a successful execution, (`255`) means invalid -/// execution (e.g. trap) and any value in between represents a non- -/// specified invalid execution. -/// -/// Other error codes are subject to future proposals. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct RetCode(u8); - -impl RetCode { - /// Indicates a successful execution. - pub fn success() -> Self { - Self(0) - } - - /// Indicates a failure upon execution. - pub fn failure() -> Self { - Self(255) - } - - /// Returns the internal `u32` value. - pub fn to_u32(self) -> u32 { - self.0 as u32 - } -} - -/// A simple interface to work with contracts. -pub trait Contract { - /// Deploys the contract state. - /// - /// Should be performed exactly once during contract lifetime. - /// Consumes the contract since nothing should be done afterwards. - fn deploy(self) -> RetCode; - - /// Dispatches the call input to a pre defined - /// contract message and runs its handler. - /// - /// Consumes self since it should be the default - /// action after instantiation. - /// - /// # Panics - /// - /// Panics (Wasm: traps) if the call input was invalid. - /// The call input is invalid if there was no matching - /// function selector found or if the data for a given - /// selected function was not decodable. - fn dispatch(self) -> RetCode; -} - -/// An interface that allows for simple testing of contracts. -pub trait TestableContract { - /// The arguments used for deployment. - /// - /// These must be the same as the ones defined on the deploy handler - /// of a contract declaration. - type DeployArgs: scale::Encode; - - /// Deploys the contract given the provided arguments for deployment. - /// - /// # Note - /// - /// This shall be performed only once during the lifetime of a contract. - /// - /// # Panics - /// - /// This might panic if the provided arguments do not match the expected. - fn deploy(&mut self, deploy_args: Self::DeployArgs); - - /// Calls the contract with the given message and its - /// inputs and upon successful execution returns its result. - /// - /// # Note - /// - /// Takes `&mut self` since it could potentially call a message - /// that mutates state. There currently is no separation between - /// messages that mutate state and those that do not. - /// - /// # Panics - /// - /// If the contract has no message handler setup for the given message. - fn call(&mut self, input: ::Input) -> ::Output - where - Msg: Message, - ::Input: scale::Encode, - ::Output: scale::Decode; -} - -/// An instance of a contract. -/// -/// This resembles the concrete contract that is the result of -/// an instantiation of a contract declaration. -pub struct ContractInstance { - /// The execution environment that is wrapping the actual state. - env: ExecutionEnv, - /// The deploy functionality. - deployer: DeployHandler, - /// The contract's message handlers. - handlers: HandlerChain, -} - -impl Contract - for ContractInstance -where - State: ContractState, - Env: env::Env, - DeployArgs: scale::Decode, - HandlerChain: crate::HandleCall, -{ - /// Deploys the contract. - /// - /// This runs exactly once during the lifetime of a contract and - /// is used to initialize the contract's state. - /// - /// # Note - /// - /// Accessing uninitialized contract state can end in trapping execution - /// or in the worst case in undefined behaviour. - fn deploy(self) -> RetCode { - // Deploys the contract state. - // - // Should be performed exactly once during contract lifetime. - // Consumes the contract since nothing should be done afterwards. - let input = Env::input(); - let mut this = self; - if let Err(err) = this.deploy_with(input.as_slice()) { - return err - } - core::mem::forget(this.env); - RetCode::success() - } - - /// Dispatches the input buffer and calls the associated message. - /// - /// Returns the result to the caller if there is any. - fn dispatch(self) -> RetCode { - // Dispatches the given input to a pre defined - // contract message and runs its handler. - // - // Consumes self since it should be the default - // action after instantiation. - // - // Internally calls the associated call. - use scale::Decode; - let input = Env::input(); - let call_data = CallData::decode(&mut &input[..]).unwrap(); - let mut this = self; - if let Err(err) = this.call_with_and_return(call_data) { - return err - } - core::mem::forget(this.env); - RetCode::success() - } -} - -impl - ContractInstance -where - State: ContractState, - Env: env::Env, - DeployArgs: scale::Decode, - HandlerChain: crate::HandleCall, -{ - /// Deploys the contract. - /// - /// This runs exactly once during the lifetime of a contract and - /// is used to initialize the contract's state. - /// - /// # Note - /// - /// Accessing uninitialized contract state can end in trapping execution - /// or in the worst case in undefined behaviour. - fn deploy_with(&mut self, input: &[u8]) -> Result<(), RetCode> { - // Deploys the contract state. - // - // Should be performed exactly once during contract lifetime. - // Consumes the contract since nothing should be done afterwards. - use ink_core::storage::alloc::Initialize as _; - self.env.initialize(()); - let deploy_params = - DeployArgs::decode(&mut &input[..]).map_err(|_err| RetCode::failure())?; - (self.deployer.deploy_fn)(&mut self.env, deploy_params); - self.env.state.flush(); - Ok(()) - } - - /// Calls the message encoded by the given call data - /// and returns the resulting value back to the caller. - fn call_with_and_return(&mut self, call_data: CallData) -> Result<(), RetCode> { - let result = self.call_with(call_data)?; - if !result.is_empty() { - self.env.return_data(result) - } - Ok(()) - } - - /// Calls the message encoded by the given call data. - /// - /// # Panics - /// - /// - If the contract has no message handler setup for the - /// message that is encoded by the given call data. - /// - If the encoded input arguments for the message do not - /// match the expected format. - fn call_with(&mut self, call_data: CallData) -> Result, RetCode> { - match self.handlers.handle_call(&mut self.env, call_data) { - Ok(encoded_result) => Ok(encoded_result), - Err(_err) => Err(RetCode::failure()), - } - } -} - -impl TestableContract - for ContractInstance -where - State: ContractState, - Env: env::Env, - DeployArgs: scale::Codec, - HandlerChain: crate::HandleCall, -{ - type DeployArgs = DeployArgs; - - fn deploy(&mut self, input: Self::DeployArgs) { - self.deploy_with(&input.encode()[..]) - .expect("`deploy` failed to execute properly") - } - - fn call(&mut self, input: ::Input) -> ::Output - where - Msg: Message, - ::Input: scale::Encode, - ::Output: scale::Decode, - { - let encoded_result = self - .call_with(CallData::from_msg::(input)) - .expect("`call` failed to execute properly"); - use scale::Decode; - ::Output::decode(&mut &encoded_result[..]) - .expect("`call_with` only encodes the correct types") - } -} diff --git a/model/src/exec_env.rs b/model/src/exec_env.rs deleted file mode 100644 index ce5d6dda079..00000000000 --- a/model/src/exec_env.rs +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2018-2019 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 core::marker::PhantomData; - -use ink_core::{ - env::{ - self, - CallError, - Env, - }, - storage::alloc::{ - Allocate, - AllocateUsing, - DynAlloc, - Initialize, - }, -}; -use scale::{ - Decode, - Encode as _, -}; - -use crate::ContractState; - -/// Provides a safe interface to an environment given a contract state. -pub struct ExecutionEnv { - /// The environment handler. - env_handler: EnvHandler, - /// The contract state. - pub state: State, -} - -impl AllocateUsing for ExecutionEnv -where - State: ContractState, -{ - unsafe fn allocate_using(alloc: &mut A) -> Self - where - A: Allocate, - { - let env_handler = AllocateUsing::allocate_using(alloc); - let state = AllocateUsing::allocate_using(alloc); - Self { env_handler, state } - } -} - -impl Initialize for ExecutionEnv -where - State: ContractState, -{ - type Args = (); - - fn initialize(&mut self, _: Self::Args) { - self.env_handler.initialize(()); - self.state.try_default_initialize(); - } -} - -impl core::ops::Deref for ExecutionEnv { - type Target = EnvHandler; - - fn deref(&self) -> &Self::Target { - &self.env_handler - } -} - -impl core::ops::DerefMut for ExecutionEnv { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.env_handler - } -} - -impl ExecutionEnv { - /// Splits the execution environment into shared references - /// to the environment handler and the state. - /// - /// # Note - /// - /// This can be useful if you want to implement a message as - /// a method of the state to make it callable from other messages. - pub fn split(&self) -> (&EnvHandler, &State) { - (&self.env_handler, &self.state) - } - - /// Splits the execution environment into mutable references - /// to the environment handler and the state. - /// - /// # Note - /// - /// This can be useful if you want to implement a message as - /// a method of the state to make it callable from other messages. - pub fn split_mut(&mut self) -> (&mut EnvHandler, &mut State) { - (&mut self.env_handler, &mut self.state) - } -} - -/// The actual handler for the environment and for dynamic -/// allocations and deallocations. -pub struct EnvHandler { - /// The dynamic allocator. - pub dyn_alloc: DynAlloc, - env_marker: PhantomData, -} - -impl AllocateUsing for EnvHandler { - unsafe fn allocate_using(alloc: &mut A) -> Self - where - A: Allocate, - { - Self { - dyn_alloc: AllocateUsing::allocate_using(alloc), - env_marker: PhantomData, - } - } -} - -impl Initialize for EnvHandler { - type Args = (); - - fn initialize(&mut self, _: Self::Args) { - self.dyn_alloc.initialize(()) - } -} - -impl EnvHandler { - /// Returns the address of the current smart contract. - pub fn address(&self) -> T::AccountId { - T::address() - } - - /// Returns the balance of the current smart contract. - pub fn balance(&self) -> T::Balance { - T::balance() - } - - /// Returns the caller address of the current smart contract execution. - pub fn caller(&self) -> T::AccountId { - T::caller() - } - - /// Returns the given data back to the caller. - /// - /// # Note - /// - /// This must be the last operation executed before returning execution back to the caller. - pub fn return_data(&self, data: V) - where - V: scale::Encode, - { - env::return_data::(data) - } - - /// Prints the given content. - /// - /// # Note - /// - /// Only usable in development (`--dev`) chains. - pub fn println(&self, content: &str) { - T::println(content) - } - - /// Deposits raw event data through the Contracts module. - pub fn deposit_raw_event(&self, topics: &[T::Hash], event: &[u8]) { - T::deposit_raw_event(topics, event) - } - - /// Returns the random seed from the latest block. - pub fn random_seed(&self) -> T::Hash { - T::random_seed() - } - - /// Returns the timestamp of the latest block. - pub fn now(&self) -> T::Moment { - T::now() - } - - /// Returns the latest block number. - pub fn block_number(&self) -> T::BlockNumber { - T::block_number() - } - - /// Dispatches a call into the runtime. - pub fn dispatch_call(&self, call: C) - where - C: Into, - { - T::dispatch_raw_call(call.into().encode().as_slice()) - } - - /// Calls a remote smart contract without returning data. - pub fn call_invoke( - &mut self, - callee: T::AccountId, - gas: u64, - value: T::Balance, - input_data: &[u8], - ) -> Result<(), CallError> { - T::call_invoke(callee, gas, value, input_data) - } - - /// Calls a remote smart contract with returning encoded data. - pub fn call_evaluate( - &mut self, - callee: T::AccountId, - gas: u64, - value: T::Balance, - input_data: &[u8], - ) -> Result { - T::call_evaluate(callee, gas, value, input_data) - } -} diff --git a/model/src/lib.rs b/model/src/lib.rs deleted file mode 100644 index f811d75af10..00000000000 --- a/model/src/lib.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2018-2019 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. - -#![cfg_attr(not(feature = "std"), no_std)] -#![feature(const_fn)] -#![deny( - bad_style, - const_err, - dead_code, - improper_ctypes, - non_shorthand_field_patterns, - no_mangle_generic_items, - overflowing_literals, - path_statements, - patterns_in_fns_without_body, - private_in_public, - unconditional_recursion, - unused, - unused_allocation, - unused_comparisons, - unused_parens, - while_true, - trivial_casts, - trivial_numeric_casts, - unused_extern_crates, - unused_qualifications, - unused_results -)] - -#[macro_use] -mod state; - -#[macro_use] -mod msg; - -mod contract; -mod exec_env; -mod msg_handler; - -pub use crate::{ - contract::{ - Contract, - ContractDecl, - ContractInstance, - DeployHandler, - EmptyContractState, - NoDeployArgs, - TestableContract, - }, - exec_env::{ - EnvHandler, - ExecutionEnv, - }, - msg::Message, - msg_handler::{ - CallData, - Error, - HandleCall, - MessageHandler, - MessageHandlerMut, - MessageHandlerSelector, - RawMessageHandler, - RawMessageHandlerMut, - Result, - UnreachableMessageHandler, - }, - state::ContractState, -}; diff --git a/model/src/msg.rs b/model/src/msg.rs deleted file mode 100644 index 81848b90302..00000000000 --- a/model/src/msg.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2018-2019 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::msg_handler::MessageHandlerSelector; - -/// A message with an expected input type and output (result) type. -pub trait Message { - /// The expected input type, also known as parameter types. - type Input: scale::Decode; - - /// The output of the message, also known as return type. - type Output: scale::Encode + 'static; - - /// The user provided message selector. - /// - /// This identifier must be unique for every message. - const ID: MessageHandlerSelector; - - /// The name of the message. - /// - /// # Note - /// - /// This must be a valid Rust identifier. - const NAME: &'static str; -} - -/// Defines messages for contracts with less boilerplate code. -#[macro_export] -macro_rules! messages { - // There are three macros to handle the different cases of - // `$prefix => Foo(maybe_args, …) -> maybe_return_type;` - // where `$prefix` can be either `[a, b, c, d]` or `[a; 4]`. - - // Matches `[a, b, c, d] => Foo(maybe_args, …) -> return type;`. - ( - $( #[$msg_meta:meta] )* - [ $a:literal, $b:literal, $c:literal, $d:literal $(,)? ] => $msg_name:ident ( - $( $param_name:ident : $param_ty:ty ),* - ) -> $ret_ty:ty ; - $($rest:tt)* - ) => { - $( #[$msg_meta] )* - #[derive(Copy, Clone)] - pub(crate) struct $msg_name; - - impl $crate::Message for $msg_name { - type Input = ($($param_ty),*); - type Output = $ret_ty; - - const ID: $crate::MessageHandlerSelector = - $crate::MessageHandlerSelector::new([$a, $b, $c, $d]); - const NAME: &'static str = stringify!($msg_name); - } - - messages!($($rest)*); - }; - - // Matches `[a, b, c, d] => Foo(maybe_args, …);` (no return type). - ( - $( #[$msg_meta:meta] )* - [ $a:literal, $b:literal, $c:literal, $d:literal $(,)? ] => $msg_name:ident ( - $( $param_name:ident : $param_ty:ty ),* - ) ; - $($rest:tt)* - ) => { - messages!( - $( #[$msg_meta] )* - [$a, $b, $c, $d] => $msg_name ( - $( $param_name : $param_ty ),* - ) -> (); - $($rest)* - ); - }; - - // Matches - // * `[a; 4] => Foo(maybe_args, …) -> return type;` and - // * `[a; 4] => Foo(maybe_args, …);` - ( - $( #[$msg_meta:meta] )* - [ $a:literal; 4 $(,)? ] => $msg_name:ident ( - $( $param_name:ident : $param_ty:ty ),* - ) $(-> $maybe_ret:tt)*; - $($rest:tt)* - ) => { - messages!( - $( #[$msg_meta] )* - [$a, $a, $a, $a] => $msg_name ( - $( $param_name : $param_ty ),* - ) $(-> $maybe_ret)* ; - $($rest)* - ); - }; - - () => {}; -} diff --git a/model/src/msg_handler.rs b/model/src/msg_handler.rs deleted file mode 100644 index 55876431a87..00000000000 --- a/model/src/msg_handler.rs +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright 2018-2019 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 core::{ - marker::PhantomData, - result::Result as CoreResult, -}; - -use ink_core::env; -use ink_prelude::vec::Vec; -use scale::Decode; - -use crate::{ - exec_env::ExecutionEnv, - msg::Message, - state::ContractState, -}; - -/// A raw read-only message handler for the given message and state. -/// -/// # Note -/// -/// - Read-only message handlers cannot mutate contract state. -/// - Requires `Msg` to impl `Message` and `State` to impl `ContractState`. -pub type RawMessageHandler = - fn(&ExecutionEnv, ::Input) -> ::Output; - -/// A raw mutable message handler for the given message and state. -/// -/// # Note -/// -/// - Mutable message handlers may mutate contract state. -/// - Requires `Msg` to impl `Message` and `State` to impl `ContractState`. -pub type RawMessageHandlerMut = fn( - &mut ExecutionEnv, - ::Input, -) -> ::Output; - -/// The raw data with which a contract is being called. -pub struct CallData { - /// The decoded message selector. - selector: MessageHandlerSelector, - /// The raw undecoded parameter bytes. - raw_params: Vec, -} - -impl Decode for CallData { - fn decode(input: &mut I) -> CoreResult { - let selector = MessageHandlerSelector::decode(input)?; - let mut param_buf = Vec::new(); - while let Ok(byte) = input.read_byte() { - param_buf.push(byte) - } - Ok(Self { - selector, - raw_params: param_buf, - }) - } -} - -impl CallData { - /// Returns the message handler selector part of this call data. - pub fn selector(&self) -> MessageHandlerSelector { - self.selector - } - - /// Returns the actual call data in binary format. - pub fn params(&self) -> &[u8] { - self.raw_params.as_slice() - } - - /// Creates a proper call data from a message and its required input. - /// - /// # Note - /// - /// This should normally only be needed in test code if a user - /// wants to test the handling of a specific message. - pub fn from_msg(args: ::Input) -> Self - where - Msg: Message, - ::Input: scale::Encode, - { - use scale::Encode; - Self { - selector: ::ID, - raw_params: args.encode(), - } - } -} - -/// A hash to identify a called function. -#[derive(Copy, Clone, PartialEq, Eq, Decode)] -pub struct MessageHandlerSelector([u8; 4]); - -impl MessageHandlerSelector { - /// Creates a new message handler selector from the given value. - pub const fn new(raw: [u8; 4]) -> Self { - Self(raw) - } -} - -/// A read-only message handler. -/// -/// Read-only message handlers cannot mutate contract state. -pub struct MessageHandler -where - Msg: Message, - State: ContractState, - Env: env::Env, -{ - /// Required in order to trick Rust into thinking that it actually owns a message. - /// - /// However, in general message types are zero-sized-types (ZST). - msg_marker: PhantomData, - /// The actual mutable handler for the message and state. - raw_handler: RawMessageHandler, -} - -impl MessageHandler -where - Msg: Message, - State: ContractState, - Env: env::Env, -{ - /// Returns the associated handler selector. - pub const fn selector() -> MessageHandlerSelector { - ::ID - } -} - -impl Copy for MessageHandler -where - Msg: Message, - State: ContractState, - Env: env::Env, -{ -} - -impl Clone for MessageHandler -where - Msg: Message, - State: ContractState, - Env: env::Env, -{ - fn clone(&self) -> Self { - Self { - msg_marker: self.msg_marker, - raw_handler: self.raw_handler, - } - } -} - -impl MessageHandler -where - Msg: Message, - State: ContractState, - Env: env::Env, -{ - /// Constructs a message handler from its raw counterpart. - pub const fn from_raw(raw_handler: RawMessageHandler) -> Self { - Self { - msg_marker: PhantomData, - raw_handler, - } - } -} - -/// A mutable message handler. -/// -/// Mutable message handlers may mutate contract state. -/// -/// # Note -/// -/// This is a thin wrapper around a raw message handler in order -/// to provide more type safety and better interfaces. -pub struct MessageHandlerMut -where - Msg: Message, - State: ContractState, - Env: env::Env, -{ - /// Required in order to trick Rust into thinking that it actually owns a message. - /// - /// However, in general message types are zero-sized-types (ZST). - msg_marker: PhantomData, - /// The actual read-only handler for the message and state. - raw_handler: RawMessageHandlerMut, -} - -impl Copy for MessageHandlerMut -where - Msg: Message, - State: ContractState, - Env: env::Env, -{ -} - -impl Clone for MessageHandlerMut -where - Msg: Message, - State: ContractState, - Env: env::Env, -{ - fn clone(&self) -> Self { - Self { - msg_marker: self.msg_marker, - raw_handler: self.raw_handler, - } - } -} - -impl MessageHandlerMut -where - Msg: Message, - State: ContractState, - Env: env::Env, -{ - /// Constructs a message handler from its raw counterpart. - pub const fn from_raw(raw_handler: RawMessageHandlerMut) -> Self { - Self { - msg_marker: PhantomData, - raw_handler, - } - } -} - -impl MessageHandlerMut -where - Msg: Message, - State: ContractState, - Env: env::Env, -{ - /// Returns the associated handler selector. - pub const fn selector() -> MessageHandlerSelector { - ::ID - } -} - -/// Errors the may occure during message handling. -pub enum Error { - /// Encountered when no function selector - /// matched the given input bytes representing - /// the function selector. - InvalidFunctionSelector, - /// Encountered when wrong parameters have - /// been given to a selected function. - InvalidArguments, -} - -impl Error { - /// Returns a short description of the error. - pub fn description(&self) -> &'static str { - match self { - Error::InvalidFunctionSelector => "encountered invalid message selector", - Error::InvalidArguments => { - "encountered invalid arguments for selected message" - } - } - } -} - -/// Results of message handling operations. -pub type Result = CoreResult; - -/// Types implementing this trait can handle contract calls. -pub trait HandleCall { - /// Handles the call and returns the encoded result. - fn handle_call( - &self, - env: &mut ExecutionEnv, - data: CallData, - ) -> Result>; -} - -/// A message handler that shall never handle a message. -/// -/// # Note -/// -/// Since this always comes last in a chain of message -/// handlers it can be used to check for incoming unknown -/// message selectors in call datas from the outside. -#[derive(Copy, Clone)] -pub struct UnreachableMessageHandler; - -impl HandleCall for UnreachableMessageHandler { - fn handle_call( - &self, - _env: &mut ExecutionEnv, - _data: CallData, - ) -> Result> { - Err(Error::InvalidFunctionSelector) - } -} - -macro_rules! impl_handle_call_for_chain { - ( $msg_handler_kind:ident, requires_flushing: $requires_flushing:literal ) => { - impl HandleCall - for $msg_handler_kind - where - Msg: Message, - ::Output: scale::Encode, - State: ContractState, - Env: env::Env, - { - fn handle_call( - &self, - env: &mut ExecutionEnv, - data: CallData, - ) -> Result> { - let args = ::Input::decode(&mut &data.params()[..]) - .map_err(|_| Error::InvalidArguments)?; - let result = (self.raw_handler)(env, args); - if $requires_flushing { - env.state.flush() - } - use scale::Encode; - Ok(result.encode()) - } - } - - impl HandleCall - for ($msg_handler_kind, Rest) - where - Msg: Message, - ::Output: 'static, - State: ContractState, - Env: env::Env, - Rest: HandleCall, - { - fn handle_call( - &self, - env: &mut ExecutionEnv, - data: CallData, - ) -> Result> { - let (handler, rest) = self; - if $msg_handler_kind::::selector() == data.selector() { - handler.handle_call(env, data) - } else { - rest.handle_call(env, data) - } - } - } - }; -} - -impl_handle_call_for_chain!(MessageHandler, requires_flushing: false); -impl_handle_call_for_chain!(MessageHandlerMut, requires_flushing: true); diff --git a/model/src/state.rs b/model/src/state.rs deleted file mode 100644 index 72fa19e9a02..00000000000 --- a/model/src/state.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2018-2019 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_core::storage::{ - alloc::{ - AllocateUsing, - Initialize, - }, - Flush, -}; - -/// Types implementing this type can be used as contract state. -pub trait ContractState: AllocateUsing + Initialize + Flush { - /// The name of the contract state. - /// - /// # Note - /// - /// - This must be a valid Rust identifier. - /// - Normally this reflects the name of the contract. - // const NAME: &'static str; - const NAME: &'static str; -} - -/// Define contract state with less boilerplate code. -#[macro_export] -macro_rules! state { - ( - $( #[$state_meta:meta] )* - $vis:vis struct $state_name:ident { - $( - $( #[$field_meta:meta] )* - $field_name:ident : $field_ty:ty , - )* - } - ) => { - $( #[$state_meta] )* - $vis struct $state_name { - $( - $( #[$field_meta] )* - $field_name : $field_ty - ),* - } - - impl ink_core::storage::Flush for $state_name { - fn flush(&mut self) { - $( - self.$field_name.flush() - );* - } - } - - impl ink_core::storage::alloc::AllocateUsing for $state_name { - unsafe fn allocate_using(alloc: &mut A) -> Self - where - A: ink_core::storage::alloc::Allocate, - { - use ink_core::storage::alloc::AllocateUsing; - Self { - $( - $field_name : AllocateUsing::allocate_using(alloc) - ),* - } - } - } - - impl ink_core::storage::alloc::Initialize for $state_name { - type Args = (); - - #[inline(always)] - fn default_value() -> Option { - // With this we can also default initialize storage state structs. - Some(()) - } - - fn initialize(&mut self, args: Self::Args) { - $( - self.$field_name.try_default_initialize(); - )* - } - } - - impl $crate::ContractState for $state_name { - const NAME: &'static str = stringify!($state_name); - } - }; - ( - $( #[$state_meta:meta] )* - $vis:vis struct $state_name:ident { - $( - $( #[$field_meta:meta] )* - $field_name:ident : $field_ty:ty - ),* - } - ) => { - $crate::state! { - $vis struct $state_name { - $( - $( #[$field_meta] )* - $field_name : $field_ty , - )* - } - } - }; -} diff --git a/model/tests/incrementer.rs b/model/tests/incrementer.rs deleted file mode 100644 index 2edd2424f88..00000000000 --- a/model/tests/incrementer.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2018-2019 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_core::{ - env::{ - ContractEnv, - DefaultSrmlTypes, - }, - storage, -}; -use ink_model::{ - messages, - state, - ContractDecl, - TestableContract, -}; - -state! { - /// A simple contract having just one value that can be incremented and returned. - struct Adder { - /// The simple value on the contract storage. - val: storage::Value - } -} - -messages! { - /// Increases the storage value by the given amount. - [0u8; 4] => Inc(by: u32); - /// Returns the storage value. - [1, 0, 0, 0] => Get() -> u32; -} - -#[rustfmt::skip] -fn instantiate() -> impl TestableContract { - ContractDecl::using::>() - .on_deploy(|env, init_val| { - env.state.val.set(init_val) - }) - .on_msg_mut::(|env, by| { - env.state.val += by - }) - .on_msg::(|env, _| { - *env.state.val - }) - .instantiate() -} - -#[test] -fn inc_and_read() { - let mut contract = instantiate(); - contract.deploy(0_u32); - assert_eq!(contract.call::(()), 0_u32); - contract.call::(1); - assert_eq!(contract.call::(()), 1_u32); - contract.call::(41); - assert_eq!(contract.call::(()), 42_u32); -} diff --git a/model/tests/mod.rs b/model/tests/mod.rs deleted file mode 100644 index 76ded14909f..00000000000 --- a/model/tests/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2018-2019 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 incrementer; diff --git a/model/tests/noop.rs b/model/tests/noop.rs deleted file mode 100644 index 02e421c0c26..00000000000 --- a/model/tests/noop.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018-2019 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. - -#![allow(dead_code)] - -use ink_core::env::{ - ContractEnv, - DefaultSrmlTypes, -}; -use ink_model::{ - messages, - state, - Contract, - ContractDecl, - EnvHandler, -}; - -state! { - /// The simplest contract that can still be deployed and called. - struct Noop {} -} - -messages! { - /// Multiline comment - /// for a function - /// which does nothing. - [0u8; 4] => DoNothing(); -} - -impl Noop { - pub fn deploy(&mut self, _env: &mut EnvHandler>) {} - pub fn do_nothing(&self, _env: &EnvHandler>) {} -} - -#[rustfmt::skip] -fn instantiate() -> impl Contract { - ContractDecl::using::>() - .on_deploy(|env, ()| { - let (handler, state) = env.split_mut(); - state.deploy(handler) - }) - .on_msg::(|env, ()| { - let (handler, state) = env.split(); - state.do_nothing(handler) - }) - .instantiate() -}