Unverified Commit ba5aed19 authored by Hero Bird's avatar Hero Bird Committed by GitHub
Browse files

Sync with Substrate RC6 (#478)



* [core] remove get_runtime_storage and invoke_runtime

* [core] invalidate all implementations and usages of ext functions

* [core] adjust ext_ functions in ext.rs

* [core] add ReturnFlags to public API

* [core] remove off-chain impls for get_runtime_storage and invoke_runtime

* [core] rename ext::ext_input -> ext::input

* [core] change return type of get_contract_storage

Previously returned Option<Result<R>> and now returns Result<Option<R>>.
This change is more pragmatic.

* [core] add conversion from ext::Error to EnvError

* [core] on-chain: add new utilities for static buffer modifications

- EncodeScope: for efficiently appending encoded values
- ScopedBuffer: for efficiently chunking buffers

* [core] add impls for on-chain property getters

* [core] remove no longer needed utiltiy function

* [core] on-chain: new impl for invoke_contract and eval_contract

* [core] on-chain: new impl for get_contract_storage

* [core] on-chain: new impl for decode_input

We should also rename this to simply "input" later.

* [core] on-chain: new impl for output

Now also uses the new ReturnFlags abstraction.

* [core] on-chain: new impl for instantiate_contract

* [core] on-chain: remove unused API

* [core] on-chain: move EncodeScope and ScopedBuffer to buffer.rs

* [core] on-chain: implement rest of the on-chain API

* [core] add ScopedBuffer::take_bytes

* [core] Add Env::call_chain_extension trait method

* [core] remove unused helper methods

* [core] on-chain: simplify static buffer

- No more length
- No more encodable
- Only full range access

* [core] add env::call_chain_extension public API

* [core] rename Env::output -> return_value

* [core] off-chain: adjust a bunch of off-chain methods to new interfaces

* [core] off-chain: adjust get_contract_storage return type

* [core] off-chain: remove RuntimeCallHandler and RuntimeStorage facilities

* [core] off-chain: adjust some tests

* [core] off-chain: remove OffCall utility type

* [core] off-chain: implement chain extension handler

* [core] adjust panic message

* [core] apply rustfmt

* [core] apply clippy suggestion

* [core] off-chain: re-export ChainExtension and ChainSpec types from test API

* [core] on-chain: retain panic messages

* [core] rename ext_ to seal_ for all on-chain functions

* [alloc] fix some warnings and add some minor comments

* [core] add wasm_import_module = "seal0"

* [core] introduce new Seal error codes

* [core] directly return ReturnCode from C-FII

* [core] make transfer return Result

* [core] improve some doc comments

* rename some old errors to their new names

* [core] remove unused env errors and rename some off-chain errors

* [core] fix bug in ReturnFlags::set_reverted (formerly known as set_trapped)

* [core] avoid From impl for () for OffChainError

* [lang] adjust lang layer for changes in core

- Remove invoke_runtime
- Remove get_runtime_storage
- Rename gas_price -> weight_to_fee
- Add ReturnFlags to return_value call

* [core] turn redundant asserts into debug_assert

* [core] be more strict when handling ext::get_storage error

* [core] implement clippy suggestion

* [examples] remove no longer useful runtime-storage contract

We no longer support the get_runtime_storage host function.
It will eventually be reintroduced at a later point through chain extensions.

* [core] disable chain extensions by default

Can be enabled by experimental crate feature:
- unstable_chain_extensions

* [lang] apply clippy suggestion to use matches! macro

* [core] fix docs for ext_return
Co-authored-by: Alexander Theißen's avatarAlexander Theißen <alex.theissen@me.com>

* [core] fix return type of api::return_value

Not sure why the compiler didn't mention this return type failure ...
Co-authored-by: Alexander Theißen's avatarAlexander Theißen <alex.theissen@me.com>

* [core] fix doc comment of Env::return_value

* [core] introduce RawReturnCode as a layer between Seal error codes and ink!

* [alloc] make #[alloc_error_handler] private

* [alloc, core] move alloc_handler from ink_alloc to ink_core

* [core] replace useless intermediate ReturnCode type

* [core] rename RawReturnCode -> ReturnCode

* [core] apply rustfmt

* [core] apply rustfmt

* [core] off-chain: rename invoke -> eval for chain extension calling

* [core] use Ptr32 and Ptr32Mut to encapsulate pointer -> u32 conversions

* [core] make new Ptr32 and Ptr32Mut abstractions more type safe
Co-authored-by: Alexander Theißen's avatarAlexander Theißen <alex.theissen@me.com>
parent eeb6bc0f
Pipeline #104772 canceled with stages
in 5 minutes and 42 seconds
// 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.
......@@ -12,14 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#[panic_handler]
pub fn panic(_info: &core::panic::PanicInfo) -> ! {
core::intrinsics::abort()
}
// `extern` fn uses type `core::alloc::Layout`, which is not FFI-safe
#[allow(improper_ctypes)]
#[alloc_error_handler]
pub extern "C" fn oom(_: core::alloc::Layout) -> ! {
fn oom(_: core::alloc::Layout) -> ! {
core::intrinsics::abort()
}
// 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.
......@@ -15,7 +15,8 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(feature = "std"), feature(alloc_error_handler, core_intrinsics))]
// Use `wee_alloc` as the global allocator.
// We use `wee_alloc` as the global allocator since it is optimized for binary file size
// so that contracts compiled with it as allocator do not grow too much in size.
#[cfg(not(feature = "std"))]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
......
......@@ -73,6 +73,7 @@ std = [
"blake2",
]
ink-fuzz-tests = ["std"]
ink-unstable-chain-extensions = []
[[bench]]
name = "bench_lazy"
......
......@@ -17,6 +17,7 @@
use crate::env::{
backend::{
Env,
ReturnFlags,
TypedEnv,
},
call::{
......@@ -65,12 +66,12 @@ where
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn gas_price<T>(gas: u64) -> Result<T::Balance>
pub fn weight_to_fee<T>(gas: u64) -> Result<T::Balance>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnv::gas_price::<T>(instance, gas)
TypedEnv::weight_to_fee::<T>(instance, gas)
})
}
......@@ -208,6 +209,10 @@ where
}
/// Writes the value to the contract storage under the given key.
///
/// # Panics
///
/// - If the encode length of value exceeds the configured maximum value length of a storage entry.
pub fn set_contract_storage<V>(key: &Key, value: &V)
where
V: scale::Encode,
......@@ -221,8 +226,8 @@ where
///
/// # Errors
///
/// - If the decoding of the typed value failed
pub fn get_contract_storage<R>(key: &Key) -> Option<Result<R>>
/// - If the decoding of the typed value failed (`KeyNotFound`)
pub fn get_contract_storage<R>(key: &Key) -> Result<Option<R>>
where
R: scale::Decode,
{
......@@ -238,25 +243,6 @@ pub fn clear_contract_storage(key: &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<T>(params: &T::Call) -> Result<()>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnv::invoke_runtime::<T>(instance, params)
})
}
/// Invokes a contract message.
///
/// # Note
......@@ -268,7 +254,8 @@ where
///
/// # Errors
///
/// - If the called contract does not exist.
/// - If the called account does not exist.
/// - If the called account is not a contract.
/// - If the called contract is a tombstone.
/// - If arguments passed to the called contract message are invalid.
/// - If the called contract execution has trapped.
......@@ -292,7 +279,8 @@ where
///
/// # Errors
///
/// - If the called contract does not exist.
/// - If the called account does not exist.
/// - If the called account is not a contract.
/// - If the called contract is a tombstone.
/// - If arguments passed to the called contract message are invalid.
/// - If the called contract execution has trapped.
......@@ -424,7 +412,7 @@ where
/// contract call or invoke a runtime function that performs the
/// transaction.
///
/// # Errors
/// # Panics
///
/// If the contract doesn't have sufficient funds.
pub fn transfer<T>(destination: T::AccountId, value: T::Balance) -> Result<()>
......@@ -436,6 +424,27 @@ where
})
}
/// Calls the chain extension with the given ID and inputs.
///
/// Returns the given output type.
///
/// # Errors
///
/// - If the given function ID does not exist in the runtime.
/// - If the given inputs cannot be properly decoded by the runtime.
/// - If the given output type cannot be properly decoded by the contract.
/// - If some chain extension specific conditions are not met.
#[cfg(feature = "ink-unstable-chain-extensions")]
pub fn call_chain_extension<I, O>(func_id: u32, input: &I) -> Result<O>
where
I: scale::Codec + 'static,
O: scale::Codec + 'static,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
Env::call_chain_extension(instance, func_id, input)
})
}
/// Returns the execution input to the executed contract and decodes it as `T`.
///
/// # Note
......@@ -470,14 +479,13 @@ where
///
/// # Note
///
/// This call must be the last call to the contract
/// environment for every contract execution.
pub fn output<R>(return_value: &R)
/// This function stops the execution of the contract immediately.
pub fn return_value<R>(return_flags: ReturnFlags, return_value: &R) -> !
where
R: scale::Encode,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
Env::output::<R>(instance, return_value)
Env::return_value::<R>(instance, return_flags, return_value)
})
}
......@@ -505,20 +513,6 @@ pub fn println(content: &str) {
<EnvInstance as OnInstance>::on_instance(|instance| Env::println(instance, content))
}
/// Returns the value from the *runtime* storage at the position of the key if any.
///
/// # Errors
///
/// - If the decoding of the typed value failed
pub fn get_runtime_storage<R>(runtime_key: &[u8]) -> Option<Result<R>>
where
R: scale::Decode,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
Env::get_runtime_storage::<R>(instance, runtime_key)
})
}
/// Built-in efficient cryptographic hash functions.
pub mod hash {
use super::*;
......
......@@ -24,6 +24,33 @@ use crate::env::{
};
use ink_primitives::Key;
/// The flags to indicate further information about the end of a contract execution.
pub struct ReturnFlags {
value: u32,
}
impl Default for ReturnFlags {
fn default() -> Self {
Self { value: 0 }
}
}
impl ReturnFlags {
/// Sets the bit to indicate that the execution is going to be reverted.
pub fn set_reverted(mut self, has_reverted: bool) -> Self {
match has_reverted {
true => self.value |= has_reverted as u32,
false => self.value &= !has_reverted as u32,
}
self
}
/// Returns the underlying `u32` representation.
pub(crate) fn into_u32(self) -> u32 {
self.value
}
}
/// Environmental contract functionality that does not require `EnvTypes`.
pub trait Env {
/// Writes the value to the contract storage under the given key.
......@@ -36,22 +63,13 @@ pub trait Env {
/// # Errors
///
/// - If the decoding of the typed value failed
fn get_contract_storage<R>(&mut self, key: &Key) -> Option<Result<R>>
fn get_contract_storage<R>(&mut self, key: &Key) -> Result<Option<R>>
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 if any.
///
/// # Errors
///
/// - If the decoding of the typed value failed
fn get_runtime_storage<R>(&mut self, runtime_key: &[u8]) -> Option<Result<R>>
where
R: scale::Decode;
/// Returns the execution input to the executed contract and decodes it as `T`.
///
/// # Note
......@@ -83,10 +101,12 @@ pub trait Env {
///
/// # 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<R>(&mut self, return_value: &R)
/// Calling this method will end contract execution immediately.
/// It will return the given return value back to its caller.
///
/// The `flags` parameter can be used to revert the state changes of the
/// entire execution if necessary.
fn return_value<R>(&mut self, flags: ReturnFlags, return_value: &R) -> !
where
R: scale::Encode;
......@@ -108,6 +128,22 @@ pub trait Env {
/// Conducts the BLAKE2 128-bit hash of the input
/// puts the result into the output buffer.
fn hash_blake2_128(input: &[u8], output: &mut [u8; 16]);
/// Calls the chain extension with the given ID and inputs.
///
/// Returns the output of the chain extension of the specified type.
///
/// # Errors
///
/// - If the chain extension with the given ID does not exist.
/// - If the inputs had an unexpected encoding.
/// - If the output could not be properly decoded.
/// - If some extension specific condition has not been met.
#[cfg(feature = "ink-unstable-chain-extensions")]
fn call_chain_extension<I, O>(&mut self, func_id: u32, input: &I) -> Result<O>
where
I: scale::Codec + 'static,
O: scale::Codec + 'static;
}
/// Environmental contract functionality.
......@@ -131,7 +167,7 @@ pub trait TypedEnv: Env {
/// # Note
///
/// For more details visit: [`ink_core::env::gas_price`]
fn gas_price<T: EnvTypes>(&mut self, gas: u64) -> Result<T::Balance>;
fn weight_to_fee<T: EnvTypes>(&mut self, gas: u64) -> Result<T::Balance>;
/// Returns the amount of gas left for the contract execution.
///
......@@ -208,15 +244,6 @@ pub trait TypedEnv: Env {
where
T: EnvTypes;
/// Invokes a call of the runtime.
///
/// # Note
///
/// For more details visit: [`ink_core::env::invoke_runtime`]
fn invoke_runtime<T>(&mut self, call: &T::Call) -> Result<()>
where
T: EnvTypes;
/// Invokes a contract message.
///
/// # Note
......
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::OffChainError;
use crate::env::Result;
use std::collections::HashMap;
type FuncId = u32;
/// Types implementing this trait can be used as chain extensions.
///
/// This trait is only useful for testing contract via the off-chain environment.
pub trait ChainExtension {
/// The expected input type.
///
/// # Note
///
/// This can be a tuple to expect multiple input types.
type Input: scale::Codec;
/// The expected output type.
type Output: scale::Codec;
/// The static function ID of the chain extension.
///
/// # Note
///
/// This is expected to return a constant value.
fn func_id(&self) -> u32;
/// Calls the chain extension with the given input.
fn call(&mut self, input: &Self::Input) -> Result<Self::Output>;
}
/// A raw chain extension function.
///
/// This is mostly a wrapper closure around the real chain extension function
/// that handles marshalling of types between their encoded and decoded
/// representations.
type ChainExtensionFn = Box<dyn FnMut(Vec<u8>) -> Result<Vec<u8>>>;
/// Runtime call handler.
///
/// More generically a mapping from bytes to bytes.
pub struct ChainExtensionHandler {
/// The currently registered runtime call handler.
registered: HashMap<FuncId, ChainExtensionFn>,
}
impl ChainExtensionHandler {
/// Creates a new chain extension handler.
///
/// Initialized with an empty set of chain extensions.
pub fn new() -> Self {
Self {
registered: HashMap::new(),
}
}
/// Resets the chain extension handler to uninitialized state.
pub fn reset(&mut self) {
self.registered.clear()
}
/// Register a new chain extension.
pub fn register<I, O>(
&mut self,
mut extension: Box<dyn ChainExtension<Input = I, Output = O>>,
) where
I: scale::Codec + 'static,
O: scale::Codec + 'static,
{
let func_id = extension.func_id();
self.registered.insert(
func_id,
Box::new(move |encoded_input: Vec<u8>| {
let decoded_input = scale::Decode::decode(&mut &encoded_input[..])?;
let decoded_output = extension.call(&decoded_input)?;
Ok(scale::Encode::encode(&decoded_output))
}),
);
}
/// Evaluates the chain extension with the given parameters.
///
/// Upon success returns the values returned by the evaluated chain extension.
pub fn eval<I, O>(&mut self, func_id: FuncId, input: &I) -> Result<O>
where
I: scale::Codec + 'static,
O: scale::Codec + 'static,
{
use std::collections::hash_map::Entry;
match self.registered.entry(func_id) {
Entry::Occupied(mut occupied) => {
let encoded_input = scale::Encode::encode(input);
let encoded_output = occupied.get_mut()(encoded_input)?;
scale::Decode::decode(&mut &encoded_output[..]).map_err(Into::into)
}
Entry::Vacant(_vacant) => {
Err(OffChainError::UnregisteredChainExtension.into())
}
}
}
}
......@@ -247,13 +247,12 @@ impl Account {
}
/// Returns the value stored in the contract storage at the given key.
pub fn get_storage<T>(&self, at: Key) -> Option<Result<T>>
pub fn get_storage<T>(&self, at: Key) -> Result<Option<T>>
where
T: scale::Decode,
{
self.contract_or_err()
.and_then(|contract| contract.storage.get_storage::<T>(at))
.transpose()
}
/// Returns the total number of reads and write from and to the contract's storage.
......
......@@ -27,11 +27,12 @@ use crate::env::{
EnvError,
EnvTypes,
Result,
ReturnFlags,
Topics,
TypedEnv,
};
use ink_primitives::Key;
use core::convert::TryInto;
use ink_primitives::Key;
use num_traits::Bounded;
impl EnvInstance {
......@@ -70,13 +71,13 @@ impl Env for EnvInstance {
.expect("callee account is not a smart contract");
}
fn get_contract_storage<R>(&mut self, key: &Key) -> Option<Result<R>>
fn get_contract_storage<R>(&mut self, key: &Key) -> Result<Option<R>>
where
R: scale::Decode,
{
self.callee_account()
.get_storage::<R>(*key)
.map(|result| result.map_err(Into::into))
.map_err(Into::into)
}
fn clear_contract_storage(&mut self, key: &Key) {
......@@ -85,13 +86,6 @@ impl Env for EnvInstance {
.expect("callee account is not a smart contract");
}
fn get_runtime_storage<R>(&mut self, runtime_key: &[u8]) -> Option<Result<R>>
where
R: scale::Decode,
{
self.runtime_storage.load::<R>(runtime_key)
}
fn decode_input<T>(&mut self) -> Result<T>
where
T: scale::Decode,
......@@ -107,7 +101,7 @@ impl Env for EnvInstance {
})
}
fn output<R>(&mut self, return_value: &R)
fn return_value<R>(&mut self, flags: ReturnFlags, return_value: &R) -> !
where
R: scale::Encode,
{
......@@ -115,6 +109,7 @@ impl Env for EnvInstance {
.exec_context_mut()
.expect("uninitialized execution context");
ctx.output = Some(return_value.encode());
std::process::exit(flags.into_u32() as i32)
}
fn println(&mut self, content: &str) {
......@@ -136,6 +131,45 @@ impl Env for EnvInstance {
fn hash_sha2_256(input: &[u8], output: &mut [u8; 32]) {
hashing::sha2_256(input, output)
}
#[cfg(feature = "ink-unstable-chain-extensions")]
fn call_chain_extension<I, O>(&mut self, func_id: u32, input: &I) -> Result<O>
where
I: scale::Codec + 'static,
O: scale::Codec + 'static,
{
self.chain_extension_handler.eval(func_id, input)
}
}
impl EnvInstance {
fn transfer_impl<T>(&mut self, destination: T::AccountId, value: T::Balance) -> Result<()>
where
T: EnvTypes,
{
let src_id = self.account_id::<T>()?;
let src_value = self
.accounts
.get_account::<T>(&src_id)
.expect("account of executed contract must exist")
.balance::<T>()?;
if src_value < value {
return Err(EnvError::TransferFailed)
}
let dst_value = self
.accounts
.get_or_create_account::<T>(&destination)
.balance::<T>()?;
self.accounts
.get_account_mut::<T>(&src_id)
.expect("account of executed contract must exist")
.set_balance::<T>(src_value - value)?;
self.accounts
.get_account_mut::<T>(&destination)
.expect("the account must exist already or has just been created")
.set_balance::<T>(dst_value + value)?;
Ok(())
}
}
impl TypedEnv for EnvInstance {
......@@ -156,14 +190,16 @@ impl TypedEnv for EnvInstance {
}
/// Emulates gas price calculation
fn gas_price<T: EnvTypes>(&mut self, gas: u64) -> Result<T::Balance> {
fn weight_to_fee<T: EnvTypes>(&mut self, gas: u64) -> Result<T::Balance> {
use crate::env::arithmetic::Saturating as _;
let gas_price = self.chain_spec
let gas_price = self
.chain_spec
.gas_price::<T>()
.map_err(|_| scale::Error::from("could not decode gas price"))?;
Ok(gas_price.saturating_mul(gas.try_into().unwrap_or_else(|_| Bounded::max_value())))
Ok(gas_price
.saturating_mul(gas.try_into().unwrap_or_else(|_| Bounded::max_value())))
}
fn gas_left<T: EnvTypes>(&mut self) -> Result<T::Balance> {
......@@ -254,13 +290,6 @@ impl TypedEnv for EnvInstance {
unimplemented!("off-chain environment does not support contract invokation")
}
fn invoke_runtime<T>(&mut self, params: &T::Call) -> Result<()>
where
T: EnvTypes,
{
self.runtime_call_handler.invoke::<T>(params)
}
fn eval_contract<T, Args, R>(
&mut self,
_call_params: &CallParams<T, Args, ReturnType<R>>,
......@@ -307,28 +336,7 @@ impl TypedEnv for EnvInstance {
where
T: EnvTypes,
{
let src_id = self.account_id::<T>()?;
let src_value = self
.accounts
.get_account::<T>(&src_id)
.expect("account of executed contract must exist")
.balance::<T>()?;
if src_value < value {
return Err(EnvError::TransferCallFailed)