Skip to content

Repository files navigation

dcroxide

A from-scratch Rust implementation of the Decred full-node daemon, built as a drop-in replacement for dcrd.

Parity target: dcrd master 29f17894 (version 2.2.0-pre) — wire protocol 12, JSON-RPC API 8.3.0. dcrd's behavior at that commit is the specification; see QUIRKS.md for deliberate bug-for-bug reproductions and PARITY.md for per-package status. The full plan lives in dcroxide-project-brief.md.

Status: pre-alpha — the dcrd surface is ported end to end. Every dcrd package has a Rust counterpart and the threaded daemon assembles them all: config, chain engine with full consensus validation, mempool, mining and CPU miner, P2P server with sync, relay, and mixing, the JSON-RPC/websocket server, the tool commands, the pipe IPC lifecycle, and the Windows service wrapper. The deliberate non-ports are small and documented in PARITY.md (Go GC tuning, the pprof servers, UPnP, the Windows event log, and the wallet-side mixclient). The test suite runs 711 tests across 232 suites, most differential against dcrd itself or replaying sessions generated inside dcrd's own packages.

Since the surface completed, three rounds of work have landed on it. A performance campaign: ffldb's metadata write cache, block scripts validated on dcrd's worker pool, a ported SigCache, one transaction hash per block, batched UTXO reads, and a layered dbCache overlay. A security campaign closing the blockers an audit found in the RPC and peer surfaces: authentication and admission control, bounded peer message paths with stall deadlines and queue limits, OS-seeded CSPRNGs, secret-file handling, and panic = "abort" in release builds. And the rename onto dcroxide's own identity — data directory ~/.dcroxide, configuration file dcroxide.conf, DCROXIDE_* environment variables.

