Skip to content

Parallax v2.0.0

Latest

Choose a tag to compare

@github-actions github-actions released this 16 Aug 21:38
· 52 commits to main since this release
2bc0f87

Parallax v2.0.0 is the first release of the 2.x line. These notes cover everything since v1.2.0, including the four release candidates.

A note up front: every Go import path has changed. If you depend on Parallax as a library, your build will break until you update the paths.

What's new

P2P / networking

I've rewritten the discovery and handshake stack. The relevant pieces:

  • A v2 RLPx handshake along the lines of BIP324, wired into the Server. The client runs both v4 (UDP) discovery and the new TCP-based discovery by default and prefers the TCP path; v4 stays on for backwards compatibility. UDP discv4 will be fully deprecated in a later release. The longer-term motivation for moving discovery off UDP is to open the door to native Tor integration, so node operators and tx broadcasters can run with better privacy and anonymity.
  • parallax-disc/1, a gossip discovery subprotocol. It uses the new address manager as its backend, with quorum and rate limiting on top. Address relay follows Bitcoin Core's ADDR gates: an exact-parity token bucket on ingest, fan-out with a daily-rotating PRF, reduced fanout for unreachable-network addresses, and a rolling known-address filter.
  • A new address manager (addrman), ported from Bitcoin Core's bucketing design and audited against the pinned reference (Core v31.0): bucketed, persisted across restarts, dual-stack aware, and source-aware when picking peers. Five sources are tracked (tcp_gossip, legacy_udp, dns_seed, manual, self_advertised) and feed both Select() weighting and source-aware bucket eviction. Long-lived outbound sessions re-stamp their entries so healthy peers don't age out, and the node's own identity refreshes from first-hand outbound sessions.
  • ENR-driven dialing for v2, ip:port bootnodes, dedup of v2 peers on (IP, port), and up to 4 concurrent inbound attempts per IP.
  • --dnsseed for plain-DNS seed consumption (default seed is seed.prlxdisc.org, port 32110). --legacy-discovery=[auto|on|off] is the single operator knob for the v1.x compatibility surface (UDP discv4 responder + legacy RLPx accept). Default is auto; off is fully v2-only.
  • DNS-seed publisher under cmd/devp2p dns-seed with compile, to-zonefile, to-cloudflare, to-route53 subcommands. Consumes crawler JSON, filters to default-port v2.0-native entries, and reconciles A/AAAA records idempotently. The parallax-disc crawler is a multi-hop stateful walker that always probes v2, judges seed candidates by bitcoin-seeder-style reliability windows, and evicts unreachable nodes from its state.

Peer management

Alongside the transport work, v2.0 ports Bitcoin Core's peer-management machinery:

  • Bitcoin Core's inbound eviction algorithm, triggered on inbound saturation, with protection slots (including localhost peers) and deterministic tie-breaking. Per-peer quality telemetry feeds it: ping RTT, payload bytes received, and last-block / last-tx receipt timestamps.
  • Outbound network-group diversity is enforced on every dial path, including v2, block-relay, and anchor dials. 6to4 and Teredo addresses group by their embedded IPv4 /16.
  • Feeler and addrfetch dial loops keep the address manager tested and fresh. Feelers never join the peerset, never receive pool announcements, and can't reach snap serving.
  • Block-relay-only outbound peers, with anchor persistence on by default. The (IP, listen-port) of currently-connected anchor peers is written to <datadir>/anchors.dat on clean shutdown and replayed on next startup, mirroring Bitcoin Core's m_anchors. Disabled when MaxBlockRelayPeers=0. Block-relay peers that send transactions are disconnected.
  • A persistent ban list (<datadir>/banlist.json) backs admin_setban / listbanned / clearbanned, surfaced as parallax-cli setban, listbanned, clearbanned, with argument semantics matching Bitcoin Core (subnet bans included). The inbound-accept path rejects banned source IPs before the handshake, outbound dials refuse banned or discouraged addresses, and misbehavior during a session adds the peer's IP to a rolling in-memory discourage filter consulted under inbound saturation. Trusted, static, and local peers are exempt from discouragement.
  • MinLegacyPeers floor (default 2): hard-caps tcp_gossip-sourced peers at MaxPeers - 2 while non-tcp_gossip alternatives are reachable, so a v2.0-specific bug can't take down 100% of peers during early v2.x. Removed at v3.0 alongside the legacy transports. Set negative to disable.

