Skip to content

Getting Started for Developers

Michael Fazio edited this page Jul 31, 2026 · 7 revisions

Getting Started for Developers

This guide covers everything you need to start contributing to Dugite.

Prerequisites

  • Rust — latest stable toolchain, edition 2021. Install via rustup. The repository does not pin a toolchain file; CI builds with dtolnay/rust-toolchain@stable, so rustup update stable keeps you aligned.
  • protoc (Protocol Buffers compiler) — required. dugite-rpc's build.rs runs tonic-prost-buildprost-build, which shells out to protoc. Without it the workspace does not build.
    • Debian/Ubuntu: sudo apt-get install -y protobuf-compiler libprotobuf-dev
    • macOS: brew install protobuf
    • libprotobuf-dev supplies the well-known google/protobuf/*.proto includes. GitHub's Ubuntu runners already have them; a clean Debian base does not (see the Dockerfile).
  • just — the task runner that wraps every common workflow. just --list shows all recipes.
  • cargo-nextest — the test runner used by CI and by just test. Install with cargo install cargo-nextest --locked.
  • Git, ~8 GB RAM to build and run the test suite, and Linux or macOS (Windows is untested).

Beyond protoc, no system libraries are needed: block storage is append-only chunk files and the UTxO set uses dugite-lsm, a pure-Rust LSM tree. On Linux, dugite-storage (re-exported by dugite-node) offers an optional io-uring feature that swaps the memmap chunk reader for a kernel io_uring backend.

Clone and Build

git clone git@github.com:michaeljfazio/dugite.git
cd dugite

# Release build of every workspace target
just build

# Equivalent cargo invocations
cargo build --release --all-targets     # what `just build` runs
cargo build --all-targets               # debug build
cargo build --release                   # binaries only, fastest path to a node

Run Tests

just test        # cargo nextest run --workspace  (matches CI)
just test-doc    # cargo test --doc  (nextest cannot run doc tests)

Narrower runs go straight through cargo:

cargo nextest run -p dugite-ledger                       # one crate
cargo nextest run -p dugite-ledger -E 'test(test_name)'  # one test

Lint and Format

just clippy      # cargo clippy --all-targets -- -D warnings
just fmt-check   # cargo fmt --all -- --check
just fmt         # cargo fmt --all  (apply)

The Full Gate

just check runs the complete CI gate in order — fmt-check, clippy, build, test, test-doc. Run it before every commit.

just check

Do not pipe just check into tail/head: the pipeline reports the exit status of the last command, so a failing gate looks green.

Workspace Structure

The workspace has 16 crates under crates/, plus xtask and two test-suite crates under tests/.

Crate Purpose
dugite-primitives Core types: hashes, blocks, transactions, addresses, values, protocol parameters (all eras Byron–Conway)
dugite-crypto Ed25519 keys, VRF (ECVRF-ED25519-SHA512-Elligator2), KES (Sum6Kes), text envelope format
dugite-serialization In-house multi-era CBOR encoder/decoder (minicbor-based)
dugite-uplc In-house UPLC CEK machine — 100% conformant; used by dugite-ledger for Phase-2 Plutus evaluation
dugite-network Ouroboros mini-protocols (N2N/N2C), multiplexer, pipelined ChainSync client
dugite-consensus Ouroboros Praos, chain selection, epoch transitions, VRF leader check
dugite-ledger UTxO set (UTxO-HD), transaction validation (Phase-1/Phase-2), ledger state, certificates, rewards, governance
dugite-mempool Thread-safe transaction mempool with input-conflict checking and TTL sweep
dugite-lsm Pure-Rust LSM-tree engine backing the on-disk UTxO set (no workspace dependencies)
dugite-storage ChainDB = ImmutableDB (append-only chunk files) + VolatileDB (in-memory). Owns the optional Linux io-uring chunk-read backend
dugite-rpc Native UTxO RPC (gRPC) server built from vendored utxorpc protos. The only crate that depends on tonic/prost, so the gRPC stack stays out of the node's graph when RPC is off
dugite-node Binary. The node: config, topology, pipelined sync, Mithril import, block forging, metrics, RPC adapter
dugite-cli Binary. cardano-cli compatible CLI: 11 command groups, ~92 leaf subcommands, era-prefix aliases
dugite-monitor Binary. Terminal monitoring dashboard (ratatui-based, polls the Prometheus endpoint)
dugite-config Binary. Interactive TUI config editor: tree navigation, inline editing, type validation, search/filter, diff view, plus init/validate/get/set subcommands
dugite-integration-tests End-to-end integration tests (test-only crate, no binary)

Test-suite crates: tests/conformance (package dugite-conformance — upstream golden replay) and tests/golden (package dugite-golden-tests). xtask hosts repo automation such as cargo xtask download-upstream-fixtures.

Dependency flow (workspace edges only):

dugite-node ──► network, consensus, ledger, storage, mempool, rpc, uplc, serialization, crypto, primitives
dugite-cli  ──► network, consensus, serialization, crypto, primitives
dugite-rpc  ──► mempool, serialization, primitives
dugite-network / dugite-storage ──► consensus, serialization, crypto, primitives
dugite-mempool ──► ledger, crypto, primitives
dugite-ledger  ──► lsm, uplc, serialization, crypto, primitives
dugite-consensus / dugite-uplc ──► serialization, crypto*, primitives   (*consensus only)
dugite-serialization / dugite-crypto ──► primitives
dugite-monitor, dugite-lsm, dugite-primitives ──► (no workspace dependencies)

dugite-config depends on dugite-node only as a dev-dependency (it validates against the node's real config schema).

Key Files to Read First

If you are new to the codebase, start with these:

  1. CLAUDE.md (repo root) — architecture overview, build commands, key patterns, per-release notes
  2. crates/dugite-node/src/main.rs — entry point, CLI subcommands (run, mithril-import, db, dump-snapshot, verify-ledger-snapshot, snapshot-convert), startup
  3. crates/dugite-node/src/node/mod.rs and node/sync.rs — node lifecycle and the pipelined sync loop
  4. crates/dugite-ledger/src/lib.rs — ledger state and block application
  5. crates/dugite-storage/src/chain_db.rs — ChainDB (ImmutableDB + VolatileDB)
  6. crates/dugite-network/src/lib.rs — mini-protocols and the multiplexer
  7. crates/dugite-consensus/src/lib.rs — consensus engine and epoch transitions
  8. crates/dugite-primitives/src/lib.rs — core type definitions

Running on a Testnet

The fastest way to a running node is a Mithril snapshot import followed by a relay run. Both are one-liners:

just build                     # cargo build --release --all-targets
just mithril-import preview    # ./scripts/mithril/import.sh preview
just run-relay preview         # ./scripts/run/relay-preview.sh

mithril-import, run-relay, and run-bp all take mainnet | preview | preprod.

The underlying commands, if you prefer to drive the binary directly:

./target/release/dugite-node mithril-import \
  --network-magic 2 \
  --database-path ./db-preview

./target/release/dugite-node run \
  --config config/preview/config.json \
  --topology config/preview/topology.json \
  --database-path ./db-preview \
  --socket-path ./node.sock \
  --host-addr 0.0.0.0 --port 3001
Network Magic Config directory
Mainnet 764824073 config/mainnet/
Preview 2 config/preview/
Preprod 1 config/preprod/

Each directory holds config.json, topology.json, and the Byron, Shelley, Alonzo, and Conway genesis files, with relative paths — no rewriting needed.

Metrics

The Prometheus endpoint port is resolved with this precedence: --no-metrics (0, disabled) → --metrics-portTurnOnLogMetrics: false in the config (0) → MetricsPort in the config → built-in default 12796. The shipped configs set it explicitly, so a stock run uses:

Config MetricsPort
config/preview/config.json 12796
config/preprod/config.json 12799
config/mainnet/config.json 12800

Tail whichever port you are on with just watch-metrics <port> (the recipe's own default argument is 12798, cardano-node's port — pass yours explicitly). just monitor-start brings up a local Prometheus + Grafana stack in Docker.

Local Devnet and Release Validation

A three-node loopback devnet (dugite-bp, dugite-relay, cardano-node-bp) lives under testnet/local-devnet/:

just devnet-setup             # render configs, generate keys, fetch reference binaries
just devnet-run               # start all three nodes
just devnet-soak              # 30-minute soak
just devnet-verify            # check the evidence from the last run
just devnet-stop
just devnet-validate-smoke    # single boot, ~5 min — the PR gate for core crates
just devnet-validate-extended # 3 rounds, ~75 min — the release gate

Reports are written to reports/devnet-validate/. Note that the tx-zoo/09-cli-parity suite runs cardano-cli against both sockets and diffs the answers: it measures dugite-node's LSQ responses, never dugite-cli. A failure on both sides is a harness bug.

Upstream Conformance

Upstream artefacts are republished as a pinned dugite release and consumed as fixtures.

just download-upstream-fixtures        # all areas, at the tag in tests/conformance/upstream/manifest.toml
just download-upstream-fixtures-area plutus
just test-conformance                  # UPLC corpus + every upstream golden suite
just test-conformance-ledger-rules     # one area (per-area recipes exist for each)

Seven areas: ouroboros-consensus, cardano-ledger, cardano-node, plutus (999 UPLC vectors), ledger-rules (ImpSpec CBOR vectors), cardano-base (VRF vectors), mithril. Per-area recipes report "N skipped" for the tests they filtered out — that is not a coverage gap; use the unfiltered just test-conformance for a real zero-skip run.

Development Workflow

  1. Assess — evaluate the current state and pick the highest-impact gap or bug
  2. Implement — keep changes focused and reviewable
  3. Testjust test (and just test-doc), zero failures
  4. Verifyjust clippy and just fmt-check, or just check for the whole gate
  5. Commit — descriptive message, explicit filenames staged
  6. Repeat

Hard Rules

  • Zero compiler warnings (RUSTFLAGS="-D warnings")
  • Clippy clean at -D warnings
  • All tests passing
  • Code formatted
  • CI green before merge
  • Stage explicit filenames — no git add -A / git commit -a. The repo ships .githooks/pre-commit, which warns when staged paths span more than two crates; enable it with git config core.hooksPath .githooks, and set DUGITE_PRECOMMIT_STRICT=1 to make the warning fatal.

Useful Environment Variables

Runtime knobs (node):

Variable Default Description
DUGITE_PIPELINE_DEPTH 300 ChainSync pipeline high-water mark; refill happens at ~67%
DUGITE_FETCHED_BLOCKS_CAP 4096 In-flight fetched-block channel capacity; lower it to cap peak memory
DUGITE_VOLATILE_RETAIN 10000 Blocks retained in the VolatileDB live set
DUGITE_SHUTDOWN_DEADLINE_SECS 90 Graceful-shutdown deadline; a second signal forces exit
DUGITE_TRUSTED_CATCHUP unset Set to 1 to opt out of full validation during catch-up
DUGITE_BLOCKFETCH_MAX_RANGE config Overrides the per-batch BlockFetch range cap at runtime
RUST_LOG info Log filter (trace, debug, info, warn, error); overrides --log-level

Test and tooling knobs:

Variable Description
DUGITE_REQUIRE_UPSTREAM=1 Make upstream conformance tests fail rather than skip when fixtures are missing (CI sets this)
DUGITE_UPSTREAM_FIXTURES_DIR Override the fixture lookup root (default tests/conformance/upstream/fixtures/)
DUGITE_DUAL_DECODE panic or dump — cross-check the in-house decoder against the shadow decoder (just dual-decode-smoke, just dual-decode-soak)
DUGITE_PRECOMMIT_STRICT=1 Make the multi-crate pre-commit tripwire fatal

The node also honours a long tail of diagnostic dumps (DUGITE_EPOCH_STATE_DUMP, DUGITE_PHASE2_DUMP_DIR, DUGITE_REWARD_DEBUG_DUMP, …). Grep crates/ for DUGITE_ to see the full set.

Further Reading