Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6e0ca26
add new trait
shawntabrizi May 17, 2022
9f0d290
implement DispatchableWithStorageLayer
shawntabrizi May 17, 2022
57fe674
at least one transactional
shawntabrizi May 17, 2022
28d35bd
all dispatch is at least transactional
shawntabrizi May 17, 2022
eaa330d
storage_layer api
shawntabrizi May 17, 2022
213e537
add test
shawntabrizi May 17, 2022
b8d32b9
storage layer tests
shawntabrizi May 17, 2022
a03c13f
deprecate transactional tag
shawntabrizi May 17, 2022
5dc88cd
i guess no reason to deprecate
shawntabrizi May 17, 2022
2856ecd
remove transactional from batch_all
shawntabrizi May 17, 2022
b0f7241
update tests
shawntabrizi May 17, 2022
0f26a7d
Merge remote-tracking branch 'origin/master' into shawntabrizi-dispat…
shawntabrizi May 17, 2022
f9f65db
extend trait
shawntabrizi May 19, 2022
738c021
Merge branch 'master' of https://github.com/paritytech/substrate into…
May 19, 2022
d1fd82f
cargo run --quiet --profile=production --features runtime-benchmarks …
May 19, 2022
5afa7e7
cargo run --quiet --profile=production --features runtime-benchmarks …
May 19, 2022
1a447b1
cargo run --quiet --profile=production --features runtime-benchmarks …
May 20, 2022
1e657bf
fix copy paste name
shawntabrizi May 20, 2022
5793a59
Merge branch 'master' of https://github.com/paritytech/substrate into…
May 20, 2022
09b4274
cargo run --quiet --profile=production --features runtime-benchmarks …
May 20, 2022
03c19ec
Create run_all_benchmarks.sh
shawntabrizi May 21, 2022
0e7e939
uncomment build
shawntabrizi May 21, 2022
fe1a750
update number of steps and repeats
shawntabrizi May 21, 2022
2068319
add skip build
shawntabrizi May 21, 2022
71b45db
Update run_all_benchmarks.sh
shawntabrizi May 21, 2022
c357618
Update run_all_benchmarks.sh
shawntabrizi May 21, 2022
85e7a28
new benchmarks
shawntabrizi May 21, 2022
d2aa0ed
Merge remote-tracking branch 'origin/master' into shawntabrizi-dispat…
shawntabrizi May 23, 2022
e0abb8f
Update frame/support/src/traits/dispatch.rs
shawntabrizi May 23, 2022
a87f4bc
Update frame/support/src/traits/dispatch.rs
shawntabrizi May 23, 2022
3633d3c
Update frame/support/test/tests/storage_layers.rs
shawntabrizi May 23, 2022
82b3576
Update frame/support/test/tests/storage_layers.rs
shawntabrizi May 23, 2022
db8dbe3
weights
shawntabrizi May 24, 2022
630f149
Update dispatch.rs
shawntabrizi May 24, 2022
3035fc8
Merge branch 'master' into shawntabrizi-dispatchable-with-storage-layer
shawntabrizi May 25, 2022
993383a
doc link
shawntabrizi May 26, 2022
d7530da
decl_macro support
shawntabrizi May 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 18 additions & 20 deletions frame/balances/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,22 @@
//! Autogenerated weights for pallet_balances
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-01-30, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2022-05-19, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024

// Executed Command:
// ./target/production/substrate
// target/production/substrate
// benchmark
// pallet
// --chain=dev
// --steps=50
// --repeat=20
// --pallet=pallet_balances
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --output=./frame/balances/src/weights.rs
// --template=.maintain/frame-weight-template.hbs
// --header=HEADER-APACHE2
// --raw
// --template=./.maintain/frame-weight-template.hbs

#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
Expand All @@ -60,43 +58,43 @@ pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Storage: System Account (r:1 w:1)
fn transfer() -> Weight {
(34_200_000 as Weight)
(40_167_000 as Weight)
.saturating_add(T::DbWeight::get().reads(1 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
// Storage: System Account (r:1 w:1)
fn transfer_keep_alive() -> Weight {
(27_263_000 as Weight)
(31_669_000 as Weight)
.saturating_add(T::DbWeight::get().reads(1 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
// Storage: System Account (r:1 w:1)
fn set_balance_creating() -> Weight {
(17_425_000 as Weight)
(21_660_000 as Weight)
.saturating_add(T::DbWeight::get().reads(1 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
// Storage: System Account (r:1 w:1)
fn set_balance_killing() -> Weight {
(19_979_000 as Weight)
(24_558_000 as Weight)
.saturating_add(T::DbWeight::get().reads(1 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
// Storage: System Account (r:2 w:2)
fn force_transfer() -> Weight {
(34_783_000 as Weight)
(40_024_000 as Weight)
.saturating_add(T::DbWeight::get().reads(2 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
// Storage: System Account (r:1 w:1)
fn transfer_all() -> Weight {
(31_620_000 as Weight)
(36_806_000 as Weight)
.saturating_add(T::DbWeight::get().reads(1 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
// Storage: System Account (r:1 w:1)
fn force_unreserve() -> Weight {
(15_750_000 as Weight)
(19_292_000 as Weight)
.saturating_add(T::DbWeight::get().reads(1 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
Expand All @@ -106,43 +104,43 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
impl WeightInfo for () {
// Storage: System Account (r:1 w:1)
fn transfer() -> Weight {
(34_200_000 as Weight)
(40_167_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
// Storage: System Account (r:1 w:1)
fn transfer_keep_alive() -> Weight {
(27_263_000 as Weight)
(31_669_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
// Storage: System Account (r:1 w:1)
fn set_balance_creating() -> Weight {
(17_425_000 as Weight)
(21_660_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
// Storage: System Account (r:1 w:1)
fn set_balance_killing() -> Weight {
(19_979_000 as Weight)
(24_558_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
// Storage: System Account (r:2 w:2)
fn force_transfer() -> Weight {
(34_783_000 as Weight)
(40_024_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
// Storage: System Account (r:1 w:1)
fn transfer_all() -> Weight {
(31_620_000 as Weight)
(36_806_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
// Storage: System Account (r:1 w:1)
fn force_unreserve() -> Weight {
(15_750_000 as Weight)
(19_292_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
Expand Down
13 changes: 13 additions & 0 deletions frame/support/procedural/src/construct_runtime/expand/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,19 @@ pub fn expand_outer_dispatch(
}
}
}
impl #scrate::traits::DispatchableWithStorageLayer for Call {
type Origin = Origin;
fn dispatch_with_storage_layer(self, origin: Origin) -> #scrate::dispatch::DispatchResultWithPostInfo {
#scrate::storage::with_storage_layer(|| {
#scrate::dispatch::Dispatchable::dispatch(self, origin)
})
}
fn dispatch_bypass_filter_with_storage_layer(self, origin: Origin) -> #scrate::dispatch::DispatchResultWithPostInfo {
#scrate::storage::with_storage_layer(|| {
#scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(self, origin)
})
}
}

#(
impl #scrate::traits::IsSubType<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for Call {
Expand Down
12 changes: 6 additions & 6 deletions frame/support/procedural/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,12 @@ pub fn transactional(attr: TokenStream, input: TokenStream) -> TokenStream {
transactional::transactional(attr, input).unwrap_or_else(|e| e.to_compile_error().into())
}

#[proc_macro_attribute]
pub fn require_transactional(attr: TokenStream, input: TokenStream) -> TokenStream {
transactional::require_transactional(attr, input)
.unwrap_or_else(|e| e.to_compile_error().into())
}

/// Derive [`Clone`] but do not bound any generic. Docs are at `frame_support::CloneNoBound`.
#[proc_macro_derive(CloneNoBound)]
pub fn derive_clone_no_bound(input: TokenStream) -> TokenStream {
Expand Down Expand Up @@ -510,12 +516,6 @@ pub fn derive_default_no_bound(input: TokenStream) -> TokenStream {
default_no_bound::derive_default_no_bound(input)
}

#[proc_macro_attribute]
pub fn require_transactional(attr: TokenStream, input: TokenStream) -> TokenStream {
transactional::require_transactional(attr, input)
.unwrap_or_else(|e| e.to_compile_error().into())
}

#[proc_macro]
pub fn crate_to_crate_version(input: TokenStream) -> TokenStream {
crate_version::crate_to_crate_version(input)
Expand Down
8 changes: 6 additions & 2 deletions frame/support/procedural/src/pallet/expand/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,12 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream {
#frame_support::sp_tracing::enter_span!(
#frame_support::sp_tracing::trace_span!(stringify!(#fn_name))
);
<#pallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* )
.map(Into::into).map_err(Into::into)
// We execute all dispatchable in at least one storage layer, allowing them
// to return an error at any point, and undoing any storage changes.
#frame_support::storage::in_storage_layer(|| {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This change would also need to be done for the decl_module macro.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

done and test

<#pallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* )
.map(Into::into).map_err(Into::into)
})
},
)*
Self::__Ignore(_, _) => {
Expand Down
3 changes: 2 additions & 1 deletion frame/support/src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ pub use crate::{
result,
},
traits::{
CallMetadata, GetCallMetadata, GetCallName, GetStorageVersion, UnfilteredDispatchable,
CallMetadata, DispatchableWithStorageLayer, GetCallMetadata, GetCallName,
GetStorageVersion, UnfilteredDispatchable,
},
weights::{
ClassifyDispatch, DispatchInfo, GetDispatchInfo, PaysFee, PostDispatchInfo,
Expand Down
4 changes: 3 additions & 1 deletion frame/support/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ use sp_runtime::generic::{Digest, DigestItem};
use sp_std::prelude::*;

pub use self::{
transactional::{with_transaction, with_transaction_unchecked},
transactional::{
in_storage_layer, with_storage_layer, with_transaction, with_transaction_unchecked,
},
types::StorageEntryMetadataBuilder,
};
pub use sp_runtime::TransactionOutcome;
Expand Down
63 changes: 63 additions & 0 deletions frame/support/src/storage/transactional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,40 @@ pub fn with_transaction_unchecked<R>(f: impl FnOnce() -> TransactionOutcome<R>)
}
}

/// Execute the supplied function, adding a new storage layer.
///
/// This is the same as `with_transaction`, but assuming that any function returning
/// an `Err` should rollback, and any function returning `Ok` should commit. This
/// provides a cleaner API to the developer who wants this behavior.
pub fn with_storage_layer<T, E>(f: impl FnOnce() -> Result<T, E>) -> Result<T, E>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

where
E: From<DispatchError>,
Comment thread
KiChjang marked this conversation as resolved.
{
with_transaction(|| {
let r = f();
if r.is_ok() {
TransactionOutcome::Commit(r)
} else {
TransactionOutcome::Rollback(r)
}
})
}

/// Execute the supplied function, ensuring we are at least in one storage layer.
///
/// If we are already in a storage layer, we just execute the provided closure.
/// If we are not, we execute the closure within a [`with_storage_layer`].
pub fn in_storage_layer<T, E>(f: impl FnOnce() -> Result<T, E>) -> Result<T, E>
where
E: From<DispatchError>,
Comment thread
KiChjang marked this conversation as resolved.
{
if is_transactional() {
f()
} else {
with_storage_layer(f)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -229,4 +263,33 @@ mod tests {
assert_eq!(get_transaction_level(), 0);
});
}

#[test]
fn in_storage_layer_works() {
TestExternalities::default().execute_with(|| {
assert_eq!(get_transaction_level(), 0);

let res = in_storage_layer(|| -> DispatchResult {
assert_eq!(get_transaction_level(), 1);
in_storage_layer(|| -> DispatchResult {
// We are still in the same layer :)
assert_eq!(get_transaction_level(), 1);
Ok(())
})
});

assert_ok!(res);

let res = in_storage_layer(|| -> DispatchResult {
assert_eq!(get_transaction_level(), 1);
in_storage_layer(|| -> DispatchResult {
// We are still in the same layer :)
assert_eq!(get_transaction_level(), 1);
Err("epic fail".into())
})
});

assert_noop!(res, "epic fail");
});
}
}
4 changes: 2 additions & 2 deletions frame/support/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ pub use storage::{

mod dispatch;
pub use dispatch::{
AsEnsureOriginWithArg, EnsureOneOf, EnsureOrigin, EnsureOriginWithArg, OriginTrait,
UnfilteredDispatchable,
AsEnsureOriginWithArg, DispatchableWithStorageLayer, EnsureOneOf, EnsureOrigin,
EnsureOriginWithArg, OriginTrait, UnfilteredDispatchable,
};

mod voting;
Expand Down
17 changes: 17 additions & 0 deletions frame/support/src/traits/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,23 @@ pub trait UnfilteredDispatchable {
fn dispatch_bypass_filter(self, origin: Self::Origin) -> DispatchResultWithPostInfo;
}

/// Type that can be dispatched with an additional storage layer which is used to execute the call.
pub trait DispatchableWithStorageLayer {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would split this in two and do the following:

trait DispatchableWithStorage {
    type Origin;

    fn dispatch_with_storage_layer(self, origin: Self::Origin) -> DispatchResultWithPostInfo;
}

impl<T: Dispatchable> DispatchableWithStorage for T {
    type Origin = T::Origin;
    
    fn dispatch_with_storage_layer(self, origin: Self::Origin) -> DispatchResultWithPostInfo {
        with_storage_layer(|| self.dispatch(origin))
    }
}

And then the same for UnfilteredDispatchable. Then you don't need to implement this in the macros.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure how useful this is. The caller can just explicitly create a storage layer?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Don't see the downside of this though either. Seems like just another friendly impl

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Actually, the impl here isnt very clean, and the restrictions you end up needing to put makes it pretty much useless. So I agree at this point, probably not useful to include.

/// The origin type of the runtime, (i.e. `frame_system::Config::Origin`).
type Origin;

/// Same as `dispatch` from the `Dispatchable` trait, but specifically spawns a new storage
Comment thread
shawntabrizi marked this conversation as resolved.
Outdated
/// layer to execute the call inside of.
fn dispatch_with_storage_layer(self, origin: Self::Origin) -> DispatchResultWithPostInfo;

/// Same as `dispatch_bypass_filter` from the `UnfilteredDispatchable` trait, but specifically
Comment thread
shawntabrizi marked this conversation as resolved.
Outdated
/// spawns a new storage layer to execute the call inside of.
fn dispatch_bypass_filter_with_storage_layer(
self,
origin: Self::Origin,
) -> DispatchResultWithPostInfo;
}

/// Methods available on `frame_system::Config::Origin`.
pub trait OriginTrait: Sized {
/// Runtime call type, as in `frame_system::Config::Call`
Expand Down
Loading