Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defensive Programming in Substrate Reference Document #2615

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
85740c0
Add to new branch
CrackTheCode016 Dec 4, 2023
db5d2d0
Merge branch 'master' into bader-defensive-programming
CrackTheCode016 Dec 4, 2023
9f76b7b
add decision chart
CrackTheCode016 Jan 1, 2024
6d232fc
add feedback
CrackTheCode016 Jan 1, 2024
a7a4ee1
few tweaks
CrackTheCode016 Jan 1, 2024
3014e20
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
48d2f16
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
8ac9f44
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
8b20c60
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
3a2c603
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
1962902
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
263f5c7
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
e357c60
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
4341e28
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
36c2258
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
082c30b
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
cd1b330
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
9894d28
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
9d7e1ec
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
94de958
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
9ae0bd3
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
98e1539
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
9b260f8
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
92412d9
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
73b9e5d
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
1738854
Update developer-hub/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 8, 2024
4adb257
Update developer-hub/src/reference_docs/mod.rs
CrackTheCode016 Jan 8, 2024
fdd26df
new branch
CrackTheCode016 Jan 8, 2024
794ef40
Merge branch 'master' of github.com:CrackTheCode016/polkadot-sdk
CrackTheCode016 Jan 8, 2024
dfe74cc
Merge pull request #1 from CrackTheCode016/master
CrackTheCode016 Jan 8, 2024
8b1510f
reapply changes, fix test
CrackTheCode016 Jan 8, 2024
2e4e076
fmt + add license
CrackTheCode016 Jan 11, 2024
702990e
defensive list, clarification
CrackTheCode016 Jan 11, 2024
1451cdd
spruce up tests
CrackTheCode016 Jan 11, 2024
5cf16e3
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 12, 2024
baab755
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 12, 2024
910cb9d
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 12, 2024
5ff3159
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 12, 2024
7d64bb6
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 12, 2024
6920c58
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 12, 2024
570f0cb
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 12, 2024
eec2700
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 12, 2024
2c8c781
address feedback
CrackTheCode016 Jan 12, 2024
a0a631f
added example of valid panic
CrackTheCode016 Jan 15, 2024
bec3672
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Jan 15, 2024
5607328
Merge branch 'master' into bader-defensive-programming
CrackTheCode016 Jan 15, 2024
94b32c5
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
fa707bb
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
3cb2f67
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
1421905
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
5ad43f9
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
5a0393f
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
89926c1
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
023fa13
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
f2da603
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
b07facb
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
f832eed
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
2114c71
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
b4b12e1
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
0b25f7c
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
07fe21f
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
d294b6a
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
75ba359
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
f46c4ae
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
d3c4b7c
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
ce75101
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
3b68daa
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
679171b
Update docs/sdk/src/reference_docs/defensive_programming.rs
CrackTheCode016 Feb 13, 2024
171aca0
Merge branch 'master' into bader-defensive-programming
CrackTheCode016 Feb 13, 2024
1abe527
Merge branch 'master' into bader-defensive-programming
kianenigma Feb 15, 2024
2a31634
Merge branch 'master' into bader-defensive-programming
kianenigma Feb 23, 2024
8b7fcb9
Merge branch 'master' into bader-defensive-programming
CrackTheCode016 Mar 6, 2024
fc45d70
(inital) move things around
CrackTheCode016 Mar 7, 2024
2966925
start moving things around (unfinished)
CrackTheCode016 Mar 8, 2024
83a13d7
polish + grammar
CrackTheCode016 Mar 11, 2024
114b677
fmt + other checks
CrackTheCode016 Mar 11, 2024
81f366f
make perthings more concise
CrackTheCode016 Mar 11, 2024
2a6521e
fmt and clippy
CrackTheCode016 Mar 11, 2024
ba9cd07
Merge branch 'master' into bader-defensive-programming
CrackTheCode016 Mar 11, 2024
5987b6d
make sure tests pass
CrackTheCode016 Mar 13, 2024
875444c
Merge branch 'master' into bader-defensive-programming
kianenigma Mar 18, 2024
0be68cc
Merge branch 'master' into bader-defensive-programming
CrackTheCode016 Mar 19, 2024
110c23c
lock update
CrackTheCode016 Mar 19, 2024
bd4c745
attempt to make clippy happy
CrackTheCode016 Mar 19, 2024
5ee7b1a
attempt 2
CrackTheCode016 Mar 19, 2024
562b2b4
more clippy (facepalm)
CrackTheCode016 Mar 19, 2024
75265e3
fmt
CrackTheCode016 Mar 19, 2024
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
6 changes: 6 additions & 0 deletions Cargo.lock

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

