runtime.rs 81.1 KiB
Newer Older
	//                 directly into this buffer.
	[seal0] seal_hash_blake2_128(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
		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.
	[seal0] seal_call_chain_extension(
		input_ptr: u32,
		input_len: u32,
		output_ptr: u32,
		output_len_ptr: u32
	) -> u32 => {
		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::Converging(val) => Ok(val),
			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.
	[seal0] seal_debug_message(ctx, str_ptr: u32, str_len: u32) -> ReturnCode => {
		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 successfully executed and
	// returned `Ok`. When the dispatchable was executed 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 generally 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__] seal_call_runtime(ctx, call_ptr: u32, call_len: u32) -> ReturnCode => {
		use frame_support::{dispatch::GetDispatchInfo, weights::extract_actual_weight};
		ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?;
		let call: <E::T as Config>::Call = 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`
	[seal0] seal_ecdsa_recover(ctx, signature_ptr: u32, message_hash_ptr: u32, output_ptr: u32) -> ReturnCode => {
		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`
	[seal0] seal_set_code_hash(ctx, code_hash_ptr: u32) -> ReturnCode => {
		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) {
			Err(err) =>	{
				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`
	[seal0] seal_ecdsa_to_eth_address(ctx, key_ptr: u32, out_ptr: u32) -> ReturnCode => {
		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),
		}
	},