Skip to content

Commit

Permalink
Default Pallet Config Trait / derive_impl (paritytech#13454)
Browse files Browse the repository at this point in the history
* first draft, probably won't work

* first draft, probably won't work

* good progress..

* good milestone, still a lot to do.

* EVERYTHING WORKS

* Update frame/support/procedural/src/derive_impl.rs

Co-authored-by: Oliver Tale-Yazdi <[email protected]>

* Update frame/support/procedural/src/derive_impl.rs

Co-authored-by: Oliver Tale-Yazdi <[email protected]>

* clean up + cargo fmt

* import tokens WIP

* export_tokens working with impl Trait

* WIP / notes

* use macro_magic 0.2.0's export_tokens to access foreign items

* token importing working properly using macro_magic 0.2.5

* combine_impls almost working

* successfully get foreign path via macro_magic 0.2.6

* combine_impls using implementing_type generics

* working + clean up

* more clean up

* decrease rightwards drift and add docs to combine_impls

* add support for macros to impl_item_ident in case we hit that

* add docs for impl_item_ident method

* fix no_std issues

* re-export of macro_magic working in pallets 🎉

* clean up + fully resolve no_std issue with macro_magic with v0.2.11

* remove trait item code for different trait item types since this
is now handled directly by combine_impls

* clean up

* remove dev comments

* only generate default trait if #[pallet::default_trait] is attached

* authorship and most other pallets now compiling

* compiling 🎉

* add check for more than two pallet attributes on Config trait

* remove unused import in nomination-pool

* clean up debug code

* upgrade to macro_magic v0.2.12

* add neater #[register_default_config(SomeIdent)] macro

* really just a thin wrapper around #[export_tokens]

* upgrade to macro_magic 0.3.1

* rewrite parsing to be compatible with syn 2.x, compiling 🎉

* remove unused keywords

* macro stubs for the new pallet:: macros, preliminary docs

* upgrade to macro_magic v0.3.2

* rename register_default_config => register_default_impl

* bump to macro_magic v0.3.3

* custom disambiguation_path working as 2nd arg to derive_impl

* overhaul docs

* fixes, ident-style paths shortcut working

* remove ident-style shortcut because it makes testing difficult

* add passing UI tests for derive_impl

* switch to `ForeignPath as DisambiguationPath` syntax + update docs

* add UI test for bad foreign path

* add UI test for bad disambiguation path

* add UI test for missing disambiguation path

* add UI test for attached to non impl

* fix derive_impl_attr_args_parsing test

* move tests to bottom

* fix nightly issue

* add doc notes on importing/re-exporting

* remove explicit use of macro_magic::use_attr

Co-authored-by: Bastian Köcher <[email protected]>

* use explicit macro_magic::use_attr

Co-authored-by: Bastian Köcher <[email protected]>

* remove unneeded {}

Co-authored-by: Bastian Köcher <[email protected]>

* remove unneeded collect

Co-authored-by: Bastian Köcher <[email protected]>

* add docs for TestDefaultConfig

* remove unneeded `#[export_tokens]` on `DefaultConfig`

* add docs for auto-generated `DefaultConfig`

* no need to clone

Co-authored-by: Bastian Köcher <[email protected]>

* clean up combine_impls + compiling again

* remove unused dependency

* simplify struct definition

Co-authored-by: Bastian Köcher <[email protected]>

* fix register_default_impl docs

* reduce rightward drift / refactor

Co-authored-by: Keith Yeung <[email protected]>

* fix derive_impl after keith's changes

* simplify disambiguation_path calculation

Co-authored-by: Keith Yeung <[email protected]>

* compiling again

* simplify parsing of trait item

Co-authored-by: Keith Yeung <[email protected]>

* rename preludes => prelude

Co-authored-by: Keith Yeung <[email protected]>

* fix other places where we used preludes instead of prelude

* fix indents

* simplify PalletAttr parsing

Co-authored-by: Keith Yeung <[email protected]>

* go back to having no_default and constant as keywords

* make it more clear that disambiguation_path is optional

* make default_trait_items just a Vec instead of Option<Vec>

* rename foreign_path => default_impl_path within substrate

* fix docs

* Change {} to ;

Co-authored-by: Bastian Köcher <[email protected]>

* highlight full end-to-end example with link

* add pallet-default-config-example, start by copying dev mode code

* update dev-mode specific docs

* use Person and Points instead of Dummy and Bar

* add docs to example pallet

* revert changes to pallets other than the default config example

* fix outdated references to basic example pallet

* re-order docs to be a bit more clear

* better errors for extra attributes

* add UI tests for duplicate/extra attributes on trait items

* change `#[pallet::default_config]` to option on `#[pallet::config()]`

* update UI tests
* add UI test covering missing `#[pallet::config(with_default)]` when
  `#[pallet::no_default]` is used

* add note about new optional conventions

* improve docs about `DefaultConfig` and link to these from a few places

* fix doc comment

* fix old comment referencing `pallet::default_config`

* use u32 instead of u64 for block number

Co-authored-by: Kian Paimani <[email protected]>

* use () instead of u32 for `AccountData`

Co-authored-by: Kian Paimani <[email protected]>

* use ConstU32<10> for BlockHashCount instead of ConstU64<10>

Co-authored-by: Kian Paimani <[email protected]>

* people are not dummies

Co-authored-by: Liam Aharon <[email protected]>

* fix wording

Co-authored-by: Just van Stam <[email protected]>

* Person => People and compiling again

* add docs for `prelude` module in frame_system

* update Cargo.lock

* cleaner example

* tweaks

* update docs more

* update docs more

* update docs more

* update docs more

* fix ui tests

* err

* Update frame/support/test/tests/pallet_ui.rs

* update ui tests

---------

Co-authored-by: Oliver Tale-Yazdi <[email protected]>
Co-authored-by: Sam Johnson <[email protected]>
Co-authored-by: parity-processbot <>
Co-authored-by: Bastian Köcher <[email protected]>
Co-authored-by: Keith Yeung <[email protected]>
Co-authored-by: Liam Aharon <[email protected]>
Co-authored-by: Just van Stam <[email protected]>
  • Loading branch information
7 people authored and nathanwhit committed Jul 19, 2023
1 parent 56882be commit fded365
Show file tree
Hide file tree
Showing 35 changed files with 1,445 additions and 80 deletions.
64 changes: 64 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ members = [
"frame/examples/basic",
"frame/examples/offchain-worker",
"frame/examples/dev-mode",
"frame/examples/default-config",
"frame/executive",
"frame/nis",
"frame/grandpa",
Expand Down
38 changes: 38 additions & 0 deletions frame/examples/default-config/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[package]
name = "pallet-default-config-example"
version = "4.0.0-dev"
authors = ["Parity Technologies <[email protected]>"]
edition = "2021"
license = "MIT-0"
homepage = "https://substrate.io"
repository = "https://github.com/paritytech/substrate/"
description = "FRAME example pallet demonstrating derive_impl / default_config in action"
readme = "README.md"

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false }
log = { version = "0.4.17", default-features = false }
scale-info = { version = "2.5.0", default-features = false, features = ["derive"] }
frame-support = { default-features = false, path = "../../support" }
frame-system = { default-features = false, path = "../../system" }

sp-io = { default-features = false, path = "../../../primitives/io" }
sp-runtime = { default-features = false, path = "../../../primitives/runtime" }
sp-std = { default-features = false, path = "../../../primitives/std" }

[features]
default = ["std"]
std = [
"codec/std",
"frame-support/std",
"frame-system/std",
"log/std",
"scale-info/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
]
try-runtime = ["frame-support/try-runtime"]
8 changes: 8 additions & 0 deletions frame/examples/default-config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Default Config Example Pallet

An example pallet demonstrating the ability to derive default testing configs via
`#[derive_impl]` and `#[pallet::config(with_default)]`.

Run `cargo doc --package pallet-default-config-example --open` to view this pallet's documentation.

License: MIT-0
210 changes: 210 additions & 0 deletions frame/examples/default-config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! # Default Config Pallet Example
//!
//! A simple example of a FRAME pallet that utilizes [`frame_support::derive_impl`] to demonstrate
//! the simpler way to implement `Config` trait of pallets. This example only showcases this in a
//! `mock.rs` environment, but the same applies to a real runtime as well.
//!
//! See the source code of [`tests`] for a real examples.
//!
//! Study the following types:
//!
//! - [`pallet::DefaultConfig`], and how it differs from [`pallet::Config`].
//! - [`pallet::config_preludes::TestDefaultConfig`] and how it implements
//! [`pallet::DefaultConfig`].
//! - Notice how [`pallet::DefaultConfig`] is independent of [`frame_system::Config`].

#![cfg_attr(not(feature = "std"), no_std)]

#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;

/// This pallet is annotated to have a default config. This will auto-generate
/// [`DefaultConfig`].
///
/// It will be an identical, but won't have anything that is `#[pallet::no_default]`.
#[pallet::config(with_default)]
pub trait Config: frame_system::Config {
/// The overarching event type. This is coming from the runtime, and cannot have a default.
/// In general, `Runtime*`-oriented types cannot have a sensible default.
#[pallet::no_default] // optional. `RuntimeEvent` is automatically excluded as well.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

/// An input parameter to this pallet. This value can have a default, because it is not
/// reliant on `frame_system::Config` or the overarching runtime in any way.
type WithDefaultValue: Get<u32>;

/// Same as [`Config::WithDefaultValue`], but we don't intend to define a default for this
/// in our tests below.
type OverwrittenDefaultValue: Get<u32>;

/// An input parameter that relies on `<Self as frame_system::Config>::AccountId`. As of
/// now, such types cannot have defaults and need to be annotated as such, iff
/// `#[pallet::config(with_default)]` is enabled:
#[pallet::no_default]
type CannotHaveDefault: Get<Self::AccountId>;

/// Something that is a normal type, with default.
type WithDefaultType;

/// Same as [`Config::WithDefaultType`], but we don't intend to define a default for this
/// in our tests below.
type OverwrittenDefaultType;
}

/// Container for different types that implement [`DefaultConfig`]` of this pallet.
pub mod config_preludes {
// This will help use not need to disambiguate anything when using `derive_impl`.
use super::*;

/// A type providing default configurations for this pallet in testing environment.
pub struct TestDefaultConfig;
#[frame_support::register_default_impl(TestDefaultConfig)]
impl DefaultConfig for TestDefaultConfig {
type WithDefaultValue = frame_support::traits::ConstU32<42>;
type OverwrittenDefaultValue = frame_support::traits::ConstU32<42>;

type WithDefaultType = u32;
type OverwrittenDefaultType = u32;
}

/// A type providing default configurations for this pallet in a parachain environment.
pub struct ParachainDefaultConfig;
#[frame_support::register_default_impl(ParachainDefaultConfig)]
impl DefaultConfig for ParachainDefaultConfig {
type WithDefaultValue = frame_support::traits::ConstU32<66>;
type OverwrittenDefaultValue = frame_support::traits::ConstU32<66>;
type WithDefaultType = u32;
type OverwrittenDefaultType = u32;
}
}

#[pallet::pallet]
pub struct Pallet<T>(_);

#[pallet::event]
pub enum Event<T: Config> {}
}