7 changes: 7 additions & 0 deletions docs/sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ sp-api = { path = "../../substrate/primitives/api" }
sp-core = { path = "../../substrate/primitives/core" }
sp-keyring = { path = "../../substrate/primitives/keyring" }
sp-runtime = { path = "../../substrate/primitives/runtime" }
sp-arithmetic = { path = "../../substrate/primitives/arithmetic" }

# Misc pallet dependencies
pallet-referenda = { path = "../../substrate/frame/referenda" }
pallet-broker = { path = "../../substrate/frame/broker" }
pallet-babe = { path = "../../substrate/frame/babe" }

sp-offchain = { path = "../../substrate/primitives/offchain" }
sp-version = { path = "../../substrate/primitives/version" }

Expand Down
6 changes: 3 additions & 3 deletions docs/sdk/src/guides/your_first_pallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@
//! This macro will call `.into()` under the hood.
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_better)]
//!
//! Moreover, you will learn in the [Safe Defensive Programming
//! section](crate::reference_docs::safe_defensive_programming) that it is always recommended to use
//! Moreover, you will learn in the [Defensive Programming
//! section](crate::reference_docs::defensive_programming) that it is always recommended to use
//! safe arithmetic operations in your runtime. By using [`frame::traits::CheckedSub`], we can not
//! only take a step in that direction, but also improve the error handing and make it slightly more
//! ergonomic.
Expand Down Expand Up @@ -294,7 +294,7 @@
//! The following topics where used in this guide, but not covered in depth. It is suggested to
//! study them subsequently:
//!
//! - [`crate::reference_docs::safe_defensive_programming`].
//! - [`crate::reference_docs::defensive_programming`].
//! - [`crate::reference_docs::frame_origin`].
//! - [`crate::reference_docs::frame_runtime_types`].
//! - The pallet we wrote in this guide was using `dev_mode`, learn more in
Expand Down
395 changes: 395 additions & 0 deletions docs/sdk/src/reference_docs/defensive_programming.rs

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions docs/sdk/src/reference_docs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ pub mod signed_extensions;
pub mod frame_origin;

/// Learn about how to write safe and defensive code in your FRAME runtime.
// TODO: @CrackTheCode016 https://github.com/paritytech/polkadot-sdk-docs/issues/44
pub mod safe_defensive_programming;
pub mod defensive_programming;

/// Learn about composite enums and other runtime level types, such as "RuntimeEvent" and
/// "RuntimeCall".
Expand Down
1 change: 0 additions & 1 deletion docs/sdk/src/reference_docs/safe_defensive_programming.rs

This file was deleted.

3 changes: 3 additions & 0 deletions substrate/primitives/arithmetic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ num-traits = { version = "0.2.17", default-features = false }
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
serde = { features = ["alloc", "derive"], optional = true, workspace = true }
static_assertions = "1.1.0"
sp-std = { path = "../std", default-features = false }
docify = "0.2.7"

[dev-dependencies]
criterion = "0.4.0"
Expand All @@ -41,6 +43,7 @@ std = [
"scale-info/std",
"serde/std",
"sp-crypto-hashing/std",
"sp-std/std",
]
# Serde support without relying on std features.
serde = ["dep:serde", "scale-info/serde"]
Expand Down
27 changes: 27 additions & 0 deletions substrate/primitives/arithmetic/src/fixed_point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,33 @@
// limitations under the License.

//! Decimal Fixed Point implementations for Substrate runtime.
//! Similar to types that implement [`PerThing`](crate::per_things), these are also
//! fixed-point types, however, they are able to represent larger fractions:
#![doc = docify::embed!("./src/lib.rs", fixed_u64)]
//!
//! ### Fixed Point Types in Practice
//!
//! If one needs to exceed the value of one (1), then
//! [`FixedU64`](FixedU64) (and its signed and `u128` counterparts) can be utilized.
//! Take for example this very rudimentary pricing mechanism, where we wish to calculate the demand
//! / supply to get a price for some on-chain compute:
#![doc = docify::embed!(
"./src/lib.rs",
fixed_u64_block_computation_example
)]
//!
//! For a much more comprehensive example, be sure to look at the source for broker (the "coretime")
//! pallet.
//!
//! #### Fixed Point Types in Practice
//!
//! Just as with [`PerThing`](PerThing), you can also perform regular mathematical
//! expressions:
#![doc = docify::embed!(
"./src/lib.rs",
fixed_u64_operation_example
)]
//!

