mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 23:31:07 +00:00
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:
@@ -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())?];
|
||||
|
||||
@@ -1487,6 +1487,36 @@ pub mod pallet_prelude {
|
||||
/// non-instantiable pallets. For an example of an instantiable pallet, see [this
|
||||
/// example](#example-of-an-instantiable-pallet).
|
||||
///
|
||||
/// # Dev Mode (`#[pallet(dev_mode)]`)
|
||||
///
|
||||
/// Specifying the argument `dev_mode` on the `#[pallet]` or `#[frame_support::pallet]`
|
||||
/// attribute attached to your pallet module 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>
|
||||
///
|
||||
/// # Pallet struct placeholder: `#[pallet::pallet]` (mandatory)
|
||||
///
|
||||
/// The pallet struct placeholder `#[pallet::pallet]` is mandatory and allows you to specify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
error: Invalid pallet macro call: expected no attributes, e.g. macro call must be just `#[frame_support::pallet]` or `#[pallet]`
|
||||
--> $DIR/attr_non_empty.rs:1:26
|
||||
error: 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)].
|
||||
--> tests/pallet_ui/attr_non_empty.rs:1:26
|
||||
|
|
||||
1 | #[frame_support::pallet [foo]]
|
||||
| ^^^
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
|
||||
// The struct on which we build all of our Pallet logic.
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
// Your Pallet's configuration trait, representing custom external types and interfaces.
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::storage]
|
||||
type MyStorage<T: Config> = StorageValue<_, Vec<u8>>;
|
||||
|
||||
// Your Pallet's callable functions.
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
pub fn my_call(_origin: OriginFor<T>) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Your Pallet's internal functions.
|
||||
impl<T: Config> Pallet<T> {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,11 @@
|
||||
error: Invalid pallet::call, requires weight attribute i.e. `#[pallet::weight($expr)]`
|
||||
--> tests/pallet_ui/dev_mode_without_arg.rs:24:7
|
||||
|
|
||||
24 | pub fn my_call(_origin: OriginFor<T>) -> DispatchResult {
|
||||
| ^^
|
||||
|
||||
error[E0432]: unresolved import `pallet`
|
||||
--> tests/pallet_ui/dev_mode_without_arg.rs:3:9
|
||||
|
|
||||
3 | pub use pallet::*;
|
||||
| ^^^^^^ help: a similar path exists: `test_pallet::pallet`
|
||||
@@ -0,0 +1,34 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
|
||||
// The struct on which we build all of our Pallet logic.
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
// Your Pallet's configuration trait, representing custom external types and interfaces.
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::storage]
|
||||
type MyStorage<T: Config> = StorageValue<_, Vec<u8>>;
|
||||
|
||||
// Your Pallet's callable functions.
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
#[pallet::weight(0)]
|
||||
pub fn my_call(_origin: OriginFor<T>) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Your Pallet's internal functions.
|
||||
impl<T: Config> Pallet<T> {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
error[E0277]: the trait bound `Vec<u8>: MaxEncodedLen` is not satisfied
|
||||
--> tests/pallet_ui/dev_mode_without_arg_max_encoded_len.rs:11:12
|
||||
|
|
||||
11 | #[pallet::pallet]
|
||||
| ^^^^^^ the trait `MaxEncodedLen` is not implemented for `Vec<u8>`
|
||||
|
|
||||
= help: the following other types implement trait `MaxEncodedLen`:
|
||||
()
|
||||
(TupleElement0, TupleElement1)
|
||||
(TupleElement0, TupleElement1, TupleElement2)
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3)
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4)
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5)
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6)
|
||||
(TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7)
|
||||
and 78 others
|
||||
= note: required because of the requirements on the impl of `StorageInfoTrait` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageMyStorage<T>, Vec<u8>>`
|
||||
@@ -0,0 +1,4 @@
|
||||
#[frame_support::pallet(foo)]
|
||||
pub mod pallet {}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: 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)].
|
||||
--> tests/pallet_ui/pallet_invalid_arg.rs:1:25
|
||||
|
|
||||
1 | #[frame_support::pallet(foo)]
|
||||
| ^^^
|
||||
@@ -0,0 +1,35 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
#[frame_support::pallet(dev_mode)]
|
||||
pub mod pallet {
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
|
||||
// The struct on which we build all of our Pallet logic.
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
// Your Pallet's configuration trait, representing custom external types and interfaces.
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
// The MEL requirement for bounded pallets is skipped by `dev_mode`.
|
||||
#[pallet::storage]
|
||||
type MyStorage<T: Config> = StorageValue<_, Vec<u8>>;
|
||||
|
||||
// Your Pallet's callable functions.
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
// No need to define a `weight` attribute here because of `dev_mode`.
|
||||
pub fn my_call(_origin: OriginFor<T>) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Your Pallet's internal functions.
|
||||
impl<T: Config> Pallet<T> {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
Reference in New Issue
Block a user