Unverified Commit 4107f98d authored by Hero Bird's avatar Hero Bird Committed by GitHub

Implementation of ink_env 2.0 (#195)

* [core] initial implementation of new env module

* [core] add SRML implementations for some more Env trait methods

* [core] replace SmallVec with Vec since it isn't ready yet

* [core] add license header to ret_code.rs

* [core] take generic buffer params &mut instead of by value

* [core] add implementation of create_contract for SRML env

* [core] remove unused SmallVec imports

* [core] implement deposit_event for SRML contracts env

* [core] implement invoke_runtime for SRML contract env

* [core] adjust invoke_runtime at trait level

* [core] implement restore_to for SRML contracts env

* [core] fix minor warnings and formatting issues

* [core] refactoring and clean ups

Output is no longer a contract property since it was too hard to make it work properly and efficiently.

* [core] add initial version of EnvAccess

* [core] add runtime checks to EnvAccess for input and output

* [core] GetProperty and SetProperty no longer return Result

* [core] introduce macro to remove some duplicate code in EnvAccess

* [core] make buffer always the first argument in Env

* [core] some minor clean ups

* [core] implement {get,set,clear}_contract_storage for EnvAccess

* [core] make assert_not_yet_returned and set_has_interacted private fns

* [core] implement EnvAccess::random

* [core] implement EnvAccess::println

* [core] implement EnvAccess::{invoke_contract, eval_contract, create_contract}

* [core] refactor module imports and exports

* [core] add DynEnv utility

* [core] move EnvAccess{Mut} into their own sub-module

* [core] improve docs and add docs where they were missing

* [core] add Default impl for EnvAccess

* [core] add DynEnv

* [core] greatly improve module docs

* [core] remove some warning-errors that were too strict

* [core] introduce initial implementation of test env

* [core] initial Storage implementation for TestEnv

* [core] improve module documentation

* [core] continue implementation of TestEnv

* [core] improve byte_utils docs for negate_bytes

* [core] add new DefaultSrmlTypes implementation

* [core] continue initial TestEnv implementation

* [core] add TypedEncoded utility that we might use in TestEnvInstance

* [core] enhance TypedEncoded utility

* [core] add Debug impl for Storage and Entry

* [core] implement the TestEnvInstance part of TestEnv

This is the part that actually holds the data in a generic fashion and is controlled later on by the TestEnv through thread_local storage.

* [core] remove unnecessary imports

* [core] remove unnecessary derive impls for some marker types

* [core] greatly improve doc comment of TestEnvInstance

* [core] implement Clone, PartialEq, Eq and Hash for any TypedEncoded<M>

* [ci] update Travis CI rust version: nightly-2019-08-30 -> nightly-2019-09-25

* [core] include TestEnv or SrmlEnv conditionally exclusively

* [core] apply rust fmt

* [core] use Input::remaining_len to optimize scale::Decode impl for CallData

* [core] align usage of PhantomData

* [core] resolve call/create TODOs

* [core] fix minor doc issues

* [core] add a note to Env::println to make users aware of it being a debug utility

* [core] apply rust fmt

* [core] add Debug impl to Storage and Entry

* [core] add PartialOrd and Ord impl to TypedEncoded

* [core] split TestEnv TypedEncoded types into their own module

* [core] add an account database for the TestEnv

* [core] move impl_get_property_for next to its macro definition

* [core] enhance test::AccountsDb

* [core] make fields in TestEnvInstance pub

* [core] fix some mod re-exports

* [core] improve AccountsDb get and get_mut interface

* [core] add TypedEncoded::{assign, try_assign} methods

* [core] add 'static to all EnvTypes associated types

* [core] add initial implementation of accessor for TestEnvInstance

* [core] rename some fields in instance.rs

* [core] implement the rest of the properties getters and setters for TestEnv

* [core] move Input property getter into macro

* [core] add initial stubs for Env impl of TestEnv

* [core] slightly improve property getters

* [core] implement Env::get_contract_storage for TestEnv

* [ci] update nightly-2019-09-25 -> nightly-2019-10-03

* [core] improve Storage::write by using Entry API

* [core] silence warning of unused buffer in Env impl for TestEnv

* [core] implement Env::set_contract_storage for TestEnv

* [core] impl Env::clear_contract_storage for TestEnv

* [core] add dev-comments for implementations of many Env methods for TestEnv

These need some explanation since they partly contradict the off-chain nature of the TestEnv.

* [core] implement Env::random for TestEnv

* [core] improve dev-comment for Env::create_contract

* [core] implement Env::output for TestEnv

* [core] add AccountIdGen to TestEnvInstance

* [core] add records to test environment module

Used for later use in recording implementations of Env for TestEnv.

* [core] apply rustfmt

* [core] fix some warnings

* [core] add constructors for records

* [core] make record constructors pub

* [core] apply rustfmt

* [core] implement Env::{invoke_contract, eval_contract} for TestEnv

* [core] implement restoration and runtime invokation records

* [core] implement Env::{emit_event, invoke_runtime, restore_to} for TestEnv

* [core] implement AccountsDb::insert

* [core] implement Env::create_contract for TestEnv

* [core] add doc-comment for test environment module

* [core] fix copy/paste error in accessor.rs

* [core] add initialization routines to TestEnvInstance

* [core] make use of TypedEncoded in record.rs

* [core] re-export concrete Env impls as EnvImpl

* [core] make most of the test environment definitions publicly visible

* [core] apply rustfmt

* [core] fix 2 compile errors in no_std mode

* [core] add doc-comments to all test sub-modules

* [core] add doc-comments to EnvImpl type alias

* [core] add initial implementation of TestEnv::try_initialize

* [core] add dev-notes to SrmlEnv

* [ci] update travis nightly usage nightly-2019-10-03 -> nightly-2019-10-04
parent d92462b3
......@@ -7,7 +7,7 @@ rust:
# - stable
# - beta
- nightly
- nightly-2019-08-30
- nightly-2019-10-04
matrix:
allow_failures:
......
......@@ -24,16 +24,22 @@ ink_utils = { path = "../utils/", default-features = false }
scale = { package = "parity-scale-codec", version = "1.0", 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.15", default-features = false, features = ["no_std"] }
smallvec = { version = "0.6.10", default-features = false, features = ["union"] }
cfg-if = "0.1"
[features]
default = ["test-env"]
test-env = ["std"]
test-env = [
"std",
]
std = [
"ink_abi/std",
"ink_alloc/std",
"ink_utils/std",
"scale/std",
"type-metadata/std",
"smallvec/std",
]
ink-generate-abi = [
"ink_abi",
......
......@@ -25,7 +25,7 @@ fn invert_bytes(bytes: &mut [u8]) {
/// Negate the given bytes inplace.
///
/// Interprets the bytes as twos-complement number.
/// Interprets the bytes as big endian twos-complement number.
pub fn negate_bytes(bytes: &mut [u8]) {
invert_bytes(bytes);
bytes_add_bytes(bytes, &[0x01]);
......
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of ink!.
//
// ink! is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ink! is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ink!. If not, see <http://www.gnu.org/licenses/>.
//! Infrastructure for calling and instantiating contracts from within contracts.
use crate::{
env2::{
errors::{
CallError,
CreateError,
},
CallParams,
CreateParams,
Env,
EnvTypes,
},
memory::{
vec,
vec::Vec,
},
};
use core::marker::PhantomData;
use derive_more::From;
use scale::Decode;
/// 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.
pub fn from_str(name: &str) -> Self {
From::from(name.as_bytes())
}
/// Returns the underlying bytes of the selector.
pub 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<u8>,
}
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<A>(&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<T: scale::Output>(&self, dest: &mut T) {
dest.write(self.bytes.as_slice());
}
}
impl scale::Decode for CallData {
fn decode<I: scale::Input>(
input: &mut I,
) -> core::result::Result<Self, scale::Error> {
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);
}
Ok(Self { bytes })
}
}
/// 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<T>(PhantomData<fn() -> T>);
/// Builds up contract instantiations.
pub struct CreateBuilder<E, C>
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<fn() -> C>,
}
impl<E, C> CreateParams<E> for CreateBuilder<E, C>
where
E: EnvTypes,
{
/// The code hash of the contract.
fn code_hash(&self) -> &E::Hash {
&self.code_hash
}
/// The gas limit for the contract instantiation.
fn gas_limit(&self) -> u64 {
self.gas_limit
}
/// The endowment for the instantiated contract.
fn endowment(&self) -> &E::Balance {
&self.value
}
/// The raw encoded input data.
fn input_data(&self) -> &CallData {
&self.call_data
}
}
impl<E, C> CreateBuilder<E, C>
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, selector: Selector) -> Self {
Self {
code_hash,
gas_limit: 0,
value: Default::default(),
call_data: CallData::new(selector),
contract_marker: Default::default(),
}
}
}
/// Builds up a call.
pub struct CallBuilder<E, R>
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<ReturnType<R>>,
/// The already encoded call data respecting the ABI.
call_data: CallData,
}
impl<E, R> CallParams<E> for CallBuilder<E, R>
where
E: EnvTypes,
{
/// The code hash of the contract.
fn callee(&self) -> &E::AccountId {
&self.account_id
}
/// The gas limit for the contract instantiation.
fn gas_limit(&self) -> u64 {
self.gas_limit
}
/// The endowment for the instantiated contract.
fn endowment(&self) -> &E::Balance {
&self.value
}
/// The raw encoded input data.
fn input_data(&self) -> &CallData {
&self.call_data
}
}
impl<E, C> CreateBuilder<E, C>
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<A>(self, arg: &A) -> Self
where
A: scale::Encode,
{
let mut this = self;
this.call_data.push_arg(arg);
this
}
}
/// Types that can be contructed from an `AccountId`
///
/// # Note
///
/// This is needed because of conflicting implementations of `From<T> for T`
/// in the generated code of `ink_lang`.
pub trait FromAccountId<E>
where
E: EnvTypes,
{
fn from_account_id(account_id: <E as EnvTypes>::AccountId) -> Self;
}
impl<E, C> CreateBuilder<E, C>
where
E: Env,
C: FromAccountId<E>,
{
/// 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<C, CreateError> {
E::create_contract(&mut Vec::new(), &self)
.map(FromAccountId::from_account_id)
.map_err(|_| CreateError)
}
}
impl<E, R> CallBuilder<E, ReturnType<R>>
where
E: EnvTypes,
E::Balance: Default,
{
/// Instantiates an evaluatable (returns data) remote smart contract call.
pub fn eval(account_id: E::AccountId, selector: Selector) -> Self {
Self {
account_id,
gas_limit: 0,
value: E::Balance::default(),
return_type: PhantomData,
call_data: CallData::new(selector),
}
}
}
impl<E> CallBuilder<E, ()>
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: Selector) -> Self {
Self {
account_id,
gas_limit: 0,
value: E::Balance::default(),
return_type: PhantomData,
call_data: CallData::new(selector),
}
}
}
impl<E, R> CallBuilder<E, R>
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<A>(self, arg: &A) -> Self
where
A: scale::Encode,
{
let mut this = self;
this.call_data.push_arg(arg);
this
}
}
impl<E, R> CallBuilder<E, ReturnType<R>>
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<R, CallError> {
E::eval_contract(&mut Vec::new(), &self).map_err(|_| CallError)
}
}
impl<E> CallBuilder<E, ()>
where
E: Env,
{
/// Fires the call to the remote smart contract.
pub fn fire(self) -> Result<(), CallError> {
E::invoke_contract(&mut Vec::new(), &self).map_err(|_| CallError)
}
}
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of ink!.
//
// ink! is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ink! is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ink!. If not, see <http://www.gnu.org/licenses/>.
use crate::{
env2::{
EnvAccess,
EnvAccessMut,
},
storage::{
alloc::{
Allocate,
AllocateUsing,
DynAlloc,
Initialize,
},
Flush,
},
};
use core::ops::{
Deref,
DerefMut,
};
/// Environment with `&self` access and a dynamic allocator.
///
/// # Note
///
/// - Accesses to `DynEnvAccess` are checked at runtime.
/// - The dynamic allocator allows to dynamically allocate and deallocate objects on the storage.
pub type DynEnvAccess<E> = DynEnv<EnvAccess<E>>;
/// Environment with `&mut self`-only access and a dynamic allocator.
///
/// # Note
///
/// - Accesses to `DynEnvAccessMut` are checked at compiletime.
/// - The dynamic allocator allows to dynamically allocate and deallocate objects on the storage.
pub type DynEnvAccessMut<E> = DynEnv<EnvAccessMut<E>>;
/// A wrapper around `EnvAccess` or `EnvAccessMut` that adds a dynamic storage allocator.
pub struct DynEnv<E> {
/// The wrapped environment.
env: E,
/// The dynamic storage allocator.
alloc: DynAlloc,
}
impl<E> Deref for DynEnv<E> {
type Target = E;
fn deref(&self) -> &Self::Target {
&self.env
}
}
impl<E> DerefMut for DynEnv<E> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.env
}
}
impl<E> Flush for DynEnv<E> {
fn flush(&mut self) {
self.alloc.flush()
}
}
impl<E> AllocateUsing for DynEnv<E>
where
E: Default,
{
unsafe fn allocate_using<A>(alloc: &mut A) -> Self
where
A: Allocate,
{
Self {
env: Default::default(),
alloc: AllocateUsing::allocate_using(alloc),
}
}
}
impl<E> Initialize for DynEnv<E> {
type Args = ();
fn initialize(&mut self, _args: Self::Args) {
self.alloc.initialize(());
}
}
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of ink!.
//
// ink! is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ink! is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ink!. If not, see <http://www.gnu.org/licenses/>.
use crate::{
env2::{
call::CallData,
CallParams,
CreateParams,
Env,
EnvAccessMut,
Result,
},
storage::Key,
};
use core::cell::RefCell;
/// 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<T> {
/// Allows accessing the inner environment by `&self` instead of `&mut self`.
///
/// This is important to make `DynEnv` work also in conjunction with `&self` messages.
access: RefCell<EnvAccessMut<T>>,
}
impl<T> Default for EnvAccess<T> {
fn default() -> Self {
Self {
access: RefCell::new(Default::default()),
}
}
}
impl<T> From<EnvAccessMut<T>> for EnvAccess<T> {
fn from(env_access_mut: EnvAccessMut<T>) -> 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)*