use crate::{
helpers_128bit::{multiply_by_rational_with_rounding, sqrt},
Expand Down
138 changes: 110 additions & 28 deletions substrate/primitives/arithmetic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ where
fn tcmp(&self, other: &T, threshold: T) -> Ordering {
// early exit.
if threshold.is_zero() {
return self.cmp(other)
return self.cmp(other);
}

let upper_bound = other.saturating_add(threshold);
Expand Down Expand Up @@ -206,12 +206,12 @@ where

// Nothing to do here.
if count.is_zero() {
return Ok(Vec::<T>::new())
return Ok(Vec::<T>::new());
}

let diff = targeted_sum.max(sum) - targeted_sum.min(sum);
if diff.is_zero() {
return Ok(input.to_vec())
return Ok(input.to_vec());
}

let needs_bump = targeted_sum > sum;
Expand Down Expand Up @@ -254,7 +254,7 @@ where
min_index += 1;
min_index %= count;
}
leftover -= One::one()
leftover -= One::one();
}
} else {
// must decrease the stakes a bit. decrement from the max element. index of maximum is now
Expand Down Expand Up @@ -288,7 +288,7 @@ where
if output_with_idx[max_index].1 <= threshold {
max_index = max_index.checked_sub(1).unwrap_or(count - 1);
}
leftover -= One::one()
leftover -= One::one();
} else {
max_index = max_index.checked_sub(1).unwrap_or(count - 1);
}
Expand All @@ -300,7 +300,7 @@ where
targeted_sum,
"sum({:?}) != {:?}",
output_with_idx,
targeted_sum,
targeted_sum
);

// sort again based on the original index.
Expand Down Expand Up @@ -356,7 +356,7 @@ mod normalize_tests {
vec![
Perbill::from_parts(333333334),
Perbill::from_parts(333333333),
Perbill::from_parts(333333333),
Perbill::from_parts(333333333)
]
);

Expand All @@ -367,7 +367,7 @@ mod normalize_tests {
vec![
Perbill::from_parts(316666668),
Perbill::from_parts(383333332),
Perbill::from_parts(300000000),
Perbill::from_parts(300000000)
]
);
}
Expand All @@ -378,13 +378,13 @@ mod normalize_tests {
// could have a situation where the sum cannot be calculated in the inner type. Calculating
// using the upper type of the per_thing should assure this to be okay.
assert_eq!(
vec![PerU16::from_percent(40), PerU16::from_percent(40), PerU16::from_percent(40),]
vec![PerU16::from_percent(40), PerU16::from_percent(40), PerU16::from_percent(40)]
.normalize(PerU16::one())
.unwrap(),
vec![
PerU16::from_parts(21845), // 33%
PerU16::from_parts(21845), // 33%
PerU16::from_parts(21845), // 33%
PerU16::from_parts(21845) // 33%
]
);
}
Expand Down Expand Up @@ -428,6 +428,88 @@ mod normalize_tests {
}
}

#[cfg(test)]
mod per_and_fixed_examples {
use super::*;

#[docify::export]
#[test]
fn percent_mult() {
let percent = Percent::from_rational(5u32, 100u32); // aka, 5%
let five_percent_of_100 = percent * 100u32; // 5% of 100 is 5.
assert_eq!(five_percent_of_100, 5)
}
#[docify::export]
#[test]
fn perbill_example() {
let p = Perbill::from_percent(80);
// 800000000 bil, or a representative of 0.800000000.
// Precision is in the billions place.
assert_eq!(p.deconstruct(), 800000000);
}

#[docify::export]
#[test]
fn percent_example() {
let percent = Percent::from_rational(190u32, 400u32);
assert_eq!(percent.deconstruct(), 47);
}

#[docify::export]
#[test]
fn fixed_u64_block_computation_example() {
// Calculate a very rudimentary on-chain price from supply / demand
// Supply: Cores available per block
// Demand: Cores being ordered per block
let price = FixedU64::from_rational(5u128, 10u128);

// 0.5 DOT per core
assert_eq!(price, FixedU64::from_float(0.5));

// Now, the story has changed - lots of demand means we buy as many cores as there
// available. This also means that price goes up! For the sake of simplicity, we don't care
// about who gets a core - just about our very simple price model

// Calculate a very rudimentary on-chain price from supply / demand
// Supply: Cores available per block
// Demand: Cores being ordered per block
let price = FixedU64::from_rational(19u128, 10u128);

// 1.9 DOT per core
assert_eq!(price, FixedU64::from_float(1.9));
}

#[docify::export]
#[test]
fn fixed_u64() {
// The difference between this and perthings is perthings operates within the relam of [0,
// 1] In cases where we need > 1, we can used fixed types such as FixedU64

let rational_1 = FixedU64::from_rational(10, 5); //" 200%" aka 2.
let rational_2 = FixedU64::from_rational_with_rounding(5, 10, Rounding::Down); // "50%" aka 0.50...

assert_eq!(rational_1, (2u64).into());
assert_eq!(rational_2.into_perbill(), Perbill::from_float(0.5));
}

#[docify::export]
#[test]
fn fixed_u64_operation_example() {
let rational_1 = FixedU64::from_rational(10, 5); // "200%" aka 2.
let rational_2 = FixedU64::from_rational(8, 5); // "160%" aka 1.6.

let addition = rational_1 + rational_2;
let multiplication = rational_1 * rational_2;
let division = rational_1 / rational_2;
let subtraction = rational_1 - rational_2;

assert_eq!(addition, FixedU64::from_float(3.6));
assert_eq!(multiplication, FixedU64::from_float(3.2));
assert_eq!(division, FixedU64::from_float(1.25));
assert_eq!(subtraction, FixedU64::from_float(0.4));
}
}

