Skip to content

Architecture Overview

System Stack

flowchart TD
    app["Application<br/>(wiring + lifecycle)"]
    http["HTTP Server<br/>(Servant REST API + Swagger UI)"]
    idx["CageFollower<br/>(ChainSync block processing)<br/>unified transaction per block"]
    mpf["MPF Trie<br/>(merkle-patricia-forestry)<br/>Proofs, insertion, deletion"]
    txb["TxBuilder<br/>(MPFS protocol operations)<br/>boot, update, retract, end"]
    bal["Balance<br/>(fee estimation fixpoint)"]
    n2c["Node Client<br/>(node-to-client)<br/>ChainSync + LSQ + LTxS"]

    app --> http
    http --> txb
    http --> idx
    app --> idx --> mpf
    app --> txb --> bal --> n2c
    idx --> n2c

The service connects to a Cardano node via two N2C connections:

  1. ChainSync — blocks are processed by the CageFollower, which applies UTxO, cage state, and trie mutations in a single atomic RocksDB transaction per block (see Block Processing).
  2. LocalStateQuery + LocalTxSubmission — the Provider queries UTxOs and protocol parameters; the Submitter sends signed transactions.

The TxBuilder constructs MPFS protocol transactions (boot, update, retract, end) and balanceTx handles fee estimation through a fixpoint loop.

Singleton Dependency Graph

Every major component is a record of functions (no typeclasses). Records are created bottom-up and torn down top-down using bracket patterns.

graph TD
    APP["Application<br/>(withApplication)"]
    CTX["Context<br/>(facade record)"]
    PRV["Provider<br/>(N2C LocalStateQuery)"]
    TM["TrieManager<br/>(per-token MPF tries)"]
    ST["State<br/>(tokens, requests, checkpoints)"]
    IDX["Indexer<br/>(skeleton / chain sync)"]
    SUB["Submitter<br/>(N2C LocalTxSubmission)"]
    TXB["TxBuilder<br/>(MPFS protocol ops)"]
    NODE["Cardano Node<br/>(Unix socket)"]

    APP --> CTX
    CTX --> PRV
    CTX --> TM
    CTX --> ST
    CTX --> IDX
    CTX --> SUB
    CTX --> TXB
    PRV --> NODE
    SUB --> NODE

Application Wiring

withApplication creates and wires all components:

graph LR
    DB["RocksDB<br/>(11 CFs)"]
    DB --> ST["State<br/>(persistent)"]
    DB --> TM["TrieManager<br/>(persistent)"]
    DB --> CF["CageFollower<br/>(unified txn)"]
    N2C1["N2C #1<br/>(ChainSync)"] --> CF
    N2C2["N2C #2<br/>(LSQ + LTxS)"] --> PRV["Provider"]
    N2C2 --> SUB["Submitter"]
    PRV & SUB & ST & TM --> TXB["TxBuilder"]
    PRV & SUB & ST & TM & TXB --> CTX["Context"]

All components use real implementations backed by RocksDB and N2C connections. The CageFollower runs on connection 1 (ChainSync), processing each block in a single atomic transaction. The Provider, Submitter, and TxBuilder use connection 2 (LSQ + LTxS).

External Dependencies

graph TD
    OFFCHAIN["cardano-mpfs-offchain<br/>Service interfaces + N2C client"]
    MPF["merkle-patricia-forestry<br/>MPF trie library"]
    ONCHAIN["cardano-mpfs-onchain<br/>Aiken validators"]
    LEDGER["cardano-ledger<br/>Conway era types"]
    OUROBOROS["ouroboros-network<br/>N2C protocols"]
    PLUTUS["plutus-core / plutus-tx<br/>PlutusData encoding"]

    OFFCHAIN --> MPF
    OFFCHAIN --> LEDGER
    OFFCHAIN --> OUROBOROS
    OFFCHAIN --> PLUTUS
    OFFCHAIN -.->|"on-chain types<br/>& script hash"| ONCHAIN

    style OFFCHAIN fill:#e1f5fe
    style MPF fill:#e8f5e9
    style ONCHAIN fill:#fff3e0
    style LEDGER fill:#f3e5f5
    style OUROBOROS fill:#f3e5f5
    style PLUTUS fill:#f3e5f5
Color Meaning
Blue This project
Green MPF trie library (same repo)
Orange On-chain Aiken validators (separate repo)
Purple Cardano ecosystem dependencies

Module Hierarchy

The cardano-mpfs-offchain library is organized in layers. All modules live under Cardano.MPFS.

Core — domain types and pure logic

Module Purpose
Core.Types TokenId, Root, Request, TokenState, CageConfig
Core.OnChain Datum/redeemer encodings, cagePolicyId, cageAddress, PlutusV3 script
Core.Blueprint CIP-57 blueprint schema loading and validation
Core.Proof MPF proof to on-chain ProofStep conversion
Core.Balance balanceTx — fee estimation fixpoint loop

Interfaces — record-of-functions singletons