It has synced testnet and mainnet to the tip from genesis with full consensus validation, and syncs against dcrd in both directions (see Performance). That says the consensus rules agree with dcrd's across the whole chain; it does not say the node is safe to operate. Do not expose it to the internet and do not use it with funds — see SECURITY.md for what is known to be missing and how to report a vulnerability. Currently implemented:

  • dcroxide-crypto — BLAKE-256 (vendored from dcr-rs, KAT-pinned, differential-tested against dcrd live) and RIPEMD-160 (RustCrypto-backed, KAT-pinned)

  • dcroxide-chainhash — the 32-byte hash type with dcrd's byte-reversed string encoding, including its short-string parsing quirk

  • dcroxide-wire — message framing with dcrd's exact validation order and error identities, plus all 41 P2P message types at protocol version 12, including addrv2 with its typed NetAddressV2 (IPv4/IPv6/TorV3) and the eight StakeShuffle mixing messages; MsgTx, blocks, headers, filters, state, and mixing messages all under differential test, fuzzing, and round-trip property tests — including the first QUIRKS.md entry (write-only reject)

  • dcroxide-uint256 — fixed-precision 256-bit arithmetic (difficulty/work math) ported operation-for-operation from dcrd's math/uint256, differentially tested against it across every operation

  • dcroxide-dcrec — all three Decred signature types with dcrd's exact acceptance rules and error identities: ECDSA-secp256k1 (type 0, over libsecp256k1), Ed25519 (type 1, over curve25519-dalek with dcrd's 2017-agl verify semantics), and EC-Schnorr-DCRv0 (type 2, over k256 with dcrd's RFC6979 nonce variant); every signing path differentially verified byte-for-byte against dcrd

  • dcroxide-chaincfg — all four networks' consensus parameters (mainnet/testnet3/simnet/regnet): genesis blocks reproducing dcrd's exact hashes and quirks, the full consensus-agenda deployment history, and the block-one premine ledgers; the complete parameter set is dumped field-by-field and compared byte-for-byte against dcrd's chaincfg through the oracle

  • dcroxide-txscript — the version-0 Decred script engine ported from dcrd's txscript: tokenizer, ScriptNum, the full 256-opcode set (including the stake and treasury opcodes), the execution engine with all flag combinations and P2SH handling, strict-encoding checks, signature hashing, and signature checking across all three suites; dcrd's entire script_tests.json/tx_valid/tx_invalid/sighash.json corpora run green, backed by a live differential script fuzzer against dcrd. The stdaddr, stdscript, and sign modules add all seven version-0 address kinds, standard-script classification, and transaction signing across every suite and script shape (P2PK/P2PKH, multisig, P2SH, stake and treasury outputs), all differentially matched against dcrd across every network

  • dcroxide-base58 — modified base58 and Decred base58check from decred/base58, vector- and differentially-tested

  • dcroxide-stake — the stake transaction primitives from dcrd's blockchain/stake: ticket/vote/revocation/treasury format checks and classification with all 72 of dcrd's error kinds, commitment and vote extraction, the Hash256PRNG ticket lottery, vote/revocation reward math (including auto-revocation remainder distribution), and revocation construction; dcrd's own test vectors replay oracle-free and the whole surface is differentially matched against dcrd; the ticket-database state machinery also lives here and is described under dcroxide-blockchain, which drives it

  • dcroxide-standalone — dcrd's blockchain/standalone consensus functions: merkle roots and inclusion proofs, compact-difficulty conversions and proof-of-work checks (including the BLAKE3 PowHashV2 from DCP0011, added to dcroxide-wire), the ASERT difficulty algorithm replaying dcrd's reference vectors, the full subsidy schedule across all three split regimes (validated by dcrd's exact total-supply figures), treasury spend window math, and context-free transaction sanity checks — all additionally differentially matched against dcrd

  • dcroxide-database — block and metadata storage with dcrd's database interface semantics (buckets, transactions, block storage APIs, all error kinds), backed by redb per ADR-0004 with dcrd's exact ffldb key layout and flat-file block record format, plus bulk block import/export in dcrd's addblock bootstrap format, plus ffldb's metadata write cache — layered snapshots over one durable flush per window — so a sync commits on dcrd's schedule rather than per block; pinned by the ported ffldb interface-test battery and a crash-consistency rig (fresh-sync stance: no in-place dcrd datadir reuse)

  • dcroxide-blockchain — the chain engine from dcrd's internal/blockchain, ported complete.

    The serialization and consensus-math foundations: dcrd's UTXO serialization layer (VLQs, the domain-specific script and amount compression, UTXO entries, outpoint keys, and the set state), the legacy work and stake difficulty algorithms, the stake-version voting machinery, the agenda threshold state machine, the agenda-driven algorithm selectors, and the chain persistence formats.

    The validation layers: context-free transaction validation and block sanity, the DCP0003 sequence lock calculation, positional and contextual header validation, the full transaction input validation (tickets, votes, revocations, and treasury spends through the fee-computing CheckTransactionInputs), and the block sigop and stake amount accounting.

    The chain state: the in-memory block index and chain view (skip-list ancestors, chain tips, best-chain candidates, invalidation propagation, and block locators), plus the immutable ticket treap, the ticket database serialization formats, and the full ticket pool state machine (connect/disconnect with lottery winners and undo data) in dcroxide-stake — wired into validation through the header stake commitments, the ticket redeemer checks, and the full contextual block assembly (checkBlockContext).

    Block connection: the utxo viewpoint with block connect/disconnect and spend journaling, the fee-accounting checkTransactionsAndConnect loop, and the full checkConnectBlock battery (treasury payouts, both tree connects, sequence locks, the header commitment filter, and block script execution).

    Block intake: the headers-first processing layer (maybeAcceptBlockHeader over the real block index with assumed-valid tracking and old fork rejection), the stake node attachment layer (fetchStakeNode with the pruned-node regeneration walk and side chain replay), the reorganization engine (connectBlock/disconnectBlock with best state snapshots and reorganizeChain over dcrd-exact utxo cache semantics), the complete ProcessBlock intake path (duplicate/orphan/invalid handling, headers-first data linking, and best chain selection), and the manual chain manipulation surface (InvalidateBlock/ReconsiderBlock/ForceHeadReorganization).

    Persistence and the consumer surface: the ticket database persistence layer over redb (byte-identical bucket rows against dcrd's ffldb), durable chain state (createChainState/initChainState with restart round trips over the reorganization ground truth), mining support (CheckConnectBlockTemplate, ticket exhaustion checks, and the chain query surface), the treasury account with the complete treasury spend checks (balances, vote tallies, and expenditure policies), and the RPC/netsync query surface (threshold state queries, vote counting, stake version walks, block locators, and the stake difficulty estimators).

    Pinned by dcrd's own test vectors, by synthetic-chain scenarios generated inside dcrd's internal package, and end to end by dcrd's own full block test battery (fullblocktests): 573 instances of fully signed blocks and invalid variants replayed through the real ProcessBlock with scripts on, matching every acceptance, rejection kind, and expected tip

  • dcroxide-mempool — the transaction memory pool from dcrd's internal/mempool: the mempool error kinds, the relay policy layer (minimum relay fees, dust outputs, and the transaction, output script, and input standardness checks), and the TxPool itself with the full acceptance gauntlet, orphan processing, ticket staging, batch acceptance, pruning, and the vote, revocation, and treasury spend acceptance paths — pinned by dcrd's own policy verdicts and scripted pool sessions generated with dcrd's own test harness

  • dcroxide-fees — the smart fee estimator from dcrd's internal/fees: decaying confirmation tracking over exponential fee rate buckets and the median fee estimation, replaying dcrd's floating point accounting bit for bit

  • dcroxide-mining — block template mining support from dcrd's internal/mining: the transaction dependency graph and mining view with ancestor statistics tracking, the priority queue with Go's exact heap semantics, the priority calculation, the block template building blocks (coinbase and treasurybase construction, parent vote sorting, and the template roots), the full NewBlockTemplate assembly replayed byte for byte against dcrd's own harness, wired into the mempool's mining hooks, and the background template generator's regeneration state machine replayed against dcrd's own event handlers

  • dcroxide-gcs — Golomb-coded set filters (versions 1 and 2) and the DCP0005 version 2 block committed filters for light clients, matched differentially against dcrd over random filters and structured blocks with real stake transactions

  • dcroxide-indexers — the optional block chain indexes from dcrd's internal/blockchain/indexers: the transaction index with its block-ID compaction, the exists address index with the unconfirmed overlay, and the subscriber machinery with dependent relay, catch-up, recovery, and incremental drops, replayed against a real redb-backed database from a session scripted inside dcrd's own package

  • dcroxide-containers — the container data structures from dcrd's container packages: the age-partitioned bloom filter used for P2P relay deduplication and the generic LRU map and set with optional time-based expiration, replayed bit for bit from sessions scripted inside dcrd's own packages with injected hash keys and a mock clock

  • dcroxide-addrmgr — the peer address manager from dcrd's addrmgr: address keys and network groups with dcrd's exact formatting, RFC-range routability and reachability, the new/tried bucket machinery over BLAKE-256 derivations with viability tracking, dcrd-compatible peers.json persistence, and the HTTPS seeder and Tor SOCKS DNS resolution dcrd 2.2 moved into this package, pinned by grids and state transitions scripted inside dcrd's own package with the randomized paths covered under an injected RNG, plus scripted proxy exchanges and seeder parse batteries

  • dcroxide-dcrjson — the JSON-RPC command infrastructure from dcrd's dcrjson/v4 module: Go's reflection-driven registry, marshalling, parameter parsing, usage, and help generation made explicit over type descriptors, with Go encoding/json semantics (HTML escaping, float formatting, sorted map keys, exact decode error messages) and a text/tabwriter port reimplemented so every byte of JSON, error text, and help output matches, pinned by a scripted session generated inside dcrd's own package

  • dcroxide-rpctypes — the chain server command, result, and notification definitions from dcrd's rpc/jsonrpc/types module: all 105 registered methods and every struct type as descriptors over the dcrjson base, including the custom five-shape Vin marshaling, pinned by usage text, zero-value marshals of every type, and curated populated round trips generated inside dcrd's own package

  • dcroxide-rpc — RPC server components from dcrd's internal/rpcserver: the help subsystem (the English help description map, the per-method result types, and the caching help/usage provider) and the handlers' pure transform layer (the RPC error constructors, address/hash/difficulty helpers, getwork serialization, and the vin/vout/raw-transaction result builders), plus the command handler slices (the stateless, chain-query, stake-query, mempool/connection, tx/utxo lookup, peer/address, submission/control, fee-info/node-info, mining/network/mix, and treasury-vote, getwork, and help commands — all 77 dcrd handlers) plus the request dispatch core (parse, route, reply marshalling, and the limited-user gate), Basic auth over HMAC'd credentials, the single/batched request body processing, and the websocket client core (transaction filters, rescans, and the websocket command handlers), and the websocket notification builders over a Server scaffold with the chain, mempool, sync and connection managers, indexes, database, filterer, log manager, fee estimator, sanity checker, time source, CPU miner, mix pooler, profiler and address managers, block templater, and clock behind trait seams, pinned by the complete generated help text, fully marshalled transaction results, and per-handler request/response cases from sessions generated inside dcrd's own package

  • dcroxide-netsync — the network chain synchronization manager from dcrd's internal/netsync: the sync manager as a synchronous decision core returning message/disconnect/timer actions, with header-first sync, block download scheduling, announcement tracking, and the rejected/recently-confirmed filters, pinned by a scripted 87-step session against a real dcrd sync manager, chain, peers, and pools, replayed over the real Rust chain engine

  • dcroxide-node — the daemon itself from dcrd's package main, as OS threads over channels rather than goroutines: the full configuration layer (every option with dcrd's defaults, the go-flags command line, INI, and environment semantics with dcrd's exact error strings, and the generated help text byte for byte), the server dispatch wiring every ported handler over live TCP peers (handshake, sync, relay, addr exchange, bans, mixing, and getdata serving with absolute per-message read deadlines), the outbound connection driver with addrmgr-backed dialing, the SOCKS5/Tor dial path with stream isolation, the HTTPS seeder (proxy-routed when configured), the JSON-RPC/websocket server over TLS with the shutdown drain and request-read watchdog, the mempool/chain/index/fee-estimator glue, the background template generator and CPU miner, the pipe IPC lifecycle, and the tool binaries (gencerts, addblock, promptsecret) — the P2P and RPC decision cores replayed against dcrd's real handlers, the runtime pinned by end-to-end socket tests

  • dcroxide-peer — the peer-to-peer protocol decision core from dcrd's peer package: version negotiation with self-connection detection and dcrd's exact acceptance rules, local version construction including proxy address hiding, the push builders with duplicate filters, ping/pong state, known-inventory tracking, and the stall deadline table, pinned by negotiations against dcrd's own package over real piped connections byte for byte

  • dcroxide-certgen — self-signed TLS certificate generation from dcrd's certgen over an exact DER writer for Go's certificate shape: Ed25519 pairs pin byte for byte and ECDSA pairs pin their to-be-signed bytes and keys, from a scripted session mirroring dcrd's template construction

  • dcroxide-ratelimit — dcrd 2.2's internal/ratelimit token bucket: a bucket seeded with burst tokens refilling at a fixed rate, reproducing dcrd's f64 operations in dcrd's order — including time.Duration.Seconds()'s whole-second/nanosecond split — so the token counts drift bit for bit with dcrd's, with Go's zero time.Time refill saturation carried by an explicit sentinel and Go's platform-defined uint64(float64) conversion pinned to the oracle platform; pinned by differential vectors including the cases where accumulated rounding error denies an event the documented average rate would allow

  • dcroxide-connmgr — connection management from dcrd 2.2's internal/connmgr: the dynamic ban score over a bit-exact port of Go's portable math.Exp, and the rewritten connection manager as a synchronous state machine with injectable dialers and event-driven retries — inbound anti-flood admission over per-network-group token buckets with the S-curve drop probability, outbound group spreading, per-host permits, and the persistent retry policy with dcrd's backoff scaling (including the upstream shift overflow, which the wrapping port reproduces); pinned by the full decay domain and by a state-machine dump driving dcrd's real ConnManager under a stub dialer and a scripted CSPRNG. dcrd 2.2 relocated HTTPS seeding and Tor DNS resolution into addrmgr, and the port follows

  • dcroxide-mixing — the StakeShuffle mixing support from dcrd's mixing package: message identity hashes and Schnorr signatures, session ID derivation and validation, the DC-net finite field and vector math, the per-run ChaCha20 PRNG, UTXO ownership proofs, and the mixpool itself with its acceptance rules, orphan handling, expiry, and misbehavior observer, replayed bit for bit from sessions scripted inside dcrd's own packages including a full honest 4-peer mix run and two observer strike rounds

  • dcroxide-winsvc — dcrd's Windows service wrapper over the windows-service crate: SCM detection and the service body with dcrd's status transitions, and the --service install/remove/start/stop commands (the option registered only on Windows, exactly like dcrd)

  • dcroxide-testutil — the differential-test harness every crate shares (no dcrd counterpart): the line-delimited JSON transport to tools/oracle, the toolchain gate (a missing Go toolchain skips the test, or fails it when DCROXIDE_REQUIRE_ORACLE is set), a deterministic SplitMix64 PRNG that prints its seed so a failure reproduces, and hex helpers; a dev-dependency only, never published

  • dcroxide-bench — the block replay harness (no dcrd counterpart): export writes the main chain of a stopped data directory to a bootstrap-format corpus, and replay drives that corpus back through the live chain engine with full validation — a network sync without the network — reporting throughput at a fixed block interval, so an optimization is measured on the same blocks before and after

  • tools/oracle — Go shim linking dcrd's own packages (pinned to the master 452c1a6c module versions) as a test oracle over line-delimited JSON

  • tools/helpgen — the go-flags help-vector generator over dcrd's verbatim config struct

Performance

Mainnet sync from genesis to the tip (~1,100,400 blocks) over loopback, one machine, a fresh data directory per run, both nodes --norpc: dcroxide 2.2.0-pre against dcrd 2.2.0-pre+452c1a6c3 (go1.26.5), in all four combinations.

syncer / source from dcroxide from dcrd
dcroxide 2.47 h — 124 blk/s 2.51 h — 122 blk/s
dcrd 1.11 h — 276 blk/s 1.02 h — 299 blk/s

The syncer decides the time and the source barely matters: swapping the source moves the result 1.6-8.8%, swapping the syncer moves it 2.2x. dcroxide is about 2.2x slower than dcrd at initial block download. Interop holds in both directions — dcrd accepts /dcrwire:1.0.0/dcroxide:2.2.0/ and dcroxide accepts /dcrwire:1.0.0/dcrd:2.2.0(pre)/, each reaching a matching tip.

The cause is the storage engine's commit shape, not validation. Across its two runs dcroxide spent 80.1% and 82.4% of wall time in progress stalls over 20 seconds; dcrd stalled zero times in 754 windows. goleveldb's LSM commit is O(dirty) with background compaction, while redb is a copy-on-write B-tree with no background work, so commit cost tracks the size of the tree.

At the tip the same chain costs 23.73 GiB under dcrd and 32.06 GiB under dcroxide. Block bytes are consensus data and match to within a mebibyte — 17.580 GiB against 17.579 GiB — so the whole 8.33 GiB difference is metadata: dcrd's 6.045 GiB blocks_ffldb/metadata leveldb plus a 0.108 GiB utxodb, against one 14.483 GiB metadata.redb. Neither side compresses; dcrd opens its chain databases with opt.NoCompression. The redb file holds 5.65 GiB of payload over 76,302,003 rows, and the rest divides into 0.69 GiB of per-pair overhead, 3.44 GiB of intra-page slack at 64.86% B-tree fill, and 4.69 GiB of allocated-but-free pages. That last figure — free space the allocator holds rather than a packing loss — is the largest single component and the least understood. Both it and the flush-bound ingest are open and tracked, not fixed. The full measurement record is in ADR-0004; PARITY.md records the divergence from dcrd's two-database layout.

Layout

  • crates/ — the Cargo workspace: one crate per dcrd package (see PARITY.md), plus the shared test harness and the replay bench
  • tools/oracle/ — the dcrd differential-test oracle (Go)
  • tools/helpgen/ — the go-flags help-vector generator (Go)
  • fuzz/cargo-fuzz targets (nightly toolchain)
  • docs/adr/ — architecture decision records

Development

Rust ≥ 1.88 (MSRV) and a Go toolchain (for the oracle-backed differential tests; without Go those tests skip). DCROXIDE_REQUIRE_ORACLE=1 turns a missing toolchain into a failure instead, so a run cannot silently pass with the differential coverage skipped — CI sets it.

cargo test --workspace          # unit + KAT + differential tests
DCROXIDE_REQUIRE_ORACLE=1 cargo test --workspace   # oracle mandatory
cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings
cargo +nightly fuzz list                       # requires cargo-fuzz
cargo +nightly fuzz run wire_msgtx_decode
cargo build --profile dist      # release artifacts

dist inherits release and strips debug info. release itself sets panic = "abort"; dev and test builds keep unwinding.

The workspace forbids unsafe_code and denies missing_docs everywhere (the one exception: dcroxide-winsvc denies rather than forbids unsafe because the service-entry macro expands an FFI shim, while writing none itself).

License

ISC. Portions derived from dcrd and dcr-rs, both ISC; see LICENSE and per-file attribution headers.

About

A from-scratch Rust implementation of the Decred full-node daemon

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages