Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Add pallet dev mode #12536

Merged
merged 33 commits into from
Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8e171a1
stub for construct_dev_runtime!
sam0x17 Oct 17, 2022
ba45d0b
revert
sam0x17 Oct 18, 2022
e1883c9
stub for dev_mode proc macro
sam0x17 Oct 19, 2022
1f3eb5a
preliminary docs for pallet::dev_mode (attribute) proc macro
sam0x17 Oct 19, 2022
ef886e2
add dev_mode to pallet_macros module
sam0x17 Oct 19, 2022
a62437a
add docs item for dev_mode to frame_support
sam0x17 Oct 19, 2022
89337a6
parsing of #[pallet(dev_mode)]
sam0x17 Oct 20, 2022
4d4f235
strip out dev_mode stub since it will be an arg for pallet instead
sam0x17 Oct 20, 2022
76eb78b
make pallet Def struct aware of dev mode
sam0x17 Oct 20, 2022
7ac54de
WIP
sam0x17 Oct 22, 2022
2a5857e
revert changes to call.rs
sam0x17 Oct 25, 2022
fce8e41
pass dev_mode to pallet parsing code
sam0x17 Oct 25, 2022
011f1e0
auto-specify default weights when in dev mode if not specified
sam0x17 Oct 25, 2022
41f159c
add proof / expect for syn::parse in dev mode weight processing
sam0x17 Oct 25, 2022
a7c85b3
set all storages to unbounded when in dev mode
sam0x17 Oct 25, 2022
bb71c7f
just use 0
sam0x17 Oct 25, 2022
45f9932
add invalid pallet arg test
sam0x17 Oct 27, 2022
0470808
add passing dev mode pallet test
sam0x17 Oct 27, 2022
b0cfc6a
add test confirming that dev mode features only work in dev mode
sam0x17 Oct 27, 2022
b5a82cb
cargo fmt + clean up
sam0x17 Oct 27, 2022
450f09d
bump CI
sam0x17 Oct 27, 2022
e765c42
fix pallet ui test
sam0x17 Oct 28, 2022
755a62c
add docs for dev mode
sam0x17 Oct 28, 2022
69411ab
add warning about using dev mode in production circumstances
sam0x17 Nov 1, 2022
4f8b58f
remove comment about no other attributes being supported
sam0x17 Nov 1, 2022
da619d7
fix unneeded assignment
sam0x17 Nov 1, 2022
d350781
make warning more explicit
sam0x17 Nov 7, 2022
6ac7643
more explicit warning about using dev mode in production
sam0x17 Nov 7, 2022
dc038d4
simpler assignment for dev_mode boolean
sam0x17 Nov 7, 2022
555516d
add note about MEL requirement
sam0x17 Nov 7, 2022
386a8c1
add comment specifying why weights can be omitted in example
sam0x17 Nov 7, 2022
b2d988a
tweak wording of comments
sam0x17 Nov 7, 2022
0aea2c8
bump ci
sam0x17 Nov 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions frame/support/procedural/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,28 @@ 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.
///
/// <div class="example-wrap" style="display:inline-block"><pre class="compile_fail"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it common to put HTML into the docs?

Copy link
Contributor Author

@sam0x17 sam0x17 Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now AFAIK there is no built-in way of doing a warning block unless something about the syntax of your code creates a doc warning (which isn't the case here), so the common workaround is to just use the same CSS classes rust docs use when there is a real warning and hard-code the HTML for now.

rust-lang/rust#73935 has tracked it since 2020

The actual HTML I use here was based on an SO answer that I am now having trouble locating, but yes it is using some of the built-in classes to achieve this warning look

TLDR: yes, for warnings

/// style="white-space:normal;font:inherit;">
/// <strong>WARNING</strong>:
/// You should not deploy or use dev mode pallets in production. Once you are done
sam0x17 marked this conversation as resolved.
Show resolved Hide resolved
/// 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 {
Expand Down
22 changes: 16 additions & 6 deletions frame/support/procedural/src/pallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)].";
sam0x17 marked this conversation as resolved.
Show resolved Hide resolved
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(),
}
Expand Down
9 changes: 9 additions & 0 deletions frame/support/procedural/src/pallet/parse/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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));
Comment on lines +220 to +222
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weight_attrs.push(FunctionAttr::Weight(syn::parse_quote!(0))

Would be the same.

}

if weight_attrs.len() != 1 {
let msg = if weight_attrs.is_empty() {
"Invalid pallet::call, requires weight attribute i.e. `#[pallet::weight($expr)]`"
Expand Down
8 changes: 5 additions & 3 deletions frame/support/procedural/src/pallet/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")?;

Expand Down Expand Up @@ -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() =>
Expand All @@ -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);
Expand Down Expand Up @@ -173,6 +174,7 @@ impl Def {
type_values,
frame_system,
frame_support,
dev_mode,
};

def.check_instance_usage()?;
Expand Down
8 changes: 7 additions & 1 deletion frame/support/procedural/src/pallet/parse/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -686,9 +687,14 @@ 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)?;

if dev_mode {
// set all storages to be unbounded if dev_mode is enabled
unbounded = true;
}

sam0x17 marked this conversation as resolved.
Show resolved Hide resolved
let cfg_attrs = helper::get_item_cfg_attrs(&item.attrs);

let instances = vec![helper::check_type_def_gen(&item.generics, item.ident.span())?];
Expand Down
23 changes: 23 additions & 0 deletions frame/support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,29 @@ 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` 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.
///
/// <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. 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
Expand Down
4 changes: 2 additions & 2 deletions frame/support/test/tests/pallet_ui/attr_non_empty.stderr
Original file line number Diff line number Diff line change
@@ -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]]
| ^^^
33 changes: 33 additions & 0 deletions frame/support/test/tests/pallet_ui/dev_mode_without_arg.rs
Original file line number Diff line number Diff line change
@@ -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() {}
11 changes: 11 additions & 0 deletions frame/support/test/tests/pallet_ui/dev_mode_without_arg.stderr
Original file line number Diff line number Diff line change
@@ -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`
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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>>`
4 changes: 4 additions & 0 deletions frame/support/test/tests/pallet_ui/pallet_invalid_arg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[frame_support::pallet(foo)]
pub mod pallet {}

fn main() {}
5 changes: 5 additions & 0 deletions frame/support/test/tests/pallet_ui/pallet_invalid_arg.stderr
Original file line number Diff line number Diff line change
@@ -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)]
| ^^^
33 changes: 33 additions & 0 deletions frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#![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 {}

#[pallet::storage]
sam0x17 marked this conversation as resolved.
Show resolved Hide resolved
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 {
sam0x17 marked this conversation as resolved.
Show resolved Hide resolved
Ok(())
}
}

// Your Pallet's internal functions.
impl<T: Config> Pallet<T> {}
}

fn main() {}