From b6be91506cb69f3d0e85a138578db5b9ae3f136d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 18 Jan 2020 13:29:17 +0100 Subject: [PATCH 001/112] [core] initial implementation of env revision 3 --- core/Cargo.toml | 2 +- core/src/env3/api.rs | 359 ++++++++++++++++++++++++++++++ core/src/env3/backend.rs | 229 +++++++++++++++++++ core/src/env3/buffer.rs | 67 ++++++ core/src/env3/call/builder.rs | 190 ++++++++++++++++ core/src/env3/call/create.rs | 201 +++++++++++++++++ core/src/env3/call/mod.rs | 49 ++++ core/src/env3/call/utils.rs | 145 ++++++++++++ core/src/env3/error.rs | 25 +++ core/src/env3/mod.rs | 48 ++++ core/src/env3/off_chain/mod.rs | 13 ++ core/src/env3/on_chain/ext.rs | 255 +++++++++++++++++++++ core/src/env3/on_chain/impls.rs | 315 ++++++++++++++++++++++++++ core/src/env3/on_chain/mod.rs | 25 +++ core/src/env3/on_chain/retcode.rs | 38 ++++ core/src/env3/property.rs | 133 +++++++++++ core/src/env3/test.rs | 15 ++ core/src/env3/types.rs | 148 ++++++++++++ core/src/lib.rs | 1 + 19 files changed, 2257 insertions(+), 1 deletion(-) create mode 100644 core/src/env3/api.rs create mode 100644 core/src/env3/backend.rs create mode 100644 core/src/env3/buffer.rs create mode 100644 core/src/env3/call/builder.rs create mode 100644 core/src/env3/call/create.rs create mode 100644 core/src/env3/call/mod.rs create mode 100644 core/src/env3/call/utils.rs create mode 100644 core/src/env3/error.rs create mode 100644 core/src/env3/mod.rs create mode 100644 core/src/env3/off_chain/mod.rs create mode 100644 core/src/env3/on_chain/ext.rs create mode 100644 core/src/env3/on_chain/impls.rs create mode 100644 core/src/env3/on_chain/mod.rs create mode 100644 core/src/env3/on_chain/retcode.rs create mode 100644 core/src/env3/property.rs create mode 100644 core/src/env3/test.rs create mode 100644 core/src/env3/types.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 027992c873d..c3adaa3a209 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -23,7 +23,7 @@ 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" diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs new file mode 100644 index 00000000000..f02466eee05 --- /dev/null +++ b/core/src/env3/api.rs @@ -0,0 +1,359 @@ +//! The public raw interface towards the host Wasm engine. +//! +//! # Note +//! +//! Prefer using the dedicated `EnvAccess` and `EnvAccessMut` types in order +//! to interoperate with the environment as they already have their associated +//! environemntal types associated to them and provide additional safety in some +//! scenarios. + +use crate::{ + env3::{ + call::{ + CallData, + CallParams, + CreateParams, + ReturnType, + }, + 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, +{ + todo!() +} + +/// 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, +{ + todo!() +} + +/// Returns the current price for gas. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn gas_price() -> Result +where + T: EnvTypes, +{ + todo!() +} + +/// 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, +{ + todo!() +} + +/// Returns the current block time in milliseconds. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn now_in_ms() -> Result +where + T: EnvTypes, +{ + todo!() +} + +/// Returns the address of the executed contract. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn address() -> Result +where + T: EnvTypes, +{ + todo!() +} + +/// Returns the balance of the executed contract. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn balance() -> Result +where + T: EnvTypes, +{ + todo!() +} + +/// 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, +{ + todo!() +} + +/// Returns the current block number. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn block_number() -> Result +where + T: EnvTypes, +{ + todo!() +} + +/// Returns the minimum balance of the executed contract. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn minimum_balance() -> Result +where + T: EnvTypes, +{ + todo!() +} + +/// Emits an event with the given event data. +pub fn emit_event(event: Event) +where + T: EnvTypes, + Event: Topics + scale::Encode, +{ + todo!() +} + +/// Sets the rent allowance of the executed contract to the new value. +pub fn set_rent_allowance(new_value: T::Balance) +where + T: EnvTypes, +{ + todo!() +} + +/// Writes the value to the contract storage under the given key. +pub fn set_contract_storage(key: Key, value: &V) +where + V: scale::Encode, +{ + todo!() +} + +/// 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, +{ + todo!() +} + +/// Clears the contract's storage key entry. +pub fn clear_contract_storage(key: Key) { + todo!() +} + +/// 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. +/// - If given too few endowment. +pub fn invoke_contract(call_data: &CallParams) -> Result<()> +where + T: EnvTypes, +{ + todo!() +} + +/// 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 given too few endowment. +/// - If the returned value failed to decode properly. +pub fn eval_contract(call_data: &CallParams>) -> Result +where + T: EnvTypes, + R: scale::Decode, +{ + todo!() +} + +/// 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: &CreateParams) -> Result +where + T: EnvTypes, +{ + todo!() +} + +/// 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. +pub fn restore_contract( + account_id: T::AccountId, + code_hash: T::Hash, + rent_allowance: T::Balance, + filtered_keys: &[Key], +) where + T: EnvTypes +{ + todo!() +} + +/// 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 { + todo!() +} + +/// 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, +{ + todo!() +} + +/// Returns a random hash. +/// +/// # Note +/// +/// The subject buffer can be used to further randomize the hash. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn random(subject: &[u8]) -> Result +where + T: EnvTypes, +{ + todo!() +} + +/// Prints the given contents to the environmental log. +pub fn println(content: &str) { + todo!() +} + +/// 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(runtime_key: &[u8]) -> Result +where + R: scale::Decode, +{ + todo!() +} diff --git a/core/src/env3/backend.rs b/core/src/env3/backend.rs new file mode 100644 index 00000000000..d046ddc0502 --- /dev/null +++ b/core/src/env3/backend.rs @@ -0,0 +1,229 @@ +// 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::{ + env3::{ + call::{ + CallData, + CallParams, + CreateParams, + 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]) -> Result + 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 current block time in milliseconds. + fn now_in_ms(&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 executed contract. + fn minimum_balance(&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 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: &CreateParams, + ) -> 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/env3/buffer.rs b/core/src/env3/buffer.rs new file mode 100644 index 00000000000..bf18db66ba6 --- /dev/null +++ b/core/src/env3/buffer.rs @@ -0,0 +1,67 @@ +// 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()) + } + } +} diff --git a/core/src/env3/call/builder.rs b/core/src/env3/call/builder.rs new file mode 100644 index 00000000000..84f5f13a241 --- /dev/null +++ b/core/src/env3/call/builder.rs @@ -0,0 +1,190 @@ +// 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::env3::{ + call::{ + state, + CallData, + Selector, + }, + EnvTypes, + Result, +}; + +/// 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 T>); + +/// The final parameters to the cross-contract call. +pub struct CallParams +where + E: EnvTypes, +{ + /// The account ID of the to-be-called smart contract. + callee: 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 data respecting the ABI. + call_data: CallData, +} + +/// Builds up a call. +pub struct CallBuilder +where + E: EnvTypes, +{ + /// The current parameters that have been built up so far. + params: CallParams, + /// Seal state. + seal: PhantomData, +} + +impl CallParams +where + E: EnvTypes, +{ + /// The code hash of the contract. + pub fn callee(&self) -> &E::AccountId { + &self.callee + } + + /// The gas limit for the contract instantiation. + pub fn gas_limit(&self) -> u64 { + self.gas_limit + } + /// The endowment for the instantiated contract. + pub fn endowment(&self) -> &E::Balance { + &self.value + } + + /// The raw encoded input data. + pub fn input_data(&self) -> &CallData { + &self.call_data + } +} + +impl CallParams +where + E: EnvTypes, + E::Balance: Default, +{ + /// Creates the default set of parameters for the cross-contract call. + fn new(callee: E::AccountId, selector: Selector) -> Self { + Self { + callee, + gas_limit: 0, + value: E::Balance::default(), + return_type: PhantomData, + call_data: CallData::new(selector), + } + } + + /// Returns a builder for a cross-contract call that might return data. + pub fn eval( + callee: E::AccountId, + selector: Selector, + ) -> CallBuilder, state::Unsealed> { + CallBuilder { + params: CallParams::new(callee, selector), + seal: Default::default(), + } + } + + /// Returns a builder for a cross-contract call that cannot return data. + /// + /// Prefer this over [`CallParams::eval`] if possible since it is the more efficient operation. + pub fn invoke( + callee: E::AccountId, + selector: Selector, + ) -> CallBuilder { + CallBuilder { + params: CallParams::new(callee, selector), + seal: Default::default(), + } + } +} + +impl CallBuilder +where + E: EnvTypes, +{ + /// Sets the maximumly 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; + self + } +} + +impl CallBuilder +where + E: EnvTypes, +{ + /// Pushes an argument to the inputs of the call. + pub fn push_arg(mut self, arg: &A) -> Self + where + A: scale::Encode, + { + self.params.call_data.push_arg(arg); + self + } + + /// Seals the call builder to prevent further arguments. + pub fn seal(self) -> CallBuilder { + CallBuilder { + params: self.params, + seal: Default::default(), + } + } +} + +impl CallBuilder, Seal> +where + E: EnvTypes, + R: scale::Decode, +{ + /// Fires the call to the remote smart contract. + /// Returns the returned data back to the caller. + pub fn fire(self) -> Result + where + R: scale::Decode, + { + // E::eval_contract(&mut Vec::new(), &self.params) + todo!() + } +} + +impl CallBuilder +where + E: EnvTypes, +{ + /// Fires the cross-call to the smart contract. + pub fn fire(self) -> Result<()> { + // E::invoke_contract(&mut Vec::new(), &self.params) + todo!() + } +} diff --git a/core/src/env3/call/create.rs b/core/src/env3/call/create.rs new file mode 100644 index 00000000000..d650785b4db --- /dev/null +++ b/core/src/env3/call/create.rs @@ -0,0 +1,201 @@ +// 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::env3::{ + call::{ + CallData, + Selector, + }, + EnvTypes, + Result, +}; + +pub mod state { + pub use crate::env2::call::state::{ + Sealed, + Unsealed, + }; + + /// Type state to indicate that the `code_hash` for cross-contract + /// instantiation has already been provided. + pub enum CodeHashAssigned {} + /// Type state to indicate that the `code_hash` for cross-contract + /// instantitation has not yet been provided. + pub enum CodeHashUnassigned {} +} + +/// Contracts that can be contructed from an `AccountId` +/// +/// # Note +/// +/// This is needed because of conflicting implementations of `From for T` +/// in the generated code of `ink_lang`. +pub trait FromAccountId +where + T: EnvTypes, +{ + /// Creates the contract instance from the account ID of the already instantiated contract. + fn from_account_id(account_id: ::AccountId) -> Self; +} + +/// Builds up contract instantiations. +pub struct CreateParams +where + T: EnvTypes, +{ + /// The code hash of the created contract. + code_hash: T::Hash, + /// The maximum gas costs allowed for the instantiation. + gas_limit: u64, + /// The transferred value for the newly created contract. + value: T::Balance, + /// The input data for the instantation. + call_data: CallData, + /// The type of the instantiated contract. + contract_marker: PhantomData C>, +} + +/// Builds up contract instantiations. +pub struct CreateBuilder +where + T: EnvTypes, +{ + /// The parameters of the cross-contract instantiation. + params: CreateParams, + /// Seal state. + state: PhantomData (Seal, CodeHash)>, +} + +impl CreateParams +where + T: EnvTypes, +{ + /// The code hash of the contract. + pub fn code_hash(&self) -> &T::Hash { + &self.code_hash + } + + /// The gas limit for the contract instantiation. + pub fn gas_limit(&self) -> u64 { + self.gas_limit + } + /// The endowment for the instantiated contract. + pub fn endowment(&self) -> &T::Balance { + &self.value + } + + /// The raw encoded input data. + pub fn input_data(&self) -> &CallData { + &self.call_data + } +} + +impl CreateParams +where + 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(), + call_data: CallData::new(selector), + contract_marker: Default::default(), + } + } + + /// Creates a new create builder without setting any presets. + pub fn build( + selector: Selector, + ) -> CreateBuilder { + CreateBuilder { + params: CreateParams::new(selector), + state: Default::default(), + } + } +} + +impl CreateBuilder +where + T: EnvTypes, +{ + /// Sets the maximumly 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: T::Balance) -> Self { + self.params.value = value; + self + } +} + +impl CreateBuilder +where + T: EnvTypes, +{ + /// Using the given code hash. + pub fn using_code( + mut self, + code_hash: T::Hash, + ) -> CreateBuilder { + self.params.code_hash = code_hash; + CreateBuilder { + params: self.params, + state: Default::default(), + } + } +} + +impl CreateBuilder +where + T: EnvTypes, +{ + /// Pushes an argument to the inputs of the call. + pub fn push_arg(mut self, arg: &A) -> Self + where + A: scale::Encode, + { + self.params.call_data.push_arg(arg); + self + } + + /// Seals the create builder to prevent further arguments. + pub fn seal(self) -> CreateBuilder { + CreateBuilder { + params: self.params, + state: Default::default(), + } + } +} + +impl CreateBuilder +where + T: EnvTypes, + C: FromAccountId, +{ + /// Instantiates the contract and returns its account ID back to the caller. + pub fn create(self) -> Result { + // E::create_contract(&mut Vec::new(), &self.params) + // .map(FromAccountId::from_account_id) + todo!() + } +} diff --git a/core/src/env3/call/mod.rs b/core/src/env3/call/mod.rs new file mode 100644 index 00000000000..aacfec3c211 --- /dev/null +++ b/core/src/env3/call/mod.rs @@ -0,0 +1,49 @@ +// 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. + +//! Utilities to call or instantiate contracts on the chain. + +mod builder; +mod create; +mod utils; + +pub mod state { + pub use crate::env3::call::{ + create::state::{ + CodeHashAssigned, + CodeHashUnassigned, + }, + utils::seal::{ + Sealed, + Unsealed, + }, + }; +} + +pub use self::{ + builder::{ + CallBuilder, + CallParams, + ReturnType, + }, + create::{ + CreateBuilder, + CreateParams, + FromAccountId, + }, + utils::{ + CallData, + Selector, + }, +}; diff --git a/core/src/env3/call/utils.rs b/core/src/env3/call/utils.rs new file mode 100644 index 00000000000..8c3bb9fbb6d --- /dev/null +++ b/core/src/env3/call/utils.rs @@ -0,0 +1,145 @@ +// 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; +use ink_prelude::{ + vec, + vec::Vec, +}; + +/// Seals to guard pushing arguments to already satisfied parameter builders. +pub mod seal { + /// The call builder is sealed and won't accept further arguments. + pub enum Sealed {} + /// The call builder is unsealed and will accept further arguments. + pub enum Unsealed {} +} + +/// The function selector. +#[derive(Debug, Copy, Clone, PartialEq, Eq, From, scale::Decode, scale::Encode)] +pub struct Selector { + /// The 4 underlying bytes. + bytes: [u8; 4], +} + +impl<'a> From<&'a [u8]> for Selector { + /// Computes the selector from the given input bytes. + /// + /// # Note + /// + /// Normally this is invoked through `Selector::from_str`. + fn from(input: &'a [u8]) -> Self { + let keccak = ink_utils::hash::keccak256(input); + Self { + bytes: [keccak[0], keccak[1], keccak[2], keccak[3]], + } + } +} + +impl Selector { + /// Returns the selector for the given name. + #[allow(clippy::should_implement_trait)] + pub fn from_str(name: &str) -> Self { + From::from(name.as_bytes()) + } + + /// Creates a selector directly from 4 bytes. + pub const fn new(bytes: [u8; 4]) -> Self { + Self { bytes } + } + + /// Returns the underlying bytes of the selector. + pub const fn to_bytes(self) -> [u8; 4] { + self.bytes + } +} + +/// The raw ABI respecting input data to a call. +/// +/// # Note +/// +/// The first four bytes are the function selector and the rest are SCALE encoded inputs. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CallData { + /// Already encoded function selector and inputs. + /// + /// # Note + /// + /// Has the invariant of always holding at least 4 bytes (the selector). + bytes: Vec, +} + +impl CallData { + /// Creates new call ABI data for the given selector. + pub fn new(selector: Selector) -> Self { + let bytes = selector.to_bytes(); + Self { + bytes: vec![bytes[0], bytes[1], bytes[2], bytes[3]], + } + } + + /// Pushes the given argument onto the call ABI data in encoded form. + pub fn push_arg(&mut self, arg: &A) + where + A: scale::Encode, + { + arg.encode_to(&mut self.bytes) + } + + /// Returns the selector of `self`. + pub fn selector(&self) -> Selector { + debug_assert!(self.bytes.len() >= 4); + let bytes = [self.bytes[0], self.bytes[1], self.bytes[2], self.bytes[3]]; + bytes.into() + } + + /// Returns the underlying bytes of the encoded input parameters. + pub fn params(&self) -> &[u8] { + debug_assert!(self.bytes.len() >= 4); + &self.bytes[4..] + } + + /// Returns the underlying byte representation. + pub fn to_bytes(&self) -> &[u8] { + &self.bytes + } +} + +impl scale::Encode for CallData { + fn size_hint(&self) -> usize { + self.bytes.len() + } + + fn encode_to(&self, dest: &mut T) { + dest.write(self.bytes.as_slice()); + } +} + +impl scale::Decode for CallData { + fn decode( + input: &mut I, + ) -> core::result::Result { + let remaining_len = input.remaining_len().unwrap_or(None).unwrap_or(0); + let mut bytes = Vec::with_capacity(remaining_len); + while let Ok(byte) = input.read_byte() { + bytes.push(byte); + } + if bytes.len() < 4 { + return Err(scale::Error::from( + "require at least 4 bytes for input data", + )) + } + Ok(Self { bytes }) + } +} diff --git a/core/src/env3/error.rs b/core/src/env3/error.rs new file mode 100644 index 00000000000..b877343724a --- /dev/null +++ b/core/src/env3/error.rs @@ -0,0 +1,25 @@ +// 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, Display}; + +/// Errors that can be encountered upon environmental interaction. +#[derive(From, Display)] +pub enum EnvError { + #[display(msg = "error upon decoding: {:?}", self.0)] + Decode(scale::Error), +} + +/// 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..343216f445f --- /dev/null +++ b/core/src/env3/mod.rs @@ -0,0 +1,48 @@ +// 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. + +//! 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; +mod buffer; +pub mod call; +mod error; +mod off_chain; +mod on_chain; +pub(self) mod property; +#[cfg(test)] +pub mod test; +mod types; + +pub use self::{ + api::*, + error::{ + EnvError, + Result, + }, + types::{ + DefaultEnvTypes, + EnvTypes, + Topics, + }, +}; + +pub(crate) use self::backend::{ + Env, + TypedEnv, +}; diff --git a/core/src/env3/off_chain/mod.rs b/core/src/env3/off_chain/mod.rs new file mode 100644 index 00000000000..07627650b1d --- /dev/null +++ b/core/src/env3/off_chain/mod.rs @@ -0,0 +1,13 @@ +// 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. diff --git a/core/src/env3/on_chain/ext.rs b/core/src/env3/on_chain/ext.rs new file mode 100644 index 00000000000..b5106300cbf --- /dev/null +++ b/core/src/env3/on_chain/ext.rs @@ -0,0 +1,255 @@ +// 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. + +use super::RetCode; +use crate::storage::Key; + +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); + } +} + +pub fn create( + code_hash: &[u8], + gas_limit: u64, + value: &[u8], + create_data: &[u8], +) -> RetCode { + unsafe { + sys::ext_instantiate( + code_hash.as_ptr() as u32, + code_hash.len() as u32, + gas_limit, + value.as_ptr() as u32, + value.len() as u32, + create_data.as_ptr() as u32, + create_data.len() as u32, + ) + } + .into() +} + +pub fn call(callee: &[u8], gas_limit: u64, value: &[u8], call_data: &[u8]) -> RetCode { + unsafe { + sys::ext_call( + callee.as_ptr() as u32, + callee.len() as u32, + gas_limit, + value.as_ptr() as u32, + value.len() as u32, + call_data.as_ptr() as u32, + call_data.len() as u32, + ) + } + .into() +} + +pub fn deposit_event(topics: &[u8], 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, + ) + } +} + +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 clear_storage(key: &[u8]) { + unsafe { sys::ext_set_storage(key.as_ptr() as u32, 0, 0, 0) } +} + +pub fn get_storage(key: &[u8]) -> RetCode { + unsafe { sys::ext_get_storage(key.as_ptr() as u32) }.into() +} + +pub fn get_runtime_storage(runtime_key: &[u8]) -> RetCode { + unsafe { + sys::ext_get_runtime_storage( + runtime_key.as_ptr() as u32, + runtime_key.len() as u32, + ) + } + .into() +} + +/// 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( + account_id: &[u8], + code_hash: &[u8], + rent_allowance: &[u8], + filtered_keys: &[Key], +) { + unsafe { + sys::ext_restore_to( + 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, + rent_allowance.len() as u32, + filtered_keys.as_ptr() as u32, + filtered_keys.len() as u32, + ) + } +} + +pub fn dispatch_call(call: &[u8]) { + unsafe { sys::ext_dispatch_call(call.as_ptr() as u32, call.len() as u32) } +} + +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_write(src: &[u8]) -> RetCode { + unsafe { sys::ext_scratch_write(src.as_ptr() as u32, src.len() as u32) }; + RetCode::success() +} + +macro_rules! impl_ext_wrapper_for { + ( $( ($name:ident => $ext_name:ident), )* ) => { + $( + pub fn $name() { + unsafe { + sys::$ext_name() + } + } + )* + } +} +impl_ext_wrapper_for! { + (caller => ext_caller), + (block_number => ext_block_number), + (address => ext_address), + (balance => ext_balance), + (gas_price => ext_gas_price), + (gas_left => ext_gas_left), + (value_transferred => ext_value_transferred), + (now => ext_now), + (rent_allowance => ext_rent_allowance), + (minimum_balance => ext_minimum_balance), +} + +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]) { + unsafe { sys::ext_random_seed(subject.as_ptr() as u32, subject.len() as u32) } +} + +pub fn println(content: &str) { + let bytes = content.as_bytes(); + unsafe { sys::ext_println(bytes.as_ptr() as u32, bytes.len() as u32) } +} diff --git a/core/src/env3/on_chain/impls.rs b/core/src/env3/on_chain/impls.rs new file mode 100644 index 00000000000..44d4234f502 --- /dev/null +++ b/core/src/env3/on_chain/impls.rs @@ -0,0 +1,315 @@ +// 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::WasmEnv; +use crate::{ + env3::{ + call::{ + CallData, + CallParams, + CreateParams, + ReturnType, + }, + on_chain::ext, + property, + property::ReadProperty, + Env, + EnvTypes, + Result, + Topics, + TypedEnv, + }, + storage::Key, +}; + +impl WasmEnv { + /// 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.buffer.resize(req_len, Default::default()); + 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) -> core::result::Result + where + T: scale::Decode, + { + let req_len = self.read_scratch_buffer(); + scale::Decode::decode(&mut &self.buffer[0..req_len]) + } + + /// Encodes the value into the contract-side scratch buffer. + fn encode_into_buffer(&mut self, value: T) + where + T: scale::Encode, + { + 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 + P: ReadProperty, + { + ext_fn(); + self.decode_scratch_buffer().map_err(Into::into) + } + + fn invoke_contract_impl( + &mut self, + call_params: &CallParams, + ) -> Result<()> + where + T: EnvTypes, + { + // Reset the contract-side buffer to append onto clean slate. + self.buffer.clear(); + // 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.endowment()); + 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. + let ret = ext::call(callee, call_params.gas_limit(), endowment, call_data); + if !ret.is_success() { + // Return an error if `ret` refers to an error code. + todo!() + } + Ok(()) + } +} + +impl Env for WasmEnv { + 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, + { + if !ext::get_storage(key.as_bytes()).is_success() { + todo!() + } + 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]) -> Result + where + R: scale::Decode, + { + if !ext::get_runtime_storage(runtime_key).is_success() { + todo!() + } + 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 WasmEnv { + 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 now_in_ms(&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 emit_event(&mut self, event: Event) + where + T: EnvTypes, + Event: Topics + scale::Encode, + { + // Reset the contract-side buffer to append onto clean slate. + self.buffer.clear(); + // 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_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: &CreateParams, + ) -> Result + where + T: EnvTypes, + { + // Reset the contract-side buffer to append onto clean slate. + self.buffer.clear(); + // 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. + let ret = ext::create(code_hash, params.gas_limit(), endowment, create_data); + if !ret.is_success() { + // Return an error if `ret` refers to an error code. + todo!() + } + // 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.buffer.clear(); + // 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/on_chain/mod.rs b/core/src/env3/on_chain/mod.rs new file mode 100644 index 00000000000..21c15073f7d --- /dev/null +++ b/core/src/env3/on_chain/mod.rs @@ -0,0 +1,25 @@ +// 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 ext; +mod impls; +mod retcode; + +pub(crate) use self::retcode::RetCode; + +/// The on-chain environment. +pub struct WasmEnv { + /// Encode & decode buffer for potentially reusing required dynamic allocations. + buffer: Vec, +} diff --git a/core/src/env3/on_chain/retcode.rs b/core/src/env3/on_chain/retcode.rs new file mode 100644 index 00000000000..622c4d840b4 --- /dev/null +++ b/core/src/env3/on_chain/retcode.rs @@ -0,0 +1,38 @@ +// 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/env3/property.rs b/core/src/env3/property.rs new file mode 100644 index 00000000000..d1f5ae2700d --- /dev/null +++ b/core/src/env3/property.rs @@ -0,0 +1,133 @@ +// 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::env3::{ + 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_read_property_for { + ( + $( #[$meta:meta] )* + struct $name:ident { read: Some<$in:ty> }, $($tt:tt)* + ) => { + $( #[$meta] )* + pub struct $name { marker: PhantomData E>, } + impl $crate::env3::property::Property for $name {} + impl $crate::env3::property::private::PropertySeal for $name {} + impl $crate::env3::property::ReadProperty for $name + where + E: EnvTypes, + { + type In = $in; + } + impl $crate::env3::property::private::ReadPropertySeal for $name {} + + impl_read_property_for! { $($tt)* } + }; + () => {}; +} + +impl_read_property_for! { + /// The caller of an executed contract. + struct Caller { read: Some }, + /// The transferred balance for the contract execution. + struct TransferredBalance { read: Some }, + /// The current gas price. + struct GasPrice { read: Some }, + /// The amount of gas left for the current contract execution. + struct GasLeft { read: Some }, + /// The block time in milli seconds. + struct NowInMs { read: Some }, + /// The account ID of the executed contract. + struct Address { read: Some }, + /// The balance of the executed contract. + struct Balance { read: Some }, + /// The current block number. + struct BlockNumber { read: Some }, + /// The minimum possible balance for a contract. + struct MinimumBalance { read: Some }, +} + +/// The input data for this contract execution. +pub struct Input; + +impl Property for Input {} +impl ReadProperty for Input { + type In = CallData; +} +impl private::PropertySeal for Input {} +impl private::ReadPropertySeal for Input {} + +/// The rent allowance of the executed contract. +pub struct RentAllowance { + marker: PhantomData T>, +} + +impl Property for RentAllowance {} +impl WriteProperty for RentAllowance +where + T: EnvTypes, +{ + type Out = T::Balance; +} +impl ReadProperty for RentAllowance +where + T: EnvTypes, +{ + type In = T::Balance; +} +impl private::PropertySeal for RentAllowance {} +impl private::WritePropertySeal for RentAllowance {} +impl private::ReadPropertySeal for RentAllowance {} diff --git a/core/src/env3/test.rs b/core/src/env3/test.rs new file mode 100644 index 00000000000..4705e50b7a1 --- /dev/null +++ b/core/src/env3/test.rs @@ -0,0 +1,15 @@ +// 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. + +//! Operations on the off-chain testing environment. diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs new file mode 100644 index 00000000000..040d31e3c60 --- /dev/null +++ b/core/src/env3/types.rs @@ -0,0 +1,148 @@ +// 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 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; + +/// 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; + /// The type of balances. + type Balance: 'static + scale::Codec + Clone + PartialEq + Eq; + /// The type of hash. + type Hash: 'static + scale::Codec + Clone + PartialEq + Eq; + /// The type of timestamps. + type Moment: 'static + scale::Codec + Clone + PartialEq + Eq; + /// The type of block number. + type BlockNumber: 'static + scale::Codec + Clone + PartialEq + Eq; + /// The type of a call into the runtime + type Call: 'static + scale::Encode; +} + +/// 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 SRML 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 Moment = Moment; + type BlockNumber = BlockNumber; + type Call = Call; +} + +/// The default balance type. +pub type Balance = u128; + +/// The default moment type. +pub type Moment = 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, Clone, PartialEq, Eq)] +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() + } +} + +/// 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. +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/lib.rs b/core/src/lib.rs index d685d5ca80b..7d60a9fbae1 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -52,6 +52,7 @@ 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. From 937ed698f72ccf92613b083eae921c7282d22258 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 18 Jan 2020 14:31:54 +0100 Subject: [PATCH 002/112] add engine abstraction layer and implement api functions --- core/src/env3/api.rs | 92 ++++++--- core/src/env3/engine/mod.rs | 38 ++++ core/src/env3/engine/off_chain/impls.rs | 179 ++++++++++++++++++ .../{on_chain => engine/off_chain}/mod.rs | 19 +- core/src/env3/{ => engine}/on_chain/ext.rs | 0 core/src/env3/{ => engine}/on_chain/impls.rs | 8 +- .../{off_chain => engine/on_chain}/mod.rs | 29 +++ .../src/env3/{ => engine}/on_chain/retcode.rs | 0 core/src/env3/error.rs | 2 +- core/src/env3/mod.rs | 5 +- 10 files changed, 331 insertions(+), 41 deletions(-) create mode 100644 core/src/env3/engine/mod.rs create mode 100644 core/src/env3/engine/off_chain/impls.rs rename core/src/env3/{on_chain => engine/off_chain}/mod.rs (74%) rename core/src/env3/{ => engine}/on_chain/ext.rs (100%) rename core/src/env3/{ => engine}/on_chain/impls.rs (99%) rename core/src/env3/{off_chain => engine/on_chain}/mod.rs (53%) rename core/src/env3/{ => engine}/on_chain/retcode.rs (100%) diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index f02466eee05..095948f02c9 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -9,12 +9,20 @@ use crate::{ env3::{ + backend::{ + Env, + TypedEnv, + }, call::{ CallData, CallParams, CreateParams, ReturnType, }, + engine::{ + Accessor, + Instance, + }, EnvTypes, Result, Topics, @@ -31,7 +39,7 @@ pub fn caller() -> Result where T: EnvTypes, { - todo!() + ::run(|instance| TypedEnv::caller::(instance)) } /// Returns the transferred balance for the contract execution. @@ -43,7 +51,7 @@ pub fn transferred_balance() -> Result where T: EnvTypes, { - todo!() + ::run(|instance| TypedEnv::transferred_balance::(instance)) } /// Returns the current price for gas. @@ -55,7 +63,7 @@ pub fn gas_price() -> Result where T: EnvTypes, { - todo!() + ::run(|instance| TypedEnv::gas_price::(instance)) } /// Returns the amount of gas left for the contract execution. @@ -67,7 +75,7 @@ pub fn gas_left() -> Result where T: EnvTypes, { - todo!() + ::run(|instance| TypedEnv::gas_left::(instance)) } /// Returns the current block time in milliseconds. @@ -79,7 +87,7 @@ pub fn now_in_ms() -> Result where T: EnvTypes, { - todo!() + ::run(|instance| TypedEnv::now_in_ms::(instance)) } /// Returns the address of the executed contract. @@ -91,7 +99,7 @@ pub fn address() -> Result where T: EnvTypes, { - todo!() + ::run(|instance| TypedEnv::address::(instance)) } /// Returns the balance of the executed contract. @@ -103,7 +111,7 @@ pub fn balance() -> Result where T: EnvTypes, { - todo!() + ::run(|instance| TypedEnv::balance::(instance)) } /// Returns the current rent allowance for the executed contract. @@ -115,7 +123,7 @@ pub fn rent_allowance() -> Result where T: EnvTypes, { - todo!() + ::run(|instance| TypedEnv::rent_allowance::(instance)) } /// Returns the current block number. @@ -127,7 +135,7 @@ pub fn block_number() -> Result where T: EnvTypes, { - todo!() + ::run(|instance| TypedEnv::block_number::(instance)) } /// Returns the minimum balance of the executed contract. @@ -139,7 +147,7 @@ pub fn minimum_balance() -> Result where T: EnvTypes, { - todo!() + ::run(|instance| TypedEnv::minimum_balance::(instance)) } /// Emits an event with the given event data. @@ -148,7 +156,9 @@ where T: EnvTypes, Event: Topics + scale::Encode, { - todo!() + ::run(|instance| { + TypedEnv::emit_event::(instance, event) + }) } /// Sets the rent allowance of the executed contract to the new value. @@ -156,7 +166,9 @@ pub fn set_rent_allowance(new_value: T::Balance) where T: EnvTypes, { - todo!() + ::run(|instance| { + TypedEnv::set_rent_allowance::(instance, new_value) + }) } /// Writes the value to the contract storage under the given key. @@ -164,7 +176,9 @@ pub fn set_contract_storage(key: Key, value: &V) where V: scale::Encode, { - todo!() + ::run(|instance| { + Env::set_contract_storage::(instance, key, value) + }) } /// Returns the value stored under the given key in the contract's storage. @@ -177,12 +191,12 @@ pub fn get_contract_storage(key: Key) -> Result where R: scale::Decode, { - todo!() + ::run(|instance| Env::get_contract_storage::(instance, key)) } /// Clears the contract's storage key entry. pub fn clear_contract_storage(key: Key) { - todo!() + ::run(|instance| Env::clear_contract_storage(instance, key)) } /// Invokes a contract message. @@ -195,11 +209,13 @@ pub fn clear_contract_storage(key: Key) { /// - 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(call_data: &CallParams) -> Result<()> +pub fn invoke_contract(params: &CallParams) -> Result<()> where T: EnvTypes, { - todo!() + ::run(|instance| { + TypedEnv::invoke_contract::(instance, params) + }) } /// Evaluates a contract message and returns its result. @@ -213,12 +229,14 @@ where /// - 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(call_data: &CallParams>) -> Result +pub fn eval_contract(params: &CallParams>) -> Result where T: EnvTypes, R: scale::Decode, { - todo!() + ::run(|instance| { + TypedEnv::eval_contract::(instance, params) + }) } /// Instantiates another contract. @@ -235,7 +253,9 @@ pub fn create_contract(params: &CreateParams) -> Result::run(|instance| { + TypedEnv::create_contract::(instance, params) + }) } /// Restores a smart contract tombstone. @@ -286,9 +306,17 @@ pub fn restore_contract( rent_allowance: T::Balance, filtered_keys: &[Key], ) where - T: EnvTypes + T: EnvTypes, { - todo!() + ::run(|instance| { + TypedEnv::restore_contract::( + instance, + account_id, + code_hash, + rent_allowance, + filtered_keys, + ) + }) } /// Returns the input to the executed contract. @@ -308,7 +336,9 @@ pub fn restore_contract( /// - This happens only if the host runtime provides less than 4 bytes for /// the function selector upon this query. pub fn input() -> Result { - todo!() + ::run(|instance| { + Env::input(instance) + }) } /// Returns the value back to the caller of the executed contract. @@ -321,7 +351,9 @@ pub fn output(return_value: &R) where R: scale::Encode, { - todo!() + ::run(|instance| { + Env::output::(instance, return_value) + }) } /// Returns a random hash. @@ -337,12 +369,16 @@ pub fn random(subject: &[u8]) -> Result where T: EnvTypes, { - todo!() + ::run(|instance| { + TypedEnv::random::(instance, subject) + }) } /// Prints the given contents to the environmental log. pub fn println(content: &str) { - todo!() + ::run(|instance| { + Env::println(instance, content) + }) } /// Returns the value from the *runtime* storage at the position of the key. @@ -355,5 +391,7 @@ pub fn get_runtime_storage(runtime_key: &[u8]) -> Result where R: scale::Decode, { - todo!() + ::run(|instance| { + Env::get_runtime_storage::(instance, runtime_key) + }) } diff --git a/core/src/env3/engine/mod.rs b/core/src/env3/engine/mod.rs new file mode 100644 index 00000000000..92695ed34d4 --- /dev/null +++ b/core/src/env3/engine/mod.rs @@ -0,0 +1,38 @@ +// 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 cfg_if::cfg_if; +use crate::env3::backend::{TypedEnv, Env}; + +pub trait Instance { + type Engine: Env + TypedEnv; + + fn run(f: F) -> R + where + F: FnOnce(&mut Self::Engine) -> R; +} + +cfg_if! { + if #[cfg(all(not(feature = "std"), target_arch = "wasm32-unknown"))] { + mod off_chain; + pub use self::off_chain::Accessor; + } else if #[cfg(feature = "std")] { + mod on_chain; + pub use self::on_chain::Accessor; + } else { + compile_error! { + "ink! only support compilation as `std` or `no_std` + `wasm32-unknown`" + } + } +} 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..ddbfc3a004d --- /dev/null +++ b/core/src/env3/engine/off_chain/impls.rs @@ -0,0 +1,179 @@ +// 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::TestEnv; +use crate::{ + env3::{ + call::{ + CallData, + CallParams, + CreateParams, + ReturnType, + }, + property, + property::ReadProperty, + Env, + EnvTypes, + Result, + Topics, + TypedEnv, + }, + storage::Key, +}; + +impl Env for TestEnv { + fn set_contract_storage(&mut self, key: Key, value: &V) + where + V: scale::Encode, + { + todo!() + } + + fn get_contract_storage(&mut self, key: Key) -> Result + where + R: scale::Decode, + { + todo!() + } + + fn clear_contract_storage(&mut self, key: Key) { + todo!() + } + + fn get_runtime_storage(&mut self, runtime_key: &[u8]) -> Result + where + R: scale::Decode, + { + todo!() + } + + fn input(&mut self) -> Result { + todo!() + } + + fn output(&mut self, return_value: &R) + where + R: scale::Encode, + { + todo!() + } + + fn println(&mut self, content: &str) { + println!("{}", content) + } +} + +impl TypedEnv for WasmEnv { + fn caller(&mut self) -> Result { + todo!() + } + + fn transferred_balance(&mut self) -> Result { + todo!() + } + + fn gas_price(&mut self) -> Result { + todo!() + } + + fn gas_left(&mut self) -> Result { + todo!() + } + + fn now_in_ms(&mut self) -> Result { + todo!() + } + + fn address(&mut self) -> Result { + todo!() + } + + fn balance(&mut self) -> Result { + todo!() + } + + fn rent_allowance(&mut self) -> Result { + todo!() + } + + fn block_number(&mut self) -> Result { + todo!() + } + + fn minimum_balance(&mut self) -> Result { + todo!() + } + + fn emit_event(&mut self, event: Event) + where + T: EnvTypes, + Event: Topics + scale::Encode, + { + todo!() + } + + fn set_rent_allowance(&mut self, new_value: T::Balance) + where + T: EnvTypes, + { + todo!() + } + + fn invoke_contract(&mut self, call_params: &CallParams) -> Result<()> + where + T: EnvTypes, + { + todo!() + } + + fn eval_contract( + &mut self, + call_params: &CallParams>, + ) -> Result + where + T: EnvTypes, + R: scale::Decode, + { + todo!() + } + + fn create_contract( + &mut self, + params: &CreateParams, + ) -> Result + where + T: EnvTypes, + { + todo!() + } + + fn restore_contract( + &mut self, + account_id: T::AccountId, + code_hash: T::Hash, + rent_allowance: T::Balance, + filtered_keys: &[Key], + ) where + T: EnvTypes, + { + todo!() + } + + fn random(&mut self, subject: &[u8]) -> Result + where + T: EnvTypes, + { + todo!() + } +} diff --git a/core/src/env3/on_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs similarity index 74% rename from core/src/env3/on_chain/mod.rs rename to core/src/env3/engine/off_chain/mod.rs index 21c15073f7d..8ffb210b4ec 100644 --- a/core/src/env3/on_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -12,14 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod ext; mod impls; -mod retcode; -pub(crate) use self::retcode::RetCode; +pub enum Accessor {} -/// The on-chain environment. -pub struct WasmEnv { - /// Encode & decode buffer for potentially reusing required dynamic allocations. - buffer: Vec, +pub struct TestEnv {} + +impl Instance for Accessor { + type Engine = TestEnv; + + fn run(f: F) -> R + where + F: FnOnce(&mut Self::Engine) -> R + { + todo!() + } } diff --git a/core/src/env3/on_chain/ext.rs b/core/src/env3/engine/on_chain/ext.rs similarity index 100% rename from core/src/env3/on_chain/ext.rs rename to core/src/env3/engine/on_chain/ext.rs diff --git a/core/src/env3/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs similarity index 99% rename from core/src/env3/on_chain/impls.rs rename to core/src/env3/engine/on_chain/impls.rs index 44d4234f502..6978bf4bcd2 100644 --- a/core/src/env3/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::WasmEnv; +use super::{ + ext, + WasmEnv, +}; use crate::{ env3::{ call::{ @@ -21,7 +24,6 @@ use crate::{ CreateParams, ReturnType, }, - on_chain::ext, property, property::ReadProperty, Env, @@ -287,7 +289,7 @@ impl TypedEnv for WasmEnv { rent_allowance: T::Balance, filtered_keys: &[Key], ) where - T: EnvTypes + T: EnvTypes, { // Reset the contract-side buffer to append onto clean slate. self.buffer.clear(); diff --git a/core/src/env3/off_chain/mod.rs b/core/src/env3/engine/on_chain/mod.rs similarity index 53% rename from core/src/env3/off_chain/mod.rs rename to core/src/env3/engine/on_chain/mod.rs index 07627650b1d..716cd50f941 100644 --- a/core/src/env3/off_chain/mod.rs +++ b/core/src/env3/engine/on_chain/mod.rs @@ -11,3 +11,32 @@ // 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 ext; +mod impls; +mod retcode; + +use super::Instance; + +pub(crate) use self::retcode::RetCode; + +/// The on-chain environment. +pub struct WasmEnv { + /// Encode & decode buffer for potentially reusing required dynamic allocations. + buffer: Vec, +} + +static mut INSTANCE: WasmEnv = WasmEnv { buffer: Vec::new() }; + +pub enum Accessor {} + +impl Instance for Accessor { + type Engine = WasmEnv; + + fn run(f: F) -> R + where + F: FnOnce(&mut Self::Engine) -> R + { + f(unsafe { &mut INSTANCE }) + } +} diff --git a/core/src/env3/on_chain/retcode.rs b/core/src/env3/engine/on_chain/retcode.rs similarity index 100% rename from core/src/env3/on_chain/retcode.rs rename to core/src/env3/engine/on_chain/retcode.rs diff --git a/core/src/env3/error.rs b/core/src/env3/error.rs index b877343724a..d2ea3e2c36c 100644 --- a/core/src/env3/error.rs +++ b/core/src/env3/error.rs @@ -17,7 +17,7 @@ use derive_more::{From, Display}; /// Errors that can be encountered upon environmental interaction. #[derive(From, Display)] pub enum EnvError { - #[display(msg = "error upon decoding: {:?}", self.0)] + #[display(msg = "error upon decoding")] Decode(scale::Error), } diff --git a/core/src/env3/mod.rs b/core/src/env3/mod.rs index 343216f445f..39fa37dd808 100644 --- a/core/src/env3/mod.rs +++ b/core/src/env3/mod.rs @@ -22,10 +22,9 @@ mod backend; mod buffer; pub mod call; mod error; -mod off_chain; -mod on_chain; +mod engine; pub(self) mod property; -#[cfg(test)] +#[cfg(any(test, rustdoc))] pub mod test; mod types; From 20923a912ea9cbafa2f067269a03f19fd2860383 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 18 Jan 2020 14:38:12 +0100 Subject: [PATCH 003/112] add support for dispatch_call to env3 --- core/src/env3/backend.rs | 5 +++++ core/src/env3/engine/on_chain/impls.rs | 9 +++++++++ core/src/env3/mod.rs | 13 ++++++------- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/core/src/env3/backend.rs b/core/src/env3/backend.rs index d046ddc0502..33882c85596 100644 --- a/core/src/env3/backend.rs +++ b/core/src/env3/backend.rs @@ -126,6 +126,11 @@ pub trait TypedEnv: Env { 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 diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index 6978bf4bcd2..80dfe5a4df8 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -233,6 +233,15 @@ impl TypedEnv for WasmEnv { 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, diff --git a/core/src/env3/mod.rs b/core/src/env3/mod.rs index 39fa37dd808..cc8617b9c13 100644 --- a/core/src/env3/mod.rs +++ b/core/src/env3/mod.rs @@ -21,13 +21,17 @@ mod api; mod backend; mod buffer; pub mod call; -mod error; mod engine; -pub(self) mod property; +mod error; +mod property; #[cfg(any(test, rustdoc))] pub mod test; mod types; +use self::backend::{ + Env, + TypedEnv, +}; pub use self::{ api::*, error::{ @@ -40,8 +44,3 @@ pub use self::{ Topics, }, }; - -pub(crate) use self::backend::{ - Env, - TypedEnv, -}; From 9f729a2097a045f825143e138ba7e6d07dd4b3bf Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 18 Jan 2020 14:40:38 +0100 Subject: [PATCH 004/112] add missing invoke_runtime to api.rs --- core/src/env3/api.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index 095948f02c9..309ee2de7a3 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -199,6 +199,25 @@ pub fn clear_contract_storage(key: Key) { ::run(|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, +{ + ::run(|instance| { + TypedEnv::invoke_runtime::(instance, params) + }) +} + /// Invokes a contract message. /// /// # Errors From def6ec6350bc9dd9ce90146a7728ad716bc411f8 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 18 Jan 2020 17:45:10 +0100 Subject: [PATCH 005/112] [core] fix restore_contract docs --- core/src/env3/api.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index 309ee2de7a3..3b42c88c6ec 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -277,15 +277,15 @@ where }) } -/// Restores a smart contract tombstone. +/// Restores a smart contract in tombstone state. /// /// # 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 +/// - `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 ignored for the tombstone hash +/// - `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. /// From 0260146ff91ad9a76b47316d7055f06b8abcb28c Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 18 Jan 2020 17:45:40 +0100 Subject: [PATCH 006/112] [core] initial implementation of off-chain environment --- core/src/env3/engine/mod.rs | 8 ++++---- core/src/env3/engine/off_chain/impls.rs | 11 ++++++++--- core/src/env3/engine/off_chain/mod.rs | 14 ++++++++++++-- core/src/env3/engine/on_chain/mod.rs | 4 ++-- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/core/src/env3/engine/mod.rs b/core/src/env3/engine/mod.rs index 92695ed34d4..eb037b12a1e 100644 --- a/core/src/env3/engine/mod.rs +++ b/core/src/env3/engine/mod.rs @@ -24,12 +24,12 @@ pub trait Instance { } cfg_if! { - if #[cfg(all(not(feature = "std"), target_arch = "wasm32-unknown"))] { - mod off_chain; - pub use self::off_chain::Accessor; - } else if #[cfg(feature = "std")] { + if #[cfg(all(not(feature = "std"), target_arch = "wasm32"))] { mod on_chain; pub use self::on_chain::Accessor; + } else if #[cfg(feature = "std")] { + mod off_chain; + pub use self::off_chain::Accessor; } else { compile_error! { "ink! only support compilation as `std` or `no_std` + `wasm32-unknown`" diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index ddbfc3a004d..7321e8d6c31 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -21,8 +21,6 @@ use crate::{ CreateParams, ReturnType, }, - property, - property::ReadProperty, Env, EnvTypes, Result, @@ -74,7 +72,7 @@ impl Env for TestEnv { } } -impl TypedEnv for WasmEnv { +impl TypedEnv for TestEnv { fn caller(&mut self) -> Result { todo!() } @@ -137,6 +135,13 @@ impl TypedEnv for WasmEnv { todo!() } + fn invoke_runtime(&mut self, params: &T::Call) -> Result<()> + where + T: EnvTypes, + { + todo!() + } + fn eval_contract( &mut self, call_params: &CallParams>, diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 8ffb210b4ec..f136dededf5 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -14,6 +14,9 @@ mod impls; +use super::Instance; +use core::cell::RefCell; + pub enum Accessor {} pub struct TestEnv {} @@ -23,8 +26,15 @@ impl Instance for Accessor { fn run(f: F) -> R where - F: FnOnce(&mut Self::Engine) -> R + F: FnOnce(&mut Self::Engine) -> R, { - todo!() + thread_local!( + static INSTANCE: RefCell = RefCell::new( + TestEnv {} + ) + ); + INSTANCE.with(|instance| { + f(&mut instance.borrow_mut()) + }) } } diff --git a/core/src/env3/engine/on_chain/mod.rs b/core/src/env3/engine/on_chain/mod.rs index 716cd50f941..114874734f8 100644 --- a/core/src/env3/engine/on_chain/mod.rs +++ b/core/src/env3/engine/on_chain/mod.rs @@ -16,6 +16,7 @@ mod ext; mod impls; mod retcode; +use ink_prelude::vec::Vec; use super::Instance; pub(crate) use self::retcode::RetCode; @@ -26,8 +27,6 @@ pub struct WasmEnv { buffer: Vec, } -static mut INSTANCE: WasmEnv = WasmEnv { buffer: Vec::new() }; - pub enum Accessor {} impl Instance for Accessor { @@ -37,6 +36,7 @@ impl Instance for Accessor { where F: FnOnce(&mut Self::Engine) -> R { + static mut INSTANCE: WasmEnv = WasmEnv { buffer: Vec::new() }; f(unsafe { &mut INSTANCE }) } } From 5787941427c699a008d034a66e7d156c61b8779b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 18 Jan 2020 18:03:06 +0100 Subject: [PATCH 007/112] [core] clean-up of env3 fundamental types --- core/src/env3/mod.rs | 2 ++ core/src/env3/types.rs | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/core/src/env3/mod.rs b/core/src/env3/mod.rs index cc8617b9c13..1dde110e0dc 100644 --- a/core/src/env3/mod.rs +++ b/core/src/env3/mod.rs @@ -39,8 +39,10 @@ pub use self::{ Result, }, types::{ + AccountId, DefaultEnvTypes, EnvTypes, + Hash, Topics, }, }; diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs index 040d31e3c60..41142942a9f 100644 --- a/core/src/env3/types.rs +++ b/core/src/env3/types.rs @@ -59,7 +59,7 @@ where fn topics(&self) -> &'static [::Hash]; } -/// The fundamental types of the SRML default configuration. +/// The fundamental types of the default configuration. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "ink-generate-abi", derive(Metadata))] pub enum DefaultEnvTypes {} @@ -106,16 +106,21 @@ impl 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. 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 SRML `AccountId` type. +/// 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, Hash, Encode, Decode, From, Default)] #[cfg_attr(feature = "ink-generate-abi", derive(Metadata))] pub struct AccountId([u8; 32]); @@ -131,7 +136,12 @@ impl<'a> TryFrom<&'a [u8]> for AccountId { impl Flush for AccountId {} -/// The default SRML `Hash` type. +/// 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, Hash, Encode, Decode, From, Default)] #[cfg_attr(feature = "ink-generate-abi", derive(Metadata))] pub struct Hash([u8; 32]); From f8c359e5754b37dc67afd1891d01a502842a258d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 19 Jan 2020 14:06:17 +0100 Subject: [PATCH 008/112] [core] add WasmEnv::reset_buffer --- core/src/env3/engine/on_chain/impls.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index 80dfe5a4df8..3347cc3fa7a 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -36,6 +36,16 @@ use crate::{ }; impl WasmEnv { + /// 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() + } + /// Reads the current scratch buffer into the contract-side buffer. /// /// Returns the amount of bytes read. @@ -88,6 +98,7 @@ impl WasmEnv { self.decode_scratch_buffer().map_err(Into::into) } + /// Reusable implementation for invoking another contract message. fn invoke_contract_impl( &mut self, call_params: &CallParams, @@ -96,7 +107,7 @@ impl WasmEnv { T: EnvTypes, { // Reset the contract-side buffer to append onto clean slate. - self.buffer.clear(); + 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()); @@ -213,7 +224,7 @@ impl TypedEnv for WasmEnv { Event: Topics + scale::Encode, { // Reset the contract-side buffer to append onto clean slate. - self.buffer.clear(); + 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()); @@ -269,7 +280,7 @@ impl TypedEnv for WasmEnv { T: EnvTypes, { // Reset the contract-side buffer to append onto clean slate. - self.buffer.clear(); + 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()); @@ -301,7 +312,7 @@ impl TypedEnv for WasmEnv { T: EnvTypes, { // Reset the contract-side buffer to append onto clean slate. - self.buffer.clear(); + 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. From a8bf71ba641fbfeeed5f9dc00299716f0d18a444 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 20 Jan 2020 11:08:01 +0100 Subject: [PATCH 009/112] show test submodule of env3 when compiling with rustdoc --- core/src/env3/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/env3/mod.rs b/core/src/env3/mod.rs index 1dde110e0dc..913eba3a5a4 100644 --- a/core/src/env3/mod.rs +++ b/core/src/env3/mod.rs @@ -24,7 +24,7 @@ pub mod call; mod engine; mod error; mod property; -#[cfg(any(test, rustdoc))] +#[cfg(any(test, doc))] pub mod test; mod types; From e1a2c490d10f3a914073d8c288442570208c4792 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 02:16:33 +0100 Subject: [PATCH 010/112] [core] initial implementation of the off-chain env instance Also many other adjustments. --- core/src/env3/api.rs | 74 +++-- core/src/env3/engine/mod.rs | 14 +- core/src/env3/engine/off_chain/db/accounts.rs | 146 +++++++++ core/src/env3/engine/off_chain/db/block.rs | 46 +++ .../env3/engine/off_chain/db/chain_spec.rs | 7 + core/src/env3/engine/off_chain/db/codes.rs | 51 +++ .../env3/engine/off_chain/db/exec_context.rs | 212 +++++++++++++ core/src/env3/engine/off_chain/db/mod.rs | 41 +++ core/src/env3/engine/off_chain/impls.rs | 6 +- core/src/env3/engine/off_chain/mod.rs | 61 +++- .../env3/engine/off_chain/typed_encoded.rs | 299 ++++++++++++++++++ core/src/env3/engine/off_chain/types.rs | 58 ++++ core/src/env3/engine/on_chain/impls.rs | 8 +- core/src/env3/engine/on_chain/mod.rs | 16 +- core/src/env3/mod.rs | 2 +- 15 files changed, 971 insertions(+), 70 deletions(-) create mode 100644 core/src/env3/engine/off_chain/db/accounts.rs create mode 100644 core/src/env3/engine/off_chain/db/block.rs create mode 100644 core/src/env3/engine/off_chain/db/chain_spec.rs create mode 100644 core/src/env3/engine/off_chain/db/codes.rs create mode 100644 core/src/env3/engine/off_chain/db/exec_context.rs create mode 100644 core/src/env3/engine/off_chain/db/mod.rs create mode 100644 core/src/env3/engine/off_chain/typed_encoded.rs create mode 100644 core/src/env3/engine/off_chain/types.rs diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index 3b42c88c6ec..0d09526102b 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -20,8 +20,8 @@ use crate::{ ReturnType, }, engine::{ - Accessor, - Instance, + EnvInstance, + OnInstance, }, EnvTypes, Result, @@ -39,7 +39,7 @@ pub fn caller() -> Result where T: EnvTypes, { - ::run(|instance| TypedEnv::caller::(instance)) + ::on_instance(|instance| TypedEnv::caller::(instance)) } /// Returns the transferred balance for the contract execution. @@ -51,7 +51,9 @@ pub fn transferred_balance() -> Result where T: EnvTypes, { - ::run(|instance| TypedEnv::transferred_balance::(instance)) + ::on_instance(|instance| { + TypedEnv::transferred_balance::(instance) + }) } /// Returns the current price for gas. @@ -63,7 +65,9 @@ pub fn gas_price() -> Result where T: EnvTypes, { - ::run(|instance| TypedEnv::gas_price::(instance)) + ::on_instance(|instance| { + TypedEnv::gas_price::(instance) + }) } /// Returns the amount of gas left for the contract execution. @@ -75,7 +79,7 @@ pub fn gas_left() -> Result where T: EnvTypes, { - ::run(|instance| TypedEnv::gas_left::(instance)) + ::on_instance(|instance| TypedEnv::gas_left::(instance)) } /// Returns the current block time in milliseconds. @@ -87,7 +91,9 @@ pub fn now_in_ms() -> Result where T: EnvTypes, { - ::run(|instance| TypedEnv::now_in_ms::(instance)) + ::on_instance(|instance| { + TypedEnv::now_in_ms::(instance) + }) } /// Returns the address of the executed contract. @@ -99,7 +105,7 @@ pub fn address() -> Result where T: EnvTypes, { - ::run(|instance| TypedEnv::address::(instance)) + ::on_instance(|instance| TypedEnv::address::(instance)) } /// Returns the balance of the executed contract. @@ -111,7 +117,7 @@ pub fn balance() -> Result where T: EnvTypes, { - ::run(|instance| TypedEnv::balance::(instance)) + ::on_instance(|instance| TypedEnv::balance::(instance)) } /// Returns the current rent allowance for the executed contract. @@ -123,7 +129,9 @@ pub fn rent_allowance() -> Result where T: EnvTypes, { - ::run(|instance| TypedEnv::rent_allowance::(instance)) + ::on_instance(|instance| { + TypedEnv::rent_allowance::(instance) + }) } /// Returns the current block number. @@ -135,7 +143,9 @@ pub fn block_number() -> Result where T: EnvTypes, { - ::run(|instance| TypedEnv::block_number::(instance)) + ::on_instance(|instance| { + TypedEnv::block_number::(instance) + }) } /// Returns the minimum balance of the executed contract. @@ -147,7 +157,9 @@ pub fn minimum_balance() -> Result where T: EnvTypes, { - ::run(|instance| TypedEnv::minimum_balance::(instance)) + ::on_instance(|instance| { + TypedEnv::minimum_balance::(instance) + }) } /// Emits an event with the given event data. @@ -156,7 +168,7 @@ where T: EnvTypes, Event: Topics + scale::Encode, { - ::run(|instance| { + ::on_instance(|instance| { TypedEnv::emit_event::(instance, event) }) } @@ -166,7 +178,7 @@ pub fn set_rent_allowance(new_value: T::Balance) where T: EnvTypes, { - ::run(|instance| { + ::on_instance(|instance| { TypedEnv::set_rent_allowance::(instance, new_value) }) } @@ -176,7 +188,7 @@ pub fn set_contract_storage(key: Key, value: &V) where V: scale::Encode, { - ::run(|instance| { + ::on_instance(|instance| { Env::set_contract_storage::(instance, key, value) }) } @@ -191,12 +203,16 @@ pub fn get_contract_storage(key: Key) -> Result where R: scale::Decode, { - ::run(|instance| Env::get_contract_storage::(instance, key)) + ::on_instance(|instance| { + Env::get_contract_storage::(instance, key) + }) } /// Clears the contract's storage key entry. pub fn clear_contract_storage(key: Key) { - ::run(|instance| Env::clear_contract_storage(instance, key)) + ::on_instance(|instance| { + Env::clear_contract_storage(instance, key) + }) } /// Invokes a call to the runtime. @@ -213,7 +229,7 @@ pub fn invoke_runtime(params: &T::Call) -> Result<()> where T: EnvTypes, { - ::run(|instance| { + ::on_instance(|instance| { TypedEnv::invoke_runtime::(instance, params) }) } @@ -232,7 +248,7 @@ pub fn invoke_contract(params: &CallParams) -> Result<()> where T: EnvTypes, { - ::run(|instance| { + ::on_instance(|instance| { TypedEnv::invoke_contract::(instance, params) }) } @@ -253,7 +269,7 @@ where T: EnvTypes, R: scale::Decode, { - ::run(|instance| { + ::on_instance(|instance| { TypedEnv::eval_contract::(instance, params) }) } @@ -272,7 +288,7 @@ pub fn create_contract(params: &CreateParams) -> Result::run(|instance| { + ::on_instance(|instance| { TypedEnv::create_contract::(instance, params) }) } @@ -327,7 +343,7 @@ pub fn restore_contract( ) where T: EnvTypes, { - ::run(|instance| { + ::on_instance(|instance| { TypedEnv::restore_contract::( instance, account_id, @@ -355,9 +371,7 @@ pub fn restore_contract( /// - This happens only if the host runtime provides less than 4 bytes for /// the function selector upon this query. pub fn input() -> Result { - ::run(|instance| { - Env::input(instance) - }) + ::on_instance(|instance| Env::input(instance)) } /// Returns the value back to the caller of the executed contract. @@ -370,7 +384,7 @@ pub fn output(return_value: &R) where R: scale::Encode, { - ::run(|instance| { + ::on_instance(|instance| { Env::output::(instance, return_value) }) } @@ -388,16 +402,14 @@ pub fn random(subject: &[u8]) -> Result where T: EnvTypes, { - ::run(|instance| { + ::on_instance(|instance| { TypedEnv::random::(instance, subject) }) } /// Prints the given contents to the environmental log. pub fn println(content: &str) { - ::run(|instance| { - Env::println(instance, content) - }) + ::on_instance(|instance| Env::println(instance, content)) } /// Returns the value from the *runtime* storage at the position of the key. @@ -410,7 +422,7 @@ pub fn get_runtime_storage(runtime_key: &[u8]) -> Result where R: scale::Decode, { - ::run(|instance| { + ::on_instance(|instance| { Env::get_runtime_storage::(instance, runtime_key) }) } diff --git a/core/src/env3/engine/mod.rs b/core/src/env3/engine/mod.rs index eb037b12a1e..64362817636 100644 --- a/core/src/env3/engine/mod.rs +++ b/core/src/env3/engine/mod.rs @@ -15,21 +15,19 @@ use cfg_if::cfg_if; use crate::env3::backend::{TypedEnv, Env}; -pub trait Instance { - type Engine: Env + TypedEnv; - - fn run(f: F) -> R +pub trait OnInstance: Env + TypedEnv { + fn on_instance(f: F) -> R where - F: FnOnce(&mut Self::Engine) -> R; + F: FnOnce(&mut Self) -> R; } cfg_if! { if #[cfg(all(not(feature = "std"), target_arch = "wasm32"))] { mod on_chain; - pub use self::on_chain::Accessor; + pub use self::on_chain::EnvInstance; } else if #[cfg(feature = "std")] { - mod off_chain; - pub use self::off_chain::Accessor; + pub mod off_chain; + pub use self::off_chain::EnvInstance; } 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..af87df1a35a --- /dev/null +++ b/core/src/env3/engine/off_chain/db/accounts.rs @@ -0,0 +1,146 @@ +use super::{ + super::TypedEncodedError, + OffAccountId, + OffBalance, + OffBlockNumber, + OffCall, + OffHash, + OffMoment, +}; +use crate::{ + env3::EnvTypes, + storage::Key, +}; +use ink_prelude::collections::BTreeMap; +use derive_more::From; + +/// Errors encountered upon interacting with the accounts database. +#[derive(Debug, From)] +pub enum AccountError { + TypedEncoded(TypedEncodedError), + #[from(ignore)] + UnexpectedUserAccount, +} + +/// 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(), + } + } +} + +/// 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) + } + + /// 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 of 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) + }) + } + + /// Returns the code hash of the contract account of an error. + pub fn code_hash(&self) -> Result + where + T: EnvTypes, + { + self.contract_or_err().and_then(|contract| { + contract.code_hash.decode().map_err(Into::into) + }) + } + + /// Sets the contract storage of key to the new value. + pub fn set_storage(&mut self, key: Key, new_value: &T) -> Result<()> + where + T: scale::Encode + 'static, + { + self.contract_or_err_mut().and_then(|contract| { + todo!() + }) + } + + /// Clears the contract storage at key. + pub fn clear_storage(&mut self, key: Key) -> Result<()> { + self.contract_or_err_mut().and_then(|contract| { + todo!() + }) + } + + /// Clears the contract storage at key. + pub fn get_storage(&self, key: Key) -> Result + where + T: scale::Decode + 'static, + { + self.contract_or_err().and_then(|contract| { + todo!() + }) + } +} + +/// 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's code hash. + code_hash: OffHash, + /// The contract storage. + storage: ContractStorage, +} + +/// The storage of a contract instance. +pub struct ContractStorage { + /// The entries within the contract storage. + entries: BTreeMap>, +} 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..fbd4f87cfb8 --- /dev/null +++ b/core/src/env3/engine/off_chain/db/block.rs @@ -0,0 +1,46 @@ +use super::super::{ + types::{ + OffBlockNumber, + OffMoment, + }, + TypedEncoded, + Result, +}; +use crate::env3::EnvTypes; + +/// An emulated block in the chain. +pub struct Block { + /// The current block number. + number: OffBlockNumber, + /// The current moment of block creation. + moment: OffMoment, +} + +impl Block { + /// Creates a new block for the given number and moment. + pub fn new(number: T::BlockNumber, moment: T::Moment) -> Self + where + T: EnvTypes, + { + Self { + number: TypedEncoded::new(&number), + moment: TypedEncoded::new(&moment), + } + } + + /// Returns the block number. + pub fn number(&self) -> Result + where + T: EnvTypes, + { + self.number.decode().map_err(Into::into) + } + + /// Returns the moment of the block. + pub fn moment(&self) -> Result + where + T: EnvTypes, + { + self.moment.decode().map_err(Into::into) + } +} 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..0d384e3cbdd --- /dev/null +++ b/core/src/env3/engine/off_chain/db/chain_spec.rs @@ -0,0 +1,7 @@ +use super::OffBalance; + +/// The chain specification. +pub struct ChainSpec { + /// The current gas price. + gas_price: OffBalance, +} diff --git a/core/src/env3/engine/off_chain/db/codes.rs b/core/src/env3/engine/off_chain/db/codes.rs new file mode 100644 index 00000000000..5cdefd5ed74 --- /dev/null +++ b/core/src/env3/engine/off_chain/db/codes.rs @@ -0,0 +1,51 @@ +use super::{ + OffAccountId, + OffBalance, + OffBlockNumber, + OffCall, + OffHash, + OffMoment, +}; +use crate::env3::EnvTypes; +use ink_prelude::collections::BTreeMap; + +/// The Wasm codes data base. +pub struct CodeDb { + /// Mapping from (code)hash to Wasm blob. + codes: BTreeMap, +} + +impl CodeDb { + /// Creates a new empty Wasm codes database. + pub fn new() -> Self { + Self { + codes: BTreeMap::new(), + } + } + + /// Puts the wasm code (as bytes) onto the chain and returns its code hash. + pub fn put_code(wasm_bytes: &[u8]) -> T::Hash + where + T: EnvTypes, + { + todo!() + } +} + +/// A Wasm blob on the chain. +pub struct WasmBlob { + /// The bytes of the Wasm blob. + wasm_bytes: Vec, + /// The references to this Wasm blob to count usages. + references: usize, +} + +impl WasmBlob { + /// Creates a new empty Wasm blob with no usage references. + pub fn empty() -> Self { + Self { + wasm_bytes: Vec::new(), + references: 0, + } + } +} 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..40c60ec8ce0 --- /dev/null +++ b/core/src/env3/engine/off_chain/db/exec_context.rs @@ -0,0 +1,212 @@ +use super::{ + super::{ + Result, + TypedEncoded, + }, + OffAccountId, + OffBalance, + OffCall, + OffHash, + OffMoment, +}; +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. + caller: OffAccountId, + /// The callee of the contract execution. + callee: OffAccountId, + /// The transferred value from caller to callee. + transferred_value: OffBalance, + /// The gas provided for the whole execution. + gas: OffBalance, + /// The inputs provided for the whole execution. + /// + /// # Note + /// + /// This includes selector and encoded arguments. + call_data: CallData, + /// The output of the contract execution. + 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. + pub fn call_data(&self) -> &CallData { + &self.call_data + } + + /// Returns the contract execution output. + 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.callee.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/env3/engine/off_chain/db/mod.rs b/core/src/env3/engine/off_chain/db/mod.rs new file mode 100644 index 00000000000..7eaaae34b1d --- /dev/null +++ b/core/src/env3/engine/off_chain/db/mod.rs @@ -0,0 +1,41 @@ +// 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 accounts; +mod block; +mod chain_spec; +mod codes; +mod exec_context; + +pub use self::{ + accounts::{ + Account, + AccountKind, + AccountError, + AccountsDb, + ContractAccount, + ContractStorage, + }, + block::Block, + codes::CodeDb, + exec_context::ExecContext, +}; +use super::{ + OffAccountId, + OffBalance, + OffBlockNumber, + OffCall, + OffHash, + OffMoment, +}; diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index 7321e8d6c31..ff1d7b0a314 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::TestEnv; +use super::EnvInstance; use crate::{ env3::{ call::{ @@ -30,7 +30,7 @@ use crate::{ storage::Key, }; -impl Env for TestEnv { +impl Env for EnvInstance { fn set_contract_storage(&mut self, key: Key, value: &V) where V: scale::Encode, @@ -72,7 +72,7 @@ impl Env for TestEnv { } } -impl TypedEnv for TestEnv { +impl TypedEnv for EnvInstance { fn caller(&mut self) -> Result { todo!() } diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index f136dededf5..a0d08a21f23 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -12,29 +12,64 @@ // See the License for the specific language governing permissions and // limitations under the License. +pub mod db; mod impls; +pub mod typed_encoded; +pub mod types; -use super::Instance; +use self::{ + db::{ + AccountsDb, + AccountError, + CodeDb, + ExecContext, + }, + typed_encoded::{TypedEncoded, TypedEncodedError, Result}, + types::{ + OffAccountId, + OffBalance, + OffHash, + OffMoment, + OffBlockNumber, + OffCall, + }, +}; +use super::OnInstance; use core::cell::RefCell; -pub enum Accessor {} - -pub struct TestEnv {} +/// The off-chain environment. +/// +/// Mainly used for off-chain testing. +pub struct EnvInstance { + /// The accounts database of the environment. + accounts: AccountsDb, + /// Uploaded Wasm contract codes. + codes: CodeDb, + /// Current execution context and context. + session: Option, +} -impl Instance for Accessor { - type Engine = TestEnv; +impl EnvInstance { + /// Creates a new uninitialized off-chain environment. + pub fn uninitialized() -> Self { + Self { + accounts: AccountsDb::new(), + codes: CodeDb::new(), + session: None, + } + } +} - fn run(f: F) -> R +impl OnInstance for EnvInstance { + fn on_instance(f: F) -> R where - F: FnOnce(&mut Self::Engine) -> R, + F: FnOnce(&mut Self) -> R, { thread_local!( - static INSTANCE: RefCell = RefCell::new( - TestEnv {} + static INSTANCE: RefCell = RefCell::new( + EnvInstance::uninitialized() ) ); - INSTANCE.with(|instance| { - f(&mut instance.borrow_mut()) - }) + INSTANCE.with(|instance| f(&mut instance.borrow_mut())) } } 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..1c504b5c728 --- /dev/null +++ b/core/src/env3/engine/off_chain/typed_encoded.rs @@ -0,0 +1,299 @@ +// 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, +}; +use derive_more::From; + +/// 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. + /// + /// # 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)] +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, +} + +/// 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 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 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 `other` to `self` + pub fn assign<'a, T>(&mut self, other: &'a Self) -> Result<()> + where + T: scale::Decode + scale::Encode + 'static, + { + Self::check_matching_types(self, other)?; + self.encoded.clear(); + self.encoded.extend(&other.encoded); + 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/env3/engine/off_chain/types.rs b/core/src/env3/engine/off_chain/types.rs new file mode 100644 index 00000000000..5a3efdfd094 --- /dev/null +++ b/core/src/env3/engine/off_chain/types.rs @@ -0,0 +1,58 @@ +// 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. + +//! Emulated environmental types of the test environment. +//! +//! Due to technical constraints it is not possible to define the test +//! environment instance thread-locally and generically over the actual +//! environmental types. +//! +//! For that reason we store the type information of all the environmental +//! types at runtime to at least have some kind of type safety upon access. +//! This is done via the `TypedEncoded` abstraction that stores the +//! SCALE encoded bytes and also has a runtime type information marker +//! assigned upon initialization to check whether accesses to it are +//! type safe. + +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 {} + /// Type marker representing an environmental `Balance`. + #[derive(Debug)] 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 {} + /// Type marker representing an environmental `BlockNumber`. + #[derive(Debug)] pub enum BlockNumber {} + /// Type marker representing an environmental `Call`. + #[derive(Debug)] pub enum Call {} +} + +/// 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 OffMoment = 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/impls.rs b/core/src/env3/engine/on_chain/impls.rs index 3347cc3fa7a..38b07d3f9be 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -14,7 +14,7 @@ use super::{ ext, - WasmEnv, + EnvInstance, }; use crate::{ env3::{ @@ -35,7 +35,7 @@ use crate::{ storage::Key, }; -impl WasmEnv { +impl EnvInstance { /// Empties the contract-side scratch buffer. /// /// # Note @@ -127,7 +127,7 @@ impl WasmEnv { } } -impl Env for WasmEnv { +impl Env for EnvInstance { fn set_contract_storage(&mut self, key: Key, value: &V) where V: scale::Encode, @@ -177,7 +177,7 @@ impl Env for WasmEnv { } } -impl TypedEnv for WasmEnv { +impl TypedEnv for EnvInstance { fn caller(&mut self) -> Result { self.get_property::>(ext::caller) } diff --git a/core/src/env3/engine/on_chain/mod.rs b/core/src/env3/engine/on_chain/mod.rs index 114874734f8..2c300f1ea33 100644 --- a/core/src/env3/engine/on_chain/mod.rs +++ b/core/src/env3/engine/on_chain/mod.rs @@ -17,26 +17,22 @@ mod impls; mod retcode; use ink_prelude::vec::Vec; -use super::Instance; +use super::OnInstance; pub(crate) use self::retcode::RetCode; /// The on-chain environment. -pub struct WasmEnv { +pub struct EnvInstance { /// Encode & decode buffer for potentially reusing required dynamic allocations. buffer: Vec, } -pub enum Accessor {} - -impl Instance for Accessor { - type Engine = WasmEnv; - - fn run(f: F) -> R +impl OnInstance for EnvInstance { + fn on_instance(f: F) -> R where - F: FnOnce(&mut Self::Engine) -> R + F: FnOnce(&mut Self) -> R { - static mut INSTANCE: WasmEnv = WasmEnv { buffer: Vec::new() }; + static mut INSTANCE: EnvInstance = EnvInstance { buffer: Vec::new() }; f(unsafe { &mut INSTANCE }) } } diff --git a/core/src/env3/mod.rs b/core/src/env3/mod.rs index 913eba3a5a4..b95c0681afd 100644 --- a/core/src/env3/mod.rs +++ b/core/src/env3/mod.rs @@ -21,7 +21,7 @@ mod api; mod backend; mod buffer; pub mod call; -mod engine; +pub mod engine; mod error; mod property; #[cfg(any(test, doc))] From 58320ee3e56ca17a9983ae6026a02410cc2cfaa0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 10:02:55 +0100 Subject: [PATCH 011/112] [core] further enhancements to off-chain env --- core/src/env3/engine/off_chain/db/accounts.rs | 116 ++++++++++++++---- core/src/env3/engine/off_chain/db/block.rs | 14 +++ .../env3/engine/off_chain/db/chain_spec.rs | 33 +++++ core/src/env3/engine/off_chain/db/codes.rs | 14 +++ .../env3/engine/off_chain/db/exec_context.rs | 15 ++- core/src/env3/engine/off_chain/db/mod.rs | 1 + core/src/env3/engine/off_chain/mod.rs | 4 + .../env3/engine/off_chain/typed_encoded.rs | 9 ++ 8 files changed, 181 insertions(+), 25 deletions(-) diff --git a/core/src/env3/engine/off_chain/db/accounts.rs b/core/src/env3/engine/off_chain/db/accounts.rs index af87df1a35a..b327eac090d 100644 --- a/core/src/env3/engine/off_chain/db/accounts.rs +++ b/core/src/env3/engine/off_chain/db/accounts.rs @@ -1,9 +1,21 @@ +// 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::{ super::TypedEncodedError, OffAccountId, OffBalance, - OffBlockNumber, - OffCall, OffHash, OffMoment, }; @@ -11,8 +23,8 @@ use crate::{ env3::EnvTypes, storage::Key, }; -use ink_prelude::collections::BTreeMap; use derive_more::From; +use ink_prelude::collections::BTreeMap; /// Errors encountered upon interacting with the accounts database. #[derive(Debug, From)] @@ -22,6 +34,12 @@ pub enum AccountError { UnexpectedUserAccount, } +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; @@ -38,6 +56,30 @@ impl AccountsDb { 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)) + } + + /// Creates a new user account. + pub fn new_user_account(&mut self) -> T::AccountId + where + T: EnvTypes, + { + todo!() + } + + /// Creates a new contract account. + pub fn new_contract_account(&mut self) -> T::AccountId + where + T: EnvTypes, + { + todo!() + } } /// An account within the chain. @@ -60,7 +102,9 @@ impl Account { /// 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::User => { + Err(AccountError::UnexpectedUserAccount).map_err(Into::into) + } AccountKind::Contract(contract_account) => Ok(contract_account), } } @@ -68,7 +112,9 @@ impl 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::User => { + Err(AccountError::UnexpectedUserAccount).map_err(Into::into) + } AccountKind::Contract(contract_account) => Ok(contract_account), } } @@ -78,9 +124,8 @@ impl Account { where T: EnvTypes, { - self.contract_or_err().and_then(|contract| { - contract.rent_allowance.decode().map_err(Into::into) - }) + self.contract_or_err() + .and_then(|contract| contract.rent_allowance.decode().map_err(Into::into)) } /// Returns the code hash of the contract account of an error. @@ -88,36 +133,32 @@ impl Account { where T: EnvTypes, { - self.contract_or_err().and_then(|contract| { - contract.code_hash.decode().map_err(Into::into) - }) + self.contract_or_err() + .and_then(|contract| contract.code_hash.decode().map_err(Into::into)) } /// Sets the contract storage of key to the new value. - pub fn set_storage(&mut self, key: Key, new_value: &T) -> Result<()> + pub fn set_storage(&mut self, at: Key, new_value: &T) -> Result<()> where T: scale::Encode + 'static, { - self.contract_or_err_mut().and_then(|contract| { - todo!() - }) + 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, key: Key) -> Result<()> { - self.contract_or_err_mut().and_then(|contract| { - todo!() - }) + 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, key: Key) -> Result + pub fn get_storage(&self, at: Key) -> Result> where T: scale::Decode + 'static, { - self.contract_or_err().and_then(|contract| { - todo!() - }) + self.contract_or_err() + .and_then(|contract| contract.storage.get_storage::(at)) } } @@ -136,7 +177,7 @@ pub struct ContractAccount { /// The contract's code hash. code_hash: OffHash, /// The contract storage. - storage: ContractStorage, + pub storage: ContractStorage, } /// The storage of a contract instance. @@ -144,3 +185,30 @@ pub struct ContractStorage { /// The entries within the contract storage. entries: BTreeMap>, } + +impl ContractStorage { + /// Returns the decoded storage at the key if any. + pub fn get_storage(&self, at: Key) -> Result> + where + T: scale::Decode + 'static, + { + 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 + 'static, + { + 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.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 index fbd4f87cfb8..04b11678313 100644 --- a/core/src/env3/engine/off_chain/db/block.rs +++ b/core/src/env3/engine/off_chain/db/block.rs @@ -1,3 +1,17 @@ +// 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::super::{ types::{ OffBlockNumber, diff --git a/core/src/env3/engine/off_chain/db/chain_spec.rs b/core/src/env3/engine/off_chain/db/chain_spec.rs index 0d384e3cbdd..d5535ed6101 100644 --- a/core/src/env3/engine/off_chain/db/chain_spec.rs +++ b/core/src/env3/engine/off_chain/db/chain_spec.rs @@ -1,7 +1,40 @@ +// 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::OffBalance; +use crate::env3::EnvTypes; +use super::super::Result; /// The chain specification. pub struct ChainSpec { /// The current gas price. gas_price: OffBalance, } + +impl ChainSpec { + /// Creates a new uninitialized chain specification. + pub fn uninitialized() -> Self { + Self { + gas_price: OffBalance::uninitialized(), + } + } + + /// Returns the gas price for the chain. + pub fn gas_price(&self) -> Result + where + T: EnvTypes, + { + self.gas_price.decode() + } +} diff --git a/core/src/env3/engine/off_chain/db/codes.rs b/core/src/env3/engine/off_chain/db/codes.rs index 5cdefd5ed74..d72c1e0a69f 100644 --- a/core/src/env3/engine/off_chain/db/codes.rs +++ b/core/src/env3/engine/off_chain/db/codes.rs @@ -1,3 +1,17 @@ +// 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::{ OffAccountId, OffBalance, diff --git a/core/src/env3/engine/off_chain/db/exec_context.rs b/core/src/env3/engine/off_chain/db/exec_context.rs index 40c60ec8ce0..adba61e3ad2 100644 --- a/core/src/env3/engine/off_chain/db/exec_context.rs +++ b/core/src/env3/engine/off_chain/db/exec_context.rs @@ -1,3 +1,17 @@ +// 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::{ super::{ Result, @@ -5,7 +19,6 @@ use super::{ }, OffAccountId, OffBalance, - OffCall, OffHash, OffMoment, }; diff --git a/core/src/env3/engine/off_chain/db/mod.rs b/core/src/env3/engine/off_chain/db/mod.rs index 7eaaae34b1d..418af2d13fd 100644 --- a/core/src/env3/engine/off_chain/db/mod.rs +++ b/core/src/env3/engine/off_chain/db/mod.rs @@ -27,6 +27,7 @@ pub use self::{ ContractAccount, ContractStorage, }, + chain_spec::ChainSpec, block::Block, codes::CodeDb, exec_context::ExecContext, diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index a0d08a21f23..00924409841 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -23,6 +23,7 @@ use self::{ AccountError, CodeDb, ExecContext, + ChainSpec, }, typed_encoded::{TypedEncoded, TypedEncodedError, Result}, types::{ @@ -47,6 +48,8 @@ pub struct EnvInstance { codes: CodeDb, /// Current execution context and context. session: Option, + /// The general chain spec. + chain_spec: ChainSpec, } impl EnvInstance { @@ -56,6 +59,7 @@ impl EnvInstance { accounts: AccountsDb::new(), codes: CodeDb::new(), session: None, + chain_spec: ChainSpec::uninitialized(), } } } diff --git a/core/src/env3/engine/off_chain/typed_encoded.rs b/core/src/env3/engine/off_chain/typed_encoded.rs index 1c504b5c728..1506e1ac902 100644 --- a/core/src/env3/engine/off_chain/typed_encoded.rs +++ b/core/src/env3/engine/off_chain/typed_encoded.rs @@ -106,6 +106,15 @@ impl Default for TypedEncoded { } 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 From 6daf61368887b480192ba608a727e4f5479b4125 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 11:09:04 +0100 Subject: [PATCH 012/112] [core] implement untyped Env for off-chain environment --- core/src/env3/engine/off_chain/db/accounts.rs | 21 +++++-- core/src/env3/engine/off_chain/db/block.rs | 12 ++-- .../env3/engine/off_chain/db/chain_spec.rs | 2 +- core/src/env3/engine/off_chain/db/codes.rs | 52 +++++++++++------ .../env3/engine/off_chain/db/exec_context.rs | 14 ++--- core/src/env3/engine/off_chain/db/mod.rs | 1 - core/src/env3/engine/off_chain/impls.rs | 56 +++++++++++++++++-- core/src/env3/engine/off_chain/mod.rs | 49 +++++++++++++--- .../env3/engine/off_chain/typed_encoded.rs | 2 +- core/src/env3/engine/off_chain/types.rs | 12 ++-- 10 files changed, 163 insertions(+), 58 deletions(-) diff --git a/core/src/env3/engine/off_chain/db/accounts.rs b/core/src/env3/engine/off_chain/db/accounts.rs index b327eac090d..f03bf97fa38 100644 --- a/core/src/env3/engine/off_chain/db/accounts.rs +++ b/core/src/env3/engine/off_chain/db/accounts.rs @@ -17,7 +17,6 @@ use super::{ OffAccountId, OffBalance, OffHash, - OffMoment, }; use crate::{ env3::EnvTypes, @@ -62,7 +61,17 @@ impl AccountsDb { where T: EnvTypes, { - self.accounts.get(&OffAccountId::new(&at)) + self.get_account_off(&OffAccountId::new(&at)) + } + + /// Returns the account for the given off-account ID if any. + pub fn get_account_off(&self, at: &OffAccountId) -> Option<&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) } /// Creates a new user account. @@ -140,7 +149,7 @@ impl Account { /// Sets the contract storage of key to the new value. pub fn set_storage(&mut self, at: Key, new_value: &T) -> Result<()> where - T: scale::Encode + 'static, + T: scale::Encode, { self.contract_or_err_mut() .map(|contract| contract.storage.set_storage::(at, new_value)) @@ -155,7 +164,7 @@ impl Account { /// Clears the contract storage at key. pub fn get_storage(&self, at: Key) -> Result> where - T: scale::Decode + 'static, + T: scale::Decode, { self.contract_or_err() .and_then(|contract| contract.storage.get_storage::(at)) @@ -190,7 +199,7 @@ impl ContractStorage { /// Returns the decoded storage at the key if any. pub fn get_storage(&self, at: Key) -> Result> where - T: scale::Decode + 'static, + T: scale::Decode, { self.entries .get(&at) @@ -202,7 +211,7 @@ impl ContractStorage { /// 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 + 'static, + T: scale::Encode, { self.entries.insert(at, new_value.encode()); } diff --git a/core/src/env3/engine/off_chain/db/block.rs b/core/src/env3/engine/off_chain/db/block.rs index 04b11678313..eaff5abd941 100644 --- a/core/src/env3/engine/off_chain/db/block.rs +++ b/core/src/env3/engine/off_chain/db/block.rs @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::super::{ - types::{ - OffBlockNumber, - OffMoment, +use super::{ + super::{ + Result, + TypedEncoded, }, - TypedEncoded, - Result, + OffBlockNumber, + OffMoment, }; use crate::env3::EnvTypes; diff --git a/core/src/env3/engine/off_chain/db/chain_spec.rs b/core/src/env3/engine/off_chain/db/chain_spec.rs index d5535ed6101..df55b3e2042 100644 --- a/core/src/env3/engine/off_chain/db/chain_spec.rs +++ b/core/src/env3/engine/off_chain/db/chain_spec.rs @@ -35,6 +35,6 @@ impl ChainSpec { where T: EnvTypes, { - self.gas_price.decode() + self.gas_price.decode().map_err(Into::into) } } diff --git a/core/src/env3/engine/off_chain/db/codes.rs b/core/src/env3/engine/off_chain/db/codes.rs index d72c1e0a69f..73577aca270 100644 --- a/core/src/env3/engine/off_chain/db/codes.rs +++ b/core/src/env3/engine/off_chain/db/codes.rs @@ -12,21 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{ - OffAccountId, - OffBalance, - OffBlockNumber, - OffCall, - OffHash, - OffMoment, -}; +use super::OffHash; use crate::env3::EnvTypes; use ink_prelude::collections::BTreeMap; /// The Wasm codes data base. pub struct CodeDb { /// Mapping from (code)hash to Wasm blob. - codes: BTreeMap, + codes: BTreeMap, } impl CodeDb { @@ -38,28 +31,55 @@ impl CodeDb { } /// Puts the wasm code (as bytes) onto the chain and returns its code hash. - pub fn put_code(wasm_bytes: &[u8]) -> T::Hash + pub fn put_code(&mut self, code_hash: T::Hash, wasm_bytes: &[u8]) where T: EnvTypes, { - todo!() + self.codes + .insert(OffHash::new(&code_hash), Code::new(wasm_bytes.to_vec())); + } + + /// Gets the wasm code blob associated with the given code hash if any. + pub fn get_code(&self, code_hash: T::Hash) -> Option<&Code> + where + T: EnvTypes, + { + self.codes.get(&OffHash::new(&code_hash)) + } + + /// Gets the wasm code blob associated with the given code hash if any. + pub fn get_code_mut(&mut self, code_hash: T::Hash) -> Option<&mut Code> + where + T: EnvTypes, + { + self.codes.get_mut(&OffHash::new(&code_hash)) } } /// A Wasm blob on the chain. -pub struct WasmBlob { +pub struct Code { /// The bytes of the Wasm blob. wasm_bytes: Vec, /// The references to this Wasm blob to count usages. - references: usize, + pub references: usize, } -impl WasmBlob { - /// Creates a new empty Wasm blob with no usage references. +impl Code { + /// Creates a new empty code. pub fn empty() -> Self { + Self::new(Vec::new()) + } + + /// Creates a new code from the given Wasm bytes. + pub fn new(wasm_bytes: Vec) -> Self { Self { - wasm_bytes: Vec::new(), + wasm_bytes, references: 0, } } + + /// Returns the Wasm bytes. + pub fn wasm_bytes(&self) -> &[u8] { + &self.wasm_bytes + } } diff --git a/core/src/env3/engine/off_chain/db/exec_context.rs b/core/src/env3/engine/off_chain/db/exec_context.rs index adba61e3ad2..af6999cdf9f 100644 --- a/core/src/env3/engine/off_chain/db/exec_context.rs +++ b/core/src/env3/engine/off_chain/db/exec_context.rs @@ -19,8 +19,6 @@ use super::{ }, OffAccountId, OffBalance, - OffHash, - OffMoment, }; use crate::env3::{ call::CallData, @@ -35,21 +33,21 @@ pub struct ExecContext { /// The caller of the contract execution. /// /// Might be user or another contract. - caller: OffAccountId, + pub caller: OffAccountId, /// The callee of the contract execution. - callee: OffAccountId, + pub callee: OffAccountId, /// The transferred value from caller to callee. - transferred_value: OffBalance, + pub transferred_value: OffBalance, /// The gas provided for the whole execution. - gas: OffBalance, + pub gas: OffBalance, /// The inputs provided for the whole execution. /// /// # Note /// /// This includes selector and encoded arguments. - call_data: CallData, + pub call_data: CallData, /// The output of the contract execution. - output: Option, + pub output: Option, } impl ExecContext { diff --git a/core/src/env3/engine/off_chain/db/mod.rs b/core/src/env3/engine/off_chain/db/mod.rs index 418af2d13fd..c80cdb514d7 100644 --- a/core/src/env3/engine/off_chain/db/mod.rs +++ b/core/src/env3/engine/off_chain/db/mod.rs @@ -36,7 +36,6 @@ use super::{ OffAccountId, OffBalance, OffBlockNumber, - OffCall, OffHash, OffMoment, }; diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index ff1d7b0a314..a79ff3bbc94 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::EnvInstance; +use super::{ + Account, + EnvInstance, +}; use crate::{ env3::{ call::{ @@ -30,23 +33,57 @@ use crate::{ 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, { - todo!() + 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, { - todo!() + self.callee_account() + .get_storage(key) + .expect("callee account is not a smart contract") + .ok_or(scale::Error::from("could not decode contract storage")) + .map_err(Into::into) } fn clear_contract_storage(&mut self, key: Key) { - todo!() + self.callee_account_mut() + .clear_storage(key) + .expect("callee account is not a smart contract"); } fn get_runtime_storage(&mut self, runtime_key: &[u8]) -> Result @@ -57,14 +94,21 @@ impl Env for EnvInstance { } fn input(&mut self) -> Result { - todo!() + 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, { - todo!() + let ctx = self + .exec_context_mut() + .expect("uninitialized execution context"); + ctx.output = Some(return_value.encode()); } fn println(&mut self, content: &str) { diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 00924409841..6f100da8023 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -19,24 +19,41 @@ pub mod types; use self::{ db::{ - AccountsDb, AccountError, + AccountsDb, + Account, + Block, + ChainSpec, CodeDb, ExecContext, - ChainSpec, }, - typed_encoded::{TypedEncoded, TypedEncodedError, Result}, + typed_encoded::{ + TypedEncoded, + TypedEncodedError, + }, types::{ OffAccountId, OffBalance, + OffBlockNumber, OffHash, OffMoment, - OffBlockNumber, - OffCall, }, }; use super::OnInstance; use core::cell::RefCell; +use derive_more::From; + +#[derive(Debug, From)] +pub enum InstanceError { + Account(AccountError), + TypedEncoded(TypedEncodedError), + #[from(ignore)] + UninitializedBlocks, + #[from(ignore)] + UninitializedExecutionContext, +} + +pub type Result = core::result::Result; /// The off-chain environment. /// @@ -47,9 +64,11 @@ pub struct EnvInstance { /// Uploaded Wasm contract codes. codes: CodeDb, /// Current execution context and context. - session: Option, + exec_context: Option, /// The general chain spec. chain_spec: ChainSpec, + /// The blocks of the chain. + blocks: Vec, } impl EnvInstance { @@ -58,10 +77,26 @@ impl EnvInstance { Self { accounts: AccountsDb::new(), codes: CodeDb::new(), - session: None, + exec_context: None, chain_spec: ChainSpec::uninitialized(), + blocks: Vec::new(), } } + + /// Returns the current execution context. + fn exec_context(&self) -> Result<&ExecContext> { + self.exec_context.as_ref().ok_or(InstanceError::UninitializedExecutionContext) + } + + /// Returns the current execution context. + fn exec_context_mut(&mut self) -> Result<&mut ExecContext> { + self.exec_context.as_mut().ok_or(InstanceError::UninitializedExecutionContext) + } + + /// Returns the current block of the chain. + fn current_block(&self) -> Result<&Block> { + self.blocks.last().ok_or(InstanceError::UninitializedBlocks) + } } impl OnInstance for EnvInstance { diff --git a/core/src/env3/engine/off_chain/typed_encoded.rs b/core/src/env3/engine/off_chain/typed_encoded.rs index 1506e1ac902..b6bae301e0a 100644 --- a/core/src/env3/engine/off_chain/typed_encoded.rs +++ b/core/src/env3/engine/off_chain/typed_encoded.rs @@ -28,7 +28,7 @@ use derive_more::From; /// # Note /// /// Checks are implemented at runtime. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TypedEncoded { /// The bytes of the encoded representation of the type. encoded: Vec, diff --git a/core/src/env3/engine/off_chain/types.rs b/core/src/env3/engine/off_chain/types.rs index 5a3efdfd094..7a95f571a73 100644 --- a/core/src/env3/engine/off_chain/types.rs +++ b/core/src/env3/engine/off_chain/types.rs @@ -31,17 +31,17 @@ use super::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 {} + #[derive(Debug, Clone)] pub enum Hash {} /// Type marker representing an environmental `Moment`. - #[derive(Debug)] pub enum Moment {} + #[derive(Debug, Clone)] pub enum Moment {} /// 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 {} } /// Off-chain environment account ID type. From 722dfe7a9c93d9dd8fbe7e4126eb950a0f3b39a8 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 13:26:18 +0100 Subject: [PATCH 013/112] [core] implemented a good chunk of TypedEnv for the off-chain environment --- core/src/env3/engine/off_chain/db/accounts.rs | 15 +++- .../env3/engine/off_chain/db/chain_spec.rs | 11 +++ core/src/env3/engine/off_chain/db/console.rs | 74 +++++++++++++++++++ .../env3/engine/off_chain/db/exec_context.rs | 4 +- core/src/env3/engine/off_chain/db/mod.rs | 9 ++- core/src/env3/engine/off_chain/impls.rs | 64 ++++++++++++---- core/src/env3/engine/off_chain/mod.rs | 4 + 7 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 core/src/env3/engine/off_chain/db/console.rs diff --git a/core/src/env3/engine/off_chain/db/accounts.rs b/core/src/env3/engine/off_chain/db/accounts.rs index f03bf97fa38..8b9cbd77e32 100644 --- a/core/src/env3/engine/off_chain/db/accounts.rs +++ b/core/src/env3/engine/off_chain/db/accounts.rs @@ -128,7 +128,7 @@ impl Account { } } - /// Returns the rent allowance of the contract account of an error. + /// Returns the rent allowance of the contract account or an error. pub fn rent_allowance(&self) -> Result where T: EnvTypes, @@ -137,6 +137,19 @@ impl Account { .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::(&OffBalance::new(&new_rent_allowance)) + .map_err(Into::into) + }) + } + /// Returns the code hash of the contract account of an error. pub fn code_hash(&self) -> Result where diff --git a/core/src/env3/engine/off_chain/db/chain_spec.rs b/core/src/env3/engine/off_chain/db/chain_spec.rs index df55b3e2042..d875a94f608 100644 --- a/core/src/env3/engine/off_chain/db/chain_spec.rs +++ b/core/src/env3/engine/off_chain/db/chain_spec.rs @@ -20,6 +20,8 @@ use super::super::Result; pub struct ChainSpec { /// The current gas price. gas_price: OffBalance, + /// The existential balance needed to create a tombstone upon contract eviction. + existential_balance: OffBalance, } impl ChainSpec { @@ -27,6 +29,7 @@ impl ChainSpec { pub fn uninitialized() -> Self { Self { gas_price: OffBalance::uninitialized(), + existential_balance: OffBalance::uninitialized(), } } @@ -37,4 +40,12 @@ impl ChainSpec { { self.gas_price.decode().map_err(Into::into) } + + /// Returns the existential balance for the chain. + pub fn existential_balance(&self) -> Result + where + T: EnvTypes, + { + self.existential_balance.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..e99616547c6 --- /dev/null +++ b/core/src/env3/engine/off_chain/db/console.rs @@ -0,0 +1,74 @@ +// 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_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/exec_context.rs b/core/src/env3/engine/off_chain/db/exec_context.rs index af6999cdf9f..dc92ebf5522 100644 --- a/core/src/env3/engine/off_chain/db/exec_context.rs +++ b/core/src/env3/engine/off_chain/db/exec_context.rs @@ -76,7 +76,7 @@ impl ExecContext { } /// Returns the transferred value. - pub fn transferred_value(&self) -> Result + pub fn transferred_value(&self) -> Result where T: EnvTypes, { @@ -84,7 +84,7 @@ impl ExecContext { } /// Returns the gas. - pub fn gas(&self) -> Result + pub fn gas(&self) -> Result where T: EnvTypes, { diff --git a/core/src/env3/engine/off_chain/db/mod.rs b/core/src/env3/engine/off_chain/db/mod.rs index c80cdb514d7..25fba97e53d 100644 --- a/core/src/env3/engine/off_chain/db/mod.rs +++ b/core/src/env3/engine/off_chain/db/mod.rs @@ -16,20 +16,25 @@ mod accounts; mod block; mod chain_spec; mod codes; +mod console; mod exec_context; pub use self::{ accounts::{ Account, - AccountKind, AccountError, + AccountKind, AccountsDb, ContractAccount, ContractStorage, }, - chain_spec::ChainSpec, block::Block, + chain_spec::ChainSpec, codes::CodeDb, + console::{ + Console, + PastPrints, + }, exec_context::ExecContext, }; use super::{ diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index a79ff3bbc94..6a21178e1cb 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -112,49 +112,85 @@ impl Env for EnvInstance { } fn println(&mut self, content: &str) { - println!("{}", content) + self.console.println(content) } } impl TypedEnv for EnvInstance { fn caller(&mut self) -> Result { - todo!() + 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 { - todo!() + 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 { - todo!() + 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 { - todo!() + self.exec_context() + .expect("uninitialized execution context") + .gas::() + .map_err(|_| scale::Error::from("could not decode gas left")) + .map_err(Into::into) } fn now_in_ms(&mut self) -> Result { - todo!() + self.current_block() + .expect("uninitialized execution context") + .moment::() + .map_err(|_| scale::Error::from("could not decode block time")) + .map_err(Into::into) } fn address(&mut self) -> Result { - todo!() + 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 { - todo!() + self.callee_account() + .balance::() + .map_err(|_| scale::Error::from("could not decode callee balance")) + .map_err(Into::into) } fn rent_allowance(&mut self) -> Result { - todo!() + 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 { - todo!() + 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 { - todo!() + self.chain_spec + .existential_balance::() + .map_err(|_| scale::Error::from("could not decode existential balance")) + .map_err(Into::into) } fn emit_event(&mut self, event: Event) @@ -165,11 +201,13 @@ impl TypedEnv for EnvInstance { todo!() } - fn set_rent_allowance(&mut self, new_value: T::Balance) + fn set_rent_allowance(&mut self, new_rent_allowance: T::Balance) where T: EnvTypes, { - todo!() + 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<()> diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 6f100da8023..77035b3e493 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -26,6 +26,7 @@ use self::{ ChainSpec, CodeDb, ExecContext, + Console, }, typed_encoded::{ TypedEncoded, @@ -69,6 +70,8 @@ pub struct EnvInstance { chain_spec: ChainSpec, /// The blocks of the chain. blocks: Vec, + /// The console to print debug contents. + console: Console, } impl EnvInstance { @@ -80,6 +83,7 @@ impl EnvInstance { exec_context: None, chain_spec: ChainSpec::uninitialized(), blocks: Vec::new(), + console: Console::new(), } } From f843820f67d6f45a129336123e105f12ca934806 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 15:10:21 +0100 Subject: [PATCH 014/112] [core] minor improvements to env3 --- core/src/env3/engine/off_chain/mod.rs | 7 +- core/src/env3/engine/off_chain/test_api.rs | 131 +++++++++++++++++++++ core/src/env3/mod.rs | 8 +- 3 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 core/src/env3/engine/off_chain/test_api.rs diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 77035b3e493..f42545b47a3 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub mod db; +mod db; mod impls; -pub mod typed_encoded; -pub mod types; +mod typed_encoded; +mod types; +pub mod test_api; use self::{ db::{ 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..31bdbb1cc74 --- /dev/null +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -0,0 +1,131 @@ +// 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. + +//! Operations on the off-chain testing environment. + +use crate::env3::{ + EnvTypes, + Result, +}; + +/// 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, +) -> Result<()> +where + T: EnvTypes, +{ + todo!() +} + +/// 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() { + todo!() +} + +/// 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_balance(account: T::AccountId, new_balance: T::Balance) -> Result<()> +where + T: EnvTypes, +{ + todo!() +} + +/// 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_balance(account: T::AccountId) -> T::Balance +where + T: EnvTypes, +{ + todo!() +} + +/// 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_rent_allowance( + account: T::AccountId, + new_rent_allowance: T::Balance, +) -> Result<()> +where + T: EnvTypes, +{ + todo!() +} + +/// 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_rent_allowance(account: T::AccountId) -> Result +where + T: EnvTypes, +{ + todo!() +} + +/// Creates a new user account and returns its account ID. +/// +/// # Errors +/// +/// - If `initial_balance` cannot be properly encoded. +pub fn create_user_account(initial_balance: T::Balance) -> Result +where + T: EnvTypes, +{ + todo!() +} diff --git a/core/src/env3/mod.rs b/core/src/env3/mod.rs index b95c0681afd..04087144603 100644 --- a/core/src/env3/mod.rs +++ b/core/src/env3/mod.rs @@ -21,13 +21,15 @@ mod api; mod backend; mod buffer; pub mod call; -pub mod engine; +mod engine; mod error; mod property; -#[cfg(any(test, doc))] -pub mod test; mod types; +#[cfg(any(test, doc))] +#[doc(inline)] +pub use self::engine::off_chain::test_api as test; + use self::backend::{ Env, TypedEnv, From 54d38f425c2cf5aea4176de8e6fb1ac1927bf81e Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 18:15:44 +0100 Subject: [PATCH 015/112] [core] further improvements and additions to test-api of env3 --- core/src/env3/engine/off_chain/db/accounts.rs | 18 +++++++ core/src/env3/engine/off_chain/mod.rs | 8 +-- core/src/env3/engine/off_chain/test_api.rs | 51 ++++++++++++++++--- core/src/env3/error.rs | 2 + 4 files changed, 67 insertions(+), 12 deletions(-) diff --git a/core/src/env3/engine/off_chain/db/accounts.rs b/core/src/env3/engine/off_chain/db/accounts.rs index 8b9cbd77e32..b21a98553a6 100644 --- a/core/src/env3/engine/off_chain/db/accounts.rs +++ b/core/src/env3/engine/off_chain/db/accounts.rs @@ -64,6 +64,14 @@ impl AccountsDb { self.get_account_off(&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.get_account_off_mut(&OffAccountId::new(&at)) + } + /// Returns the account for the given off-account ID if any. pub fn get_account_off(&self, at: &OffAccountId) -> Option<&Account> { self.accounts.get(at) @@ -108,6 +116,16 @@ impl Account { 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::(&OffBalance::new(&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 { diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index f42545b47a3..88cbb76d4cb 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -66,7 +66,7 @@ pub struct EnvInstance { /// Uploaded Wasm contract codes. codes: CodeDb, /// Current execution context and context. - exec_context: Option, + exec_context: Vec, /// The general chain spec. chain_spec: ChainSpec, /// The blocks of the chain. @@ -81,7 +81,7 @@ impl EnvInstance { Self { accounts: AccountsDb::new(), codes: CodeDb::new(), - exec_context: None, + exec_context: Vec::new(), chain_spec: ChainSpec::uninitialized(), blocks: Vec::new(), console: Console::new(), @@ -90,12 +90,12 @@ impl EnvInstance { /// Returns the current execution context. fn exec_context(&self) -> Result<&ExecContext> { - self.exec_context.as_ref().ok_or(InstanceError::UninitializedExecutionContext) + self.exec_context.last().ok_or(InstanceError::UninitializedExecutionContext) } /// Returns the current execution context. fn exec_context_mut(&mut self) -> Result<&mut ExecContext> { - self.exec_context.as_mut().ok_or(InstanceError::UninitializedExecutionContext) + self.exec_context.last_mut().ok_or(InstanceError::UninitializedExecutionContext) } /// Returns the current block of the chain. diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 31bdbb1cc74..002c18837b9 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -14,7 +14,14 @@ //! Operations on the off-chain testing environment. +use super::{ + db::ExecContext, + EnvInstance, +}; use crate::env3::{ + call::CallData, + engine::OnInstance, + EnvError, EnvTypes, Result, }; @@ -32,11 +39,21 @@ pub fn push_execution_context( callee: T::AccountId, gas_limit: T::Balance, endowment: T::Balance, -) -> Result<()> -where + call_data: CallData, +) where T: EnvTypes, { - todo!() + ::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. @@ -46,7 +63,9 @@ where /// Together with [`push_execution_context`] this can be used to emulated /// nested calls. pub fn pop_execution_context() { - todo!() + ::on_instance(|instance| { + instance.exec_context.pop(); + }) } /// Sets the balance of the account to the given balance. @@ -61,11 +80,19 @@ pub fn pop_execution_context() { /// - If `account` does not exist. /// - If the underlying `account` type does not match. /// - If the underlying `new_balance` type does not match. -pub fn set_balance(account: T::AccountId, new_balance: T::Balance) -> Result<()> +pub fn set_balance(account_id: T::AccountId, new_balance: T::Balance) -> Result<()> where T: EnvTypes, { - todo!() + ::on_instance(|instance| { + instance + .accounts + .get_account_mut::(account_id) + .ok_or(EnvError::OffChain) + .and_then(|account| { + account.set_balance::(new_balance).map_err(|_| EnvError::OffChain) + }) + }) } /// Returns the balance of the account. @@ -80,11 +107,19 @@ where /// /// - If `account` does not exist. /// - If the underlying `account` type does not match. -pub fn get_balance(account: T::AccountId) -> T::Balance +pub fn get_balance(account_id: T::AccountId) -> Result where T: EnvTypes, { - todo!() + ::on_instance(|instance| { + instance + .accounts + .get_account::(account_id) + .ok_or(EnvError::OffChain) + .and_then(|account| { + account.balance::().map_err(|_| EnvError::OffChain) + }) + }) } /// Sets the rent allowance of the contract account to the given rent allowance. diff --git a/core/src/env3/error.rs b/core/src/env3/error.rs index d2ea3e2c36c..31c31a37be5 100644 --- a/core/src/env3/error.rs +++ b/core/src/env3/error.rs @@ -19,6 +19,8 @@ use derive_more::{From, Display}; pub enum EnvError { #[display(msg = "error upon decoding")] Decode(scale::Error), + #[cfg(any(feature = "std", test, doc))] + OffChain, } /// A result of environmental operations. From ad2344d5453bda8dd8ee102dfb25ed834ebdf3f4 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 18:16:02 +0100 Subject: [PATCH 016/112] [core] remove former test-api file --- core/src/env3/test.rs | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 core/src/env3/test.rs diff --git a/core/src/env3/test.rs b/core/src/env3/test.rs deleted file mode 100644 index 4705e50b7a1..00000000000 --- a/core/src/env3/test.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. - -//! Operations on the off-chain testing environment. From 2ced2d83a0e745a5cee1c01d9eb1ff639bdd493b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 18:23:36 +0100 Subject: [PATCH 017/112] [core] env3: add support for ext_tombstone_deposit --- core/src/env3/api.rs | 16 ++++++++++++- core/src/env3/backend.rs | 5 +++- .../env3/engine/off_chain/db/chain_spec.rs | 23 ++++++++++++++----- core/src/env3/engine/off_chain/impls.rs | 11 +++++++-- core/src/env3/engine/on_chain/ext.rs | 2 ++ core/src/env3/engine/on_chain/impls.rs | 4 ++++ core/src/env3/property.rs | 4 +++- 7 files changed, 54 insertions(+), 11 deletions(-) diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index 0d09526102b..b50b62120ba 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -148,7 +148,7 @@ where }) } -/// Returns the minimum balance of the executed contract. +/// Returns the minimum balance for the contracts chain. /// /// # Errors /// @@ -162,6 +162,20 @@ where }) } +/// 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 diff --git a/core/src/env3/backend.rs b/core/src/env3/backend.rs index 33882c85596..7398c8db02d 100644 --- a/core/src/env3/backend.rs +++ b/core/src/env3/backend.rs @@ -112,9 +112,12 @@ pub trait TypedEnv: Env { /// Returns the current block number. fn block_number(&mut self) -> Result; - /// Returns the minimum balance of the executed contract. + /// 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 diff --git a/core/src/env3/engine/off_chain/db/chain_spec.rs b/core/src/env3/engine/off_chain/db/chain_spec.rs index d875a94f608..5c20e9934cf 100644 --- a/core/src/env3/engine/off_chain/db/chain_spec.rs +++ b/core/src/env3/engine/off_chain/db/chain_spec.rs @@ -20,8 +20,10 @@ use super::super::Result; pub struct ChainSpec { /// The current gas price. gas_price: OffBalance, - /// The existential balance needed to create a tombstone upon contract eviction. - existential_balance: OffBalance, + /// The minimum value an account of the chain may have. + minimum_balance: OffBalance, + /// The tombstone deposit. + tombstone_deposit: OffBalance, } impl ChainSpec { @@ -29,7 +31,8 @@ impl ChainSpec { pub fn uninitialized() -> Self { Self { gas_price: OffBalance::uninitialized(), - existential_balance: OffBalance::uninitialized(), + minimum_balance: OffBalance::uninitialized(), + tombstone_deposit: OffBalance::uninitialized(), } } @@ -41,11 +44,19 @@ impl ChainSpec { self.gas_price.decode().map_err(Into::into) } - /// Returns the existential balance for the chain. - pub fn existential_balance(&self) -> Result + /// Returns the minimum balance for an account on the chain. + pub fn minimum_balance(&self) -> Result where T: EnvTypes, { - self.existential_balance.decode().map_err(Into::into) + 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) } } diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index 6a21178e1cb..2dbd2f81fbc 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -188,8 +188,15 @@ impl TypedEnv for EnvInstance { fn minimum_balance(&mut self) -> Result { self.chain_spec - .existential_balance::() - .map_err(|_| scale::Error::from("could not decode existential balance")) + .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) } diff --git a/core/src/env3/engine/on_chain/ext.rs b/core/src/env3/engine/on_chain/ext.rs index b5106300cbf..0d1982a693f 100644 --- a/core/src/env3/engine/on_chain/ext.rs +++ b/core/src/env3/engine/on_chain/ext.rs @@ -85,6 +85,7 @@ mod sys { 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); @@ -239,6 +240,7 @@ 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]) { diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index 38b07d3f9be..a7af6e875c8 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -218,6 +218,10 @@ impl TypedEnv for EnvInstance { 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, diff --git a/core/src/env3/property.rs b/core/src/env3/property.rs index d1f5ae2700d..6c28b9fe27f 100644 --- a/core/src/env3/property.rs +++ b/core/src/env3/property.rs @@ -96,8 +96,10 @@ impl_read_property_for! { struct Balance { read: Some }, /// The current block number. struct BlockNumber { read: Some }, - /// The minimum possible balance for a contract. + /// The minimum possible balance for an account on the chain. struct MinimumBalance { read: Some }, + /// The tombstone deposit on the contract chain. + struct TombstoneDeposit { read: Some }, } /// The input data for this contract execution. From 3c98a8be4cb011d584d07bf002aab1ff6671b2fb Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 18:27:19 +0100 Subject: [PATCH 018/112] [core] env3: add setting of rent allowance in off-chain env --- core/src/env3/engine/off_chain/test_api.rs | 24 ++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 002c18837b9..815b4910b6b 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -130,13 +130,21 @@ where /// - If the underlying `account` type does not match. /// - If the underlying `new_rent_allowance` type does not match. pub fn set_rent_allowance( - account: T::AccountId, + account_id: T::AccountId, new_rent_allowance: T::Balance, ) -> Result<()> where T: EnvTypes, { - todo!() + ::on_instance(|instance| { + instance + .accounts + .get_account_mut::(account_id) + .ok_or(EnvError::OffChain) + .and_then(|account| { + account.set_rent_allowance::(new_rent_allowance).map_err(|_| EnvError::OffChain) + }) + }) } /// Returns the rent allowance of the contract account. @@ -146,11 +154,19 @@ where /// - 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_rent_allowance(account: T::AccountId) -> Result +pub fn get_rent_allowance(account_id: T::AccountId) -> Result where T: EnvTypes, { - todo!() + ::on_instance(|instance| { + instance + .accounts + .get_account::(account_id) + .ok_or(EnvError::OffChain) + .and_then(|account| { + account.rent_allowance::().map_err(|_| EnvError::OffChain) + }) + }) } /// Creates a new user account and returns its account ID. From c0e70f8e566d9fe9aa04d9c3216c22f8fb636701 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 18:59:09 +0100 Subject: [PATCH 019/112] [core] env3: remove buffer.rs --- core/src/env3/buffer.rs | 67 ----------------------------------------- core/src/env3/mod.rs | 1 - 2 files changed, 68 deletions(-) delete mode 100644 core/src/env3/buffer.rs diff --git a/core/src/env3/buffer.rs b/core/src/env3/buffer.rs deleted file mode 100644 index bf18db66ba6..00000000000 --- a/core/src/env3/buffer.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. - -//! 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()) - } - } -} diff --git a/core/src/env3/mod.rs b/core/src/env3/mod.rs index 04087144603..97f2155ecbf 100644 --- a/core/src/env3/mod.rs +++ b/core/src/env3/mod.rs @@ -19,7 +19,6 @@ mod api; mod backend; -mod buffer; pub mod call; mod engine; mod error; From 95eafb7f574a88e87db22b83397a35eeaab02892 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 19:06:54 +0100 Subject: [PATCH 020/112] [core] env3: remove property.rs --- core/src/env3/engine/on_chain/impls.rs | 30 +++--- core/src/env3/mod.rs | 1 - core/src/env3/property.rs | 135 ------------------------- 3 files changed, 14 insertions(+), 152 deletions(-) delete mode 100644 core/src/env3/property.rs diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index a7af6e875c8..5767e319197 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -24,8 +24,6 @@ use crate::{ CreateParams, ReturnType, }, - property, - property::ReadProperty, Env, EnvTypes, Result, @@ -90,9 +88,9 @@ impl EnvInstance { } /// Returns the contract property value. - fn get_property

