Panics occur when the node enters a state that it cannot handle and stops the program / process instead of trying to proceed. Panics can occur for a large variety of reasons such as out-of-bounds array access, incorrect data validation, type conversions, and much more. A well-designed Substrate node must NEVER panic! If a node panics, it opens up the possibility for a denial-of-service (DoS) attack.
In the pallet-dont-panic pallet, the find_important_value dispatchable checks to see if useful_amounts[0] is greater than 1_000. If so, it sets the ImportantVal StorageValue to the value held in useful_amounts[0].
/// Do some work
///
/// Parameters:
/// - `useful_amounts`: A vector of u64 values in which there is a important value.
///
/// Emits `FoundVal` event when successful.
#[pallet::weight(10_000)]
pub fn find_important_value(
origin: OriginFor<T>,
useful_amounts: Vec<u64>,
) -> DispatchResultWithPostInfo {
let sender = ensure_signed(origin)?;
ensure!(useful_amounts[0] > 1_000, <Error<T>>::NoImportantValueFound);
// Found the important value
ImportantValue::<T>::put(&useful_amounts[0]);
[...]
}However, notice that there is no check before the array indexing to see whether the length of useful_amounts is greater than zero. Thus, if useful_amounts is empty, the indexing will cause an array out-of-bounds error which will make the node panic. Since the find_important_value function is callable by anyone, an attacker can set useful_amounts to an empty array and spam the network with malicious transactions to launch a DoS attack.
- Write non-throwing Rust code (e.g. prefer returning
Resulttypes, useensure!, etc.). - Proper data validation of all input parameters is crucial to ensure that an unexpected panic does not occur.
- A thorough suite of unit tests should be implemented.
- Fuzz testing (e.g. using
test-fuzz) can aid in exploring more of the input space.