Skip to content

Commit

Permalink
Tasks: general system for recognizing and executing service work (par…
Browse files Browse the repository at this point in the history
…itytech#1343)

`polkadot-sdk` version of original tasks PR located here:
paritytech/substrate#14329

Fixes paritytech#206

## Status
- [x] Generic `Task` trait
- [x] `RuntimeTask` aggregated enum, compatible with
`construct_runtime!`
- [x] Casting between `Task` and `RuntimeTask` without needing `dyn` or
`Box`
- [x] Tasks Example pallet
- [x] Runtime tests for Tasks example pallet
- [x] Parsing for task-related macros
- [x] Retrofit parsing to make macros optional
- [x] Expansion for task-related macros
- [x] Adds support for args in tasks
- [x] Retrofit tasks example pallet to use macros instead of manual
syntax
- [x] Weights
- [x] Cleanup
- [x] UI tests
- [x] Docs

## Target Syntax
Adapted from
paritytech#206 (comment)

```rust
// NOTE: this enum is optional and is auto-generated by the other macros if not present
#[pallet::task]
pub enum Task<T: Config> {
    AddNumberIntoTotal {
        i: u32,
    }
}

/// Some running total.
#[pallet::storage]
pub(super) type Total<T: Config<I>, I: 'static = ()> =
StorageValue<_, (u32, u32), ValueQuery>;

/// Numbers to be added into the total.
#[pallet::storage]
pub(super) type Numbers<T: Config<I>, I: 'static = ()> =
StorageMap<_, Twox64Concat, u32, u32, OptionQuery>;

#[pallet::tasks_experimental]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
	/// Add a pair of numbers into the totals and remove them.
	#[pallet::task_list(Numbers::<T, I>::iter_keys())]
	#[pallet::task_condition(|i| Numbers::<T, I>::contains_key(i))]
	#[pallet::task_index(0)]
	pub fn add_number_into_total(i: u32) -> DispatchResult {
		let v = Numbers::<T, I>::take(i).ok_or(Error::<T, I>::NotFound)?;
		Total::<T, I>::mutate(|(total_keys, total_values)| {
			*total_keys += i;
			*total_values += v;
		});
		Ok(())
	}
}
```

---------

Co-authored-by: Nikhil Gupta <17176722+gupnik@users.noreply.github.com>
Co-authored-by: kianenigma <kian@parity.io>
Co-authored-by: Nikhil Gupta <>
Co-authored-by: Gavin Wood <gavin@parity.io>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: gupnik <nikhilgupta.iitk@gmail.com>
  • Loading branch information
6 people authored Dec 8, 2023
1 parent 3078eca commit 2073ba9
Show file tree
Hide file tree
Showing 70 changed files with 3,463 additions and 22 deletions.
4 changes: 4 additions & 0 deletions substrate/bin/node/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ pallet-democracy = { path = "../../../frame/democracy", default-features = false
pallet-election-provider-multi-phase = { path = "../../../frame/election-provider-multi-phase", default-features = false }
pallet-election-provider-support-benchmarking = { path = "../../../frame/election-provider-support/benchmarking", default-features = false, optional = true }
pallet-elections-phragmen = { path = "../../../frame/elections-phragmen", default-features = false }
pallet-example-tasks = { path = "../../../frame/examples/tasks", default-features = false }
pallet-fast-unstake = { path = "../../../frame/fast-unstake", default-features = false }
pallet-nis = { path = "../../../frame/nis", default-features = false }
pallet-grandpa = { path = "../../../frame/grandpa", default-features = false }
Expand Down Expand Up @@ -177,6 +178,7 @@ std = [
"pallet-election-provider-multi-phase/std",
"pallet-election-provider-support-benchmarking?/std",
"pallet-elections-phragmen/std",
"pallet-example-tasks/std",
"pallet-fast-unstake/std",
"pallet-glutton/std",
"pallet-grandpa/std",
Expand Down Expand Up @@ -279,6 +281,7 @@ runtime-benchmarks = [
"pallet-election-provider-multi-phase/runtime-benchmarks",
"pallet-election-provider-support-benchmarking/runtime-benchmarks",
"pallet-elections-phragmen/runtime-benchmarks",
"pallet-example-tasks/runtime-benchmarks",
"pallet-fast-unstake/runtime-benchmarks",
"pallet-glutton/runtime-benchmarks",
"pallet-grandpa/runtime-benchmarks",
Expand Down Expand Up @@ -353,6 +356,7 @@ try-runtime = [
"pallet-democracy/try-runtime",
"pallet-election-provider-multi-phase/try-runtime",
"pallet-elections-phragmen/try-runtime",
"pallet-example-tasks/try-runtime",
"pallet-fast-unstake/try-runtime",
"pallet-glutton/try-runtime",
"pallet-grandpa/try-runtime",
Expand Down
7 changes: 7 additions & 0 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,11 @@ impl frame_system::Config for Runtime {

impl pallet_insecure_randomness_collective_flip::Config for Runtime {}

impl pallet_example_tasks::Config for Runtime {
type RuntimeTask = RuntimeTask;
type WeightInfo = pallet_example_tasks::weights::SubstrateWeight<Runtime>;
}

impl pallet_utility::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type RuntimeCall = RuntimeCall;
Expand Down Expand Up @@ -2135,6 +2140,7 @@ construct_runtime!(
SafeMode: pallet_safe_mode,
Statement: pallet_statement,
Broker: pallet_broker,
TasksExample: pallet_example_tasks,
Mixnet: pallet_mixnet,
SkipFeelessPayment: pallet_skip_feeless_payment,
}
Expand Down Expand Up @@ -2227,6 +2233,7 @@ mod benches {
[pallet_conviction_voting, ConvictionVoting]
[pallet_contracts, Contracts]
[pallet_core_fellowship, CoreFellowship]
[tasks_example, TasksExample]
[pallet_democracy, Democracy]
[pallet_asset_conversion, AssetConversion]
[pallet_election_provider_multi_phase, ElectionProviderMultiPhase]
Expand Down
3 changes: 3 additions & 0 deletions substrate/frame/examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pallet-example-frame-crate = { path = "frame-crate", default-features = false }
pallet-example-kitchensink = { path = "kitchensink", default-features = false }
pallet-example-offchain-worker = { path = "offchain-worker", default-features = false }
pallet-example-split = { path = "split", default-features = false }
pallet-example-tasks = { path = "tasks", default-features = false }

[features]
default = ["std"]
Expand All @@ -31,6 +32,7 @@ std = [
"pallet-example-kitchensink/std",
"pallet-example-offchain-worker/std",
"pallet-example-split/std",
"pallet-example-tasks/std",
]
try-runtime = [
"pallet-default-config-example/try-runtime",
Expand All @@ -39,4 +41,5 @@ try-runtime = [
"pallet-example-kitchensink/try-runtime",
"pallet-example-offchain-worker/try-runtime",
"pallet-example-split/try-runtime",
"pallet-example-tasks/try-runtime",
]
5 changes: 5 additions & 0 deletions substrate/frame/examples/default-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ pub mod pallet {
#[pallet::no_default] // optional. `RuntimeEvent` is automatically excluded as well.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

/// The overarching task type.
#[pallet::no_default]
type RuntimeTask: Task;

/// An input parameter to this pallet. This value can have a default, because it is not
/// reliant on `frame_system::Config` or the overarching runtime in any way.
type WithDefaultValue: Get<u32>;
Expand Down Expand Up @@ -193,6 +197,7 @@ pub mod tests {
impl pallet_default_config_example::Config for Runtime {
// These two both cannot have defaults.
type RuntimeEvent = RuntimeEvent;
type RuntimeTask = RuntimeTask;

type HasNoDefault = frame_support::traits::ConstU32<1>;
type CannotHaveDefault = SomeCall;
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/examples/kitchensink/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ license = "MIT-0"
homepage = "https://substrate.io"
repository.workspace = true
description = "FRAME example kitchensink pallet"
publish = false

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
Expand Down
2 changes: 2 additions & 0 deletions substrate/frame/examples/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,6 @@
//! - [`pallet_example_frame_crate`]: Example pallet showcasing how one can be
//! built using only the `frame` umbrella crate.
//!
//! - [`pallet_example_tasks`]: This pallet demonstrates the use of `Tasks` to execute service work.
//!
//! **Tip**: Use `cargo doc --package <pallet-name> --open` to view each pallet's documentation.
52 changes: 52 additions & 0 deletions substrate/frame/examples/tasks/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
[package]
name = "pallet-example-tasks"
version = "1.0.0-dev"
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
description = "Pallet to demonstrate the usage of Tasks to recongnize and execute service work"

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

[dependencies]
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false }
log = { version = "0.4.17", default-features = false }
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }

frame-support = { path = "../../support", default-features = false }
frame-system = { path = "../../system", default-features = false }

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

frame-benchmarking = { path = "../../benchmarking", default-features = false, optional = true }

[features]
default = ["std"]
std = [
"codec/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"log/std",
"scale-info/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"sp-runtime/try-runtime",
]
42 changes: 42 additions & 0 deletions substrate/frame/examples/tasks/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// This file is part of Substrate.

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

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

//! Benchmarking for `pallet-example-tasks`.

#![cfg(feature = "runtime-benchmarks")]

use crate::*;
use frame_benchmarking::v2::*;

#[benchmarks]
mod benchmarks {
use super::*;

#[benchmark]
fn add_number_into_total() {
Numbers::<T>::insert(0, 1);

#[block]
{
Task::<T>::add_number_into_total(0).unwrap();
}

assert_eq!(Numbers::<T>::get(0), None);
}

impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::mock::Runtime);
}
78 changes: 78 additions & 0 deletions substrate/frame/examples/tasks/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// This file is part of Substrate.

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

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

//! This pallet demonstrates the use of the `pallet::task` api for service work.
#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::dispatch::DispatchResult;
// Re-export pallet items so that they can be accessed from the crate namespace.
pub use pallet::*;

pub mod mock;
pub mod tests;

#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;

pub mod weights;
pub use weights::*;

#[frame_support::pallet(dev_mode)]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;

#[pallet::error]
pub enum Error<T> {
/// The referenced task was not found.
NotFound,
}

#[pallet::tasks_experimental]
impl<T: Config> Pallet<T> {
/// Add a pair of numbers into the totals and remove them.
#[pallet::task_list(Numbers::<T>::iter_keys())]
#[pallet::task_condition(|i| Numbers::<T>::contains_key(i))]
#[pallet::task_weight(T::WeightInfo::add_number_into_total())]
#[pallet::task_index(0)]
pub fn add_number_into_total(i: u32) -> DispatchResult {
let v = Numbers::<T>::take(i).ok_or(Error::<T>::NotFound)?;
Total::<T>::mutate(|(total_keys, total_values)| {
*total_keys += i;
*total_values += v;
});
Ok(())
}
}

#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeTask: frame_support::traits::Task;
type WeightInfo: WeightInfo;
}

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

/// Some running total.
#[pallet::storage]
pub type Total<T: Config> = StorageValue<_, (u32, u32), ValueQuery>;

/// Numbers to be added into the total.
#[pallet::storage]
pub type Numbers<T: Config> = StorageMap<_, Twox64Concat, u32, u32, OptionQuery>;
}
43 changes: 43 additions & 0 deletions substrate/frame/examples/tasks/src/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// This file is part of Substrate.

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

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

//! Mock runtime for `tasks-example` tests.
#![cfg(test)]

use crate::{self as tasks_example};
use frame_support::derive_impl;

pub type AccountId = u32;
pub type Balance = u32;

type Block = frame_system::mocking::MockBlock<Runtime>;
frame_support::construct_runtime!(
pub struct Runtime {
System: frame_system,
TasksExample: tasks_example,
}
);

#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl frame_system::Config for Runtime {
type Block = Block;
}

impl tasks_example::Config for Runtime {
type RuntimeTask = RuntimeTask;
type WeightInfo = ();
}
Loading

0 comments on commit 2073ba9

Please sign in to comment.