Skip to content

Commit

Permalink
Correct arithmetical semantic of PerDispatchClass (paritytech#13194)
Browse files Browse the repository at this point in the history
* Fix PerDispatchClass arithmetic

Signed-off-by: Oliver Tale-Yazdi <[email protected]>

* Test PerDispatchClass arithmetic

Signed-off-by: Oliver Tale-Yazdi <[email protected]>

* Add helpers for Weight

Signed-off-by: Oliver Tale-Yazdi <[email protected]>

* Remove stale doc

Signed-off-by: Oliver Tale-Yazdi <[email protected]>

* Dont mention Polkadot in Substrate

Signed-off-by: Oliver Tale-Yazdi <[email protected]>

* Tests

Signed-off-by: Oliver Tale-Yazdi <[email protected]>

* fmt

Signed-off-by: Oliver Tale-Yazdi <[email protected]>

* Remove saturating_ prefix

Signed-off-by: Oliver Tale-Yazdi <[email protected]>

* Fix tests

Signed-off-by: Oliver Tale-Yazdi <[email protected]>

* Fix usage

Signed-off-by: Oliver Tale-Yazdi <[email protected]>

Signed-off-by: Oliver Tale-Yazdi <[email protected]>
  • Loading branch information
ggwpez authored Jan 26, 2023
1 parent 2dbf625 commit 495cc0e
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 36 deletions.
209 changes: 193 additions & 16 deletions frame/support/src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,33 +423,35 @@ impl<T: Clone> PerDispatchClass<T> {

impl PerDispatchClass<Weight> {
/// Returns the total weight consumed by all extrinsics in the block.
///
/// Saturates on overflow.
pub fn total(&self) -> Weight {
let mut sum = Weight::zero();
for class in DispatchClass::all() {
sum = sum.saturating_add(*self.get(*class));
sum.saturating_accrue(*self.get(*class));
}
sum
}

/// Add some weight of a specific dispatch class, saturating at the numeric bounds of `Weight`.
pub fn add(&mut self, weight: Weight, class: DispatchClass) {
let value = self.get_mut(class);
*value = value.saturating_add(weight);
/// Add some weight to the given class. Saturates at the numeric bounds.
pub fn add(mut self, weight: Weight, class: DispatchClass) -> Self {
self.accrue(weight, class);
self
}

/// Increase the weight of the given class. Saturates at the numeric bounds.
pub fn accrue(&mut self, weight: Weight, class: DispatchClass) {
self.get_mut(class).saturating_accrue(weight);
}

/// Try to add some weight of a specific dispatch class, returning Err(()) if overflow would
/// occur.
pub fn checked_add(&mut self, weight: Weight, class: DispatchClass) -> Result<(), ()> {
let value = self.get_mut(class);
*value = value.checked_add(&weight).ok_or(())?;
Ok(())
/// Try to increase the weight of the given class. Saturates at the numeric bounds.
pub fn checked_accrue(&mut self, weight: Weight, class: DispatchClass) -> Result<(), ()> {
self.get_mut(class).checked_accrue(weight).ok_or(())
}

/// Subtract some weight of a specific dispatch class, saturating at the numeric bounds of
/// `Weight`.
pub fn sub(&mut self, weight: Weight, class: DispatchClass) {
let value = self.get_mut(class);
*value = value.saturating_sub(weight);
/// Reduce the weight of the given class. Saturates at the numeric bounds.
pub fn reduce(&mut self, weight: Weight, class: DispatchClass) {
self.get_mut(class).saturating_reduce(weight);
}
}

Expand Down Expand Up @@ -3693,3 +3695,178 @@ mod weight_tests {
assert_eq!(extract_actual_pays_fee(&Ok((Some(1000), Pays::Yes).into()), &pre), Pays::No);
}
}

#[cfg(test)]
mod per_dispatch_class_tests {
use super::*;
use sp_runtime::traits::Zero;
use DispatchClass::*;

#[test]
fn add_works() {
let a = PerDispatchClass {
normal: (5, 10).into(),
operational: (20, 30).into(),
mandatory: Weight::MAX,
};
assert_eq!(
a.clone()
.add((20, 5).into(), Normal)
.add((10, 10).into(), Operational)
.add((u64::MAX, 3).into(), Mandatory),
PerDispatchClass {
normal: (25, 15).into(),
operational: (30, 40).into(),
mandatory: Weight::MAX
}
);
let b = a
.add(Weight::MAX, Normal)
.add(Weight::MAX, Operational)
.add(Weight::MAX, Mandatory);
assert_eq!(
b,
PerDispatchClass {
normal: Weight::MAX,
operational: Weight::MAX,
mandatory: Weight::MAX
}
);
assert_eq!(b.total(), Weight::MAX);
}

#[test]
fn accrue_works() {
let mut a = PerDispatchClass::default();

a.accrue((10, 15).into(), Normal);
assert_eq!(a.normal, (10, 15).into());
assert_eq!(a.total(), (10, 15).into());

a.accrue((20, 25).into(), Operational);
assert_eq!(a.operational, (20, 25).into());
assert_eq!(a.total(), (30, 40).into());

a.accrue((30, 35).into(), Mandatory);
assert_eq!(a.mandatory, (30, 35).into());
assert_eq!(a.total(), (60, 75).into());

a.accrue((u64::MAX, 10).into(), Operational);
assert_eq!(a.operational, (u64::MAX, 35).into());
assert_eq!(a.total(), (u64::MAX, 85).into());

a.accrue((10, u64::MAX).into(), Normal);
assert_eq!(a.normal, (20, u64::MAX).into());
assert_eq!(a.total(), Weight::MAX);
}

#[test]
fn reduce_works() {
let mut a = PerDispatchClass {
normal: (10, u64::MAX).into(),
mandatory: (u64::MAX, 10).into(),
operational: (20, 20).into(),
};

a.reduce((5, 100).into(), Normal);
assert_eq!(a.normal, (5, u64::MAX - 100).into());
assert_eq!(a.total(), (u64::MAX, u64::MAX - 70).into());

a.reduce((15, 5).into(), Operational);
assert_eq!(a.operational, (5, 15).into());
assert_eq!(a.total(), (u64::MAX, u64::MAX - 75).into());

a.reduce((50, 0).into(), Mandatory);
assert_eq!(a.mandatory, (u64::MAX - 50, 10).into());
assert_eq!(a.total(), (u64::MAX - 40, u64::MAX - 75).into());

a.reduce((u64::MAX, 100).into(), Operational);
assert!(a.operational.is_zero());
assert_eq!(a.total(), (u64::MAX - 45, u64::MAX - 90).into());

a.reduce((5, u64::MAX).into(), Normal);
assert!(a.normal.is_zero());
assert_eq!(a.total(), (u64::MAX - 50, 10).into());
}

#[test]
fn checked_accrue_works() {
let mut a = PerDispatchClass::default();

a.checked_accrue((1, 2).into(), Normal).unwrap();
a.checked_accrue((3, 4).into(), Operational).unwrap();
a.checked_accrue((5, 6).into(), Mandatory).unwrap();
a.checked_accrue((7, 8).into(), Operational).unwrap();
a.checked_accrue((9, 0).into(), Normal).unwrap();

assert_eq!(
a,
PerDispatchClass {
normal: (10, 2).into(),
operational: (10, 12).into(),
mandatory: (5, 6).into(),
}
);

a.checked_accrue((u64::MAX - 10, u64::MAX - 2).into(), Normal).unwrap();
a.checked_accrue((0, 0).into(), Normal).unwrap();
a.checked_accrue((1, 0).into(), Normal).unwrap_err();
a.checked_accrue((0, 1).into(), Normal).unwrap_err();

assert_eq!(
a,
PerDispatchClass {
normal: Weight::MAX,
operational: (10, 12).into(),
mandatory: (5, 6).into(),
}
);
}

#[test]
fn checked_accrue_does_not_modify_on_error() {
let mut a = PerDispatchClass {
normal: 0.into(),
operational: Weight::MAX / 2 + 2.into(),
mandatory: 10.into(),
};

a.checked_accrue(Weight::MAX / 2, Operational).unwrap_err();
a.checked_accrue(Weight::MAX - 9.into(), Mandatory).unwrap_err();
a.checked_accrue(Weight::MAX, Normal).unwrap(); // This one works

assert_eq!(
a,
PerDispatchClass {
normal: Weight::MAX,
operational: Weight::MAX / 2 + 2.into(),
mandatory: 10.into(),
}
);
}

#[test]
fn total_works() {
assert!(PerDispatchClass::default().total().is_zero());

assert_eq!(
PerDispatchClass {
normal: 0.into(),
operational: (10, 20).into(),
mandatory: (20, u64::MAX).into(),
}
.total(),
(30, u64::MAX).into()
);

assert_eq!(
PerDispatchClass {
normal: (u64::MAX - 10, 10).into(),
operational: (3, u64::MAX).into(),
mandatory: (4, u64::MAX).into(),
}
.total(),
(u64::MAX - 3, u64::MAX).into()
);
}
}
10 changes: 1 addition & 9 deletions frame/support/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

//! Re-exports `sp-weights` public API, and contains benchmarked weight constants specific to
//! FRAME.
//!
//! Latest machine specification used to benchmark are:
//! - Digital Ocean: ubuntu-s-2vcpu-4gb-ams3-01
//! - 2x Intel(R) Xeon(R) CPU E5-2650 v4 @ 2.20GHz
//! - 4GB RAM
//! - Ubuntu 19.10 (GNU/Linux 5.3.0-18-generic x86_64)
//! - rustc 1.42.0 (b8cedc004 2020-03-09)
//! Re-exports `sp-weights` public API, and contains benchmarked weight constants specific to FRAME.
mod block_weights;
mod extrinsic_weights;
Expand Down
6 changes: 3 additions & 3 deletions frame/system/src/extensions/check_weight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,10 @@ where

// add the weight. If class is unlimited, use saturating add instead of checked one.
if limit_per_class.max_total.is_none() && limit_per_class.reserved.is_none() {
all_weight.add(extrinsic_weight, info.class)
all_weight.accrue(extrinsic_weight, info.class)
} else {
all_weight
.checked_add(extrinsic_weight, info.class)
.checked_accrue(extrinsic_weight, info.class)
.map_err(|_| InvalidTransaction::ExhaustsResources)?;
}

Expand Down Expand Up @@ -229,7 +229,7 @@ where
let unspent = post_info.calc_unspent(info);
if unspent.any_gt(Weight::zero()) {
crate::BlockWeight::<T>::mutate(|current_weight| {
current_weight.sub(unspent, info.class);
current_weight.reduce(unspent, info.class);
})
}

Expand Down
2 changes: 1 addition & 1 deletion frame/system/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1321,7 +1321,7 @@ impl<T: Config> Pallet<T> {
/// Another potential use-case could be for the `on_initialize` and `on_finalize` hooks.
pub fn register_extra_weight_unchecked(weight: Weight, class: DispatchClass) {
BlockWeight::<T>::mutate(|current_weight| {
current_weight.add(weight, class);
current_weight.accrue(weight, class);
});
}

Expand Down
7 changes: 0 additions & 7 deletions primitives/weights/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@
// limitations under the License.

//! # Primitives for transaction weighting.
//!
//! Latest machine specification used to benchmark are:
//! - Digital Ocean: ubuntu-s-2vcpu-4gb-ams3-01
//! - 2x Intel(R) Xeon(R) CPU E5-2650 v4 @ 2.20GHz
//! - 4GB RAM
//! - Ubuntu 19.10 (GNU/Linux 5.3.0-18-generic x86_64)
//! - rustc 1.42.0 (b8cedc004 2020-03-09)
#![cfg_attr(not(feature = "std"), no_std)]

Expand Down
Loading

0 comments on commit 495cc0e

Please sign in to comment.