Skip to main content

Module lazy_origin_cache

Module lazy_origin_cache 

Source
Expand description

Lazy, bounded, TTL-split cache of getOrigins(namespaceId) results.

ChainOriginDirectory resolves a namespace to its authorized origin operators lazily, on first request, rather than by watching every getOrigins event up front. Namespace creation is permissionless and free, so an attacker can mint namespace IDs at will; if every request for an unknown namespace forced a fresh getOrigins RPC, a burst of attacker-chosen namespaces would turn into a proportional burst of chain calls. This cache exists to break that link: it remembers a namespace’s result — including “this namespace has no origins” — for a bounded time, so repeated lookups against the same (possibly bogus) namespace cost one RPC, not one per request.

A non-empty getOrigins result is a POSITIVE entry and lives for positive_ttl. An empty result (no origins, including a namespace that does not exist on chain) is a NEGATIVE entry and lives for the shorter negative_ttl — the actual DoS bound: it caps how often an attacker can force a real RPC per bogus namespace, while keeping genuinely-empty namespaces from squatting on cache space or masking a legitimate getOrigins call for very long once one is registered.

The cache stores the raw operator ADDRESS set, never resolved NodeIds. Resolving addresses to live NodeIds (via the capacity-bond registry’s reverse projection) and filtering by StakerSet liveness both happen at READ time, in resolve_active — so a TTL-anchored cache entry never goes stale on the parts of the answer that change without a new getOrigins call: operator staking status and operator→node bindings.

Modeled directly on super::probe_cache: an IndexMap doubles as the store and the LRU order (index 0 = most-recently-used), the TTL is anchored on Instant at insert and never refreshed on read, and the lock is poison-tolerant.