Skip to content

feat: carry the PEX-known DIG peer count, not just the connected one - #210

Merged
MichaelTaylor3d merged 3 commits into
mainfrom
feat/2570-known-peer-count
Aug 10, 2026
Merged

feat: carry the PEX-known DIG peer count, not just the connected one#210
MichaelTaylor3d merged 3 commits into
mainfrom
feat/2570-known-peer-count

Conversation

@MichaelTaylor3d

Copy link
Copy Markdown
Contributor

DO NOT MERGE — work in progress, lane is live

Early draft per the standing directive that all work carries a PR from the start.

Intent (#2570)

Surface the number of DIG-network peers this node knows of, not only those it is connected to.

PeerCountsResult currently carries dig_peer_count (connected) and chia_peer_count. The node already
computes known_count() via PEX, but the contract does not carry it, so dig-app cannot render it. On my
machine that is the difference between a useless number and a useful one: connected_peers: 0 while the
relay reports 6.

PEX already exists on both sides — nothing is being built here. The relay has
dig-relay/src/pex.rs (RLY-008), embedding dig_pex::PexEngine in its introducer role, one engine per
network_id, advertising only first-hand peers and never re-advertising node-sent data ("an introducer
is not a gossip amplifier"). The node has the node↔node half (#166). This change is exposure only.

The honesty constraint that decides the field name

No count anywhere is "the total DIG network." The node's known_count() is peers this node has
learned of
— a lower bound from one node's view. The relay's is peers registered with that relay
nodes on another relay, or none, are invisible to it. Naming either "total network peers" asserts global
knowledge from a local view, the same class of false claim as reporting a wrong balance. The field is
named and documented as view-scoped, and its doc states what it does NOT include.

Existing Option semantics are preserved: the contract already documents "0 is an observed zero;
null is unobservable"
— an unknown count must not render as zero.

Also being answered with evidence

connected_peers: 0 against relay.peer_count: 6 has two very different explanations: introductions
arriving and every dial failing, or introductions never arriving at all. The lane has a live node and
logs and is measuring which, rather than inferring.

Rendering belongs to #2569's lane, which owns the header — not this branch.

Refs #2570, #2569.

@MichaelTaylor3d

Copy link
Copy Markdown
ContributorAuthor

Lane report — and my brief's premise was WRONG

The field shipped, but not from PexEngineHandle::known_count() as I specified. That would have
produced a fabricated number.

Why:dig_pex::PexEngine::known_count() returns the set PEX advertises, and
spawn_pool_feeder mirrors the connected gossip pool into it (upsert_known on PeerAdded,
remove_known on PeerRemoved). On the node it is a mirror of connected_peers — so the new field
would have read "6 known / 6 connected" always, and permanently "0 known" in exactly the lonely-node
case this ticket exists to diagnose. That is the fabricated-number class the honesty constraint warns
about, and I walked the lane straight at it.

What it used instead:GossipStats::known_addressesAddressManager::size(), "total distinct
tracked nodes"
. A genuine superset of connected, populated by every discovery route (relay
introductions, PEX candidates, dig.getPeers), read from the snapshot the pool loop already takes, so
no new machinery.

Also relevant: the node's PEX engine is wired only to the mTLS node↔node listener. Relay-discovered
peers arrive by a different path (dig-natRelayStatusattach_relay_status → address book), so a
PEX-sourced count would have been blind to relay introductions regardless.

The field, for the #2569 lane

pub known_dig_peer_count:Option<u32>,// wire key: "known_dig_peer_count"

PeerCountsResult, contract 0.10.0. 0 = looked, book empty. null = could not look — peer network
down, first pool pass not yet run, or the node predates the field (serde maps the missing key to
None, so an old node does not break a new client).

Render as "known" / "discovered", never "total" or "network size". known >= connected is normal but
documented as not an invariant — do not assert it in UI logic.

The pair is the diagnostic:0 connected / 41 known = a reachability fault; 0 / 0 = a discovery
fault. That distinction is the whole reason the field exists.

dign peers now prints DIG network 0 connected, 41 known · Chia full nodes 3.

Version-claim collision — the recurring shape

feat/2565-sent-notifications (0e065b6) also touches src/results.rs and also bumps to
0.10.0. Different regions (a field inside PeerCountsResult vs appended structs), so a textual
conflict is unlikely — but whichever merges second must re-bump to 0.11.0 or its version-increment
gate fails.

Two concurrent branches independently claiming the same version is now the third time this shape has
cost a round tonight.

Deliberately left undone

dig-node still pins dig-node-control-interface = "0.9". The node builds this payload with json!
rather than the typed struct, so it compiles and passes on 0.9. Release-first (§4.1): the contract
publishes 0.10.0 first, then the node bumps. Pinning an unpublished version now would red the CI.

Gates

Contract: fmt, clippy -D warnings (all targets + features), 84 tests + 1 doc-test.
dig-node: fmt, clippy clean, full workspace suite EXIT=0, 37 result lines, 0 failures — asserted on
the exit code, not on a tail through a pipe.

Four mutations, each applied to a committed tree by file copy and restored:

mutationcaught by
#[serde(default)]Some(0)an_older_nodes_peer_counts_decode_with_the_known_count_unknown_not_zero
rename the wire key3 tests incl. golden_response_result_vectors_are_byte_stable
source known_peers from connected_peers (the aliasing bug)knowing_of_peers_while_connected_to_none_survives_the_snapshot
drop the sampled flag (null0)an_unsampled_known_peer_count_is_null_not_zero

Unverified

The field's live value — everything is proven by test; nothing was measured against a running
0.110.0 node, because that needs a build+install cycle the disk could not fit. "known_addresses will
show > 0 on this host" is an expectation from backed_off: 3, not a measurement.

The investigation answer, and it is bigger than this ticket

(a) — introductions arrive and every dial fails. Root cause: the peers advertise IPv6-only
candidates and this host has no global IPv6 egress. Filed as #2576 (MVP): 348 dials, 0 pool peers,
158 established connections producing zero pool members and zero log lines.

MichaelTaylor3d added a commit that referenced this pull request Aug 10, 2026
…eason field
Bump the workspace version to 0.113.0 (0.112.0 is claimed by the open #210 branch).
MichaelTaylor3dand others added 3 commits August 10, 2026 14:06
Refs dig_ecosystem#2570
Co-Authored-By: Claude <noreply@anthropic.com>
…holds
`control.peerCounts` could say a node has zero DIG peers but not why. A node
connected to nobody and a node with nobody to connect to render as the same
zero, yet they are different faults: the first is reachability, the second is
discovery. This adds `known_dig_peer_count` -- the size of the gossip layer's
discovered-peer address book (`GossipStats::known_addresses`), sampled in the
same pool pass that already reads the connected count, and surfaced on
`control.peerStatus` as `known_peers` and on `dign peers` beside it.
The count is sourced from the address book, never from `connected_peers`:
aliasing the two would reproduce the exact ambiguity the field removes, so the
snapshot test drives them to different values with the connected count at zero.
An unsampled count reports `null`, tracked by an explicit sampled flag rather
than a sentinel, because every u64 is a plausible peer count and a zero would
claim an empty address book nobody looked at.
It is documented -- in SPEC 18.6c and at every rendering -- as ONE node's local
view and a lower bound, never the size of the network.
Refs dig_ecosystem#2570
Co-Authored-By: Claude <noreply@anthropic.com>
Refs dig_ecosystem#2570
Co-Authored-By: Claude <noreply@anthropic.com>
@MichaelTaylor3d
MichaelTaylor3dforce-pushed the feat/2570-known-peer-count branch from d351d48 to 92c2b77CompareAugust 10, 2026 21:16
@MichaelTaylor3d
MichaelTaylor3d marked this pull request as ready for review August 10, 2026 21:29
@MichaelTaylor3d
MichaelTaylor3d merged commit f74b4c1 into mainAug 10, 2026
15 checks passed
@MichaelTaylor3d
MichaelTaylor3d deleted the feat/2570-known-peer-count branch August 10, 2026 21:36
MichaelTaylor3d added a commit that referenced this pull request Aug 10, 2026
…eason field
Bump the workspace version to 0.113.0 (0.112.0 is claimed by the open #210 branch).
MichaelTaylor3d added a commit that referenced this pull request Aug 10, 2026
… watch (#212)
* chore: open #2609 lane
Fix the wallet sync phase never settling on a default install.
* fix(wallet): report an honest phase when custody has nothing to watch
An empty puzzle-hash set never runs a catch-up, so initial_sync_complete cannot latch while the peak advances. Add SyncPhase::NoAddressesToWatch, gated on a writing peer AND a measured-zero watched set.
* test(wallet): prove the default-install phase settles through the real supervisor
* docs(spec): specify no_addresses_to_watch and the watched_addresses reason field
Bump the workspace version to 0.113.0 (0.112.0 is claimed by the open #210 branch).
* docs(devlog): record why initial_sync_complete cannot latch on a default install
* docs(spec): clarify no_addresses_to_watch precedence
Co-authored-by: MichaelTaylor3d <5665004+MichaelTaylor3d@users.noreply.github.com>
* test(wallet): make the measured-vs-unmeasured watched set load-bearing
Review measured that replacing == Some(0) with unwrap_or(0) == 0 left the whole suite green: the only unmeasured test also left may_write false, so the trust condition rejected it a step earlier. Split set_subscription into set_trust + set_watched so the real in-between state (trust settled, set not yet resolved) is reachable, and add a test for it. The mutation now fails that test.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@MichaelTaylor3d