Skip to content

Commit ce74d6c

Browse files
sandreimJay Pan
authored andcommitted
glutton: also increase parachain block length (paritytech#4728)
Glutton currently is useful mostly for stress testing relay chain validators. It is unusable for testing the collator networking and block announcement and import scenarios. This PR resolves that by improving glutton pallet to also buff up the blocks, up to the runtime configured `BlockLength`. ### How it works Includes an additional inherent in each parachain block. The `garbage` argument passed to the inherent is filled with trash data. It's size is computed by applying the newly introduced `block_length` percentage to the maximum block length for mandatory dispatch class. After paritytech#4765 is merged, the length of inherent extrinsic will be added to the total block proof size. The remaining weight is burnt in `on_idle` as configured by the `storage` percentage parameter. TODO: - [x] PRDoc - [x] Readme update - [x] Add tests --------- Signed-off-by: Andrei Sandu <andrei-mihail@parity.io>
1 parent c3aca59 commit ce74d6c

10 files changed

Lines changed: 154 additions & 13 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ pub type SignedExtra = (
296296
frame_system::CheckGenesis<Runtime>,
297297
frame_system::CheckEra<Runtime>,
298298
frame_system::CheckNonce<Runtime>,
299+
frame_system::CheckWeight<Runtime>,
299300
);
300301
/// Unchecked extrinsic type as expected by this runtime.
301302
pub type UncheckedExtrinsic =

prdoc/pr_4728.prdoc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
2+
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
3+
4+
title: "Glutton - add support for bloating the parachain block length"
5+
6+
doc:
7+
- audience: [Runtime Dev, Runtime User]
8+
description: |
9+
Introduce a new configuration parameter `block_length` which can be configured via a call to
10+
`set_block_length`. This sets the ration of the block length that is to be filled with trash.
11+
This is implemented by an inherent that takes trash data as a parameter filling the block length.
12+
13+
crates:
14+
- name: pallet-glutton
15+
bump: major
16+
- name: glutton-westend-runtime
17+
bump: major

substrate/bin/node/bench/src/import.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ impl core::Benchmark for ImportBenchmark {
122122
match self.block_type {
123123
BlockType::RandomTransfersKeepAlive => {
124124
// should be 8 per signed extrinsic + 1 per unsigned
125-
// we have 1 unsigned and the rest are signed in the block
125+
// we have 2 unsigned (timestamp and glutton bloat) while the rest are
126+
// signed in the block.
126127
// those 8 events per signed are:
127128
// - transaction paid for the transaction payment
128129
// - withdraw (Balances::Withdraw) for charging the transaction fee
@@ -135,18 +136,18 @@ impl core::Benchmark for ImportBenchmark {
135136
// - extrinsic success
136137
assert_eq!(
137138
kitchensink_runtime::System::events().len(),
138-
(self.block.extrinsics.len() - 1) * 8 + 1,
139+
(self.block.extrinsics.len() - 2) * 8 + 2,
139140
);
140141
},
141142
BlockType::Noop => {
142143
assert_eq!(
143144
kitchensink_runtime::System::events().len(),
144145
// should be 2 per signed extrinsic + 1 per unsigned
145-
// we have 1 unsigned and the rest are signed in the block
146+
// we have 2 unsigned and the rest are signed in the block
146147
// those 2 events per signed are:
147148
// - deposit event for charging transaction fee
148149
// - extrinsic success
149-
(self.block.extrinsics.len() - 1) * 2 + 1,
150+
(self.block.extrinsics.len() - 2) * 2 + 2,
150151
);
151152
},
152153
_ => {},

substrate/bin/node/cli/tests/res/default_genesis_config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
"glutton": {
7575
"compute": "0",
7676
"storage": "0",
77+
"blockLength": "0",
7778
"trashDataCount": 0
7879
},
7980
"assets": {

substrate/frame/glutton/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ sp-core = { path = "../../primitives/core", default-features = false }
2727
sp-io = { path = "../../primitives/io", default-features = false }
2828
sp-runtime = { path = "../../primitives/runtime", default-features = false }
2929
sp-std = { path = "../../primitives/std", default-features = false }
30+
sp-inherents = { path = "../../primitives/inherents", default-features = false }
3031

3132
[dev-dependencies]
3233
pallet-balances = { path = "../balances" }
@@ -43,6 +44,7 @@ std = [
4344
"pallet-balances/std",
4445
"scale-info/std",
4546
"sp-core/std",
47+
"sp-inherents/std",
4648
"sp-io/std",
4749
"sp-runtime/std",
4850
"sp-std/std",

substrate/frame/glutton/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
The `Glutton` pallet gets the name from its property to consume vast amounts of resources. It can be used to push
88
para-chains and their relay-chains to the limits. This is good for testing out theoretical limits in a practical way.
99

10-
The `Glutton` can be set to consume a fraction of the available unused weight of a chain. It accomplishes this by
11-
utilizing the `on_idle` hook and consuming a specific ration of the remaining weight. The rations can be set via
12-
`set_compute` and `set_storage`. Initially the `Glutton` needs to be initialized once with `initialize_pallet`.
10+
The `Glutton` can be set to consume a fraction of the available block length and unused weight of a chain. It
11+
accomplishes this by filling the block length up to a ration and utilizing the `on_idle` hook to consume a
12+
specific ration of the remaining weight. The rations can be set via `set_compute`, `set_storage` and `set_block_length`.
13+
Initially the `Glutton` needs to be initialized once with `initialize_pallet`.

substrate/frame/glutton/src/lib.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ pub mod pallet {
8989
/// The storage limit.
9090
storage: FixedU64,
9191
},
92+
/// The block length limit has been updated.
93+
BlockLengthLimitSet {
94+
/// The block length limit.
95+
block_length: FixedU64,
96+
},
9297
}
9398

9499
#[pallet::error]
@@ -116,6 +121,13 @@ pub mod pallet {
116121
#[pallet::storage]
117122
pub(crate) type Storage<T: Config> = StorageValue<_, FixedU64, ValueQuery>;
118123

124+
/// The proportion of the `block length` to consume on each block.
125+
///
126+
/// `1.0` is mapped to `100%`. Must be at most [`crate::RESOURCE_HARD_LIMIT`]. Setting this to
127+
/// over `1.0` could stall the chain.
128+
#[pallet::storage]
129+
pub(crate) type Length<T: Config> = StorageValue<_, FixedU64, ValueQuery>;
130+
119131
/// Storage map used for wasting proof size.
120132
///
121133
/// It contains no meaningful data - hence the name "Trash". The maximal number of entries is
@@ -146,6 +158,8 @@ pub mod pallet {
146158
pub storage: FixedU64,
147159
/// The amount of trash data for wasting proof size.
148160
pub trash_data_count: u32,
161+
/// The block length limit.
162+
pub block_length: FixedU64,
149163
#[serde(skip)]
150164
/// The required configuration field.
151165
pub _config: sp_std::marker::PhantomData<T>,
@@ -170,6 +184,9 @@ pub mod pallet {
170184

171185
assert!(self.storage <= RESOURCE_HARD_LIMIT, "Storage limit is insane");
172186
<Storage<T>>::put(self.storage);
187+
188+
assert!(self.block_length <= RESOURCE_HARD_LIMIT, "Block length limit is insane");
189+
<Length<T>>::put(self.block_length);
173190
}
174191
}
175192

@@ -208,6 +225,40 @@ pub mod pallet {
208225
}
209226
}
210227

228+
#[pallet::inherent]
229+
impl<T: Config> ProvideInherent for Pallet<T> {
230+
type Call = Call<T>;
231+
type Error = sp_inherents::MakeFatalError<()>;
232+
233+
const INHERENT_IDENTIFIER: InherentIdentifier = *b"bloated0";
234+
235+
fn create_inherent(_data: &InherentData) -> Option<Self::Call> {
236+
let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Mandatory);
237+
let bloat_size = Length::<T>::get().saturating_mul_int(max_block_length) as usize;
238+
let amount_trash = bloat_size / VALUE_SIZE;
239+
let garbage = TrashData::<T>::iter()
240+
.map(|(_k, v)| v)
241+
.collect::<Vec<_>>()
242+
.into_iter()
243+
.cycle()
244+
.take(amount_trash)
245+
.collect::<Vec<_>>();
246+
247+
Some(Call::bloat { garbage })
248+
}
249+
250+
fn is_inherent(call: &Self::Call) -> bool {
251+
matches!(call, Call::bloat { .. })
252+
}
253+
254+
fn check_inherent(call: &Self::Call, _: &InherentData) -> Result<(), Self::Error> {
255+
match call {
256+
Call::bloat { .. } => Ok(()),
257+
_ => unreachable!("other calls are not inherents"),
258+
}
259+
}
260+
}
261+
211262
#[pallet::call(weight = T::WeightInfo)]
212263
impl<T: Config> Pallet<T> {
213264
/// Initialize the pallet. Should be called once, if no genesis state was provided.
@@ -277,6 +328,31 @@ pub mod pallet {
277328
Self::deposit_event(Event::StorageLimitSet { storage });
278329
Ok(())
279330
}
331+
332+
/// Increase the block size by including the specified garbage bytes.
333+
#[pallet::call_index(3)]
334+
#[pallet::weight((0, DispatchClass::Mandatory))]
335+
pub fn bloat(_origin: OriginFor<T>, _garbage: Vec<[u8; VALUE_SIZE]>) -> DispatchResult {
336+
Ok(())
337+
}
338+
339+
/// Set how much of the block length should be filled with trash data on each block.
340+
///
341+
/// `1.0` means that all block should be filled. If set to `1.0`, storage proof size will
342+
/// be close to zero.
343+
///
344+
/// Only callable by Root or `AdminOrigin`.
345+
#[pallet::call_index(4)]
346+
#[pallet::weight({1})]
347+
pub fn set_block_length(origin: OriginFor<T>, block_length: FixedU64) -> DispatchResult {
348+
T::AdminOrigin::ensure_origin_or_root(origin)?;
349+
350+
ensure!(block_length <= RESOURCE_HARD_LIMIT, Error::<T>::InsaneLimit);
351+
Length::<T>::set(block_length);
352+
353+
Self::deposit_event(Event::BlockLengthLimitSet { block_length });
354+
Ok(())
355+
}
280356
}
281357

282358
impl<T: Config> Pallet<T> {

substrate/frame/glutton/src/mock.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,14 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
5050
ext
5151
}
5252

53-
/// Set the `compute` and `storage` limits.
53+
/// Set the `compute`, `storage` and `block_length` limits.
5454
///
5555
/// `1.0` corresponds to `100%`.
56-
pub fn set_limits(compute: f64, storage: f64) {
56+
pub fn set_limits(compute: f64, storage: f64, block_length: f64) {
5757
assert_ok!(Glutton::set_compute(RuntimeOrigin::root(), FixedU64::from_float(compute)));
5858
assert_ok!(Glutton::set_storage(RuntimeOrigin::root(), FixedU64::from_float(storage)));
59+
assert_ok!(Glutton::set_block_length(
60+
RuntimeOrigin::root(),
61+
FixedU64::from_float(block_length)
62+
));
5963
}

substrate/frame/glutton/src/tests.rs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,43 @@ fn setting_compute_respects_limit() {
123123
});
124124
}
125125

126+
#[test]
127+
fn setting_block_length_works() {
128+
new_test_ext().execute_with(|| {
129+
assert_eq!(Compute::<Test>::get(), Zero::zero());
130+
131+
assert_ok!(Glutton::set_block_length(RuntimeOrigin::root(), FixedU64::from_float(0.3)));
132+
assert_eq!(Length::<Test>::get(), FixedU64::from_float(0.3));
133+
System::assert_last_event(
134+
Event::BlockLengthLimitSet { block_length: FixedU64::from_float(0.3) }.into(),
135+
);
136+
137+
assert_noop!(
138+
Glutton::set_block_length(RuntimeOrigin::signed(1), FixedU64::from_float(0.5)),
139+
DispatchError::BadOrigin
140+
);
141+
assert_noop!(
142+
Glutton::set_block_length(RuntimeOrigin::none(), FixedU64::from_float(0.5)),
143+
DispatchError::BadOrigin
144+
);
145+
});
146+
}
147+
148+
#[test]
149+
fn setting_block_length_respects_limit() {
150+
new_test_ext().execute_with(|| {
151+
// < 1000% is fine
152+
assert_ok!(Glutton::set_block_length(RuntimeOrigin::root(), FixedU64::from_float(9.99)),);
153+
// == 1000% is fine
154+
assert_ok!(Glutton::set_block_length(RuntimeOrigin::root(), FixedU64::from_u32(10)),);
155+
// > 1000% is not
156+
assert_noop!(
157+
Glutton::set_block_length(RuntimeOrigin::root(), FixedU64::from_float(10.01)),
158+
Error::<Test>::InsaneLimit
159+
);
160+
});
161+
}
162+
126163
#[test]
127164
fn setting_storage_works() {
128165
new_test_ext().execute_with(|| {
@@ -163,7 +200,7 @@ fn setting_storage_respects_limit() {
163200
#[test]
164201
fn on_idle_works() {
165202
new_test_ext().execute_with(|| {
166-
set_limits(One::one(), One::one());
203+
set_limits(One::one(), One::one(), One::one());
167204

168205
Glutton::on_idle(1, Weight::from_parts(20_000_000, 0));
169206
});
@@ -173,7 +210,7 @@ fn on_idle_works() {
173210
#[test]
174211
fn on_idle_weight_high_proof_is_close_enough_works() {
175212
new_test_ext().execute_with(|| {
176-
set_limits(One::one(), One::one());
213+
set_limits(One::one(), One::one(), One::one());
177214

178215
let should = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, WEIGHT_PROOF_SIZE_PER_MB * 5);
179216
let got = Glutton::on_idle(1, should);
@@ -196,7 +233,7 @@ fn on_idle_weight_high_proof_is_close_enough_works() {
196233
#[test]
197234
fn on_idle_weight_low_proof_is_close_enough_works() {
198235
new_test_ext().execute_with(|| {
199-
set_limits(One::one(), One::one());
236+
set_limits(One::one(), One::one(), One::one());
200237

201238
let should = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, WEIGHT_PROOF_SIZE_PER_KB * 20);
202239
let got = Glutton::on_idle(1, should);
@@ -224,7 +261,7 @@ fn on_idle_weight_over_unity_is_close_enough_works() {
224261
let max_block =
225262
Weight::from_parts(500 * WEIGHT_REF_TIME_PER_MILLIS, 5 * WEIGHT_PROOF_SIZE_PER_MB);
226263
// But now we tell it to consume more than that.
227-
set_limits(1.75, 1.5);
264+
set_limits(1.75, 1.5, 0.0);
228265
let want = Weight::from_parts(
229266
(1.75 * max_block.ref_time() as f64) as u64,
230267
(1.5 * max_block.proof_size() as f64) as u64,

0 commit comments

Comments
 (0)