Nimbus is a framework for building parachain consensus systems on cumulus-based parachains.

Overview

Cumulo -- Nimbus ⛈️

Nimbus is a framework for building parachain consensus systems on cumulus-based parachains.

Given the regular six-second pulse-like nature of the relay chain, it is natural to think about slot- based consensus algorithms for parachains. The parachain network is responsible for liveness and decetralization and the relay chain is responsible for finality. There is a rich design space for such algorithms, yet some tasks are common to all (or most) of them. These common tasks include:

  • Signing and signature checking blocks
  • Injecting authorship information into the parachain
  • Block authorship and import accounting
  • Filtering a large (potentially unbounded) set of potential authors to a smaller (but still potentially unbounded) set.
  • Detecting when it is your turn to author an skipping other slots

Nimbus aims to provide standard implementations for the logistical parts of such consensus engines, along with helpful traits for implementing the parts that researchers and developers want to customize.

Try the Demo

While Nimbus is primarily a development framework meant to be included in other projects, it is useful to see a basic network in action. An example network is included in the polkadot-parachains example collator. You can build it with cargo build --release and launch it like any other cumulus parachian. Make sure to specify --chain nimbus.

Rather than reiterate how to start a relay-para network here, I'll simply recommend you use the excellent Polkadot Launch tool. This repo was tested with version 1.4.1. A lauch config file is provided.

# Install polkadot launch (I used v1.4.1)
npm i -g polkadot-launch

# Build polkadot (I used 82aa404c; check Cargo.lock to be sure)
cd polkadot
cargo build --release
cd ..

# Build Polkadot-parachains example collator
cd cumulus
git checkout nimbus
cargo build --release

# Launch the multi-chain
polkdot-launch ./nimbus-launch-config.json

To learn more about launching relay-para networks, check out the cumulus workshop.

Design Overview

If you want to start using Nimbus in your project, it is worth reading this.

At its core nimbus is a consensus engine that considers blocks valid if and only if they inject the author's public identity into the runtime, and seal the block with a signature by the author's private key.

Compared to most consensus engines, this is very permissive -- anyone who can create a signature can author valid blocks. In order to build more useful and familiar consensus engine on this foundation, nimbus provides a framework for creating filters to further restrict the set of eligible authors. These filters live inside the runtime.

Being general in the consensus layer and deferring most checks to the runtime is the key to nimbus's re-usability as a framework. And is the reason that writing a consensus engine is as easy as writing a pallet when you use nimbus.

Author Inherent

The Author inherent pallet allows block authors to insert their identity into the runtime. This feature alone is useful in many blockchains and can be used for things like block rewards.

The author inherent provides a validation hook called CanAuthor. This check will be called during the inherent execution and is the main entry point to nimbus's author filters. If you don't want to restrict authorship at all, you can just use ().

As a concrete example, in a simple Proof of Stake system this check will determine whether the author is staked. In a more realistic PoS system the CanAuthor check might first make sure the author is staked, and then make sure they are eligible in this slot according to round robin rules.

Finally, the pallet copies the authorship information into a consensus digest that will stick around in the block header. This digest can be used by UIs to display the author, and also by the consensus engine to verify the block authorship.

PreRuntimeDigest I believe the design should be changed slightly to use a preruntime digest rather than an inherent for a few reasons:

  • The data wouldn't be duplicated between an inherent and a digest.
  • Nimbus client-side worker would support non-frame runtimes.
  • That's how sc-consensus-aura does it.

Author Filters

A primary job of a consensus engine is deciding who can author each block. Some may have a static set, others may rotate the set each era, others may elect an always-changing subset of all potential authors. There is much space for creativity, research, and design, and Nimbus strives to provide a flexible interface for this creative work. You can express all the interesting parts of your consensus engine simply by creating filters that implement the CanAuthor trait. The rest of Nimubs will #JustWork for you.

This repository comes with a few example filters already, and additional examples are welcome. The examples are:

  • PseudoRandom FixedSized Subset - This filter takes a finite set (eg a staked set) and filters it down to a pseudo-random subset at each height. The eligible ratio is configurable in the pallet. This is a good learning example.
  • Aura - The authority round consensus engine is popular in the Substrate ecosystem because it was one of the first (and simplest!) engines implemented in Substrate. Aura can be expressed in the Nimbus filter framework and is included as an example filter. If you are considering using aura, that crate has good documentation on how it differs from sc-consensus-aura.
  • (Planned) FixedSizedSubset - The author submits a VRF output that has to be below a threshold to be able to author.
  • (Planed) Filter Combinator - A filter that wraps two other filters. It uses one in even slots and the other in odd slots.