Daemon mode and commands

There's a proper headless daemon mode now, with start, stop, status.

A fair number of commands moved into parallax-cli: chaininfo, netinfo, mempool-content, getblockhash, getheader, tip, balance, nonce, code, storage, estimategas, addpeer, removepeer, addtrusted, removetrusted, mining, startmining, stopmining, setcoinbase, setextra, loglevel, trace, dbstats, decoderaw, toaddr, account, newaccount, listaccounts, lock, unlock, sign, sendtx, uptime, setban, listbanned, clearbanned. Also addnode, addrbook, and the parallax-disc crawler.

Trusted peers receive full blocks rather than just headers.

RPC

New: net_peers, admin_uptime, dbstats, admin_setban / admin_listbanned / admin_clearbanned. New addrman-backed admin RPC: admin_addnode, admin_removenode, admin_addrbookStatus, admin_addrbookResetKey (surfaced as parallax-cli addnode, removenode, addrbook status, addrbook reset-key). addnode accepts both ip:port (v2-native) and enode://nodeID@ip:port (legacy) and stores entries with source=manual so they survive restarts and outrank gossip. admin.uptime and admin.stop are registered on the console.

admin.peers: the handshake variant is exposed (v2 vs legacy+v2). Enode/ENR/ID marshal as null for v2 sessions, but the session-scoped ID is still visible. admin.nodeInfo follows the same rule in v2-only mode, and ports.discovery mirrors the TCP port when UDP is disabled. admin_addPeer/admin_removePeer branch on input format and reject enode:// inputs in v2-only mode.

Removed

  • The light client (LES) is gone. All --light.* and --ultralight.* flags, the LightSync mode. If you ran a light node, you'll need a full or snap-syncing node now.
  • discv5 and --v5disc are gone.
  • The deprecated --nousb, --whitelist, --miner.gastarget, --show-deprecated-flags are gone.
  • The GUI (prlx-gui) moved to its own repository.

Testing and hardening

v2.0.0 ships with a substantially expanded test program:

  • All fuzzers migrated from go-fuzz to native testing.F, with shared corpus seeding. New fuzz targets across the tree: addrman persistence, netlist parsing, disc Hello round-trip, v4wire/ENR/enode codecs, v2 AEAD framing, hexutil JSON, transaction codecs, fork ID, keystore decrypt, calldata and selector parsing, ASERT difficulty, and the fee estimator's txConfirmStats.
  • New stress tests: transaction-pool load, deep/shallow/randomized sidechain reorgs, and concurrent state-snapshot reads with reverts.
  • CI now runs race, coverage, and conformance jobs plus a sharded nightly fuzz smoke, and release branches (N.N.x, v*) get the full build / lint / unit-test lanes.

The fuzzers found real bugs, fixed in this release (see Fixes below): the supply-reporting era math, a selector-parser panic, and unbounded keystore KDF parameters.

Fixes

  • kernel/xhash: the reported total supply dropped the entire first era's emission once height passed 210000 and mishandled era boundaries past the first halving. Supply is now computed by exact per-era summation, with drift tests, and coinbase maturity is read from the chain config. Reported (not consensus) values change.
  • script/abi: ParseSelector no longer panics on a missing parenthesis, and rejects top-level array suffixes.
  • wallet/keystore: decrypt now validates KDF params, IV and ciphertext lengths, and bounds scrypt/pbkdf2 cost parameters, so a malicious keystore file can't crash the process or blow up memory.
  • validation/state/snapshot: stale check now happens before the bloom shortcut.
  • validation/state/pruner: bloom-filter filename parsing guards against overlapping prefix/suffix and requires canonical hash spelling.
  • policy/fees: estimateSmartFee falls back to estimateFee when there isn't enough data. Log message in the gas-price oracle clarified.
  • kernel/xhash: switched to hash.Sum after x/crypto v0.45 dropped Read() on sha3.

Security

