runtime.rs 70.6 KiB
Newer Older
	/// Computes the BLAKE2 128-bit hash on the given input buffer.
	/// See [`pallet_contracts_uapi::HostFn::hash_blake2_128`].
		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.
	/// See [`pallet_contracts_uapi::HostFn::call_chain_extension`].
		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 just drops the message with no 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.
	) -> Result<ReturnErrorCode, TrapReason> {
		let str_len = str_len.min(DebugBufferVec::<E::T>::bound() as u32);
		ctx.charge_gas(RuntimeCosts::DebugMessage(str_len))?;
		if ctx.ext.append_debug_buffer("") {
			let data = ctx.read_sandbox_memory(memory, str_ptr, str_len)?;
			if let Some(msg) = core::str::from_utf8(&data).ok() {
				ctx.ext.append_debug_buffer(msg);
			}
		Ok(ReturnErrorCode::Success)
	/// Call some dispatchable of the runtime.
	/// See [`frame_support::traits::call_runtime`].
	) -> Result<ReturnErrorCode, TrapReason> {
		use frame_support::dispatch::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)?;
		ctx.call_dispatchable::<CallRuntimeFailed>(
			call.get_dispatch_info(),
			RuntimeCosts::CallRuntime,
			|ctx| ctx.ext.call_runtime(call),
		)
	}

	/// Execute an XCM program locally, using the contract's address as the origin.
	/// See [`pallet_contracts_uapi::HostFn::execute_xcm`].
	fn xcm_execute(
		ctx: _,
		memory: _,
		msg_ptr: u32,
		msg_len: u32,
	) -> Result<ReturnErrorCode, TrapReason> {
		use frame_support::dispatch::DispatchInfo;
		use xcm::VersionedXcm;
		use xcm_builder::{ExecuteController, ExecuteControllerWeightInfo};

		ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?;
		let message: VersionedXcm<CallOf<E::T>> =
			ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?;
		ensure_executable::<E::T>(&message)?;

		let execute_weight =
			<<E::T as Config>::Xcm as ExecuteController<_, _>>::WeightInfo::execute();
		let weight = ctx.ext.gas_meter().gas_left().max(execute_weight);
		let dispatch_info = DispatchInfo { weight, ..Default::default() };

		ctx.call_dispatchable::<XcmExecutionFailed>(
			dispatch_info,
			RuntimeCosts::CallXcmExecute,
			|ctx| {
				let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into();
				let weight_used = <<E::T as Config>::Xcm>::execute(
					weight.saturating_sub(execute_weight),
				)?;
				Ok(Some(weight_used.saturating_add(execute_weight)).into())
			},
		)
	}

	/// Send an XCM program from the contract to the specified destination.
	/// See [`pallet_contracts_uapi::HostFn::send_xcm`].
	fn xcm_send(
		ctx: _,
		memory: _,
		dest_ptr: u32,
		msg_ptr: u32,
		msg_len: u32,
		output_ptr: u32,
	) -> Result<ReturnErrorCode, TrapReason> {
		use xcm::{VersionedLocation, VersionedXcm};
		use xcm_builder::{SendController, SendControllerWeightInfo};

		ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?;
Francisco Aguirre's avatar
Francisco Aguirre committed
		let dest: VersionedLocation = ctx.read_sandbox_memory_as(memory, dest_ptr)?;
		let message: VersionedXcm<()> =
			ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?;
		let weight = <<E::T as Config>::Xcm as SendController<_>>::WeightInfo::send();
		ctx.charge_gas(RuntimeCosts::CallRuntime(weight))?;
		let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into();

		match <<E::T as Config>::Xcm>::send(origin, dest.into(), message.into()) {
			Ok(message_id) => {
				ctx.write_sandbox_memory(memory, output_ptr, &message_id.encode())?;
				Ok(ReturnErrorCode::Success)
			Err(e) => {
				if ctx.ext.append_debug_buffer("") {
					ctx.ext.append_debug_buffer("seal0::xcm_send failed with: ");
					ctx.ext.append_debug_buffer(e.into());
				};
				Ok(ReturnErrorCode::XcmSendFailed)
	/// Recovers the ECDSA public key from the given message hash and signature.
	/// See [`pallet_contracts_uapi::HostFn::ecdsa_recover`].
		signature_ptr: u32,
		message_hash_ptr: u32,
		output_ptr: u32,
	) -> Result<ReturnErrorCode, 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(ReturnErrorCode::Success)
			Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed),
	/// Verify a sr25519 signature
	/// See [`pallet_contracts_uapi::HostFn::sr25519_verify`].
	fn sr25519_verify(
		ctx: _,
		memory: _,
		signature_ptr: u32,
		pub_key_ptr: u32,
		message_len: u32,
		message_ptr: u32,
	) -> Result<ReturnErrorCode, TrapReason> {
		ctx.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?;

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

		let mut pub_key: [u8; 32] = [0; 32];
		ctx.read_sandbox_memory_into_buf(memory, pub_key_ptr, &mut pub_key)?;

		let message: Vec<u8> = ctx.read_sandbox_memory(memory, message_ptr, message_len)?;

		if ctx.ext.sr25519_verify(&signature, &message, &pub_key) {
			Ok(ReturnErrorCode::Success)
			Ok(ReturnErrorCode::Sr25519VerifyFailed)
	/// Replace the contract code at the specified address with new code.
	/// See [`pallet_contracts_uapi::HostFn::set_code_hash`].
	fn set_code_hash(ctx: _, memory: _, code_hash_ptr: u32) -> Result<ReturnErrorCode, 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(ReturnErrorCode::Success),
	/// Calculates Ethereum address from the ECDSA compressed public key and stores
	/// See [`pallet_contracts_uapi::HostFn::ecdsa_to_eth_address`].
	) -> Result<ReturnErrorCode, 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(ReturnErrorCode::Success)
			Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed),

	/// Returns the number of times the currently executing contract exists on the call stack in
	/// addition to the calling instance.
	/// See [`pallet_contracts_uapi::HostFn::reentrance_count`].
	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.
	/// See [`pallet_contracts_uapi::HostFn::account_reentrance_count`].
	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.
	/// See [`pallet_contracts_uapi::HostFn::instantiation_nonce`].
	fn instantiation_nonce(ctx: _, _memory: _) -> Result<u64, TrapReason> {
		ctx.charge_gas(RuntimeCosts::InstantiationNonce)?;

	/// Adds a new delegate dependency to the contract.
	/// See [`pallet_contracts_uapi::HostFn::lock_delegate_dependency`].
	fn lock_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::LockDelegateDependency)?;
		let code_hash = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?;
		ctx.ext.lock_delegate_dependency(code_hash)?;
		Ok(())
	}

	/// Removes the delegate dependency from the contract.
	/// see [`pallet_contracts_uapi::HostFn::unlock_delegate_dependency`].
	fn unlock_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::UnlockDelegateDependency)?;
		let code_hash = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?;
		ctx.ext.unlock_delegate_dependency(&code_hash)?;