runtime.rs 84.3 KiB
Newer Older
			_ => ctx.read_sandbox_memory_as_unbounded(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(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.
	#[prefixed_alias]
	fn set_rent_allowance(
		ctx: Runtime<E>,
		_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)]
	#[prefixed_alias]
	fn set_rent_allowance(ctx: Runtime<E>, _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.
	#[prefixed_alias]
	fn rent_allowance(ctx: Runtime<E>, 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.
	#[prefixed_alias]
	fn block_number(ctx: Runtime<E>, 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(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(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(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(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.
	#[prefixed_alias]
	fn call_chain_extension(
		ctx: Runtime<E>,
		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, 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(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(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(signature_ptr, &mut signature)?;
		let mut message_hash: [u8; 32] = [0; 32];
		ctx.read_sandbox_memory_into_buf(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(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`
	#[prefixed_alias]
	fn set_code_hash(ctx: Runtime<E>, 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(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`
	#[prefixed_alias]
	fn ecdsa_to_eth_address(
		ctx: Runtime<E>,
		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(key_ptr, &mut compressed_key)?;
		let result = ctx.ext.ecdsa_to_eth_address(&compressed_key);
		match result {
			Ok(eth_address) => {
				ctx.write_sandbox_memory(out_ptr, eth_address.as_ref())?;
				Ok(ReturnCode::Success)
			},
			Err(_) => Ok(ReturnCode::EcdsaRecoverFailed),
		}