mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 11:41:04 +00:00
Allow pallet error enum variants to contain fields (#10242)
* Allow pallet errors to contain at most one field * Update docs on pallet::error * Reword documentation * cargo fmt * Introduce CompactPalletError trait and require #[pallet::error] fields to implement them * cargo fmt * Do not assume tuple variants * Add CompactPalletError derive macro * Check for error type compactness in construct_runtime * cargo fmt * Derive CompactPalletError instead of implementing it directly during macro expansion * Implement CompactPalletError on OptionBool instead of Option<bool> * Check for type idents instead of variant ident * Add doc comments for ErrorCompactnessTest * Add an trait implementation of ErrorCompactnessTest for () * Convert the error field of DispatchError to a 4-element byte array * Add static check for pallet error size * Rename to MAX_PALLET_ERROR_ENCODED_SIZE * Remove ErrorCompactnessTest trait * Remove check_compactness * Return only the most significant byte when constructing a custom InvalidTransaction * Rename CompactPalletError to PalletError * Use counter to generate unique idents for assert macros * Make declarative pallet macros compile with pallet error size checks * Remove unused doc comment * Try and fix build errors * Fix build errors * Add macro_use for some test modules * Test fix * Fix compilation errors * Remove unneeded #[macro_use] * Resolve import ambiguity * Make path to pallet Error enum more specific * Fix test expectation * Disambiguate imports * Fix test expectations * Revert appending pallet module name to path * Rename bags_list::list::Error to BagError * Fixes * Fixes * Fixes * Fix test expectations * Fix test expectation * Add more implementations for PalletError * Lift the 1-field requirement for nested pallet errors * Fix UI test expectation * Remove PalletError impl for OptionBool * Use saturating operations * cargo fmt * Delete obsolete test * Fix test expectation * Try and use assert macro in const context * Pull out the pallet error size check macro * Fix UI test for const assertion * cargo fmt * Apply clippy suggestion * Fix doc comment * Docs for create_tt_return_macro * Ensure TryInto is imported in earlier Rust editions * Apply suggestions from code review Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Fix up comments and names * Implement PalletError for Never * cargo fmt * Don't compile example code * Bump API version for block builder * Factor in codec attributes while derving PalletError * Rename module and fix unit test * Add missing attribute * Check API version and convert ApplyExtrinsicResult accordingly * Rename BagError to ListError Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Use codec crate re-exported from frame support * Add links to types mentioned in doc comments Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * cargo fmt * cargo fmt * Re-add attribute for hidden docs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
@@ -35,6 +35,7 @@ use sp_blockchain::{ApplyExtrinsicFailed, Error};
|
||||
use sp_core::ExecutionContext;
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
legacy,
|
||||
traits::{Block as BlockT, Hash, HashFor, Header as HeaderT, NumberFor, One},
|
||||
Digest,
|
||||
};
|
||||
@@ -135,6 +136,7 @@ where
|
||||
pub struct BlockBuilder<'a, Block: BlockT, A: ProvideRuntimeApi<Block>, B> {
|
||||
extrinsics: Vec<Block::Extrinsic>,
|
||||
api: ApiRef<'a, A::Api>,
|
||||
version: u32,
|
||||
block_id: BlockId<Block>,
|
||||
parent_hash: Block::Hash,
|
||||
backend: &'a B,
|
||||
@@ -183,10 +185,15 @@ where
|
||||
|
||||
api.initialize_block_with_context(&block_id, ExecutionContext::BlockConstruction, &header)?;
|
||||
|
||||
let version = api
|
||||
.api_version::<dyn BlockBuilderApi<Block>>(&block_id)?
|
||||
.ok_or_else(|| Error::VersionInvalid("BlockBuilderApi".to_string()))?;
|
||||
|
||||
Ok(Self {
|
||||
parent_hash,
|
||||
extrinsics: Vec::new(),
|
||||
api,
|
||||
version,
|
||||
block_id,
|
||||
backend,
|
||||
estimated_header_size,
|
||||
@@ -199,13 +206,26 @@ where
|
||||
pub fn push(&mut self, xt: <Block as BlockT>::Extrinsic) -> Result<(), Error> {
|
||||
let block_id = &self.block_id;
|
||||
let extrinsics = &mut self.extrinsics;
|
||||
let version = self.version;
|
||||
|
||||
self.api.execute_in_transaction(|api| {
|
||||
match api.apply_extrinsic_with_context(
|
||||
block_id,
|
||||
ExecutionContext::BlockConstruction,
|
||||
xt.clone(),
|
||||
) {
|
||||
let res = if version < 6 {
|
||||
#[allow(deprecated)]
|
||||
api.apply_extrinsic_before_version_6_with_context(
|
||||
block_id,
|
||||
ExecutionContext::BlockConstruction,
|
||||
xt.clone(),
|
||||
)
|
||||
.map(legacy::byte_sized_error::convert_to_latest)
|
||||
} else {
|
||||
api.apply_extrinsic_with_context(
|
||||
block_id,
|
||||
ExecutionContext::BlockConstruction,
|
||||
xt.clone(),
|
||||
)
|
||||
};
|
||||
|
||||
match res {
|
||||
Ok(Ok(_)) => {
|
||||
extrinsics.push(xt);
|
||||
TransactionOutcome::Commit(Ok(()))
|
||||
|
||||
@@ -527,7 +527,7 @@ fn should_return_runtime_version() {
|
||||
|
||||
let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\
|
||||
\"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\
|
||||
[\"0x37e397fc7c91f5e4\",1],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",5],\
|
||||
[\"0x37e397fc7c91f5e4\",1],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\
|
||||
[\"0xc6e9a76309f39b09\",1],[\"0xdd718d5cc53262d4\",1],[\"0xcbca25e39f142387\",2],\
|
||||
[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],[\"0xbc9d89904f5b923f\",1]],\
|
||||
\"transactionVersion\":1,\"stateVersion\":1}";
|
||||
|
||||
@@ -70,7 +70,7 @@ pub mod mock;
|
||||
mod tests;
|
||||
pub mod weights;
|
||||
|
||||
pub use list::{notional_bag_for, Bag, Error, List, Node};
|
||||
pub use list::{notional_bag_for, Bag, List, ListError, Node};
|
||||
pub use pallet::*;
|
||||
pub use weights::WeightInfo;
|
||||
|
||||
@@ -270,7 +270,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> SortedListProvider<T::AccountId> for Pallet<T, I> {
|
||||
type Error = Error;
|
||||
type Error = ListError;
|
||||
|
||||
type Score = T::Score;
|
||||
|
||||
@@ -286,7 +286,7 @@ impl<T: Config<I>, I: 'static> SortedListProvider<T::AccountId> for Pallet<T, I>
|
||||
List::<T, I>::contains(id)
|
||||
}
|
||||
|
||||
fn on_insert(id: T::AccountId, score: T::Score) -> Result<(), Error> {
|
||||
fn on_insert(id: T::AccountId, score: T::Score) -> Result<(), ListError> {
|
||||
List::<T, I>::insert(id, score)
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ use sp_std::{
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
pub enum ListError {
|
||||
/// A duplicate id has been detected.
|
||||
Duplicate,
|
||||
}
|
||||
@@ -266,9 +266,9 @@ impl<T: Config<I>, I: 'static> List<T, I> {
|
||||
/// Insert a new id into the appropriate bag in the list.
|
||||
///
|
||||
/// Returns an error if the list already contains `id`.
|
||||
pub(crate) fn insert(id: T::AccountId, score: T::Score) -> Result<(), Error> {
|
||||
pub(crate) fn insert(id: T::AccountId, score: T::Score) -> Result<(), ListError> {
|
||||
if Self::contains(&id) {
|
||||
return Err(Error::Duplicate)
|
||||
return Err(ListError::Duplicate)
|
||||
}
|
||||
|
||||
let bag_score = notional_bag_for::<T, I>(score);
|
||||
|
||||
@@ -248,7 +248,7 @@ mod list {
|
||||
// then
|
||||
assert_storage_noop!(assert_eq!(
|
||||
List::<Runtime>::insert(3, 20).unwrap_err(),
|
||||
Error::Duplicate
|
||||
ListError::Duplicate
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -518,7 +518,7 @@ mod sorted_list_provider {
|
||||
// then
|
||||
assert_storage_noop!(assert_eq!(
|
||||
BagsList::on_insert(3, 20).unwrap_err(),
|
||||
Error::Duplicate
|
||||
ListError::Duplicate
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1583,7 +1583,7 @@ impl<T: Config> ElectionProvider for Pallet<T> {
|
||||
/// number.
|
||||
pub fn dispatch_error_to_invalid(error: DispatchError) -> InvalidTransaction {
|
||||
let error_number = match error {
|
||||
DispatchError::Module(ModuleError { error, .. }) => error,
|
||||
DispatchError::Module(ModuleError { error, .. }) => error[0],
|
||||
_ => 0,
|
||||
};
|
||||
InvalidTransaction::Custom(error_number)
|
||||
|
||||
@@ -931,7 +931,7 @@ mod tests {
|
||||
#[test]
|
||||
#[should_panic(expected = "Invalid unsigned submission must produce invalid block and \
|
||||
deprive validator from their authoring reward.: \
|
||||
Module(ModuleError { index: 2, error: 1, message: \
|
||||
Module(ModuleError { index: 2, error: [1, 0, 0, 0], message: \
|
||||
Some(\"PreDispatchWrongWinnerCount\") })")]
|
||||
fn unfeasible_solution_panics() {
|
||||
ExtBuilder::default().build_and_execute(|| {
|
||||
@@ -1053,7 +1053,7 @@ mod tests {
|
||||
MultiPhase::basic_checks(&solution, "mined").unwrap_err(),
|
||||
MinerError::PreDispatchChecksFailed(DispatchError::Module(ModuleError {
|
||||
index: 2,
|
||||
error: 1,
|
||||
error: [1, 0, 0, 0],
|
||||
message: Some("PreDispatchWrongWinnerCount"),
|
||||
})),
|
||||
);
|
||||
|
||||
@@ -38,7 +38,7 @@ use sp_runtime::{
|
||||
// Logger module to track execution.
|
||||
#[frame_support::pallet]
|
||||
pub mod logger {
|
||||
use super::*;
|
||||
use super::{OriginCaller, OriginTrait};
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
use std::cell::RefCell;
|
||||
@@ -71,7 +71,7 @@ pub mod logger {
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T>
|
||||
where
|
||||
<T as system::Config>::Origin: OriginTrait<PalletsOrigin = OriginCaller>,
|
||||
<T as frame_system::Config>::Origin: OriginTrait<PalletsOrigin = OriginCaller>,
|
||||
{
|
||||
#[pallet::weight(*weight)]
|
||||
pub fn log(origin: OriginFor<T>, i: u32, weight: Weight) -> DispatchResult {
|
||||
|
||||
@@ -34,7 +34,6 @@ use sp_runtime::{
|
||||
// Logger module to track execution.
|
||||
#[frame_support::pallet]
|
||||
pub mod logger {
|
||||
use super::*;
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
|
||||
|
||||
@@ -241,6 +241,7 @@ fn construct_runtime_final_expansion(
|
||||
expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate);
|
||||
let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate);
|
||||
let integrity_test = decl_integrity_test(&scrate);
|
||||
let static_assertions = decl_static_assertions(&name, &pallets, &scrate);
|
||||
|
||||
let res = quote!(
|
||||
#scrate_decl
|
||||
@@ -282,6 +283,8 @@ fn construct_runtime_final_expansion(
|
||||
#validate_unsigned
|
||||
|
||||
#integrity_test
|
||||
|
||||
#static_assertions
|
||||
);
|
||||
|
||||
Ok(res)
|
||||
@@ -471,3 +474,34 @@ fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn decl_static_assertions(
|
||||
runtime: &Ident,
|
||||
pallet_decls: &[Pallet],
|
||||
scrate: &TokenStream2,
|
||||
) -> TokenStream2 {
|
||||
let error_encoded_size_check = pallet_decls.iter().map(|decl| {
|
||||
let path = &decl.path;
|
||||
let assert_message = format!(
|
||||
"The maximum encoded size of the error type in the `{}` pallet exceeds \
|
||||
`MAX_MODULE_ERROR_ENCODED_SIZE`",
|
||||
decl.name,
|
||||
);
|
||||
|
||||
quote! {
|
||||
#scrate::tt_call! {
|
||||
macro = [{ #path::tt_error_token }]
|
||||
frame_support = [{ #scrate }]
|
||||
~~> #scrate::assert_error_encoded_size! {
|
||||
path = [{ #path }]
|
||||
runtime = [{ #runtime }]
|
||||
assert_message = [{ #assert_message }]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#(#error_encoded_size_check)*
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,11 @@ mod dummy_part_checker;
|
||||
mod key_prefix;
|
||||
mod match_and_insert;
|
||||
mod pallet;
|
||||
mod pallet_error;
|
||||
mod partial_eq_no_bound;
|
||||
mod storage;
|
||||
mod transactional;
|
||||
mod tt_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use std::{cell::RefCell, str::FromStr};
|
||||
@@ -41,9 +43,9 @@ thread_local! {
|
||||
static COUNTER: RefCell<Counter> = RefCell::new(Counter(0));
|
||||
}
|
||||
|
||||
/// Counter to generate a relatively unique identifier for macros querying for the existence of
|
||||
/// pallet parts. This is necessary because declarative macros gets hoisted to the crate root,
|
||||
/// which shares the namespace with other pallets containing the very same query macros.
|
||||
/// Counter to generate a relatively unique identifier for macros. This is necessary because
|
||||
/// declarative macros gets hoisted to the crate root, which shares the namespace with other pallets
|
||||
/// containing the very same macros.
|
||||
struct Counter(u64);
|
||||
|
||||
impl Counter {
|
||||
@@ -562,3 +564,14 @@ pub fn __generate_dummy_part_checker(input: TokenStream) -> TokenStream {
|
||||
pub fn match_and_insert(input: TokenStream) -> TokenStream {
|
||||
match_and_insert::match_and_insert(input)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(PalletError, attributes(codec))]
|
||||
pub fn derive_pallet_error(input: TokenStream) -> TokenStream {
|
||||
pallet_error::derive_pallet_error(input)
|
||||
}
|
||||
|
||||
/// Internal macro used by `frame_support` to create tt-call-compliant macros
|
||||
#[proc_macro]
|
||||
pub fn __create_tt_macro(input: TokenStream) -> TokenStream {
|
||||
tt_macro::create_tt_return_macro(input)
|
||||
}
|
||||
|
||||
@@ -15,20 +15,48 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::pallet::Def;
|
||||
use crate::{
|
||||
pallet::{parse::error::VariantField, Def},
|
||||
COUNTER,
|
||||
};
|
||||
use frame_support_procedural_tools::get_doc_literals;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
///
|
||||
/// * impl various trait on Error
|
||||
pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let error = if let Some(error) = &def.error { error } else { return Default::default() };
|
||||
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
|
||||
let error_token_unique_id =
|
||||
syn::Ident::new(&format!("__tt_error_token_{}", count), def.item.span());
|
||||
|
||||
let error_ident = &error.error;
|
||||
let frame_support = &def.frame_support;
|
||||
let frame_system = &def.frame_system;
|
||||
let config_where_clause = &def.config.where_clause;
|
||||
|
||||
let error = if let Some(error) = &def.error {
|
||||
error
|
||||
} else {
|
||||
return quote::quote! {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #error_token_unique_id {
|
||||
{
|
||||
$caller:tt
|
||||
frame_support = [{ $($frame_support:ident)::* }]
|
||||
} => {
|
||||
$($frame_support::)*tt_return! {
|
||||
$caller
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub use #error_token_unique_id as tt_error_token;
|
||||
}
|
||||
};
|
||||
|
||||
let error_ident = &error.error;
|
||||
let type_impl_gen = &def.type_impl_generics(error.attr_span);
|
||||
let type_use_gen = &def.type_use_generics(error.attr_span);
|
||||
let config_where_clause = &def.config.where_clause;
|
||||
|
||||
let phantom_variant: syn::Variant = syn::parse_quote!(
|
||||
#[doc(hidden)]
|
||||
@@ -39,13 +67,19 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
)
|
||||
);
|
||||
|
||||
let as_u8_matches = error.variants.iter().enumerate().map(
|
||||
|(i, (variant, _))| quote::quote_spanned!(error.attr_span => Self::#variant => #i as u8,),
|
||||
);
|
||||
|
||||
let as_str_matches = error.variants.iter().map(|(variant, _)| {
|
||||
let variant_str = format!("{}", variant);
|
||||
quote::quote_spanned!(error.attr_span => Self::#variant => #variant_str,)
|
||||
let as_str_matches = error.variants.iter().map(|(variant, field_ty, _)| {
|
||||
let variant_str = variant.to_string();
|
||||
match field_ty {
|
||||
Some(VariantField { is_named: true }) => {
|
||||
quote::quote_spanned!(error.attr_span => Self::#variant { .. } => #variant_str,)
|
||||
},
|
||||
Some(VariantField { is_named: false }) => {
|
||||
quote::quote_spanned!(error.attr_span => Self::#variant(..) => #variant_str,)
|
||||
},
|
||||
None => {
|
||||
quote::quote_spanned!(error.attr_span => Self::#variant => #variant_str,)
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
let error_item = {
|
||||
@@ -62,9 +96,14 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" };
|
||||
|
||||
// derive TypeInfo for error metadata
|
||||
error_item
|
||||
.attrs
|
||||
.push(syn::parse_quote!( #[derive(#frame_support::scale_info::TypeInfo)] ));
|
||||
error_item.attrs.push(syn::parse_quote! {
|
||||
#[derive(
|
||||
#frame_support::codec::Encode,
|
||||
#frame_support::codec::Decode,
|
||||
#frame_support::scale_info::TypeInfo,
|
||||
#frame_support::PalletError,
|
||||
)]
|
||||
});
|
||||
error_item.attrs.push(syn::parse_quote!(
|
||||
#[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)]
|
||||
));
|
||||
@@ -90,14 +129,6 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause {
|
||||
#[doc(hidden)]
|
||||
pub fn as_u8(&self) -> u8 {
|
||||
match &self {
|
||||
Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"),
|
||||
#( #as_u8_matches )*
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match &self {
|
||||
@@ -120,18 +151,37 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
#config_where_clause
|
||||
{
|
||||
fn from(err: #error_ident<#type_use_gen>) -> Self {
|
||||
use #frame_support::codec::Encode;
|
||||
let index = <
|
||||
<T as #frame_system::Config>::PalletInfo
|
||||
as #frame_support::traits::PalletInfo
|
||||
>::index::<Pallet<#type_use_gen>>()
|
||||
.expect("Every active module has an index in the runtime; qed") as u8;
|
||||
let mut encoded = err.encode();
|
||||
encoded.resize(#frame_support::MAX_MODULE_ERROR_ENCODED_SIZE, 0);
|
||||
|
||||
#frame_support::sp_runtime::DispatchError::Module(#frame_support::sp_runtime::ModuleError {
|
||||
index,
|
||||
error: err.as_u8(),
|
||||
error: core::convert::TryInto::try_into(encoded).expect("encoded error is resized to be equal to the maximum encoded error size; qed"),
|
||||
message: Some(err.as_str()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #error_token_unique_id {
|
||||
{
|
||||
$caller:tt
|
||||
frame_support = [{ $($frame_support:ident)::* }]
|
||||
} => {
|
||||
$($frame_support::)*tt_return! {
|
||||
$caller
|
||||
error = [{ #error_ident }]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub use #error_token_unique_id as tt_error_token;
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,20 +18,26 @@
|
||||
use super::helper;
|
||||
use frame_support_procedural_tools::get_doc_literals;
|
||||
use quote::ToTokens;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{spanned::Spanned, Fields};
|
||||
|
||||
/// List of additional token to be used for parsing.
|
||||
mod keyword {
|
||||
syn::custom_keyword!(Error);
|
||||
}
|
||||
|
||||
/// Records information about the error enum variants.
|
||||
pub struct VariantField {
|
||||
/// Whether or not the field is named, i.e. whether it is a tuple variant or struct variant.
|
||||
pub is_named: bool,
|
||||
}
|
||||
|
||||
/// This checks error declaration as a enum declaration with only variants without fields nor
|
||||
/// discriminant.
|
||||
pub struct ErrorDef {
|
||||
/// The index of error item in pallet module.
|
||||
pub index: usize,
|
||||
/// Variants ident and doc literals (ordered as declaration order)
|
||||
pub variants: Vec<(syn::Ident, Vec<syn::Lit>)>,
|
||||
/// Variants ident, optional field and doc literals (ordered as declaration order)
|
||||
pub variants: Vec<(syn::Ident, Option<VariantField>, Vec<syn::Lit>)>,
|
||||
/// A set of usage of instance, must be check for consistency with trait.
|
||||
pub instances: Vec<helper::InstanceUsage>,
|
||||
/// The keyword error used (contains span).
|
||||
@@ -70,18 +76,19 @@ impl ErrorDef {
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
if !matches!(variant.fields, syn::Fields::Unit) {
|
||||
let msg = "Invalid pallet::error, unexpected fields, must be `Unit`";
|
||||
return Err(syn::Error::new(variant.fields.span(), msg))
|
||||
}
|
||||
let field_ty = match &variant.fields {
|
||||
Fields::Unit => None,
|
||||
Fields::Named(_) => Some(VariantField { is_named: true }),
|
||||
Fields::Unnamed(_) => Some(VariantField { is_named: false }),
|
||||
};
|
||||
if variant.discriminant.is_some() {
|
||||
let msg = "Invalid pallet::error, unexpected discriminant, discriminant \
|
||||
let msg = "Invalid pallet::error, unexpected discriminant, discriminants \
|
||||
are not supported";
|
||||
let span = variant.discriminant.as_ref().unwrap().0.span();
|
||||
return Err(syn::Error::new(span, msg))
|
||||
}
|
||||
|
||||
Ok((variant.ident.clone(), get_doc_literals(&variant.attrs)))
|
||||
Ok((variant.ident.clone(), field_ty, get_doc_literals(&variant.attrs)))
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2021 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.
|
||||
|
||||
use frame_support_procedural_tools::generate_crate_access_2018;
|
||||
use quote::ToTokens;
|
||||
use std::str::FromStr;
|
||||
|
||||
// Derive `PalletError`
|
||||
pub fn derive_pallet_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let syn::DeriveInput { ident: name, generics, data, .. } = match syn::parse(input) {
|
||||
Ok(input) => input,
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
|
||||
let frame_support = match generate_crate_access_2018("frame-support") {
|
||||
Ok(c) => c,
|
||||
Err(e) => return e.into_compile_error().into(),
|
||||
};
|
||||
let frame_support = &frame_support;
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let max_encoded_size = match data {
|
||||
syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
|
||||
syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) |
|
||||
syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => {
|
||||
let maybe_field_tys = fields
|
||||
.iter()
|
||||
.map(|f| generate_field_types(f, &frame_support))
|
||||
.collect::<syn::Result<Vec<_>>>();
|
||||
let field_tys = match maybe_field_tys {
|
||||
Ok(tys) => tys.into_iter().flatten(),
|
||||
Err(e) => return e.into_compile_error().into(),
|
||||
};
|
||||
quote::quote! {
|
||||
0_usize
|
||||
#(
|
||||
.saturating_add(<
|
||||
#field_tys as #frame_support::traits::PalletError
|
||||
>::MAX_ENCODED_SIZE)
|
||||
)*
|
||||
}
|
||||
},
|
||||
syn::Fields::Unit => quote::quote!(0),
|
||||
},
|
||||
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
|
||||
let field_tys = variants
|
||||
.iter()
|
||||
.map(|variant| generate_variant_field_types(variant, &frame_support))
|
||||
.collect::<Result<Vec<Option<Vec<proc_macro2::TokenStream>>>, syn::Error>>();
|
||||
|
||||
let field_tys = match field_tys {
|
||||
Ok(tys) => tys.into_iter().flatten().collect::<Vec<_>>(),
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
|
||||
// We start with `1`, because the discriminant of an enum is stored as u8
|
||||
if field_tys.is_empty() {
|
||||
quote::quote!(1)
|
||||
} else {
|
||||
let variant_sizes = field_tys.into_iter().map(|variant_field_tys| {
|
||||
quote::quote! {
|
||||
1_usize
|
||||
#(.saturating_add(<
|
||||
#variant_field_tys as #frame_support::traits::PalletError
|
||||
>::MAX_ENCODED_SIZE))*
|
||||
}
|
||||
});
|
||||
|
||||
quote::quote! {{
|
||||
let mut size = 1_usize;
|
||||
let mut tmp = 0_usize;
|
||||
#(
|
||||
tmp = #variant_sizes;
|
||||
size = if tmp > size { tmp } else { size };
|
||||
tmp = 0_usize;
|
||||
)*
|
||||
size
|
||||
}}
|
||||
}
|
||||
},
|
||||
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
|
||||
let msg = "Cannot derive `PalletError` for union; please implement it directly";
|
||||
return syn::Error::new(union_token.span, msg).into_compile_error().into()
|
||||
},
|
||||
};
|
||||
|
||||
quote::quote!(
|
||||
const _: () = {
|
||||
impl #impl_generics #frame_support::traits::PalletError
|
||||
for #name #ty_generics #where_clause
|
||||
{
|
||||
const MAX_ENCODED_SIZE: usize = #max_encoded_size;
|
||||
}
|
||||
};
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn generate_field_types(
|
||||
field: &syn::Field,
|
||||
scrate: &syn::Ident,
|
||||
) -> syn::Result<Option<proc_macro2::TokenStream>> {
|
||||
let attrs = &field.attrs;
|
||||
|
||||
for attr in attrs {
|
||||
if attr.path.is_ident("codec") {
|
||||
match attr.parse_meta()? {
|
||||
syn::Meta::List(ref meta_list) if meta_list.nested.len() == 1 => {
|
||||
match meta_list
|
||||
.nested
|
||||
.first()
|
||||
.expect("Just checked that there is one item; qed")
|
||||
{
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(path))
|
||||
if path.get_ident().map_or(false, |i| i == "skip") =>
|
||||
return Ok(None),
|
||||
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(path))
|
||||
if path.get_ident().map_or(false, |i| i == "compact") =>
|
||||
{
|
||||
let field_ty = &field.ty;
|
||||
return Ok(Some(quote::quote!(#scrate::codec::Compact<#field_ty>)))
|
||||
},
|
||||
|
||||
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
|
||||
path,
|
||||
lit: syn::Lit::Str(lit_str),
|
||||
..
|
||||
})) if path.get_ident().map_or(false, |i| i == "encoded_as") => {
|
||||
let ty = proc_macro2::TokenStream::from_str(&lit_str.value())?;
|
||||
return Ok(Some(ty))
|
||||
},
|
||||
|
||||
_ => (),
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(field.ty.to_token_stream()))
|
||||
}
|
||||
|
||||
fn generate_variant_field_types(
|
||||
variant: &syn::Variant,
|
||||
scrate: &syn::Ident,
|
||||
) -> syn::Result<Option<Vec<proc_macro2::TokenStream>>> {
|
||||
let attrs = &variant.attrs;
|
||||
|
||||
for attr in attrs {
|
||||
if attr.path.is_ident("codec") {
|
||||
match attr.parse_meta()? {
|
||||
syn::Meta::List(ref meta_list) if meta_list.nested.len() == 1 => {
|
||||
match meta_list
|
||||
.nested
|
||||
.first()
|
||||
.expect("Just checked that there is one item; qed")
|
||||
{
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(path))
|
||||
if path.get_ident().map_or(false, |i| i == "skip") =>
|
||||
return Ok(None),
|
||||
|
||||
_ => (),
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match &variant.fields {
|
||||
syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) |
|
||||
syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => {
|
||||
let field_tys = fields
|
||||
.iter()
|
||||
.map(|field| generate_field_types(field, scrate))
|
||||
.collect::<syn::Result<Vec<_>>>()?;
|
||||
Ok(Some(field_tys.into_iter().flatten().collect()))
|
||||
},
|
||||
syn::Fields::Unit => Ok(None),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2022 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.
|
||||
|
||||
//! Implementation of the `create_tt_return_macro` macro
|
||||
|
||||
use crate::COUNTER;
|
||||
use frame_support_procedural_tools::generate_crate_access_2018;
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::format_ident;
|
||||
|
||||
struct CreateTtReturnMacroDef {
|
||||
name: Ident,
|
||||
args: Vec<(Ident, TokenStream)>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for CreateTtReturnMacroDef {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let name = input.parse()?;
|
||||
let _ = input.parse::<syn::Token![,]>()?;
|
||||
|
||||
let mut args = Vec::new();
|
||||
while !input.is_empty() {
|
||||
let mut value;
|
||||
let key: Ident = input.parse()?;
|
||||
let _ = input.parse::<syn::Token![=]>()?;
|
||||
let _: syn::token::Bracket = syn::bracketed!(value in input);
|
||||
let _: syn::token::Brace = syn::braced!(value in value);
|
||||
let value: TokenStream = value.parse()?;
|
||||
|
||||
args.push((key, value))
|
||||
}
|
||||
|
||||
Ok(Self { name, args })
|
||||
}
|
||||
}
|
||||
|
||||
/// A proc macro that accepts a name and any number of key-value pairs, to be used to create a
|
||||
/// declarative macro that follows tt-call conventions and simply calls [`tt_call::tt_return`],
|
||||
/// accepting an optional `frame-support` argument and returning the key-value pairs that were
|
||||
/// supplied to the proc macro.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// __create_tt_macro! {
|
||||
/// my_tt_macro,
|
||||
/// foo = [{ bar }]
|
||||
/// }
|
||||
///
|
||||
/// // Creates the following declarative macro:
|
||||
///
|
||||
/// macro_rules! my_tt_macro {
|
||||
/// {
|
||||
/// $caller:tt
|
||||
/// $(frame_support = [{ $($frame_support:ident)::* }])?
|
||||
/// } => {
|
||||
/// frame_support::tt_return! {
|
||||
/// $caller
|
||||
/// foo = [{ bar }]
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn create_tt_return_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let CreateTtReturnMacroDef { name, args } =
|
||||
syn::parse_macro_input!(input as CreateTtReturnMacroDef);
|
||||
|
||||
let frame_support = match generate_crate_access_2018("frame-support") {
|
||||
Ok(i) => i,
|
||||
Err(e) => return e.into_compile_error().into(),
|
||||
};
|
||||
let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().unzip();
|
||||
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
|
||||
let unique_name = format_ident!("{}_{}", name, count);
|
||||
|
||||
let decl_macro = quote::quote! {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #unique_name {
|
||||
{
|
||||
$caller:tt
|
||||
$(frame_support = [{ $($frame_support:ident)::* }])?
|
||||
} => {
|
||||
#frame_support::tt_return! {
|
||||
$caller
|
||||
#(
|
||||
#keys = [{ #values }]
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use #unique_name as #name;
|
||||
};
|
||||
|
||||
decl_macro.into()
|
||||
}
|
||||
@@ -2003,6 +2003,10 @@ macro_rules! decl_module {
|
||||
pub type Pallet<$trait_instance $(, $instance $( = $module_default_instance)?)?>
|
||||
= $mod_type<$trait_instance $(, $instance)?>;
|
||||
|
||||
$crate::__create_tt_macro! {
|
||||
tt_error_token,
|
||||
}
|
||||
|
||||
$crate::decl_module! {
|
||||
@impl_on_initialize
|
||||
{ $system }
|
||||
|
||||
@@ -85,7 +85,12 @@ macro_rules! decl_error {
|
||||
}
|
||||
) => {
|
||||
$(#[$attr])*
|
||||
#[derive($crate::scale_info::TypeInfo)]
|
||||
#[derive(
|
||||
$crate::codec::Encode,
|
||||
$crate::codec::Decode,
|
||||
$crate::scale_info::TypeInfo,
|
||||
$crate::PalletError,
|
||||
)]
|
||||
#[scale_info(skip_type_params($generic $(, $inst_generic)?), capture_docs = "always")]
|
||||
pub enum $error<$generic: $trait $(, $inst_generic: $instance)?>
|
||||
$( where $( $where_ty: $where_bound ),* )?
|
||||
@@ -114,17 +119,6 @@ macro_rules! decl_error {
|
||||
impl<$generic: $trait $(, $inst_generic: $instance)?> $error<$generic $(, $inst_generic)?>
|
||||
$( where $( $where_ty: $where_bound ),* )?
|
||||
{
|
||||
fn as_u8(&self) -> u8 {
|
||||
$crate::decl_error! {
|
||||
@GENERATE_AS_U8
|
||||
self
|
||||
$error
|
||||
{}
|
||||
0,
|
||||
$( $name ),*
|
||||
}
|
||||
}
|
||||
|
||||
fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"),
|
||||
@@ -149,47 +143,19 @@ macro_rules! decl_error {
|
||||
$( where $( $where_ty: $where_bound ),* )?
|
||||
{
|
||||
fn from(err: $error<$generic $(, $inst_generic)?>) -> Self {
|
||||
use $crate::codec::Encode;
|
||||
let index = <$generic::PalletInfo as $crate::traits::PalletInfo>
|
||||
::index::<$module<$generic $(, $inst_generic)?>>()
|
||||
.expect("Every active module has an index in the runtime; qed") as u8;
|
||||
let mut error = err.encode();
|
||||
error.resize($crate::MAX_MODULE_ERROR_ENCODED_SIZE, 0);
|
||||
|
||||
$crate::sp_runtime::DispatchError::Module($crate::sp_runtime::ModuleError {
|
||||
index,
|
||||
error: err.as_u8(),
|
||||
error: core::convert::TryInto::try_into(error).expect("encoded error is resized to be equal to the maximum encoded error size; qed"),
|
||||
message: Some(err.as_str()),
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
(@GENERATE_AS_U8
|
||||
$self:ident
|
||||
$error:ident
|
||||
{ $( $generated:tt )* }
|
||||
$index:expr,
|
||||
$name:ident
|
||||
$( , $rest:ident )*
|
||||
) => {
|
||||
$crate::decl_error! {
|
||||
@GENERATE_AS_U8
|
||||
$self
|
||||
$error
|
||||
{
|
||||
$( $generated )*
|
||||
$error::$name => $index,
|
||||
}
|
||||
$index + 1,
|
||||
$( $rest ),*
|
||||
}
|
||||
};
|
||||
(@GENERATE_AS_U8
|
||||
$self:ident
|
||||
$error:ident
|
||||
{ $( $generated:tt )* }
|
||||
$index:expr,
|
||||
) => {
|
||||
match $self {
|
||||
$error::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"),
|
||||
$( $generated )*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,9 @@ pub use self::{
|
||||
StorageMap, StorageNMap, StoragePrefixedMap, StorageValue,
|
||||
},
|
||||
};
|
||||
pub use sp_runtime::{self, print, traits::Printable, ConsensusEngineId};
|
||||
pub use sp_runtime::{
|
||||
self, print, traits::Printable, ConsensusEngineId, MAX_MODULE_ERROR_ENCODED_SIZE,
|
||||
};
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use scale_info::TypeInfo;
|
||||
@@ -103,7 +105,7 @@ use sp_runtime::TypeId;
|
||||
pub const LOG_TARGET: &'static str = "runtime::frame-support";
|
||||
|
||||
/// A type that cannot be instantiated.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, TypeInfo)]
|
||||
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
|
||||
pub enum Never {}
|
||||
|
||||
/// A pallet identifier. These are per pallet and should be stored in a registry somewhere.
|
||||
@@ -598,11 +600,12 @@ pub fn debug(data: &impl sp_std::fmt::Debug) {
|
||||
|
||||
#[doc(inline)]
|
||||
pub use frame_support_procedural::{
|
||||
construct_runtime, decl_storage, match_and_insert, transactional, RuntimeDebugNoBound,
|
||||
construct_runtime, decl_storage, match_and_insert, transactional, PalletError,
|
||||
RuntimeDebugNoBound,
|
||||
};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use frame_support_procedural::__generate_dummy_part_checker;
|
||||
pub use frame_support_procedural::{__create_tt_macro, __generate_dummy_part_checker};
|
||||
|
||||
/// Derive [`Clone`] but do not bound any generic.
|
||||
///
|
||||
@@ -847,6 +850,32 @@ macro_rules! assert_ok {
|
||||
};
|
||||
}
|
||||
|
||||
/// Assert that the maximum encoding size does not exceed the value defined in
|
||||
/// [`MAX_MODULE_ERROR_ENCODED_SIZE`] during compilation.
|
||||
///
|
||||
/// This macro is intended to be used in conjunction with `tt_call!`.
|
||||
#[macro_export]
|
||||
macro_rules! assert_error_encoded_size {
|
||||
{
|
||||
path = [{ $($path:ident)::+ }]
|
||||
runtime = [{ $runtime:ident }]
|
||||
assert_message = [{ $assert_message:literal }]
|
||||
error = [{ $error:ident }]
|
||||
} => {
|
||||
const _: () = assert!(
|
||||
<
|
||||
$($path::)+$error<$runtime> as $crate::traits::PalletError
|
||||
>::MAX_ENCODED_SIZE <= $crate::MAX_MODULE_ERROR_ENCODED_SIZE,
|
||||
$assert_message
|
||||
);
|
||||
};
|
||||
{
|
||||
path = [{ $($path:ident)::+ }]
|
||||
runtime = [{ $runtime:ident }]
|
||||
assert_message = [{ $assert_message:literal }]
|
||||
} => {};
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[doc(hidden)]
|
||||
pub use serde::{Deserialize, Serialize};
|
||||
@@ -1375,6 +1404,7 @@ pub mod pallet_prelude {
|
||||
TransactionTag, TransactionValidity, TransactionValidityError, UnknownTransaction,
|
||||
ValidTransaction,
|
||||
},
|
||||
MAX_MODULE_ERROR_ENCODED_SIZE,
|
||||
};
|
||||
pub use sp_std::marker::PhantomData;
|
||||
}
|
||||
@@ -1652,10 +1682,25 @@ pub mod pallet_prelude {
|
||||
/// pub enum Error<T> {
|
||||
/// /// $some_optional_doc
|
||||
/// $SomeFieldLessVariant,
|
||||
/// /// $some_more_optional_doc
|
||||
/// $SomeVariantWithOneField(FieldType),
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
/// I.e. a regular rust enum named `Error`, with generic `T` and fieldless variants.
|
||||
/// I.e. a regular rust enum named `Error`, with generic `T` and fieldless or multiple-field
|
||||
/// variants.
|
||||
///
|
||||
/// Any field type in the enum variants must implement [`scale_info::TypeInfo`] in order to be
|
||||
/// properly used in the metadata, and its encoded size should be as small as possible,
|
||||
/// preferably 1 byte in size in order to reduce storage size. The error enum itself has an
|
||||
/// absolute maximum encoded size specified by [`MAX_MODULE_ERROR_ENCODED_SIZE`].
|
||||
///
|
||||
/// Field types in enum variants must also implement [`PalletError`](traits::PalletError),
|
||||
/// otherwise the pallet will fail to compile. Rust primitive types have already implemented
|
||||
/// the [`PalletError`](traits::PalletError) trait along with some commonly used stdlib types
|
||||
/// such as `Option` and `PhantomData`, and hence in most use cases, a manual implementation is
|
||||
/// not necessary and is discouraged.
|
||||
///
|
||||
/// The generic `T` mustn't bound anything and where clause is not allowed. But bounds and
|
||||
/// where clause shouldn't be needed for any usecase.
|
||||
///
|
||||
|
||||
@@ -46,6 +46,9 @@ pub use validation::{
|
||||
ValidatorSetWithIdentification, VerifySeal,
|
||||
};
|
||||
|
||||
mod error;
|
||||
pub use error::PalletError;
|
||||
|
||||
mod filter;
|
||||
pub use filter::{ClearFilterGuard, FilterStack, FilterStackGuard, InstanceFilter, IntegrityTest};
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2022 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.
|
||||
|
||||
//! Traits for describing and constraining pallet error types.
|
||||
use codec::{Compact, Decode, Encode};
|
||||
use sp_std::marker::PhantomData;
|
||||
|
||||
/// Trait indicating that the implementing type is going to be included as a field in a variant of
|
||||
/// the `#[pallet::error]` enum type.
|
||||
///
|
||||
/// ## Notes
|
||||
///
|
||||
/// The pallet error enum has a maximum encoded size as defined by
|
||||
/// [`frame_support::MAX_MODULE_ERROR_ENCODED_SIZE`]. If the pallet error type exceeds this size
|
||||
/// limit, a static assertion during compilation will fail. The compilation error will be in the
|
||||
/// format of `error[E0080]: evaluation of constant value failed` due to the usage of
|
||||
/// const assertions.
|
||||
pub trait PalletError: Encode + Decode {
|
||||
/// The maximum encoded size for the implementing type.
|
||||
///
|
||||
/// This will be used to check whether the pallet error type is less than or equal to
|
||||
/// [`frame_support::MAX_MODULE_ERROR_ENCODED_SIZE`], and if it is, a compilation error will be
|
||||
/// thrown.
|
||||
const MAX_ENCODED_SIZE: usize;
|
||||
}
|
||||
|
||||
macro_rules! impl_for_types {
|
||||
(size: $size:expr, $($typ:ty),+) => {
|
||||
$(
|
||||
impl PalletError for $typ {
|
||||
const MAX_ENCODED_SIZE: usize = $size;
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
impl_for_types!(size: 0, (), crate::Never);
|
||||
impl_for_types!(size: 1, u8, i8, bool);
|
||||
impl_for_types!(size: 2, u16, i16, Compact<u8>);
|
||||
impl_for_types!(size: 4, u32, i32, Compact<u16>);
|
||||
impl_for_types!(size: 5, Compact<u32>);
|
||||
impl_for_types!(size: 8, u64, i64);
|
||||
impl_for_types!(size: 9, Compact<u64>);
|
||||
// Contains a u64 for secs and u32 for nanos, hence 12 bytes
|
||||
impl_for_types!(size: 12, core::time::Duration);
|
||||
impl_for_types!(size: 16, u128, i128);
|
||||
impl_for_types!(size: 17, Compact<u128>);
|
||||
|
||||
impl<T> PalletError for PhantomData<T> {
|
||||
const MAX_ENCODED_SIZE: usize = 0;
|
||||
}
|
||||
|
||||
impl<T: PalletError> PalletError for core::ops::Range<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE.saturating_mul(2);
|
||||
}
|
||||
|
||||
impl<T: PalletError, const N: usize> PalletError for [T; N] {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE.saturating_mul(N);
|
||||
}
|
||||
|
||||
impl<T: PalletError> PalletError for Option<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE.saturating_add(1);
|
||||
}
|
||||
|
||||
impl<T: PalletError, E: PalletError> PalletError for Result<T, E> {
|
||||
const MAX_ENCODED_SIZE: usize = if T::MAX_ENCODED_SIZE > E::MAX_ENCODED_SIZE {
|
||||
T::MAX_ENCODED_SIZE
|
||||
} else {
|
||||
E::MAX_ENCODED_SIZE
|
||||
}
|
||||
.saturating_add(1);
|
||||
}
|
||||
|
||||
#[impl_trait_for_tuples::impl_for_tuples(1, 18)]
|
||||
impl PalletError for Tuple {
|
||||
const MAX_ENCODED_SIZE: usize = {
|
||||
let mut size = 0_usize;
|
||||
for_tuples!( #(size = size.saturating_add(Tuple::MAX_ENCODED_SIZE);)* );
|
||||
size
|
||||
};
|
||||
}
|
||||
@@ -271,139 +271,95 @@ pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, Call, Signature, ()>;
|
||||
|
||||
mod origin_test {
|
||||
use super::{module3, nested, system, Block, UncheckedExtrinsic};
|
||||
use frame_support::traits::{Contains, OriginTrait};
|
||||
|
||||
impl nested::module3::Config for RuntimeOriginTest {}
|
||||
impl module3::Config for RuntimeOriginTest {}
|
||||
|
||||
pub struct BaseCallFilter;
|
||||
impl Contains<Call> for BaseCallFilter {
|
||||
fn contains(c: &Call) -> bool {
|
||||
match c {
|
||||
Call::NestedModule3(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl system::Config for RuntimeOriginTest {
|
||||
type BaseCallFilter = BaseCallFilter;
|
||||
type Hash = super::H256;
|
||||
type Origin = Origin;
|
||||
type BlockNumber = super::BlockNumber;
|
||||
type AccountId = u32;
|
||||
type Event = Event;
|
||||
type PalletInfo = PalletInfo;
|
||||
type Call = Call;
|
||||
type DbWeight = ();
|
||||
}
|
||||
|
||||
frame_support::construct_runtime!(
|
||||
pub enum RuntimeOriginTest where
|
||||
Block = Block,
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic
|
||||
{
|
||||
System: system::{Pallet, Event<T>, Origin<T>},
|
||||
NestedModule3: nested::module3::{Pallet, Origin, Call},
|
||||
Module3: module3::{Pallet, Origin<T>, Call},
|
||||
}
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn origin_default_filter() {
|
||||
let accepted_call = nested::module3::Call::fail {}.into();
|
||||
let rejected_call = module3::Call::fail {}.into();
|
||||
|
||||
assert_eq!(Origin::root().filter_call(&accepted_call), true);
|
||||
assert_eq!(Origin::root().filter_call(&rejected_call), true);
|
||||
assert_eq!(Origin::none().filter_call(&accepted_call), true);
|
||||
assert_eq!(Origin::none().filter_call(&rejected_call), false);
|
||||
assert_eq!(Origin::signed(0).filter_call(&accepted_call), true);
|
||||
assert_eq!(Origin::signed(0).filter_call(&rejected_call), false);
|
||||
assert_eq!(Origin::from(Some(0)).filter_call(&accepted_call), true);
|
||||
assert_eq!(Origin::from(Some(0)).filter_call(&rejected_call), false);
|
||||
assert_eq!(Origin::from(None).filter_call(&accepted_call), true);
|
||||
assert_eq!(Origin::from(None).filter_call(&rejected_call), false);
|
||||
assert_eq!(Origin::from(super::nested::module3::Origin).filter_call(&accepted_call), true);
|
||||
assert_eq!(Origin::from(super::nested::module3::Origin).filter_call(&rejected_call), false);
|
||||
|
||||
let mut origin = Origin::from(Some(0));
|
||||
origin.add_filter(|c| matches!(c, Call::Module3(_)));
|
||||
assert_eq!(origin.filter_call(&accepted_call), false);
|
||||
assert_eq!(origin.filter_call(&rejected_call), false);
|
||||
|
||||
// Now test for root origin and filters:
|
||||
let mut origin = Origin::from(Some(0));
|
||||
origin.set_caller_from(Origin::root());
|
||||
assert!(matches!(origin.caller, OriginCaller::system(super::system::RawOrigin::Root)));
|
||||
|
||||
// Root origin bypass all filter.
|
||||
assert_eq!(origin.filter_call(&accepted_call), true);
|
||||
assert_eq!(origin.filter_call(&rejected_call), true);
|
||||
|
||||
origin.set_caller_from(Origin::from(Some(0)));
|
||||
|
||||
// Back to another signed origin, the filtered are now effective again
|
||||
assert_eq!(origin.filter_call(&accepted_call), true);
|
||||
assert_eq!(origin.filter_call(&rejected_call), false);
|
||||
|
||||
origin.set_caller_from(Origin::root());
|
||||
origin.reset_filter();
|
||||
|
||||
// Root origin bypass all filter, even when they are reset.
|
||||
assert_eq!(origin.filter_call(&accepted_call), true);
|
||||
assert_eq!(origin.filter_call(&rejected_call), true);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_modules_error_type() {
|
||||
assert_eq!(
|
||||
Module1_1::fail(system::Origin::<Runtime>::Root.into()),
|
||||
Err(DispatchError::Module(ModuleError { index: 31, error: 0, message: Some("Something") })),
|
||||
Err(DispatchError::Module(ModuleError {
|
||||
index: 31,
|
||||
error: [0; 4],
|
||||
message: Some("Something")
|
||||
})),
|
||||
);
|
||||
assert_eq!(
|
||||
Module2::fail(system::Origin::<Runtime>::Root.into()),
|
||||
Err(DispatchError::Module(ModuleError { index: 32, error: 0, message: Some("Something") })),
|
||||
Err(DispatchError::Module(ModuleError {
|
||||
index: 32,
|
||||
error: [0; 4],
|
||||
message: Some("Something")
|
||||
})),
|
||||
);
|
||||
assert_eq!(
|
||||
Module1_2::fail(system::Origin::<Runtime>::Root.into()),
|
||||
Err(DispatchError::Module(ModuleError { index: 33, error: 0, message: Some("Something") })),
|
||||
Err(DispatchError::Module(ModuleError {
|
||||
index: 33,
|
||||
error: [0; 4],
|
||||
message: Some("Something")
|
||||
})),
|
||||
);
|
||||
assert_eq!(
|
||||
NestedModule3::fail(system::Origin::<Runtime>::Root.into()),
|
||||
Err(DispatchError::Module(ModuleError { index: 34, error: 0, message: Some("Something") })),
|
||||
Err(DispatchError::Module(ModuleError {
|
||||
index: 34,
|
||||
error: [0; 4],
|
||||
message: Some("Something")
|
||||
})),
|
||||
);
|
||||
assert_eq!(
|
||||
Module1_3::fail(system::Origin::<Runtime>::Root.into()),
|
||||
Err(DispatchError::Module(ModuleError { index: 6, error: 0, message: Some("Something") })),
|
||||
Err(DispatchError::Module(ModuleError {
|
||||
index: 6,
|
||||
error: [0; 4],
|
||||
message: Some("Something")
|
||||
})),
|
||||
);
|
||||
assert_eq!(
|
||||
Module1_4::fail(system::Origin::<Runtime>::Root.into()),
|
||||
Err(DispatchError::Module(ModuleError { index: 3, error: 0, message: Some("Something") })),
|
||||
Err(DispatchError::Module(ModuleError {
|
||||
index: 3,
|
||||
error: [0; 4],
|
||||
message: Some("Something")
|
||||
})),
|
||||
);
|
||||
assert_eq!(
|
||||
Module1_5::fail(system::Origin::<Runtime>::Root.into()),
|
||||
Err(DispatchError::Module(ModuleError { index: 4, error: 0, message: Some("Something") })),
|
||||
Err(DispatchError::Module(ModuleError {
|
||||
index: 4,
|
||||
error: [0; 4],
|
||||
message: Some("Something")
|
||||
})),
|
||||
);
|
||||
assert_eq!(
|
||||
Module1_6::fail(system::Origin::<Runtime>::Root.into()),
|
||||
Err(DispatchError::Module(ModuleError { index: 1, error: 0, message: Some("Something") })),
|
||||
Err(DispatchError::Module(ModuleError {
|
||||
index: 1,
|
||||
error: [0; 4],
|
||||
message: Some("Something")
|
||||
})),
|
||||
);
|
||||
assert_eq!(
|
||||
Module1_7::fail(system::Origin::<Runtime>::Root.into()),
|
||||
Err(DispatchError::Module(ModuleError { index: 2, error: 0, message: Some("Something") })),
|
||||
Err(DispatchError::Module(ModuleError {
|
||||
index: 2,
|
||||
error: [0; 4],
|
||||
message: Some("Something")
|
||||
})),
|
||||
);
|
||||
assert_eq!(
|
||||
Module1_8::fail(system::Origin::<Runtime>::Root.into()),
|
||||
Err(DispatchError::Module(ModuleError { index: 12, error: 0, message: Some("Something") })),
|
||||
Err(DispatchError::Module(ModuleError {
|
||||
index: 12,
|
||||
error: [0; 4],
|
||||
message: Some("Something")
|
||||
})),
|
||||
);
|
||||
assert_eq!(
|
||||
Module1_9::fail(system::Origin::<Runtime>::Root.into()),
|
||||
Err(DispatchError::Module(ModuleError { index: 13, error: 0, message: Some("Something") })),
|
||||
Err(DispatchError::Module(ModuleError {
|
||||
index: 13,
|
||||
error: [0; 4],
|
||||
message: Some("Something")
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
use frame_support::construct_runtime;
|
||||
use sp_runtime::{generic, traits::BlakeTwo256};
|
||||
use sp_core::sr25519;
|
||||
|
||||
#[frame_support::pallet]
|
||||
mod pallet {
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
MyError(crate::Nested1),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(scale_info::TypeInfo, frame_support::PalletError, codec::Encode, codec::Decode)]
|
||||
pub enum Nested1 {
|
||||
Nested2(Nested2)
|
||||
}
|
||||
|
||||
#[derive(scale_info::TypeInfo, frame_support::PalletError, codec::Encode, codec::Decode)]
|
||||
pub enum Nested2 {
|
||||
Nested3(Nested3)
|
||||
}
|
||||
|
||||
#[derive(scale_info::TypeInfo, frame_support::PalletError, codec::Encode, codec::Decode)]
|
||||
pub enum Nested3 {
|
||||
Nested4(Nested4)
|
||||
}
|
||||
|
||||
#[derive(scale_info::TypeInfo, frame_support::PalletError, codec::Encode, codec::Decode)]
|
||||
pub enum Nested4 {
|
||||
Num(u8)
|
||||
}
|
||||
|
||||
pub type Signature = sr25519::Signature;
|
||||
pub type BlockNumber = u32;
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, Call, Signature, ()>;
|
||||
|
||||
impl pallet::Config for Runtime {}
|
||||
|
||||
impl frame_system::Config for Runtime {
|
||||
type BaseCallFilter = frame_support::traits::Everything;
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u32;
|
||||
type Call = Call;
|
||||
type Hash = sp_runtime::testing::H256;
|
||||
type Hashing = sp_runtime::traits::BlakeTwo256;
|
||||
type AccountId = u64;
|
||||
type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type Event = Event;
|
||||
type BlockHashCount = frame_support::traits::ConstU32<250>;
|
||||
type BlockWeights = ();
|
||||
type BlockLength = ();
|
||||
type DbWeight = ();
|
||||
type Version = ();
|
||||
type PalletInfo = PalletInfo;
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type SystemWeightInfo = ();
|
||||
type SS58Prefix = ();
|
||||
type OnSetCode = ();
|
||||
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
||||
}
|
||||
|
||||
construct_runtime! {
|
||||
pub enum Runtime where
|
||||
Block = Block,
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic
|
||||
{
|
||||
System: frame_system::{Pallet, Call, Storage, Config, Event<T>},
|
||||
Pallet: pallet::{Pallet},
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,13 @@
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> tests/construct_runtime_ui/pallet_error_too_large.rs:74:1
|
||||
|
|
||||
74 | / construct_runtime! {
|
||||
75 | | pub enum Runtime where
|
||||
76 | | Block = Block,
|
||||
77 | | NodeBlock = Block,
|
||||
... |
|
||||
82 | | }
|
||||
83 | | }
|
||||
| |_^ the evaluated program panicked at 'The maximum encoded size of the error type in the `Pallet` pallet exceeds `MAX_MODULE_ERROR_ENCODED_SIZE`', $DIR/tests/construct_runtime_ui/pallet_error_too_large.rs:74:1
|
||||
|
|
||||
= note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -0,0 +1,214 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2021 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.
|
||||
|
||||
//! Origin tests for construct_runtime macro
|
||||
|
||||
#![recursion_limit = "128"]
|
||||
|
||||
use frame_support::traits::{Contains, OriginTrait};
|
||||
use scale_info::TypeInfo;
|
||||
use sp_core::{sr25519, H256};
|
||||
use sp_runtime::{generic, traits::BlakeTwo256};
|
||||
|
||||
mod system;
|
||||
|
||||
mod nested {
|
||||
use super::*;
|
||||
|
||||
pub mod module {
|
||||
use super::*;
|
||||
|
||||
pub trait Config: system::Config {}
|
||||
|
||||
frame_support::decl_module! {
|
||||
pub struct Module<T: Config> for enum Call
|
||||
where origin: <T as system::Config>::Origin, system=system
|
||||
{
|
||||
#[weight = 0]
|
||||
pub fn fail(_origin) -> frame_support::dispatch::DispatchResult {
|
||||
Err(Error::<T>::Something.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode, TypeInfo)]
|
||||
pub struct Origin;
|
||||
|
||||
frame_support::decl_event! {
|
||||
pub enum Event {
|
||||
A,
|
||||
}
|
||||
}
|
||||
|
||||
frame_support::decl_error! {
|
||||
pub enum Error for Module<T: Config> {
|
||||
Something
|
||||
}
|
||||
}
|
||||
|
||||
frame_support::decl_storage! {
|
||||
trait Store for Module<T: Config> as Module {}
|
||||
add_extra_genesis {
|
||||
build(|_config| {})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod module {
|
||||
use super::*;
|
||||
|
||||
pub trait Config: system::Config {}
|
||||
|
||||
frame_support::decl_module! {
|
||||
pub struct Module<T: Config> for enum Call
|
||||
where origin: <T as system::Config>::Origin, system=system
|
||||
{
|
||||
#[weight = 0]
|
||||
pub fn fail(_origin) -> frame_support::dispatch::DispatchResult {
|
||||
Err(Error::<T>::Something.into())
|
||||
}
|
||||
#[weight = 0]
|
||||
pub fn aux_1(_origin, #[compact] _data: u32) -> frame_support::dispatch::DispatchResult {
|
||||
unreachable!()
|
||||
}
|
||||
#[weight = 0]
|
||||
pub fn aux_2(_origin, _data: i32, #[compact] _data2: u32) -> frame_support::dispatch::DispatchResult {
|
||||
unreachable!()
|
||||
}
|
||||
#[weight = 0]
|
||||
fn aux_3(_origin, _data: i32, _data2: String) -> frame_support::dispatch::DispatchResult {
|
||||
unreachable!()
|
||||
}
|
||||
#[weight = 3]
|
||||
fn aux_4(_origin) -> frame_support::dispatch::DispatchResult { unreachable!() }
|
||||
#[weight = (5, frame_support::weights::DispatchClass::Operational)]
|
||||
fn operational(_origin) { unreachable!() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode, TypeInfo)]
|
||||
pub struct Origin<T>(pub core::marker::PhantomData<T>);
|
||||
|
||||
frame_support::decl_event! {
|
||||
pub enum Event {
|
||||
A,
|
||||
}
|
||||
}
|
||||
|
||||
frame_support::decl_error! {
|
||||
pub enum Error for Module<T: Config> {
|
||||
Something
|
||||
}
|
||||
}
|
||||
|
||||
frame_support::decl_storage! {
|
||||
trait Store for Module<T: Config> as Module {}
|
||||
add_extra_genesis {
|
||||
build(|_config| {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl nested::module::Config for RuntimeOriginTest {}
|
||||
impl module::Config for RuntimeOriginTest {}
|
||||
|
||||
pub struct BaseCallFilter;
|
||||
impl Contains<Call> for BaseCallFilter {
|
||||
fn contains(c: &Call) -> bool {
|
||||
match c {
|
||||
Call::NestedModule(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl system::Config for RuntimeOriginTest {
|
||||
type BaseCallFilter = BaseCallFilter;
|
||||
type Hash = H256;
|
||||
type Origin = Origin;
|
||||
type BlockNumber = BlockNumber;
|
||||
type AccountId = u32;
|
||||
type Event = Event;
|
||||
type PalletInfo = PalletInfo;
|
||||
type Call = Call;
|
||||
type DbWeight = ();
|
||||
}
|
||||
|
||||
frame_support::construct_runtime!(
|
||||
pub enum RuntimeOriginTest where
|
||||
Block = Block,
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic
|
||||
{
|
||||
System: system::{Pallet, Event<T>, Origin<T>},
|
||||
NestedModule: nested::module::{Pallet, Origin, Call},
|
||||
Module: module::{Pallet, Origin<T>, Call},
|
||||
}
|
||||
);
|
||||
|
||||
pub type Signature = sr25519::Signature;
|
||||
pub type BlockNumber = u64;
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, Call, Signature, ()>;
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
|
||||
#[test]
|
||||
fn origin_default_filter() {
|
||||
let accepted_call = nested::module::Call::fail {}.into();
|
||||
let rejected_call = module::Call::fail {}.into();
|
||||
|
||||
assert_eq!(Origin::root().filter_call(&accepted_call), true);
|
||||
assert_eq!(Origin::root().filter_call(&rejected_call), true);
|
||||
assert_eq!(Origin::none().filter_call(&accepted_call), true);
|
||||
assert_eq!(Origin::none().filter_call(&rejected_call), false);
|
||||
assert_eq!(Origin::signed(0).filter_call(&accepted_call), true);
|
||||
assert_eq!(Origin::signed(0).filter_call(&rejected_call), false);
|
||||
assert_eq!(Origin::from(Some(0)).filter_call(&accepted_call), true);
|
||||
assert_eq!(Origin::from(Some(0)).filter_call(&rejected_call), false);
|
||||
assert_eq!(Origin::from(None).filter_call(&accepted_call), true);
|
||||
assert_eq!(Origin::from(None).filter_call(&rejected_call), false);
|
||||
assert_eq!(Origin::from(nested::module::Origin).filter_call(&accepted_call), true);
|
||||
assert_eq!(Origin::from(nested::module::Origin).filter_call(&rejected_call), false);
|
||||
|
||||
let mut origin = Origin::from(Some(0));
|
||||
origin.add_filter(|c| matches!(c, Call::Module(_)));
|
||||
assert_eq!(origin.filter_call(&accepted_call), false);
|
||||
assert_eq!(origin.filter_call(&rejected_call), false);
|
||||
|
||||
// Now test for root origin and filters:
|
||||
let mut origin = Origin::from(Some(0));
|
||||
origin.set_caller_from(Origin::root());
|
||||
assert!(matches!(origin.caller, OriginCaller::system(system::RawOrigin::Root)));
|
||||
|
||||
// Root origin bypass all filter.
|
||||
assert_eq!(origin.filter_call(&accepted_call), true);
|
||||
assert_eq!(origin.filter_call(&rejected_call), true);
|
||||
|
||||
origin.set_caller_from(Origin::from(Some(0)));
|
||||
|
||||
// Back to another signed origin, the filtered are now effective again
|
||||
assert_eq!(origin.filter_call(&accepted_call), true);
|
||||
assert_eq!(origin.filter_call(&rejected_call), false);
|
||||
|
||||
origin.set_caller_from(Origin::root());
|
||||
origin.reset_filter();
|
||||
|
||||
// Root origin bypass all filter, even when they are reset.
|
||||
assert_eq!(origin.filter_call(&accepted_call), true);
|
||||
assert_eq!(origin.filter_call(&rejected_call), true);
|
||||
}
|
||||
@@ -20,7 +20,7 @@ use frame_support::{
|
||||
storage::unhashed,
|
||||
traits::{
|
||||
ConstU32, GetCallName, GetStorageVersion, OnFinalize, OnGenesis, OnInitialize,
|
||||
OnRuntimeUpgrade, PalletInfoAccess, StorageVersion,
|
||||
OnRuntimeUpgrade, PalletError, PalletInfoAccess, StorageVersion,
|
||||
},
|
||||
weights::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays, RuntimeDbWeight},
|
||||
};
|
||||
@@ -229,9 +229,14 @@ pub mod pallet {
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum Error<T> {
|
||||
/// doc comment put into metadata
|
||||
InsufficientProposersBalance,
|
||||
Code(u8),
|
||||
#[codec(skip)]
|
||||
Skipped(u128),
|
||||
CompactU8(#[codec(compact)] u8),
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
@@ -656,10 +661,11 @@ fn error_expand() {
|
||||
DispatchError::from(pallet::Error::<Runtime>::InsufficientProposersBalance),
|
||||
DispatchError::Module(ModuleError {
|
||||
index: 1,
|
||||
error: 0,
|
||||
error: [0, 0, 0, 0],
|
||||
message: Some("InsufficientProposersBalance")
|
||||
}),
|
||||
);
|
||||
assert_eq!(<pallet::Error::<Runtime> as PalletError>::MAX_ENCODED_SIZE, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -343,7 +343,7 @@ fn error_expand() {
|
||||
DispatchError::from(pallet::Error::<Runtime>::InsufficientProposersBalance),
|
||||
DispatchError::Module(ModuleError {
|
||||
index: 1,
|
||||
error: 0,
|
||||
error: [0; 4],
|
||||
message: Some("InsufficientProposersBalance")
|
||||
}),
|
||||
);
|
||||
@@ -364,7 +364,7 @@ fn error_expand() {
|
||||
),
|
||||
DispatchError::Module(ModuleError {
|
||||
index: 2,
|
||||
error: 0,
|
||||
error: [0; 4],
|
||||
message: Some("InsufficientProposersBalance")
|
||||
}),
|
||||
);
|
||||
|
||||
+4
-10
@@ -1,25 +1,19 @@
|
||||
#[frame_support::pallet]
|
||||
mod pallet {
|
||||
use frame_support::pallet_prelude::Hooks;
|
||||
use frame_system::pallet_prelude::BlockNumberFor;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
U8(u8),
|
||||
CustomError(crate::MyError),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(scale_info::TypeInfo, codec::Encode, codec::Decode)]
|
||||
enum MyError {}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
error[E0277]: the trait bound `MyError: PalletError` is not satisfied
|
||||
--> tests/pallet_ui/error_does_not_derive_pallet_error.rs:1:1
|
||||
|
|
||||
1 | #[frame_support::pallet]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `PalletError` is not implemented for `MyError`
|
||||
|
|
||||
note: required by `MAX_ENCODED_SIZE`
|
||||
--> $WORKSPACE/frame/support/src/traits/error.rs
|
||||
|
|
||||
| const MAX_ENCODED_SIZE: usize;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: this error originates in the derive macro `frame_support::PalletError` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -1,5 +0,0 @@
|
||||
error: Invalid pallet::error, unexpected fields, must be `Unit`
|
||||
--> $DIR/error_no_fieldless.rs:20:5
|
||||
|
|
||||
20 | U8(u8),
|
||||
| ^^^^
|
||||
@@ -0,0 +1,41 @@
|
||||
use codec::{Decode, Encode};
|
||||
use frame_support::PalletError;
|
||||
|
||||
#[frame_support::pallet]
|
||||
mod pallet {
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
CustomError(crate::MyError),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, PalletError, scale_info::TypeInfo)]
|
||||
pub enum MyError {
|
||||
Foo,
|
||||
Bar,
|
||||
Baz(NestedError),
|
||||
Struct(MyStruct),
|
||||
Wrapper(Wrapper),
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, PalletError, scale_info::TypeInfo)]
|
||||
pub enum NestedError {
|
||||
Quux
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, PalletError, scale_info::TypeInfo)]
|
||||
pub struct MyStruct {
|
||||
field: u8,
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, PalletError, scale_info::TypeInfo)]
|
||||
pub struct Wrapper(bool);
|
||||
|
||||
fn main() {
|
||||
}
|
||||
@@ -38,7 +38,6 @@ use sp_runtime::{
|
||||
// example module to test behaviors.
|
||||
#[frame_support::pallet]
|
||||
pub mod example {
|
||||
use super::*;
|
||||
use frame_support::{dispatch::WithPostDispatchInfo, pallet_prelude::*};
|
||||
use frame_system::pallet_prelude::*;
|
||||
|
||||
|
||||
@@ -20,11 +20,14 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use sp_inherents::{CheckInherentsResult, InherentData};
|
||||
use sp_runtime::{traits::Block as BlockT, ApplyExtrinsicResult};
|
||||
use sp_runtime::{
|
||||
legacy::byte_sized_error::ApplyExtrinsicResult as ApplyExtrinsicResultBeforeV6,
|
||||
traits::Block as BlockT, ApplyExtrinsicResult,
|
||||
};
|
||||
|
||||
sp_api::decl_runtime_apis! {
|
||||
/// The `BlockBuilder` api trait that provides the required functionality for building a block.
|
||||
#[api_version(5)]
|
||||
#[api_version(6)]
|
||||
pub trait BlockBuilder {
|
||||
/// Apply the given extrinsic.
|
||||
///
|
||||
@@ -32,6 +35,9 @@ sp_api::decl_runtime_apis! {
|
||||
/// this block or not.
|
||||
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult;
|
||||
|
||||
#[changed_in(6)]
|
||||
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResultBeforeV6;
|
||||
|
||||
/// Finish the current block.
|
||||
#[renamed("finalise_block", 3)]
|
||||
fn finalize_block() -> <Block as BlockT>::Header;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2022 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.
|
||||
|
||||
//! Runtime types that existed in old API versions.
|
||||
|
||||
pub mod byte_sized_error;
|
||||
@@ -0,0 +1,100 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2022 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.
|
||||
|
||||
//! Runtime types that existed prior to BlockBuilder API version 6.
|
||||
|
||||
use crate::{ArithmeticError, TokenError};
|
||||
use codec::{Decode, Encode};
|
||||
use scale_info::TypeInfo;
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// [`ModuleError`] type definition before BlockBuilder API version 6.
|
||||
#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct ModuleError {
|
||||
/// Module index, matching the metadata module index.
|
||||
pub index: u8,
|
||||
/// Module specific error value.
|
||||
pub error: u8,
|
||||
/// Optional error message.
|
||||
#[codec(skip)]
|
||||
#[cfg_attr(feature = "std", serde(skip_deserializing))]
|
||||
pub message: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl PartialEq for ModuleError {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(self.index == other.index) && (self.error == other.error)
|
||||
}
|
||||
}
|
||||
|
||||
/// [`DispatchError`] type definition before BlockBuilder API version 6.
|
||||
#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, PartialEq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum DispatchError {
|
||||
/// Some error occurred.
|
||||
Other(
|
||||
#[codec(skip)]
|
||||
#[cfg_attr(feature = "std", serde(skip_deserializing))]
|
||||
&'static str,
|
||||
),
|
||||
/// Failed to lookup some data.
|
||||
CannotLookup,
|
||||
/// A bad origin.
|
||||
BadOrigin,
|
||||
/// A custom error in a module.
|
||||
Module(ModuleError),
|
||||
/// At least one consumer is remaining so the account cannot be destroyed.
|
||||
ConsumerRemaining,
|
||||
/// There are no providers so the account cannot be created.
|
||||
NoProviders,
|
||||
/// There are too many consumers so the account cannot be created.
|
||||
TooManyConsumers,
|
||||
/// An error to do with tokens.
|
||||
Token(TokenError),
|
||||
/// An arithmetic error.
|
||||
Arithmetic(ArithmeticError),
|
||||
}
|
||||
|
||||
/// [`DispatchOutcome`] type definition before BlockBuilder API version 6.
|
||||
pub type DispatchOutcome = Result<(), DispatchError>;
|
||||
|
||||
/// [`ApplyExtrinsicResult`] type definition before BlockBuilder API version 6.
|
||||
pub type ApplyExtrinsicResult =
|
||||
Result<DispatchOutcome, crate::transaction_validity::TransactionValidityError>;
|
||||
|
||||
/// Convert the legacy `ApplyExtrinsicResult` type to the latest version.
|
||||
pub fn convert_to_latest(old: ApplyExtrinsicResult) -> crate::ApplyExtrinsicResult {
|
||||
old.map(|outcome| {
|
||||
outcome.map_err(|e| match e {
|
||||
DispatchError::Other(s) => crate::DispatchError::Other(s),
|
||||
DispatchError::CannotLookup => crate::DispatchError::CannotLookup,
|
||||
DispatchError::BadOrigin => crate::DispatchError::BadOrigin,
|
||||
DispatchError::Module(err) => crate::DispatchError::Module(crate::ModuleError {
|
||||
index: err.index,
|
||||
error: [err.error, 0, 0, 0],
|
||||
message: err.message,
|
||||
}),
|
||||
DispatchError::ConsumerRemaining => crate::DispatchError::ConsumerRemaining,
|
||||
DispatchError::NoProviders => crate::DispatchError::NoProviders,
|
||||
DispatchError::TooManyConsumers => crate::DispatchError::TooManyConsumers,
|
||||
DispatchError::Token(err) => crate::DispatchError::Token(err),
|
||||
DispatchError::Arithmetic(err) => crate::DispatchError::Arithmetic(err),
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -57,6 +57,7 @@ use scale_info::TypeInfo;
|
||||
|
||||
pub mod curve;
|
||||
pub mod generic;
|
||||
pub mod legacy;
|
||||
mod multiaddress;
|
||||
pub mod offchain;
|
||||
pub mod runtime_logger;
|
||||
@@ -97,6 +98,10 @@ pub use sp_arithmetic::{
|
||||
|
||||
pub use either::Either;
|
||||
|
||||
/// The number of bytes of the module-specific `error` field defined in [`ModuleError`].
|
||||
/// In FRAME, this is the maximum encoded size of a pallet error type.
|
||||
pub const MAX_MODULE_ERROR_ENCODED_SIZE: usize = 4;
|
||||
|
||||
/// An abstraction over justification for a block's validity under a consensus algorithm.
|
||||
///
|
||||
/// Essentially a finality proof. The exact formulation will vary between consensus
|
||||
@@ -468,7 +473,7 @@ pub struct ModuleError {
|
||||
/// Module index, matching the metadata module index.
|
||||
pub index: u8,
|
||||
/// Module specific error value.
|
||||
pub error: u8,
|
||||
pub error: [u8; MAX_MODULE_ERROR_ENCODED_SIZE],
|
||||
/// Optional error message.
|
||||
#[codec(skip)]
|
||||
#[cfg_attr(feature = "std", serde(skip_deserializing))]
|
||||
@@ -922,15 +927,15 @@ mod tests {
|
||||
fn dispatch_error_encoding() {
|
||||
let error = DispatchError::Module(ModuleError {
|
||||
index: 1,
|
||||
error: 2,
|
||||
error: [2, 0, 0, 0],
|
||||
message: Some("error message"),
|
||||
});
|
||||
let encoded = error.encode();
|
||||
let decoded = DispatchError::decode(&mut &encoded[..]).unwrap();
|
||||
assert_eq!(encoded, vec![3, 1, 2]);
|
||||
assert_eq!(encoded, vec![3, 1, 2, 0, 0, 0]);
|
||||
assert_eq!(
|
||||
decoded,
|
||||
DispatchError::Module(ModuleError { index: 1, error: 2, message: None })
|
||||
DispatchError::Module(ModuleError { index: 1, error: [2, 0, 0, 0], message: None })
|
||||
);
|
||||
}
|
||||
|
||||
@@ -943,9 +948,9 @@ mod tests {
|
||||
Other("bar"),
|
||||
CannotLookup,
|
||||
BadOrigin,
|
||||
Module(ModuleError { index: 1, error: 1, message: None }),
|
||||
Module(ModuleError { index: 1, error: 2, message: None }),
|
||||
Module(ModuleError { index: 2, error: 1, message: None }),
|
||||
Module(ModuleError { index: 1, error: [1, 0, 0, 0], message: None }),
|
||||
Module(ModuleError { index: 1, error: [2, 0, 0, 0], message: None }),
|
||||
Module(ModuleError { index: 2, error: [1, 0, 0, 0], message: None }),
|
||||
ConsumerRemaining,
|
||||
NoProviders,
|
||||
Token(TokenError::NoFunds),
|
||||
@@ -970,8 +975,8 @@ mod tests {
|
||||
|
||||
// Ignores `message` field in `Module` variant.
|
||||
assert_eq!(
|
||||
Module(ModuleError { index: 1, error: 1, message: Some("foo") }),
|
||||
Module(ModuleError { index: 1, error: 1, message: None }),
|
||||
Module(ModuleError { index: 1, error: [1, 0, 0, 0], message: Some("foo") }),
|
||||
Module(ModuleError { index: 1, error: [1, 0, 0, 0], message: None }),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1554,6 +1554,12 @@ impl Printable for &[u8] {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Printable for [u8; N] {
|
||||
fn print(&self) {
|
||||
sp_io::misc::print_hex(&self[..]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Printable for &str {
|
||||
fn print(&self) {
|
||||
sp_io::misc::print_utf8(self.as_bytes());
|
||||
|
||||
@@ -25,10 +25,11 @@ use jsonrpc_core::{Error as RpcError, ErrorCode};
|
||||
use jsonrpc_derive::rpc;
|
||||
use sc_rpc_api::DenyUnsafe;
|
||||
use sc_transaction_pool_api::{InPoolTransaction, TransactionPool};
|
||||
use sp_api::ApiExt;
|
||||
use sp_block_builder::BlockBuilder;
|
||||
use sp_blockchain::HeaderBackend;
|
||||
use sp_core::{hexdisplay::HexDisplay, Bytes};
|
||||
use sp_runtime::{generic::BlockId, traits};
|
||||
use sp_runtime::{generic::BlockId, legacy, traits};
|
||||
|
||||
pub use self::gen_client::Client as SystemClient;
|
||||
pub use frame_system_rpc_runtime_api::AccountNonceApi;
|
||||
@@ -135,14 +136,40 @@ where
|
||||
.map_err(|e| RpcError {
|
||||
code: ErrorCode::ServerError(Error::DecodeError.into()),
|
||||
message: "Unable to dry run extrinsic.".into(),
|
||||
data: Some(format!("{:?}", e).into()),
|
||||
data: Some(e.to_string().into()),
|
||||
})?;
|
||||
|
||||
let result = api.apply_extrinsic(&at, uxt).map_err(|e| RpcError {
|
||||
code: ErrorCode::ServerError(Error::RuntimeError.into()),
|
||||
message: "Unable to dry run extrinsic.".into(),
|
||||
data: Some(e.to_string().into()),
|
||||
})?;
|
||||
let api_version = api
|
||||
.api_version::<dyn BlockBuilder<Block>>(&at)
|
||||
.map_err(|e| RpcError {
|
||||
code: ErrorCode::ServerError(Error::RuntimeError.into()),
|
||||
message: "Unable to dry run extrinsic.".into(),
|
||||
data: Some(e.to_string().into()),
|
||||
})?
|
||||
.ok_or_else(|| RpcError {
|
||||
code: ErrorCode::ServerError(Error::RuntimeError.into()),
|
||||
message: "Unable to dry run extrinsic.".into(),
|
||||
data: Some(
|
||||
format!("Could not find `BlockBuilder` api for block `{:?}`.", at).into(),
|
||||
),
|
||||
})?;
|
||||
|
||||
let result = if api_version < 6 {
|
||||
#[allow(deprecated)]
|
||||
api.apply_extrinsic_before_version_6(&at, uxt)
|
||||
.map(legacy::byte_sized_error::convert_to_latest)
|
||||
.map_err(|e| RpcError {
|
||||
code: ErrorCode::ServerError(Error::RuntimeError.into()),
|
||||
message: "Unable to dry run extrinsic.".into(),
|
||||
data: Some(e.to_string().into()),
|
||||
})?
|
||||
} else {
|
||||
api.apply_extrinsic(&at, uxt).map_err(|e| RpcError {
|
||||
code: ErrorCode::ServerError(Error::RuntimeError.into()),
|
||||
message: "Unable to dry run extrinsic.".into(),
|
||||
data: Some(e.to_string().into()),
|
||||
})?
|
||||
};
|
||||
|
||||
Ok(Encode::encode(&result).into())
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user