Skip to main content

Module capacity_bond_registry

Module capacity_bond_registry 

Source
Expand description

One CapacityBond watcher feeding five registry projections.

ChainStakerSet (membership: NodeId → active?), ChainNodeAddressDirectory (bindings: NodeId → operator address), the region map (NodeId → regionHint), the operator reverse map (operator address → NodeId), and the DialAddrDirectory (NodeId → registry multiaddrs, as iroh direct-dial addresses) are all derived from the same CapacityBond contract. This module runs one enumeration and one eth_getLogs loop over the shared CapacityBond address, demuxing to all five projections; node-address’s two topics are a strict subset of the route’s eight. RegionUpdated feeds the region map alone, and NodeMultiaddrUpdated the dial-address directory alone.

Bindings is gated on cache.node_to_node_pull_through_enabled: when pull-through is off, the bindings projection is not built at all. Regions, the operator reverse map, and the dial-address directory are always built, regardless of pull-through — the ADR-030 selection penalty needs the region map unconditionally, the chain-backed origin directory needs the reverse map to resolve operators locally with no nodeIdOf RPC, and every node dial (DHT included) resolves direct addresses through the dial-address directory.

§What actually differs between the projections

Mostly the enumeration. The live event arms are close to a clean union: NodeRegistered inserts into active, bindings (if built), regions (if non-empty), the reverse map, and the dial-address directory (if any address decodes), all unfiltered — staker-set’s live arm applies no isActive check either (its filter is bootstrap-only). It is tempting to read “staker-set is the filtered view, the rest are the unfiltered ones” as a live-path difference and encode it in the sink; that would be wrong, and would drop registrations from the active set.

At bootstrap active and bindings genuinely diverge:

  • active takes the per-entry active[i] that getRegisteredNodes computes on-chain (isActive(operator): registered AND bond ≥ minBond AND no unbonding AND not ejected) alongside the _registeredAddrs page.
  • bindings applies no filter: an operator mid-unbonding has isActive = false but is still payable, so its binding must survive.
  • regions, the reverse map, and the dial-address directory are likewise unfiltered: liveness is applied separately at read time via the shared StakerSet, and an inactive node stays dialable for channel settlement.

§Fatality

The single getRegisteredNodes bootstrap is fatal: staker-set requires it. A transient RPC failure retries on the shared boot budget; a deterministic fault or an exhausted budget propagates. The bindings projection (pull-through is opportunistic, so it would otherwise be non-fatal) has no RPC of its own: it is derived from page data already in hand and cannot fail independently. The same is true of regions, the reverse map, and the dial-address directory. A node either boots with all five projections or fails at the one shared read, so pull-through is never lost on its own.

Structs§

RegistryHandles
Both projections, plus the shared watcher that keeps them current.

Functions§

bootstrap
Enumerate CapacityBond once, then spawn the single watcher that keeps both projections current.