Dependency updates in this cycle close a number of published vulnerabilities:

  • CVE-2025-30204: golang-jwt/jwt could OOM on a malformed Authorization header. This one was reachable through the Engine API auth path, so treat it as a real fix, not hygiene. Fixed by the bump to v4.5.2.
  • CVE-2026-46600: DoS in x/net (dns/dnsmessage) via invalid DNS records. Fixed in v0.56.0.
  • CVE-2026-56852: DoS in x/text on invalid UTF-8. Fixed in v0.39.0. Both this and the x/net CVE were caught by the new Trivy scanning of the release images.
  • CVE-2023-44273 and GHSA-pffg-92cg-xf5c in gnark-crypto (ECDSA/EdDSA deserialization range checks, GT-GLV exponentiation). The vulnerable paths are only reachable from our fuzzers, but the bump to v0.18.1 clears the alerts.
  • Lower-severity advisories in x/crypto and protobuf whose vulnerable code paths aren't reached from this codebase, bumped alongside as no-risk hygiene.

Related hardening in this release: the keystore decrypt bounds (see Fixes) close a DoS vector via malicious keystore files, and Docker images now run unprivileged on pinned bases with Trivy scans in CI.

Binaries and build

The daemon is now parallaxd, not prlx. The IPC socket is parallax.ipc and the metrics prefix is parallax..

There are three binaries:

  • parallaxd: the full node daemon.
  • parallax-cli: the client/admin tool.
  • parallax-wallet: offline wallet tooling, consolidated.

There is also a parallax wrapper. parallax node … execs parallaxd, parallax rpc … execs parallax-cli. The wrapper resolves its companion via EvalSymlinks first and $PATH second, so both go-run and symlinked installs work.

The Makefile, ci.go, Dockerfile, and release.yml know about all three binaries. There are bash and zsh completions. The dlgo bootstrap is on Go 1.26.1.

Docker is now a first-class install path: multi-arch images are published to Docker Hub on release tags, built with Go 1.26, running as an unprivileged user on pinned base images with OCI labels, and including parallax-wallet. Images are scanned with Trivy in CI.

I've removed a fair amount of legacy: AppVeyor, CircleCI, Travis, NSIS, Maven, CocoaPods, Debian build files; the mobile bindings; swarm/; the contracts/ checkpoint oracle; cmd/checkpoint-admin/. clef and parallaxkey are no longer in release artifacts.

Dependency updates: jwt/v4 v4.5.2, x/crypto v0.53.0, x/net v0.56.0, x/text v0.39.0, protobuf, gnark-crypto v0.18.1. Several of these fix CVEs; see the Security section. reexec moved to github.com/moby/sys/reexec.

Code layout

The tree has been restructured into layers: kernel/, validation/, script/, primitives/, p2p/, node/, rpc/, wallet/, policy/, support/, util/, logging/, dbstore/, crypto/.

kernel/ no longer imports anything from validation, p2p, node, or wallet, and it has no RPC code. It does this through two new interfaces, StateAccessor and ChainConfigurator. kernel/consensus/ and kernel/misc/ were folded into the kernel root.

Package names match directory names: common -> util, log -> logging, prldb -> dbstore, params -> chainparams, core -> validation, vm -> script, accounts -> wallet, prl -> protocol, prlclient -> client, prlstats -> stats.

The old consensus wrapper is gone, engine APIs are registered directly from node/fullnode/backend.go. The node/fullnode <-> node/light cross-imports were resolved before the light client was removed. The gasprice forwarding shim is gone; callers go directly to policy/fees/.

Documentation

The documentation now lives in this repo under docs/, not in the separate parallax-docs repo. It's still served at https://docs.parallaxprotocol.org via Mintlify. New in this cycle: a full XHash technical specification, a peer-management and operator RPC reference, Docker install/run guides, and links to the second edition of the whitepaper.

Upgrading

  • Replace prlx with parallaxd, or use the parallax wrapper. The IPC socket is parallax.ipc; metrics prefix is parallax..
  • If you import Parallax as a Go module, your imports will all need updating.
  • If you were running a light client, you'll need a full or snap-syncing node.
  • Drop --nousb, --whitelist, --miner.gastarget, --v5disc from any startup scripts.
  • v4 (UDP) discovery is still on by default and runs alongside the new TCP-based discovery, with TCP preferred. This is a backwards-compatibility measure; v4 will be fully deprecated in a later release. Operators who want pure-TCP can flip --legacy-discovery=off (no UDP socket, legacy RLPx refused, enode URLs become diagnostic-only). --legacy-discovery=[auto|on|off] is the single new P2P knob.
  • If you consume the total-supply RPC, expect corrected values (see Fixes).