#[cfg(any(test, doc))]
pub mod tests {
use super::*;

use frame_support::macro_magic::use_attr;
// Because `derive_impl` is a [macro_magic](https://crates.io/crates/macro_magic) attribute
// macro, [`#[use_attr]`](`frame_support::macro_magic::use_attr`) must be attached to any use
// statement that brings it into scope.
#[use_attr]
use frame_support::derive_impl;

use super::pallet as pallet_default_config_example;

type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;

frame_support::construct_runtime!(
pub enum Test where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system,
DefaultPallet: pallet_default_config_example,
}
);

#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl frame_system::Config for Test {
// these items are defined by frame-system as `no_default`, so we must specify them here.
// Note that these are types that actually rely on the outer runtime, and can't sensibly
// have an _independent_ default.
type BaseCallFilter = frame_support::traits::Everything;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type RuntimeEvent = RuntimeEvent;
type PalletInfo = PalletInfo;
type OnSetCode = ();

// all of this is coming from `frame_system::config_preludes::TestDefaultConfig`.

// type Index = u32;
// type BlockNumber = u32;
// type Header = sp_runtime::generic::Header<Self::BlockNumber, Self::Hashing>;
// type Hash = sp_core::hash::H256;
// type Hashing = sp_runtime::traits::BlakeTwo256;
// type AccountId = u64;
// type Lookup = sp_runtime::traits::IdentityLookup<u64>;
// type BlockHashCount = frame_support::traits::ConstU32<10>;
// type MaxConsumers = frame_support::traits::ConstU32<16>;
// type AccountData = ();
// type OnNewAccount = ();
// type OnKilledAccount = ();
// type SystemWeightInfo = ();
// type SS58Prefix = ();
// type Version = ();
// type BlockWeights = ();
// type BlockLength = ();
// type DbWeight = ();