Module Purpose
Context Facade bundling all singletons
Provider queryUTxOs, queryProtocolParams
State Tokens, Requests, Checkpoints sub-records
Trie TrieManager — per-token MPF trie access
TxBuilder Cage protocol operations (boot, request, update, retract, end)
Indexer Chain follower lifecycle (start, stop, getTip)
Submitter submitTx :: Tx ConwayEra -> m SubmitResult
Application withApplication — wiring and lifecycle

HTTP — REST API

Module Purpose
HTTP.API Servant type-level API definition
HTTP.Types JSON wire types (StatusResponse, TokenIdJSON, etc.)
HTTP.Encoding Hex newtype for binary-as-hex transport
HTTP.Server WAI application wiring, mkApp
HTTP.Swagger OpenAPI spec generation, Swagger UI

Indexer — chain sync and persistence

Module Purpose
Indexer.CageFollower Unified rollForward/rollBackwardone block = one transaction
Indexer.Event detectCageEvents — cage tx classification
Indexer.Follower detectCageBlockEvents, applyCageBlockEvents — generic over Monad m
Indexer.Persistent mkTransactionalState (composable) + mkPersistentState (IO)
Indexer.Columns AllColumns + UnifiedColumns GADTs — full DB schema
Indexer.Codecs CBOR serialization for column key-value types
Indexer.Rollback storeRollbackT, rollbackToSlotT — transactional rollback

NodeClient — N2C protocol clients

Module Purpose
NodeClient.Connection runNodeClient — multiplexed N2C connection
NodeClient.LocalStateQuery LSQ protocol client for UTxOs and PParams
NodeClient.LocalTxSubmission Tx submission protocol client
NodeClient.Codecs N2C codec bundle
NodeClient.Types LSQChannel, LTxSChannel

TxBuilder — cage protocol transactions

Module Purpose
TxBuilder.Config CageConfig loading
TxBuilder.Real mkRealTxBuilder entry point
TxBuilder.Real.Boot Mint cage token
TxBuilder.Real.Request Submit insert/delete request
TxBuilder.Real.Update Consume requests, update root
TxBuilder.Real.Retract Cancel pending request
TxBuilder.Real.End Burn cage token
TxBuilder.Real.Internal Shared helpers, POSIX-to-slot conversion

Trie — MPF backends

Module Purpose
Trie.Persistent mkUnifiedTrieManager (transactional) + mkPersistentTrieManager (IO with caches)
Trie.PureManager mkPureTrieManager — in-memory TrieManager for tests

Mock — test doubles

Module Purpose
Mock.Context withMockContext — full mock wiring
Mock.Provider In-memory UTxO store
Mock.State mkMockStateIORef-backed state
Mock.Submitter Always-succeeds submitter
Mock.TxBuilder mkMockTxBuilder — placeholder builder
Mock.Indexer No-op indexer
Mock.Skeleton mkSkeletonIndexer — lifecycle-only skeleton

Utilities

Module Purpose
Trace AppTrace structured tracing type, jsonLinesTracer for stderr JSON-lines logging

Design Principles

  • No typeclasses — closed world with explicit records of functions.
  • All types from cardano-ledgerTx ConwayEra, PParams ConwayEra, Addr, TxIn, etc.
  • Visible dependency graph — no implicit resolution surprises.
  • Trivial testing — swap the record for a mock backend.
  • No orphan instances.

Implementation Phases

graph LR
    P0["Phase 0<br/>MPF Library ✓"]
    P1["Phase 1<br/>Service Interfaces ✓"]
    P2["Phase 2<br/>N2C Client +<br/>Provider ✓"]
    P3["Phase 3<br/>Transaction<br/>Builders"]
    P4["Phase 4<br/>ChainSync Indexer +<br/>Persistent State"]
    P5["Phase 5<br/>HTTP API +<br/>Deployment"]

    P0 --> P1 --> P2 --> P3 --> P4 --> P5

    style P0 fill:#2d6,color:#fff
    style P1 fill:#2d6,color:#fff
    style P2 fill:#2d6,color:#fff
    style P3 fill:#2d6,color:#fff
    style P4 fill:#2d6,color:#fff
    style P5 fill:#2d6,color:#fff
Phase Description Status
0 MPF library — 16-ary Merkle Patricia Forestry, Blake2b-256 hashing, insertion/deletion/proofs, pure and RocksDB backends Done
1 Service interfaces — Provider, Submitter, TxBuilder, State, Indexer, TrieManager, Context records; mock implementations; balanceTx with fixpoint fee estimation; on-chain type encodings; CIP-57 blueprint validation; Aiken-compatible proof serialization Done
2 N2C client + Provider — ouroboros-network LocalStateQuery and LocalTxSubmission clients; mkNodeClientProvider for UTxO and PParams queries; mkN2CSubmitter for transaction submission; E2E tests with cardano-node subprocess Done
3 Transaction builders — real TxBuilder implementations for boot, update, retract, end operations with Plutus script witnesses, proof embedding, and on-chain datum construction Done
4 ChainSync indexer + persistent state — replace skeleton indexer with real ChainSync follower; RocksDB-backed State and TrieManager; block processing with rollback support Done
5 HTTP API + deployment — Servant HTTP layer with Swagger UI, token lifecycle and trie query endpoints, transaction building and submission REST API, WAI application wiring Done