referrerpolicy=no-referrer-when-downgrade

pallet_contracts_proc_macro/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// 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 at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// 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 and
16// limitations under the License.
17
18//! Procedural macros used in the contracts module.
19//!
20//! Most likely you should use the [`#[define_env]`][`macro@define_env`] attribute macro which hides
21//! boilerplate of defining external environment for a wasm module.
22
23use core::cmp::Reverse;
24use proc_macro::TokenStream;
25use proc_macro2::{Span, TokenStream as TokenStream2};
26use quote::{quote, quote_spanned, ToTokens};
27use syn::{
28	parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DeriveInput,
29	Fields, FnArg, Ident,
30};
31
32/// This derives `Debug` for a struct where each field must be of some numeric type.
33/// It interprets each field as its represents some weight and formats it as times so that
34/// it is readable by humans.
35#[proc_macro_derive(WeightDebug)]
36pub fn derive_weight_debug(input: TokenStream) -> TokenStream {
37	let input = parse_macro_input!(input as DeriveInput);
38	let name = &input.ident;
39	let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
40	let data = if let Data::Struct(data) = &input.data {
41		data
42	} else {
43		return quote_spanned! {
44			name.span() =>
45			compile_error!("WeightDebug is only supported for structs.");
46		}
47		.into();
48	};
49
50	let fields = match &data.fields {
51		Fields::Named(fields) => {
52			let recurse = fields.named.iter().filter_map(|f| {
53				let name = f.ident.as_ref()?;
54				if name.to_string().starts_with('_') {
55					return None;
56				}
57				let ret = quote_spanned! { f.span() =>
58					formatter.field(stringify!(#name), &HumanWeight(self.#name));
59				};
60				Some(ret)
61			});
62			quote! {
63				#( #recurse )*
64			}
65		},
66		Fields::Unnamed(fields) => quote_spanned! {
67			fields.span() =>
68			compile_error!("Unnamed fields are not supported")
69		},
70		Fields::Unit => quote!(),
71	};
72
73	let tokens = quote! {
74		impl #impl_generics ::core::fmt::Debug for #name #ty_generics #where_clause {
75			fn fmt(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> core::fmt::Result {
76				use ::sp_runtime::{FixedPointNumber, FixedU128 as Fixed};
77				use ::core::{fmt, write};
78
79				struct HumanWeight(Weight);
80
81				impl fmt::Debug for HumanWeight {
82					fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
83						if self.0.ref_time() > 1_000_000_000 {
84							write!(
85								formatter,
86								"{} ms, {} bytes",
87								Fixed::saturating_from_rational(self.0.ref_time(), 1_000_000_000).into_inner() / Fixed::accuracy(),
88								self.0.proof_size()
89							)
90						} else if self.0.ref_time() > 1_000_000 {
91							write!(
92								formatter,
93								"{} µs, {} bytes",
94								Fixed::saturating_from_rational(self.0.ref_time(), 1_000_000).into_inner() / Fixed::accuracy(),
95								self.0.proof_size()
96							)
97						} else if self.0.ref_time() > 1_000 {
98							write!(
99								formatter,
100								"{} ns, {} bytes",
101								Fixed::saturating_from_rational(self.0.ref_time(), 1_000).into_inner() / Fixed::accuracy(),
102								self.0.proof_size()
103							)
104						} else {
105							write!(formatter, "{} ps, {} bytes", self.0.ref_time(), self.0.proof_size())
106						}
107					}
108				}
109
110				let mut formatter = formatter.debug_struct(stringify!(#name));
111				#fields
112				formatter.finish()
113			}
114		}
115	};
116
117	tokens.into()
118}
119
120/// Parsed environment definition.
121struct EnvDef {
122	host_funcs: Vec<HostFn>,
123}
124
125/// Parsed host function definition.
126struct HostFn {
127	item: syn::ItemFn,
128	version: u8,
129	name: String,
130	returns: HostFnReturn,
131	is_stable: bool,
132	alias_to: Option<String>,
133	/// Formulating the predicate inverted makes the expression using it simpler.
134	not_deprecated: bool,
135	cfg: Option<syn::Attribute>,
136}
137
138enum HostFnReturn {
139	Unit,
140	U32,
141	U64,
142	ReturnCode,
143}
144
145impl HostFnReturn {
146	fn to_wasm_sig(&self) -> TokenStream2 {
147		let ok = match self {
148			Self::Unit => quote! { () },
149			Self::U32 | Self::ReturnCode => quote! { ::core::primitive::u32 },
150			Self::U64 => quote! { ::core::primitive::u64 },
151		};
152		quote! {
153			::core::result::Result<#ok, ::wasmi::Error>
154		}
155	}
156}
157
158impl ToTokens for HostFn {
159	fn to_tokens(&self, tokens: &mut TokenStream2) {
160		self.item.to_tokens(tokens);
161	}
162}
163
164impl HostFn {
165	pub fn try_from(mut item: syn::ItemFn) -> syn::Result<Self> {
166		let err = |span, msg| {
167			let msg = format!("Invalid host function definition.\n{}", msg);
168			syn::Error::new(span, msg)
169		};
170
171		// process attributes
172		let msg =
173			"Only #[version(<u8>)], #[unstable], #[prefixed_alias], #[cfg], #[mutating] and #[deprecated] attributes are allowed.";
174		let span = item.span();
175		let mut attrs = item.attrs.clone();
176		attrs.retain(|a| !a.path().is_ident("doc"));
177		let mut maybe_version = None;
178		let mut is_stable = true;
179		let mut alias_to = None;
180		let mut not_deprecated = true;
181		let mut mutating = false;
182		let mut cfg = None;
183		while let Some(attr) = attrs.pop() {
184			let ident = attr.path().get_ident().ok_or(err(span, msg))?.to_string();
185			match ident.as_str() {
186				"version" => {
187					if maybe_version.is_some() {
188						return Err(err(span, "#[version] can only be specified once"));
189					}
190					maybe_version =
191						Some(attr.parse_args::<syn::LitInt>().and_then(|lit| lit.base10_parse())?);
192				},
193				"unstable" => {
194					if !is_stable {
195						return Err(err(span, "#[unstable] can only be specified once"));
196					}
197					is_stable = false;
198				},
199				"prefixed_alias" => {
200					alias_to = Some(item.sig.ident.to_string());
201					item.sig.ident = syn::Ident::new(
202						&format!("seal_{}", &item.sig.ident.to_string()),
203						item.sig.ident.span(),
204					);
205				},
206				"deprecated" => {
207					if !not_deprecated {
208						return Err(err(span, "#[deprecated] can only be specified once"));
209					}
210					not_deprecated = false;
211				},
212				"mutating" => {
213					if mutating {
214						return Err(err(span, "#[mutating] can only be specified once"));
215					}
216					mutating = true;
217				},
218				"cfg" => {
219					if cfg.is_some() {
220						return Err(err(span, "#[cfg] can only be specified once"));
221					}
222					cfg = Some(attr);
223				},
224				id => return Err(err(span, &format!("Unsupported attribute \"{id}\". {msg}"))),
225			}
226		}
227
228		if mutating {
229			let stmt = syn::parse_quote! {
230				if ctx.ext().is_read_only() {
231					return Err(Error::<E::T>::StateChangeDenied.into());
232				}
233			};
234			item.block.stmts.insert(0, stmt);
235		}
236
237		let name = item.sig.ident.to_string();
238
239		if !(is_stable || not_deprecated) {
240			return Err(err(span, "#[deprecated] is mutually exclusive with #[unstable]"));
241		}
242
243		// process arguments: The first and second args are treated differently (ctx, memory)
244		// they must exist and be `ctx: _` and `memory: _`.
245		let msg = "Every function must start with two inferred parameters: ctx: _ and memory: _";
246		let special_args = item
247			.sig
248			.inputs
249			.iter()
250			.take(2)
251			.enumerate()
252			.map(|(i, arg)| is_valid_special_arg(i, arg))
253			.fold(0u32, |acc, valid| if valid { acc + 1 } else { acc });
254
255		if special_args != 2 {
256			return Err(err(span, msg));
257		}
258
259		// process return type
260		let msg = r#"Should return one of the following:
261				- Result<(), TrapReason>,
262				- Result<ReturnErrorCode, TrapReason>,
263				- Result<u64, TrapReason>,
264				- Result<u32, TrapReason>"#;
265		let ret_ty = match item.clone().sig.output {
266			syn::ReturnType::Type(_, ty) => Ok(ty.clone()),
267			_ => Err(err(span, &msg)),
268		}?;
269		match *ret_ty {
270			syn::Type::Path(tp) => {
271				let result = &tp.path.segments.last().ok_or(err(span, &msg))?;
272				let (id, span) = (result.ident.to_string(), result.ident.span());
273				id.eq(&"Result".to_string()).then_some(()).ok_or(err(span, &msg))?;
274
275				match &result.arguments {
276					syn::PathArguments::AngleBracketed(group) => {
277						if group.args.len() != 2 {
278							return Err(err(span, &msg));
279						};
280
281						let arg2 = group.args.last().ok_or(err(span, &msg))?;
282
283						let err_ty = match arg2 {
284							syn::GenericArgument::Type(ty) => Ok(ty.clone()),
285							_ => Err(err(arg2.span(), &msg)),
286						}?;
287
288						match err_ty {
289							syn::Type::Path(tp) => Ok(tp
290								.path
291								.segments
292								.first()
293								.ok_or(err(arg2.span(), &msg))?
294								.ident
295								.to_string()),
296							_ => Err(err(tp.span(), &msg)),
297						}?
298						.eq("TrapReason")
299						.then_some(())
300						.ok_or(err(span, &msg))?;
301
302						let arg1 = group.args.first().ok_or(err(span, &msg))?;
303						let ok_ty = match arg1 {
304							syn::GenericArgument::Type(ty) => Ok(ty.clone()),
305							_ => Err(err(arg1.span(), &msg)),
306						}?;
307						let ok_ty_str = match ok_ty {
308							syn::Type::Path(tp) => Ok(tp
309								.path
310								.segments
311								.first()
312								.ok_or(err(arg1.span(), &msg))?
313								.ident
314								.to_string()),
315							syn::Type::Tuple(tt) => {
316								if !tt.elems.is_empty() {
317									return Err(err(arg1.span(), &msg));
318								};
319								Ok("()".to_string())
320							},
321							_ => Err(err(ok_ty.span(), &msg)),
322						}?;
323						let returns = match ok_ty_str.as_str() {
324							"()" => Ok(HostFnReturn::Unit),
325							"u32" => Ok(HostFnReturn::U32),
326							"u64" => Ok(HostFnReturn::U64),
327							"ReturnErrorCode" => Ok(HostFnReturn::ReturnCode),
328							_ => Err(err(arg1.span(), &msg)),
329						}?;
330
331						Ok(Self {
332							item,
333							version: maybe_version.unwrap_or_default(),
334							name,
335							returns,
336							is_stable,
337							alias_to,
338							not_deprecated,
339							cfg,
340						})
341					},
342					_ => Err(err(span, &msg)),
343				}
344			},
345			_ => Err(err(span, &msg)),
346		}
347	}
348
349	fn module(&self) -> String {
350		format!("seal{}", self.version)
351	}
352}
353
354impl EnvDef {
355	pub fn try_from(item: syn::ItemMod) -> syn::Result<Self> {
356		let span = item.span();
357		let err = |msg| syn::Error::new(span, msg);
358		let items = &item
359			.content
360			.as_ref()
361			.ok_or(err("Invalid environment definition, expected `mod` to be inlined."))?
362			.1;
363
364		let extract_fn = |i: &syn::Item| match i {
365			syn::Item::Fn(i_fn) => Some(i_fn.clone()),
366			_ => None,
367		};
368
369		let selector = |a: &syn::Attribute| a.path().is_ident("prefixed_alias");
370
371		let aliases = items
372			.iter()
373			.filter_map(extract_fn)
374			.filter(|i| i.attrs.iter().any(selector))
375			.map(|i| HostFn::try_from(i));
376
377		let host_funcs = items
378			.iter()
379			.filter_map(extract_fn)
380			.map(|mut i| {
381				i.attrs.retain(|i| !selector(i));
382				i
383			})
384			.map(|i| HostFn::try_from(i))
385			.chain(aliases)
386			.collect::<Result<Vec<_>, _>>()?;
387
388		Ok(Self { host_funcs })
389	}
390}
391
392fn is_valid_special_arg(idx: usize, arg: &FnArg) -> bool {
393	let FnArg::Typed(pat) = arg else { return false };
394	let ident =
395		if let syn::Pat::Ident(ref ident) = *pat.pat { &ident.ident } else { return false };
396	let name_ok = match idx {
397		0 => ident == "ctx" || ident == "_ctx",
398		1 => ident == "memory" || ident == "_memory",
399		_ => false,
400	};
401	if !name_ok {
402		return false;
403	}
404	matches!(*pat.ty, syn::Type::Infer(_))
405}
406
407fn expand_func_doc(func: &HostFn) -> TokenStream2 {
408	// Remove auxiliary args: `ctx: _` and `memory: _`
409	let func_decl = {
410		let mut sig = func.item.sig.clone();
411		sig.inputs = sig
412			.inputs
413			.iter()
414			.skip(2)
415			.map(|p| p.clone())
416			.collect::<Punctuated<FnArg, Comma>>();
417		sig.to_token_stream()
418	};
419	let func_doc = {
420		let func_docs = if let Some(origin_fn) = &func.alias_to {
421			let alias_doc = format!(
422				"This is just an alias function to [`{0}()`][`Self::{0}`] with backwards-compatible prefixed identifier.",
423				origin_fn,
424			);
425			quote! { #[doc = #alias_doc] }
426		} else {
427			let docs = func.item.attrs.iter().filter(|a| a.path().is_ident("doc")).map(|d| {
428				let docs = d.to_token_stream();
429				quote! { #docs }
430			});
431			quote! { #( #docs )* }
432		};
433		let deprecation_notice = if !func.not_deprecated {
434			let warning = "\n # Deprecated\n\n \
435								This function is deprecated and will be removed in future versions.\n \
436								No new code or contracts with this API can be deployed.";
437			quote! { #[doc = #warning] }
438		} else {
439			quote! {}
440		};
441		let import_notice = {
442			let info = format!(
443				"\n# Wasm Import Statement\n```wat\n(import \"seal{}\" \"{}\" (func ...))\n```",
444				func.version, func.name,
445			);
446			quote! { #[doc = #info] }
447		};
448		let unstable_notice = if !func.is_stable {
449			let warning = "\n # Unstable\n\n \
450								This function is unstable and it is a subject to change (or removal) in the future.\n \
451								Do not deploy a contract using it to a production chain.";
452			quote! { #[doc = #warning] }
453		} else {
454			quote! {}
455		};
456		quote! {
457			#deprecation_notice
458			#func_docs
459			#import_notice
460			#unstable_notice
461		}
462	};
463	quote! {
464		#func_doc
465		#func_decl;
466	}
467}
468
469/// Expands documentation for host functions.
470fn expand_docs(def: &EnvDef) -> TokenStream2 {
471	// Create the `Current` trait with only the newest versions
472	// we sort so that only the newest versions make it into `docs`
473	let mut current_docs = std::collections::HashMap::new();
474	let mut funcs: Vec<_> = def.host_funcs.iter().filter(|f| f.alias_to.is_none()).collect();
475	funcs.sort_unstable_by_key(|func| Reverse(func.version));
476	for func in funcs {
477		if current_docs.contains_key(&func.name) {
478			continue;
479		}
480		current_docs.insert(func.name.clone(), expand_func_doc(&func));
481	}
482	let current_docs = current_docs.values();
483
484	// Create the `legacy` module with all functions
485	// Maps from version to list of functions that have this version
486	let mut legacy_doc = std::collections::BTreeMap::<u8, Vec<TokenStream2>>::new();
487	for func in def.host_funcs.iter() {
488		legacy_doc.entry(func.version).or_default().push(expand_func_doc(&func));
489	}
490	let legacy_doc = legacy_doc.into_iter().map(|(version, funcs)| {
491		let doc = format!("All functions available in the **seal{}** module", version);
492		let version = Ident::new(&format!("Version{version}"), Span::call_site());
493		quote! {
494			#[doc = #doc]
495			pub trait #version {
496				#( #funcs )*
497			}
498		}
499	});
500
501	quote! {
502		/// Contains only the latest version of each function.
503		///
504		/// In reality there are more functions available but they are all obsolete: When a function
505		/// is updated a new **version** is added and the old versions stays available as-is.
506		/// We only list the newest version here. Some functions are available under additional
507		/// names (aliases) for historic reasons which are omitted here.
508		///
509		/// If you want an overview of all the functions available to a contact all you need
510		/// to look at is this trait. It contains only the latest version of each
511		/// function and no aliases. If you are writing a contract(language) from scratch
512		/// this is where you should look at.
513		pub trait Current {
514			#( #current_docs )*
515		}
516		#( #legacy_doc )*
517	}
518}
519
520/// Expands environment definition.
521/// Should generate source code for:
522///  - implementations of the host functions to be added to the wasm runtime environment (see
523///    `expand_impls()`).
524fn expand_env(def: &EnvDef, docs: bool) -> TokenStream2 {
525	let impls = expand_impls(def);
526	let docs = docs.then(|| expand_docs(def)).unwrap_or(TokenStream2::new());
527	let stable_api_count = def.host_funcs.iter().filter(|f| f.is_stable).count();
528
529	quote! {
530		pub struct Env;
531
532		#[cfg(test)]
533		pub const STABLE_API_COUNT: usize = #stable_api_count;
534
535		#impls
536		/// Documentation of the API (host functions) available to contracts.
537		///
538		/// The `Current` trait might be the most useful doc to look at. The versioned
539		/// traits only exist for reference: If trying to find out if a specific version of
540		/// `pallet-contracts` contains a certain function.
541		///
542		/// # Note
543		///
544		/// This module is not meant to be used by any code. Rather, it is meant to be
545		/// consumed by humans through rustdoc.
546		#[cfg(doc)]
547		pub mod api_doc {
548			use super::{TrapReason, ReturnErrorCode};
549			#docs
550		}
551	}
552}
553
554/// Generates for every host function:
555///   - real implementation, to register it in the contract execution environment;
556///   - dummy implementation, to be used as mocks for contract validation step.
557fn expand_impls(def: &EnvDef) -> TokenStream2 {
558	let impls = expand_functions(def, ExpandMode::Impl);
559	let dummy_impls = expand_functions(def, ExpandMode::MockImpl);
560	let bench_impls = expand_functions(def, ExpandMode::BenchImpl);
561
562	quote! {
563		impl<'a, E: Ext> crate::wasm::Environment<crate::wasm::runtime::Runtime<'a, E>> for Env
564		{
565			fn define(
566				store: &mut ::wasmi::Store<crate::wasm::Runtime<E>>,
567				linker: &mut ::wasmi::Linker<crate::wasm::Runtime<E>>,
568				allow_unstable: AllowUnstableInterface,
569				allow_deprecated: AllowDeprecatedInterface,
570			) -> Result<(),::wasmi::errors::LinkerError> {
571				#impls
572				Ok(())
573			}
574		}
575
576		#[cfg(feature = "runtime-benchmarks")]
577		pub struct BenchEnv<E>(::core::marker::PhantomData<E>);
578
579		#[cfg(feature = "runtime-benchmarks")]
580		impl<E: Ext> BenchEnv<E> {
581			#bench_impls
582		}
583
584		impl crate::wasm::Environment<()> for Env
585		{
586			fn define(
587				store: &mut ::wasmi::Store<()>,
588				linker: &mut ::wasmi::Linker<()>,
589				allow_unstable: AllowUnstableInterface,
590				allow_deprecated: AllowDeprecatedInterface,
591			) -> Result<(), ::wasmi::errors::LinkerError> {
592				#dummy_impls
593				Ok(())
594			}
595		}
596	}
597}
598
599enum ExpandMode {
600	Impl,
601	BenchImpl,
602	MockImpl,
603}
604
605impl ExpandMode {
606	fn expand_blocks(&self) -> bool {
607		match *self {
608			ExpandMode::Impl | ExpandMode::BenchImpl => true,
609			ExpandMode::MockImpl => false,
610		}
611	}
612
613	fn host_state(&self) -> TokenStream2 {
614		match *self {
615			ExpandMode::Impl | ExpandMode::BenchImpl => quote! { crate::wasm::runtime::Runtime<E> },
616			ExpandMode::MockImpl => quote! { () },
617		}
618	}
619}
620
621fn expand_functions(def: &EnvDef, expand_mode: ExpandMode) -> TokenStream2 {
622	let impls = def.host_funcs.iter().map(|f| {
623		// skip the context and memory argument
624		let params = f.item.sig.inputs.iter().skip(2);
625		let module = f.module();
626		let cfg = &f.cfg;
627		let name = &f.name;
628		let body = &f.item.block;
629		let wasm_output = f.returns.to_wasm_sig();
630		let output = &f.item.sig.output;
631		let is_stable = f.is_stable;
632		let not_deprecated = f.not_deprecated;
633
634		// wrapped host function body call with host function traces
635		// see https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/contracts#host-function-tracing
636		let wrapped_body_with_trace = {
637			let trace_fmt_args = params.clone().filter_map(|arg| match arg {
638				syn::FnArg::Receiver(_) => None,
639				syn::FnArg::Typed(p) => {
640					match *p.pat.clone() {
641						syn::Pat::Ident(ref pat_ident) => Some(pat_ident.ident.clone()),
642						_ => None,
643					}
644				},
645			});
646
647			let params_fmt_str = trace_fmt_args.clone().map(|s| format!("{s}: {{:?}}")).collect::<Vec<_>>().join(", ");
648			let trace_fmt_str = format!("{}::{}({}) = {{:?}}\n", module, name, params_fmt_str);
649
650			quote! {
651				let result = #body;
652				if ::log::log_enabled!(target: "runtime::contracts::strace", ::log::Level::Trace) {
653						use core::fmt::Write;
654						let mut msg = alloc::string::String::default();
655						let _ = core::write!(&mut msg, #trace_fmt_str, #( #trace_fmt_args, )* result);
656						ctx.ext().append_debug_buffer(&msg);
657				}
658				result
659			}
660		};
661
662		// If we don't expand blocks (implementing for `()`) we change a few things:
663		// - We replace any code by unreachable!
664		// - Allow unused variables as the code that uses is not expanded
665		// - We don't need to map the error as we simply panic if they code would ever be executed
666		let expand_blocks = expand_mode.expand_blocks();
667		let inner = match expand_mode {
668			ExpandMode::Impl => {
669				quote! { || #output {
670					let (memory, ctx) = __caller__
671						.data()
672						.memory()
673						.expect("Memory must be set when setting up host data; qed")
674						.data_and_store_mut(&mut __caller__);
675					#wrapped_body_with_trace
676				} }
677			},
678			ExpandMode::BenchImpl => {
679				let body = &body.stmts;
680				quote!{
681					#(#body)*
682				}
683			},
684			ExpandMode::MockImpl => {
685				quote! { || -> #wasm_output {
686					// This is part of the implementation for `Environment<()>` which is not
687					// meant to be actually executed. It is only for validation which will
688					// never call host functions.
689					::core::unreachable!()
690				} }
691			},
692		};
693
694		let into_host = if expand_blocks {
695			quote! {
696				|reason| {
697					::wasmi::Error::host(reason)
698				}
699			}
700		} else {
701			quote! {
702				|reason| { reason }
703			}
704		};
705		let allow_unused =  if expand_blocks {
706			quote! { }
707		} else {
708			quote! { #[allow(unused_variables)] }
709		};
710		let sync_gas_before = if expand_blocks {
711			quote! {
712				// Write gas from wasmi into pallet-contracts before entering the host function.
713				let __gas_left_before__ = {
714					let fuel =
715						__caller__.get_fuel().expect("Fuel metering is enabled; qed");
716					__caller__
717						.data_mut()
718						.ext()
719						.gas_meter_mut()
720						.sync_from_executor(fuel)
721						.map_err(TrapReason::from)
722						.map_err(#into_host)?
723				};
724
725				// Charge gas for host function execution.
726				__caller__.data_mut().charge_gas(crate::wasm::RuntimeCosts::HostFn)
727						.map_err(TrapReason::from)
728						.map_err(#into_host)?;
729			}
730		} else {
731			quote! { }
732		};
733		// Write gas from pallet-contracts into wasmi after leaving the host function.
734		let sync_gas_after = if expand_blocks {
735			quote! {
736				let fuel = __caller__
737					.data_mut()
738					.ext()
739					.gas_meter_mut()
740					.sync_to_executor(__gas_left_before__)
741					.map_err(|err| {
742						let err = TrapReason::from(err);
743						wasmi::Error::host(err)
744					})?;
745				 __caller__
746					 .set_fuel(fuel.into())
747					 .expect("Fuel metering is enabled; qed");
748			}
749		} else {
750			quote! { }
751		};
752
753		match expand_mode {
754			ExpandMode::BenchImpl => {
755				let name = Ident::new(&format!("{module}_{name}"), Span::call_site());
756				quote! {
757					pub fn #name(ctx: &mut crate::wasm::Runtime<E>, memory: &mut [u8], #(#params),*) #output {
758						#inner
759					}
760				}
761			},
762			_ => {
763				let host_state = expand_mode.host_state();
764				quote! {
765					// We need to allow all interfaces when runtime benchmarks are performed because
766					// we generate the weights even when those interfaces are not enabled. This
767					// is necessary as the decision whether we allow unstable or deprecated functions
768					// is a decision made at runtime. Generation of the weights happens statically.
769					#cfg
770					if ::core::cfg!(feature = "runtime-benchmarks") ||
771						((#is_stable || __allow_unstable__) && (#not_deprecated || __allow_deprecated__))
772					{
773						#allow_unused
774						linker.define(#module, #name, ::wasmi::Func::wrap(&mut*store, |mut __caller__: ::wasmi::Caller<#host_state>, #( #params, )*| -> #wasm_output {
775							#sync_gas_before
776							let mut func = #inner;
777							let result = func().map_err(#into_host).map(::core::convert::Into::into);
778							#sync_gas_after
779							result
780						}))?;
781					}
782				}
783			},
784		}
785	});
786
787	match expand_mode {
788		ExpandMode::BenchImpl => {
789			quote! {
790			 #( #impls )*
791			}
792		},
793		_ => quote! {
794			let __allow_unstable__ = matches!(allow_unstable, AllowUnstableInterface::Yes);
795			let __allow_deprecated__ = matches!(allow_deprecated, AllowDeprecatedInterface::Yes);
796			#( #impls )*
797		},
798	}
799}
800
801/// Defines a host functions set that can be imported by contract wasm code.
802///
803/// **NB**: Be advised that all functions defined by this macro
804/// will panic if called with unexpected arguments.
805///
806/// It's up to you as the user of this macro to check signatures of wasm code to be executed
807/// and reject the code if any imported function has a mismatched signature.
808///
809/// ## Example
810///
811/// ```nocompile
812/// #[define_env]
813/// pub mod some_env {
814/// 	fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<(), TrapReason> {
815/// 		ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ())
816/// 	}
817/// }
818/// ```
819/// This example will expand to the `foo()` defined in the wasm module named `seal0`. This is
820/// because the module `seal0` is the default when no module is specified.
821///
822/// To define a host function in `seal2` and `seal3` modules, it should be annotated with the
823/// appropriate attribute as follows:
824///
825/// ## Example
826///
827/// ```nocompile
828/// #[define_env]
829/// pub mod some_env {
830/// 	#[version(2)]
831/// 	fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<ReturnErrorCode, TrapReason> {
832/// 		ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ())
833/// 	}
834///
835/// 	#[version(3)]
836/// 	#[unstable]
837/// 	fn bar(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<u32, TrapReason> {
838/// 		ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ())
839/// 	}
840/// }
841/// ```
842/// The function `bar` is additionally annotated with `unstable` which removes it from the stable
843/// interface. Check out the README to learn about unstable functions.
844///
845/// In legacy versions of pallet_contracts, it was a naming convention that all host functions had
846/// to be named with the `seal_` prefix. For the sake of backwards compatibility, each host function
847/// now can get a such prefix-named alias function generated by marking it by the
848/// `#[prefixed_alias]` attribute:
849///
850/// ## Example
851///
852/// ```nocompile
853/// #[define_env]
854/// pub mod some_env {
855/// 	#[version(1)]
856/// 	#[prefixed_alias]
857/// 	fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<ReturnErrorCode, TrapReason> {
858/// 		ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ())
859/// 	}
860///
861/// 	#[version(42)]
862/// 	fn bar(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<u32, TrapReason> {
863/// 		ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ())
864/// 	}
865/// }
866/// ```
867///
868/// In this example, the following host functions will be generated by the macro:
869/// - `foo()` in module `seal1`,
870/// - `seal_foo()` in module `seal1`,
871/// - `bar()` in module `seal42`.
872///
873/// Only following return types are allowed for the host functions defined with the macro:
874/// - `Result<(), TrapReason>`,
875/// - `Result<ReturnErrorCode, TrapReason>`,
876/// - `Result<u32, TrapReason>`.
877///
878/// The macro expands to `pub struct Env` declaration, with the following traits implementations:
879/// - `pallet_contracts::wasm::Environment<Runtime<E>> where E: Ext`
880/// - `pallet_contracts::wasm::Environment<()>`
881///
882/// The implementation on `()` can be used in places where no `Ext` exists, yet. This is useful
883/// when only checking whether a code can be instantiated without actually executing any code.
884///
885/// # Generating Documentation
886///
887/// Passing `doc` attribute to the macro (like `#[define_env(doc)]`) will make it also expand
888/// additional `pallet_contracts::api_doc::seal0`, `pallet_contracts::api_doc::seal1`,
889/// `...` modules each having its `Api` trait containing functions holding documentation for every
890/// host function defined by the macro.
891///
892/// # Deprecated Interfaces
893///
894/// An interface can be annotated with `#[deprecated]`. It is mutually exclusive with `#[unstable]`.
895/// Deprecated interfaces have the following properties:
896/// 	- New contract codes utilizing those interfaces cannot be uploaded.
897/// 	- New contracts from existing codes utilizing those interfaces cannot be instantiated.
898/// - Existing contracts containing those interfaces still work.
899///
900/// Those interfaces will eventually be removed.
901///
902/// To build up these docs, run:
903///
904/// ```nocompile
905/// cargo doc
906/// ```
907#[proc_macro_attribute]
908pub fn define_env(attr: TokenStream, item: TokenStream) -> TokenStream {
909	if !attr.is_empty() && !(attr.to_string() == "doc".to_string()) {
910		let msg = r#"Invalid `define_env` attribute macro: expected either no attributes or a single `doc` attribute:
911					 - `#[define_env]`
912					 - `#[define_env(doc)]`"#;
913		let span = TokenStream2::from(attr).span();
914		return syn::Error::new(span, msg).to_compile_error().into();
915	}
916
917	let item = syn::parse_macro_input!(item as syn::ItemMod);
918
919	match EnvDef::try_from(item) {
920		Ok(mut def) => expand_env(&mut def, !attr.is_empty()).into(),
921		Err(e) => e.to_compile_error().into(),
922	}
923}