Skip to content
runtime.rs 51.4 KiB
Newer Older
// Copyright 2018-2020 Parity Technologies (UK) Ltd.
Sergey Pepyakin's avatar
Sergey Pepyakin committed
// 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::{
	HostFnWeights, Schedule, Trait, CodeHash, BalanceOf, Error,
	exec::{Ext, StorageKey, TopicOf},
	gas::{Gas, GasMeter, Token, GasMeterResult},
	wasm::env_def::ConvertibleToWasm,
use parity_wasm::elements::ValueType;
use frame_system;
use frame_support::dispatch::DispatchError;
use sp_std::prelude::*;
use codec::{Decode, Encode};
use sp_runtime::traits::SaturatedConversion;
use sp_io::hashing::{
	keccak_256,
	blake2_256,
	blake2_128,
	sha2_256,
};
use pallet_contracts_primitives::{ExecResult, ExecReturnValue, ReturnFlags, ExecError};
/// Every error that can be returned to a contract when it calls any of the host functions.
#[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.
	CalleeTrapped = 1,
	/// The called function ran to completion but decided to revert its state.
	/// An output buffer is returned when one was supplied.
	CalleeReverted = 2,
	/// The passed key does not exist in storage.
	KeyNotFound = 3,
	/// Transfer failed because it would have brought the sender's total balance below the
	/// subsistence threshold.
	BelowSubsistenceThreshold = 4,
	/// Transfer failed for other reasons. Most probably reserved or locked balance of the
	/// sender prevents the transfer.
	TransferFailed = 5,
	/// The newly created contract is below the subsistence threshold after executing
	/// its constructor.
	NewContractNotFunded = 6,
	/// No code could be found at the supplied code hash.
	CodeNotFound = 7,
	/// The contract that was called is either no contract at all (a plain account)
	/// or is a tombstone.
	NotCallable = 8,
}

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) -> Self {
		if from.flags.contains(ReturnFlags::REVERT) {
			Self::CalleeReverted
		} else {
			Self::Success
		}
	}
}
/// The data passed through when a contract uses `seal_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.
Sergey Pepyakin's avatar
Sergey Pepyakin committed
///
/// 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 `seal_return` host function.
	Return(ReturnData),
	/// Signals that a trap was generated in response to a successful call to the
	Termination,
	/// Signals that a trap was generated because of a successful restoration.
	Restoration,
/// Can only be used for one call.
pub(crate) struct Runtime<'a, E: Ext + 'a> {
Sergey Pepyakin's avatar
Sergey Pepyakin committed
	ext: &'a mut E,
	input_data: Option<Vec<u8>>,
	schedule: &'a Schedule<E::T>,
	memory: sp_sandbox::Memory,
Sergey Pepyakin's avatar
Sergey Pepyakin committed
	gas_meter: &'a mut GasMeter<E::T>,
	trap_reason: Option<TrapReason>,
impl<'a, E: Ext + 'a> Runtime<'a, E> {
Sergey Pepyakin's avatar
Sergey Pepyakin committed
	pub(crate) fn new(
		ext: &'a mut E,
		input_data: Vec<u8>,
		schedule: &'a Schedule<E::T>,
		memory: sp_sandbox::Memory,
Sergey Pepyakin's avatar
Sergey Pepyakin committed
		gas_meter: &'a mut GasMeter<E::T>,
	) -> Self {
		Runtime {
			ext,
			input_data: Some(input_data),
Sergey Pepyakin's avatar
Sergey Pepyakin committed
			memory,
			gas_meter,
			trap_reason: None,
/// Converts the sandbox result and the runtime state into the execution outcome.
///
/// It evaluates information stored in the `trap_reason` variable of the runtime and
/// bases the outcome on the value if this variable. Only if `trap_reason` is `None`
/// the result of the sandbox is evaluated.
Sergey Pepyakin's avatar
Sergey Pepyakin committed
pub(crate) fn to_execution_result<E: Ext>(
	runtime: Runtime<E>,
	sandbox_result: Result<sp_sandbox::ReturnValue, sp_sandbox::Error>,
	// If a trap reason is set we base our decision solely on that.
	if let Some(trap_reason) = runtime.trap_reason {
		return match trap_reason {
			// The trap was the result of the execution `return` host function.
			TrapReason::Return(ReturnData{ flags, data }) => {
				let flags = ReturnFlags::from_bits(flags).ok_or_else(||
					"used reserved bit in return flags"
				)?;
				Ok(ExecReturnValue {
					flags,
					data,
				})
			},
			TrapReason::Termination => {
				Ok(ExecReturnValue {
					flags: ReturnFlags::empty(),
					data: Vec::new(),
				})
			},
			TrapReason::Restoration => {
				Ok(ExecReturnValue {
					flags: ReturnFlags::empty(),
					data: Vec::new(),
				})
			},
			TrapReason::SupervisorError(error) => Err(error)?,
	}

	// Check the exact type of the error.
	match sandbox_result {
Sergey Pepyakin's avatar
Sergey Pepyakin committed
		// No traps were generated. Proceed normally.
		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) =>
			Err("validation error")?,
Sergey Pepyakin's avatar
Sergey Pepyakin committed
		// Any other kind of a trap should result in a failure.
		Err(sp_sandbox::Error::Execution) | Err(sp_sandbox::Error::OutOfBounds) =>
Loading full blame...