// you could still overwrite any of them if desired.
type SS58Prefix = frame_support::traits::ConstU16<456>;
}

// Similarly, we use the defaults provided by own crate as well.
use pallet::config_preludes::TestDefaultConfig;
#[derive_impl(TestDefaultConfig as pallet::DefaultConfig)]
impl crate::pallet::Config for Test {
// These two both cannot have defaults.
type RuntimeEvent = RuntimeEvent;
// Note that the default account-id type in
// `frame_system::config_preludes::TestDefaultConfig` is `u64`.
type CannotHaveDefault = frame_support::traits::ConstU64<1>;

type OverwrittenDefaultValue = frame_support::traits::ConstU32<678>;
type OverwrittenDefaultType = u128;
}

#[test]
fn it_works() {
use frame_support::traits::Get;
use pallet::{Config, DefaultConfig};

// assert one of the value types that is not overwritten.
assert_eq!(
<<Test as Config>::WithDefaultValue as Get<u32>>::get(),
<<TestDefaultConfig as DefaultConfig>::WithDefaultValue as Get<u32>>::get()
);

// assert one of the value types that is overwritten.
assert_eq!(<<Test as Config>::OverwrittenDefaultValue as Get<u32>>::get(), 678u32);

// assert one of the types that is not overwritten.
assert_eq!(
std::any::TypeId::of::<<Test as Config>::WithDefaultType>(),
std::any::TypeId::of::<<TestDefaultConfig as DefaultConfig>::WithDefaultType>()
);

// assert one of the types that is overwritten.
assert_eq!(
std::any::TypeId::of::<<Test as Config>::OverwrittenDefaultType>(),
std::any::TypeId::of::<u128>()
)
}
}
1 change: 1 addition & 0 deletions frame/support/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../pr
sp-weights = { version = "5.0.0", default-features = false, path = "../../primitives/weights" }
sp-debug-derive = { default-features = false, path = "../../primitives/debug-derive" }
tt-call = "1.0.8"
macro_magic = "0.3.3"
frame-support-procedural = { version = "4.0.0-dev", default-features = false, path = "./procedural" }
paste = "1.0"
once_cell = { version = "1", default-features = false, optional = true }
Expand Down
1 change: 1 addition & 0 deletions frame/support/procedural/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ quote = "1.0.28"
syn = { version = "2.0.16", features = ["full"] }
frame-support-procedural-tools = { version = "4.0.0-dev", path = "./tools" }
proc-macro-warning = { version = "0.4.1", default-features = false }
macro_magic = { version = "0.3.3", features = ["proc_support"] }

[features]
default = ["std"]
Expand Down
2 changes: 1 addition & 1 deletion frame/support/procedural/src/construct_runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
//! `::{Call, ...}` or implicitly.
//!
//! In case a pallet defines its parts implicitly, then the pallet must provide the
//! `tt_default_parts` macro. `construct_rutime` will generate some code which utilizes `tt_call`
//! `tt_default_parts` macro. `construct_runtime` will generate some code which utilizes `tt_call`
//! to call the `tt_default_parts` macro of the pallet. `tt_default_parts` will then return the
//! default pallet parts as input tokens to the `match_and_replace` macro, which ultimately
//! generates a call to `construct_runtime` again, this time with all the pallet parts explicitly
Expand Down
Loading

0 comments on commit fded365

Please sign in to comment.