Add pallet dev mode (#12536)

* stub for construct_dev_runtime!

* revert

* stub for dev_mode proc macro

* preliminary docs for pallet::dev_mode (attribute) proc macro

* add dev_mode to pallet_macros module

* add docs item for dev_mode to frame_support

* parsing of #[pallet(dev_mode)]

* strip out dev_mode stub since it will be an arg for pallet instead

* make pallet Def struct aware of dev mode

* WIP

* revert changes to call.rs

* pass dev_mode to pallet parsing code

* auto-specify default weights when in dev mode if not specified

* add proof / expect for syn::parse in dev mode weight processing

* set all storages to unbounded when in dev mode

* just use 0

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* add invalid pallet arg test

* add passing dev mode pallet test

* add test confirming that dev mode features only work in dev mode

* cargo fmt + clean up

* bump CI

* fix pallet ui test

* add docs for dev mode

* add warning about using dev mode in production circumstances

* remove comment about no other attributes being supported

* fix unneeded assignment

* make warning more explicit

* more explicit warning about using dev mode in production

* simpler assignment for dev_mode boolean

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* add note about MEL requirement

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* add comment specifying why weights can be omitted in example

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* tweak wording of comments

* bump ci

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
This commit is contained in:
Sam Johnson
2022-11-07 16:40:55 -05:00
committed by GitHub
parent cf5c78dbe8
commit 7c4bfc9749
14 changed files with 233 additions and 12 deletions
@@ -445,6 +445,34 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream {
/// pallet. Otherwise it implements `StorageInfoTrait` for the pallet using the
/// `PartialStorageInfoTrait` implementation of storages.
///
/// ## Dev Mode (`#[pallet(dev_mode)]`)
///
/// Specifying the argument `dev_mode` will allow you to enable dev mode for a pallet. The aim
/// of dev mode is to loosen some of the restrictions and requirements placed on production
/// pallets for easy tinkering and development. Dev mode pallets should not be used in
/// production. Enabling dev mode has the following effects:
///
/// * Weights no longer need to be specified on every `#[pallet::call]` declaration. By default, dev
/// mode pallets will assume a weight of zero (`0`) if a weight is not specified. This is
/// equivalent to specifying `#[weight(0)]` on all calls that do not specify a weight.
/// * All storages are marked as unbounded, meaning you do not need to implement `MaxEncodedLen` on
/// storage types. This is equivalent to specifying `#[pallet::unbounded]` on all storage type
/// definitions.
///
/// Note that the `dev_mode` argument can only be supplied to the `#[pallet]` or
/// `#[frame_support::pallet]` attribute macro that encloses your pallet module. This argument
/// cannot be specified anywhere else, including but not limited to the `#[pallet::pallet]`
/// attribute macro.
///
/// <div class="example-wrap" style="display:inline-block"><pre class="compile_fail"
/// style="white-space:normal;font:inherit;">
/// <strong>WARNING</strong>:
/// You should not deploy or use dev mode pallets in production. Doing so can break your chain
/// and therefore should never be done. Once you are done tinkering, you should remove the
/// 'dev_mode' argument from your #[pallet] declaration and fix any compile errors before
/// attempting to use your pallet in a production scenario.
/// </pre></div>
///
/// See `frame_support::pallet` docs for more info.
#[proc_macro_attribute]
pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream {
@@ -31,20 +31,30 @@ mod parse;
pub use parse::Def;
use syn::spanned::Spanned;
mod keyword {
syn::custom_keyword!(dev_mode);
}
pub fn pallet(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let mut dev_mode = false;
if !attr.is_empty() {
let msg =
"Invalid pallet macro call: expected no attributes, e.g. macro call must be just \
`#[frame_support::pallet]` or `#[pallet]`";
let span = proc_macro2::TokenStream::from(attr).span();
return syn::Error::new(span, msg).to_compile_error().into()
if let Ok(_) = syn::parse::<keyword::dev_mode>(attr.clone()) {
dev_mode = true;
} else {
let msg = "Invalid pallet macro call: unexpected attribute. Macro call must be \
bare, such as `#[frame_support::pallet]` or `#[pallet]`, or must specify the \
`dev_mode` attribute, such as `#[frame_support::pallet(dev_mode)]` or \
#[pallet(dev_mode)].";
let span = proc_macro2::TokenStream::from(attr).span();
return syn::Error::new(span, msg).to_compile_error().into()
}
}
let item = syn::parse_macro_input!(item as syn::ItemMod);
match parse::Def::try_from(item) {
match parse::Def::try_from(item, dev_mode) {
Ok(def) => expand::expand(def).into(),
Err(e) => e.to_compile_error().into(),
}
@@ -144,6 +144,7 @@ impl CallDef {
attr_span: proc_macro2::Span,
index: usize,
item: &mut syn::Item,
dev_mode: bool,
) -> syn::Result<Self> {
let item_impl = if let syn::Item::Impl(item) = item {
item
@@ -213,6 +214,14 @@ impl CallDef {
},
);
if weight_attrs.is_empty() && dev_mode {
// inject a default O(1) weight when dev mode is enabled and no weight has
// been specified on the call
let empty_weight: syn::Expr = syn::parse(quote::quote!(0).into())
.expect("we are parsing a quoted string; qed");
weight_attrs.push(FunctionAttr::Weight(empty_weight));
}
if weight_attrs.len() != 1 {
let msg = if weight_attrs.is_empty() {
"Invalid pallet::call, requires weight attribute i.e. `#[pallet::weight($expr)]`"
@@ -59,10 +59,11 @@ pub struct Def {
pub type_values: Vec<type_value::TypeValueDef>,
pub frame_system: syn::Ident,
pub frame_support: syn::Ident,
pub dev_mode: bool,
}
impl Def {
pub fn try_from(mut item: syn::ItemMod) -> syn::Result<Self> {
pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result<Self> {
let frame_system = generate_crate_access_2018("frame-system")?;
let frame_support = generate_crate_access_2018("frame-support")?;
@@ -106,7 +107,7 @@ impl Def {
hooks = Some(m);
},
Some(PalletAttr::RuntimeCall(span)) if call.is_none() =>
call = Some(call::CallDef::try_from(span, index, item)?),
call = Some(call::CallDef::try_from(span, index, item, dev_mode)?),
Some(PalletAttr::Error(span)) if error.is_none() =>
error = Some(error::ErrorDef::try_from(span, index, item)?),
Some(PalletAttr::RuntimeEvent(span)) if event.is_none() =>
@@ -124,7 +125,7 @@ impl Def {
Some(PalletAttr::Inherent(_)) if inherent.is_none() =>
inherent = Some(inherent::InherentDef::try_from(index, item)?),
Some(PalletAttr::Storage(span)) =>
storages.push(storage::StorageDef::try_from(span, index, item)?),
storages.push(storage::StorageDef::try_from(span, index, item, dev_mode)?),
Some(PalletAttr::ValidateUnsigned(_)) if validate_unsigned.is_none() => {
let v = validate_unsigned::ValidateUnsignedDef::try_from(index, item)?;
validate_unsigned = Some(v);
@@ -173,6 +174,7 @@ impl Def {
type_values,
frame_system,
frame_support,
dev_mode,
};
def.check_instance_usage()?;
@@ -678,6 +678,7 @@ impl StorageDef {
attr_span: proc_macro2::Span,
index: usize,
item: &mut syn::Item,
dev_mode: bool,
) -> syn::Result<Self> {
let item = if let syn::Item::Type(item) = item {
item
@@ -686,9 +687,11 @@ impl StorageDef {
};
let attrs: Vec<PalletStorageAttr> = helper::take_item_pallet_attrs(&mut item.attrs)?;
let PalletStorageAttrInfo { getter, rename_as, unbounded, whitelisted } =
let PalletStorageAttrInfo { getter, rename_as, mut unbounded, whitelisted } =
PalletStorageAttrInfo::from_attrs(attrs)?;
// set all storages to be unbounded if dev_mode is enabled
unbounded |= dev_mode;
let cfg_attrs = helper::get_item_cfg_attrs(&item.attrs);
let instances = vec![helper::check_type_def_gen(&item.generics, item.ident.span())?];