runtime.rs 86.9 KiB
Newer Older
			out_len_ptr,
			&ctx.ext.minimum_balance().encode(),
			false,
			already_charged,
	/// Stores the tombstone deposit into the supplied buffer.
	///
	/// The value is stored to linear memory at the address pointed to by `out_ptr`.
	/// `out_len_ptr` must point to a u32 value that describes the available space at
	/// `out_ptr`. This call overwrites it with the size of the value. If the available
	/// space at `out_ptr` is less than the size of the value a trap is triggered.
	///
	/// # Deprecation
	///
	/// There is no longer a tombstone deposit. This function always returns 0.
		out_ptr: u32,
		out_len_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::Balance)?;
		let deposit = <BalanceOf<E::T>>::zero().encode();
		Ok(ctx.write_sandbox_output(
			memory,
			out_ptr,
			out_len_ptr,
			&deposit,
			false,
			already_charged,
		)?)
	/// Was used to restore the given destination contract sacrificing the caller.
	///
	/// # Note
	///
	/// The state rent functionality was removed. This is stub only exists for
	/// backwards compatiblity
		_dest_ptr: u32,
		_code_hash_ptr: u32,
		_rent_allowance_ptr: u32,
		_rent_allowance_len: u32,
		_delta_ptr: u32,
		_delta_count: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::DebugMessage)?;
		Ok(())
	/// Was used to restore the given destination contract sacrificing the caller.
	///
	/// # Note
	///
	/// The state rent functionality was removed. This is stub only exists for
	/// backwards compatiblity
	#[version(1)]
		_dest_ptr: u32,
		_code_hash_ptr: u32,
		_rent_allowance_ptr: u32,
		_delta_ptr: u32,
		_delta_count: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::DebugMessage)?;
		Ok(())
	/// Deposit a contract event with the data buffer and optional list of topics. There is a limit
	/// on the maximum number of topics specified by `event_topics`.
	///
	/// - topics_ptr - a pointer to the buffer of topics encoded as `Vec<T::Hash>`. The value of
	///   this is ignored if `topics_len` is set to 0. The topics list can't contain duplicates.
	/// - topics_len - the length of the topics buffer. Pass 0 if you want to pass an empty vector.
	/// - data_ptr - a pointer to a raw data buffer which will saved along the event.
	/// - data_len - the length of the data buffer.
		topics_ptr: u32,
		topics_len: u32,
		data_ptr: u32,
		data_len: u32,
	) -> Result<(), TrapReason> {
		fn has_duplicates<T: Ord>(items: &mut Vec<T>) -> bool {
			items.sort();
			// Find any two consecutive equal elements.
			items.windows(2).any(|w| match &w {
				&[a, b] => a == b,
				_ => false,
		let num_topic = topics_len
			.checked_div(sp_std::mem::size_of::<TopicOf<E::T>>() as u32)
			.ok_or("Zero sized topics are not allowed")?;
		ctx.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?;
		if data_len > ctx.ext.max_value_size() {
			return Err(Error::<E::T>::ValueTooLarge.into())
		let mut topics: Vec<TopicOf<<E as Ext>::T>> = match topics_len {
			0 => Vec::new(),
			_ => ctx.read_sandbox_memory_as_unbounded(memory, topics_ptr, topics_len)?,
		// If there are more than `event_topics`, then trap.
		if topics.len() > ctx.ext.schedule().limits.event_topics as usize {
			return Err(Error::<E::T>::TooManyTopics.into())
		}

		// Check for duplicate topics. If there are any, then trap.
		// Complexity O(n * log(n)) and no additional allocations.
		// This also sorts the topics.
		if has_duplicates(&mut topics) {
			return Err(Error::<E::T>::DuplicateTopics.into())
		let event_data = ctx.read_sandbox_memory(memory, data_ptr, data_len)?;

		ctx.ext.deposit_event(topics, event_data);
	/// Was used to set rent allowance of the contract.
	///
	/// # Note
	///
	/// The state rent functionality was removed. This is stub only exists for
	/// backwards compatiblity.
		_value_ptr: u32,
		_value_len: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::DebugMessage)?;
	/// Was used to set rent allowance of the contract.
	///
	/// # Note
	///
	/// The state rent functionality was removed. This is stub only exists for
	/// backwards compatiblity.
	#[version(1)]
	fn set_rent_allowance(ctx: _, _memory: _, _value_ptr: u32) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::DebugMessage)?;
	/// Was used to store the rent allowance into the supplied buffer.
	///
	/// # Note
	///
	/// The state rent functionality was removed. This is stub only exists for
	/// backwards compatiblity.
	fn rent_allowance(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::Balance)?;
		let rent_allowance = <BalanceOf<E::T>>::max_value().encode();
			out_ptr,
			out_len_ptr,
			&rent_allowance,
			false,
			already_charged,
	/// Stores the current block number of the current contract into the supplied buffer.
	///
	/// The value is stored to linear memory at the address pointed to by `out_ptr`.
	/// `out_len_ptr` must point to a u32 value that describes the available space at
	/// `out_ptr`. This call overwrites it with the size of the value. If the available
	/// space at `out_ptr` is less than the size of the value a trap is triggered.
	fn block_number(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::BlockNumber)?;
			out_ptr,
			out_len_ptr,
			&ctx.ext.block_number().encode(),
			false,
			already_charged,
	/// Computes the SHA2 256-bit hash on the given input buffer.
	///
	/// Returns the result directly into the given output buffer.
	///
	/// # Note
	///
	/// - The `input` and `output` buffer may overlap.
	/// - The output buffer is expected to hold at least 32 bytes (256 bits).
	/// - It is the callers responsibility to provide an output buffer that is large enough to hold
	///   the expected amount of bytes returned by the chosen hash function.
	///
	/// # Parameters
	///
	/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
	/// - `input_len`: the length of the input data in bytes.
	/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
	///   function will write the result directly into this buffer.
		input_ptr: u32,
		input_len: u32,
		output_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::HashSha256(input_len))?;
		Ok(ctx.compute_hash_on_intermediate_buffer(
			memory, sha2_256, input_ptr, input_len, output_ptr,
		)?)
	/// Computes the KECCAK 256-bit hash on the given input buffer.
	///
	/// Returns the result directly into the given output buffer.
	///
	/// # Note
	///
	/// - The `input` and `output` buffer may overlap.
	/// - The output buffer is expected to hold at least 32 bytes (256 bits).
	/// - It is the callers responsibility to provide an output buffer that is large enough to hold
	///   the expected amount of bytes returned by the chosen hash function.
	///
	/// # Parameters
	///
	/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
	/// - `input_len`: the length of the input data in bytes.
	/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
	///   function will write the result directly into this buffer.
		input_ptr: u32,
		input_len: u32,
		output_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::HashKeccak256(input_len))?;
		Ok(ctx.compute_hash_on_intermediate_buffer(
			memory, keccak_256, input_ptr, input_len, output_ptr,
		)?)
	/// Computes the BLAKE2 256-bit hash on the given input buffer.
	///
	/// Returns the result directly into the given output buffer.
	///
	/// # Note
	///
	/// - The `input` and `output` buffer may overlap.
	/// - The output buffer is expected to hold at least 32 bytes (256 bits).
	/// - It is the callers responsibility to provide an output buffer that is large enough to hold
	///   the expected amount of bytes returned by the chosen hash function.
	///
	/// # Parameters
	///
	/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
	/// - `input_len`: the length of the input data in bytes.
	/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
	///   function will write the result directly into this buffer.
		input_ptr: u32,
		input_len: u32,
		output_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::HashBlake256(input_len))?;
		Ok(ctx.compute_hash_on_intermediate_buffer(
			memory, blake2_256, input_ptr, input_len, output_ptr,
		)?)
	/// Computes the BLAKE2 128-bit hash on the given input buffer.
	///
	/// Returns the result directly into the given output buffer.
	///
	/// # Note
	///
	/// - The `input` and `output` buffer may overlap.
	/// - The output buffer is expected to hold at least 16 bytes (128 bits).
	/// - It is the callers responsibility to provide an output buffer that is large enough to hold
	///   the expected amount of bytes returned by the chosen hash function.
	///
	/// # Parameters
	///
	/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
	/// - `input_len`: the length of the input data in bytes.
	/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
	///   function will write the result directly into this buffer.
		input_ptr: u32,
		input_len: u32,
		output_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::HashBlake128(input_len))?;
		Ok(ctx.compute_hash_on_intermediate_buffer(
			memory, blake2_128, input_ptr, input_len, output_ptr,
		)?)
	/// Call into the chain extension provided by the chain if any.
	///
	/// Handling of the input values is up to the specific chain extension and so is the
	/// return value. The extension can decide to use the inputs as primitive inputs or as
	/// in/out arguments by interpreting them as pointers. Any caller of this function
	/// must therefore coordinate with the chain that it targets.
	///
	/// # Note
	///
	/// If no chain extension exists the contract will trap with the `NoChainExtension`
	/// module error.
		input_ptr: u32,
		input_len: u32,
		output_ptr: u32,
		output_len_ptr: u32,
	) -> Result<u32, TrapReason> {
		use crate::chain_extension::{ChainExtension, Environment, RetVal};
		if !<E::T as Config>::ChainExtension::enabled() {
			return Err(Error::<E::T>::NoChainExtension.into())
		let mut chain_extension = ctx.chain_extension.take().expect(
			"Constructor initializes with `Some`. This is the only place where it is set to `None`.\
			It is always reset to `Some` afterwards. qed"
		);
		let env =
			Environment::new(ctx, memory, id, input_ptr, input_len, output_ptr, output_len_ptr);
		let ret = match chain_extension.call(env)? {
			RetVal::Diverging { flags, data } =>
				Err(TrapReason::Return(ReturnData { flags: flags.bits(), data })),
		};
		ctx.chain_extension = Some(chain_extension);
		ret
	/// Emit a custom debug message.
	///
	/// No newlines are added to the supplied message.
	/// Specifying invalid UTF-8 triggers a trap.
	///
	/// This is a no-op if debug message recording is disabled which is always the case
	/// when the code is executing on-chain. The message is interpreted as UTF-8 and
	/// appended to the debug buffer which is then supplied to the calling RPC client.
	///
	/// # Note
	///
	/// Even though no action is taken when debug message recording is disabled there is still
	/// a non trivial overhead (and weight cost) associated with calling this function. Contract
	/// languages should remove calls to this function (either at runtime or compile time) when
	/// not being executed as an RPC. For example, they could allow users to disable logging
	/// through compile time flags (cargo features) for on-chain deployment. Additionally, the
	/// return value of this function can be cached in order to prevent further calls at runtime.
		str_ptr: u32,
		str_len: u32,
	) -> Result<ReturnCode, TrapReason> {
		ctx.charge_gas(RuntimeCosts::DebugMessage)?;
		if ctx.ext.append_debug_buffer("")? {
			let data = ctx.read_sandbox_memory(memory, str_ptr, str_len)?;
			let msg =
				core::str::from_utf8(&data).map_err(|_| <Error<E::T>>::DebugMessageInvalidUTF8)?;
			ctx.ext.append_debug_buffer(msg)?;
			return Ok(ReturnCode::Success)
		}
		Ok(ReturnCode::LoggingDisabled)
	/// Call some dispatchable of the runtime.
	///
	/// This function decodes the passed in data as the overarching `Call` type of the
	/// runtime and dispatches it. The weight as specified in the runtime is charged
	/// from the gas meter. Any weight refunds made by the dispatchable are considered.
	///
	/// The filter specified by `Config::CallFilter` is attached to the origin of
	/// the dispatched call.
	///
	/// # Parameters
	///
	/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
	/// - `input_len`: the length of the input data in bytes.
	///
	/// # Return Value
	///
	/// Returns `ReturnCode::Success` when the dispatchable was succesfully executed and
	/// returned `Ok`. When the dispatchable was exeuted but returned an error
	/// `ReturnCode::CallRuntimeReturnedError` is returned. The full error is not
	/// provided because it is not guaranteed to be stable.
	///
	/// # Comparison with `ChainExtension`
	///
	/// Just as a chain extension this API allows the runtime to extend the functionality
	/// of contracts. While making use of this function is generelly easier it cannot be
	/// used in call cases. Consider writing a chain extension if you need to do perform
	/// one of the following tasks:
	///
	/// - Return data.
	/// - Provide functionality **exclusively** to contracts.
	/// - Provide custom weights.
	/// - Avoid the need to keep the `Call` data structure stable.
	///
	/// # Unstable
	///
	/// This function is unstable and subject to change (or removal) in the future. Do not
	/// deploy a contract using it to a production chain.
	#[unstable]
		call_ptr: u32,
		call_len: u32,
	) -> Result<ReturnCode, TrapReason> {
		use frame_support::dispatch::{extract_actual_weight, GetDispatchInfo};
		ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?;
		let call: <E::T as Config>::RuntimeCall =
			ctx.read_sandbox_memory_as_unbounded(memory, call_ptr, call_len)?;
		let dispatch_info = call.get_dispatch_info();
		let charged = ctx.charge_gas(RuntimeCosts::CallRuntime(dispatch_info.weight))?;
		let result = ctx.ext.call_runtime(call);
		let actual_weight = extract_actual_weight(&result, &dispatch_info);
		ctx.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight));
		match result {
			Ok(_) => Ok(ReturnCode::Success),
			Err(_) => Ok(ReturnCode::CallRuntimeReturnedError),
		}
	/// Recovers the ECDSA public key from the given message hash and signature.
	///
	/// Writes the public key into the given output buffer.
	/// Assumes the secp256k1 curve.
	///
	/// # Parameters
	///
	/// - `signature_ptr`: the pointer into the linear memory where the signature is placed. Should
	///   be decodable as a 65 bytes. Traps otherwise.
	/// - `message_hash_ptr`: the pointer into the linear memory where the message hash is placed.
	///   Should be decodable as a 32 bytes. Traps otherwise.
	/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
	///   buffer should be 33 bytes. The function will write the result directly into this buffer.
	///
	/// # Errors
	///
	/// `ReturnCode::EcdsaRecoverFailed`
		signature_ptr: u32,
		message_hash_ptr: u32,
		output_ptr: u32,
	) -> Result<ReturnCode, TrapReason> {
		ctx.charge_gas(RuntimeCosts::EcdsaRecovery)?;

		let mut signature: [u8; 65] = [0; 65];
		ctx.read_sandbox_memory_into_buf(memory, signature_ptr, &mut signature)?;
		let mut message_hash: [u8; 32] = [0; 32];
		ctx.read_sandbox_memory_into_buf(memory, message_hash_ptr, &mut message_hash)?;

		let result = ctx.ext.ecdsa_recover(&signature, &message_hash);

		match result {
			Ok(pub_key) => {
				// Write the recovered compressed ecdsa public key back into the sandboxed output
				// buffer.
				ctx.write_sandbox_memory(memory, output_ptr, pub_key.as_ref())?;

				Ok(ReturnCode::Success)
			},
			Err(_) => Ok(ReturnCode::EcdsaRecoverFailed),
		}
	/// Replace the contract code at the specified address with new code.
	///
	/// # Note
	///
	/// There are a couple of important considerations which must be taken into account when
	/// using this API:
	///
	/// 1. The storage at the code address will remain untouched. This means that contract
	/// developers must ensure that the storage layout of the new code is compatible with that of
	/// the old code.
	///
	/// 2. Contracts using this API can't be assumed as having deterministic addresses. Said another
	/// way, when using this API you lose the guarantee that an address always identifies a specific
	/// code hash.
	/// 3. If a contract calls into itself after changing its code the new call would use
	/// the new code. However, if the original caller panics after returning from the sub call it
	/// would revert the changes made by `seal_set_code_hash` and the next caller would use
	/// the old code.
	///
	/// # Parameters
	///
	/// - `code_hash_ptr`: A pointer to the buffer that contains the new code hash.
	///
	/// # Errors
	///
	/// `ReturnCode::CodeNotFound`
	fn set_code_hash(ctx: _, memory: _, code_hash_ptr: u32) -> Result<ReturnCode, TrapReason> {
		ctx.charge_gas(RuntimeCosts::SetCodeHash)?;
		let code_hash: CodeHash<<E as Ext>::T> =
			ctx.read_sandbox_memory_as(memory, code_hash_ptr)?;
		match ctx.ext.set_code_hash(code_hash) {
				let code = Runtime::<E>::err_into_return_code(err)?;
				Ok(code)
			},
			Ok(()) => Ok(ReturnCode::Success),
	/// Calculates Ethereum address from the ECDSA compressed public key and stores
	/// it into the supplied buffer.
	///
	/// # Parameters
	///
	/// - `key_ptr`: a pointer to the ECDSA compressed public key. Should be decodable as a 33 bytes
	///   value. Traps otherwise.
	/// - `out_ptr`: the pointer into the linear memory where the output data is placed. The
	///   function will write the result directly into this buffer.
	///
	/// The value is stored to linear memory at the address pointed to by `out_ptr`.
	/// If the available space at `out_ptr` is less than the size of the value a trap is triggered.
	///
	/// # Errors
	///
	/// `ReturnCode::EcdsaRecoverFailed`
		key_ptr: u32,
		out_ptr: u32,
	) -> Result<ReturnCode, TrapReason> {
		ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?;
		let mut compressed_key: [u8; 33] = [0; 33];
		ctx.read_sandbox_memory_into_buf(memory, key_ptr, &mut compressed_key)?;
		let result = ctx.ext.ecdsa_to_eth_address(&compressed_key);
		match result {
			Ok(eth_address) => {
				ctx.write_sandbox_memory(memory, out_ptr, eth_address.as_ref())?;
				Ok(ReturnCode::Success)
			},
			Err(_) => Ok(ReturnCode::EcdsaRecoverFailed),
		}

	/// Returns the number of times the currently executing contract exists on the call stack in
	/// addition to the calling instance.
	///
	/// # Return Value
	///
	/// Returns 0 when there is no reentrancy.
	#[unstable]
	fn reentrance_count(ctx: _, memory: _) -> Result<u32, TrapReason> {
		ctx.charge_gas(RuntimeCosts::ReentrantCount)?;
	}

	/// Returns the number of times specified contract exists on the call stack. Delegated calls are
	/// not counted as separate calls.
	///
	/// # Parameters
	///
	/// - `account_ptr`: a pointer to the contract address.
	///
	/// # Return Value
	///
	/// Returns 0 when the contract does not exist on the call stack.
	#[unstable]
	fn account_reentrance_count(ctx: _, memory: _, account_ptr: u32) -> Result<u32, TrapReason> {
		ctx.charge_gas(RuntimeCosts::AccountEntranceCount)?;
		let account_id: <<E as Ext>::T as frame_system::Config>::AccountId =
			ctx.read_sandbox_memory_as(memory, account_ptr)?;
		Ok(ctx.ext.account_reentrance_count(&account_id))
	}

	/// Returns a nonce that is unique per contract instantiation.
	///
	/// The nonce is incremented for each succesful contract instantiation. This is a
	/// sensible default salt for contract instantiations.
	#[unstable]
	fn instantiation_nonce(ctx: _, _memory: _) -> Result<u64, TrapReason> {
		ctx.charge_gas(RuntimeCosts::InstantationNonce)?;
		Ok(ctx.ext.nonce())
	}