Skip to content

Commit

Permalink
Tasks: general system for recognizing and executing service work (#1343)
Browse files Browse the repository at this point in the history
`polkadot-sdk` version of original tasks PR located here:
paritytech/substrate#14329

Fixes #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
#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 34c991e commit ac3f14d
Show file tree
Hide file tree
Showing 75 changed files with 3,516 additions and 24 deletions.
19 changes: 19 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ members = [
"substrate/frame/examples/kitchensink",
"substrate/frame/examples/offchain-worker",
"substrate/frame/examples/split",
"substrate/frame/examples/tasks",
"substrate/frame/executive",
"substrate/frame/fast-unstake",
"substrate/frame/glutton",
Expand Down
3 changes: 2 additions & 1 deletion cumulus/pallets/collator-selection/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use super::*;
use crate as collator_selection;
use frame_support::{
ord_parameter_types, parameter_types,
derive_impl, ord_parameter_types, parameter_types,
traits::{ConstBool, ConstU32, ConstU64, FindAuthor, ValidatorRegistration},
PalletId,
};
Expand Down Expand Up @@ -50,6 +50,7 @@ parameter_types! {
pub const SS58Prefix: u8 = 42;
}

#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl system::Config for Test {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
Expand Down
3 changes: 2 additions & 1 deletion cumulus/parachain-template/pallets/template/src/mock.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use frame_support::{parameter_types, traits::Everything};
use frame_support::{derive_impl, parameter_types, traits::Everything};
use frame_system as system;
use sp_core::H256;
use sp_runtime::{
Expand All @@ -22,6 +22,7 @@ parameter_types! {
pub const SS58Prefix: u8 = 42;
}

#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl system::Config for Test {
type BaseCallFilter = Everything;
type BlockWeights = ();
Expand Down
29 changes: 29 additions & 0 deletions prdoc/pr_1343.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
title: Tasks API - A general system for recognizing and executing service work

doc:
- audience: Runtime Dev
description: |
The Tasks API allows you to define some service work that can be recognized by a script or an off-chain worker.
Such a script can then create and submit all such work items at any given time.
`#[pallet:tasks_experimental]` provides a convenient way to define such work items. It can be attached to an
`impl` block inside a pallet, whose functions can then be annotated by the following attributes:
1. `#[pallet::task_list]`: Define an iterator over the available work items for a task
2. `#[pallet::task_condition]`: Define the conditions for a given work item to be valid
3. `#[pallet::task_weight]`: Define the weight of a given work item
4. `#[pallet::task_index]`: Define the index of a given work item
Each such function becomes a variant of the autogenerated enum `Task<T>` for this pallet.
All such enums are aggregated into a `RuntimeTask` by `construct_runtime`.
An example pallet that uses the Tasks API is available at `substrate/frame/example/tasks`.

migrations:
db: []

runtime: []

crates:
- name: frame-system
- name: frame-support
- name: frame-support-procedural
- name: pallet-example-tasks

host_functions: []
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);
}
Loading

0 comments on commit ac3f14d

Please sign in to comment.