Newer
Older
// Copyright 2018-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Environment definition of the wasm smart-contract runtime.
use crate::{Schedule, Trait, CodeHash, BalanceOf, Error};
Ext, ExecResult, ExecReturnValue, StorageKey, TopicOf, ReturnFlags,
use crate::gas::{Gas, GasMeter, Token, GasMeterResult};
use crate::wasm::env_def::ConvertibleToWasm;
use parity_wasm::elements::ValueType;
use frame_support::dispatch::DispatchError;
use sp_std::prelude::*;
use sp_runtime::traits::{Bounded, SaturatedConversion};
use sp_io::hashing::{
keccak_256,
blake2_256,
blake2_128,
sha2_256,
};
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/// Every error that can be returned from a runtime API call.
#[repr(u32)]
pub enum ReturnCode {
/// API call successful.
Success = 0,
/// The called function trapped and has its state changes reverted.
/// In this case no output buffer is returned.
/// Can only be returned from `ext_call` and `ext_instantiate`.
CalleeTrapped = 1,
/// The called function ran to completion but decided to revert its state.
/// An output buffer is returned when one was supplied.
/// Can only be returned from `ext_call` and `ext_instantiate`.
CalleeReverted = 2,
/// The passed key does not exist in storage.
KeyNotFound = 3,
}
impl ConvertibleToWasm for ReturnCode {
type NativeType = Self;
const VALUE_TYPE: ValueType = ValueType::I32;
fn to_typed_value(self) -> sp_sandbox::Value {
sp_sandbox::Value::I32(self as i32)
}
fn from_typed_value(_: sp_sandbox::Value) -> Option<Self> {
debug_assert!(false, "We will never receive a ReturnCode but only send it to wasm.");
None
}
}
impl From<ExecReturnValue> for ReturnCode {
fn from(from: ExecReturnValue) -> ReturnCode {
if from.flags.contains(ReturnFlags::REVERT) {
Self::CalleeReverted
} else {
Self::Success
}
}
}
/// The data passed through when a contract uses `ext_return`.
struct ReturnData {
/// The flags as passed through by the contract. They are still unchecked and
/// will later be parsed into a `ReturnFlags` bitflags struct.
flags: u32,
/// The output buffer passed by the contract as return data.
data: Vec<u8>,
}
/// Enumerates all possible reasons why a trap was generated.
/// This is either used to supply the caller with more information about why an error
/// occurred (the SupervisorError variant).
/// The other case is where the trap does not constitute an error but rather was invoked
/// as a quick way to terminate the application (all other variants).
enum TrapReason {
/// The supervisor trapped the contract because of an error condition occurred during
/// execution in privileged code.
SupervisorError(DispatchError),
/// Signals that trap was generated in response to call `ext_return` host function.
/// Signals that a trap was generated in response to a succesful call to the
/// `ext_terminate` host function.
Termination,
/// Signals that a trap was generated because of a successful restoration.
Restoration,
pub(crate) struct Runtime<'a, E: Ext + 'a> {
memory: sp_sandbox::Memory,
memory: sp_sandbox::Memory,
gas_meter: &'a mut GasMeter<E::T>,
) -> Self {
Runtime {
ext,
}
}
}
pub(crate) fn to_execution_result<E: Ext>(
runtime: Runtime<E>,
sandbox_result: Result<sp_sandbox::ReturnValue, sp_sandbox::Error>,
) -> ExecResult {
// The trap was the result of the execution `return` host function.
Some(TrapReason::Return(ReturnData{ flags, data })) => {
let flags = ReturnFlags::from_bits(flags).ok_or_else(||
"used reserved bit in return flags"
)?;
return Ok(ExecReturnValue {
data,
})
Some(TrapReason::Termination) => {
Some(TrapReason::Restoration) => {
return Ok(ExecReturnValue {
data: Vec::new(),
})
}
Some(TrapReason::SupervisorError(error)) => Err(error)?,
}
// Check the exact type of the error.
match sandbox_result {
Ok(_) => {
Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })
}
// `Error::Module` is returned only if instantiation or linking failed (i.e.
// wasm binary tried to import a function that is not provided by the host).
// This shouldn't happen because validation process ought to reject such binaries.
//
// Because panics are really undesirable in the runtime code, we treat this as
// a trap for now. Eventually, we might want to revisit this.
Err(sp_sandbox::Error::Module) =>
// Any other kind of a trap should result in a failure.
Err(sp_sandbox::Error::Execution) | Err(sp_sandbox::Error::OutOfBounds) =>
Err("contract trapped during execution")?,
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
#[derive(Copy, Clone)]
/// Explicit call to the `gas` function. Charge the gas meter
/// with the value provided.
Explicit(u32),
/// The given number of bytes is read from the sandbox memory.
ReadMemory(u32),
/// The given number of bytes is written to the sandbox memory.
WriteMemory(u32),
/// The given number of bytes is read from the sandbox memory and
/// is returned as the return data buffer of the call.
ReturnData(u32),
/// (topic_count, data_bytes): A buffer of the given size is posted as an event indexed with the
/// given number of topics.
DepositEvent(u32, u32),
Loading full blame...