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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
//! Range-keyed block coverage bitmap (ADR 039-adjacent partial-holder
//! discovery).
//!
//! A blob is divided into fixed-size [`DISCOVERY_BLOCK_BYTES`] "discovery
//! blocks" (64 MiB each); [`Coverage`] is a bitmap over those block indices
//! recording which blocks a holder can serve. This is the wire-level
//! granularity partial-holder discovery advertises and queries at — coarse
//! enough that a 64 MiB blob (this codebase's common case, #1164) still fits
//! in one bit, fine enough that a multi-GB blob's holders can be told apart
//! by which slice they actually hold.
//!
//! `Coverage` itself only carries the bitmap; deriving one from a node's
//! actual cache state (or from origin capability) is the caller's job — see
//! `decdn_cache::CacheEngine::coverage` for the cached-block derivation.
use ;
/// Size of one discovery block: 64 MiB.
///
/// Blob byte ranges are bucketed into blocks of this size for coverage
/// bitmaps — see [`num_blocks`] and [`Coverage`].
pub const DISCOVERY_BLOCK_BYTES: u64 = 64 << 20;
/// Largest blob the discovery layer describes on the wire: 100 GiB.
///
/// This is a protocol ceiling that sizes the coverage-bitmap bound
/// ([`MAX_COVERAGE_BYTES`]), not a per-node store limit — `max_blob_size_mb`
/// is the separate, operator-tunable cache cap. A blob larger than this spans
/// more discovery blocks than a well-formed [`Coverage`] can name on the wire,
/// so it is not partial-holder-discoverable as a single blob. A blob this large
/// is published as a chunked bundle of smaller content-addressed chunk-blobs
/// (each independently discoverable) rather than one monolithic blob, so the
/// ceiling never rejects a legitimate advertisement.
pub const MAX_DISCOVERABLE_BLOB_BYTES: u64 = 100 << 30;
/// Upper bound, in bytes, on a [`Coverage`] bitmap decoded from untrusted wire.
///
/// A well-formed bitmap for a [`MAX_DISCOVERABLE_BLOB_BYTES`] blob is this many
/// bytes: bit-packed at 8 blocks per byte over the fixed 64 MiB
/// [`DISCOVERY_BLOCK_BYTES`] (never the `test-support` overridable size, so the
/// bound is a stable security constant). A frame whose `Coverage` is longer
/// names more discovery blocks than a 100 GiB blob spans — beyond what
/// partial-holder discovery represents — and is rejected at decode. This is a
/// wire representability bound, not a serving cap: a node may still be
/// configured to hold and serve a larger blob, it just cannot advertise partial
/// coverage for one over the DHT.
///
/// Without this cap a bonded publisher could pin arbitrary receiver memory:
/// coverage length is otherwise bounded only by the 16 MiB frame, and the
/// per-publisher record quota multiplies it (stored memory ≈ records × bitmap).
/// The cap makes an oversized bitmap unrepresentable rather than defended after
/// the fact. For 100 GiB it is 200 bytes.
pub const MAX_COVERAGE_BYTES: usize = MAX_DISCOVERABLE_BLOB_BYTES
.div_ceil
.div_ceil as usize;
/// The active discovery-block size in bytes.
///
/// Production reads the fixed [`DISCOVERY_BLOCK_BYTES`] constant — this
/// accessor inlines to it, so the block geometry is byte-identical to naming
/// the constant directly. Test builds that enable the `test-support` feature
/// get an overridable value instead (see
/// `override_discovery_block_bytes_for_test`), so a loopback test can drive
/// the block-spanning planner with tiny blocks and tiny blobs rather than the
/// 64 MiB a real two-block blob would need.
pub const
/// Number of [`discovery_block_bytes`] blocks a blob of `total_bytes` spans.
///
/// The empty blob spans zero blocks (`num_blocks(0) == 0`), not one — an
/// empty [`Coverage`] over it is trivially complete.
// a blob would need to be ~256 EiB to truncate here
pub const
/// The active discovery-block size in bytes — the `test-support` twin whose
/// value a test can override. Defaults to [`DISCOVERY_BLOCK_BYTES`] until
/// [`override_discovery_block_bytes_for_test`] sets it.
/// Number of [`discovery_block_bytes`] blocks a blob of `total_bytes` spans —
/// the `test-support` twin, which reads the overridable block size rather than
/// the constant. Same arithmetic as the production `const fn`.
// a blob would need to be ~256 EiB to truncate here
/// The overridable discovery-block size, compiled only under `test-support`.
/// A live override of the discovery-block size for the duration of the returned
/// guard (test-support only).
///
/// While the guard is held, [`discovery_block_bytes`] and [`num_blocks`] report
/// `bytes` instead of [`DISCOVERY_BLOCK_BYTES`], so the block-spanning planner
/// (`decdn_client::plan_covered_runs`) and the node's coverage-union probe
/// gather split a tiny blob into several blocks. Dropping the guard restores the
/// production default.
///
/// The override is process-global, so it assumes the one-process-per-test
/// isolation `cargo nextest run` gives; two tests that override it concurrently
/// in one process (plain `cargo test`) would race.
/// Restores the production discovery-block size when dropped (test-support only).
/// A bitmap over discovery-block indices: bit `i` records whether block `i`
/// (bytes `[i * DISCOVERY_BLOCK_BYTES, (i + 1) * DISCOVERY_BLOCK_BYTES)`) is
/// covered.
///
/// Bit-packed as `Vec<u8>`: bit `i` lives at byte `i / 8`, bit position
/// `i % 8` (little-endian within the byte). Serializes over postcard as a
/// length-prefixed byte vector — cheap on the wire and trivial to union/merge
/// byte-wise if a future caller needs to.
///
/// Carries no `num_blocks` field. A `Coverage` built locally (via
/// [`Self::from_block_indices`] / [`Self::full`]) leaves the trailing partial
/// byte's unused high bits zero. A `Coverage` **deserialized from untrusted
/// wire** does not: the byte length only rounds the block count up to the next
/// multiple of 8, and nothing on the wire pins the exact count, so a peer may
/// set spurious high bits in that trailing byte. [`Self::covers`] and
/// [`Self::covered_blocks`] report the raw bits as-is, so such a bit makes
/// `covers(i)` true for an `i` past the blob's real block count. Consumers MUST
/// clamp interpretation to the blob's own [`num_blocks`]`(total_bytes)` — query
/// only `i < num_blocks`, and ignore any [`Self::covered_blocks`] index beyond
/// it. The signed size, not the bitmap, is the authority on how many blocks
/// exist.
/// Bounds a wire `Coverage` bitmap to [`MAX_COVERAGE_BYTES`] at decode.
///
/// A longer bitmap names more blocks than a [`MAX_DISCOVERABLE_BLOB_BYTES`] blob
/// spans — beyond what partial-holder discovery represents — so it is rejected
/// rather than stored, and an untrusted peer cannot amplify one record into
/// arbitrary receiver memory. This bounds what the wire represents, not what a
/// node may serve. Decoding the
/// bytes before the length check is safe: the enclosing 16 MiB frame cap and
/// serde's cautious capacity hint bound the allocation independently of the
/// wire length prefix, so the prefix is never trusted for sizing (#845).