runtime.rs 43.2 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::{Schedule, Trait, CodeHash, BalanceOf, Error};
use crate::exec::{
	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_system;
use frame_support::dispatch::DispatchError;
use sp_std::prelude::*;
use codec::{Decode, Encode};
use sp_runtime::traits::{Bounded, SaturatedConversion};
use sp_io::hashing::{
	keccak_256,
	blake2_256,
	blake2_128,
	sha2_256,
};
/// 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.
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),
Sergey Pepyakin's avatar
Sergey Pepyakin committed
	/// Signals that trap was generated in response to call `ext_return` host function.
	Return(ReturnData),
	/// 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,
/// 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,
	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,
		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,
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>,
	match runtime.trap_reason {
		// 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 {
		Some(TrapReason::Termination) => {
			return Ok(ExecReturnValue {
				flags: ReturnFlags::empty(),
				data: Vec::new(),
			})
		},
		Some(TrapReason::Restoration) => {
				flags: ReturnFlags::empty(),
		Some(TrapReason::SupervisorError(error)) => Err(error)?,
		None => (),
	}

	// 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) =>
			Err("contract trapped during execution")?,
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
#[derive(Copy, Clone)]
pub enum RuntimeToken {
	/// 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...