Author Filter Runtime API

Nimbus makes the design choice to include the author checking logic in the runtime. This is in contrast to the existing implementations of Aura and Babe where the authorship checks are offchain.

While moving the check in-runtime, provides a lot of flexibility, and simplifies interfacing with relay-chain validators, it makes it impossible for authoring nodes to predict whether they will be eligible without calling into the runtime. To achieve this, we provide a runtime API that makes the minimal calculation necessary to determine whether a specified author will be eligible at the specified slot.

Nimbus Consensus Worker

Nimbus consensus is the primary client-side consensus worker. It implements the ParachainConsensus trait introduced to cumulus in https://github.com/paritytech/cumulus/pull/329. It is not likely that you will need to change this code directly to implement your engine as it is entirely abstracted over the filters you use. The consensus engine performs these tasks:

  • Slot prediction - it calls the runtime API mentioned previously to determine whether ti is eligible. If not, it returns early.
  • Authorship - It calls into a standard Substrate proposer to construct a block (probably including the author inherent).
  • Self import - it imports the block that the proposer created (called the pre-block) into the node's local database.
  • Sealing - It adds a seal digest to the block - This is what is used by other nodes to verify the authorship information.

Verifier and Import Queue

For a parachain node to import a sealed block authored by one of its peers, it needs to first check that the signature is valid by the author that was injected into the runtime. This is the job of the verifier. It will remove the nimbus seal and check it against the nimbus consensus digest from the runtime. If that process fails, the block is immediately thrown away before the expensive execution even begins. If it succeeds, then the pre-block (the part that's left after the seal is stripped) is passed into the import pipeline for processing and execution. Finally, the locally produced result is compared to the result received across the network.

Custom Block Executor

We've already discussed how parachain nodes (both the one that authors a block, and also its peers) import blocks. In a standalone blockchain, that's the end of the story. But for a parachain, we also need our relay chain validators to re-execute and validate the parachain block. Validators do this in a unique way, and entirely in wasm. Providing the validate_block function that the validators use is the job of the register_validate_block! macro from Cumulus.

Typically a cumulus runtime invokes that macro like this:

cumulus_pallet_parachain_system::register_validate_block!(Runtime, Executive);

You can see that the validators use the exact same executive that the parachain nodes do. Now that we have sealed blocks, that must change. The validators need to strip and verify the seal, and re-execute the pre-block just like the parachain nodes did. And without access to an offchain verifier, they must do this all in the runtime. For that purpose, we provide and alternate executive which wraps the normal FRAME executive. The wrapper strips and checks the seal, just like the verifier did, and then passes the pre-block to the inner FRAME executive for re-execution.

Write Your Own Consensus Logic

If you have an idea for a new slot-based parachain consensus algorithm, Nimbus is a quick way to get it working! The fastest way to start hacking is to fork this repo and customize the template node.

If you'd rather dive in than read one more sentence, then start hacking in the author-slot-filter pallet.

In most cases, you can use all the off-the-shelf components and simply write your filters. It is also possible to compose existing filters to build more complex logic from smaller pieces.

Authoring and Import Diagrams

One node authors the block, then it is processed in three different ways.

Author Parachain Peer Relay Validator
Predict Eligibility
Author Block
Runs Verifier
Import Pipeline
Custom Pre exec
Normal FRAME exec

Roadmap

The Nimbus framework is intended to be loosely coupled with Cumulus. It remains to be seen whether it should live with Cumulus or in its own repository.

Next tasks

  • Proper trait for interacting with digests
  • More example filters
  • Share code between verifier and wrapper executive
  • Client-side worker for standalone (non para) blockchain
  • Aurand as an example of composing filters
  • Second filter trait for exhaustive sets (As opposed to current propositional approach)

Contributions Welcome

Try it out, open issues, submit PRs, review code. Whether you like to tinker with a running node, or analyze security from an academic perspective, your contributions are welcome.

I am happy to support users who want to use nimbus, or want feedback on their consensus engines.

Comments
  • update to polkadot v0.9.24

    update to polkadot v0.9.24

    We are using nimbus in our hackathon project and we must use the latest polkadot version. is it possible to get a v0.9.24 branch or should we do it ourselves? :)

    opened by DappGuy 7
  • v0.9.19 update

    v0.9.19 update

    This is a merge back into main of the relevant changes of the v0.9.19 dependency update.

    Currently, it is just a clone of the moonbeam-polkadot-v0.9.19 branch but can diverge later (e.g. to restore the upstream branches).

    We (I) failed to do this with the v0.9.18 update, so this includes both .18 and .19.

    opened by notlesh 5
  • Use little endian instead of big endian for index bytes (author slot filter fix)

    Use little endian instead of big endian for index bytes (author slot filter fix)

    This fixes the bug we saw wherein the same randomness was produced for every iteration.

    The fix changes it from using big endian to little endian. We want to sample the least significant bits because the most significant bits do not change in every iteration

    After this PR, we use the first 2 bytes of index.to_le_bytes() and the first 4 bytes of seed.to_be_bytes()

    The bug was introduced in https://github.com/PureStake/nimbus/pull/76

    opened by 4meta5 4
  • Move note author to inherent so it comes after ParachainStaking::on_initialize

    Move note author to inherent so it comes after ParachainStaking::on_initialize

    assumption is that inherents come after on_initialize and before extrinsics. If this is wrong, ignore this PR.

    Otherwise this change ensures that the note_author call happens after ParachainStaking::on_initialize

    opened by 4meta5 4
  • time-based slots and non-parachain support

    time-based slots and non-parachain support

    Solves #3

    This PR (currently WIP) adds an implementation of Substrates SlotWorker trait that works with Nimbus.

    This will allow standalone blockchains (not parachains) to use nimbus in production.

    This PR will also add an example of using nimbus in this way in the template node.

    opened by JoshOrndorff 4
  • Ensure slot number is strictly increasing

    Ensure slot number is strictly increasing

    This PR adds a simple storage item and validation check that ensures the slot number associated with each block is always strictly greater than the previous highest slot number in this chain.

    Nimbus has always assumed this invariant to be true, but has not enforced it. In the parachain context, we are guaranteed increasing slot numbers because we use the relay chain parent number which is always strictly increasing. But in other contexts, nimbus will need to ensure this manually. (Plus nimbus should ensure it manually anyways in case the design of cumulus ever changes without warning.)

    This works toward #1 and #3

    opened by JoshOrndorff 4
  • Remove EventHandler trait and expose block author directly

    Remove EventHandler trait and expose block author directly

    We made a decision to move note_author into on_finalize in staking and removing it from nimbus altogether.

    Read the associated moonbeam PR 1701 which mentions this PR below (for the link)

    opened by 4meta5 2
  • Add additional verifiers

    Add additional verifiers

    to verify VRF upon block import in https://github.com/PureStake/moonbeam/pull/1376

    same design as additional_digests_provider in previous PRs

    This change ensures verification upon block import. The BlockExecutor ensures verification by relay chain validators for the parachain block.

    opened by 4meta5 2
  • Bring cumulus to latest master commit

    Bring cumulus to latest master commit

    Brings master to latest cumulus commit. First I merged our moonbeam-polkadot-v0.9.19 branch and then I updated cargo tomls and locks.

    I need the latest commit to bring https://github.com/paritytech/cumulus/commit/68b4da78d079e110cd616b937ed2de738d32a2bf

    opened by girazoki 2
  • Stop providing author inherent data

    Stop providing author inherent data

    DO NOT MERGE until after nimbus 0.9 has been tagged.

    This PR is a followup to #15. It simplifies the client-side code by no longer providing any real inherent data. Previously it provided the author id, but since #15, the data supplied there has not been used, and the pre-runtime digest is used instead.

    opened by JoshOrndorff 2
  • Improve (or remove?) `EventHandler`

    Improve (or remove?) `EventHandler`

    After reading the authorship info from the pre-runtime digest, the nimbus system (fka pallet-author-inherent) notifies other subscribers (probably the staking system, etc) of the author. There are two main shortcomings of the current approach:

    1. It doesn't account for weight consumed by the code being notified.
    2. It introduces a custom trait that is very similar to https://crates.parity.io/pallet_authorship/trait.EventHandler.html

    Ideally, we could share code with Substrate for the common task of authorship, and I've made a proposal in https://github.com/paritytech/substrate/issues/10437, and if Parity likes it, that is the preferred way to go. Solution candidates would be:

    1. Follow the proposal I linked.
    2. Update our trait to return weight.
    3. Remove the notification system entirely, and make other pallets query for the author using FindAuthor
    opened by JoshOrndorff 2
  • [pallet_author_inherent] `kick_off_authorship_validation` benchmark fails

    [pallet_author_inherent] `kick_off_authorship_validation` benchmark fails

    It fails here. The T::SlotBeacon::slot() seems to return zero, although it was set in the benchmarks here.

    set_block_number has an empty default implementation here. So I assume the problem here is, that set_block_number was not properly implemented on the moonbeam nimbus side.

    This is useful in case the block number provider is different than System

    So the fix is to implement set_block_number for BlockNumberProvider:

    impl BlockNumberProvider {
          type BlockNumber = u32;
    
          fn current_block_number() -> Self {
    	      frame_system::Pallet<Config>::block_number()
          }
    
          #[cfg(features = "runtime-benchmarks")]
          fn set_block_number(slot: u32) -> Self {
    	      frame_system::Pallet<Config>::set_block_number(slot);
          }
    }
    
    opened by Chralt98 0
  • Updating Nimbus SessionKey is ignored. Client continues proposing new blocks with the old key on runtime upgrade

    Updating Nimbus SessionKey is ignored. Client continues proposing new blocks with the old key on runtime upgrade

    I'm seeing that after calling setKeys to update the node's Nimbus key, the node continues proposing with the old SessionKey.

    Our implementation of AccountLookup uses pallet_session::key_owner to map NimbusId -> AccountId, so the Nimbus key must match what's set in pallet_session::queuedKeys for the node to produce valid blocks.

    Because of this, our polkadot-launch --chain parachain-local nodes can not start block production after a chain upgrade that enables nimbus on a chain that didn't use it before

    Expected Results

    The node queries the current nimbus key from the runtime and uses that to author

    Analysis

    The fn the client uses to build the digest is nimbus_consensus::seal_header nimbus-consensus/src/lib.rs which calls SyncCryptoStore::sign_with

    Rustdocs of sign_with state

    Given a list of public keys, find the first supported key and sign the provided message with that key.

    Now the problem is that the two ways to change your node's session keys - author_rotateKeys and author_insertKey both internally call SessionKeys::generate_session_keys which adds the new keys to the Crypto store, but does NOT remove the old keys.

    So after running one of the above, there will be multiple keys in store matching the nmbs session key type and Nimbus Consensus implicitly picks the first one to propose blocks, which then get rejected by the runtime as they don't match the key set in pallet_session::queuedKeys, preventing the node from producing blocks.

    Workaround

    1. Stop the node
    2. Find the keystore folder and manually delete all old nimbus key that don't match what you have provided to the most recent call of setKeys. Nimbus keys start with 6e6d6273 ( nmbs in hex ) and have the public key after that.
    3. Restart the node

    This is extra problematic on polkadot-launch --chain something-local deployments with the --alice etc. nodes as their well-known keys are not held in the keystore on the filesystem and thus can not be deleted.

    opened by Garandor 7
  • Rename `pallet_author_inherent` to `pallet_nimbus_system`

    Rename `pallet_author_inherent` to `pallet_nimbus_system`

    We're transitioning toward preruntime digests in #15 and plan to drop support for the inherent entirely in #23.

    Therefore, the pallet name no longer makes sense.

    opened by JoshOrndorff 0
  • Remove support for Author Inherent

    Remove support for Author Inherent

    In #15 we added support for injecting author information via a pre-runtime digest. We also maintained support for the older author inherent to allow live chains to migrate. This ticket is to remove support for the older author inherent, and leave only the newer preruntime digest.

    opened by JoshOrndorff 0
  • Consider equivocation detection

    Consider equivocation detection

    For sovereign chains hoping to use proof of stake, equivocation detection is necessary so validators who work on multiple chains can be slashed. Current nimbus does not have any such detection. Hopefully copying it from Aura will be straightforward, but I haven't looked into it.

    In any case, equivocation detection is not necessary for manual seal or relay chain.

    opened by JoshOrndorff 4
Releases(v0.9.0)
  • v0.9.0(Dec 14, 2021)

    This is planned to be the last pre-1.0.0 release of nimbus. This release brings two main changes as well as several code quality improvements and minor updates.

    • https://github.com/PureStake/nimbus/pull/15
    • https://github.com/PureStake/nimbus/pull/21
    Source code(tar.gz)
    Source code(zip)
  • v0.8.0(Nov 18, 2021)

    First nimbus release. This basically represents the nimbus code base as it was when it was extracted from the cumulus repository with just a few cleanups. Future releases will make nimbus better stand on its own and have better notes.

    For some history of nimbus and its life in the cumulus repo see: https://github.com/paritytech/cumulus/pull/429

    Source code(tar.gz)
    Source code(zip)
Owner
null
Buildomat manages the provisioning of ephemeral UNIX systems on which to run software builds

B U I L D O M A T a software build labour-saving device Buildomat manages the provisioning of ephemeral UNIX systems (e.g., instances in AWS EC2) on w

Oxide Computer Company 33 Dec 4, 2022
QueingSimulator is an application that can be used to build intuitions about behavior of synchronous request/reply systems

Queueing Simulator QueingSimulator is an application that can be used to build intuitions about behavior of synchronous request/reply systems (such as

Joe Magerramov 7 Sep 11, 2022
`fugit` provides a comprehensive library of `Duration` and `Instant` for the handling of time in embedded systems, doing all it can at compile time.

fugit fugit provides a comprehensive library of Duration and Instant for the handling of time in embedded systems, doing all it can at compile time. T

Emil Fresk 40 Oct 2, 2022
Amethyst is a systems language aimed at being simple, small, portable, and safe.

amethyst Amethyst is a systems language aimed at being simple, small, portable, and safe. What is this language? From the r/ProgLangs discord server:

Amethyst Language 34 Dec 18, 2022
🐀 Building a federated alternative to reddit in rust

Lemmy A link aggregator / Reddit clone for the fediverse. Join Lemmy · Documentation · Report Bug · Request Feature · Releases · Code of Conduct About

LemmyNet 7.2k Jan 3, 2023
Cassette A simple, single-future, non-blocking executor intended for building state machines.

Cassette A simple, single-future, non-blocking executor intended for building state machines. Designed to be no-std and embedded friendly. This execut

James Munns 50 Jan 2, 2023
🐀 Building a federated link aggregator in rust

English | Español | Русский Lemmy A link aggregator / Reddit clone for the fediverse. Join Lemmy · Documentation · Report Bug · Request Feature · Rele

LemmyNet 7.2k Jan 2, 2023
An aimless attempt at building a PC from scratch, in a vaguely eurorack/modular synth style.

An aimless attempt at building a PC from scratch, in a vaguely eurorack/modular synth style.

James Munns 66 Nov 30, 2022
Building a better screen reader for the Linux desktop, one step at a time.

Building a better screen reader for the Linux desktop, one step at a time.

Odilia 44 Dec 31, 2022
The 峨眉 (EMei) JIT/AOT backend framework.

emei The 峨眉 (EMei) JIT/AOT backend framework. Support Instructions x86_64 mov mov mov_zero_extend_bit8/16 mov_sign_extend_bit8/16/32 mov_rev movs(is m

Lyzh 14 Apr 11, 2022
OptFrame is an optimization framework focused in metaheuristic techniques

optframe-rust Welcome to OptFrame project in Rust. What is OptFrame? OptFrame is an optimization framework focused in metaheuristic techniques, develo

OptFrame 4 Jan 30, 2022
Elegant, clean Rust development framework

Preview version, will not guarantee the stability of the API! Elegant, clean Rust development framework Core Features Relational database client for M

Ideal World 35 Dec 29, 2022
BlackBird is a framework for Rust Tokio

BlackBird Blackbird is framework like OTP for Erlang. Blackbird is not a Actor Framewrok, it's Behavior for around Tokio task This project currently p

DanyalMh 25 Dec 15, 2022
Moonshine CSS - 🥃 High-proof atomic CSS framework

Moonshine CSS - ?? High-proof atomic CSS framework

Econify 25 Nov 25, 2022
RustHunter is a modular incident response framework to build and compare environmental baselines

RustHunter is a modular incident response framework to build and compare environmental baselines. It is written in Rust and uses Ansible to collect data across multiple hosts.

Giovanni Pecoraro 13 Dec 12, 2022
Blazingly fast spam classification API built using Rocket Web Framework.

Telegram Antispam API Blazingly fast spam classification API built using Rocket Web Framework. Notes The classifier works in aggressive mode, it can s

Akshay Rajput 13 May 5, 2023
simple lottery maker made with rust, just web framework

Toy project for fun It's just for fun! making plausible lottery numbers for Korea made with rust This lottery web framework generates and presents pla

yacho (bakjuna) 4 Nov 24, 2023
⚡rustygram is a minimal and blazing fast telegram notification framework for Rust

⚡rustygram ⚡rustygram is a minimal and blazing fast telegram notification framework using Rust. Abstracts away the Telegram API complexity so your app

Chia Yong Kang 15 Dec 1, 2023
dm-jitaux is a Rust-based JIT compiler using modified auxtools, dmasm and Inkwell LLVM wrapper for boosting Byond DM performance without any hassle!

dm-jitaux is a Rust-based JIT compiler using modified auxtools, dmasm and Inkwell LLVM wrapper for boosting Byond DM performance without any hassle (such as rewriting/refactroing your DM code).

SS220 20 Dec 13, 2022