Architecture Overview
Architecture Overview
UltraDAG is a lightweight DAG-BFT cryptocurrency designed from first principles for IoT and machine-to-machine micropayments. This page describes the system architecture, design philosophy, and how the major components fit together.
Design Philosophy
Three principles guide every design decision in UltraDAG:
-
Simplicity — Every component must justify its complexity. The entire node compiles to a sub-2 MB binary. No virtual machine, no smart contracts, no account abstraction layers. Just a DAG-based ledger optimized for value transfer.
-
Community-first governance — The Council of 21 uses one-vote-per-seat governance with no stake requirement. Technical, business, legal, academic, and community seats ensure diverse representation. Stake does not buy governance power.
-
Real decentralization — The node runs on a $5/month VPS. A Raspberry Pi can be a full validator. If only well-funded entities can run nodes, the network is not decentralized. UltraDAG makes participation economically accessible.
System Architecture
The system is composed of five crates: the ultradag-node binary (CLI and RPC server), ultradag-network (Noise transport, DAG sync, message routing), ultradag-coin (BlockDag, FinalityTracker, StateEngine, Mempool, ValidatorSet), ultradag-sim (VirtualNetwork and test scenarios), and ultradag-sdk (Rust SDK client). The RPC server connects to the DAG, state engine, and mempool. The network transport feeds into DAG sync and gossip, which connect to the BlockDag and mempool. The DAG feeds into finality, which feeds into the state engine, which manages the validator set.
Crate Structure
UltraDAG is organized as a Rust workspace with five crates:
| Crate | Type | Purpose |
|---|---|---|
ultradag-coin | Library | Consensus engine, DAG, finality, state machine, tokenomics, governance |
ultradag-network | Library | P2P transport, Noise encryption, DAG sync protocol, rate limiting |
ultradag-node | Binary | Node entry point, CLI parsing, RPC server, orchestration |
ultradag-sim | Library + Tests | Deterministic simulation harness, fault injection, invariant checking |
ultradag-sdk | Library | Rust SDK for programmatic node interaction |
Dependency Graph
ultradag-node
├── ultradag-coin
├── ultradag-network
│ └── ultradag-coin
└── ultradag-sdk (optional)
ultradag-sim
├── ultradag-coin
└── (no network dependency — uses VirtualNetwork)
The simulation crate deliberately avoids depending on ultradag-network. It replaces TCP with a VirtualNetwork abstraction that supports perfect delivery, random ordering, message drops, and network partitions — all deterministically seeded.
Core Data Flow
The fundamental data flow through an UltraDAG node:
- Vertex production: The local validator creates a
DagVertexreferencing parents from the current DAG tip - Gossip: The vertex is broadcast to all connected peers via the P2P layer
- DAG insertion: Received vertices are validated and inserted into the
BlockDag - Finality check: The
FinalityTrackerchecks if any vertices have achieved >2/3 validator coverage - State application: Newly finalized vertices are ordered deterministically and applied to the
StateEngine - Persistence: Updated state is written to the
redbdatabase
The data flows sequentially: the Validator produces a vertex into the BlockDag, the FinalityTracker checks for finality, finalized vertices are passed to the StateEngine for application, and the StateEngine persists state to the redb database.
Key Types
DagVertex
The fundamental unit of the DAG. Each vertex contains:
validator: Address: address of the producing validatorpub_key: [u8; 32]: Ed25519 public key of the producing validatorround: u64: monotonically increasing round numberparent_hashes: Vec<[u8; 32]>: parent vertex hashes (up toMAX_PARENTS=64)block: Block: contains the coinbase transaction and a list of user transactionssignature: Signature: Ed25519 signature over the vertex contenttopo_level: u64: topological level (computed on insert, not persisted)
The vertex hash is not stored as a field — it is computed via DagVertex::hash() using Blake3.
BlockDag
The in-memory DAG structure. Holds vertices from the current window (after pruning). Provides:
- Vertex insertion with validation (signature, parent existence, round monotonicity)
- Parent selection for new vertex production
- Pruning of vertices below the pruning horizon
FinalityTracker
Determines when vertices achieve BFT finality. Uses BitVec for O(1) per-vertex coverage tracking. A vertex is finalized when more than 2/3 of known validators (by count, not stake) have it as an ancestor. The BFT threshold is ceil(2n/3) by validator count.
StateEngine
Derives account state from finalized DAG vertices. Maintains:
- Account balances and nonces
- Staking accounts (amount, commission)
- Delegation accounts (amount, validator, unlock round)
- Governance state (proposals, votes, council)
- Supply tracking with invariant enforcement
ValidatorSet
Tracks the active and pending validator sets. Recalculated at epoch boundaries (every 210,000 rounds). Top 21 by effective stake become active.
Workspace Layout
ultradag/
├── crates/
│ ├── ultradag-coin/ # Consensus, state, tokenomics
│ │ ├── src/
│ │ │ ├── address/ # Address type, derivation
│ │ │ ├── block/ # Block, BlockHeader, merkle root
│ │ │ ├── block_producer/ # Block/vertex creation
│ │ │ ├── consensus/ # dag.rs, finality.rs, vertex.rs, ordering.rs, checkpoint.rs, epoch.rs, validator_set.rs, persistence.rs
│ │ │ ├── governance/ # Council, proposals, voting, params
│ │ │ ├── persistence/ # redb persistence (db.rs)
│ │ │ ├── state/ # StateEngine (engine.rs)
│ │ │ ├── tx/ # Transaction types, mempool
│ │ │ ├── constants.rs # All protocol constants
│ │ │ ├── error.rs # CoinError type
│ │ │ └── lib.rs
│ │ └── Cargo.toml
│ ├── ultradag-network/ # P2P, Noise, sync
│ │ ├── src/
│ │ │ ├── node/ # server.rs (NodeServer, P2P handlers)
│ │ │ ├── peer/ # connection.rs, noise.rs, registry.rs
│ │ │ ├── protocol/ # message.rs (wire protocol types)
│ │ │ ├── bootstrap.rs # Testnet bootstrap nodes
│ │ │ ├── metrics.rs # Checkpoint metrics
│ │ │ └── lib.rs
│ │ └── Cargo.toml
│ ├── ultradag-node/ # Binary, CLI, RPC
│ │ ├── src/
│ │ │ ├── bin/ # loadtest.rs
│ │ │ ├── main.rs # CLI parsing, node init, shutdown
│ │ │ ├── rpc.rs # HTTP API handlers
│ │ │ ├── rate_limit.rs # RPC rate limiting
│ │ │ ├── validator.rs # Validator loop
│ │ │ └── lib.rs
│ │ └── Cargo.toml
│ ├── ultradag-sim/ # Simulation harness
│ │ ├── src/
│ │ │ ├── p2p/ # Virtual P2P network
│ │ │ ├── byzantine.rs # Byzantine strategies
│ │ │ ├── fuzz.rs # Fuzz testing
│ │ │ ├── harness.rs # Test harness
│ │ │ ├── invariants.rs # Invariant checking
│ │ │ ├── network.rs # VirtualNetwork
│ │ │ ├── oracle.rs # Test oracle
│ │ │ ├── properties.rs # Property-based tests
│ │ │ ├── txgen.rs # Transaction generation
│ │ │ ├── validator.rs # Simulated validator
│ │ │ └── lib.rs
│ │ └── Cargo.toml
│ └── ultradag-sdk/ # Rust SDK
│ ├── src/
│ │ └── lib.rs
│ └── Cargo.toml
├── sdk/
│ ├── python/ # Python SDK
│ ├── javascript/ # JavaScript/TypeScript SDK
│ └── go/ # Go SDK
├── formal/
│ └── UltraDAGConsensus.tla # TLA+ specification
├── site/ # Website assets
└── Cargo.toml # Workspace root
Conventions
UltraDAG follows strict code organization conventions:
mod.rsonly re-exports — no logic in module root files- Small files — target < 200 lines per file, one concern per file
- Inline unit tests — every module has
#[cfg(test)]tests - Integration tests — cross-module tests live in
tests/ - No unsafe code — zero instances of
unsafein the entire codebase - Comprehensive testing — 836 tests across the core workspace
Next Steps
- DAG-BFT Consensus — deep dive into the consensus protocol
- P2P Network — transport, encryption, and sync
- State Engine — account state derivation and persistence