Skip to main content

Module node_address

Module node_address 

Source
Expand description

Node-id → operator Ethereum address resolver (ADR 003 §Node Registry, #831).

The node-to-node cache-miss pull path discovers upstream providers by iroh NodeId (DHT FIND_VALUE / origin directory), but paying one requires its bonded operator address: crate::buyer_channel::BuyerPoolService opens the USDC pool to that address, and the pull’s open stage (open_progressive_pull) verifies the delivery slash_sig recovers to it. This module resolves that binding from the same CapacityBond data crate::dht::chain_staker_set::ChainStakerSet already reads — getRegisteredNodes() returns NodeInfo { nodeId, ethAddress, .. } and NodeRegistered(nodeId, ethAddress, ..) carries both — so no new contract surface is needed.

This module owns the projection only. Because both views come from one contract, crate::dht::capacity_bond_registry does a single getRegisteredNodes enumeration and runs a single eth_getLogs loop feeding both, building this via ChainNodeAddressDirectory::from_parts (#1110).

The binding is set at registerNode and cleared at deregisterNode; it is unaffected by bond / unbonding / ejection transitions (those flip isActive, which the staker set tracks separately). So the registry’s bindings arms follow only NodeRegistered (insert) and NodeDeregistered (remove) — notably NodeAutoEjected deactivates without clearing a binding — and the enumeration applies no isActive filter: an operator mid-unbonding is momentarily inactive but still payable.

§Failure model

Bootstrap: the bindings projection is derived from page data the (unconditional; fatal on a deterministic fault or once its boot retries are exhausted) staker-set enumeration already read, so it has no RPC of its own and cannot fail independently — see capacity_bond_registry’s §Fatality. It is simply not built when cache.node_to_node_pull_through_enabled is off.

Watcher RPC failure mid-run → the shared loop logs at warn!, backs off (1s → 60s cap), and re-polls. The cursor is retained across the backoff, so the next eth_getLogs tick re-scans [cursor, head] and re-applies any binding event that landed during the outage — no backoff-gap drift. A missing binding fails the pull closed — the orchestrator skips a provider it cannot resolve rather than guessing an address it would then pay.

Structs§

ChainNodeAddressDirectory
Chain-backed NodeAddressResolver. Cheap to clone via the shared inner Arc; the background loop that keeps the cache fresh is the single shared multiplexed-poller task the runtime owns, so a node-restart cycle never leaks chain-poll tasks.
StaticNodeAddressDirectory
Static, in-memory NodeAddressResolver from a known map. Used by tests and any caller wiring a fixed binding set without a live chain.

Traits§

NodeAddressResolver
Read-only resolver from a provider’s iroh NodeId to its bonded operator Ethereum Address. Implementations MUST be cheap to clone (typically Arc<inner>); the pull orchestrator holds a long-lived Arc<dyn NodeAddressResolver> and calls Self::address_of once per candidate it intends to pay.