#[cfg(test)]
mod threshold_compare_tests {
use super::*;
Expand All @@ -440,15 +522,15 @@ mod threshold_compare_tests {
let e = Perbill::from_percent(10).mul_ceil(b);

// [115 - 11,5 (103,5), 115 + 11,5 (126,5)] is all equal
assert_eq!(103u32.tcmp(&b, e), Ordering::Equal);
assert_eq!(104u32.tcmp(&b, e), Ordering::Equal);
assert_eq!(115u32.tcmp(&b, e), Ordering::Equal);
assert_eq!(120u32.tcmp(&b, e), Ordering::Equal);
assert_eq!(126u32.tcmp(&b, e), Ordering::Equal);
assert_eq!(127u32.tcmp(&b, e), Ordering::Equal);

assert_eq!(128u32.tcmp(&b, e), Ordering::Greater);
assert_eq!(102u32.tcmp(&b, e), Ordering::Less);
assert_eq!((103u32).tcmp(&b, e), Ordering::Equal);
assert_eq!((104u32).tcmp(&b, e), Ordering::Equal);
assert_eq!((115u32).tcmp(&b, e), Ordering::Equal);
assert_eq!((120u32).tcmp(&b, e), Ordering::Equal);
assert_eq!((126u32).tcmp(&b, e), Ordering::Equal);
assert_eq!((127u32).tcmp(&b, e), Ordering::Equal);

assert_eq!((128u32).tcmp(&b, e), Ordering::Greater);
assert_eq!((102u32).tcmp(&b, e), Ordering::Less);
}

#[test]
Expand All @@ -458,15 +540,15 @@ mod threshold_compare_tests {
let e = Perbill::from_parts(100) * b;

// [115 - 11,5 (103,5), 115 + 11,5 (126,5)] is all equal
assert_eq!(103u32.tcmp(&b, e), 103u32.cmp(&b));
assert_eq!(104u32.tcmp(&b, e), 104u32.cmp(&b));
assert_eq!(115u32.tcmp(&b, e), 115u32.cmp(&b));
assert_eq!(120u32.tcmp(&b, e), 120u32.cmp(&b));
assert_eq!(126u32.tcmp(&b, e), 126u32.cmp(&b));
assert_eq!(127u32.tcmp(&b, e), 127u32.cmp(&b));

assert_eq!(128u32.tcmp(&b, e), 128u32.cmp(&b));
assert_eq!(102u32.tcmp(&b, e), 102u32.cmp(&b));
assert_eq!((103u32).tcmp(&b, e), (103u32).cmp(&b));
assert_eq!((104u32).tcmp(&b, e), (104u32).cmp(&b));
assert_eq!((115u32).tcmp(&b, e), (115u32).cmp(&b));
assert_eq!((120u32).tcmp(&b, e), (120u32).cmp(&b));
assert_eq!((126u32).tcmp(&b, e), (126u32).cmp(&b));
assert_eq!((127u32).tcmp(&b, e), (127u32).cmp(&b));

assert_eq!((128u32).tcmp(&b, e), (128u32).cmp(&b));
assert_eq!((102u32).tcmp(&b, e), (102u32).cmp(&b));
}

#[test]
Expand Down
Loading
Loading