-
Notifications
You must be signed in to change notification settings - Fork 703
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make construct_runtime macro more flexible #232
Make construct_runtime macro more flexible #232
Comments
I am sure I've pasted this in another issue as well (found it), but I think the new version of this macro should look like this: #[frame::contruct_rutnime]
mod runtime {
// The rest, like `AllPalletsReserved` and such have to be
// unfortunately aut-generated, or better, deprecated.
#[frame::pallets]
type AllPallets = (
frame_system::pallet,
pallet_balances::pallet,
pallet_staking::pallet,
pallet_sessin::pallet,
);
// don't think there is much to this.
#[frame::runtime]
pub struct Runtime;
// Auto-generate call from `AllPallets`
#[frame::call]
pub enum RuntimeCall = ..AllPallets;
// Alternative to the spread syntax above: Explicitly build an enum with the given
// pallet paths.
//
// This is very dangerous, only for experts. Probably no real use case for it either..
#[frame::event]
pub enum RuntimeEvent = (frame_system, pallet_staking);
#[frame::origin]
pub enum RuntimeOrigin = ..AllPallets;
#[frame::genesis_config]
pun enum RuntimeGenesis = ..AllPallet;
// Have not thought it through, but see
https://github.com/paritytech/substrate/issues/13463#issuecomment-1464013060
#[frame::ext_builder]
pub struct ExtBuilder {...}
}
pub use runtime::{Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; Where if you write |
I like this a lot, it not only solves the flexibility issues I mentioned,
but also helps with visibility for the enums generated, which are right now
created inside the macro and thus hidden in the source code of the runtime.
```
…On Sun, Jan 15, 2023 at 9:36 AM Kian Paimani ***@***.***> wrote:
I am sure I've pasted this in another issue as well, but I think the new
version of this macro should look like this:
#[frame::contruct_rutnime]
mod runtime {
// The rest, like `AllPalletsReserved` and such have to be
// unfortunately aut-generated, or better, deprecated.
#[frame::pallets]
type AllPallets = (
frame_system::pallet,
pallet_balances::pallet,
pallet_staking::pallet,
pallet_sessin::pallet,
);
// don't think there is much to this.
#[frame::runtime]
pub struct Runtime;
// Auto-generate call from `AllPallets`
#[frame::call]
pub enum RuntimeCall = ..AllPallets;
// Alternative to the speard syntax above: Explicitely build an enum with the given
// pallet paths.
//
// This is very dangerous, only for experts. Probably no real use case for it either..
#[frame::event]
pub enum RuntimeEvent = (frame_system, pallet_staking);
#[frame::origin]
pub enum Origin = ..AllPallets
}
pub use runtime::{Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin};
Where if you write pub enum Origin = ..AllPallets you get the default
implementation, but you can opt out of it any at any point as well.
—
Reply to this email directly, view it on GitHub
<#232>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AHGXNIUDDRNXPJG2AW45S5TWSPVMLANCNFSM6AAAAAAT2PWTBI>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
--
*Gabriel Facco de Arruda*
+55 (51) 99826-0101
***@***.*** ***@***.***>*
|
This doesn't really makes sense, why passing your custom origin when the function only needs a signed origin? And how should the The general idea of @kianenigma looks good and something we should speak about! |
I don't actually need help with that, I already have it working how I want it but I had to clone and modify the frame-support-procedural crate so I could implement it myself, which is why I came to the conclusion that making the construct_runtime macro more flexible would be of help not only to me but possibly to others that want to have unconventional implementation like mine. And to answer your question of why I want my custom origin to act as signed, it's because the origin is for a multisig pallet that has calls within it to change parameters and handle operations that modify the multisig itself, those need specific data like the multisig id and the original caller who initiated the multisig, I have that data in the custom origin, but I also want the multisig to act as a normal account for everything else. I know it can be easily solved by adding those to the function arguments and then verifying within the function, but I'm building with UX in mind, if something can be achieved by the runtime without concerning the users and developers, then I'll handle that. |
I see what you want to achieve. Currently this is already achievable without forking We could also add some attribute |
This issue has been mentioned on Polkadot Forum. There might be relevant details there: https://forum.polkadot.network/t/less-magic-in-frame-good-or-bad/2287/8 |
How does it handle instances, indices and instance naming? |
Not a whole lot has changed, but here is a more up-to-date and well-documented version of the above idea, which also answers the question raised by @gavofyork: #[frame::construct_runtime]
mod runtime {
/// Mandatory, just to declare `pub struct Runtime`.
#[frame::runtime]
pub struct Runtime;
/// Mandatory; and the benefit now is that we can elide a LOT of the types for this. The macro
/// will elide all generics for you as:
/// 1. `System` is already know.
/// 2. `Block` is already known from system (assuming
/// https://github.com/paritytech/substrate/pull/14193)
/// 3. `Context` is already know from System.
/// 4. `UnsignedValidator` is known from `AllPallets`
/// 5. `AllPallets` is known
/// 6. `OnRuntimeUpgrade` will be left blank to its default to `()`.
///
/// All of the above can be replaced with `_`. So the typical syntax is:
///
/// `pub struct Executive<_, _, _, _, _, Migration>`
///
/// Which can be further reduced to:
///
/// If you provide `pub struct Executive<Migrations>`
///
/// NOTE: In the future, the execution of `OnRuntimeUpgrade` should be fully delegated to a
/// single "migration dispatcher" pallet (probably the same one as
/// https://github.com/paritytech/substrate/pull/14275), so this last argument can be removed.
#[frame::executive]
pub struct Executive<_, _, _, _ , _>;
/// Almost exactly the same syntax as the existing one.
///
/// This will not be real rust-like, but it will demonstrate the existence of `type AllPallets`.
#[frame::pallets]
pub type AllPallets = (
System = frame_system::pallet = 0,
BalancesFoo = pallet_balances::pallet = 1,
BalancesBar = pallet_balances::pallet = 2,
Staking = pallet_staking::pallet = 42,
Session = pallet_session::pallet = 43,
);
/// This can either be a tuple of pallet names, eg
///
/// #[frame::call] pub enum RuntimeCall = (System, BalancesFoo, Staking)
///
/// which omits a number of calls, or be the below syntax, which means "derive it from
/// AllPallets, in the exact same manner".
#[frame::call]
pub enum RuntimeCall = ..AllPallets;
/// Same semantics as `RuntimeCall`.
#[frame::event]
pub enum RuntimeEvent = ..AllPallets;
/// Same semantics as `RuntimeCall`.
#[frame::origin]
pub enum RuntimeOrigin = ..AllPallets;
/// Same semantics as `RuntimeCall`.
#[frame::genesis_config]
pub enum RuntimeGenesis = ..AllPallet;
/// Same semantics as `RuntimeCall`.
/// This is slightly different from how we do this now. Now, we implement
/// `ValidateUnsigned` for `Runtime`, now we should implement it for a new
/// type and pass that one to executive.
#[frame::validate_unsigned]
pub type UnsignedValidator = ..AllPallets;
/// This will auto-populate this module with a known list of types and re-exports that will be
/// useful either in the rest of this block (`Block`, `Header` etc), or the ones that are
/// typically used by the node to build genesis (`AccountId`, `OpaqueBlock`) etc.
#[frame::runtime_prelude]
pub mod prelude {}
/// Further Idea paritytech/substrate#1: This can automatically generate nested builders that create a struct with
/// fields corresponding to the `GenesisConfig` of each pallet.
///
/// See: https://github.com/paritytech/substrate/issues/13463#issuecomment-1464013060
#[frame::ext_builder]
pub struct ExtBuilder {}
/// Further idea paritytech/substrate#2: The parent annotation could be
/// #[frame::construct_runtime(testing|parachain|solo-chain)] which could enforce more rules,
/// elide more types.
///
/// See: https://github.com/paritytech/substrate/issues/13321
}
/// Finally, other than `#[frame::runtime] mod runtime`, this is your single exposed
pub use runtime::prelude::*; |
Migrations are running before |
How do we feel about the following to maintain rust-like syntax for #[frame_support::construct_runtime_v2]
mod runtime {
#[frame::runtime]
pub struct Runtime;
#[frame::pallets]
pub struct AllPallets {
System: frame_system,
Timestamp: pallet_timestamp,
Aura: pallet_aura,
Grandpa: pallet_grandpa,
Balances: pallet_balances,
TransactionPayment: pallet_transaction_payment,
Sudo: pallet_sudo,
TemplateModule: pallet_template,
}
// Optional
#[frame::indices]
pub enum AllPalletIndices {
System = 0,
Timestamp = 1,
Aura = 2,
Grandpa = 3,
Balances = 4,
TransactionPayment = 5,
Sudo = 6,
TemplateModule = 7,
}
} |
IMO declaring the pallets and indices should be done in one place and not split up. |
Taking naming and instancing into account, maybe: #[frame::pallets]
pub enum Pallets {
System = frame_system::pallet { index: 0, ..Default::default() },
BalancesFoo = pallet_balances::pallet { index: 1, instance: 1 },
BalancesBar = pallet_balances::pallet { index: 2, instance: 2 }
}; or when wanting to indicate that the #[frame::pallets]
pub enum Pallets {
System = (0, frame_system::pallet::default()),
BalancesFoo = (1, pallet_balances::pallet { instance: 1 }),
BalancesBar = (2, pallet_balances::pallet { instance: 2 })
}; |
@ggwpez I really like this one. Will explore this further. #[frame::pallets]
pub enum Pallets {
System = (0, frame_system::pallet::default()),
BalancesFoo = (1, pallet_balances::pallet { instance: 1 }),
BalancesBar = (2, pallet_balances::pallet { instance: 2 })
}; |
Not sure this syntax is that great 🙈 First we should think about what kind of features we need to support. I can think of |
Trying to use a pseudo rust types to declare pallets can be confusing. Kian proposal where items the declared items are really defined by the macro is less confusing IMO. maybe for declaring a pallet we can go with a more explicit declaration a bit like json like this, but it can be too verbose:
or
|
while trying to figure out what flag are interesting for users, I made this summary of the code generated by construct_runtime
Final notes
|
I can't find I think all of this should handled outside of where you define the list of pallets. This is one fundamental difference between my suggestion (or its general direction) and what we currently have. Now, if you have 3 pallets and you want to include the call of two of them only, it will look something like this: construct_runtime!(
PalletA: pallet_a::{Call, GenesisConfig} = 0,
PalletB: pallet_b::{Call, GenesisConfig} = 0
PalletB: pallet_c::{GenesisConfig} = 0
) This is mixing 3 concepts: instancing, indexing, and individual parts. I am suggesting moving the latter to separate types/macros. This is:
So the above example, in my suggestion, would be: #[frame::runtime] {
// details of this block's syntax is itself TBD, but it should only specify index and instance
#[frame::pallets]
pub type AllPallets = (
PalletA = 0,
PalletB = 1,
PalletC = 2
);
#[frame::call]
pub enum RuntimeCall = (PalletA, PalletB);
#[frame::genesis_config]
pub enum RuntimeGenesis = ..AllPallet;
} I skipped @gupnik The action-item for you here to investigate an exhaustive list of features that the current For example, I know that you can specify Also, you should investigate the initial request from @arrudagates and if/how it can be fulfilled with the new proposal. I feel like I hijacked his issue here for something unrelated 😅 (As a side note, the keyword |
They don't exist currently. Only the ones you mentioned. I thought about the new syntax.
Nothing and we should just forget about this. This is what I tried to express, that we don't need all these config options.
Not sure we should touch this and not just keep it until we have the new syntax. |
Agree, I have created an issue anyway to keep it in mind though. |
As discussed during the retreat, we could use the following syntax to provide a reasonable default: #[frame::construct_runtime]
mod runtime {
#[frame::runtime]
pub struct Runtime;
#[frame::pallets]
#[derive(RuntimeGenesisConfig, RuntimeCall, RuntimeOrigin)] // Aggregate all pallets by default
pub type AllPallets = (
System = frame_system::pallet = 0,
BalancesFoo = pallet_balances::pallet = 1,
BalancesBar = pallet_balances::pallet = 2,
Staking = pallet_staking::pallet = 42,
Session = pallet_session::pallet = 43,
);
} There could be an additional syntax to exclude some pallet parts if needed. |
@bkchr How do you suggest wrapping I have tried and it seems to be a lot of work with all the implementations required. For now, the only easy solution I see, is to also fork |
Yes. |
I wanted exactly this when trying to learn about Deeper digging, I realized it was being generated by |
Moved from paritytech/substrate#14788 ---- Fixes #232 This PR introduces outer-macro approach for `construct_runtime` as discussed in the linked issue. It looks like the following: ```rust #[frame_support::runtime] mod runtime { #[runtime::runtime] #[runtime::derive( RuntimeCall, RuntimeEvent, RuntimeError, RuntimeOrigin, RuntimeFreezeReason, RuntimeHoldReason, RuntimeSlashReason, RuntimeLockId, RuntimeTask, )] pub struct Runtime; #[runtime::pallet_index(0)] pub type System = frame_system; #[runtime::pallet_index(1)] pub type Timestamp = pallet_timestamp; #[runtime::pallet_index(2)] pub type Aura = pallet_aura; #[runtime::pallet_index(3)] pub type Grandpa = pallet_grandpa; #[runtime::pallet_index(4)] pub type Balances = pallet_balances; #[runtime::pallet_index(5)] pub type TransactionPayment = pallet_transaction_payment; #[runtime::pallet_index(6)] pub type Sudo = pallet_sudo; // Include the custom logic from the pallet-template in the runtime. #[runtime::pallet_index(7)] pub type TemplateModule = pallet_template; } ``` ## Features - `#[runtime::runtime]` attached to a struct defines the main runtime - `#[runtime::derive]` attached to this struct defines the types generated by runtime - `#[runtime::pallet_index]` must be attached to a pallet to define its index - `#[runtime::disable_call]` can be optionally attached to a pallet to disable its calls - `#[runtime::disable_unsigned]` can be optionally attached to a pallet to disable unsigned calls - A pallet instance can be defined as `TemplateModule: pallet_template<Instance>` - An optional attribute can be defined as `#[frame_support::runtime(legacy_ordering)]` to ensure that the order of hooks is same as the order of pallets (and not based on the pallet_index). This is to support legacy runtimes and should be avoided for new ones. ## Todo - [x] Update the latest syntax in kitchensink and tests - [x] Update UI tests - [x] Docs ## Extension - Abstract away the Executive similar to paritytech/substrate#14742 - Optionally avoid the need to specify all runtime types (TBD) --------- Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: Nikhil Gupta <>
Moved from paritytech/substrate#14788 ---- Fixes paritytech#232 This PR introduces outer-macro approach for `construct_runtime` as discussed in the linked issue. It looks like the following: ```rust #[frame_support::runtime] mod runtime { #[runtime::runtime] #[runtime::derive( RuntimeCall, RuntimeEvent, RuntimeError, RuntimeOrigin, RuntimeFreezeReason, RuntimeHoldReason, RuntimeSlashReason, RuntimeLockId, RuntimeTask, )] pub struct Runtime; #[runtime::pallet_index(0)] pub type System = frame_system; #[runtime::pallet_index(1)] pub type Timestamp = pallet_timestamp; #[runtime::pallet_index(2)] pub type Aura = pallet_aura; #[runtime::pallet_index(3)] pub type Grandpa = pallet_grandpa; #[runtime::pallet_index(4)] pub type Balances = pallet_balances; #[runtime::pallet_index(5)] pub type TransactionPayment = pallet_transaction_payment; #[runtime::pallet_index(6)] pub type Sudo = pallet_sudo; // Include the custom logic from the pallet-template in the runtime. #[runtime::pallet_index(7)] pub type TemplateModule = pallet_template; } ``` ## Features - `#[runtime::runtime]` attached to a struct defines the main runtime - `#[runtime::derive]` attached to this struct defines the types generated by runtime - `#[runtime::pallet_index]` must be attached to a pallet to define its index - `#[runtime::disable_call]` can be optionally attached to a pallet to disable its calls - `#[runtime::disable_unsigned]` can be optionally attached to a pallet to disable unsigned calls - A pallet instance can be defined as `TemplateModule: pallet_template<Instance>` - An optional attribute can be defined as `#[frame_support::runtime(legacy_ordering)]` to ensure that the order of hooks is same as the order of pallets (and not based on the pallet_index). This is to support legacy runtimes and should be avoided for new ones. ## Todo - [x] Update the latest syntax in kitchensink and tests - [x] Update UI tests - [x] Docs ## Extension - Abstract away the Executive similar to paritytech/substrate#14742 - Optionally avoid the need to specify all runtime types (TBD) --------- Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: Nikhil Gupta <>
Moved from paritytech/substrate#14788 ---- Fixes paritytech#232 This PR introduces outer-macro approach for `construct_runtime` as discussed in the linked issue. It looks like the following: ```rust #[frame_support::runtime] mod runtime { #[runtime::runtime] #[runtime::derive( RuntimeCall, RuntimeEvent, RuntimeError, RuntimeOrigin, RuntimeFreezeReason, RuntimeHoldReason, RuntimeSlashReason, RuntimeLockId, RuntimeTask, )] pub struct Runtime; #[runtime::pallet_index(0)] pub type System = frame_system; #[runtime::pallet_index(1)] pub type Timestamp = pallet_timestamp; #[runtime::pallet_index(2)] pub type Aura = pallet_aura; #[runtime::pallet_index(3)] pub type Grandpa = pallet_grandpa; #[runtime::pallet_index(4)] pub type Balances = pallet_balances; #[runtime::pallet_index(5)] pub type TransactionPayment = pallet_transaction_payment; #[runtime::pallet_index(6)] pub type Sudo = pallet_sudo; // Include the custom logic from the pallet-template in the runtime. #[runtime::pallet_index(7)] pub type TemplateModule = pallet_template; } ``` ## Features - `#[runtime::runtime]` attached to a struct defines the main runtime - `#[runtime::derive]` attached to this struct defines the types generated by runtime - `#[runtime::pallet_index]` must be attached to a pallet to define its index - `#[runtime::disable_call]` can be optionally attached to a pallet to disable its calls - `#[runtime::disable_unsigned]` can be optionally attached to a pallet to disable unsigned calls - A pallet instance can be defined as `TemplateModule: pallet_template<Instance>` - An optional attribute can be defined as `#[frame_support::runtime(legacy_ordering)]` to ensure that the order of hooks is same as the order of pallets (and not based on the pallet_index). This is to support legacy runtimes and should be avoided for new ones. ## Todo - [x] Update the latest syntax in kitchensink and tests - [x] Update UI tests - [x] Docs ## Extension - Abstract away the Executive similar to paritytech/substrate#14742 - Optionally avoid the need to specify all runtime types (TBD) --------- Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: Nikhil Gupta <>
Usually the assumptions made by the
construct_runtime
macro are correct and help keep the runtime boilerplate free, but sometimes it's desirable to override the implementations generated by the macro.In my case, for example, I want to override the implementation of
From<Origin> for Result<RawOrigin<AccountId>, Origin>
so I can allow my custom origins to pass through the defaultensure_signed
assertions in most pallets' calls.I would like to propose and get feedback/suggestions on how to make the
construct_runtime
macro more flexible, while still maintaining the minimum amount of boilerplate. The solution I currently have in mind is probably not the best possible, thus I'd like to brainstorm solutions.I'll preface this with a note that I'm not super knowledgeable in Rust macros, so I'm not sure if this is possible to implement like this, but here's what I had in mind:
Perhaps we could even break down the implementations from
construct_runtime
into multiple macros (likeimpl_call
,impl_origin
, etc...) and these could be used by themselves or if the runtime does not require any special implementations, we could have the overarchingconstruct_runtime
calling these within itself if they are declared in the macro.The text was updated successfully, but these errors were encountered: