Introduction

This document is an exploration on the cardano-node architecture and how it could be made more modular, approachable and flexible.

Why should we care

The Cardano Node was developed over the last 8+ years to become the reference implementation of the Ouroboros consensus protocols, extended UTxO (eUTxO) ledger model and plutus smart contract language at Input Output Group (IOG, or just IO).

While based on peer-reviewed research and significant engineering efforts in ensuring correctness through formal methods with extensive testing, the codebase is largely opaque for non-IO Cardano developers and definitely unused outside of the Cardano ecosystem. For example, it is concerning that even IO's sister projects to Cardano like Midnight and Partner-Chains were reaching to other frameworks to build their blockchain components, despite building sidechains of Cardano.

While there will always be demand for "node diversity" in a public blockchain and multiple teams using their favorite tools and languages to build them, we must not shy away of making the ways the Cardano node is built more approachable to a wider developer community. This will not only improve development capacity, but also enable maintenance to outlive the current company & team structures. Furthermore, if we could make the components of the Cardano node be re-used more flexibly in diverse scenarios, this would lead to a rich feature-set and at the same time counter-act natural fragmentation to a healthy balance.

After all, software quality is not only about rigor and testing, but also "how easy a system can be changed".

Node architecture

While some documentation for users and developers can be found, the available documents about the "inner workings" of the Cardano node is scarce.

The main repository is cardano-node which integrates the several components. The linked repository do contain individual Haskell package dependency diagrams and bigger technical specification documents, but generally it's quite hard to read about how the various components interact with each other.

The lack of an easy accessible and clear visual breakdown of the individual responsibilities of a cardano-node and how they could be re-used is maybe already a hint to why external contributors are seeing it as non-inviting to be re-used or extended. In fact, the rotating release engineers at IO even needed to come up with dependency diagrams on their own to get an overview (for example Yura and his mermaid diagram).

For this document, the following diagram of relevant components and their relations will make do:

Todo

Introduce relevant components sufficient for remainder of document

Plutus

  • Plutus Intermediate Representation (IR) and Plutus Core syntax
  • Plutus Core interpreter
  • Cost model estimation
  • plutus-tx / plinth is separate, compiler to build plutus core

Ledger

  • Validates transactions according to ledger rules
  • Updates stake distribution
  • Keeps reward accounts
  • Governance features?
  • Could host multiple languages (not only plutus)!

Consensus

  • Ledger eras (here? or above)
  • Chain selection
  • Hard-fork combinator
  • Multiple consensus protocols that determine which blocks are valid and who to mint them
  • Currently: Praos, TPraos, PBFT (still?)

Network

  • Node-to-node
    • BlockFetch
    • ChainSync
    • TxSubmission
  • Node-to-client
    • Local variants of chain sync and tx submission
    • State Query
    • Mempool monitor

About alternatives

Modularization and clear interfaces are only enforced when there are alternatives. That is, alternative implementations of components or alternative compositions of components.

Large parts of the Cardano codebase created by IO are optimized for a single target use case: producing blocks for the public Cardano network. While this is certainly the main use case that requires special care and tuning of performance characteristics to meet the timing demands of the consensus protocols, it currently does not leave much room for experimentation and re-use outside of that use case.

In the past, one could use the cardano-node to create permissioned networks more suitable for enterprise deployments. This was basically done by setting up Ouroboros-BFT consensus networks (through a transitional protocol named TPraos, e.g. in Alonzo), but since Babbage the parameters allowing for operating in that mode had been removed (CIP-55) and in Praos this is not possible anymore. Making this alternative use case more prominent could have naturally ensured individual components are modular enough for being used in this alternative composition, demonstrated the flexibility, and consequently could have made setting up side chains (e.g. partner chains and midnight) using the cardano codebase more appealing.

A good example of an alternative composition that enforces a well defined interface on the cardano-ledger is the Hydra project. In this case, the exact same ledger rules are re-used in a completely different application (that allows for off-chain processing of cardano transactions) and any "corruption" of the applyTx interface used here would be detected from this separate usage.

Arguably, the ledger is easiest to re-use component in the Cardano stack as it is "only" a pure function from evolving LedgerState, roughly: applyTx :: LedgerState -> Tx -> LedgerState. However, it is quite directly used by the Consensus layer and it is not clear (at least from the outside) that alternative ledgers could be used? Intuitively this should be possible, but ledger eras are also defined in the consensus layer and would it be straight-forward or complicated to establish alternative eras with, for example, an account-based ledger? Such an alternative implementation of cardano-ledger would not only demonstrate the flexibility and re-usability of the hosting consensus layer, but also ensure that the ledger abstraction does not leak into the consensus.

In summary, for each component to be re-usable we should maintain and demonstrate alternatives. Note that we often do have such alternatives already in tests and more often than not this is just a discoverability or communication issue.

Ideas

Driving forces that could improve the situation of the Cardano node architecture.

Interface first

  • Conformance tests
  • CIPs?
  • Open standards / avoid not invented here / off-the-shelf especially close to the user
    • Node-to-client niche way to implement an API
    • Many components built by the community to work around the limitations
    • Would have happened anyways, but maybe some could have been avoided?
    • Example: ogmios, was started out of frustration about unusability of n2c, now de-facto standard sidecar to a cardano-node
      • many workarounds of arcane / hard-to use n2c quirks
      • transaction submission / backward compatibility of transactions
      • fee estimation
      • test vectors
    • Missing documentation requires many projects to fill in the gaps and even reverse-engineer the haskell code, e.g. https://arsmagna.xyz/docs/network-lsq/
    • mention utxo-grpc?

Is Haskell a deterrent?

  • Current teams / components are not a "bad cut" per se

  • Without external contributions, tight (process) coupling ensues

  • Feature teams concerning about one aspect across components?

  • Competing implementations was already tried in the past

    • Rust vs. Haskell -> Jormungandr vs. cardano-node
  • In-language re-use vs. language-agnostic re-use

    • foreign function interfaces
    • process boundaries
    • WebAssembly based frameworks (hermes)

Case study: mithril

  • Evolution from userspace to kernel space
  • How can experiments and new ideas transcend into "the node" eventually?
  • Mithril completely separate -> Mithril side-car / network re-use -> Signer part of node -> Use signed data in node (for consensus)
  • Modular decentralized message queue (DMQ) node architecture we built

Why substrate?

  • Why were partner chains and midnight have been resorting to use substrate?
  • How is substrate doing it (people seem to like to use their framework)? https://substrate.io/
  • In the past: Advertised as a framework where polkadot node is just one way of combining components (pellets)
  • However, just recently substrate moved even now more into a definite Polkadot SDK?
    • Making it less a re-usable framework and already less appealling now?

Test

We can use mermaid diagrams:

graph LR;
    A-->B-->D;
    A-->C;

and math: