1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! On-chain origin-directory fallback (ADR 022 §FIND\_VALUE Flow
//! "Origin discovery" and §Bootstrap "the on-chain origin directory
//! provides the deterministic fallback").
//!
//! When a DHT lookup for a `namespaceId != 0` request returns no providers, or
//! the probed holders do not cover every block of the blob, the requester reads
//! the deterministic chain-derived directory keyed on the request's namespace:
//!
//! `OriginAssignment.getOrigins(namespace_id)` → `[operator...]`
//! → `CapacityBond.nodeIdOf(operator) → (NodeId, active)`
//! → filter `active == true`
//!
//! The bare hash carries no origin information (ADR 002 §Retrieval by
//! namespace): a request supplies the namespace its content is published under,
//! and origin authorization is a namespace-level property. `namespaceId == 0`
//! has no authorized origins, so the directory resolves nothing for it.
//!
//! This trait abstracts the directory so callers consume a single
//! `lookup_origins(namespace_id) -> Vec<NodeId>` call without caring whether the
//! implementation resolves from chain (production), from a fixed in-memory map
//! (tests), or resolves nothing at all (the fallback installed when the chain
//! contracts aren't configured).
//!
//! Three implementations exist:
//!
//! - [`crate::dht::ChainOriginDirectory`] — the production path, and the only
//! one that can resolve anything. It yields the same candidate set the chain
//! above specifies, but does not perform that literal call sequence: the
//! `namespace → operators` view is fetched lazily on a cold-namespace miss
//! and cached with a split TTL, and the `active` filter is applied from the
//! shared `StakerSet` at lookup time rather than taken from the `nodeIdOf`
//! tuple. A live cache hit resolves with no RPC; a miss issues one
//! on-demand `getOrigins` call.
//! - [`EmptyOriginDirectory`] (this module) — resolves nothing. What the
//! runtime installs when the chain contracts aren't configured.
//! - [`StaticOriginDirectory`] (this module) — a fixed in-memory map, for tests
//! and any caller wiring a known origin set without a live chain. No config
//! key populates it; it has no production call site.
//!
//! The runtime picks between them at bring-up: it constructs a
//! `ChainOriginDirectory` when `blockchain.origin_assignment_address` is set,
//! and otherwise installs an [`EmptyOriginDirectory`] (see `crate::runtime`).
//! With that fallback in place every lookup miss yields no origin candidates —
//! see [`EmptyOriginDirectory`] for what that means for each consumer.
use HashMap;
use U256;
use crateNodeId;
/// Read-only view of the on-chain origin directory, keyed by the namespace a
/// request is published under (ADR 002 §Retrieval by namespace).
///
/// Implementations MUST be cheap to clone (typically `Arc<inner>`); consumers
/// hold a long-lived `Arc<dyn OriginDirectory>` and invoke `lookup_origins` on
/// a lookup miss, or on a ranged pull whose probed holders do not cover every
/// block (ADR 022 §FIND\_VALUE Flow). Both are uncommon: under normal operation
/// the DHT holds an entry for every actively-serving authorized origin.
///
/// `async` because the chain-backed implementation may issue one on-demand
/// `getOrigins` RPC on a cold-namespace miss; `Empty`/`Static` resolve
/// synchronously.
/// [`OriginDirectory`] that resolves nothing.
///
/// The shape the runtime installs when the on-chain `OriginAssignment` address
/// is not configured. The chain-backed [`crate::dht::ChainOriginDirectory`] is
/// the only directory with a production population path (ADR 022 §FIND\_VALUE
/// Flow describes the fallback as the *on-chain* origin directory throughout),
/// so "no chain config" means "no origin directory" rather than "an
/// operator-supplied one".
///
/// Every consumer degrades to a hard deny on a node without a chain address:
/// the FIND\_VALUE fallback resolves nothing, so a `namespaceId != 0` DHT miss
/// reports the blob unavailable on the network.
;
/// Static, in-memory [`OriginDirectory`] over a fixed namespace → origin-`NodeId`
/// map. Used by tests and any caller wiring a known origin set without a live
/// chain.
///
/// No config key populates this map and the runtime never constructs one — the
/// chain-backed [`crate::dht::ChainOriginDirectory`] is the production path,
/// and [`EmptyOriginDirectory`] is what stands in when it is unconfigured.
/// Mirrors [`crate::dht::StaticNodeAddressDirectory`], the same `Chain*` /
/// `Static*` pairing one module over.