git.delta.rocks / unique-network / refs/commits / e8fdadb89923

difftreelog

source

pallets/contracts/src/chain_extension.rs14.7 KiBsourcehistory
1// This file is part of Substrate.23// Copyright (C) 2020 Parity Technologies (UK) Ltd.4// SPDX-License-Identifier: Apache-2.056// Licensed under the Apache License, Version 2.0 (the "License");7// you may not use this file except in compliance with the License.8// You may obtain a copy of the License at9//10// http://www.apache.org/licenses/LICENSE-2.011//12// Unless required by applicable law or agreed to in writing, software13// distributed under the License is distributed on an "AS IS" BASIS,14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.15// See the License for the specific language governing permissions and16// limitations under the License.1718//! A mechanism for runtime authors to augment the functionality of contracts.19//!20//! The runtime is able to call into any contract and retrieve the result using21//! [`bare_call`](crate::Pallet::bare_call). This already allows customization of runtime22//! behaviour by user generated code (contracts). However, often it is more straightforward23//! to allow the reverse behaviour: The contract calls into the runtime. We call the latter24//! one a "chain extension" because it allows the chain to extend the set of functions that are25//! callable by a contract.26//!27//! In order to create a chain extension the runtime author implements the [`ChainExtension`]28//! trait and declares it in this pallet's [configuration Trait](crate::Config). All types29//! required for this endeavour are defined or re-exported in this module. There is an30//! implementation on `()` which can be used to signal that no chain extension is available.31//!32//! # Security33//!34//! The chain author alone is responsible for the security of the chain extension.35//! This includes avoiding the exposure of exploitable functions and charging the36//! appropriate amount of weight. In order to do so benchmarks must be written and the37//! [`charge_weight`](Environment::charge_weight) function must be called **before**38//! carrying out any action that causes the consumption of the chargeable weight.39//! It cannot be overstated how delicate of a process the creation of a chain extension40//! is. Check whether using [`bare_call`](crate::Pallet::bare_call) suffices for the41//! use case at hand.42//!43//! # Benchmarking44//!45//! The builtin contract callable functions that pallet-contracts provides all have46//! benchmarks that determine the correct weight that an invocation of these functions47//! induces. In order to be able to charge the correct weight for the functions defined48//! by a chain extension benchmarks must be written, too. In the near future this crate49//! will provide the means for easier creation of those specialized benchmarks.50//!51//! # Example52//!53//! The ink! repository maintains an54//! [end-to-end example](https://github.com/paritytech/ink/tree/master/examples/rand-extension)55//! on how to use a chain extension in order to provide new features to ink! contracts.5657use crate::{58	Error,59	wasm::{Runtime, RuntimeToken},60};61use codec::Decode;62use frame_support::weights::Weight;63use sp_runtime::DispatchError;64use sp_std::{marker::PhantomData, vec::Vec};6566pub use frame_system::Config as SysConfig;67pub use pallet_contracts_primitives::ReturnFlags;68pub use sp_core::crypto::UncheckedFrom;69pub use crate::{Config, exec::Ext};70pub use state::Init as InitState;7172/// Result that returns a [`DispatchError`] on error.73pub type Result<T> = sp_std::result::Result<T, DispatchError>;7475/// A trait used to extend the set of contract callable functions.76///77/// In order to create a custom chain extension this trait must be implemented and supplied78/// to the pallet contracts configuration trait as the associated type of the same name.79/// Consult the [module documentation](self) for a general explanation of chain extensions.80pub trait ChainExtension<C: Config> {81	/// Call the chain extension logic.82	///83	/// This is the only function that needs to be implemented in order to write a84	/// chain extensions. It is called whenever a contract calls the `seal_call_chain_extension`85	/// imported wasm function.86	///87	/// # Parameters88	/// - `func_id`: The first argument to `seal_call_chain_extension`. Usually used to89	///		determine which function to realize.90	/// - `env`: Access to the remaining arguments and the execution environment.91	///92	/// # Return93	///94	/// In case of `Err` the contract execution is immediately suspended and the passed error95	/// is returned to the caller. Otherwise the value of [`RetVal`] determines the exit96	/// behaviour.97	fn call<E>(func_id: u32, env: Environment<E, InitState>) -> Result<RetVal>98	where99		E: Ext<T = C>,100		<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>;101102	/// Determines whether chain extensions are enabled for this chain.103	///104	/// The default implementation returns `true`. Therefore it is not necessary to overwrite105	/// this function when implementing a chain extension. In case of `false` the deployment of106	/// a contract that references `seal_call_chain_extension` will be denied and calling this107	/// function will return [`NoChainExtension`](Error::NoChainExtension) without first calling108	/// into [`call`](Self::call).109	fn enabled() -> bool {110		true111	}112}113114/// Implementation that indicates that no chain extension is available.115impl<C: Config> ChainExtension<C> for () {116	fn call<E>(_func_id: u32, mut _env: Environment<E, InitState>) -> Result<RetVal>117	where118		E: Ext<T = C>,119		<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,120	{121		// Never called since [`Self::enabled()`] is set to `false`. Because we want to122		// avoid panics at all costs we supply a sensible error value here instead123		// of an `unimplemented!`.124		Err(Error::<E::T>::NoChainExtension.into())125	}126127	fn enabled() -> bool {128		false129	}130}131132/// Determines the exit behaviour and return value of a chain extension.133pub enum RetVal {134	/// The chain extensions returns the supplied value to its calling contract.135	Converging(u32),136	/// The control does **not** return to the calling contract.137	///138	/// Use this to stop the execution of the contract when the chain extension returns.139	/// The semantic is the same as for calling `seal_return`: The control returns to140	/// the caller of the currently executing contract yielding the supplied buffer and141	/// flags.142	Diverging { flags: ReturnFlags, data: Vec<u8> },143}144145/// Grants the chain extension access to its parameters and execution environment.146///147/// It uses [typestate programming](https://docs.rust-embedded.org/book/static-guarantees/typestate-programming.html)148/// to enforce the correct usage of the parameters passed to the chain extension.149pub struct Environment<'a, 'b, E: Ext, S: state::State> {150	/// The actual data of this type.151	inner: Inner<'a, 'b, E>,152	/// `S` is only used in the type system but never as value.153	phantom: PhantomData<S>,154}155156/// Functions that are available in every state of this type.157impl<'a, 'b, E: Ext, S: state::State> Environment<'a, 'b, E, S>158where159	<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,160{161	/// Charge the passed `amount` of weight from the overall limit.162	///163	/// It returns `Ok` when there the remaining weight budget is larger than the passed164	/// `weight`. It returns `Err` otherwise. In this case the chain extension should165	/// abort the execution and pass through the error.166	///167	/// # Note168	///169	/// Weight is synonymous with gas in substrate.170	pub fn charge_weight(&mut self, amount: Weight) -> Result<()> {171		self.inner172			.runtime173			.charge_gas(RuntimeToken::ChainExtension(amount))174			.map(|_| ())175	}176177	/// Grants access to the execution environment of the current contract call.178	///179	/// Consult the functions on the returned type before re-implementing those functions.180	pub fn ext(&mut self) -> &mut E {181		self.inner.runtime.ext()182	}183}184185/// Functions that are only available in the initial state of this type.186///187/// Those are the functions that determine how the arguments to the chain extensions188/// should be consumed.189impl<'a, 'b, E: Ext> Environment<'a, 'b, E, state::Init> {190	/// Creates a new environment for consumption by a chain extension.191	///192	/// It is only available to this crate because only the wasm runtime module needs to193	/// ever create this type. Chain extensions merely consume it.194	pub(crate) fn new(195		runtime: &'a mut Runtime<'b, E>,196		input_ptr: u32,197		input_len: u32,198		output_ptr: u32,199		output_len_ptr: u32,200	) -> Self {201		Environment {202			inner: Inner {203				runtime,204				input_ptr,205				input_len,206				output_ptr,207				output_len_ptr,208			},209			phantom: PhantomData,210		}211	}212213	/// Use all arguments as integer values.214	pub fn only_in(self) -> Environment<'a, 'b, E, state::OnlyIn> {215		Environment {216			inner: self.inner,217			phantom: PhantomData,218		}219	}220221	/// Use input arguments as integer and output arguments as pointer to a buffer.222	pub fn prim_in_buf_out(self) -> Environment<'a, 'b, E, state::PrimInBufOut> {223		Environment {224			inner: self.inner,225			phantom: PhantomData,226		}227	}228229	/// Use input and output arguments as pointers to a buffer.230	pub fn buf_in_buf_out(self) -> Environment<'a, 'b, E, state::BufInBufOut> {231		Environment {232			inner: self.inner,233			phantom: PhantomData,234		}235	}236}237238/// Functions to use the input arguments as integers.239impl<'a, 'b, E: Ext, S: state::PrimIn> Environment<'a, 'b, E, S> {240	/// The `input_ptr` argument.241	pub fn val0(&self) -> u32 {242		self.inner.input_ptr243	}244245	/// The `input_len` argument.246	pub fn val1(&self) -> u32 {247		self.inner.input_len248	}249}250251/// Functions to use the output arguments as integers.252impl<'a, 'b, E: Ext, S: state::PrimOut> Environment<'a, 'b, E, S> {253	/// The `output_ptr` argument.254	pub fn val2(&self) -> u32 {255		self.inner.output_ptr256	}257258	/// The `output_len_ptr` argument.259	pub fn val3(&self) -> u32 {260		self.inner.output_len_ptr261	}262}263264/// Functions to use the input arguments as pointer to a buffer.265impl<'a, 'b, E: Ext, S: state::BufIn> Environment<'a, 'b, E, S>266where267	<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,268{269	/// Reads `min(max_len, in_len)` from contract memory.270	///271	/// This does **not** charge any weight. The caller must make sure that the an272	/// appropriate amount of weight is charged **before** reading from contract memory.273	/// The reason for that is that usually the costs for reading data and processing274	/// said data cannot be separated in a benchmark. Therefore a chain extension would275	/// charge the overall costs either using `max_len` (worst case approximation) or using276	/// [`in_len()`](Self::in_len).277	pub fn read(&self, max_len: u32) -> Result<Vec<u8>> {278		self.inner279			.runtime280			.read_sandbox_memory(self.inner.input_ptr, self.inner.input_len.min(max_len))281	}282283	/// Reads `min(buffer.len(), in_len) from contract memory.284	///285	/// This takes a mutable pointer to a buffer fills it with data and shrinks it to286	/// the size of the actual data. Apart from supporting pre-allocated buffers it is287	/// equivalent to to [`read()`](Self::read).288	pub fn read_into(&self, buffer: &mut &mut [u8]) -> Result<()> {289		let len = buffer.len();290		let sliced = {291			let buffer = core::mem::take(buffer);292			&mut buffer[..len.min(self.inner.input_len as usize)]293		};294		self.inner295			.runtime296			.read_sandbox_memory_into_buf(self.inner.input_ptr, sliced)?;297		*buffer = sliced;298		Ok(())299	}300301	/// Reads `in_len` from contract memory and scale decodes it.302	///303	/// This function is secure and recommended for all input types of fixed size304	/// as long as the cost of reading the memory is included in the overall already charged305	/// weight of the chain extension. This should usually be the case when fixed input types306	/// are used. Non fixed size types (like everything using `Vec`) usually need to use307	/// [`in_len()`](Self::in_len) in order to properly charge the necessary weight.308	pub fn read_as<T: Decode>(&mut self) -> Result<T> {309		self.inner310			.runtime311			.read_sandbox_memory_as(self.inner.input_ptr, self.inner.input_len)312	}313314	/// The length of the input as passed in as `input_len`.315	///316	/// A chain extension would use this value to calculate the dynamic part of its317	/// weight. For example a chain extension that calculates the hash of some passed in318	/// bytes would use `in_len` to charge the costs of hashing that amount of bytes.319	/// This also subsumes the act of copying those bytes as a benchmarks measures both.320	pub fn in_len(&self) -> u32 {321		self.inner.input_len322	}323}324325/// Functions to use the output arguments as pointer to a buffer.326impl<'a, 'b, E: Ext, S: state::BufOut> Environment<'a, 'b, E, S>327where328	<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,329{330	/// Write the supplied buffer to contract memory.331	///332	/// If the contract supplied buffer is smaller than the passed `buffer` an `Err` is returned.333	/// If `allow_skip` is set to true the contract is allowed to skip the copying of the buffer334	/// by supplying the guard value of `u32::max_value()` as `out_ptr`. The335	/// `weight_per_byte` is only charged when the write actually happens and is not skipped or336	/// failed due to a too small output buffer.337	pub fn write(338		&mut self,339		buffer: &[u8],340		allow_skip: bool,341		weight_per_byte: Option<Weight>,342	) -> Result<()> {343		self.inner.runtime.write_sandbox_output(344			self.inner.output_ptr,345			self.inner.output_len_ptr,346			buffer,347			allow_skip,348			|len| {349				weight_per_byte.map(|w| RuntimeToken::ChainExtension(w.saturating_mul(len.into())))350			},351		)352	}353}354355/// The actual data of an `Environment`.356///357/// All data is put into this struct to easily pass it around as part of the typestate358/// pattern. Also it creates the opportunity to box this struct in the future in case it359/// gets too large.360struct Inner<'a, 'b, E: Ext> {361	/// The runtime contains all necessary functions to interact with the running contract.362	runtime: &'a mut Runtime<'b, E>,363	/// Verbatim argument passed to `seal_call_chain_extension`.364	input_ptr: u32,365	/// Verbatim argument passed to `seal_call_chain_extension`.366	input_len: u32,367	/// Verbatim argument passed to `seal_call_chain_extension`.368	output_ptr: u32,369	/// Verbatim argument passed to `seal_call_chain_extension`.370	output_len_ptr: u32,371}372373/// Private submodule with public types to prevent other modules from naming them.374mod state {375	pub trait State {}376377	pub trait PrimIn: State {}378	pub trait PrimOut: State {}379	pub trait BufIn: State {}380	pub trait BufOut: State {}381382	/// The initial state of an [`Environment`](`super::Environment`).383	/// See [typestate programming](https://docs.rust-embedded.org/book/static-guarantees/typestate-programming.html).384	pub enum Init {}385	pub enum OnlyIn {}386	pub enum PrimInBufOut {}387	pub enum BufInBufOut {}388389	impl State for Init {}390	impl State for OnlyIn {}391	impl State for PrimInBufOut {}392	impl State for BufInBufOut {}393394	impl PrimIn for OnlyIn {}395	impl PrimOut for OnlyIn {}396	impl PrimIn for PrimInBufOut {}397	impl BufOut for PrimInBufOut {}398	impl BufIn for BufInBufOut {}399	impl BufOut for BufInBufOut {}400}