Don't Panic!

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].

fn main() {
    /// Do some work
    /// Parameters:
    /// - `useful_amounts`: A vector of u64 values in which there is a important value.
    /// Emits `FoundVal` event when successful.
    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

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 Result types, use ensure!, 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.