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

[developer_hub] Defensive Programming in Substrate Reference Document #2206

Conversation

CrackTheCode016
Copy link
Contributor

@CrackTheCode016 CrackTheCode016 commented Nov 7, 2023

closes paritytech/polkadot-sdk-docs#44

Description

This PR adds a reference document to the developer-hub crate (see #2102). This specific reference document covers defensive programming practices common within the context of developing a runtime with Substrate.

Please note this is meant to merge with kiz-developer-hub

In particular, this covers the following areas:

  • Default behavior of how Rust deals with numbers in general
  • How to deal with floating point numbers in runtime / fixed point arithmetic
  • How to deal with Integer overflows
  • General "safe math" / defensive programming practices for common pallet development scenarios
  • Defensive traits that exist within Substrate, i.e., defensive_saturating_add , defensive_unwrap_or
  • More general defensive programming examples (keep it concise)
  • Link to relevant examples where these practices are actually in production / being used
  • Unwrapping (or rather lack thereof) 101

@CrackTheCode016 CrackTheCode016 marked this pull request as draft November 7, 2023 17:30
@ggwpez
Copy link
Member

ggwpez commented Nov 7, 2023

If you need more defensive example in the code then search for defensive! or defensive_. Eg we also have defensive_saturating_add, defensive_unwrap_or, defensive_min etc.

@paritytech-review-bot paritytech-review-bot bot requested a review from a team November 9, 2023 14:34
@juangirini juangirini linked an issue Nov 9, 2023 that may be closed by this pull request
@CrackTheCode016 CrackTheCode016 marked this pull request as ready for review November 13, 2023 16:47
@CrackTheCode016 CrackTheCode016 requested a review from a team November 13, 2023 16:47
@liamaharon liamaharon self-requested a review November 14, 2023 06:26
Copy link
Contributor

@liamaharon liamaharon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looking very good, covers all the concepts required.

My biggest nit with this is I feel it is way more verbose than required and can be much more concise. Sometimes it felt like I was sifting through the words to try to find the important information.

I would suggest working through each sentence and evaluating if it is providing relevant information as succinctly as possible, and also repeating something from earlier or that the developer should already know.

We also shouldn't have code examples or explanations in the ref docs that couldn't be documented in the upstream crates. e.g. almost all the content on checked and saturating arithmetic I think should be pushed up into the crates themselves. Ref docs should be very brief.

@@ -1 +1,615 @@
//! As our runtime should _never_ panic; this includes carefully handling [`Result`]/[`Option`]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rename this file just defensive_programming.rs. I think safe is kind of implied.

Comment on lines 23 to 25
//! Most of the time, unless you wish for your node to be intentionally brought down - panicking is
//! something that your runtime should feverishly protect against. This includes the following
//! considerations:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean by "unless you wish for your node to be intentionally brought down". Panicing will lead to a bricked chain, not just taking down the node. I'd just remove the "unless" clause entirely, we really should never panic :P

Something like

Suggested change
//! Most of the time, unless you wish for your node to be intentionally brought down - panicking is
//! something that your runtime should feverishly protect against. This includes the following
//! considerations:
//! Panicking in the runtime will brick your chain, therefore is something you should feverishly protect against. This includes the following
//! considerations:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some panics in Substrate, i.e.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't mean it is best practice :P

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My impression is that panicking should only occur if in the event that it would actually be better (in a rare case) to shut down rather than continue operating. This is probably only for 'critical' components, i.e., finality, authoring, or consensus related, but I could be wrong here.

The aura example is a good one, I'll link to it from within and rephrase accordingly.

Comment on lines 126 to 132
//! A quick example is a user's balance overflowing: the default behavior of wrapping could result
//! in the user's balance starting from zero, or vice versa, of a `0` balance turning into the `MAX`
//! of some type. Naturally, this could lead to various exploits and issues down the road, which if
//! failing silently, would be difficult to trace and rectify.
//!
//! Luckily, there are ways to both represent and handle these scenarios depending on our specific
//! use case natively built into Rust, as well as libraries like [`sp_arithmetic`].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kinda wordy, I wonder if it could be made more concise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking actually it can be removed altogether now, given that we have examples below.

Comment on lines 134 to 149
//! ## Infallible Arithmetic
//!
//! Our main objective in runtime development is to reduce the likelihood of any *unintended* or
//! *undefined* behavior. Intentional and predictable design should be our first and foremost
//! property for ensuring a well running, safely designed system. Both Rust and Substrate both
//! provide safe ways to deal with numbers and alternatives to floating point arithmetic.
//!
//! Rather they (should) use fixed-point arithmetic to mitigate the potential for inaccuracy,
//! rounding errors, or other unexpected behavior. For more on the specifics of the peculiarities of floating point calculations, [watch this video by the Computerphile](https://www.youtube.com/watch?v=PZRI1IfStY0).
//!
//! Using **primitive** floating point number types in a blockchain context should also be avoided,
//! as a single nondeterministic result could cause chaos for consensus along with the
//! aforementioned issues.
//!
//! The following methods represent different ways one can handle numbers safely natively in Rust,
//! without fear of panic or unexpected behavior from wrapping.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's only the very end of the first para that I see this is about floating point arithmetic, I suggest making it more clear earlier

Generally it's very wordy, lots of repetition including from previous sections. I wonder if this could be condensed into just a few sentences, e.g.

## Floating Point Arithmetic

Floating point arithmetic can be non-deterministic across system architectures, making it unsuitable for usage in runtimes.

Runtimes should instead use [FixedPoint](https://crates.parity.io/sp_arithmetic/fixed_point/index.html) arithmetic.

For more on the peculiarities of floating point calculations, [see this video by the Computerphile](https://www.youtube.com/watch?v=PZRI1IfStY0).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is slightly more concise now, but I still left a few details. Let me know if it is still too wordy

@paritytech-cicd-pr
Copy link

The CI pipeline was cancelled due to failure one of the required jobs.
Job name: cargo-clippy
Logs: https://gitlab.parity.io/parity/mirrors/polkadot-sdk/-/jobs/4445600

@CrackTheCode016
Copy link
Contributor Author

@liamaharon @juangirini Great feedback, thank you!

I have tried to reduce any 'bloat', I left some explanations which may be deemed as verbose. Let me know if it needs more trimming!

Comment on lines +23 to +26
//! ***DO NOT PANIC!***
//!
//! Most of the time - there are some exceptions, such as critical operations being actually more
//! dangerous than allowing the node to continue running (block authoring, consensus, etc).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confused me initially reading a connected sentence / concept over 2 paragraphs

Suggested change
//! ***DO NOT PANIC!***
//!
//! Most of the time - there are some exceptions, such as critical operations being actually more
//! dangerous than allowing the node to continue running (block authoring, consensus, etc).
//! ***DO NOT PANIC,*** most of the time. There are some exceptions, such as critical operations being actually more
//! dangerous than allowing the node to continue running (block authoring, consensus, etc).

Comment on lines +27 to +28
//!
//! - Directly using `unwrap()` for a [`Result`] shouldn't be used.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//!
//! - Directly using `unwrap()` for a [`Result`] shouldn't be used.
//! General guidelines:
//!
//! - Avoid directly using `unwrap()` for a [`Result`].

Comment on lines +39 to +44
//! To also aid in debugging and mitigating the above issues, there is a
//! [`Defensive`](frame::traits::Defensive) trait (and its companions,
//! [`DefensiveOption`](frame::traits::DefensiveOption),
//! [`DefensiveResult`](frame::traits::DefensiveResult)) that can be used to defensively unwrap
//! values. This can be used in place of
//! an `expect`, and again, only if the developer is sure about the unwrap in the first place.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//! To also aid in debugging and mitigating the above issues, there is a
//! [`Defensive`](frame::traits::Defensive) trait (and its companions,
//! [`DefensiveOption`](frame::traits::DefensiveOption),
//! [`DefensiveResult`](frame::traits::DefensiveResult)) that can be used to defensively unwrap
//! values. This can be used in place of
//! an `expect`, and again, only if the developer is sure about the unwrap in the first place.
//! To help write defensive code, Substrate includes three traits
//! [`Defensive`](frame::traits::Defensive),
//! [`DefensiveOption`](frame::traits::DefensiveOption) and
//! [`DefensiveResult`](frame::traits::DefensiveResult) that can be used to defensively unwrap
//! values. These should usually be used in place of
//! `expect` to handle unexpected code paths.

Comment on lines +46 to +48
//! The primary difference of defensive implementations bring over vanilla ones is the usage of [`debug_assertions`](https://doc.rust-lang.org/reference/conditional-compilation.html#debug_assertions).
//! `debug_assertions` allows for panics to occur in a testing context, but in
//! production/release, they will merely log an error (i.e., `log::error`).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//! The primary difference of defensive implementations bring over vanilla ones is the usage of [`debug_assertions`](https://doc.rust-lang.org/reference/conditional-compilation.html#debug_assertions).
//! `debug_assertions` allows for panics to occur in a testing context, but in
//! production/release, they will merely log an error (i.e., `log::error`).
//! Defensive methods use [`debug_assertions`](https://doc.rust-lang.org/reference/conditional-compilation.html#debug_assertions), which panic in development, but in
//! production/release, they will merely log an error (i.e., `log::error`).

Comment on lines +56 to +58
//! This traits are useful for catching issues in the development environment, without risking
//! panicking in production.
//!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is already clear from previous paragraphs

Suggested change
//! This traits are useful for catching issues in the development environment, without risking
//! panicking in production.
//!

Comment on lines +77 to +84
//! It is actually the _silent_ portion of this behavior that presents a real issue. Such behavior should be made obvious, especially in
//! the context of blockchain development, where unsafe arithmetic could produce unexpected
//! consequences.
//!
//! A quick example is a user's balance overflowing: the default behavior of wrapping could result
//! in the user's balance starting from zero, or vice versa, of a `0` balance turning into the
//! `MAX`. This could lead to various exploits and issues down the road, which if
//! failing silently, would be difficult to trace and rectify in production.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//! It is actually the _silent_ portion of this behavior that presents a real issue. Such behavior should be made obvious, especially in
//! the context of blockchain development, where unsafe arithmetic could produce unexpected
//! consequences.
//!
//! A quick example is a user's balance overflowing: the default behavior of wrapping could result
//! in the user's balance starting from zero, or vice versa, of a `0` balance turning into the
//! `MAX`. This could lead to various exploits and issues down the road, which if
//! failing silently, would be difficult to trace and rectify in production.
//! It is the _silent_ portion of this behavior that presents a real issue. Such behavior should be made obvious, especially in
//! the context of blockchain development, where unsafe arithmetic could produce unexpected
//! consequences like a user balance over or underflowing.

Comment on lines +94 to +99
//! A developer should use fixed-point arithmetic to mitigate the potential for inaccuracy,
//! rounding errors, or other unexpected behavior. For more on the specifics of the peculiarities of floating point calculations, [watch this video by the Computerphile](https://www.youtube.com/watch?v=PZRI1IfStY0).
//!
//! Using **primitive** floating point number types in a blockchain context should be avoided,
//! as a single nondeterministic result could cause chaos for consensus along with the
//! aforementioned issues.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//! A developer should use fixed-point arithmetic to mitigate the potential for inaccuracy,
//! rounding errors, or other unexpected behavior. For more on the specifics of the peculiarities of floating point calculations, [watch this video by the Computerphile](https://www.youtube.com/watch?v=PZRI1IfStY0).
//!
//! Using **primitive** floating point number types in a blockchain context should be avoided,
//! as a single nondeterministic result could cause chaos for consensus along with the
//! aforementioned issues.
//! A developer should use fixed-point instead of floating-point arithmetic to mitigate the potential for inaccuracy,
//! rounding errors, or other unexpected behavior.
//!
//! Using floating point number types in the runtime should be avoided,
//! as a single nondeterministic result could cause chaos for consensus along with the
//! aforementioned issues. For more on the specifics of the peculiarities of floating point calculations, [watch this video by the Computerphile](https://www.youtube.com/watch?v=PZRI1IfStY0)

//! as a single nondeterministic result could cause chaos for consensus along with the
//! aforementioned issues.
//!
//! The following methods represent different ways one can handle numbers safely natively in Rust,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//! The following methods represent different ways one can handle numbers safely natively in Rust,
//! The following methods demonstrate different ways one can handle numbers safely natively in Rust,

Comment on lines +104 to +263
//!
//! Saturating calculations can be used if one is very sure that something won't overflow, but wants
//! to avoid introducing the notion of any potential-panic or wrapping behavior.
//!
//! There is also a series of defensive alternatives via
//! [`DefensiveSaturating`](frame::traits::DefensiveSaturating), which introduces the same behavior
//! of the [`Defensive`](frame::traits::Defensive) trait, only with saturating, mathematical
//! operations:
#![doc = docify::embed!(
"./src/reference_docs/defensive_programming.rs",
saturated_defensive_example
)]
//!
//! ### Mathematical Operations in Substrate Development - Further Context
//!
//! As a recap, we covered the following concepts:
//!
//! 1. **Checked** operations - using [`Option`] or [`Result`],
//! 2. **Saturating** operations - limited to the lower and upper bounds of a number type,
//! 3. **Wrapped** operations (the default) - wrap around to above or below the bounds of a type,
//!
//!
//! Known scenarios that could be fallible should be avoided: i.e., avoiding the possibility of
//! dividing/modulo by zero at any point should be mitigated. One should be, instead, opting for a
//! `checked_` method in order to introduce safe arithmetic in their code.
//!
//!
//! #### The problem with 'default' wrapped operations
//!
//! **Wrapped operations** cause the overflow to wrap around to either the maximum or minimum of
//! that type. Imagine this in the context of a blockchain, where there are balances, voting
//! counters, nonces for transactions, and other aspects of a blockchain.
//!
//! Some of these mechanisms can be more critical than others. It's for this reason that we may
//! consider some other ways of dealing with runtime arithmetic, such as saturated or checked
//! operations, that won't carry these potential consequences.
//!
//!
//! While it may seem trivial, choosing how to handle numbers is quite important. As a thought
//! exercise, here are some scenarios of which will shed more light on when to use which.
//!
//! #### Bob's Overflowed Balance
//!
//! **Bob's** balance exceeds the `Balance` type on the `EduChain`. Because the pallet developer did
//! not handle the calculation to add to Bob's balance with any regard to this overflow, **Bob's**
//! balance is now essentially `0`, the operation **wrapped**.
//!
//! <details>
//! <summary><b>Solution: Saturating or Checked</b></summary>
//! For Bob's balance problems, using a `saturated_add` or `checked_add` could've mitigated this
//! issue. They simply would've reached the upper, or lower bounds, of the particular type for an
//! on-chain balance. In other words: Bob's balance would've stayed at the maximum of the Balance
//! type. </details>
//!
//! #### Alice's 'Underflowed' Balance
//!
//! Alice's balance has reached `0` after a transfer to Bob. Suddenly, she has been slashed on
//! `EduChain`, causing her balance to reach near the limit of `u32::MAX` - a very large amount - as
//! _wrapped operations_ can go both ways. Alice can now successfully vote using her new,
//! overpowered token balance, destroying the integrity of the chain.
//!
//! <details>
//! <summary><b>Solution: Saturating</b></summary>
//! For Alice's balance problem, using `saturated_sub` could've mitigated this issue. As debt or
//! having a negative balance is not a concept within blockchains, a saturating calculation
//! would've simply limited her balance to the lower bound of u32.
//!
//! In other words: Alice's balance would've stayed at "0", even after being slashed.
//!
//! This is also an example that while one system may work in isolation, shared interfaces, such
//! as the notion of balances, are often shared across multiple pallets - meaning these small
//! changes can make a big difference in outcome. </details>
//!
//! #### Proposals' ID Overwrite
//!
//! The type for counting the number of proposals on-chain is represented by a `u8` number, called
//! `proposals_count`. Every time a new proposal is added to the system, this number increases. With
//! the proposal pallet being high in usage, it has reached `u8::MAX`'s limit of `255`, causing
//! `proposals_count` to go to `0`. Unfortunately, this resulted in new proposals overwriting old
//! ones, effectively erasing any notion of past proposals!
//!
//! <details>
//! <summary><b>Solution: Checked</b></summary>
//! For the proposal IDs, proper handling via `checked` math would've been much more suitable,
//! Saturating could've been used - but it also would've 'failed' silently. Using `checked_add` to
//! ensure that the next proposal ID would've been valid would've been a viable way to let the user
//! know the state of their proposal:
//!
//! ```ignore
//! let next_proposal_id = current_count.checked_add(1).ok_or_else(|| Error::TooManyProposals)?;
//! ```
//!
//! </details>
//!
//!
//! From the above, we can clearly see the problematic nature of seemingly simple operations in
//! runtime. Of course, it may be that using unchecked math is perfectly fine under some scenarios -
//! such as certain balance being never realistically attainable, or a number type being so large
//! that it could never realistically overflow unless one sent thousands of transactions to the
//! network.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these examples I don't think belong in the reference docs, but in sp_arithmetic. Remember ref docs should only document stuff that cannot be documented in a crate. Instead, should link to the crate docs when possible.

You can just move them there in this PR, I think.

Comment on lines +104 to +568
assert_eq!(percent.deconstruct(), 47);
}

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

