Public vs Private Nodes, and Their Participation in Blocks

BSAHI — literature finding Produced: 2026-09-21

The short answer

Two different ratios get conflated, and only one of them is published.

  1. Population ratio (public vs private nodes) — well studied. Reachable (listening) nodes are roughly one third of the network; non-listening ("unreachable") nodes are the other two thirds, i.e. about 1 : 2. Estimates across independent methods cluster between 1 : 1.6 and 1 : 2, with outlier claims up to 1 : 10–30.
  2. Block-participation ratio (what share of a block's relay is public vs private) — not published by anyone in the form people expect. There is no per-block "X% public, Y% private" figure in the literature. What exists is evidence that both classes relay, plus the structural reason the split is lopsided.

Why "participation in blocks" needs splitting three ways

SenseWho does itIs public/private the right axis?
Producing a blockminers / poolsNo. A block is produced by a miner; whether that miner's node listens is incidental. Production is measured by pool share and region, not node class.
Relaying a block (inv → getdata → block)every nodeYes — and both classes relay.
Serving a block to a new peerlistening nodes onlyNo — by definition only reachable nodes can be dialled.

The third row is the asymmetry that shapes everything else: a non-listening node can pull a block and pass it on to its own outbound peers, but nobody can dial it. So it can never be the first hop for someone else.

What the literature establishes

Population ratio.

SourceMethodFinding
Bitnodes, Apr 2026crawler + addr gossip~70,000 total; ~23,000 reachable (~1/3)
Li et al., BNS, Mathematics 2023active crawl + decision-tree classifier45,000–50,000 total; reachable : unreachable ≈ 1 : 1.6
Wang et al., PAL, 2021passive addr observation (5 yrs)~31,000 unreachable peers with useful services active per day, end-2020; validation suggests ~50% detection
Franzoni & Daza, IEEE Blockchain 2020surveyunreachable "more than 90% of the whole network"; some estimates 10–30× reachable
Kiffer et al., 2025cross-chain crawlprior work: unreachable ≈ 2× reachable
BTC Nodes crawl, Sep 2026own handshake crawl21,837 reachable by network type: onion 51%, IPv4 21%, I2P 19%, IPv6 8%

The spread is real and comes from method: an active crawler sees only listening nodes; a passive observer watching addr gossip sees a timestamped address set, which is polluted by dead and flooded entries. Bitnodes' own caveat is that an unreachable count "can only provide a rough estimation".

Do private nodes relay blocks? Yes — explicitly.

Why the relay split is lopsided (structural, not empirical).

So "how much do private nodes participate in blocks?" has a structural answer (they relay, as leaves) but no published numeric split.

What this project adds

No one publishes the per-block public/private relay split, and BSAHI does not either. What the Phase 5 instrument measures is the closest first-party proxy:

Of blocks observed, what share had an inbound peer among the announcers — and
among the earliest announcers?

Mechanics: getpeerinfo records, per block, which of our peers announced it, their network and connection_type, and whether they were inbound (the peer dialled us) or outbound, with second-granular timing. Aggregated into data/propagation_cdf.json → participation, and surfaced on the dashboard.

This is an upper bound, not the split. inbound means the peer dialled us, and a listening node can dial us too. So the inbound set is {private diallers} ∪ {public diallers}, and the share it produces overstates private participation. Separating the two needs a reachability test — dialling the peer back to see if it accepts — which this instrument does not do.

Status: wired and unit-tested, awaiting synced relay. The node was reindexing, and during reindex blocks come from disk, not from peers, so blocks_observed is still 0 and no share is published. Nothing is fabricated to fill the gap.

The axis that actually touches cost: archival, not relay

Relay ≠ storage. A node announcing a block proves it passed a message on; it proves nothing about whether it keeps the chain. So relay participation cannot move the storage-cost estimate — but the archival rate can, and that is a different measurement entirely: the service bits.

NODE_NETWORK means the node serves the full chain; NODE_NETWORK_LIMITED means it is pruned. Measured on the reachable crawl:

NODE_NETWORK          4,356
NODE_NETWORK_LIMITED  1,040      -> 80.7% full-chain, 19.3% pruned

That is the replication factor, and it is the number a "cost per node" model divides by. Its blind spot is structural: it only covers the visible third. The archival status of the non-listening majority is unknown to any crawler.

The one window into it is the nodes that dial us: they send their service bits in the version handshake, and that set includes non-listening nodes. So inbound_census.py now records services / servicesnames per inbound peer and aggregates the archival share (inbound_service_bits). It samples the replication factor of the population the reachable crawl cannot see — which is a quantity that can change the model, unlike relay share.

Early and tiny by construction (bounded by our inbound slots and uptime): the first peer with usable bits set both NETWORK and NETWORK_LIMITED, which is contradictory (BIP-159 treats them as exclusive), so it is reported in its own both_bits_ambiguous bucket rather than counted as archival.

Caveats that must travel with any number here

Sources

← All research · ← Back to Learn