-
Notifications
You must be signed in to change notification settings - Fork 680
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
[developer_hub
] Defensive Programming in Substrate Reference Document
#2206
Conversation
If you need more defensive example in the code then search for |
Co-authored-by: Oliver Tale-Yazdi <[email protected]>
Co-authored-by: Oliver Tale-Yazdi <[email protected]>
Co-authored-by: Oliver Tale-Yazdi <[email protected]>
Co-authored-by: Oliver Tale-Yazdi <[email protected]>
There was a problem hiding this 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`] |
There was a problem hiding this comment.
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.
//! 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: |
There was a problem hiding this comment.
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
//! 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: |
There was a problem hiding this comment.
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.
polkadot-sdk/substrate/frame/aura/src/lib.rs
Line 147 in 126f64a
panic!( |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
//! 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`]. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
//! ## 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. |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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
Co-authored-by: Liam Aharon <[email protected]>
Co-authored-by: Juan Girini <[email protected]>
The CI pipeline was cancelled due to failure one of the required jobs. |
@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! |
//! ***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). |
There was a problem hiding this comment.
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
//! ***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). |
//! | ||
//! - Directly using `unwrap()` for a [`Result`] shouldn't be used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//! | |
//! - Directly using `unwrap()` for a [`Result`] shouldn't be used. | |
//! General guidelines: | |
//! | |
//! - Avoid directly using `unwrap()` for a [`Result`]. |
//! 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//! 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. |
//! 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`). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//! 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`). |
//! This traits are useful for catching issues in the development environment, without risking | ||
//! panicking in production. | ||
//! |
There was a problem hiding this comment.
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
//! This traits are useful for catching issues in the development environment, without risking | |
//! panicking in production. | |
//! |
//! 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//! 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. |
//! 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//! 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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//! 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, |
//! | ||
//! 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. |
There was a problem hiding this comment.
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.
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); | ||
} | ||
} |
There was a problem hiding this comment.
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.
Was this automatically closed? strange I thought it would change the base branch to |
@liamaharon seems like it, i'll add your feedback and open a fresh PR to master #soon |
_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]>
_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]>
…ble (#2206) * Avoid using extra background task for TransactionTracker * Add docs
…ble (#2206) * Avoid using extra background task for TransactionTracker * Add docs
…ble (#2206) * Avoid using extra background task for TransactionTracker * Add docs
…ble (#2206) * Avoid using extra background task for TransactionTracker * Add docs
…ble (#2206) * Avoid using extra background task for TransactionTracker * Add docs
…ble (#2206) * Avoid using extra background task for TransactionTracker * Add docs
…ble (#2206) * Avoid using extra background task for TransactionTracker * Add docs
…ble (#2206) * Avoid using extra background task for TransactionTracker * Add docs
…ble (#2206) * Avoid using extra background task for TransactionTracker * Add docs
…ble (#2206) * Avoid using extra background task for TransactionTracker * Add docs
…ble (#2206) * Avoid using extra background task for TransactionTracker * Add docs
…ble (#2206) * Avoid using extra background task for TransactionTracker * Add docs
…ble (#2206) * Avoid using extra background task for TransactionTracker * Add docs
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:
defensive_saturating_add
,defensive_unwrap_or