(&mut self, ext_fn: fn()) -> Result + fn get_property(&mut self, ext_fn: fn()) -> Result where - P: ReadProperty, + T: scale::Decode, { ext_fn(); self.decode_scratch_buffer().map_err(Into::into) @@ -161,7 +159,7 @@ impl Env for EnvInstance { } fn input(&mut self) -> Result { - self.get_property::(|| ()) + self.get_property::(|| ()) } fn output(&mut self, return_value: &R) @@ -179,47 +177,47 @@ impl Env for EnvInstance { impl TypedEnv for EnvInstance { fn caller(&mut self) -> Result { - self.get_property::>(ext::caller) + self.get_property::(ext::caller) } fn transferred_balance(&mut self) -> Result { - self.get_property::>(ext::value_transferred) + self.get_property::(ext::value_transferred) } fn gas_price(&mut self) -> Result { - self.get_property::>(ext::gas_price) + self.get_property::(ext::gas_price) } fn gas_left(&mut self) -> Result { - self.get_property::>(ext::gas_left) + self.get_property::(ext::gas_left) } fn now_in_ms(&mut self) -> Result { - self.get_property::>(ext::now) + self.get_property::(ext::now) } fn address(&mut self) -> Result { - self.get_property::>(ext::address) + self.get_property::(ext::address) } fn balance(&mut self) -> Result { - self.get_property::>(ext::balance) + self.get_property::(ext::balance) } fn rent_allowance(&mut self) -> Result { - self.get_property::>(ext::rent_allowance) + self.get_property::(ext::rent_allowance) } fn block_number(&mut self) -> Result { - self.get_property::>(ext::block_number) + self.get_property::(ext::block_number) } fn minimum_balance(&mut self) -> Result { - self.get_property::>(ext::minimum_balance) + self.get_property::(ext::minimum_balance) } fn tombstone_deposit(&mut self) -> Result { - self.get_property::>(ext::tombstone_deposit) + self.get_property::(ext::tombstone_deposit) } fn emit_event(&mut self, event: Event) diff --git a/core/src/env3/mod.rs b/core/src/env3/mod.rs index 97f2155ecbf..3266ded7f1a 100644 --- a/core/src/env3/mod.rs +++ b/core/src/env3/mod.rs @@ -22,7 +22,6 @@ mod backend; pub mod call; mod engine; mod error; -mod property; mod types; #[cfg(any(test, doc))] diff --git a/core/src/env3/property.rs b/core/src/env3/property.rs deleted file mode 100644 index 6c28b9fe27f..00000000000 --- a/core/src/env3/property.rs +++ /dev/null @@ -1,135 +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::env3::{ - 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_read_property_for { - ( - $( #[$meta:meta] )* - struct $name:ident { read: Some<$in:ty> }, $($tt:tt)* - ) => { - $( #[$meta] )* - pub struct $name { marker: PhantomData E>, } - impl $crate::env3::property::Property for $name {} - impl $crate::env3::property::private::PropertySeal for $name {} - impl $crate::env3::property::ReadProperty for $name - where - E: EnvTypes, - { - type In = $in; - } - impl $crate::env3::property::private::ReadPropertySeal for $name {} - - impl_read_property_for! { $($tt)* } - }; - () => {}; -} - -impl_read_property_for! { - /// The caller of an executed contract. - struct Caller { read: Some }, - /// The transferred balance for the contract execution. - struct TransferredBalance { read: Some }, - /// The current gas price. - struct GasPrice { read: Some }, - /// The amount of gas left for the current contract execution. - struct GasLeft { read: Some }, - /// The block time in milli seconds. - struct NowInMs { read: Some }, - /// The account ID of the executed contract. - struct Address { read: Some }, - /// The balance of the executed contract. - struct Balance { read: Some }, - /// The current block number. - struct BlockNumber { read: Some }, - /// The minimum possible balance for an account on the chain. - struct MinimumBalance { read: Some }, - /// The tombstone deposit on the contract chain. - struct TombstoneDeposit { read: Some }, -} - -/// The input data for this contract execution. -pub struct Input; - -impl Property for Input {} -impl ReadProperty for Input { - type In = CallData; -} -impl private::PropertySeal for Input {} -impl private::ReadPropertySeal for Input {} - -/// The rent allowance of the executed contract. -pub struct RentAllowance { - marker: PhantomData T>, -} - -impl Property for RentAllowance {} -impl WriteProperty for RentAllowance -where - T: EnvTypes, -{ - type Out = T::Balance; -} -impl ReadProperty for RentAllowance -where - T: EnvTypes, -{ - type In = T::Balance; -} -impl private::PropertySeal for RentAllowance {} -impl private::WritePropertySeal for RentAllowance {} -impl private::ReadPropertySeal for RentAllowance {} From e48a4a86024b5f7c69f776be406b487af2fde0e2 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 19:10:46 +0100 Subject: [PATCH 021/112] [core] env3: mark some TypedEnv methods as not-todo --- core/src/env3/engine/off_chain/impls.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index 2dbd2f81fbc..1cdafb2fd29 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -217,11 +217,11 @@ impl TypedEnv for EnvInstance { .expect("could not encode rent allowance") } - fn invoke_contract(&mut self, call_params: &CallParams) -> Result<()> + fn invoke_contract(&mut self, _call_params: &CallParams) -> Result<()> where T: EnvTypes, { - todo!() + unimplemented!("in off-chain environment contracts call each other directly") } fn invoke_runtime(&mut self, params: &T::Call) -> Result<()> @@ -233,23 +233,23 @@ impl TypedEnv for EnvInstance { fn eval_contract( &mut self, - call_params: &CallParams>, + _call_params: &CallParams>, ) -> Result where T: EnvTypes, R: scale::Decode, { - todo!() + unimplemented!("in off-chain environment contracts call each other directly") } fn create_contract( &mut self, - params: &CreateParams, + _params: &CreateParams, ) -> Result where T: EnvTypes, { - todo!() + unimplemented!("in off-chain environment contracts create each other directly") } fn restore_contract( From 42b0507c691df7027e54c273342121f7e1b6148a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 19:11:08 +0100 Subject: [PATCH 022/112] [core] env3: enable off-chain api for non test/doc builds --- core/src/env3/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/env3/mod.rs b/core/src/env3/mod.rs index 3266ded7f1a..fec70ab4113 100644 --- a/core/src/env3/mod.rs +++ b/core/src/env3/mod.rs @@ -24,7 +24,7 @@ mod engine; mod error; mod types; -#[cfg(any(test, doc))] +#[cfg(any(feature = "std", test, doc))] #[doc(inline)] pub use self::engine::off_chain::test_api as test; From d48be62e09bba79715d9f84024952264a9803fa7 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 23 Jan 2020 23:52:30 +0100 Subject: [PATCH 023/112] [core] env3: use a static buffer instead of Vec --- core/src/env3/engine/on_chain/impls.rs | 18 +++-- core/src/env3/engine/on_chain/mod.rs | 94 ++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 11 deletions(-) diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index 5767e319197..0281e11f472 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -41,7 +41,12 @@ impl EnvInstance { /// This is useful to perform before invoking a series of /// [`WasmEnv::append_encode_into_buffer`]. fn reset_buffer(&mut self) { - self.buffer.clear() + 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. @@ -49,7 +54,7 @@ impl EnvInstance { /// Returns the amount of bytes read. fn read_scratch_buffer(&mut self) -> usize { let req_len = ext::scratch_size(); - self.buffer.resize(req_len, Default::default()); + self.resize_buffer(req_len); ext::scratch_read(&mut self.buffer[0..req_len], 0); req_len } @@ -72,6 +77,7 @@ impl EnvInstance { where T: scale::Encode, { + self.reset_buffer(); scale::Encode::encode_to(&value, &mut self.buffer); } @@ -131,7 +137,7 @@ impl Env for EnvInstance { V: scale::Encode, { self.encode_into_buffer(value); - ext::set_storage(key.as_bytes(), &self.buffer); + ext::set_storage(key.as_bytes(), &self.buffer[..]); } fn get_contract_storage(&mut self, key: Key) -> Result @@ -167,7 +173,7 @@ impl Env for EnvInstance { R: scale::Encode, { self.encode_into_buffer(return_value); - ext::scratch_write(&self.buffer); + ext::scratch_write(&self.buffer[..]); } fn println(&mut self, content: &str) { @@ -243,7 +249,7 @@ impl TypedEnv for EnvInstance { T: EnvTypes, { self.encode_into_buffer(&new_value); - ext::set_rent_allowance(&self.buffer) + ext::set_rent_allowance(&self.buffer[..]) } fn invoke_runtime(&mut self, call: &T::Call) -> Result<()> @@ -251,7 +257,7 @@ impl TypedEnv for EnvInstance { T: EnvTypes { self.encode_into_buffer(call); - ext::dispatch_call(&self.buffer); + ext::dispatch_call(&self.buffer[..]); Ok(()) } diff --git a/core/src/env3/engine/on_chain/mod.rs b/core/src/env3/engine/on_chain/mod.rs index 2c300f1ea33..b30192118ff 100644 --- a/core/src/env3/engine/on_chain/mod.rs +++ b/core/src/env3/engine/on_chain/mod.rs @@ -16,23 +16,107 @@ mod ext; mod impls; mod retcode; -use ink_prelude::vec::Vec; use super::OnInstance; pub(crate) use self::retcode::RetCode; +/// 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) + } +} + /// The on-chain environment. pub struct EnvInstance { - /// Encode & decode buffer for potentially reusing required dynamic allocations. - buffer: Vec, + /// 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 + F: FnOnce(&mut Self) -> R, { - static mut INSTANCE: EnvInstance = EnvInstance { buffer: Vec::new() }; + static mut INSTANCE: EnvInstance = EnvInstance { + buffer: StaticBuffer::new(), + }; f(unsafe { &mut INSTANCE }) } } From 839ebabb9c6c822c6df02a14993896c7df393bd5 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 24 Jan 2020 00:11:06 +0100 Subject: [PATCH 024/112] [core] env3: move static buffer into its own module --- core/src/env3/engine/on_chain/buffer.rs | 88 +++++++++++++++++++++++++ core/src/env3/engine/on_chain/mod.rs | 77 +--------------------- 2 files changed, 90 insertions(+), 75 deletions(-) create mode 100644 core/src/env3/engine/on_chain/buffer.rs 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..1cb072f5f0d --- /dev/null +++ b/core/src/env3/engine/on_chain/buffer.rs @@ -0,0 +1,88 @@ +// 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. + +/// 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/env3/engine/on_chain/mod.rs b/core/src/env3/engine/on_chain/mod.rs index b30192118ff..153663f036b 100644 --- a/core/src/env3/engine/on_chain/mod.rs +++ b/core/src/env3/engine/on_chain/mod.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod buffer; mod ext; mod impls; mod retcode; @@ -19,81 +20,7 @@ mod retcode; use super::OnInstance; pub(crate) use self::retcode::RetCode; - -/// 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) - } -} +use self::buffer::StaticBuffer; /// The on-chain environment. pub struct EnvInstance { From 64dc81f87850366b1208650f548138f4df518375 Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Fri, 24 Jan 2020 00:42:33 +0100 Subject: [PATCH 025/112] fix doc comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Michael Müller --- core/src/env3/call/create.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/env3/call/create.rs b/core/src/env3/call/create.rs index d650785b4db..7a18b538dba 100644 --- a/core/src/env3/call/create.rs +++ b/core/src/env3/call/create.rs @@ -135,7 +135,7 @@ impl CreateBuilder where 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 From b94f39313f2c00efdad0baba03db3b9acd122220 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 24 Jan 2020 00:43:35 +0100 Subject: [PATCH 026/112] [core] env3: add missing license header --- core/src/env3/api.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index b50b62120ba..370d2d380e1 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -1,3 +1,17 @@ +// 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 public raw interface towards the host Wasm engine. //! //! # Note From 58425fd64aa36caf0b9e4fd9226bf42fd7b2e3f4 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 24 Jan 2020 00:51:47 +0100 Subject: [PATCH 027/112] [core] env3: remove deprecated doc note --- core/src/env3/api.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index 370d2d380e1..1ad16afee57 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -13,13 +13,6 @@ // limitations under the License. //! The public raw interface towards the host Wasm engine. -//! -//! # Note -//! -//! Prefer using the dedicated `EnvAccess` and `EnvAccessMut` types in order -//! to interoperate with the environment as they already have their associated -//! environemntal types associated to them and provide additional safety in some -//! scenarios. use crate::{ env3::{ From 87564a7a5e39cff1c24f6768403d401e8ffd9771 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 24 Jan 2020 22:54:53 +0100 Subject: [PATCH 028/112] [core] env3: add off-chain {set/get}_runtime_storage --- core/src/env3/api.rs | 4 +- core/src/env3/backend.rs | 2 +- core/src/env3/engine/off_chain/impls.rs | 4 +- core/src/env3/engine/off_chain/mod.rs | 19 +++++-- .../env3/engine/off_chain/runtime_storage.rs | 54 +++++++++++++++++++ core/src/env3/engine/off_chain/test_api.rs | 10 ++++ core/src/env3/engine/on_chain/impls.rs | 6 +-- 7 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 core/src/env3/engine/off_chain/runtime_storage.rs diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index 1ad16afee57..1aa4fc3e11d 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -433,13 +433,13 @@ 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. +/// 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]) -> Result +pub fn get_runtime_storage(runtime_key: &[u8]) -> Option> where R: scale::Decode, { diff --git a/core/src/env3/backend.rs b/core/src/env3/backend.rs index 7398c8db02d..7d4a47c56a8 100644 --- a/core/src/env3/backend.rs +++ b/core/src/env3/backend.rs @@ -53,7 +53,7 @@ pub trait Env { /// /// - If the key's entry is empty /// - If the decoding of the typed value failed - fn get_runtime_storage(&mut self, runtime_key: &[u8]) -> Result + fn get_runtime_storage(&mut self, runtime_key: &[u8]) -> Option> where R: scale::Decode; diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index 1cdafb2fd29..8732640f595 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -86,11 +86,11 @@ impl Env for EnvInstance { .expect("callee account is not a smart contract"); } - fn get_runtime_storage(&mut self, runtime_key: &[u8]) -> Result + fn get_runtime_storage(&mut self, runtime_key: &[u8]) -> Option> where R: scale::Decode, { - todo!() + self.runtime_storage.load::(runtime_key) } fn input(&mut self) -> Result { diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 88cbb76d4cb..76bf45436ff 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -14,21 +14,23 @@ mod db; mod impls; +mod runtime_storage; +pub mod test_api; mod typed_encoded; mod types; -pub mod test_api; use self::{ db::{ + Account, AccountError, AccountsDb, - Account, Block, ChainSpec, CodeDb, - ExecContext, Console, + ExecContext, }, + runtime_storage::RuntimeStorage, typed_encoded::{ TypedEncoded, TypedEncodedError, @@ -73,6 +75,8 @@ pub struct EnvInstance { blocks: Vec, /// The console to print debug contents. console: Console, + /// The emulated runtime storage. + runtime_storage: RuntimeStorage, } impl EnvInstance { @@ -85,17 +89,22 @@ impl EnvInstance { chain_spec: ChainSpec::uninitialized(), blocks: Vec::new(), console: Console::new(), + runtime_storage: RuntimeStorage::new(), } } /// Returns the current execution context. fn exec_context(&self) -> Result<&ExecContext> { - self.exec_context.last().ok_or(InstanceError::UninitializedExecutionContext) + self.exec_context + .last() + .ok_or(InstanceError::UninitializedExecutionContext) } /// Returns the current execution context. fn exec_context_mut(&mut self) -> Result<&mut ExecContext> { - self.exec_context.last_mut().ok_or(InstanceError::UninitializedExecutionContext) + self.exec_context + .last_mut() + .ok_or(InstanceError::UninitializedExecutionContext) } /// Returns the current block of the chain. 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..098b29101e2 --- /dev/null +++ b/core/src/env3/engine/off_chain/runtime_storage.rs @@ -0,0 +1,54 @@ +// 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::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 index 815b4910b6b..2c79b3922f8 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -180,3 +180,13 @@ where { todo!() } + +/// 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) + }) +} diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index 0281e11f472..fa939fc65e9 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -154,14 +154,14 @@ impl Env for EnvInstance { ext::clear_storage(key.as_bytes()) } - fn get_runtime_storage(&mut self, runtime_key: &[u8]) -> Result + fn get_runtime_storage(&mut self, runtime_key: &[u8]) -> Option> where R: scale::Decode, { if !ext::get_runtime_storage(runtime_key).is_success() { - todo!() + return None } - self.decode_scratch_buffer().map_err(Into::into) + Some(self.decode_scratch_buffer().map_err(Into::into)) } fn input(&mut self) -> Result { From 8d5c6442e612d031e1a2ab02e11246dd8303956b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 24 Jan 2020 23:42:04 +0100 Subject: [PATCH 029/112] [core] env3: add off-chain invoke_runtime dispatch This also enables support for registering custom runtime call handlers. --- core/src/env3/engine/off_chain/impls.rs | 2 +- core/src/env3/engine/off_chain/mod.rs | 6 ++ .../env3/engine/off_chain/runtime_calls.rs | 64 +++++++++++++++++++ core/src/env3/engine/off_chain/test_api.rs | 11 ++++ core/src/env3/types.rs | 2 +- 5 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 core/src/env3/engine/off_chain/runtime_calls.rs diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index 8732640f595..03e15b783e5 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -228,7 +228,7 @@ impl TypedEnv for EnvInstance { where T: EnvTypes, { - todo!() + self.runtime_call_handler.invoke::(params) } fn eval_contract( diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 76bf45436ff..7a9feb26d84 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -14,6 +14,7 @@ mod db; mod impls; +mod runtime_calls; mod runtime_storage; pub mod test_api; mod typed_encoded; @@ -31,6 +32,7 @@ use self::{ ExecContext, }, runtime_storage::RuntimeStorage, + runtime_calls::RuntimeCallHandler, typed_encoded::{ TypedEncoded, TypedEncodedError, @@ -41,6 +43,7 @@ use self::{ OffBlockNumber, OffHash, OffMoment, + OffCall, }, }; use super::OnInstance; @@ -77,6 +80,8 @@ pub struct EnvInstance { console: Console, /// The emulated runtime storage. runtime_storage: RuntimeStorage, + /// The runtime calls handler. + runtime_call_handler: RuntimeCallHandler, } impl EnvInstance { @@ -90,6 +95,7 @@ impl EnvInstance { blocks: Vec::new(), console: Console::new(), runtime_storage: RuntimeStorage::new(), + runtime_call_handler: RuntimeCallHandler::new(), } } 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..dc5cce02c48 --- /dev/null +++ b/core/src/env3/engine/off_chain/runtime_calls.rs @@ -0,0 +1,64 @@ +// 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::OffCall; +use crate::env3::{ + EnvTypes, + Result, + EnvError, +}; + +/// 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(EnvError::OffChain), + } + } +} diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 2c79b3922f8..387c5e86a50 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -190,3 +190,14 @@ where 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) + }) +} diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs index 41142942a9f..186ec10d558 100644 --- a/core/src/env3/types.rs +++ b/core/src/env3/types.rs @@ -47,7 +47,7 @@ pub trait EnvTypes { /// The type of block number. type BlockNumber: 'static + scale::Codec + Clone + PartialEq + Eq; /// The type of a call into the runtime - type Call: 'static + scale::Encode; + type Call: 'static + scale::Codec; } /// Implemented by event types to communicate their topic hashes. From f02c6f604baa817b0bab1d74a114db72aaaafc1d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 25 Jan 2020 19:47:14 +0100 Subject: [PATCH 030/112] [core] env3: slightly improve env3::random docs comment --- core/src/env3/api.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index 1aa4fc3e11d..10581d6a5e8 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -414,7 +414,8 @@ where /// /// # Note /// -/// The subject buffer can be used to further randomize the hash. +/// - 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 /// From 0352a31db650df0fcaa0a46386ceee113bda5856 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 25 Jan 2020 19:48:45 +0100 Subject: [PATCH 031/112] [core] env3: move off-chain errors under EnvError --- core/src/env3/engine/mod.rs | 9 +++- core/src/env3/engine/off_chain/db/accounts.rs | 40 +++++++++++++---- core/src/env3/engine/off_chain/impls.rs | 20 ++++----- core/src/env3/engine/off_chain/mod.rs | 26 ++++++----- .../env3/engine/off_chain/runtime_calls.rs | 9 ++-- core/src/env3/engine/off_chain/test_api.rs | 44 +++++++++++-------- .../env3/engine/off_chain/typed_encoded.rs | 8 ++++ core/src/env3/error.rs | 9 ++-- 8 files changed, 110 insertions(+), 55 deletions(-) diff --git a/core/src/env3/engine/mod.rs b/core/src/env3/engine/mod.rs index 64362817636..345a8113fb4 100644 --- a/core/src/env3/engine/mod.rs +++ b/core/src/env3/engine/mod.rs @@ -12,8 +12,11 @@ // 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; -use crate::env3::backend::{TypedEnv, Env}; pub trait OnInstance: Env + TypedEnv { fn on_instance(f: F) -> R @@ -28,6 +31,10 @@ cfg_if! { } 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 index b21a98553a6..9d91cb43016 100644 --- a/core/src/env3/engine/off_chain/db/accounts.rs +++ b/core/src/env3/engine/off_chain/db/accounts.rs @@ -13,13 +13,19 @@ // limitations under the License. use super::{ - super::TypedEncodedError, + super::{ + OffChainError, + TypedEncodedError, + }, OffAccountId, OffBalance, OffHash, }; use crate::{ - env3::EnvTypes, + env3::{ + EnvError, + EnvTypes, + }, storage::Key, }; use derive_more::From; @@ -31,6 +37,24 @@ 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 { @@ -57,23 +81,23 @@ impl AccountsDb { } /// Returns the account for the given account ID if any. - pub fn get_account(&self, at: T::AccountId) -> Option<&Account> + pub fn get_account(&self, at: &T::AccountId) -> Option<&Account> where T: EnvTypes, { - self.get_account_off(&OffAccountId::new(&at)) + 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> + pub fn get_account_mut(&mut self, at: &T::AccountId) -> Option<&mut Account> where T: EnvTypes, { - self.get_account_off_mut(&OffAccountId::new(&at)) + self.accounts.get_mut(&OffAccountId::new(at)) } /// Returns the account for the given off-account ID if any. - pub fn get_account_off(&self, at: &OffAccountId) -> Option<&Account> { + pub fn get_account_off<'a>(&'a self, at: &OffAccountId) -> Option<&'a Account> { self.accounts.get(at) } @@ -83,7 +107,7 @@ impl AccountsDb { } /// Creates a new user account. - pub fn new_user_account(&mut self) -> T::AccountId + pub fn new_user_account(&mut self, initial_balance: T::Balance) -> T::AccountId where T: EnvTypes, { diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index 03e15b783e5..780f2b181a3 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -200,7 +200,7 @@ impl TypedEnv for EnvInstance { .map_err(Into::into) } - fn emit_event(&mut self, event: Event) + fn emit_event(&mut self, _event: Event) where T: EnvTypes, Event: Topics + scale::Encode, @@ -221,7 +221,7 @@ impl TypedEnv for EnvInstance { where T: EnvTypes, { - unimplemented!("in off-chain environment contracts call each other directly") + unimplemented!("off-chain environment does not support contract invokation") } fn invoke_runtime(&mut self, params: &T::Call) -> Result<()> @@ -239,7 +239,7 @@ impl TypedEnv for EnvInstance { T: EnvTypes, R: scale::Decode, { - unimplemented!("in off-chain environment contracts call each other directly") + unimplemented!("off-chain environment does not support contract evaluation") } fn create_contract( @@ -249,22 +249,22 @@ impl TypedEnv for EnvInstance { where T: EnvTypes, { - unimplemented!("in off-chain environment contracts create each other directly") + 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], + _account_id: T::AccountId, + _code_hash: T::Hash, + _rent_allowance: T::Balance, + _filtered_keys: &[Key], ) where T: EnvTypes, { - todo!() + unimplemented!("off-chain environment does not support contract restoration") } - fn random(&mut self, subject: &[u8]) -> Result + fn random(&mut self, _subject: &[u8]) -> Result where T: EnvTypes, { diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 7a9feb26d84..f2be0110c1e 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -20,10 +20,13 @@ pub mod test_api; mod typed_encoded; mod types; +pub use self::{ + db::AccountError, + typed_encoded::TypedEncodedError, +}; use self::{ db::{ Account, - AccountError, AccountsDb, Block, ChainSpec, @@ -31,19 +34,16 @@ use self::{ Console, ExecContext, }, - runtime_storage::RuntimeStorage, runtime_calls::RuntimeCallHandler, - typed_encoded::{ - TypedEncoded, - TypedEncodedError, - }, + runtime_storage::RuntimeStorage, + typed_encoded::TypedEncoded, types::{ OffAccountId, OffBalance, OffBlockNumber, + OffCall, OffHash, OffMoment, - OffCall, }, }; use super::OnInstance; @@ -51,16 +51,18 @@ use core::cell::RefCell; use derive_more::From; #[derive(Debug, From)] -pub enum InstanceError { +pub enum OffChainError { Account(AccountError), TypedEncoded(TypedEncodedError), #[from(ignore)] UninitializedBlocks, #[from(ignore)] UninitializedExecutionContext, + #[from(ignore)] + UnregisteredRuntimeCallHandler, } -pub type Result = core::result::Result; +pub type Result = core::result::Result; /// The off-chain environment. /// @@ -103,19 +105,19 @@ impl EnvInstance { fn exec_context(&self) -> Result<&ExecContext> { self.exec_context .last() - .ok_or(InstanceError::UninitializedExecutionContext) + .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(InstanceError::UninitializedExecutionContext) + .ok_or(OffChainError::UninitializedExecutionContext) } /// Returns the current block of the chain. fn current_block(&self) -> Result<&Block> { - self.blocks.last().ok_or(InstanceError::UninitializedBlocks) + self.blocks.last().ok_or(OffChainError::UninitializedBlocks) } } diff --git a/core/src/env3/engine/off_chain/runtime_calls.rs b/core/src/env3/engine/off_chain/runtime_calls.rs index dc5cce02c48..e3a1d04deab 100644 --- a/core/src/env3/engine/off_chain/runtime_calls.rs +++ b/core/src/env3/engine/off_chain/runtime_calls.rs @@ -12,11 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::OffCall; +use super::{ + OffCall, + OffChainError, +}; use crate::env3::{ + EnvError, EnvTypes, Result, - EnvError, }; /// Runtime call handler. @@ -58,7 +61,7 @@ impl RuntimeCallHandler { handler(OffCall::new(params)); Ok(()) } - None => Err(EnvError::OffChain), + None => Err(OffChainError::UnregisteredRuntimeCallHandler)?, } } } diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 387c5e86a50..7a5090fbb00 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -16,11 +16,13 @@ use super::{ db::ExecContext, + AccountError, EnvInstance, + OffChainError, + OnInstance, }; use crate::env3::{ call::CallData, - engine::OnInstance, EnvError, EnvTypes, Result, @@ -87,11 +89,10 @@ where ::on_instance(|instance| { instance .accounts - .get_account_mut::(account_id) - .ok_or(EnvError::OffChain) - .and_then(|account| { - account.set_balance::(new_balance).map_err(|_| EnvError::OffChain) - }) + .get_account_mut::(&account_id) + .ok_or(AccountError::no_account_for_id::(&account_id)) + .map_err(Into::into) + .and_then(|account| account.set_balance::(new_balance).map_err(Into::into)) }) } @@ -114,11 +115,10 @@ where ::on_instance(|instance| { instance .accounts - .get_account::(account_id) - .ok_or(EnvError::OffChain) - .and_then(|account| { - account.balance::().map_err(|_| EnvError::OffChain) - }) + .get_account::(&account_id) + .ok_or(AccountError::no_account_for_id::(&account_id)) + .map_err(Into::into) + .and_then(|account| account.balance::().map_err(Into::into)) }) } @@ -139,10 +139,13 @@ where ::on_instance(|instance| { instance .accounts - .get_account_mut::(account_id) - .ok_or(EnvError::OffChain) + .get_account_mut::(&account_id) + .ok_or(AccountError::no_account_for_id::(&account_id)) + .map_err(Into::into) .and_then(|account| { - account.set_rent_allowance::(new_rent_allowance).map_err(|_| EnvError::OffChain) + account + .set_rent_allowance::(new_rent_allowance) + .map_err(Into::into) }) }) } @@ -161,10 +164,13 @@ where ::on_instance(|instance| { instance .accounts - .get_account::(account_id) - .ok_or(EnvError::OffChain) + .get_account::(&account_id) + .ok_or(AccountError::no_account_for_id::(&account_id)) + .map_err(Into::into) .and_then(|account| { - account.rent_allowance::().map_err(|_| EnvError::OffChain) + account + .rent_allowance::() + .map_err(Into::into) }) }) } @@ -178,7 +184,9 @@ pub fn create_user_account(initial_balance: T::Balance) -> Result::on_instance(|instance| { + Ok(instance.accounts.new_user_account::(initial_balance)) + }) } /// Sets the runtime storage to value for the given key. diff --git a/core/src/env3/engine/off_chain/typed_encoded.rs b/core/src/env3/engine/off_chain/typed_encoded.rs index b6bae301e0a..e4874b1e7fe 100644 --- a/core/src/env3/engine/off_chain/typed_encoded.rs +++ b/core/src/env3/engine/off_chain/typed_encoded.rs @@ -21,6 +21,8 @@ use core::{ }, marker::PhantomData, }; +use super::OffChainError; +use crate::env3::EnvError; use derive_more::From; /// A wrapper around an encoded entity that only allows type safe accesses. @@ -86,6 +88,12 @@ pub enum TypedEncodedError { 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; diff --git a/core/src/env3/error.rs b/core/src/env3/error.rs index 31c31a37be5..c3c48840f70 100644 --- a/core/src/env3/error.rs +++ b/core/src/env3/error.rs @@ -14,13 +14,16 @@ use derive_more::{From, Display}; +#[cfg(any(feature = "std", test, doc))] +use crate::env3::engine::off_chain::OffChainError; + /// Errors that can be encountered upon environmental interaction. -#[derive(From, Display)] +#[derive(From)] pub enum EnvError { - #[display(msg = "error upon decoding")] + // #[display(msg = "error upon decoding")] Decode(scale::Error), #[cfg(any(feature = "std", test, doc))] - OffChain, + OffChain(OffChainError), } /// A result of environmental operations. From d4d48c0b53c1baf46b7579a723b49fa5ac7830f8 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 25 Jan 2020 20:34:17 +0100 Subject: [PATCH 032/112] [core] env3: add ext_random support for the off-chain environment --- core/src/env3/engine/off_chain/db/block.rs | 52 ++++++++++++++++++- core/src/env3/engine/off_chain/impls.rs | 7 ++- core/src/env3/engine/off_chain/mod.rs | 5 ++ core/src/env3/engine/off_chain/test_api.rs | 15 ++++++ .../env3/engine/off_chain/typed_encoded.rs | 14 ++++- 5 files changed, 89 insertions(+), 4 deletions(-) diff --git a/core/src/env3/engine/off_chain/db/block.rs b/core/src/env3/engine/off_chain/db/block.rs index eaff5abd941..d81b2d8b690 100644 --- a/core/src/env3/engine/off_chain/db/block.rs +++ b/core/src/env3/engine/off_chain/db/block.rs @@ -19,6 +19,7 @@ use super::{ }, OffBlockNumber, OffMoment, + OffHash, }; use crate::env3::EnvTypes; @@ -28,17 +29,25 @@ pub struct Block { number: OffBlockNumber, /// The current moment of block creation. moment: OffMoment, + /// 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 moment. - pub fn new(number: T::BlockNumber, moment: T::Moment) -> Self + pub fn new(number: T::BlockNumber, moment: T::Moment, entropy: T::Hash) -> Self where T: EnvTypes, { Self { number: TypedEncoded::new(&number), moment: TypedEncoded::new(&moment), + entropy: TypedEncoded::new(&entropy), } } @@ -57,4 +66,45 @@ impl Block { { self.moment.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::(&OffHash::new(&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 mut entropy_bytes = entropy.encoded_bytes_mut()?; + let mut 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/impls.rs b/core/src/env3/engine/off_chain/impls.rs index 780f2b181a3..a658c85100e 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -264,10 +264,13 @@ impl TypedEnv for EnvInstance { unimplemented!("off-chain environment does not support contract restoration") } - fn random(&mut self, _subject: &[u8]) -> Result + fn random(&mut self, subject: &[u8]) -> Result where T: EnvTypes, { - todo!() + 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 index f2be0110c1e..66547995d00 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -119,6 +119,11 @@ impl EnvInstance { fn current_block(&self) -> Result<&Block> { self.blocks.last().ok_or(OffChainError::UninitializedBlocks) } + + /// Returns a mutable reference to the current block of the chain. + fn current_block_mut(&mut self) -> Result<&mut Block> { + self.blocks.last_mut().ok_or(OffChainError::UninitializedBlocks) + } } impl OnInstance for EnvInstance { diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 7a5090fbb00..7c86e559c55 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -209,3 +209,18 @@ where 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_entropy(entropy: T::Hash) -> Result<()> +where + T: EnvTypes, +{ + ::on_instance(|instance| { + instance.current_block_mut()?.set_entropy::(entropy) + }) + .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 index e4874b1e7fe..2681a33dbaf 100644 --- a/core/src/env3/engine/off_chain/typed_encoded.rs +++ b/core/src/env3/engine/off_chain/typed_encoded.rs @@ -164,7 +164,19 @@ impl TypedEncoded { if self.type_id.is_none() { return Err(TypedEncodedError::StillUninitialized) } - Ok(&self.encoded) + 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. From e52aab822aa875e5fa42b230f6083a5562c78bdc Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 25 Jan 2020 20:34:38 +0100 Subject: [PATCH 033/112] [core] env3: minor clean ups --- core/src/env3/engine/off_chain/db/exec_context.rs | 10 ++++++++++ core/src/env3/engine/off_chain/mod.rs | 5 +++++ core/src/env3/engine/off_chain/runtime_calls.rs | 1 - core/src/env3/engine/off_chain/test_api.rs | 8 +------- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/core/src/env3/engine/off_chain/db/exec_context.rs b/core/src/env3/engine/off_chain/db/exec_context.rs index dc92ebf5522..6e365ca2edc 100644 --- a/core/src/env3/engine/off_chain/db/exec_context.rs +++ b/core/src/env3/engine/off_chain/db/exec_context.rs @@ -92,11 +92,21 @@ impl ExecContext { } /// 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() } diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 66547995d00..b25e0ab4e39 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -71,6 +71,11 @@ pub struct EnvInstance { /// The accounts database of the environment. accounts: AccountsDb, /// Uploaded Wasm contract codes. + #[allow( + dead_code, + // Needed as soon as we support to execute contracts + // directly through the off-chain environment. + )] codes: CodeDb, /// Current execution context and context. exec_context: Vec, diff --git a/core/src/env3/engine/off_chain/runtime_calls.rs b/core/src/env3/engine/off_chain/runtime_calls.rs index e3a1d04deab..35b868a2d20 100644 --- a/core/src/env3/engine/off_chain/runtime_calls.rs +++ b/core/src/env3/engine/off_chain/runtime_calls.rs @@ -17,7 +17,6 @@ use super::{ OffChainError, }; use crate::env3::{ - EnvError, EnvTypes, Result, }; diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 7c86e559c55..351f042840f 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -18,12 +18,10 @@ use super::{ db::ExecContext, AccountError, EnvInstance, - OffChainError, OnInstance, }; use crate::env3::{ call::CallData, - EnvError, EnvTypes, Result, }; @@ -167,11 +165,7 @@ where .get_account::(&account_id) .ok_or(AccountError::no_account_for_id::(&account_id)) .map_err(Into::into) - .and_then(|account| { - account - .rent_allowance::() - .map_err(Into::into) - }) + .and_then(|account| account.rent_allowance::().map_err(Into::into)) }) } From fce3401fdc672893e7e73d6ae3eb72c9dfce6d40 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 26 Jan 2020 01:02:13 +0100 Subject: [PATCH 034/112] [core] env3: improve TypedEncoded::assign --- core/src/env3/engine/off_chain/db/accounts.rs | 4 ++-- core/src/env3/engine/off_chain/db/block.rs | 2 +- core/src/env3/engine/off_chain/typed_encoded.rs | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/env3/engine/off_chain/db/accounts.rs b/core/src/env3/engine/off_chain/db/accounts.rs index 9d91cb43016..1767955db01 100644 --- a/core/src/env3/engine/off_chain/db/accounts.rs +++ b/core/src/env3/engine/off_chain/db/accounts.rs @@ -146,7 +146,7 @@ impl Account { T: EnvTypes, { self.balance - .assign::(&OffBalance::new(&new_balance)) + .assign(&new_balance) .map_err(Into::into) } @@ -187,7 +187,7 @@ impl Account { self.contract_or_err_mut().and_then(|contract| { contract .rent_allowance - .assign::(&OffBalance::new(&new_rent_allowance)) + .assign(&new_rent_allowance) .map_err(Into::into) }) } diff --git a/core/src/env3/engine/off_chain/db/block.rs b/core/src/env3/engine/off_chain/db/block.rs index d81b2d8b690..c0957a8969d 100644 --- a/core/src/env3/engine/off_chain/db/block.rs +++ b/core/src/env3/engine/off_chain/db/block.rs @@ -77,7 +77,7 @@ impl Block { where T: EnvTypes, { - self.entropy.assign::(&OffHash::new(&new_entropy)) + self.entropy.assign(&new_entropy) .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 index 2681a33dbaf..a7b19707f59 100644 --- a/core/src/env3/engine/off_chain/typed_encoded.rs +++ b/core/src/env3/engine/off_chain/typed_encoded.rs @@ -233,14 +233,14 @@ impl TypedEncoded { ::decode(&mut &self.encoded[..]).map_err(Into::into) } - /// Assigns `other` to `self` - pub fn assign<'a, T>(&mut self, other: &'a Self) -> Result<()> + /// Assigns the given `T` to `self`. + pub fn assign(&mut self, value: &T) -> Result<()> where - T: scale::Decode + scale::Encode + 'static, + T: scale::Encode + 'static, { - Self::check_matching_types(self, other)?; + self.check_enforced_type::()?; self.encoded.clear(); - self.encoded.extend(&other.encoded); + value.encode_to(&mut self.encoded); Ok(()) } From 0af4d2efd28981e19d01e44cd3f864eb0eb45f7d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 26 Jan 2020 01:11:40 +0100 Subject: [PATCH 035/112] [core] env3: un-mut some variables --- core/src/env3/engine/off_chain/db/block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/env3/engine/off_chain/db/block.rs b/core/src/env3/engine/off_chain/db/block.rs index c0957a8969d..e0df7058634 100644 --- a/core/src/env3/engine/off_chain/db/block.rs +++ b/core/src/env3/engine/off_chain/db/block.rs @@ -99,8 +99,8 @@ impl Block { T: EnvTypes, { let mut entropy = self.entropy.clone(); - let mut entropy_bytes = entropy.encoded_bytes_mut()?; - let mut len_entropy = entropy_bytes.len(); + 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); From da2f72908d1dab7d8eae7f73105506c4cc8a528d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 26 Jan 2020 01:28:25 +0100 Subject: [PATCH 036/112] [core] env3: fix some clippy warnings --- core/src/env3/engine/off_chain/impls.rs | 2 +- core/src/env3/engine/off_chain/runtime_calls.rs | 2 +- core/src/env3/engine/off_chain/test_api.rs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index a658c85100e..9423ba7e251 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -76,7 +76,7 @@ impl Env for EnvInstance { self.callee_account() .get_storage(key) .expect("callee account is not a smart contract") - .ok_or(scale::Error::from("could not decode contract storage")) + .ok_or_else(|| scale::Error::from("could not decode contract storage")) .map_err(Into::into) } diff --git a/core/src/env3/engine/off_chain/runtime_calls.rs b/core/src/env3/engine/off_chain/runtime_calls.rs index 35b868a2d20..677ac003a80 100644 --- a/core/src/env3/engine/off_chain/runtime_calls.rs +++ b/core/src/env3/engine/off_chain/runtime_calls.rs @@ -60,7 +60,7 @@ impl RuntimeCallHandler { handler(OffCall::new(params)); Ok(()) } - None => Err(OffChainError::UnregisteredRuntimeCallHandler)?, + None => Err(OffChainError::UnregisteredRuntimeCallHandler.into()), } } } diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 351f042840f..5c124f5a5cd 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -88,7 +88,7 @@ where instance .accounts .get_account_mut::(&account_id) - .ok_or(AccountError::no_account_for_id::(&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)) }) @@ -114,7 +114,7 @@ where instance .accounts .get_account::(&account_id) - .ok_or(AccountError::no_account_for_id::(&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)) }) @@ -138,7 +138,7 @@ where instance .accounts .get_account_mut::(&account_id) - .ok_or(AccountError::no_account_for_id::(&account_id)) + .ok_or_else(|| AccountError::no_account_for_id::(&account_id)) .map_err(Into::into) .and_then(|account| { account @@ -163,7 +163,7 @@ where instance .accounts .get_account::(&account_id) - .ok_or(AccountError::no_account_for_id::(&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)) }) From 84ab169afbcd5d11038f5903607fd4593ef4efd0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 26 Jan 2020 01:41:10 +0100 Subject: [PATCH 037/112] [core] env3: add past_printlns to off-chain test api --- core/src/env3/engine/off_chain/mod.rs | 11 +++++++---- core/src/env3/engine/off_chain/test_api.rs | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index b25e0ab4e39..672de082d19 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -20,10 +20,6 @@ pub mod test_api; mod typed_encoded; mod types; -pub use self::{ - db::AccountError, - typed_encoded::TypedEncodedError, -}; use self::{ db::{ Account, @@ -46,6 +42,13 @@ use self::{ OffMoment, }, }; +pub use self::{ + db::{ + AccountError, + PastPrints, + }, + typed_encoded::TypedEncodedError, +}; use super::OnInstance; use core::cell::RefCell; use derive_more::From; diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 5c124f5a5cd..32f79ee1568 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -19,12 +19,14 @@ use super::{ AccountError, EnvInstance, OnInstance, + PastPrints, }; use crate::env3::{ call::CallData, EnvTypes, Result, }; +use ink_prelude::string::String; /// Pushes a contract execution context. /// @@ -218,3 +220,19 @@ where }) .map_err(Into::into) } + +pub fn past_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() + }) +} From 4a453c46df5f6eadebd92f9e790bf5fede9b2eea Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 26 Jan 2020 01:41:27 +0100 Subject: [PATCH 038/112] [core] env3: minor refactoring --- core/src/env3/engine/off_chain/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 672de082d19..9da62940906 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -130,7 +130,9 @@ impl EnvInstance { /// Returns a mutable reference to the current block of the chain. fn current_block_mut(&mut self) -> Result<&mut Block> { - self.blocks.last_mut().ok_or(OffChainError::UninitializedBlocks) + self.blocks + .last_mut() + .ok_or_else(|| OffChainError::UninitializedBlocks) } } From e26b889aadc4013ac71f45ecd6a91d459857a41d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 26 Jan 2020 01:42:07 +0100 Subject: [PATCH 039/112] [core] env3: apply rustfmt --- core/src/env3/backend.rs | 8 ++++---- core/src/env3/engine/off_chain/db/accounts.rs | 4 +--- core/src/env3/engine/off_chain/db/block.rs | 5 ++--- core/src/env3/engine/off_chain/db/chain_spec.rs | 6 ++++-- core/src/env3/engine/off_chain/db/console.rs | 4 +++- core/src/env3/engine/off_chain/typed_encoded.rs | 4 ++-- core/src/env3/engine/on_chain/buffer.rs | 5 ++++- core/src/env3/engine/on_chain/impls.rs | 2 +- core/src/env3/engine/on_chain/mod.rs | 2 +- core/src/env3/error.rs | 5 ++++- 10 files changed, 26 insertions(+), 19 deletions(-) diff --git a/core/src/env3/backend.rs b/core/src/env3/backend.rs index 7d4a47c56a8..899185a2f73 100644 --- a/core/src/env3/backend.rs +++ b/core/src/env3/backend.rs @@ -213,10 +213,10 @@ pub trait TypedEnv: Env { /// /// - 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. + /// - 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, diff --git a/core/src/env3/engine/off_chain/db/accounts.rs b/core/src/env3/engine/off_chain/db/accounts.rs index 1767955db01..cfc2ee734c7 100644 --- a/core/src/env3/engine/off_chain/db/accounts.rs +++ b/core/src/env3/engine/off_chain/db/accounts.rs @@ -145,9 +145,7 @@ impl Account { where T: EnvTypes, { - self.balance - .assign(&new_balance) - .map_err(Into::into) + self.balance.assign(&new_balance).map_err(Into::into) } /// Returns the contract account or an error if it is a user account. diff --git a/core/src/env3/engine/off_chain/db/block.rs b/core/src/env3/engine/off_chain/db/block.rs index e0df7058634..95f35ce2536 100644 --- a/core/src/env3/engine/off_chain/db/block.rs +++ b/core/src/env3/engine/off_chain/db/block.rs @@ -18,8 +18,8 @@ use super::{ TypedEncoded, }, OffBlockNumber, - OffMoment, OffHash, + OffMoment, }; use crate::env3::EnvTypes; @@ -77,8 +77,7 @@ impl Block { where T: EnvTypes, { - self.entropy.assign(&new_entropy) - .map_err(Into::into) + self.entropy.assign(&new_entropy).map_err(Into::into) } /// Returns a randomized hash. diff --git a/core/src/env3/engine/off_chain/db/chain_spec.rs b/core/src/env3/engine/off_chain/db/chain_spec.rs index 5c20e9934cf..5f445b7c6db 100644 --- a/core/src/env3/engine/off_chain/db/chain_spec.rs +++ b/core/src/env3/engine/off_chain/db/chain_spec.rs @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::OffBalance; +use super::{ + super::Result, + OffBalance, +}; use crate::env3::EnvTypes; -use super::super::Result; /// The chain specification. pub struct ChainSpec { diff --git a/core/src/env3/engine/off_chain/db/console.rs b/core/src/env3/engine/off_chain/db/console.rs index e99616547c6..b6241b5f5af 100644 --- a/core/src/env3/engine/off_chain/db/console.rs +++ b/core/src/env3/engine/off_chain/db/console.rs @@ -49,7 +49,9 @@ pub struct PastPrints<'a> { 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() } + Self { + iter: console.past_prints.iter(), + } } } diff --git a/core/src/env3/engine/off_chain/typed_encoded.rs b/core/src/env3/engine/off_chain/typed_encoded.rs index a7b19707f59..4e443d90cc6 100644 --- a/core/src/env3/engine/off_chain/typed_encoded.rs +++ b/core/src/env3/engine/off_chain/typed_encoded.rs @@ -12,6 +12,8 @@ // 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, @@ -21,8 +23,6 @@ use core::{ }, marker::PhantomData, }; -use super::OffChainError; -use crate::env3::EnvError; use derive_more::From; /// A wrapper around an encoded entity that only allows type safe accesses. diff --git a/core/src/env3/engine/on_chain/buffer.rs b/core/src/env3/engine/on_chain/buffer.rs index 1cb072f5f0d..78c8e979280 100644 --- a/core/src/env3/engine/on_chain/buffer.rs +++ b/core/src/env3/engine/on_chain/buffer.rs @@ -27,7 +27,10 @@ impl StaticBuffer { /// Creates a new static buffer. pub const fn new() -> Self { - Self { buffer: [0; Self::CAPACITY], len: 0 } + Self { + buffer: [0; Self::CAPACITY], + len: 0, + } } /// Returns the current length of the static buffer. diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index fa939fc65e9..ba354631c47 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -254,7 +254,7 @@ impl TypedEnv for EnvInstance { fn invoke_runtime(&mut self, call: &T::Call) -> Result<()> where - T: EnvTypes + T: EnvTypes, { self.encode_into_buffer(call); ext::dispatch_call(&self.buffer[..]); diff --git a/core/src/env3/engine/on_chain/mod.rs b/core/src/env3/engine/on_chain/mod.rs index 153663f036b..69f1051f281 100644 --- a/core/src/env3/engine/on_chain/mod.rs +++ b/core/src/env3/engine/on_chain/mod.rs @@ -19,8 +19,8 @@ mod retcode; use super::OnInstance; -pub(crate) use self::retcode::RetCode; use self::buffer::StaticBuffer; +pub(crate) use self::retcode::RetCode; /// The on-chain environment. pub struct EnvInstance { diff --git a/core/src/env3/error.rs b/core/src/env3/error.rs index c3c48840f70..10742f28f7f 100644 --- a/core/src/env3/error.rs +++ b/core/src/env3/error.rs @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use derive_more::{From, Display}; +use derive_more::{ + Display, + From, +}; #[cfg(any(feature = "std", test, doc))] use crate::env3::engine::off_chain::OffChainError; From 7c9ecdded7a8acd3ed10b57ec0d26d7da63c795f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 26 Jan 2020 13:44:18 +0100 Subject: [PATCH 040/112] [core] env3: clean up of call and instantiate contract --- core/src/env3/engine/on_chain/ext.rs | 33 ++++++++++++++++++++------ core/src/env3/engine/on_chain/impls.rs | 13 ++-------- core/src/env3/error.rs | 6 +++++ 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/core/src/env3/engine/on_chain/ext.rs b/core/src/env3/engine/on_chain/ext.rs index 0d1982a693f..d4c4a173391 100644 --- a/core/src/env3/engine/on_chain/ext.rs +++ b/core/src/env3/engine/on_chain/ext.rs @@ -17,7 +17,16 @@ //! Refer to substrate SRML contract module for more documentation. use super::RetCode; -use crate::storage::Key; +use crate::{ + 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" { @@ -99,8 +108,8 @@ pub fn create( 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, @@ -110,12 +119,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, @@ -125,8 +139,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]) { diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index ba354631c47..30a247fa0d4 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -122,12 +122,7 @@ impl EnvInstance { let endowment = &self.buffer[endowment]; let call_data = &self.buffer[call_data]; // Perform the actual contract call. - let ret = ext::call(callee, call_params.gas_limit(), endowment, call_data); - if !ret.is_success() { - // Return an error if `ret` refers to an error code. - todo!() - } - Ok(()) + ext::call(callee, call_params.gas_limit(), endowment, call_data) } } @@ -299,11 +294,7 @@ impl TypedEnv for EnvInstance { let endowment = &self.buffer[endowment]; let create_data = &self.buffer[create_data]; // Do the actual contract instantiation. - let ret = ext::create(code_hash, params.gas_limit(), endowment, create_data); - if !ret.is_success() { - // Return an error if `ret` refers to an error code. - todo!() - } + 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. diff --git a/core/src/env3/error.rs b/core/src/env3/error.rs index 10742f28f7f..e594da5c159 100644 --- a/core/src/env3/error.rs +++ b/core/src/env3/error.rs @@ -27,6 +27,12 @@ pub enum EnvError { Decode(scale::Error), #[cfg(any(feature = "std", test, doc))] OffChain(OffChainError), + ContractCallTrapped, + #[from(ignore)] + ContractCallFailState(u8), + ContractInstantiationTrapped, + #[from(ignore)] + ContractInstantiationFailState(u8), } /// A result of environmental operations. From 5de95c9a86ff4fd9b815bfd61b782f11b5a03cc5 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 26 Jan 2020 13:46:35 +0100 Subject: [PATCH 041/112] [core] env3: rename moment -> time_stamp --- core/src/env3/engine/off_chain/db/block.rs | 14 +++++++------- core/src/env3/engine/off_chain/impls.rs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/env3/engine/off_chain/db/block.rs b/core/src/env3/engine/off_chain/db/block.rs index 95f35ce2536..fbcc2a993e3 100644 --- a/core/src/env3/engine/off_chain/db/block.rs +++ b/core/src/env3/engine/off_chain/db/block.rs @@ -27,8 +27,8 @@ use crate::env3::EnvTypes; pub struct Block { /// The current block number. number: OffBlockNumber, - /// The current moment of block creation. - moment: OffMoment, + /// The time stamp of the block. + time_stamp: OffMoment, /// The randomization entropy for a block. /// /// # Note @@ -40,13 +40,13 @@ pub struct Block { impl Block { /// Creates a new block for the given number and moment. - pub fn new(number: T::BlockNumber, moment: T::Moment, entropy: T::Hash) -> Self + pub fn new(number: T::BlockNumber, time_stamp: T::Moment, entropy: T::Hash) -> Self where T: EnvTypes, { Self { number: TypedEncoded::new(&number), - moment: TypedEncoded::new(&moment), + time_stamp: TypedEncoded::new(&time_stamp), entropy: TypedEncoded::new(&entropy), } } @@ -59,12 +59,12 @@ impl Block { self.number.decode().map_err(Into::into) } - /// Returns the moment of the block. - pub fn moment(&self) -> Result + /// Returns the time stamp of the block. + pub fn time_stamp(&self) -> Result where T: EnvTypes, { - self.moment.decode().map_err(Into::into) + self.time_stamp.decode().map_err(Into::into) } /// Sets the entropy of this block to the given entropy. diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index 9423ba7e251..1d647a0e823 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -151,7 +151,7 @@ impl TypedEnv for EnvInstance { fn now_in_ms(&mut self) -> Result { self.current_block() .expect("uninitialized execution context") - .moment::() + .time_stamp::() .map_err(|_| scale::Error::from("could not decode block time")) .map_err(Into::into) } From 61eaba2c88794512a8320ebe8738f47cece4f484 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 26 Jan 2020 13:50:43 +0100 Subject: [PATCH 042/112] [core] env3: make get_runtime_storage return a Result instead of RetCode --- core/src/env3/engine/on_chain/ext.rs | 10 +++++++--- core/src/env3/engine/on_chain/impls.rs | 2 +- core/src/env3/error.rs | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/core/src/env3/engine/on_chain/ext.rs b/core/src/env3/engine/on_chain/ext.rs index d4c4a173391..55a1c372220 100644 --- a/core/src/env3/engine/on_chain/ext.rs +++ b/core/src/env3/engine/on_chain/ext.rs @@ -178,14 +178,18 @@ pub fn get_storage(key: &[u8]) -> RetCode { unsafe { sys::ext_get_storage(key.as_ptr() as u32) }.into() } -pub fn get_runtime_storage(runtime_key: &[u8]) -> RetCode { - unsafe { +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"), } - .into() } /// Restores a tombstone to the original smart contract. diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index 30a247fa0d4..3bd5f5d03c4 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -153,7 +153,7 @@ impl Env for EnvInstance { where R: scale::Decode, { - if !ext::get_runtime_storage(runtime_key).is_success() { + if ext::get_runtime_storage(runtime_key).is_err() { return None } Some(self.decode_scratch_buffer().map_err(Into::into)) diff --git a/core/src/env3/error.rs b/core/src/env3/error.rs index e594da5c159..00fe49908b3 100644 --- a/core/src/env3/error.rs +++ b/core/src/env3/error.rs @@ -33,6 +33,7 @@ pub enum EnvError { ContractInstantiationTrapped, #[from(ignore)] ContractInstantiationFailState(u8), + MissingRuntimeStorageEntry, } /// A result of environmental operations. From f79c9385e694303a62f5c8fd63dcbba481661f37 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 26 Jan 2020 13:50:55 +0100 Subject: [PATCH 043/112] [core] env3: remove unneeded import --- core/src/env3/engine/off_chain/test_api.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 32f79ee1568..34f557cee2b 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -19,7 +19,6 @@ use super::{ AccountError, EnvInstance, OnInstance, - PastPrints, }; use crate::env3::{ call::CallData, From fb7d8bf2f68f974ffbb96627669b2343d643ca27 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 26 Jan 2020 23:50:27 +0100 Subject: [PATCH 044/112] [core] env3: add some trait impls for EnvTypes::Hash --- core/src/env3/mod.rs | 1 + core/src/env3/types.rs | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/core/src/env3/mod.rs b/core/src/env3/mod.rs index fec70ab4113..64c857dde51 100644 --- a/core/src/env3/mod.rs +++ b/core/src/env3/mod.rs @@ -44,5 +44,6 @@ pub use self::{ EnvTypes, Hash, Topics, + Clear, }, }; diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs index 186ec10d558..a042cea90f1 100644 --- a/core/src/env3/types.rs +++ b/core/src/env3/types.rs @@ -41,7 +41,14 @@ pub trait EnvTypes { /// The type of balances. type Balance: 'static + scale::Codec + Clone + PartialEq + Eq; /// The type of hash. - type Hash: 'static + scale::Codec + Clone + PartialEq + Eq; + type Hash: 'static + + scale::Codec + + Clone + + Clear + + PartialEq + + Eq + + AsRef<[u8]> + + AsMut<[u8]>; /// The type of timestamps. type Moment: 'static + scale::Codec + Clone + PartialEq + Eq; /// The type of block number. @@ -155,4 +162,37 @@ impl<'a> TryFrom<&'a [u8]> for Hash { } } +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 {} From bfc7f4ae581e86cd6e7464b8fbed3060c807bf1e Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 26 Jan 2020 23:56:08 +0100 Subject: [PATCH 045/112] [core] env3: remove last remaining usages of RetCode --- core/src/env3/engine/on_chain/ext.rs | 20 +++++++------ core/src/env3/engine/on_chain/impls.rs | 4 +-- core/src/env3/engine/on_chain/mod.rs | 2 -- core/src/env3/engine/on_chain/retcode.rs | 38 ------------------------ core/src/env3/error.rs | 1 + 5 files changed, 13 insertions(+), 52 deletions(-) delete mode 100644 core/src/env3/engine/on_chain/retcode.rs diff --git a/core/src/env3/engine/on_chain/ext.rs b/core/src/env3/engine/on_chain/ext.rs index 55a1c372220..24ec58a1829 100644 --- a/core/src/env3/engine/on_chain/ext.rs +++ b/core/src/env3/engine/on_chain/ext.rs @@ -16,7 +16,6 @@ //! //! Refer to substrate SRML contract module for more documentation. -use super::RetCode; use crate::{ env3::{ EnvError, @@ -174,8 +173,13 @@ pub fn clear_storage(key: &[u8]) { unsafe { sys::ext_set_storage(key.as_ptr() as u32, 0, 0, 0) } } -pub fn get_storage(key: &[u8]) -> RetCode { - unsafe { sys::ext_get_storage(key.as_ptr() 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<()> { @@ -231,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 { diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index 3bd5f5d03c4..01006117b4f 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -139,9 +139,7 @@ impl Env for EnvInstance { where R: scale::Decode, { - if !ext::get_storage(key.as_bytes()).is_success() { - todo!() - } + ext::get_storage(key.as_bytes())?; 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 index 69f1051f281..7bca34314d0 100644 --- a/core/src/env3/engine/on_chain/mod.rs +++ b/core/src/env3/engine/on_chain/mod.rs @@ -15,12 +15,10 @@ mod buffer; mod ext; mod impls; -mod retcode; use super::OnInstance; use self::buffer::StaticBuffer; -pub(crate) use self::retcode::RetCode; /// The on-chain environment. pub struct EnvInstance { diff --git a/core/src/env3/engine/on_chain/retcode.rs b/core/src/env3/engine/on_chain/retcode.rs deleted file mode 100644 index 622c4d840b4..00000000000 --- a/core/src/env3/engine/on_chain/retcode.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/env3/error.rs b/core/src/env3/error.rs index 00fe49908b3..d7ca46f904c 100644 --- a/core/src/env3/error.rs +++ b/core/src/env3/error.rs @@ -34,6 +34,7 @@ pub enum EnvError { #[from(ignore)] ContractInstantiationFailState(u8), MissingRuntimeStorageEntry, + MissingContractStorageEntry, } /// A result of environmental operations. From b2734580491d2e02dbd457873a52abe276578d5d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 00:37:46 +0100 Subject: [PATCH 046/112] [core] env3: refactor some errors --- core/src/env3/engine/on_chain/impls.rs | 4 ++-- core/src/env3/error.rs | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index 01006117b4f..6188d6cdf64 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -64,12 +64,12 @@ impl EnvInstance { /// # Errors /// /// If the decoding into a value of `T` failed. - fn decode_scratch_buffer(&mut self) -> core::result::Result + 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]) + scale::Decode::decode(&mut &self.buffer[0..req_len]).map_err(Into::into) } /// Encodes the value into the contract-side scratch buffer. diff --git a/core/src/env3/error.rs b/core/src/env3/error.rs index d7ca46f904c..c4625181ab3 100644 --- a/core/src/env3/error.rs +++ b/core/src/env3/error.rs @@ -12,10 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use derive_more::{ - Display, - From, -}; +use derive_more::From; #[cfg(any(feature = "std", test, doc))] use crate::env3::engine::off_chain::OffChainError; @@ -23,7 +20,6 @@ use crate::env3::engine::off_chain::OffChainError; /// Errors that can be encountered upon environmental interaction. #[derive(From)] pub enum EnvError { - // #[display(msg = "error upon decoding")] Decode(scale::Error), #[cfg(any(feature = "std", test, doc))] OffChain(OffChainError), From 737bdaaa935256eac2706fcbfafe17efdce5b461 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 00:38:02 +0100 Subject: [PATCH 047/112] [core] env3: document the EnvError enum --- core/src/env3/error.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/env3/error.rs b/core/src/env3/error.rs index c4625181ab3..1de738027a1 100644 --- a/core/src/env3/error.rs +++ b/core/src/env3/error.rs @@ -20,16 +20,24 @@ use crate::env3::engine::off_chain::OffChainError; /// Errors that can be encountered upon environmental interaction. #[derive(From)] 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, } From 267a1c88aebbd2f2c4550259b866cf8e221063d2 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 00:41:35 +0100 Subject: [PATCH 048/112] [core] env3: add impls for fire methods on call and create builders --- core/src/env3/call/builder.rs | 6 ++---- core/src/env3/call/create.rs | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/core/src/env3/call/builder.rs b/core/src/env3/call/builder.rs index 84f5f13a241..26922ed5115 100644 --- a/core/src/env3/call/builder.rs +++ b/core/src/env3/call/builder.rs @@ -173,8 +173,7 @@ where where R: scale::Decode, { - // E::eval_contract(&mut Vec::new(), &self.params) - todo!() + crate::env3::eval_contract(&self.params) } } @@ -184,7 +183,6 @@ where { /// Fires the cross-call to the smart contract. pub fn fire(self) -> Result<()> { - // E::invoke_contract(&mut Vec::new(), &self.params) - todo!() + crate::env3::invoke_contract(&self.params) } } diff --git a/core/src/env3/call/create.rs b/core/src/env3/call/create.rs index 7a18b538dba..3889bfa5e74 100644 --- a/core/src/env3/call/create.rs +++ b/core/src/env3/call/create.rs @@ -194,8 +194,7 @@ where { /// Instantiates the contract and returns its account ID back to the caller. pub fn create(self) -> Result { - // E::create_contract(&mut Vec::new(), &self.params) - // .map(FromAccountId::from_account_id) - todo!() + crate::env3::create_contract(&self.params) + .map(FromAccountId::from_account_id) } } From 17bc6bd028b47178869bbf55bf977d6becad1d4d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 15:26:26 +0100 Subject: [PATCH 049/112] [core] env3: add doc comment to past_printlns --- core/src/env3/engine/off_chain/test_api.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 34f557cee2b..5a58c1b515f 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -220,6 +220,7 @@ where .map_err(Into::into) } +/// Returns the contents of the past performed environmental `println` in order. pub fn past_printlns() -> impl Iterator { ::on_instance(|instance| { // We return a clone of the recorded strings instead of From 8b0c892e0742fc5d1598fdde189027638a361f18 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 15:26:42 +0100 Subject: [PATCH 050/112] [core] env3: apply rustfmt --- core/src/env3/call/create.rs | 3 +-- core/src/env3/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/env3/call/create.rs b/core/src/env3/call/create.rs index 3889bfa5e74..e89a4af609b 100644 --- a/core/src/env3/call/create.rs +++ b/core/src/env3/call/create.rs @@ -194,7 +194,6 @@ where { /// Instantiates the contract and returns its account ID back to the caller. pub fn create(self) -> Result { - crate::env3::create_contract(&self.params) - .map(FromAccountId::from_account_id) + crate::env3::create_contract(&self.params).map(FromAccountId::from_account_id) } } diff --git a/core/src/env3/mod.rs b/core/src/env3/mod.rs index 64c857dde51..960ec249ecd 100644 --- a/core/src/env3/mod.rs +++ b/core/src/env3/mod.rs @@ -40,10 +40,10 @@ pub use self::{ }, types::{ AccountId, + Clear, DefaultEnvTypes, EnvTypes, Hash, Topics, - Clear, }, }; From afdeea41fe91273b2d6bee80bdff6c8daf09c150 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 15:30:55 +0100 Subject: [PATCH 051/112] [core] env3: update license headers --- LICENSE_TEMPLATE | 2 +- core/src/env3/api.rs | 2 +- core/src/env3/backend.rs | 2 +- core/src/env3/call/builder.rs | 2 +- core/src/env3/call/create.rs | 2 +- core/src/env3/call/mod.rs | 2 +- core/src/env3/call/utils.rs | 2 +- core/src/env3/engine/mod.rs | 2 +- core/src/env3/engine/off_chain/db/accounts.rs | 2 +- core/src/env3/engine/off_chain/db/block.rs | 2 +- core/src/env3/engine/off_chain/db/chain_spec.rs | 2 +- core/src/env3/engine/off_chain/db/codes.rs | 2 +- core/src/env3/engine/off_chain/db/console.rs | 2 +- core/src/env3/engine/off_chain/db/exec_context.rs | 2 +- core/src/env3/engine/off_chain/db/mod.rs | 2 +- core/src/env3/engine/off_chain/impls.rs | 2 +- core/src/env3/engine/off_chain/mod.rs | 2 +- core/src/env3/engine/off_chain/runtime_calls.rs | 2 +- core/src/env3/engine/off_chain/runtime_storage.rs | 2 +- core/src/env3/engine/off_chain/test_api.rs | 2 +- core/src/env3/engine/off_chain/typed_encoded.rs | 2 +- core/src/env3/engine/off_chain/types.rs | 2 +- core/src/env3/engine/on_chain/buffer.rs | 2 +- core/src/env3/engine/on_chain/ext.rs | 2 +- core/src/env3/engine/on_chain/impls.rs | 2 +- core/src/env3/engine/on_chain/mod.rs | 2 +- core/src/env3/error.rs | 2 +- core/src/env3/mod.rs | 2 +- core/src/env3/types.rs | 2 +- 29 files changed, 29 insertions(+), 29 deletions(-) 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/src/env3/api.rs b/core/src/env3/api.rs index 10581d6a5e8..8d257121474 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.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. diff --git a/core/src/env3/backend.rs b/core/src/env3/backend.rs index 899185a2f73..d6981301f5b 100644 --- a/core/src/env3/backend.rs +++ b/core/src/env3/backend.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. diff --git a/core/src/env3/call/builder.rs b/core/src/env3/call/builder.rs index 26922ed5115..a4a859988ef 100644 --- a/core/src/env3/call/builder.rs +++ b/core/src/env3/call/builder.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2018-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. diff --git a/core/src/env3/call/create.rs b/core/src/env3/call/create.rs index e89a4af609b..9ac46451029 100644 --- a/core/src/env3/call/create.rs +++ b/core/src/env3/call/create.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. diff --git a/core/src/env3/call/mod.rs b/core/src/env3/call/mod.rs index aacfec3c211..5e05e49fb86 100644 --- a/core/src/env3/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. diff --git a/core/src/env3/call/utils.rs b/core/src/env3/call/utils.rs index 8c3bb9fbb6d..d7d00fdf415 100644 --- a/core/src/env3/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. diff --git a/core/src/env3/engine/mod.rs b/core/src/env3/engine/mod.rs index 345a8113fb4..43fe53cfa86 100644 --- a/core/src/env3/engine/mod.rs +++ b/core/src/env3/engine/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. diff --git a/core/src/env3/engine/off_chain/db/accounts.rs b/core/src/env3/engine/off_chain/db/accounts.rs index cfc2ee734c7..c6ed3fdcbc1 100644 --- a/core/src/env3/engine/off_chain/db/accounts.rs +++ b/core/src/env3/engine/off_chain/db/accounts.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. diff --git a/core/src/env3/engine/off_chain/db/block.rs b/core/src/env3/engine/off_chain/db/block.rs index fbcc2a993e3..7245f13c7d1 100644 --- a/core/src/env3/engine/off_chain/db/block.rs +++ b/core/src/env3/engine/off_chain/db/block.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. diff --git a/core/src/env3/engine/off_chain/db/chain_spec.rs b/core/src/env3/engine/off_chain/db/chain_spec.rs index 5f445b7c6db..7ccf2f9d257 100644 --- a/core/src/env3/engine/off_chain/db/chain_spec.rs +++ b/core/src/env3/engine/off_chain/db/chain_spec.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. diff --git a/core/src/env3/engine/off_chain/db/codes.rs b/core/src/env3/engine/off_chain/db/codes.rs index 73577aca270..077e8c284a9 100644 --- a/core/src/env3/engine/off_chain/db/codes.rs +++ b/core/src/env3/engine/off_chain/db/codes.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. diff --git a/core/src/env3/engine/off_chain/db/console.rs b/core/src/env3/engine/off_chain/db/console.rs index b6241b5f5af..f19f2c9e147 100644 --- a/core/src/env3/engine/off_chain/db/console.rs +++ b/core/src/env3/engine/off_chain/db/console.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. diff --git a/core/src/env3/engine/off_chain/db/exec_context.rs b/core/src/env3/engine/off_chain/db/exec_context.rs index 6e365ca2edc..213a81eb8ef 100644 --- a/core/src/env3/engine/off_chain/db/exec_context.rs +++ b/core/src/env3/engine/off_chain/db/exec_context.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. diff --git a/core/src/env3/engine/off_chain/db/mod.rs b/core/src/env3/engine/off_chain/db/mod.rs index 25fba97e53d..e5411a51900 100644 --- a/core/src/env3/engine/off_chain/db/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. diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index 1d647a0e823..9c994bad71e 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.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. diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 9da62940906..40e45382cf3 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/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. diff --git a/core/src/env3/engine/off_chain/runtime_calls.rs b/core/src/env3/engine/off_chain/runtime_calls.rs index 677ac003a80..23a0fcf4ef3 100644 --- a/core/src/env3/engine/off_chain/runtime_calls.rs +++ b/core/src/env3/engine/off_chain/runtime_calls.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. diff --git a/core/src/env3/engine/off_chain/runtime_storage.rs b/core/src/env3/engine/off_chain/runtime_storage.rs index 098b29101e2..8d2ee56e234 100644 --- a/core/src/env3/engine/off_chain/runtime_storage.rs +++ b/core/src/env3/engine/off_chain/runtime_storage.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. diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 5a58c1b515f..74892241114 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.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. diff --git a/core/src/env3/engine/off_chain/typed_encoded.rs b/core/src/env3/engine/off_chain/typed_encoded.rs index 4e443d90cc6..baee2988a72 100644 --- a/core/src/env3/engine/off_chain/typed_encoded.rs +++ b/core/src/env3/engine/off_chain/typed_encoded.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. diff --git a/core/src/env3/engine/off_chain/types.rs b/core/src/env3/engine/off_chain/types.rs index 7a95f571a73..18b36bdb8c8 100644 --- a/core/src/env3/engine/off_chain/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. diff --git a/core/src/env3/engine/on_chain/buffer.rs b/core/src/env3/engine/on_chain/buffer.rs index 78c8e979280..8deb5ccc68b 100644 --- a/core/src/env3/engine/on_chain/buffer.rs +++ b/core/src/env3/engine/on_chain/buffer.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. diff --git a/core/src/env3/engine/on_chain/ext.rs b/core/src/env3/engine/on_chain/ext.rs index 24ec58a1829..f9b49804db6 100644 --- a/core/src/env3/engine/on_chain/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. diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index 6188d6cdf64..1e44894168a 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.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. diff --git a/core/src/env3/engine/on_chain/mod.rs b/core/src/env3/engine/on_chain/mod.rs index 7bca34314d0..c97706c2858 100644 --- a/core/src/env3/engine/on_chain/mod.rs +++ b/core/src/env3/engine/on_chain/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. diff --git a/core/src/env3/error.rs b/core/src/env3/error.rs index 1de738027a1..3746c530db6 100644 --- a/core/src/env3/error.rs +++ b/core/src/env3/error.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. diff --git a/core/src/env3/mod.rs b/core/src/env3/mod.rs index 960ec249ecd..38ee67dbf61 100644 --- a/core/src/env3/mod.rs +++ b/core/src/env3/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. diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs index a042cea90f1..1b3ef88ff46 100644 --- a/core/src/env3/types.rs +++ b/core/src/env3/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. From 4933e491a8ae049628c78943f90fd918ac2e0415 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 16:16:17 +0100 Subject: [PATCH 052/112] [core] env3: support querying and emitting of events in off-chain env --- core/src/env3/engine/off_chain/db/events.rs | 74 +++++++++++++++++++++ core/src/env3/engine/off_chain/db/mod.rs | 5 ++ core/src/env3/engine/off_chain/impls.rs | 5 +- core/src/env3/engine/off_chain/mod.rs | 5 ++ core/src/env3/engine/off_chain/test_api.rs | 17 +++++ 5 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 core/src/env3/engine/off_chain/db/events.rs 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/mod.rs b/core/src/env3/engine/off_chain/db/mod.rs index e5411a51900..27d304ccf27 100644 --- a/core/src/env3/engine/off_chain/db/mod.rs +++ b/core/src/env3/engine/off_chain/db/mod.rs @@ -17,6 +17,7 @@ mod block; mod chain_spec; mod codes; mod console; +mod events; mod exec_context; pub use self::{ @@ -35,6 +36,10 @@ pub use self::{ Console, PastPrints, }, + events::{ + EmittedEvent, + EmittedEventsRecorder, + }, exec_context::ExecContext, }; use super::{ diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index 9c994bad71e..4606b635b77 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -200,12 +200,13 @@ impl TypedEnv for EnvInstance { .map_err(Into::into) } - fn emit_event(&mut self, _event: Event) + fn emit_event(&mut self, new_event: Event) where T: EnvTypes, Event: Topics + scale::Encode, { - todo!() + self.emitted_events + .record::(new_event) } fn set_rent_allowance(&mut self, new_rent_allowance: T::Balance) diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 40e45382cf3..b6ed68e62f7 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -29,6 +29,8 @@ use self::{ CodeDb, Console, ExecContext, + EmittedEventsRecorder, + EmittedEvent, }, runtime_calls::RuntimeCallHandler, runtime_storage::RuntimeStorage, @@ -92,6 +94,8 @@ pub struct EnvInstance { runtime_storage: RuntimeStorage, /// The runtime calls handler. runtime_call_handler: RuntimeCallHandler, + /// Emitted events recorder. + emitted_events: EmittedEventsRecorder, } impl EnvInstance { @@ -106,6 +110,7 @@ impl EnvInstance { console: Console::new(), runtime_storage: RuntimeStorage::new(), runtime_call_handler: RuntimeCallHandler::new(), + emitted_events: EmittedEventsRecorder::new(), } } diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 74892241114..5318a2be556 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -17,6 +17,7 @@ use super::{ db::ExecContext, AccountError, + EmittedEvent, EnvInstance, OnInstance, }; @@ -236,3 +237,19 @@ pub fn past_printlns() -> impl Iterator { .into_iter() }) } + +pub fn emitted_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() + }) +} From 3983b779ec1d15825bb5f983c5b756b6c962c6fe Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 16:16:34 +0100 Subject: [PATCH 053/112] [core] env3: missing license header adjustment --- core/src/env3/call/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/env3/call/builder.rs b/core/src/env3/call/builder.rs index a4a859988ef..ae48d006823 100644 --- a/core/src/env3/call/builder.rs +++ b/core/src/env3/call/builder.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2020 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. From 9475677a60156905590dfecbf65645fb194779b5 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 16:24:31 +0100 Subject: [PATCH 054/112] [core] env3: apply rustfmt --- core/src/env3/engine/off_chain/impls.rs | 3 +-- core/src/env3/engine/off_chain/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index 4606b635b77..bf96c9d2557 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -205,8 +205,7 @@ impl TypedEnv for EnvInstance { T: EnvTypes, Event: Topics + scale::Encode, { - self.emitted_events - .record::(new_event) + self.emitted_events.record::(new_event) } fn set_rent_allowance(&mut self, new_rent_allowance: T::Balance) diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index b6ed68e62f7..d50b03ec5db 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -28,9 +28,9 @@ use self::{ ChainSpec, CodeDb, Console, - ExecContext, - EmittedEventsRecorder, EmittedEvent, + EmittedEventsRecorder, + ExecContext, }, runtime_calls::RuntimeCallHandler, runtime_storage::RuntimeStorage, From c07116802f4c85a8b2adad104c2fd7d5c018df33 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 16:24:44 +0100 Subject: [PATCH 055/112] [core] env3: rename some methods in off-chain test-api --- core/src/env3/engine/off_chain/test_api.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 5318a2be556..15d04cdb01e 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -82,7 +82,7 @@ pub fn pop_execution_context() { /// - If `account` does not exist. /// - If the underlying `account` type does not match. /// - If the underlying `new_balance` type does not match. -pub fn set_balance(account_id: T::AccountId, new_balance: T::Balance) -> Result<()> +pub fn set_account_balance(account_id: T::AccountId, new_balance: T::Balance) -> Result<()> where T: EnvTypes, { @@ -108,7 +108,7 @@ where /// /// - If `account` does not exist. /// - If the underlying `account` type does not match. -pub fn get_balance(account_id: T::AccountId) -> Result +pub fn get_account_balance(account_id: T::AccountId) -> Result where T: EnvTypes, { @@ -129,7 +129,7 @@ where /// - 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_rent_allowance( +pub fn set_contract_rent_allowance( account_id: T::AccountId, new_rent_allowance: T::Balance, ) -> Result<()> @@ -157,7 +157,7 @@ where /// - 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_rent_allowance(account_id: T::AccountId) -> Result +pub fn get_contract_rent_allowance(account_id: T::AccountId) -> Result where T: EnvTypes, { @@ -211,7 +211,7 @@ where /// # Note /// /// This allows to control what [`crate::env3::random`] returns. -pub fn set_entropy(entropy: T::Hash) -> Result<()> +pub fn set_block_entropy(entropy: T::Hash) -> Result<()> where T: EnvTypes, { From f770514a1f69e48decea661c20b800f45d7e3bea Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 16:25:51 +0100 Subject: [PATCH 056/112] [core] env3: add doc comment to test::emitted_events --- core/src/env3/engine/off_chain/test_api.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 15d04cdb01e..d976ba25f66 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -238,6 +238,7 @@ pub fn past_printlns() -> impl Iterator { }) } +/// Returns the recorded emitted events in order. pub fn emitted_events() -> impl Iterator { ::on_instance(|instance| { // We return a clone of the recorded emitted events instead of From d84c877afbc0b7db3bc8733b6318851451884d45 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 16:33:38 +0100 Subject: [PATCH 057/112] [core] env3: add stub for test::advance_block --- core/src/env3/engine/off_chain/mod.rs | 5 +++++ core/src/env3/engine/off_chain/test_api.rs | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index d50b03ec5db..2cf398086ac 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -114,6 +114,11 @@ impl EnvInstance { } } + /// Advances the chain by a single block. + pub fn advance_block(&mut self) { + todo!() + } + /// Returns the current execution context. fn exec_context(&self) -> Result<&ExecContext> { self.exec_context diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index d976ba25f66..d5e73cc10e2 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -254,3 +254,10 @@ pub fn emitted_events() -> impl Iterator { .into_iter() }) } + +/// Advances the chain by a single block. +pub fn advance_block() { + ::on_instance(|instance| { + instance.advance_block() + }) +} From 0d10822fb5528f71379599a8e184d3b944762283 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 16:33:51 +0100 Subject: [PATCH 058/112] [core] env3: rename some more test API methods --- core/src/env3/engine/off_chain/test_api.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index d5e73cc10e2..86be00c4af4 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -222,7 +222,7 @@ where } /// Returns the contents of the past performed environmental `println` in order. -pub fn past_printlns() -> impl Iterator { +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` @@ -239,7 +239,7 @@ pub fn past_printlns() -> impl Iterator { } /// Returns the recorded emitted events in order. -pub fn emitted_events() -> impl Iterator { +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` From 31658b9e3a78d98df78c88581dd9f89b503b5c77 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 21:45:46 +0100 Subject: [PATCH 059/112] [core] env3: initialize entropy of block randomly --- core/Cargo.toml | 7 +++++++ core/src/env3/engine/off_chain/db/block.rs | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index c3adaa3a209..ecea24b8b21 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -27,6 +27,12 @@ derive_more = { version = "0.99.2", default-features = false, features = ["from" smallvec = { version = "1.0", default-features = false, features = ["union"] } cfg-if = "0.1" +# 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"] test-env = [ @@ -38,6 +44,7 @@ std = [ "ink_prelude/std", "scale/std", "type-metadata/std", + "rand/std", ] ink-generate-abi = [ "ink_abi", diff --git a/core/src/env3/engine/off_chain/db/block.rs b/core/src/env3/engine/off_chain/db/block.rs index 7245f13c7d1..6b65eb2483e 100644 --- a/core/src/env3/engine/off_chain/db/block.rs +++ b/core/src/env3/engine/off_chain/db/block.rs @@ -40,10 +40,14 @@ pub struct Block { impl Block { /// Creates a new block for the given number and moment. - pub fn new(number: T::BlockNumber, time_stamp: T::Moment, entropy: T::Hash) -> Self + pub fn new(number: T::BlockNumber, time_stamp: T::Moment) -> 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), From f34153d1b8157e0e1c22fee591540b2c3688c998 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 21:47:05 +0100 Subject: [PATCH 060/112] [core] env3: extend EnvTypes trait bounds This change makes the trait bounds more similar to what Substrate defines. --- core/Cargo.toml | 2 + core/src/env3/types.rs | 108 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 103 insertions(+), 7 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index ecea24b8b21..62d6dac2f07 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -26,6 +26,7 @@ type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", de 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. # @@ -45,6 +46,7 @@ std = [ "scale/std", "type-metadata/std", "rand/std", + "num-traits/std", ] ink-generate-abi = [ "ink_abi", diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs index 1b3ef88ff46..2f5ce69df82 100644 --- a/core/src/env3/types.rs +++ b/core/src/env3/types.rs @@ -34,25 +34,93 @@ use scale::{ #[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, +}; + +pub trait SimpleArithmetic: + Sized + + Bounded + + Ord + + PartialOrd + + Zero + + One + + Bounded + + Add + + AddAssign + + Sub + + SubAssign + + Mul + + MulAssign + + Div + + DivAssign +{ +} + +impl SimpleArithmetic for T where + T: Sized + + Bounded + + Ord + + PartialOrd + + Zero + + One + + Bounded + + 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; + type AccountId: 'static + scale::Codec + Clone + PartialEq + Eq + Ord; /// The type of balances. - type Balance: 'static + scale::Codec + Clone + PartialEq + Eq; + 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 Moment: 'static + scale::Codec + Clone + PartialEq + Eq; + type Moment: 'static + scale::Codec + Copy + Clone + PartialEq + Eq + SimpleArithmetic; /// The type of block number. - type BlockNumber: 'static + scale::Codec + Clone + PartialEq + Eq; + type BlockNumber: 'static + + scale::Codec + + Copy + + Clone + + PartialEq + + Eq + + SimpleArithmetic; /// The type of a call into the runtime type Call: 'static + scale::Codec; } @@ -100,7 +168,7 @@ pub type BlockNumber = u64; /// /// 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, Clone, PartialEq, Eq)] +#[derive(Debug)] pub enum Call {} impl Encode for Call { @@ -128,7 +196,20 @@ impl scale::Decode for Call { /// /// This is a mirror of the `AccountId` type used in the default configuration /// of PALLET contracts. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encode, Decode, From, Default)] +#[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]); @@ -149,7 +230,20 @@ impl Flush for AccountId {} /// /// This is a mirror of the `Hash` type used in the default configuration /// of PALLET contracts. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encode, Decode, From, Default)] +#[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]); From c60d7806a20dc0edb0cdadbe901fb51b004d8978 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 21:50:11 +0100 Subject: [PATCH 061/112] [core] env3: document SimpleArithmetic trait --- core/src/env3/types.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs index 2f5ce69df82..6ae2a7fe056 100644 --- a/core/src/env3/types.rs +++ b/core/src/env3/types.rs @@ -50,6 +50,11 @@ use num_traits::{ 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 + Bounded From b60d7e5ff8ff5efe8535a17c8eac8de17292315e Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 22:12:32 +0100 Subject: [PATCH 062/112] [core] env3: add From to SimpleArithmetic trait --- core/src/env3/types.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs index 6ae2a7fe056..4bb0c7694e3 100644 --- a/core/src/env3/types.rs +++ b/core/src/env3/types.rs @@ -57,6 +57,7 @@ use num_traits::{ /// if needed. pub trait SimpleArithmetic: Sized + + From + Bounded + Ord + PartialOrd @@ -76,6 +77,7 @@ pub trait SimpleArithmetic: impl SimpleArithmetic for T where T: Sized + + From + Bounded + Ord + PartialOrd From 183b631fde6d41883716d97bb03a611e30d716ed Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 22:13:10 +0100 Subject: [PATCH 063/112] [core] env3: made some trait bounds more explicit in SimpleArithmetic trait --- core/src/env3/types.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs index 4bb0c7694e3..d6b736a2178 100644 --- a/core/src/env3/types.rs +++ b/core/src/env3/types.rs @@ -64,14 +64,14 @@ pub trait SimpleArithmetic: + Zero + One + Bounded - + Add - + AddAssign - + Sub - + SubAssign - + Mul - + MulAssign - + Div - + DivAssign + + Add + + AddAssign + + Sub + + SubAssign + + Mul + + MulAssign + + Div + + DivAssign { } @@ -84,14 +84,14 @@ impl SimpleArithmetic for T where + Zero + One + Bounded - + Add - + AddAssign - + Sub - + SubAssign - + Mul - + MulAssign - + Div - + DivAssign + + Add + + AddAssign + + Sub + + SubAssign + + Mul + + MulAssign + + Div + + DivAssign { } From 062a4f32dafffd761a7a3c18f80bd6272a9b986d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 22:13:37 +0100 Subject: [PATCH 064/112] [core] env3: add comment describing potential future extensions to SimpleArithmetic trait --- core/src/env3/types.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs index d6b736a2178..b0aaf91b728 100644 --- a/core/src/env3/types.rs +++ b/core/src/env3/types.rs @@ -72,6 +72,38 @@ pub trait SimpleArithmetic: + 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 + { } From 7b5e3adbed884d3cb1c2171d0bebdb570decda55 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 23:19:12 +0100 Subject: [PATCH 065/112] [core] env3: rename EnvTypes::Moment to TimeStamp --- core/src/env3/api.rs | 2 +- core/src/env3/backend.rs | 2 +- core/src/env3/engine/off_chain/db/block.rs | 10 +++++----- core/src/env3/engine/off_chain/db/mod.rs | 2 +- core/src/env3/engine/off_chain/impls.rs | 2 +- core/src/env3/engine/off_chain/mod.rs | 2 +- core/src/env3/engine/off_chain/types.rs | 6 +++--- core/src/env3/types.rs | 8 ++++---- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index 8d257121474..13536868f0a 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -94,7 +94,7 @@ where /// # Errors /// /// If the returned value cannot be properly decoded. -pub fn now_in_ms() -> Result +pub fn now_in_ms() -> Result where T: EnvTypes, { diff --git a/core/src/env3/backend.rs b/core/src/env3/backend.rs index d6981301f5b..d0c69538aac 100644 --- a/core/src/env3/backend.rs +++ b/core/src/env3/backend.rs @@ -98,7 +98,7 @@ pub trait TypedEnv: Env { fn gas_left(&mut self) -> Result; /// Returns the current block time in milliseconds. - fn now_in_ms(&mut self) -> Result; + fn now_in_ms(&mut self) -> Result; /// Returns the address of the executed contract. fn address(&mut self) -> Result; diff --git a/core/src/env3/engine/off_chain/db/block.rs b/core/src/env3/engine/off_chain/db/block.rs index 6b65eb2483e..96741c2886c 100644 --- a/core/src/env3/engine/off_chain/db/block.rs +++ b/core/src/env3/engine/off_chain/db/block.rs @@ -19,7 +19,7 @@ use super::{ }, OffBlockNumber, OffHash, - OffMoment, + OffTimeStamp, }; use crate::env3::EnvTypes; @@ -28,7 +28,7 @@ pub struct Block { /// The current block number. number: OffBlockNumber, /// The time stamp of the block. - time_stamp: OffMoment, + time_stamp: OffTimeStamp, /// The randomization entropy for a block. /// /// # Note @@ -39,8 +39,8 @@ pub struct Block { } impl Block { - /// Creates a new block for the given number and moment. - pub fn new(number: T::BlockNumber, time_stamp: T::Moment) -> Self + /// 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, { @@ -64,7 +64,7 @@ impl Block { } /// Returns the time stamp of the block. - pub fn time_stamp(&self) -> Result + pub fn time_stamp(&self) -> Result where T: EnvTypes, { diff --git a/core/src/env3/engine/off_chain/db/mod.rs b/core/src/env3/engine/off_chain/db/mod.rs index 27d304ccf27..76ff7ba7663 100644 --- a/core/src/env3/engine/off_chain/db/mod.rs +++ b/core/src/env3/engine/off_chain/db/mod.rs @@ -47,5 +47,5 @@ use super::{ OffBalance, OffBlockNumber, OffHash, - OffMoment, + OffTimeStamp, }; diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index bf96c9d2557..bd37c198c32 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -148,7 +148,7 @@ impl TypedEnv for EnvInstance { .map_err(Into::into) } - fn now_in_ms(&mut self) -> Result { + fn now_in_ms(&mut self) -> Result { self.current_block() .expect("uninitialized execution context") .time_stamp::() diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 2cf398086ac..3b7bc5eef48 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -41,7 +41,7 @@ use self::{ OffBlockNumber, OffCall, OffHash, - OffMoment, + OffTimeStamp, }, }; pub use self::{ diff --git a/core/src/env3/engine/off_chain/types.rs b/core/src/env3/engine/off_chain/types.rs index 18b36bdb8c8..32b0d75e695 100644 --- a/core/src/env3/engine/off_chain/types.rs +++ b/core/src/env3/engine/off_chain/types.rs @@ -36,8 +36,8 @@ mod type_marker { #[derive(Debug, Clone)] pub enum Balance {} /// Type marker representing an environmental `Hash`. #[derive(Debug, Clone)] pub enum Hash {} - /// Type marker representing an environmental `Moment`. - #[derive(Debug, Clone)] pub enum Moment {} + /// Type marker representing an environmental `TimeStamp`. + #[derive(Debug, Clone)] pub enum OffTimeStamp {} /// Type marker representing an environmental `BlockNumber`. #[derive(Debug, Clone)] pub enum BlockNumber {} /// Type marker representing an environmental `Call`. @@ -51,7 +51,7 @@ pub type OffBalance = TypedEncoded; /// Off-chain environment hash type. pub type OffHash = TypedEncoded; /// Off-chain environment moment (block time) type. -pub type OffMoment = TypedEncoded; +pub type OffTimeStamp = TypedEncoded; /// Off-chain environment block number type. pub type OffBlockNumber = TypedEncoded; /// Off-chain environment call (runtime dispatch) type. diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs index b0aaf91b728..9548351234c 100644 --- a/core/src/env3/types.rs +++ b/core/src/env3/types.rs @@ -151,7 +151,7 @@ pub trait EnvTypes { + AsRef<[u8]> + AsMut<[u8]>; /// The type of timestamps. - type Moment: 'static + scale::Codec + Copy + Clone + PartialEq + Eq + SimpleArithmetic; + type TimeStamp: 'static + scale::Codec + Copy + Clone + PartialEq + Eq + SimpleArithmetic; /// The type of block number. type BlockNumber: 'static + scale::Codec @@ -182,7 +182,7 @@ impl EnvTypes for DefaultEnvTypes { type AccountId = AccountId; type Balance = Balance; type Hash = Hash; - type Moment = Moment; + type TimeStamp = TimeStamp; type BlockNumber = BlockNumber; type Call = Call; } @@ -190,8 +190,8 @@ impl EnvTypes for DefaultEnvTypes { /// The default balance type. pub type Balance = u128; -/// The default moment type. -pub type Moment = u64; +/// The default time stamp type. +pub type TimeStamp = u64; /// The default block number type. pub type BlockNumber = u64; From 62be749efd6c1d8e9a301363c3f6bfb145a82d06 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 23:19:32 +0100 Subject: [PATCH 066/112] [core] env3: add block_time to ChainSpec --- core/src/env3/engine/off_chain/db/chain_spec.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/src/env3/engine/off_chain/db/chain_spec.rs b/core/src/env3/engine/off_chain/db/chain_spec.rs index 7ccf2f9d257..4d054f9c544 100644 --- a/core/src/env3/engine/off_chain/db/chain_spec.rs +++ b/core/src/env3/engine/off_chain/db/chain_spec.rs @@ -15,6 +15,7 @@ use super::{ super::Result, OffBalance, + OffTimeStamp, }; use crate::env3::EnvTypes; @@ -26,6 +27,8 @@ pub struct ChainSpec { minimum_balance: OffBalance, /// The tombstone deposit. tombstone_deposit: OffBalance, + /// The targeted block time. + block_time: OffTimeStamp, } impl ChainSpec { @@ -35,6 +38,7 @@ impl ChainSpec { gas_price: OffBalance::uninitialized(), minimum_balance: OffBalance::uninitialized(), tombstone_deposit: OffBalance::uninitialized(), + block_time: OffTimeStamp::uninitialized(), } } @@ -61,4 +65,12 @@ impl ChainSpec { { 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) + } } From fb4d7453f5dc86c092227a01f537c6b277e06947 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 23:21:28 +0100 Subject: [PATCH 067/112] [core] env3: remove CodeDb again Not needed right now. Maybe re-added at a later point. --- core/src/env3/engine/off_chain/db/codes.rs | 85 ---------------------- core/src/env3/engine/off_chain/db/mod.rs | 2 - core/src/env3/engine/off_chain/mod.rs | 9 --- 3 files changed, 96 deletions(-) delete mode 100644 core/src/env3/engine/off_chain/db/codes.rs diff --git a/core/src/env3/engine/off_chain/db/codes.rs b/core/src/env3/engine/off_chain/db/codes.rs deleted file mode 100644 index 077e8c284a9..00000000000 --- a/core/src/env3/engine/off_chain/db/codes.rs +++ /dev/null @@ -1,85 +0,0 @@ -// 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::OffHash; -use crate::env3::EnvTypes; -use ink_prelude::collections::BTreeMap; - -/// The Wasm codes data base. -pub struct CodeDb { - /// Mapping from (code)hash to Wasm blob. - codes: BTreeMap, -} - -impl CodeDb { - /// Creates a new empty Wasm codes database. - pub fn new() -> Self { - Self { - codes: BTreeMap::new(), - } - } - - /// Puts the wasm code (as bytes) onto the chain and returns its code hash. - pub fn put_code(&mut self, code_hash: T::Hash, wasm_bytes: &[u8]) - where - T: EnvTypes, - { - self.codes - .insert(OffHash::new(&code_hash), Code::new(wasm_bytes.to_vec())); - } - - /// Gets the wasm code blob associated with the given code hash if any. - pub fn get_code(&self, code_hash: T::Hash) -> Option<&Code> - where - T: EnvTypes, - { - self.codes.get(&OffHash::new(&code_hash)) - } - - /// Gets the wasm code blob associated with the given code hash if any. - pub fn get_code_mut(&mut self, code_hash: T::Hash) -> Option<&mut Code> - where - T: EnvTypes, - { - self.codes.get_mut(&OffHash::new(&code_hash)) - } -} - -/// A Wasm blob on the chain. -pub struct Code { - /// The bytes of the Wasm blob. - wasm_bytes: Vec, - /// The references to this Wasm blob to count usages. - pub references: usize, -} - -impl Code { - /// Creates a new empty code. - pub fn empty() -> Self { - Self::new(Vec::new()) - } - - /// Creates a new code from the given Wasm bytes. - pub fn new(wasm_bytes: Vec) -> Self { - Self { - wasm_bytes, - references: 0, - } - } - - /// Returns the Wasm bytes. - pub fn wasm_bytes(&self) -> &[u8] { - &self.wasm_bytes - } -} diff --git a/core/src/env3/engine/off_chain/db/mod.rs b/core/src/env3/engine/off_chain/db/mod.rs index 76ff7ba7663..b497df30b88 100644 --- a/core/src/env3/engine/off_chain/db/mod.rs +++ b/core/src/env3/engine/off_chain/db/mod.rs @@ -15,7 +15,6 @@ mod accounts; mod block; mod chain_spec; -mod codes; mod console; mod events; mod exec_context; @@ -31,7 +30,6 @@ pub use self::{ }, block::Block, chain_spec::ChainSpec, - codes::CodeDb, console::{ Console, PastPrints, diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 3b7bc5eef48..aa9a09ff54c 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -26,7 +26,6 @@ use self::{ AccountsDb, Block, ChainSpec, - CodeDb, Console, EmittedEvent, EmittedEventsRecorder, @@ -75,13 +74,6 @@ pub type Result = core::result::Result; pub struct EnvInstance { /// The accounts database of the environment. accounts: AccountsDb, - /// Uploaded Wasm contract codes. - #[allow( - dead_code, - // Needed as soon as we support to execute contracts - // directly through the off-chain environment. - )] - codes: CodeDb, /// Current execution context and context. exec_context: Vec, /// The general chain spec. @@ -103,7 +95,6 @@ impl EnvInstance { pub fn uninitialized() -> Self { Self { accounts: AccountsDb::new(), - codes: CodeDb::new(), exec_context: Vec::new(), chain_spec: ChainSpec::uninitialized(), blocks: Vec::new(), From fd43a807af8cd06fd74a381072ee6b102f25a7c7 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 23:31:16 +0100 Subject: [PATCH 068/112] [core] env3: apply rustfmt --- core/src/env3/engine/off_chain/test_api.rs | 5 +- core/src/env3/types.rs | 72 ++++++++++++---------- 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 86be00c4af4..96053c2cda4 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -82,7 +82,10 @@ pub fn pop_execution_context() { /// - If `account` does not exist. /// - If the underlying `account` type does not match. /// - If the underlying `new_balance` type does not match. -pub fn set_account_balance(account_id: T::AccountId, new_balance: T::Balance) -> Result<()> +pub fn set_account_balance( + account_id: T::AccountId, + new_balance: T::Balance, +) -> Result<()> where T: EnvTypes, { diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs index 9548351234c..c295d4848b0 100644 --- a/core/src/env3/types.rs +++ b/core/src/env3/types.rs @@ -72,38 +72,38 @@ pub trait SimpleArithmetic: + 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 + +// 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 + { } @@ -151,7 +151,13 @@ pub trait EnvTypes { + AsRef<[u8]> + AsMut<[u8]>; /// The type of timestamps. - type TimeStamp: 'static + scale::Codec + Copy + Clone + PartialEq + Eq + SimpleArithmetic; + type TimeStamp: 'static + + scale::Codec + + Copy + + Clone + + PartialEq + + Eq + + SimpleArithmetic; /// The type of block number. type BlockNumber: 'static + scale::Codec From a30d8769ade998fded3ad5a613c5827b794ebd7f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 23:31:31 +0100 Subject: [PATCH 069/112] [core] env3: implement off-chain test::advance_block --- core/src/env3/engine/off_chain/mod.rs | 12 ++++++++++-- core/src/env3/engine/off_chain/test_api.rs | 9 +++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index aa9a09ff54c..271bd98928c 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -51,6 +51,7 @@ pub use self::{ typed_encoded::TypedEncodedError, }; use super::OnInstance; +use crate::env3::EnvTypes; use core::cell::RefCell; use derive_more::From; @@ -106,8 +107,15 @@ impl EnvInstance { } /// Advances the chain by a single block. - pub fn advance_block(&mut self) { - todo!() + pub fn advance_block(&mut self) -> crate::env3::Result<()> + where + T: EnvTypes, + { + let new_block_number = T::BlockNumber::from(self.blocks.len() as u32 + 1); + 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. diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 96053c2cda4..fa1629621aa 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -259,8 +259,9 @@ pub fn recorded_events() -> impl Iterator { } /// Advances the chain by a single block. -pub fn advance_block() { - ::on_instance(|instance| { - instance.advance_block() - }) +pub fn advance_block() -> Result<()> +where + T: EnvTypes, +{ + ::on_instance(|instance| instance.advance_block::()) } From e9531eb9e498fe24381c99a408d29998bb5cfee2 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 27 Jan 2020 23:32:30 +0100 Subject: [PATCH 070/112] [core] env3: fix bug in off-chain test::advance_block impl --- core/src/env3/engine/off_chain/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 271bd98928c..c8f0fe69db4 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -111,7 +111,7 @@ impl EnvInstance { where T: EnvTypes, { - let new_block_number = T::BlockNumber::from(self.blocks.len() as u32 + 1); + 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)); From 3f3de12e6949f357ef6c51281f835933731214db Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 28 Jan 2020 00:25:20 +0100 Subject: [PATCH 071/112] [core] env3: made SimpleArithmetic trait more explicit towards Output types --- core/src/env3/types.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/core/src/env3/types.rs b/core/src/env3/types.rs index c295d4848b0..51765477dc6 100644 --- a/core/src/env3/types.rs +++ b/core/src/env3/types.rs @@ -64,13 +64,13 @@ pub trait SimpleArithmetic: + Zero + One + Bounded - + Add + + Add + AddAssign - + Sub + + Sub + SubAssign - + Mul + + Mul + MulAssign - + Div + + Div + DivAssign // Further trait bounds from the original SimpleArithmetic trait // that we could use to extend ink!'s SimpleArithmetic trait. @@ -115,14 +115,13 @@ impl SimpleArithmetic for T where + PartialOrd + Zero + One - + Bounded - + Add + + Add + AddAssign - + Sub + + Sub + SubAssign - + Mul + + Mul + MulAssign - + Div + + Div + DivAssign { } From a90902e5a87d26a865414116151723e8eccdb94c Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 28 Jan 2020 00:26:08 +0100 Subject: [PATCH 072/112] [core] env3: add default initialization routines for off-chain environment --- core/src/env3/engine/off_chain/db/accounts.rs | 65 +++++++++++----- .../env3/engine/off_chain/db/chain_spec.rs | 13 ++++ core/src/env3/engine/off_chain/mod.rs | 77 ++++++++++++++++++- core/src/env3/engine/off_chain/test_api.rs | 47 +++++++++++ 4 files changed, 182 insertions(+), 20 deletions(-) diff --git a/core/src/env3/engine/off_chain/db/accounts.rs b/core/src/env3/engine/off_chain/db/accounts.rs index c6ed3fdcbc1..cb7b223cc35 100644 --- a/core/src/env3/engine/off_chain/db/accounts.rs +++ b/core/src/env3/engine/off_chain/db/accounts.rs @@ -19,7 +19,6 @@ use super::{ }, OffAccountId, OffBalance, - OffHash, }; use crate::{ env3::{ @@ -106,20 +105,39 @@ impl AccountsDb { self.accounts.get_mut(at) } - /// Creates a new user account. - pub fn new_user_account(&mut self, initial_balance: T::Balance) -> T::AccountId - where + /// 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, { - todo!() + self.accounts.insert( + OffAccountId::new(&account_id), + Account { + balance: OffBalance::new(&initial_balance), + kind: AccountKind::User, + }, + ); } /// Creates a new contract account. - pub fn new_contract_account(&mut self) -> T::AccountId - where + pub fn add_contract_account( + &mut self, + account_id: T::AccountId, + initial_balance: T::Balance, + rent_allowance: T::Balance, + ) where T: EnvTypes, { - todo!() + self.accounts.insert( + OffAccountId::new(&account_id), + Account { + balance: OffBalance::new(&initial_balance), + kind: AccountKind::Contract(ContractAccount::new::(rent_allowance)), + }, + ); } } @@ -190,15 +208,6 @@ impl Account { }) } - /// Returns the code hash of the contract account of an error. - pub fn code_hash(&self) -> Result - where - T: EnvTypes, - { - self.contract_or_err() - .and_then(|contract| contract.code_hash.decode().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 @@ -236,12 +245,23 @@ pub enum AccountKind { pub struct ContractAccount { /// The contract's rent allowance. rent_allowance: OffBalance, - /// The contract's code hash. - code_hash: OffHash, /// 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(), + } + } +} + /// The storage of a contract instance. pub struct ContractStorage { /// The entries within the contract storage. @@ -249,6 +269,13 @@ pub struct ContractStorage { } impl ContractStorage { + /// Creates a new empty contract storage. + pub fn new() -> Self { + Self { + entries: BTreeMap::new(), + } + } + /// Returns the decoded storage at the key if any. pub fn get_storage(&self, at: Key) -> Result> where diff --git a/core/src/env3/engine/off_chain/db/chain_spec.rs b/core/src/env3/engine/off_chain/db/chain_spec.rs index 4d054f9c544..cf34d457319 100644 --- a/core/src/env3/engine/off_chain/db/chain_spec.rs +++ b/core/src/env3/engine/off_chain/db/chain_spec.rs @@ -42,6 +42,19 @@ impl ChainSpec { } } + /// 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.assign::(&T::Balance::from(100))?; + self.minimum_balance.assign::(&T::Balance::from(42))?; + self.tombstone_deposit.assign::(&T::Balance::from(16))?; + self.block_time.assign::(&T::TimeStamp::from(5))?; + Ok(()) + } + /// Returns the gas price for the chain. pub fn gas_price(&self) -> Result where diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index c8f0fe69db4..13e7d7a7e0d 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -106,6 +106,80 @@ impl EnvInstance { } } + /// 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 @@ -114,7 +188,8 @@ impl EnvInstance { 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)); + self.blocks + .push(Block::new::(new_block_number, new_time_stamp)); Ok(()) } diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index fa1629621aa..2298c2c670b 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -265,3 +265,50 @@ where { ::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::() + }) +} From 07c5ac9c6880b99aed42464ef688908916a3a526 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 28 Jan 2020 00:26:48 +0100 Subject: [PATCH 073/112] [core] env3: remove test::create_user_account for now --- core/src/env3/engine/off_chain/test_api.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 2298c2c670b..3a23bd13ea3 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -174,20 +174,6 @@ where }) } -/// Creates a new user account and returns its account ID. -/// -/// # Errors -/// -/// - If `initial_balance` cannot be properly encoded. -pub fn create_user_account(initial_balance: T::Balance) -> Result -where - T: EnvTypes, -{ - ::on_instance(|instance| { - Ok(instance.accounts.new_user_account::(initial_balance)) - }) -} - /// Sets the runtime storage to value for the given key. pub fn set_runtime_storage(key: &[u8], value: T) where From 0f585cb70cc8fb694c59ccf9284ef0cd95c8c6f6 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 28 Jan 2020 00:27:37 +0100 Subject: [PATCH 074/112] [core] env3: fix missing renaming from Moment -> TimeStamp --- core/src/env3/engine/on_chain/impls.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index 1e44894168a..67c5a937cf2 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -191,8 +191,8 @@ impl TypedEnv for EnvInstance { self.get_property::(ext::gas_left) } - fn now_in_ms(&mut self) -> Result { - self.get_property::(ext::now) + fn now_in_ms(&mut self) -> Result { + self.get_property::(ext::now) } fn address(&mut self) -> Result { From 24f09f7d8dc104c525a42a18ef283faa1596a1d3 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 28 Jan 2020 12:49:53 +0100 Subject: [PATCH 075/112] [core] env3: impl PartialEq and Eq for off-chain error types --- core/src/env3/engine/off_chain/db/accounts.rs | 2 +- core/src/env3/engine/off_chain/mod.rs | 2 +- core/src/env3/engine/off_chain/typed_encoded.rs | 3 ++- core/src/env3/error.rs | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/src/env3/engine/off_chain/db/accounts.rs b/core/src/env3/engine/off_chain/db/accounts.rs index cb7b223cc35..02b450d1840 100644 --- a/core/src/env3/engine/off_chain/db/accounts.rs +++ b/core/src/env3/engine/off_chain/db/accounts.rs @@ -31,7 +31,7 @@ use derive_more::From; use ink_prelude::collections::BTreeMap; /// Errors encountered upon interacting with the accounts database. -#[derive(Debug, From)] +#[derive(Debug, From, PartialEq, Eq)] pub enum AccountError { TypedEncoded(TypedEncodedError), #[from(ignore)] diff --git a/core/src/env3/engine/off_chain/mod.rs b/core/src/env3/engine/off_chain/mod.rs index 13e7d7a7e0d..a5608e19677 100644 --- a/core/src/env3/engine/off_chain/mod.rs +++ b/core/src/env3/engine/off_chain/mod.rs @@ -55,7 +55,7 @@ use crate::env3::EnvTypes; use core::cell::RefCell; use derive_more::From; -#[derive(Debug, From)] +#[derive(Debug, From, PartialEq, Eq)] pub enum OffChainError { Account(AccountError), TypedEncoded(TypedEncodedError), diff --git a/core/src/env3/engine/off_chain/typed_encoded.rs b/core/src/env3/engine/off_chain/typed_encoded.rs index baee2988a72..ac8451aebae 100644 --- a/core/src/env3/engine/off_chain/typed_encoded.rs +++ b/core/src/env3/engine/off_chain/typed_encoded.rs @@ -67,7 +67,7 @@ pub struct TypedEncoded { } /// Errors that may be encountered upon operating on typed encoded instances. -#[derive(Debug, From)] +#[derive(Debug, From, PartialEq, Eq)] pub enum TypedEncodedError { /// Error upon decoding. Decode(scale::Error), @@ -241,6 +241,7 @@ impl TypedEncoded { self.check_enforced_type::()?; self.encoded.clear(); value.encode_to(&mut self.encoded); + self.type_id = Some(core::any::TypeId::of::()); Ok(()) } diff --git a/core/src/env3/error.rs b/core/src/env3/error.rs index 3746c530db6..daa841da458 100644 --- a/core/src/env3/error.rs +++ b/core/src/env3/error.rs @@ -18,7 +18,7 @@ use derive_more::From; use crate::env3::engine::off_chain::OffChainError; /// Errors that can be encountered upon environmental interaction. -#[derive(From)] +#[derive(Debug, From, PartialEq, Eq)] pub enum EnvError { /// Error upon decoding an encoded value. Decode(scale::Error), From 0fef2651f2b2766271da0f23673b2488e53d986f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 28 Jan 2020 12:50:42 +0100 Subject: [PATCH 076/112] [core] env3: add test::run_test for off-chain testing with default setup --- core/src/env3/engine/off_chain/test_api.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index 3a23bd13ea3..a9c9d05983b 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -298,3 +298,16 @@ where 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) +} From d653431cb8112b55a52d365b82b4bf1de0f38e3b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 28 Jan 2020 12:51:09 +0100 Subject: [PATCH 077/112] [core] env3: fix several minor bugs with the off-chain environment --- core/src/env3/engine/off_chain/db/chain_spec.rs | 8 ++++---- core/src/env3/engine/off_chain/db/exec_context.rs | 2 +- core/src/env3/engine/off_chain/impls.rs | 7 +++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/core/src/env3/engine/off_chain/db/chain_spec.rs b/core/src/env3/engine/off_chain/db/chain_spec.rs index cf34d457319..e113cb0ca46 100644 --- a/core/src/env3/engine/off_chain/db/chain_spec.rs +++ b/core/src/env3/engine/off_chain/db/chain_spec.rs @@ -48,10 +48,10 @@ impl ChainSpec { T: EnvTypes, ::AccountId: From<[u8; 32]>, { - self.gas_price.assign::(&T::Balance::from(100))?; - self.minimum_balance.assign::(&T::Balance::from(42))?; - self.tombstone_deposit.assign::(&T::Balance::from(16))?; - self.block_time.assign::(&T::TimeStamp::from(5))?; + 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(()) } diff --git a/core/src/env3/engine/off_chain/db/exec_context.rs b/core/src/env3/engine/off_chain/db/exec_context.rs index 213a81eb8ef..347caeb177f 100644 --- a/core/src/env3/engine/off_chain/db/exec_context.rs +++ b/core/src/env3/engine/off_chain/db/exec_context.rs @@ -176,7 +176,7 @@ where /// /// If there has already been set provided gas. pub fn gas(mut self, gas: T::Balance) -> Self { - if self.callee.is_some() { + if self.gas.is_some() { panic!("already has provided gas"); } self.gas = Some(gas); diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index bd37c198c32..b0ea218f1a1 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -25,6 +25,7 @@ use crate::{ ReturnType, }, Env, + EnvError, EnvTypes, Result, Topics, @@ -74,10 +75,8 @@ impl Env for EnvInstance { R: scale::Decode, { self.callee_account() - .get_storage(key) - .expect("callee account is not a smart contract") - .ok_or_else(|| scale::Error::from("could not decode contract storage")) - .map_err(Into::into) + .get_storage::(key)? + .ok_or_else(|| EnvError::MissingContractStorageEntry) } fn clear_contract_storage(&mut self, key: Key) { From 7c99611c96137df4c1d36bdd0173c64c3d3ef523 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 28 Jan 2020 12:51:22 +0100 Subject: [PATCH 078/112] [core] convert key.rs tests to new env3 --- core/src/storage/key.rs | 105 +++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/core/src/storage/key.rs b/core/src/storage/key.rs index a32a39ba292..340915ab8d6 100644 --- a/core/src/storage/key.rs +++ b/core/src/storage/key.rs @@ -230,66 +230,93 @@ 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(()) + // 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); }) } #[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(()) + // 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])); }) } #[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(()) + // unsafe { + // env::store(key0a, &[0x01]); + // } + // unsafe { + // env::store(key1a, &[0x02]); + // } + // unsafe { + // env::store(key2a, &[0x03]); + // } + // assert_eq!(unsafe { env::load(key2b) }, Some(vec![0x03])); + // assert_eq!(unsafe { env::load(key1b) }, Some(vec![0x01])); + // assert_eq!(unsafe { env::load(key0b) }, Some(vec![0x02])); }) } From 3926d7bf99079eeb74a1cb54e1865f74b1f21fcc Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 28 Jan 2020 12:51:53 +0100 Subject: [PATCH 079/112] [core] remove commented out code in key.rs tests --- core/src/storage/key.rs | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/core/src/storage/key.rs b/core/src/storage/key.rs index 340915ab8d6..3b0791dbafd 100644 --- a/core/src/storage/key.rs +++ b/core/src/storage/key.rs @@ -254,14 +254,6 @@ mod tests { Err(EnvError::MissingContractStorageEntry), ); Ok(()) - // 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); }) } @@ -277,14 +269,6 @@ mod tests { env::set_contract_storage(key10, &1337); assert_eq!(env::get_contract_storage::(key55), Ok(1337)); Ok(()) - // 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])); }) } @@ -305,18 +289,6 @@ mod tests { assert_eq!(env::get_contract_storage::(key1b), Ok(1)); assert_eq!(env::get_contract_storage::(key0b), Ok(2)); Ok(()) - // unsafe { - // env::store(key0a, &[0x01]); - // } - // unsafe { - // env::store(key1a, &[0x02]); - // } - // unsafe { - // env::store(key2a, &[0x03]); - // } - // assert_eq!(unsafe { env::load(key2b) }, Some(vec![0x03])); - // assert_eq!(unsafe { env::load(key1b) }, Some(vec![0x01])); - // assert_eq!(unsafe { env::load(key0b) }, Some(vec![0x02])); }) } From 1ca3033066c4423d1ea207151f9433fe9eaa539e Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 28 Jan 2020 20:34:48 +0100 Subject: [PATCH 080/112] [core] evn3: add test::get_contract_storage_rw --- core/src/env3/engine/off_chain/db/accounts.rs | 25 +++++++++++++++++++ core/src/env3/engine/off_chain/test_api.rs | 13 ++++++++++ 2 files changed, 38 insertions(+) diff --git a/core/src/env3/engine/off_chain/db/accounts.rs b/core/src/env3/engine/off_chain/db/accounts.rs index 02b450d1840..9ace7ac1954 100644 --- a/core/src/env3/engine/off_chain/db/accounts.rs +++ b/core/src/env3/engine/off_chain/db/accounts.rs @@ -27,6 +27,7 @@ use crate::{ }, storage::Key, }; +use core::cell::Cell; use derive_more::From; use ink_prelude::collections::BTreeMap; @@ -231,6 +232,11 @@ impl Account { 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. @@ -260,12 +266,21 @@ impl ContractAccount { 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 { @@ -273,14 +288,22 @@ impl ContractStorage { 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[..])) @@ -293,11 +316,13 @@ impl ContractStorage { 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/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index a9c9d05983b..d9383107b28 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -311,3 +311,16 @@ where 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)) + }) +} From 6d9c4434fcdc9ae7114b6a590fcacd2bea506ac7 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 28 Jan 2020 20:35:28 +0100 Subject: [PATCH 081/112] [core] use env3 instead of env1 in storage cells --- core/src/storage/cell/mod.rs | 2 - core/src/storage/cell/raw_cell.rs | 140 --------------- core/src/storage/cell/sync_cell.rs | 259 +++++++++++++++++----------- core/src/storage/cell/typed_cell.rs | 129 +++++++------- 4 files changed, 233 insertions(+), 297 deletions(-) delete mode 100644 core/src/storage/cell/raw_cell.rs 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(()) }) } } From 3c4c1899f294fafbbfaa50a873c464201b5ddf09 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 09:52:57 +0100 Subject: [PATCH 082/112] [core] integrate env3 into storage::TypedChunk --- core/src/storage/chunk/typed_chunk.rs | 290 +++++++++++++++----------- 1 file changed, 173 insertions(+), 117 deletions(-) diff --git a/core/src/storage/chunk/typed_chunk.rs b/core/src/storage/chunk/typed_chunk.rs index 5014e788fc5..7d3bc1bf932 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 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 a shared accessor the cell at the given index. + fn cell_at(&self, index: u32) -> TypedChunkCell { + TypedChunkCell::shared(self.key + index) + } + + /// 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,21 @@ 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() + // 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" + // ) + // }) } } @@ -157,31 +177,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 +221,107 @@ mod tests { chunk.clear(i); assert_eq!(chunk.load(i), None); } + Ok(()) }) } #[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(); + let contract_account_id = env::address::()?; // Reads and writes after init. - assert_eq!(env::test::total_reads(), 0); - assert_eq!(env::test::total_writes(), 0); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (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!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (i as usize + 1, 0) + ); } - assert_eq!(env::test::total_reads(), TEST_LEN as u64); - assert_eq!(env::test::total_writes(), 0); + assert_eq!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (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!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (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!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + (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!( + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + ( + TEST_LEN as usize + n + 1, + TEST_LEN as usize, + ) + ); } assert_eq!( - env::test::total_reads(), - TEST_LEN as u64 + LOAD_REPEATS as u64 + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + ( + 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 + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + ( + 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 + env::test::get_contract_storage_rw::( + &contract_account_id + )?, + ( + TEST_LEN as usize + LOAD_REPEATS, + TEST_LEN as usize + STORE_REPEATS, + ) ); + Ok(()) }) } } From 89021575110004ccf371486915112c5d5d4d9b39 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 09:53:23 +0100 Subject: [PATCH 083/112] [core] remove storage::RawChunk and NonCloneMarker --- core/src/storage/chunk/mod.rs | 4 - core/src/storage/chunk/raw_chunk.rs | 252 ---------------------------- core/src/storage/mod.rs | 5 - core/src/storage/non_clone.rs | 40 ----- 4 files changed, 301 deletions(-) delete mode 100644 core/src/storage/chunk/raw_chunk.rs delete mode 100644 core/src/storage/non_clone.rs 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/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) - } -} From e82ab0ccb080c41977e2a763104456c7bdfc7191 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 09:53:37 +0100 Subject: [PATCH 084/112] [core] adjust SyncChunk slightly for TypedChunk modifications --- core/src/storage/chunk/sync_chunk/chunk.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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() } } From 9f75a152b75d6aedeebd8efa61e4f4497ef5b0d7 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 10:13:25 +0100 Subject: [PATCH 085/112] [core] simplify transitioned env3 tests for TypedChunk --- core/src/storage/chunk/typed_chunk.rs | 46 ++++++++++----------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/core/src/storage/chunk/typed_chunk.rs b/core/src/storage/chunk/typed_chunk.rs index 7d3bc1bf932..a9dc278ba4e 100644 --- a/core/src/storage/chunk/typed_chunk.rs +++ b/core/src/storage/chunk/typed_chunk.rs @@ -225,35 +225,35 @@ mod tests { }) } + /// 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() -> Result<()> { env::test::run_test::(|_| { const TEST_LEN: u32 = 5; let mut chunk = create_typed_chunk(); - let contract_account_id = env::address::()?; // Reads and writes after init. assert_eq!( - env::test::get_contract_storage_rw::( - &contract_account_id - )?, - (0, 0) + get_contract_storage_rw(), + (0, 0), ); // Loading from all cells. for i in 0..TEST_LEN { chunk.load(i); assert_eq!( - env::test::get_contract_storage_rw::( - &contract_account_id - )?, + get_contract_storage_rw(), (i as usize + 1, 0) ); } assert_eq!( - env::test::get_contract_storage_rw::( - &contract_account_id - )?, + get_contract_storage_rw(), (TEST_LEN as usize, 0) ); @@ -261,16 +261,12 @@ mod tests { for i in 0..TEST_LEN { chunk.store(i, &i); assert_eq!( - env::test::get_contract_storage_rw::( - &contract_account_id - )?, + get_contract_storage_rw(), (TEST_LEN as usize, i as usize + 1) ); } assert_eq!( - env::test::get_contract_storage_rw::( - &contract_account_id - )?, + get_contract_storage_rw(), (TEST_LEN as usize, TEST_LEN as usize) ); @@ -279,9 +275,7 @@ mod tests { for n in 0..LOAD_REPEATS { chunk.load(0); assert_eq!( - env::test::get_contract_storage_rw::( - &contract_account_id - )?, + get_contract_storage_rw(), ( TEST_LEN as usize + n + 1, TEST_LEN as usize, @@ -289,9 +283,7 @@ mod tests { ); } assert_eq!( - env::test::get_contract_storage_rw::( - &contract_account_id - )?, + get_contract_storage_rw(), ( TEST_LEN as usize + LOAD_REPEATS, TEST_LEN as usize, @@ -303,9 +295,7 @@ mod tests { for n in 0..STORE_REPEATS { chunk.store(0, &10); assert_eq!( - env::test::get_contract_storage_rw::( - &contract_account_id - )?, + get_contract_storage_rw(), ( TEST_LEN as usize + LOAD_REPEATS, TEST_LEN as usize + n + 1, @@ -313,9 +303,7 @@ mod tests { ); } assert_eq!( - env::test::get_contract_storage_rw::( - &contract_account_id - )?, + get_contract_storage_rw(), ( TEST_LEN as usize + LOAD_REPEATS, TEST_LEN as usize + STORE_REPEATS, From 4d9437346c7618312705281135cada79f2fd7156 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 10:13:45 +0100 Subject: [PATCH 086/112] [core] transition tests for SynChunk to env3 --- core/src/storage/chunk/sync_chunk/tests.rs | 335 ++++++++++----------- 1 file changed, 157 insertions(+), 178 deletions(-) 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(()) + }) } From 6f579667e84e4ce4e50bfecedb41648e140dbdac Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 10:20:33 +0100 Subject: [PATCH 087/112] [core] transition storage::Stash tests to env3 --- core/src/storage/collections/stash/tests.rs | 58 +++++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/core/src/storage/collections/stash/tests.rs b/core/src/storage/collections/stash/tests.rs index 0b054ef8995..e9a6fb03b09 100644 --- a/core/src/storage/collections/stash/tests.rs +++ b/core/src/storage/collections/stash/tests.rs @@ -22,7 +22,8 @@ use crate::{ Key, Stash, }, - test_utils::run_test, + env3 as env, + env3::Result, }; 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(()) }) } From 9e13ea243a46ad79b0c120fd8a8c6cf9fb262fd6 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 10:27:34 +0100 Subject: [PATCH 088/112] [core] transition storage::HashMap tests to env3 --- .../src/storage/collections/hash_map/tests.rs | 53 ++++++++++++------- 1 file changed, 33 insertions(+), 20 deletions(-) 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(()) }) } From 373b2b67f54695e3dc6b282b8f8e0ffe658b9f65 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 10:27:50 +0100 Subject: [PATCH 089/112] [core] transition storage::BinaryHeap tests to env3 --- .../storage/collections/binary_heap/tests.rs | 78 +++++++++++-------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/core/src/storage/collections/binary_heap/tests.rs b/core/src/storage/collections/binary_heap/tests.rs index b2e290e7d38..830d6b8d993 100644 --- a/core/src/storage/collections/binary_heap/tests.rs +++ b/core/src/storage/collections/binary_heap/tests.rs @@ -33,7 +33,8 @@ use crate::{ BinaryHeap, Key, }, - test_utils::run_test, + env3 as env, + env3::Result, }; 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(()) }) } From 7b35691ecdc84cc4368f458c65984f8bd67529a6 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 10:42:37 +0100 Subject: [PATCH 090/112] [lang] remove the entire ink! lang crate It has been deprecated for a whole while. --- Cargo.toml | 1 - lang/Cargo.toml | 55 --- lang/LICENSE | 1 - lang/README.md | 1 - lang/src/ast.rs | 397 ----------------- lang/src/contract.rs | 40 -- lang/src/error.rs | 29 -- lang/src/gen/abi.rs | 303 ------------- lang/src/gen/as_dependency.rs | 268 ------------ lang/src/gen/build.rs | 518 ---------------------- lang/src/gen/doc.rs | 25 -- lang/src/gen/mod.rs | 54 --- lang/src/gen/test.rs | 207 --------- lang/src/hir.rs | 578 ------------------------- lang/src/ident_ext.rs | 28 -- lang/src/lib.rs | 44 -- lang/src/old_abi.rs | 794 ---------------------------------- lang/src/parser.rs | 364 ---------------- lang/src/tests/events.rs | 514 ---------------------- lang/src/tests/flipper.rs | 362 ---------------- lang/src/tests/incrementer.rs | 461 -------------------- lang/src/tests/mod.rs | 311 ------------- lang/src/tests/noop.rs | 255 ----------- lang/src/tests/utils.rs | 35 -- 24 files changed, 5645 deletions(-) delete mode 100644 lang/Cargo.toml delete mode 120000 lang/LICENSE delete mode 120000 lang/README.md delete mode 100644 lang/src/ast.rs delete mode 100644 lang/src/contract.rs delete mode 100644 lang/src/error.rs delete mode 100644 lang/src/gen/abi.rs delete mode 100644 lang/src/gen/as_dependency.rs delete mode 100644 lang/src/gen/build.rs delete mode 100644 lang/src/gen/doc.rs delete mode 100644 lang/src/gen/mod.rs delete mode 100644 lang/src/gen/test.rs delete mode 100644 lang/src/hir.rs delete mode 100644 lang/src/ident_ext.rs delete mode 100644 lang/src/lib.rs delete mode 100644 lang/src/old_abi.rs delete mode 100644 lang/src/parser.rs delete mode 100644 lang/src/tests/events.rs delete mode 100644 lang/src/tests/flipper.rs delete mode 100644 lang/src/tests/incrementer.rs delete mode 100644 lang/src/tests/mod.rs delete mode 100644 lang/src/tests/noop.rs delete mode 100644 lang/src/tests/utils.rs diff --git a/Cargo.toml b/Cargo.toml index a00d3feb174..d25ac396af0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ members = [ "abi", "core", - "lang", "lang2", "model", "prelude", 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()) - ) -} From 979031b600e747337a553c6484c1afd168769edb Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 10:43:03 +0100 Subject: [PATCH 091/112] [model] remove ink! model crate entirely It has been deprecated for a whole while. --- Cargo.toml | 1 - model/Cargo.toml | 31 --- model/README.md | 8 - model/src/contract.rs | 536 ------------------------------------- model/src/exec_env.rs | 223 --------------- model/src/lib.rs | 79 ------ model/src/msg.rs | 106 -------- model/src/msg_handler.rs | 358 ------------------------- model/src/state.rs | 115 -------- model/tests/incrementer.rs | 68 ----- model/tests/mod.rs | 15 -- model/tests/noop.rs | 58 ---- 12 files changed, 1598 deletions(-) delete mode 100644 model/Cargo.toml delete mode 100644 model/README.md delete mode 100644 model/src/contract.rs delete mode 100644 model/src/exec_env.rs delete mode 100644 model/src/lib.rs delete mode 100644 model/src/msg.rs delete mode 100644 model/src/msg_handler.rs delete mode 100644 model/src/state.rs delete mode 100644 model/tests/incrementer.rs delete mode 100644 model/tests/mod.rs delete mode 100644 model/tests/noop.rs diff --git a/Cargo.toml b/Cargo.toml index d25ac396af0..5781351fc51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,6 @@ members = [ "abi", "core", "lang2", - "model", "prelude", "utils", ] 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() -} From f2da1f823d4a6e00fdac3a667b8635cd264f22e5 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 10:46:08 +0100 Subject: [PATCH 092/112] [core] remove the old env1 sub-module of ink_core It has been deprecated for a whole while. --- core/src/env/api.rs | 138 ----- core/src/env/calls.rs | 263 --------- core/src/env/mod.rs | 79 --- core/src/env/srml/mod.rs | 27 - core/src/env/srml/srml_only/impls.rs | 248 -------- core/src/env/srml/srml_only/mod.rs | 21 - core/src/env/srml/srml_only/sys.rs | 124 ---- core/src/env/srml/types.rs | 120 ---- core/src/env/test.rs | 56 -- core/src/env/test_env.rs | 812 --------------------------- core/src/env/traits.rs | 179 ------ core/src/lib.rs | 1 - 12 files changed, 2068 deletions(-) delete mode 100644 core/src/env/api.rs delete mode 100644 core/src/env/calls.rs delete mode 100644 core/src/env/mod.rs delete mode 100644 core/src/env/srml/mod.rs delete mode 100644 core/src/env/srml/srml_only/impls.rs delete mode 100644 core/src/env/srml/srml_only/mod.rs delete mode 100644 core/src/env/srml/srml_only/sys.rs delete mode 100644 core/src/env/srml/types.rs delete mode 100644 core/src/env/test.rs delete mode 100644 core/src/env/test_env.rs delete mode 100644 core/src/env/traits.rs 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/lib.rs b/core/src/lib.rs index 7d60a9fbae1..a4283cd346c 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -50,7 +50,6 @@ extern crate ink_alloc; mod test_utils; mod byte_utils; -pub mod env; pub mod env2; pub mod env3; pub mod storage; From eacff613724de20462b7d21402af30e074e29e47 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 11:08:04 +0100 Subject: [PATCH 093/112] [examples] remove model examples --- examples/model/erc20/.cargo/config | 4 -- examples/model/erc20/Cargo.toml | 33 ----------- examples/model/erc20/src/lib.rs | 94 ------------------------------ 3 files changed, 131 deletions(-) delete mode 100644 examples/model/erc20/.cargo/config delete mode 100644 examples/model/erc20/Cargo.toml delete mode 100644 examples/model/erc20/src/lib.rs 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() -} From 6f036dd31b426e8c1c882aa9bd2ec2a657a39216 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 11:08:54 +0100 Subject: [PATCH 094/112] [examples] remove lang examples --- examples/lang/delegator/.cargo/config | 4 - examples/lang/delegator/.gitignore | 9 - .../lang/delegator/.ink/abi_gen/Cargo.toml | 15 - examples/lang/delegator/.ink/abi_gen/main.rs | 7 - examples/lang/delegator/Cargo.toml | 83 --- examples/lang/delegator/README.md | 29 - .../lang/delegator/accumulator/Cargo.toml | 51 -- .../lang/delegator/accumulator/src/lib.rs | 48 -- examples/lang/delegator/adder/Cargo.toml | 56 -- examples/lang/delegator/adder/src/lib.rs | 42 -- examples/lang/delegator/build-all.sh | 6 - examples/lang/delegator/src/lib.rs | 104 ---- examples/lang/delegator/subber/Cargo.toml | 53 -- examples/lang/delegator/subber/src/lib.rs | 41 -- examples/lang/erc20/.cargo/config | 4 - examples/lang/erc20/.ink/abi_gen/Cargo.toml | 15 - examples/lang/erc20/.ink/abi_gen/main.rs | 7 - examples/lang/erc20/Cargo.toml | 64 --- examples/lang/erc20/src/lib.rs | 237 -------- examples/lang/erc721/.cargo/config | 4 - examples/lang/erc721/.ink/abi_gen/Cargo.toml | 15 - examples/lang/erc721/.ink/abi_gen/main.rs | 7 - examples/lang/erc721/Cargo.toml | 64 --- examples/lang/erc721/src/lib.rs | 526 ------------------ examples/lang/events/.cargo/config | 4 - examples/lang/events/.gitignore | 9 - examples/lang/events/.ink/abi_gen/Cargo.toml | 15 - examples/lang/events/.ink/abi_gen/main.rs | 8 - examples/lang/events/Cargo.toml | 62 --- examples/lang/events/src/lib.rs | 86 --- examples/lang/flipper/.cargo/config | 4 - examples/lang/flipper/.gitignore | 9 - examples/lang/flipper/.ink/abi_gen/Cargo.toml | 15 - examples/lang/flipper/.ink/abi_gen/main.rs | 8 - examples/lang/flipper/Cargo.toml | 64 --- examples/lang/flipper/src/lib.rs | 67 --- examples/lang/incrementer/.cargo/config | 4 - examples/lang/incrementer/.gitignore | 9 - .../lang/incrementer/.ink/abi_gen/Cargo.toml | 15 - .../lang/incrementer/.ink/abi_gen/main.rs | 8 - examples/lang/incrementer/Cargo.toml | 65 --- examples/lang/incrementer/src/lib.rs | 72 --- examples/lang/shared_vec/.cargo/config | 4 - examples/lang/shared_vec/.gitignore | 9 - .../lang/shared_vec/.ink/abi_gen/Cargo.toml | 15 - examples/lang/shared_vec/.ink/abi_gen/main.rs | 8 - examples/lang/shared_vec/Cargo.toml | 62 --- examples/lang/shared_vec/src/lib.rs | 257 --------- 48 files changed, 2370 deletions(-) delete mode 100644 examples/lang/delegator/.cargo/config delete mode 100644 examples/lang/delegator/.gitignore delete mode 100644 examples/lang/delegator/.ink/abi_gen/Cargo.toml delete mode 100644 examples/lang/delegator/.ink/abi_gen/main.rs delete mode 100644 examples/lang/delegator/Cargo.toml delete mode 100644 examples/lang/delegator/README.md delete mode 100644 examples/lang/delegator/accumulator/Cargo.toml delete mode 100644 examples/lang/delegator/accumulator/src/lib.rs delete mode 100644 examples/lang/delegator/adder/Cargo.toml delete mode 100644 examples/lang/delegator/adder/src/lib.rs delete mode 100755 examples/lang/delegator/build-all.sh delete mode 100644 examples/lang/delegator/src/lib.rs delete mode 100644 examples/lang/delegator/subber/Cargo.toml delete mode 100644 examples/lang/delegator/subber/src/lib.rs delete mode 100644 examples/lang/erc20/.cargo/config delete mode 100644 examples/lang/erc20/.ink/abi_gen/Cargo.toml delete mode 100644 examples/lang/erc20/.ink/abi_gen/main.rs delete mode 100644 examples/lang/erc20/Cargo.toml delete mode 100644 examples/lang/erc20/src/lib.rs delete mode 100644 examples/lang/erc721/.cargo/config delete mode 100644 examples/lang/erc721/.ink/abi_gen/Cargo.toml delete mode 100644 examples/lang/erc721/.ink/abi_gen/main.rs delete mode 100644 examples/lang/erc721/Cargo.toml delete mode 100644 examples/lang/erc721/src/lib.rs delete mode 100644 examples/lang/events/.cargo/config delete mode 100644 examples/lang/events/.gitignore delete mode 100644 examples/lang/events/.ink/abi_gen/Cargo.toml delete mode 100644 examples/lang/events/.ink/abi_gen/main.rs delete mode 100644 examples/lang/events/Cargo.toml delete mode 100644 examples/lang/events/src/lib.rs delete mode 100644 examples/lang/flipper/.cargo/config delete mode 100644 examples/lang/flipper/.gitignore delete mode 100644 examples/lang/flipper/.ink/abi_gen/Cargo.toml delete mode 100644 examples/lang/flipper/.ink/abi_gen/main.rs delete mode 100644 examples/lang/flipper/Cargo.toml delete mode 100644 examples/lang/flipper/src/lib.rs delete mode 100644 examples/lang/incrementer/.cargo/config delete mode 100644 examples/lang/incrementer/.gitignore delete mode 100644 examples/lang/incrementer/.ink/abi_gen/Cargo.toml delete mode 100644 examples/lang/incrementer/.ink/abi_gen/main.rs delete mode 100644 examples/lang/incrementer/Cargo.toml delete mode 100644 examples/lang/incrementer/src/lib.rs delete mode 100644 examples/lang/shared_vec/.cargo/config delete mode 100644 examples/lang/shared_vec/.gitignore delete mode 100644 examples/lang/shared_vec/.ink/abi_gen/Cargo.toml delete mode 100644 examples/lang/shared_vec/.ink/abi_gen/main.rs delete mode 100644 examples/lang/shared_vec/Cargo.toml delete mode 100644 examples/lang/shared_vec/src/lib.rs 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)); - } -} From e1b0a8c470bea74ae86d5903baaee5b87ec8919a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 29 Jan 2020 11:10:24 +0100 Subject: [PATCH 095/112] [examples] remove core examples --- examples/core/erc20/.cargo/config | 4 - examples/core/erc20/Cargo.toml | 30 ---- examples/core/erc20/src/lib.rs | 243 ------------------------------ 3 files changed, 277 deletions(-) delete mode 100644 examples/core/erc20/.cargo/config delete mode 100644 examples/core/erc20/Cargo.toml delete mode 100644 examples/core/erc20/src/lib.rs 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); - } - } -} From 13a7d86a5d09202958226159f4ed9c23d7612511 Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Thu, 30 Jan 2020 20:59:34 +0100 Subject: [PATCH 096/112] remove "If given too few endowment" from docs Co-Authored-By: Andrew Jones --- core/src/env3/api.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index 13536868f0a..5040695d4d2 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -264,7 +264,6 @@ where /// - 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(params: &CallParams) -> Result<()> where T: EnvTypes, From 33961eb5c24bb0b48af3225e4f1a9b62d1ae7023 Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Thu, 30 Jan 2020 21:00:01 +0100 Subject: [PATCH 097/112] fix doc comment Co-Authored-By: Andrew Jones --- core/src/env3/api.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index 5040695d4d2..5e5bc8c4fe1 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -282,7 +282,6 @@ where /// - 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(params: &CallParams>) -> Result where From c600f430bc39239441da3561d51d57878f3f044c Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Thu, 30 Jan 2020 21:00:21 +0100 Subject: [PATCH 098/112] fix doc comment Co-Authored-By: Andrew Jones --- core/src/env3/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index 5e5bc8c4fe1..5704ddf5508 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -322,7 +322,7 @@ where /// 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. +/// storage and the storage of the restorer contract are equal. /// /// # Usage /// From 61c5fdd9017aab755aeafa82e2b7edacbd6df6c4 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 30 Jan 2020 21:37:54 +0100 Subject: [PATCH 099/112] [lang2] initial steps towards basing on env3 instead of env2 With this commit `cargo contract build` already works. However, cargo test etc. are still broken. --- lang2/macro/src/codegen/contract.rs | 4 +- lang2/macro/src/codegen/dispatch.rs | 12 +- lang2/macro/src/codegen/env_types.rs | 12 +- lang2/macro/src/codegen/storage.rs | 154 +----------- lang2/macro/src/codegen/testable.rs | 2 +- lang2/macro/src/ir/data.rs | 2 +- lang2/macro/src/ir/params.rs | 10 +- lang2/macro/src/ir/tests.rs | 2 +- lang2/src/contract.rs | 15 +- lang2/src/dispatcher.rs | 48 ++-- lang2/src/env_access.rs | 341 +++++++++++++++++++++++++++ lang2/src/error.rs | 12 +- lang2/src/lib.rs | 5 + lang2/src/traits.rs | 4 +- 14 files changed, 411 insertions(+), 212 deletions(-) create mode 100644 lang2/src/env_access.rs diff --git a/lang2/macro/src/codegen/contract.rs b/lang2/macro/src/codegen/contract.rs index ab2daf18cc2..654d33ee42e 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. @@ -93,7 +91,7 @@ impl GenerateCode for ContractModule<'_> { #[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/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..c31ede8c566 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; } } } 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..7d3f12c823c 100644 --- a/lang2/macro/src/codegen/testable.rs +++ b/lang2/macro/src/codegen/testable.rs @@ -65,7 +65,7 @@ impl GenerateCode for TestWrapper<'_> { &mut alloc, ) }; - ink_core::env2::test::TestEnv::::try_initialize() + ink_core::env2::test::TestEnv::::try_initialize() .expect("encountered already initialized test environment"); ink_core::storage::alloc::Initialize::try_default_initialize( &mut contract, diff --git a/lang2/macro/src/ir/data.rs b/lang2/macro/src/ir/data.rs index 0fba84102e6..7215cd2e115 100644 --- a/lang2/macro/src/ir/data.rs +++ b/lang2/macro/src/ir/data.rs @@ -88,7 +88,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/src/contract.rs b/lang2/src/contract.rs index 14913fc6fdb..0f15a9319cc 100644 --- a/lang2/src/contract.rs +++ b/lang2/src/contract.rs @@ -16,9 +16,8 @@ use core::{ marker::PhantomData, mem::ManuallyDrop, }; - +use ink_core::env3::EnvTypes; use crate::{ - AccessEnv, Dispatch, DispatchError, DispatchList, @@ -198,25 +197,25 @@ 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/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..b1700a726b1 --- /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, + CreateParams, + 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: &CreateParams) -> 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..ce76d383829 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, diff --git a/lang2/src/traits.rs b/lang2/src/traits.rs index 54d7882ba7b..faaa10a912b 100644 --- a/lang2/src/traits.rs +++ b/lang2/src/traits.rs @@ -13,9 +13,8 @@ // limitations under the License. use ink_core::{ - env2::{ + env3::{ call::Selector, - EnvAccess, }, storage::{ alloc::{ @@ -25,6 +24,7 @@ use ink_core::{ Flush, }, }; +use crate::EnvAccess; /// Dispatchable functions that have inputs. pub trait FnInput { From e20c38f66c883c01fd4d26a37a95e59593549ace Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 31 Jan 2020 17:53:12 +0100 Subject: [PATCH 100/112] [lang2] make macros work for `cargo test` --- lang2/macro/src/codegen/contract.rs | 2 +- lang2/macro/src/codegen/testable.rs | 32 ++++++++++++++--------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lang2/macro/src/codegen/contract.rs b/lang2/macro/src/codegen/contract.rs index 654d33ee42e..4972e657397 100644 --- a/lang2/macro/src/codegen/contract.rs +++ b/lang2/macro/src/codegen/contract.rs @@ -87,7 +87,7 @@ 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 diff --git a/lang2/macro/src/codegen/testable.rs b/lang2/macro/src/codegen/testable.rs index 7d3f12c823c..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 } From 32851e8e147de8ab6b2cf0eeb3e33b5e698e6244 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 31 Jan 2020 18:13:26 +0100 Subject: [PATCH 101/112] [core, lang2] make contracts compile as ink-as-dependency --- core/src/env3/call/create.rs | 2 +- lang2/macro/src/codegen/cross_calling.rs | 26 ++++++++++++------------ lang2/src/cross_calling.rs | 8 ++++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/core/src/env3/call/create.rs b/core/src/env3/call/create.rs index 9ac46451029..cf5b519598f 100644 --- a/core/src/env3/call/create.rs +++ b/core/src/env3/call/create.rs @@ -24,7 +24,7 @@ use crate::env3::{ }; pub mod state { - pub use crate::env2::call::state::{ + pub use crate::env3::call::state::{ Sealed, Unsealed, }; diff --git a/lang2/macro/src/codegen/cross_calling.rs b/lang2/macro/src/codegen/cross_calling.rs index 8de969884dd..9caecf4b681 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 @@ -215,14 +215,14 @@ impl CrossCalling<'_> { #( #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 +329,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 +349,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/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; } From d56509498461daa53709a0f92aa1b9ce9ba2824c Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 31 Jan 2020 20:18:21 +0100 Subject: [PATCH 102/112] [lang2] make events work for ink_lang2 on env3 --- lang2/macro/src/codegen/env_types.rs | 4 ++-- lang2/macro/src/codegen/events.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lang2/macro/src/codegen/env_types.rs b/lang2/macro/src/codegen/env_types.rs index c31ede8c566..1f459341bb2 100644 --- a/lang2/macro/src/codegen/env_types.rs +++ b/lang2/macro/src/codegen/env_types.rs @@ -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] { &[] } From fd3a2cc24856cb38d78b14064dfd8c0a81003f2b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 31 Jan 2020 20:21:25 +0100 Subject: [PATCH 103/112] [lang2] remove AccessEnv - no longer needed --- lang2/src/lib.rs | 1 - lang2/src/traits.rs | 12 ------------ 2 files changed, 13 deletions(-) diff --git a/lang2/src/lib.rs b/lang2/src/lib.rs index ce76d383829..5f32e402da1 100644 --- a/lang2/src/lib.rs +++ b/lang2/src/lib.rs @@ -68,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 faaa10a912b..c55172718ec 100644 --- a/lang2/src/traits.rs +++ b/lang2/src/traits.rs @@ -49,17 +49,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 {} From de1b9800571d2f7c4a6f41d4dabeb1dfa4633677 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 31 Jan 2020 20:45:58 +0100 Subject: [PATCH 104/112] [core] remove env2 --- core/src/env2/buffer_arena.rs | 370 ------------------ core/src/env2/call/builder.rs | 227 ----------- core/src/env2/call/create.rs | 223 ----------- core/src/env2/call/mod.rs | 47 --- core/src/env2/call/utils.rs | 145 ------- core/src/env2/dyn_env.rs | 143 ------- core/src/env2/env_access/immutable.rs | 333 ---------------- core/src/env2/env_access/mod.rs | 34 -- core/src/env2/env_access/mutable.rs | 334 ---------------- core/src/env2/error.rs | 51 --- core/src/env2/mod.rs | 118 ------ core/src/env2/property.rs | 147 ------- core/src/env2/srml/ext.rs | 240 ------------ core/src/env2/srml/impls.rs | 433 --------------------- core/src/env2/srml/mod.rs | 22 -- core/src/env2/srml/ret_code.rs | 38 -- core/src/env2/test/accessor.rs | 528 -------------------------- core/src/env2/test/account.rs | 198 ---------- core/src/env2/test/instance.rs | 259 ------------- core/src/env2/test/mod.rs | 47 --- core/src/env2/test/record.rs | 237 ------------ core/src/env2/test/storage.rs | 188 --------- core/src/env2/test/typed_encoded.rs | 243 ------------ core/src/env2/test/types.rs | 58 --- core/src/env2/traits.rs | 207 ---------- core/src/env2/types.rs | 138 ------- core/src/env2/utils.rs | 79 ---- core/src/lib.rs | 1 - 28 files changed, 5088 deletions(-) delete mode 100644 core/src/env2/buffer_arena.rs delete mode 100644 core/src/env2/call/builder.rs delete mode 100644 core/src/env2/call/create.rs delete mode 100644 core/src/env2/call/mod.rs delete mode 100644 core/src/env2/call/utils.rs delete mode 100644 core/src/env2/dyn_env.rs delete mode 100644 core/src/env2/env_access/immutable.rs delete mode 100644 core/src/env2/env_access/mod.rs delete mode 100644 core/src/env2/env_access/mutable.rs delete mode 100644 core/src/env2/error.rs delete mode 100644 core/src/env2/mod.rs delete mode 100644 core/src/env2/property.rs delete mode 100644 core/src/env2/srml/ext.rs delete mode 100644 core/src/env2/srml/impls.rs delete mode 100644 core/src/env2/srml/mod.rs delete mode 100644 core/src/env2/srml/ret_code.rs delete mode 100644 core/src/env2/test/accessor.rs delete mode 100644 core/src/env2/test/account.rs delete mode 100644 core/src/env2/test/instance.rs delete mode 100644 core/src/env2/test/mod.rs delete mode 100644 core/src/env2/test/record.rs delete mode 100644 core/src/env2/test/storage.rs delete mode 100644 core/src/env2/test/typed_encoded.rs delete mode 100644 core/src/env2/test/types.rs delete mode 100644 core/src/env2/traits.rs delete mode 100644 core/src/env2/types.rs delete mode 100644 core/src/env2/utils.rs 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/call/builder.rs b/core/src/env2/call/builder.rs deleted file mode 100644 index f4ba0453f50..00000000000 --- a/core/src/env2/call/builder.rs +++ /dev/null @@ -1,227 +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::{ - state, - CallData, - Selector, - }, - errors::CallError, - Env, - EnvAccessMut, - EnvTypes, -}; -use ink_prelude::vec::Vec; - -/// 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 T>); - -/// The final parameters to the cross-contract call. -pub struct CallParams -where - E: EnvTypes, -{ - /// The account ID of the to-be-called smart contract. - callee: 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 data respecting the ABI. - call_data: CallData, -} - -/// Builds up a call. -pub struct CallBuilder -where - E: EnvTypes, -{ - /// The current parameters that have been built up so far. - params: CallParams, - /// Seal state. - seal: PhantomData, -} - -impl CallParams -where - E: EnvTypes, -{ - /// The code hash of the contract. - pub fn callee(&self) -> &E::AccountId { - &self.callee - } - - /// The gas limit for the contract instantiation. - pub fn gas_limit(&self) -> u64 { - self.gas_limit - } - /// The endowment for the instantiated contract. - pub fn endowment(&self) -> &E::Balance { - &self.value - } - - /// The raw encoded input data. - pub fn input_data(&self) -> &CallData { - &self.call_data - } -} - -impl CallParams -where - E: EnvTypes, - E::Balance: Default, -{ - /// Creates the default set of parameters for the cross-contract call. - fn new(callee: E::AccountId, selector: Selector) -> Self { - Self { - callee, - gas_limit: 0, - value: E::Balance::default(), - return_type: PhantomData, - call_data: CallData::new(selector), - } - } - - /// Returns a builder for a cross-contract call that might return data. - pub fn eval( - callee: E::AccountId, - selector: Selector, - ) -> CallBuilder, state::Unsealed> { - CallBuilder { - params: CallParams::new(callee, selector), - seal: Default::default(), - } - } - - /// Returns a builder for a cross-contract call that cannot return data. - /// - /// Prefer this over [`CallParams::eval`] if possible since it is the more efficient operation. - pub fn invoke( - callee: E::AccountId, - selector: Selector, - ) -> CallBuilder { - CallBuilder { - params: CallParams::new(callee, selector), - seal: Default::default(), - } - } -} - -impl CallBuilder -where - E: EnvTypes, -{ - /// Sets the maximumly 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; - self - } -} - -impl CallBuilder -where - E: EnvTypes, -{ - /// Pushes an argument to the inputs of the call. - pub fn push_arg(mut self, arg: &A) -> Self - where - A: scale::Encode, - { - self.params.call_data.push_arg(arg); - self - } - - /// Seals the call builder to prevent further arguments. - pub fn seal(self) -> CallBuilder { - CallBuilder { - params: self.params, - seal: Default::default(), - } - } -} - -impl CallBuilder, Seal> -where - E: Env, - 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 - where - R: scale::Decode, - { - env.eval_contract(&self.params).map_err(|_| CallError) - } -} - -impl CallBuilder -where - E: Env, -{ - /// 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) - } -} diff --git a/core/src/env2/call/create.rs b/core/src/env2/call/create.rs deleted file mode 100644 index 4857c1a5889..00000000000 --- a/core/src/env2/call/create.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 crate::env2::{ - call::{ - CallData, - Selector, - }, - errors::CreateError, - Env, - EnvAccessMut, - EnvTypes, -}; -use ink_prelude::vec::Vec; - -pub mod state { - pub use crate::env2::call::state::{ - Sealed, - Unsealed, - }; - - /// Type state to indicate that the `code_hash` for cross-contract - /// instantiation has already been provided. - pub enum CodeHashAssigned {} - /// Type state to indicate that the `code_hash` for cross-contract - /// instantitation has not yet been provided. - pub enum CodeHashUnassigned {} -} - -/// Contracts that can be contructed from an `AccountId` -/// -/// # Note -/// -/// This is needed because of conflicting implementations of `From for T` -/// in the generated code of `ink_lang`. -pub trait FromAccountId -where - E: EnvTypes, -{ - /// Creates the contract instance from the account ID of the already instantiated contract. - fn from_account_id(account_id: ::AccountId) -> Self; -} - -/// Builds up contract instantiations. -pub struct CreateParams -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. - call_data: CallData, - /// The type of the instantiated contract. - contract_marker: PhantomData C>, -} - -/// Builds up contract instantiations. -pub struct CreateBuilder -where - E: EnvTypes, -{ - /// The parameters of the cross-contract instantiation. - params: CreateParams, - /// Seal state. - state: PhantomData (Seal, CodeHash)>, -} - -impl CreateParams -where - E: EnvTypes, -{ - /// The code hash of the contract. - pub fn code_hash(&self) -> &E::Hash { - &self.code_hash - } - - /// The gas limit for the contract instantiation. - pub fn gas_limit(&self) -> u64 { - self.gas_limit - } - /// The endowment for the instantiated contract. - pub fn endowment(&self) -> &E::Balance { - &self.value - } - - /// The raw encoded input data. - pub fn input_data(&self) -> &CallData { - &self.call_data - } -} - -impl CreateParams -where - E: EnvTypes, - E::Hash: Default, - E::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(), - call_data: CallData::new(selector), - contract_marker: Default::default(), - } - } - - /// Creates a new create builder without setting any presets. - pub fn build( - selector: Selector, - ) -> CreateBuilder { - CreateBuilder { - params: CreateParams::new(selector), - state: Default::default(), - } - } -} - -impl CreateBuilder -where - E: EnvTypes, -{ - /// Sets the maximumly 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; - self - } -} - -impl CreateBuilder -where - E: EnvTypes, -{ - /// Using the given code hash. - pub fn using_code( - mut self, - code_hash: E::Hash, - ) -> CreateBuilder { - self.params.code_hash = code_hash; - CreateBuilder { - params: self.params, - state: Default::default(), - } - } -} - -impl CreateBuilder -where - E: EnvTypes, -{ - /// Pushes an argument to the inputs of the call. - pub fn push_arg(mut self, arg: &A) -> Self - where - A: scale::Encode, - { - self.params.call_data.push_arg(arg); - self - } - - /// Seals the create builder to prevent further arguments. - pub fn seal(self) -> CreateBuilder { - CreateBuilder { - params: self.params, - state: Default::default(), - } - } -} - -impl CreateBuilder -where - E: Env, - 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) - } -} diff --git a/core/src/env2/call/mod.rs b/core/src/env2/call/mod.rs deleted file mode 100644 index 3f23749ce0c..00000000000 --- a/core/src/env2/call/mod.rs +++ /dev/null @@ -1,47 +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 builder; -mod create; -mod utils; - -pub mod state { - pub use crate::env2::call::{ - create::state::{ - CodeHashAssigned, - CodeHashUnassigned, - }, - utils::seal::{ - Sealed, - Unsealed, - }, - }; -} - -pub use self::{ - builder::{ - CallBuilder, - CallParams, - ReturnType, - }, - create::{ - CreateBuilder, - CreateParams, - FromAccountId, - }, - utils::{ - CallData, - Selector, - }, -}; diff --git a/core/src/env2/call/utils.rs b/core/src/env2/call/utils.rs deleted file mode 100644 index edcf8344b90..00000000000 --- a/core/src/env2/call/utils.rs +++ /dev/null @@ -1,145 +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; -use ink_prelude::{ - vec, - vec::Vec, -}; - -/// Seals to guard pushing arguments to already satisfied parameter builders. -pub mod seal { - /// The call builder is sealed and won't accept further arguments. - pub enum Sealed {} - /// The call builder is unsealed and will accept further arguments. - pub enum Unsealed {} -} - -/// The function selector. -#[derive(Debug, Copy, Clone, PartialEq, Eq, From, scale::Decode, scale::Encode)] -pub struct Selector { - /// The 4 underlying bytes. - bytes: [u8; 4], -} - -impl<'a> From<&'a [u8]> for Selector { - /// Computes the selector from the given input bytes. - /// - /// # Note - /// - /// Normally this is invoked through `Selector::from_str`. - fn from(input: &'a [u8]) -> Self { - let keccak = ink_utils::hash::keccak256(input); - Self { - bytes: [keccak[0], keccak[1], keccak[2], keccak[3]], - } - } -} - -impl Selector { - /// Returns the selector for the given name. - #[allow(clippy::should_implement_trait)] - pub fn from_str(name: &str) -> Self { - From::from(name.as_bytes()) - } - - /// Creates a selector directly from 4 bytes. - pub const fn from_bytes(bytes: [u8; 4]) -> Self { - Self { bytes } - } - - /// Returns the underlying bytes of the selector. - pub const fn to_bytes(self) -> [u8; 4] { - self.bytes - } -} - -/// The raw ABI respecting input data to a call. -/// -/// # Note -/// -/// The first four bytes are the function selector and the rest are SCALE encoded inputs. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CallData { - /// Already encoded function selector and inputs. - /// - /// # Note - /// - /// Has the invariant of always holding at least 4 bytes (the selector). - bytes: Vec, -} - -impl CallData { - /// Creates new call ABI data for the given selector. - pub fn new(selector: Selector) -> Self { - let bytes = selector.to_bytes(); - Self { - bytes: vec![bytes[0], bytes[1], bytes[2], bytes[3]], - } - } - - /// Pushes the given argument onto the call ABI data in encoded form. - pub fn push_arg(&mut self, arg: &A) - where - A: scale::Encode, - { - arg.encode_to(&mut self.bytes) - } - - /// Returns the selector of `self`. - pub fn selector(&self) -> Selector { - debug_assert!(self.bytes.len() >= 4); - let bytes = [self.bytes[0], self.bytes[1], self.bytes[2], self.bytes[3]]; - bytes.into() - } - - /// Returns the underlying bytes of the encoded input parameters. - pub fn params(&self) -> &[u8] { - debug_assert!(self.bytes.len() >= 4); - &self.bytes[4..] - } - - /// Returns the underlying byte representation. - pub fn to_bytes(&self) -> &[u8] { - &self.bytes - } -} - -impl scale::Encode for CallData { - fn size_hint(&self) -> usize { - self.bytes.len() - } - - fn encode_to(&self, dest: &mut T) { - dest.write(self.bytes.as_slice()); - } -} - -impl scale::Decode for CallData { - fn decode( - input: &mut I, - ) -> core::result::Result { - let remaining_len = input.remaining_len().unwrap_or(None).unwrap_or(0); - let mut bytes = Vec::with_capacity(remaining_len); - while let Ok(byte) = input.read_byte() { - bytes.push(byte); - } - if bytes.len() < 4 { - return Err(scale::Error::from( - "require at least 4 bytes for input data", - )) - } - Ok(Self { bytes }) - } -} diff --git a/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/ext.rs b/core/src/env2/srml/ext.rs deleted file mode 100644 index a6fc5b7727c..00000000000 --- a/core/src/env2/srml/ext.rs +++ /dev/null @@ -1,240 +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. - -use crate::{ - env2::srml::RetCode, - storage::Key, -}; - -pub fn create( - code_hash: &[u8], - gas_limit: u64, - value: &[u8], - create_data: &[u8], -) -> RetCode { - unsafe { - sys::ext_instantiate( - code_hash.as_ptr() as u32, - code_hash.len() as u32, - gas_limit, - value.as_ptr() as u32, - value.len() as u32, - create_data.as_ptr() as u32, - create_data.len() as u32, - ) - } - .into() -} - -pub fn call(callee: &[u8], gas_limit: u64, value: &[u8], call_data: &[u8]) -> RetCode { - unsafe { - sys::ext_call( - callee.as_ptr() as u32, - callee.len() as u32, - gas_limit, - value.as_ptr() as u32, - value.len() as u32, - call_data.as_ptr() as u32, - call_data.len() as u32, - ) - } - .into() -} - -pub fn deposit_event(topics: &[u8], 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, - ) - } -} - -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 get_storage(key: &[u8]) -> RetCode { - unsafe { sys::ext_get_storage(key.as_ptr() as u32) }.into() -} - -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 restore_to( - dest: &[u8], - code_hash: &[u8], - rent_allowance: &[u8], - filtered_keys: &[Key], -) { - unsafe { - sys::ext_restore_to( - dest.as_ptr() as u32, - dest.len() as u32, - code_hash.as_ptr() as u32, - code_hash.len() as u32, - rent_allowance.as_ptr() as u32, - rent_allowance.len() as u32, - filtered_keys.as_ptr() as u32, - filtered_keys.len() as u32, - ) - } -} - -pub fn dispatch_call(call: &[u8]) { - unsafe { sys::ext_dispatch_call(call.as_ptr() as u32, call.len() as u32) } -} - -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_write(src: &[u8]) -> RetCode { - unsafe { sys::ext_scratch_write(src.as_ptr() as u32, src.len() as u32) }; - RetCode::success() -} - -macro_rules! impl_ext_wrapper_for { - ( $( ($name:ident => $ext_name:ident), )* ) => { - $( - pub fn $name() { - unsafe { - sys::$ext_name() - } - } - )* - } -} -impl_ext_wrapper_for! { - (caller => ext_caller), - (block_number => ext_block_number), - (address => ext_address), - (balance => ext_balance), - (gas_price => ext_gas_price), - (gas_left => ext_gas_left), - (value_transferred => ext_value_transferred), - (now => ext_now), - (rent_allowance => ext_rent_allowance), - (minimum_balance => ext_minimum_balance), -} - -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 random_seed(subject: &[u8]) { - unsafe { sys::ext_random_seed(subject.as_ptr() as u32, subject.len() as u32) } -} - -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/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/mod.rs b/core/src/env2/test/mod.rs deleted file mode 100644 index fa8da23f650..00000000000 --- a/core/src/env2/test/mod.rs +++ /dev/null @@ -1,47 +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. - -//! Test environment for off-chain testing and utilities. - -mod accessor; -pub mod account; -mod instance; -pub mod record; -pub mod storage; -mod typed_encoded; -pub mod types; - -use self::{ - account::{ - Account, - AccountKind, - AccountsDb, - ContractAccount, - }, - instance::TestEnvInstance, - record::{ - CallContractRecord, - CreateContractRecord, - EmitEventRecord, - InvokeRuntimeRecord, - Record, - RestoreContractRecord, - }, - typed_encoded::{ - AlreadyInitialized, - TypedEncoded, - }, -}; - -pub use self::accessor::TestEnv; 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/test/types.rs b/core/src/env2/test/types.rs deleted file mode 100644 index 397b22a9ee8..00000000000 --- a/core/src/env2/test/types.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. - -//! Emulated environmental types of the test environment. -//! -//! Due to technical constraints it is not possible to define the test -//! environment instance thread-locally and generically over the actual -//! environmental types. -//! -//! For that reason we store the type information of all the environmental -//! types at runtime to at least have some kind of type safety upon access. -//! This is done via the `TypedEncoded` abstraction that stores the -//! SCALE encoded bytes and also has a runtime type information marker -//! assigned upon initialization to check whether accesses to it are -//! type safe. - -use crate::env2::test::TypedEncoded; - -/// Type markers used in conjunction with `TypedEncoded`. -#[rustfmt::skip] -mod type_marker { - /// Type marker representing an environmental `AccountId`. - #[derive(Debug)] pub enum AccountId {} - /// Type marker representing an environmental `Balance`. - #[derive(Debug)] 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 {} - /// Type marker representing an environmental `BlockNumber`. - #[derive(Debug)] pub enum BlockNumber {} - /// Type marker representing an environmental `Call`. - #[derive(Debug)] 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; 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/lib.rs b/core/src/lib.rs index a4283cd346c..13198cd8f48 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -50,7 +50,6 @@ extern crate ink_alloc; mod test_utils; mod byte_utils; -pub mod env2; pub mod env3; pub mod storage; From 3304912e6b05fbdacd49bcf4a148159c5cc6e602 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 31 Jan 2020 21:05:45 +0100 Subject: [PATCH 105/112] [lang2] fix tests - Fix warning in ink_lang2 - Remove no longer needed test case - Adjust some passing tests after default module import removal --- lang2/macro/tests/compile_tests.rs | 1 - .../ui/fail/06-access-generated-fields.rs | 23 ------------------- .../ui/fail/06-access-generated-fields.stderr | 11 --------- .../tests/ui/pass/02-flipper-contract.rs | 3 ++- .../tests/ui/pass/03-incrementer-contract.rs | 3 ++- .../macro/tests/ui/pass/04-erc20-contract.rs | 3 ++- .../macro/tests/ui/pass/05-erc721-contract.rs | 3 ++- .../tests/ui/pass/07-flipper-as-dependency.rs | 3 ++- lang2/src/traits.rs | 1 - 9 files changed, 10 insertions(+), 41 deletions(-) delete mode 100644 lang2/macro/tests/ui/fail/06-access-generated-fields.rs delete mode 100644 lang2/macro/tests/ui/fail/06-access-generated-fields.stderr 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/traits.rs b/lang2/src/traits.rs index c55172718ec..1572bf7d282 100644 --- a/lang2/src/traits.rs +++ b/lang2/src/traits.rs @@ -24,7 +24,6 @@ use ink_core::{ Flush, }, }; -use crate::EnvAccess; /// Dispatchable functions that have inputs. pub trait FnInput { From b0cc583ffbc1c0aa73ffb109b3816e8a82d2504e Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 31 Jan 2020 21:07:22 +0100 Subject: [PATCH 106/112] [*] apply rustfmt --- .../env3/engine/off_chain/db/chain_spec.rs | 12 ++++-- core/src/env3/engine/off_chain/test_api.rs | 4 +- core/src/storage/chunk/typed_chunk.rs | 38 +++---------------- .../storage/collections/binary_heap/tests.rs | 4 +- core/src/storage/collections/stash/tests.rs | 4 +- lang2/macro/src/codegen/cross_calling.rs | 4 +- lang2/src/contract.rs | 21 +++++----- lang2/src/traits.rs | 4 +- 8 files changed, 37 insertions(+), 54 deletions(-) diff --git a/core/src/env3/engine/off_chain/db/chain_spec.rs b/core/src/env3/engine/off_chain/db/chain_spec.rs index e113cb0ca46..0256d19c1ae 100644 --- a/core/src/env3/engine/off_chain/db/chain_spec.rs +++ b/core/src/env3/engine/off_chain/db/chain_spec.rs @@ -48,10 +48,14 @@ impl ChainSpec { 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))?; + 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(()) } diff --git a/core/src/env3/engine/off_chain/test_api.rs b/core/src/env3/engine/off_chain/test_api.rs index d9383107b28..3f7a98a6413 100644 --- a/core/src/env3/engine/off_chain/test_api.rs +++ b/core/src/env3/engine/off_chain/test_api.rs @@ -318,7 +318,9 @@ where T: EnvTypes, { ::on_instance(|instance| { - instance.accounts.get_account::(account_id) + 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/storage/chunk/typed_chunk.rs b/core/src/storage/chunk/typed_chunk.rs index a9dc278ba4e..04f15c7ec35 100644 --- a/core/src/storage/chunk/typed_chunk.rs +++ b/core/src/storage/chunk/typed_chunk.rs @@ -162,14 +162,6 @@ where /// If decoding of the loaded bytes fails. pub fn load(&self, index: u32) -> Option { self.cell_at(index).load() - // 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" - // ) - // }) } } @@ -239,23 +231,14 @@ mod tests { let mut chunk = create_typed_chunk(); // Reads and writes after init. - assert_eq!( - get_contract_storage_rw(), - (0, 0), - ); + assert_eq!(get_contract_storage_rw(), (0, 0),); // Loading from all cells. for i in 0..TEST_LEN { chunk.load(i); - assert_eq!( - get_contract_storage_rw(), - (i as usize + 1, 0) - ); + assert_eq!(get_contract_storage_rw(), (i as usize + 1, 0)); } - assert_eq!( - get_contract_storage_rw(), - (TEST_LEN as usize, 0) - ); + assert_eq!(get_contract_storage_rw(), (TEST_LEN as usize, 0)); // Writing to all cells. for i in 0..TEST_LEN { @@ -276,18 +259,12 @@ mod tests { chunk.load(0); assert_eq!( get_contract_storage_rw(), - ( - TEST_LEN as usize + n + 1, - TEST_LEN as usize, - ) + (TEST_LEN as usize + n + 1, TEST_LEN as usize,) ); } assert_eq!( get_contract_storage_rw(), - ( - TEST_LEN as usize + LOAD_REPEATS, - TEST_LEN as usize, - ) + (TEST_LEN as usize + LOAD_REPEATS, TEST_LEN as usize,) ); // Storing multiple times to a single cell. @@ -296,10 +273,7 @@ mod tests { chunk.store(0, &10); assert_eq!( get_contract_storage_rw(), - ( - TEST_LEN as usize + LOAD_REPEATS, - TEST_LEN as usize + n + 1, - ) + (TEST_LEN as usize + LOAD_REPEATS, TEST_LEN as usize + n + 1,) ); } assert_eq!( diff --git a/core/src/storage/collections/binary_heap/tests.rs b/core/src/storage/collections/binary_heap/tests.rs index 830d6b8d993..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,8 +35,6 @@ use crate::{ BinaryHeap, Key, }, - env3 as env, - env3::Result, }; fn empty_heap() -> BinaryHeap { diff --git a/core/src/storage/collections/stash/tests.rs b/core/src/storage/collections/stash/tests.rs index e9a6fb03b09..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,8 +24,6 @@ use crate::{ Key, Stash, }, - env3 as env, - env3::Result, }; fn empty_stash() -> Stash { diff --git a/lang2/macro/src/codegen/cross_calling.rs b/lang2/macro/src/codegen/cross_calling.rs index 9caecf4b681..50d26beb79c 100644 --- a/lang2/macro/src/codegen/cross_calling.rs +++ b/lang2/macro/src/codegen/cross_calling.rs @@ -208,7 +208,9 @@ 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=> diff --git a/lang2/src/contract.rs b/lang2/src/contract.rs index 0f15a9319cc..8fba2525f11 100644 --- a/lang2/src/contract.rs +++ b/lang2/src/contract.rs @@ -12,11 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use core::{ - marker::PhantomData, - mem::ManuallyDrop, -}; -use ink_core::env3::EnvTypes; use crate::{ Dispatch, DispatchError, @@ -30,6 +25,11 @@ use crate::{ FnOutput, PushDispatcher, }; +use core::{ + marker::PhantomData, + mem::ManuallyDrop, +}; +use ink_core::env3::EnvTypes; /// The contract definition. pub struct Contract { @@ -209,13 +209,16 @@ where self.storage.try_default_initialize(); } // Dispatch using the contract execution input. - let call_data = ink_core::env3::input() - .map_err(|_| DispatchError::CouldNotReadInput)?; + 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/traits.rs b/lang2/src/traits.rs index 1572bf7d282..3ab3ed41c9a 100644 --- a/lang2/src/traits.rs +++ b/lang2/src/traits.rs @@ -13,9 +13,7 @@ // limitations under the License. use ink_core::{ - env3::{ - call::Selector, - }, + env3::call::Selector, storage::{ alloc::{ AllocateUsing, From 6a21518cc78ba9054632a44cd581365feb586c65 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 31 Jan 2020 21:12:18 +0100 Subject: [PATCH 107/112] [lang2] silence unused warning for now --- lang2/macro/src/ir/data.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lang2/macro/src/ir/data.rs b/lang2/macro/src/ir/data.rs index 7215cd2e115..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 } From c22a01ee5fa2d28a42d0944ed3ef2a448a1d5956 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 31 Jan 2020 22:00:27 +0100 Subject: [PATCH 108/112] [core] rename {create|Create}* to {instantiate|Instantiate}* - CreateBuilder -> InstantiateBuilder - CreateParams -> InstantiateParams - create -> instantiate --- core/src/env3/api.rs | 4 +- core/src/env3/backend.rs | 4 +- .../env3/call/{create.rs => instantiate.rs} | 46 +++++++++---------- core/src/env3/call/mod.rs | 10 ++-- core/src/env3/engine/off_chain/impls.rs | 4 +- examples/lang2/delegator/lib.rs | 3 ++ lang2/src/env_access.rs | 4 +- 7 files changed, 39 insertions(+), 36 deletions(-) rename core/src/env3/call/{create.rs => instantiate.rs} (79%) diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index 5704ddf5508..2af456680e8 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -23,7 +23,7 @@ use crate::{ call::{ CallData, CallParams, - CreateParams, + InstantiateParams, ReturnType, }, engine::{ @@ -303,7 +303,7 @@ where /// - 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: &CreateParams) -> Result +pub fn create_contract(params: &InstantiateParams) -> Result where T: EnvTypes, { diff --git a/core/src/env3/backend.rs b/core/src/env3/backend.rs index d0c69538aac..6e8ae1bf9b2 100644 --- a/core/src/env3/backend.rs +++ b/core/src/env3/backend.rs @@ -17,7 +17,7 @@ use crate::{ call::{ CallData, CallParams, - CreateParams, + InstantiateParams, ReturnType, }, EnvTypes, @@ -170,7 +170,7 @@ pub trait TypedEnv: Env { /// - If the instantiation process runs out of gas. fn create_contract( &mut self, - params: &CreateParams, + params: &InstantiateParams, ) -> Result where T: EnvTypes; diff --git a/core/src/env3/call/create.rs b/core/src/env3/call/instantiate.rs similarity index 79% rename from core/src/env3/call/create.rs rename to core/src/env3/call/instantiate.rs index cf5b519598f..48c8bb354e8 100644 --- a/core/src/env3/call/create.rs +++ b/core/src/env3/call/instantiate.rs @@ -52,7 +52,7 @@ where } /// Builds up contract instantiations. -pub struct CreateParams +pub struct InstantiateParams where T: EnvTypes, { @@ -60,8 +60,8 @@ where code_hash: T::Hash, /// The maximum gas costs allowed for the instantiation. gas_limit: u64, - /// The transferred value for the newly created contract. - value: T::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. @@ -69,17 +69,17 @@ where } /// Builds up contract instantiations. -pub struct CreateBuilder +pub struct InstantiateBuilder where T: EnvTypes, { /// The parameters of the cross-contract instantiation. - params: CreateParams, + params: InstantiateParams, /// Seal state. state: PhantomData (Seal, CodeHash)>, } -impl CreateParams +impl InstantiateParams where T: EnvTypes, { @@ -94,7 +94,7 @@ where } /// The endowment for the instantiated contract. pub fn endowment(&self) -> &T::Balance { - &self.value + &self.endowment } /// The raw encoded input data. @@ -103,7 +103,7 @@ where } } -impl CreateParams +impl InstantiateParams where T: EnvTypes, T::Hash: Default, @@ -114,7 +114,7 @@ where Self { code_hash: Default::default(), gas_limit: 0, - value: Default::default(), + endowment: Default::default(), call_data: CallData::new(selector), contract_marker: Default::default(), } @@ -123,15 +123,15 @@ 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 T: EnvTypes, { @@ -142,13 +142,13 @@ where } /// Sets the value transferred upon the execution of the call. - pub fn value(mut self, value: T::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 T: EnvTypes, { @@ -156,16 +156,16 @@ where pub fn using_code( mut self, code_hash: T::Hash, - ) -> CreateBuilder { + ) -> InstantiateBuilder { self.params.code_hash = code_hash; - CreateBuilder { + InstantiateBuilder { params: self.params, state: Default::default(), } } } -impl CreateBuilder +impl InstantiateBuilder where T: EnvTypes, { @@ -179,21 +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 T: EnvTypes, C: FromAccountId, { /// Instantiates the contract and returns its account ID back to the caller. - pub fn create(self) -> Result { + pub fn instantiate(self) -> Result { crate::env3::create_contract(&self.params).map(FromAccountId::from_account_id) } } diff --git a/core/src/env3/call/mod.rs b/core/src/env3/call/mod.rs index 5e05e49fb86..5cb26e30040 100644 --- a/core/src/env3/call/mod.rs +++ b/core/src/env3/call/mod.rs @@ -15,12 +15,12 @@ //! Utilities to call or instantiate contracts on the chain. mod builder; -mod create; +mod instantiate; mod utils; pub mod state { pub use crate::env3::call::{ - create::state::{ + instantiate::state::{ CodeHashAssigned, CodeHashUnassigned, }, @@ -37,9 +37,9 @@ pub use self::{ CallParams, ReturnType, }, - create::{ - CreateBuilder, - CreateParams, + instantiate::{ + InstantiateBuilder, + InstantiateParams, FromAccountId, }, utils::{ diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index b0ea218f1a1..bab223ccf62 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -21,7 +21,7 @@ use crate::{ call::{ CallData, CallParams, - CreateParams, + InstantiateParams, ReturnType, }, Env, @@ -243,7 +243,7 @@ impl TypedEnv for EnvInstance { fn create_contract( &mut self, - _params: &CreateParams, + _params: &InstantiateParams, ) -> Result where T: EnvTypes, diff --git a/examples/lang2/delegator/lib.rs b/examples/lang2/delegator/lib.rs index c42c01ce0b6..a28454be8b2 100644 --- a/examples/lang2/delegator/lib.rs +++ b/examples/lang2/delegator/lib.rs @@ -75,16 +75,19 @@ mod delegator { let accumulator = Accumulator::new(init_value) .value(total_balance / 4) .using_code(accumulator_code_hash) + .instantiate() .create_using(self.env()) .expect("failed at instantiating the `Accumulator` contract"); let adder = Adder::new(accumulator.clone()) .value(total_balance / 4) .using_code(adder_code_hash) + .instantiate() .create_using(self.env()) .expect("failed at instantiating the `Adder` contract"); let subber = Subber::new(accumulator.clone()) .value(total_balance / 4) .using_code(subber_code_hash) + .instantiate() .create_using(self.env()) .expect("failed at instantiating the `Subber` contract"); self.accumulator.set(accumulator); diff --git a/lang2/src/env_access.rs b/lang2/src/env_access.rs index b1700a726b1..cb408e0eb19 100644 --- a/lang2/src/env_access.rs +++ b/lang2/src/env_access.rs @@ -18,7 +18,7 @@ use ink_core::{ env3::{ call::{ CallParams, - CreateParams, + InstantiateParams, ReturnType, }, EnvTypes, @@ -253,7 +253,7 @@ where /// - 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: &CreateParams) -> Result { + pub fn create_contract(self, params: &InstantiateParams) -> Result { env::create_contract::(params) } From a8e83453375e12094df1e8b12df7a2365e15910b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 31 Jan 2020 22:01:09 +0100 Subject: [PATCH 109/112] [core] rename endowment -> transferred_value for CallBuilder/Params --- core/src/env3/call/builder.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/env3/call/builder.rs b/core/src/env3/call/builder.rs index ae48d006823..229291f753f 100644 --- a/core/src/env3/call/builder.rs +++ b/core/src/env3/call/builder.rs @@ -40,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. @@ -71,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. @@ -92,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), } @@ -134,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 } } From 005b74bef186258ab9b7d6cc0b3d837227b64199 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 31 Jan 2020 22:01:22 +0100 Subject: [PATCH 110/112] [examples] adjust Delegator contracts for recents changes --- examples/lang2/delegator/adder/lib.rs | 8 ++++---- examples/lang2/delegator/lib.rs | 25 +++++++++++-------------- examples/lang2/delegator/subber/lib.rs | 8 ++++---- 3 files changed, 19 insertions(+), 22 deletions(-) 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 a28454be8b2..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,22 +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) .instantiate() - .create_using(self.env()) .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) .instantiate() - .create_using(self.env()) .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) .instantiate() - .create_using(self.env()) .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 { From fbbda3c29b352bbcae27f5a5730811a0ee31653f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 31 Jan 2020 22:07:15 +0100 Subject: [PATCH 111/112] [core] rename now_in_ms -> block_timestamp --- core/src/env3/api.rs | 4 ++-- core/src/env3/backend.rs | 4 ++-- core/src/env3/engine/off_chain/impls.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/env3/api.rs b/core/src/env3/api.rs index 2af456680e8..91df7498694 100644 --- a/core/src/env3/api.rs +++ b/core/src/env3/api.rs @@ -94,12 +94,12 @@ where /// # Errors /// /// If the returned value cannot be properly decoded. -pub fn now_in_ms() -> Result +pub fn block_timestamp() -> Result where T: EnvTypes, { ::on_instance(|instance| { - TypedEnv::now_in_ms::(instance) + TypedEnv::block_timestamp::(instance) }) } diff --git a/core/src/env3/backend.rs b/core/src/env3/backend.rs index 6e8ae1bf9b2..c9fd77a4940 100644 --- a/core/src/env3/backend.rs +++ b/core/src/env3/backend.rs @@ -97,8 +97,8 @@ pub trait TypedEnv: Env { /// Returns the amount of gas left for the contract execution. fn gas_left(&mut self) -> Result; - /// Returns the current block time in milliseconds. - fn now_in_ms(&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; diff --git a/core/src/env3/engine/off_chain/impls.rs b/core/src/env3/engine/off_chain/impls.rs index bab223ccf62..8e564822ad9 100644 --- a/core/src/env3/engine/off_chain/impls.rs +++ b/core/src/env3/engine/off_chain/impls.rs @@ -147,7 +147,7 @@ impl TypedEnv for EnvInstance { .map_err(Into::into) } - fn now_in_ms(&mut self) -> Result { + fn block_timestamp(&mut self) -> Result { self.current_block() .expect("uninitialized execution context") .time_stamp::() From 1248a9e716f4c43075185808b6a14a42fa333744 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 31 Jan 2020 22:07:29 +0100 Subject: [PATCH 112/112] [core] follow-up of the call renamings (transferred_value) --- core/src/env3/engine/on_chain/impls.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/env3/engine/on_chain/impls.rs b/core/src/env3/engine/on_chain/impls.rs index 67c5a937cf2..4cf9321779c 100644 --- a/core/src/env3/engine/on_chain/impls.rs +++ b/core/src/env3/engine/on_chain/impls.rs @@ -21,7 +21,7 @@ use crate::{ call::{ CallData, CallParams, - CreateParams, + InstantiateParams, ReturnType, }, Env, @@ -115,7 +115,7 @@ impl EnvInstance { // 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.endowment()); + 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]; @@ -191,7 +191,7 @@ impl TypedEnv for EnvInstance { self.get_property::(ext::gas_left) } - fn now_in_ms(&mut self) -> Result { + fn block_timestamp(&mut self) -> Result { self.get_property::(ext::now) } @@ -275,7 +275,7 @@ impl TypedEnv for EnvInstance { fn create_contract( &mut self, - params: &CreateParams, + params: &InstantiateParams, ) -> Result where T: EnvTypes,