// 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

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

// 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, sp_arithmetic::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));
}
#[docify::export]
#[test]
fn bad_unwrap() {
let some_result: Result<u32, &str> = Ok(10);
assert_eq!(some_result.unwrap(), 10);
}

#[docify::export]
#[test]
fn good_unwrap() {
let some_result: Result<u32, &str> = Err("Error");
assert_eq!(some_result.unwrap_or_default(), 0);
assert_eq!(some_result.unwrap_or(10), 10);
}

#[docify::export]
#[test]
fn bad_collection_retrieval() {
let my_list = vec![1, 2, 3, 4, 5];
// THIS PANICS!
// Indexing on heap allocated values, i.e., vec, can be unsafe!
assert_eq!(my_list[5], 6)
}

#[docify::export]
#[test]
fn good_collection_retrieval() {
let my_list = vec![1, 2, 3, 4, 5];
// Rust includes `.get`, returning Option<T> - so lets use that:
assert_eq!(my_list.get(5), None)
}
#[docify::export]
#[test]
#[should_panic(expected = "Defensive failure has been triggered!")]
fn saturated_defensive_example() {
let saturated_defensive = u32::MAX.defensive_saturating_add(10);
assert_eq!(saturated_defensive, u32::MAX);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of this seems like sp_arithmetic docs (which are currently empty).

I would move all this there, then just link to it from the ref doc.

@juangirini juangirini deleted the branch paritytech:kiz-developer-hub November 30, 2023 11:15
@juangirini juangirini closed this Nov 30, 2023
@liamaharon
Copy link
Contributor

Was this automatically closed? strange I thought it would change the base branch to master

@CrackTheCode016
Copy link
Contributor Author

@liamaharon seems like it, i'll add your feedback and open a fresh PR to master #soon

github-merge-queue bot pushed a commit that referenced this pull request Mar 20, 2024
_This PR is being continued from
#2206, which was closed
when the developer_hub was merged._
closes paritytech/polkadot-sdk-docs#44

---
# Description

This PR adds a reference document to the `developer-hub` crate (see
#2102). This specific
reference document covers defensive programming practices common within
the context of developing a runtime with Substrate.

In particular, this covers the following areas: 

- Default behavior of how Rust deals with numbers in general
- How to deal with floating point numbers in runtime / fixed point
arithmetic
- How to deal with Integer overflows
- General "safe math" / defensive programming practices for common
pallet development scenarios
- Defensive traits that exist within Substrate, i.e.,
`defensive_saturating_add `, `defensive_unwrap_or`
- More general defensive programming examples (keep it concise)
- Link to relevant examples where these practices are actually in
production / being used
- Unwrapping (or rather lack thereof) 101

todo
-- 
- [x] Apply feedback from previous PR
- [x] This may warrant a PR to append some of these docs to
`sp_arithmetic`

---------

Co-authored-by: Oliver Tale-Yazdi <[email protected]>
Co-authored-by: Gonçalo Pestana <[email protected]>
Co-authored-by: Kian Paimani <[email protected]>
Co-authored-by: Francisco Aguirre <[email protected]>
Co-authored-by: Radha <[email protected]>
dharjeezy pushed a commit to dharjeezy/polkadot-sdk that referenced this pull request Mar 24, 2024
_This PR is being continued from
paritytech#2206, which was closed
when the developer_hub was merged._
closes paritytech/polkadot-sdk-docs#44

---
# Description

This PR adds a reference document to the `developer-hub` crate (see
paritytech#2102). This specific
reference document covers defensive programming practices common within
the context of developing a runtime with Substrate.

In particular, this covers the following areas: 

- Default behavior of how Rust deals with numbers in general
- How to deal with floating point numbers in runtime / fixed point
arithmetic
- How to deal with Integer overflows
- General "safe math" / defensive programming practices for common
pallet development scenarios
- Defensive traits that exist within Substrate, i.e.,
`defensive_saturating_add `, `defensive_unwrap_or`
- More general defensive programming examples (keep it concise)
- Link to relevant examples where these practices are actually in
production / being used
- Unwrapping (or rather lack thereof) 101

todo
-- 
- [x] Apply feedback from previous PR
- [x] This may warrant a PR to append some of these docs to
`sp_arithmetic`

---------

Co-authored-by: Oliver Tale-Yazdi <[email protected]>
Co-authored-by: Gonçalo Pestana <[email protected]>
Co-authored-by: Kian Paimani <[email protected]>
Co-authored-by: Francisco Aguirre <[email protected]>
Co-authored-by: Radha <[email protected]>
bkontur pushed a commit that referenced this pull request May 14, 2024
…ble (#2206)

* Avoid using extra background task for TransactionTracker

* Add docs
bkontur pushed a commit that referenced this pull request May 15, 2024
…ble (#2206)

* Avoid using extra background task for TransactionTracker

* Add docs
bkontur pushed a commit that referenced this pull request May 15, 2024
…ble (#2206)

* Avoid using extra background task for TransactionTracker

* Add docs
bkontur pushed a commit that referenced this pull request May 15, 2024
…ble (#2206)

* Avoid using extra background task for TransactionTracker

* Add docs
bkontur pushed a commit that referenced this pull request May 15, 2024
…ble (#2206)

* Avoid using extra background task for TransactionTracker

* Add docs
bkontur pushed a commit that referenced this pull request May 16, 2024
…ble (#2206)

* Avoid using extra background task for TransactionTracker

* Add docs
bkontur pushed a commit that referenced this pull request May 17, 2024
…ble (#2206)

* Avoid using extra background task for TransactionTracker

* Add docs
bkontur pushed a commit that referenced this pull request May 17, 2024
…ble (#2206)

* Avoid using extra background task for TransactionTracker

* Add docs
svyatonik pushed a commit that referenced this pull request May 17, 2024
…ble (#2206)

* Avoid using extra background task for TransactionTracker

* Add docs
bkontur pushed a commit that referenced this pull request May 20, 2024
…ble (#2206)

* Avoid using extra background task for TransactionTracker

* Add docs
bkontur pushed a commit that referenced this pull request May 21, 2024
…ble (#2206)

* Avoid using extra background task for TransactionTracker

* Add docs
bkontur pushed a commit that referenced this pull request May 22, 2024
…ble (#2206)

* Avoid using extra background task for TransactionTracker

* Add docs
bkontur pushed a commit that referenced this pull request May 23, 2024
…ble (#2206)

* Avoid using extra background task for TransactionTracker

* Add docs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Ref docs for safe_defensive_programming
5 participants