1mod descriptor;
2mod memory_manager;
3mod range_authority;
4
5pub use descriptor::{AllocationSlot, AllocationSlotDescriptor};
6pub use memory_manager::{
7 IC_MEMORY_AUTHORITY_OWNER, IC_MEMORY_AUTHORITY_PURPOSE, IC_MEMORY_LEDGER_LABEL,
8 IC_MEMORY_LEDGER_STABLE_KEY, IC_MEMORY_STABLE_KEY_PREFIX, MEMORY_MANAGER_GOVERNANCE_MAX_ID,
9 MEMORY_MANAGER_INVALID_ID, MEMORY_MANAGER_LEDGER_ID, MEMORY_MANAGER_MAX_ID,
10 MEMORY_MANAGER_MIN_ID, MemoryManagerSlotError, is_ic_memory_stable_key,
11 memory_manager_governance_range, validate_memory_manager_id,
12};
13pub use range_authority::{
14 MemoryManagerAuthorityRecord, MemoryManagerIdRange, MemoryManagerRangeAuthority,
15 MemoryManagerRangeAuthorityError, MemoryManagerRangeError, MemoryManagerRangeMode,
16};
17
18#[cfg(test)]
19mod tests {
20 use super::*;
21
22 #[test]
23 fn memory_manager_default_constructor_rejects_sentinel() {
24 let err = AllocationSlotDescriptor::memory_manager(MEMORY_MANAGER_INVALID_ID)
25 .expect_err("sentinel must fail");
26
27 assert_eq!(
28 err,
29 MemoryManagerSlotError::InvalidMemoryManagerId {
30 id: MEMORY_MANAGER_INVALID_ID
31 }
32 );
33 }
34
35 #[test]
36 fn memory_manager_usable_domain_is_u8_with_255_sentinel() {
37 assert_eq!(MEMORY_MANAGER_MIN_ID, 0);
38 assert_eq!(MEMORY_MANAGER_MAX_ID, 254);
39 assert_eq!(MEMORY_MANAGER_INVALID_ID, 255);
40 assert_eq!(MEMORY_MANAGER_INVALID_ID, u8::MAX);
41
42 AllocationSlotDescriptor::memory_manager(MEMORY_MANAGER_MAX_ID)
43 .expect("254 is the last usable MemoryManager ID");
44 AllocationSlotDescriptor::memory_manager(MEMORY_MANAGER_INVALID_ID)
45 .expect_err("255 is always the unallocated sentinel");
46 }
47
48 #[test]
49 fn memory_manager_id_validates_sentinel() {
50 let slot = AllocationSlotDescriptor::memory_manager(42).expect("usable slot");
51 assert_eq!(slot.memory_manager_id().expect("usable ID"), 42);
52
53 let err = AllocationSlotDescriptor {
54 slot: AllocationSlot::MemoryManagerId(MEMORY_MANAGER_INVALID_ID),
55 }
56 .memory_manager_id()
57 .expect_err("sentinel should fail");
58 assert_eq!(
59 err,
60 MemoryManagerSlotError::InvalidMemoryManagerId {
61 id: MEMORY_MANAGER_INVALID_ID
62 }
63 );
64 }
65
66 #[test]
67 fn memory_manager_range_accepts_usable_ranges() {
68 let range = MemoryManagerIdRange::new(MEMORY_MANAGER_MIN_ID, MEMORY_MANAGER_MAX_ID)
69 .expect("usable full range");
70
71 assert!(range.contains(MEMORY_MANAGER_MIN_ID));
72 assert!(range.contains(MEMORY_MANAGER_MAX_ID));
73 assert!(!range.contains(MEMORY_MANAGER_INVALID_ID));
74 }
75
76 #[test]
77 fn memory_manager_range_all_usable_matches_usable_bounds() {
78 assert_eq!(
79 MemoryManagerIdRange::all_usable(),
80 MemoryManagerIdRange::new(MEMORY_MANAGER_MIN_ID, MEMORY_MANAGER_MAX_ID)
81 .expect("usable full range")
82 );
83 }
84
85 #[test]
86 fn memory_manager_governance_range_is_owned_by_ic_memory() {
87 let range = memory_manager_governance_range();
88
89 assert_eq!(range.start(), MEMORY_MANAGER_MIN_ID);
90 assert_eq!(MEMORY_MANAGER_LEDGER_ID, range.start());
91 assert!(range.contains(MEMORY_MANAGER_LEDGER_ID));
92 assert!(is_ic_memory_stable_key(IC_MEMORY_LEDGER_STABLE_KEY));
93 assert_eq!(IC_MEMORY_AUTHORITY_OWNER, "ic-memory");
94 }
95
96 #[test]
97 fn memory_manager_range_rejects_reversed_bounds() {
98 let err = MemoryManagerIdRange::new(10, 9).expect_err("reversed range");
99
100 assert_eq!(
101 err,
102 MemoryManagerRangeError::InvalidRange { start: 10, end: 9 }
103 );
104 }
105
106 #[test]
107 fn memory_manager_range_rejects_sentinel_bounds() {
108 let err =
109 MemoryManagerIdRange::new(240, MEMORY_MANAGER_INVALID_ID).expect_err("sentinel range");
110
111 assert_eq!(
112 err,
113 MemoryManagerRangeError::InvalidMemoryManagerId {
114 id: MEMORY_MANAGER_INVALID_ID
115 }
116 );
117 }
118
119 #[test]
120 fn memory_manager_range_authority_accepts_non_overlapping_construction() {
121 let authority = MemoryManagerRangeAuthority::new()
122 .reserve(memory_manager_governance_range(), IC_MEMORY_AUTHORITY_OWNER)
123 .expect("ic-memory range")
124 .reserve_ids(10, 99, "framework")
125 .expect("framework range")
126 .allow_ids(100, MEMORY_MANAGER_MAX_ID, "applications")
127 .expect("app range");
128
129 assert_eq!(authority.authorities().len(), 3);
130 assert_eq!(
131 authority.authorities()[0].range,
132 memory_manager_governance_range()
133 );
134 assert_eq!(
135 authority.authorities()[0].mode,
136 MemoryManagerRangeMode::Reserved
137 );
138 assert_eq!(authority.authorities()[1].range.start(), 10);
139 assert_eq!(authority.authorities()[2].range.start(), 100);
140 }
141
142 #[test]
143 fn memory_manager_range_authority_id_bound_builders_reject_invalid_ranges() {
144 let err = MemoryManagerRangeAuthority::new()
145 .allow_ids(100, MEMORY_MANAGER_INVALID_ID, "applications")
146 .expect_err("sentinel must fail");
147
148 assert_eq!(
149 err,
150 MemoryManagerRangeAuthorityError::Range(
151 MemoryManagerRangeError::InvalidMemoryManagerId {
152 id: MEMORY_MANAGER_INVALID_ID
153 }
154 )
155 );
156 }
157
158 #[test]
159 fn memory_manager_range_authority_rejects_overlap() {
160 let err = MemoryManagerRangeAuthority::new()
161 .reserve(
162 MemoryManagerIdRange::new(10, 99).expect("framework range"),
163 "framework",
164 )
165 .expect("framework range")
166 .allow(
167 MemoryManagerIdRange::new(99, 120).expect("overlapping app range"),
168 "applications",
169 )
170 .expect_err("overlap must fail");
171
172 assert_eq!(
173 err,
174 MemoryManagerRangeAuthorityError::OverlappingRanges {
175 existing_start: 10,
176 existing_end: 99,
177 candidate_start: 99,
178 candidate_end: 120,
179 }
180 );
181 }
182
183 #[test]
184 fn memory_manager_range_authority_rejects_invalid_diagnostic_strings() {
185 let err = MemoryManagerRangeAuthority::new()
186 .reserve(
187 MemoryManagerIdRange::new(10, 99).expect("framework range"),
188 "",
189 )
190 .expect_err("empty authority must fail");
191 assert_eq!(
192 err,
193 MemoryManagerRangeAuthorityError::InvalidDiagnosticString {
194 field: "authority",
195 reason: "must not be empty",
196 }
197 );
198
199 let err = MemoryManagerRangeAuthority::new()
200 .allow_with_purpose(
201 MemoryManagerIdRange::new(100, MEMORY_MANAGER_MAX_ID).expect("app range"),
202 "applications",
203 Some("bad\npurpose".to_string()),
204 )
205 .expect_err("control character purpose must fail");
206 assert_eq!(
207 err,
208 MemoryManagerRangeAuthorityError::InvalidDiagnosticString {
209 field: "purpose",
210 reason: "must not contain ASCII control characters",
211 }
212 );
213 }
214
215 #[test]
216 fn memory_manager_range_authority_rejects_sentinel_lookup() {
217 let err = MemoryManagerRangeAuthority::new()
218 .authority_for_id(MEMORY_MANAGER_INVALID_ID)
219 .expect_err("sentinel lookup must fail");
220
221 assert_eq!(
222 err,
223 MemoryManagerRangeAuthorityError::Slot(
224 MemoryManagerSlotError::InvalidMemoryManagerId {
225 id: MEMORY_MANAGER_INVALID_ID
226 }
227 )
228 );
229 }
230
231 #[test]
232 fn memory_manager_range_authority_finds_authority_for_id() {
233 let authority = MemoryManagerRangeAuthority::new()
234 .allow(
235 MemoryManagerIdRange::new(100, MEMORY_MANAGER_MAX_ID).expect("app range"),
236 "applications",
237 )
238 .expect("app range")
239 .reserve(memory_manager_governance_range(), IC_MEMORY_AUTHORITY_OWNER)
240 .expect("ic-memory range");
241
242 let record = authority
243 .authority_for_id(100)
244 .expect("valid ID")
245 .expect("authority record");
246 assert_eq!(record.authority, "applications");
247 assert_eq!(record.mode, MemoryManagerRangeMode::Allowed);
248
249 assert!(
250 authority
251 .authority_for_id(99)
252 .expect("valid unclaimed ID")
253 .is_none()
254 );
255 }
256
257 #[test]
258 fn memory_manager_range_authority_validates_slot_authority() {
259 let authority = MemoryManagerRangeAuthority::new()
260 .reserve(
261 MemoryManagerIdRange::new(10, 99).expect("framework range"),
262 "framework",
263 )
264 .expect("framework range")
265 .allow(
266 MemoryManagerIdRange::new(100, MEMORY_MANAGER_MAX_ID).expect("app range"),
267 "applications",
268 )
269 .expect("app range");
270
271 let record = authority
272 .validate_slot_authority(
273 &AllocationSlotDescriptor::memory_manager(42).expect("framework slot"),
274 "framework",
275 )
276 .expect("framework authority");
277 assert_eq!(record.mode, MemoryManagerRangeMode::Reserved);
278
279 let err = authority
280 .validate_slot_authority(
281 &AllocationSlotDescriptor::memory_manager(42).expect("framework slot"),
282 "applications",
283 )
284 .expect_err("wrong authority must fail");
285 assert_eq!(
286 err,
287 MemoryManagerRangeAuthorityError::AuthorityMismatch {
288 id: 42,
289 expected_authority: "applications".to_string(),
290 actual_authority: "framework".to_string(),
291 }
292 );
293 }
294
295 #[test]
296 fn memory_manager_range_authority_validates_slot_authority_mode() {
297 let authority = MemoryManagerRangeAuthority::new()
298 .reserve(
299 MemoryManagerIdRange::new(10, 99).expect("framework range"),
300 "framework",
301 )
302 .expect("framework range")
303 .allow(
304 MemoryManagerIdRange::new(100, MEMORY_MANAGER_MAX_ID).expect("app range"),
305 "applications",
306 )
307 .expect("app range");
308
309 let record = authority
310 .validate_slot_authority_mode(
311 &AllocationSlotDescriptor::memory_manager(42).expect("framework slot"),
312 "framework",
313 MemoryManagerRangeMode::Reserved,
314 )
315 .expect("framework reserved authority");
316 assert_eq!(record.authority, "framework");
317
318 let err = authority
319 .validate_slot_authority_mode(
320 &AllocationSlotDescriptor::memory_manager(42).expect("framework slot"),
321 "framework",
322 MemoryManagerRangeMode::Allowed,
323 )
324 .expect_err("wrong mode must fail");
325 assert_eq!(
326 err,
327 MemoryManagerRangeAuthorityError::ModeMismatch {
328 id: 42,
329 authority: "framework".to_string(),
330 expected_mode: MemoryManagerRangeMode::Allowed,
331 actual_mode: MemoryManagerRangeMode::Reserved,
332 }
333 );
334 }
335
336 #[test]
337 fn memory_manager_range_authority_validates_id_authority() {
338 let authority = MemoryManagerRangeAuthority::new()
339 .reserve(
340 MemoryManagerIdRange::new(10, 99).expect("framework range"),
341 "framework",
342 )
343 .expect("framework range")
344 .allow(
345 MemoryManagerIdRange::new(100, MEMORY_MANAGER_MAX_ID).expect("app range"),
346 "applications",
347 )
348 .expect("app range");
349
350 assert_eq!(
351 authority
352 .validate_id_authority(100, "applications")
353 .expect("application authority")
354 .mode,
355 MemoryManagerRangeMode::Allowed
356 );
357 assert_eq!(
358 authority
359 .validate_id_authority_mode(42, "framework", MemoryManagerRangeMode::Reserved)
360 .expect("framework reserved authority")
361 .range,
362 MemoryManagerIdRange::new(10, 99).expect("framework range")
363 );
364
365 let err = authority
366 .validate_id_authority_mode(100, "applications", MemoryManagerRangeMode::Reserved)
367 .expect_err("wrong mode must fail");
368 assert_eq!(
369 err,
370 MemoryManagerRangeAuthorityError::ModeMismatch {
371 id: 100,
372 authority: "applications".to_string(),
373 expected_mode: MemoryManagerRangeMode::Reserved,
374 actual_mode: MemoryManagerRangeMode::Allowed,
375 }
376 );
377 }
378
379 #[test]
380 fn memory_manager_range_authority_reports_authority_mismatch_before_mode_mismatch() {
381 let authority = MemoryManagerRangeAuthority::new()
382 .allow(
383 MemoryManagerIdRange::new(100, MEMORY_MANAGER_MAX_ID).expect("app range"),
384 "applications",
385 )
386 .expect("app range");
387
388 let err = authority
389 .validate_id_authority_mode(100, "framework", MemoryManagerRangeMode::Reserved)
390 .expect_err("authority mismatch must be distinct");
391 assert_eq!(
392 err,
393 MemoryManagerRangeAuthorityError::AuthorityMismatch {
394 id: 100,
395 expected_authority: "framework".to_string(),
396 actual_authority: "applications".to_string(),
397 }
398 );
399 }
400
401 #[test]
402 fn memory_manager_range_authority_preserves_reserve_and_allow_modes() {
403 let authority = MemoryManagerRangeAuthority::new()
404 .reserve(
405 MemoryManagerIdRange::new(10, 99).expect("framework range"),
406 "framework",
407 )
408 .expect("framework range")
409 .allow(
410 MemoryManagerIdRange::new(100, MEMORY_MANAGER_MAX_ID).expect("app range"),
411 "applications",
412 )
413 .expect("app range");
414
415 assert_eq!(
416 authority.authorities()[0].mode,
417 MemoryManagerRangeMode::Reserved
418 );
419 assert_eq!(
420 authority.authorities()[1].mode,
421 MemoryManagerRangeMode::Allowed
422 );
423 }
424
425 #[test]
426 fn memory_manager_range_authority_validates_complete_coverage() {
427 let authority = MemoryManagerRangeAuthority::new()
428 .reserve(memory_manager_governance_range(), IC_MEMORY_AUTHORITY_OWNER)
429 .expect("ic-memory range")
430 .reserve(
431 MemoryManagerIdRange::new(10, 99).expect("framework range"),
432 "framework",
433 )
434 .expect("framework range")
435 .allow(
436 MemoryManagerIdRange::new(100, MEMORY_MANAGER_MAX_ID).expect("app range"),
437 "applications",
438 )
439 .expect("app range");
440
441 authority
442 .validate_complete_coverage(
443 MemoryManagerIdRange::new(MEMORY_MANAGER_MIN_ID, MEMORY_MANAGER_MAX_ID)
444 .expect("full range"),
445 )
446 .expect("complete coverage");
447 }
448
449 #[test]
450 fn memory_manager_range_authority_rejects_complete_coverage_gaps() {
451 let authority = MemoryManagerRangeAuthority::new()
452 .reserve(memory_manager_governance_range(), IC_MEMORY_AUTHORITY_OWNER)
453 .expect("ic-memory range")
454 .allow(
455 MemoryManagerIdRange::new(100, MEMORY_MANAGER_MAX_ID).expect("app range"),
456 "applications",
457 )
458 .expect("app range");
459
460 let err = authority
461 .validate_complete_coverage(
462 MemoryManagerIdRange::new(MEMORY_MANAGER_MIN_ID, MEMORY_MANAGER_MAX_ID)
463 .expect("full range"),
464 )
465 .expect_err("coverage gap must fail");
466 assert_eq!(
467 err,
468 MemoryManagerRangeAuthorityError::MissingCoverage { start: 10, end: 99 }
469 );
470
471 let err = MemoryManagerRangeAuthority::new()
472 .validate_complete_coverage(
473 MemoryManagerIdRange::new(MEMORY_MANAGER_MIN_ID, MEMORY_MANAGER_MAX_ID)
474 .expect("full range"),
475 )
476 .expect_err("empty coverage must fail");
477 assert_eq!(
478 err,
479 MemoryManagerRangeAuthorityError::MissingCoverage {
480 start: MEMORY_MANAGER_MIN_ID,
481 end: MEMORY_MANAGER_MAX_ID,
482 }
483 );
484 }
485
486 #[test]
487 fn memory_manager_range_authority_rejects_complete_coverage_outside_target() {
488 let authority = MemoryManagerRangeAuthority::new()
489 .reserve(memory_manager_governance_range(), IC_MEMORY_AUTHORITY_OWNER)
490 .expect("ic-memory range")
491 .reserve(
492 MemoryManagerIdRange::new(10, 99).expect("framework range"),
493 "framework",
494 )
495 .expect("framework range");
496
497 let err = authority
498 .validate_complete_coverage(MemoryManagerIdRange::new(10, 99).expect("target range"))
499 .expect_err("outside range must fail");
500 assert_eq!(
501 err,
502 MemoryManagerRangeAuthorityError::RangeOutsideCoverageTarget {
503 start: MEMORY_MANAGER_MIN_ID,
504 end: MEMORY_MANAGER_GOVERNANCE_MAX_ID,
505 target_start: 10,
506 target_end: 99,
507 }
508 );
509 }
510
511 #[test]
512 fn memory_manager_range_authority_from_records_sorts_and_validates() {
513 let err = MemoryManagerRangeAuthority::from_records(vec![MemoryManagerAuthorityRecord {
514 range: MemoryManagerIdRange::new(10, 99).expect("framework range"),
515 authority: String::new(),
516 mode: MemoryManagerRangeMode::Reserved,
517 purpose: None,
518 }])
519 .expect_err("empty authority must fail");
520 assert_eq!(
521 err,
522 MemoryManagerRangeAuthorityError::InvalidDiagnosticString {
523 field: "authority",
524 reason: "must not be empty",
525 }
526 );
527
528 let authority = MemoryManagerRangeAuthority::from_records(vec![
529 MemoryManagerAuthorityRecord {
530 range: MemoryManagerIdRange::new(100, MEMORY_MANAGER_MAX_ID).expect("app range"),
531 authority: "applications".to_string(),
532 mode: MemoryManagerRangeMode::Allowed,
533 purpose: Some("application stable stores".to_string()),
534 },
535 MemoryManagerAuthorityRecord {
536 range: memory_manager_governance_range(),
537 authority: IC_MEMORY_AUTHORITY_OWNER.to_string(),
538 mode: MemoryManagerRangeMode::Reserved,
539 purpose: Some(IC_MEMORY_AUTHORITY_PURPOSE.to_string()),
540 },
541 ])
542 .expect("records");
543
544 assert_eq!(
545 authority.authorities()[0].authority,
546 IC_MEMORY_AUTHORITY_OWNER
547 );
548 assert_eq!(authority.authorities()[1].authority, "applications");
549 }
550
551 #[test]
552 fn memory_manager_authority_record_constructor_validates_metadata() {
553 let record = MemoryManagerAuthorityRecord::new(
554 MemoryManagerIdRange::new(10, 99).expect("framework range"),
555 "framework",
556 MemoryManagerRangeMode::Reserved,
557 Some("framework-owned stores".to_string()),
558 )
559 .expect("authority record");
560
561 assert_eq!(record.authority, "framework");
562 assert_eq!(record.purpose.as_deref(), Some("framework-owned stores"));
563
564 let err = MemoryManagerAuthorityRecord::new(
565 MemoryManagerIdRange::new(10, 99).expect("framework range"),
566 "",
567 MemoryManagerRangeMode::Reserved,
568 None,
569 )
570 .expect_err("empty authority must fail");
571
572 assert_eq!(
573 err,
574 MemoryManagerRangeAuthorityError::InvalidDiagnosticString {
575 field: "authority",
576 reason: "must not be empty",
577 }
578 );
579 }
580
581 #[test]
582 fn memory_manager_range_authority_from_records_rejects_overlap() {
583 let err = MemoryManagerRangeAuthority::from_records(vec![
584 MemoryManagerAuthorityRecord {
585 range: MemoryManagerIdRange::new(10, 99).expect("framework range"),
586 authority: "framework".to_string(),
587 mode: MemoryManagerRangeMode::Reserved,
588 purpose: None,
589 },
590 MemoryManagerAuthorityRecord {
591 range: MemoryManagerIdRange::new(90, 120).expect("overlap range"),
592 authority: "applications".to_string(),
593 mode: MemoryManagerRangeMode::Allowed,
594 purpose: None,
595 },
596 ])
597 .expect_err("overlap must fail");
598
599 assert_eq!(
600 err,
601 MemoryManagerRangeAuthorityError::OverlappingRanges {
602 existing_start: 10,
603 existing_end: 99,
604 candidate_start: 90,
605 candidate_end: 120,
606 }
607 );
608 }
609
610 #[test]
611 fn memory_manager_range_authority_diagnostic_export_is_stable() {
612 let authority = MemoryManagerRangeAuthority::new()
613 .allow_with_purpose(
614 MemoryManagerIdRange::new(100, MEMORY_MANAGER_MAX_ID).expect("app range"),
615 "applications",
616 Some("application stable stores".to_string()),
617 )
618 .expect("app range")
619 .reserve_with_purpose(
620 memory_manager_governance_range(),
621 IC_MEMORY_AUTHORITY_OWNER,
622 Some(IC_MEMORY_AUTHORITY_PURPOSE.to_string()),
623 )
624 .expect("ic-memory range");
625
626 assert_eq!(
627 authority.authorities(),
628 vec![
629 MemoryManagerAuthorityRecord {
630 range: memory_manager_governance_range(),
631 authority: IC_MEMORY_AUTHORITY_OWNER.to_string(),
632 mode: MemoryManagerRangeMode::Reserved,
633 purpose: Some(IC_MEMORY_AUTHORITY_PURPOSE.to_string()),
634 },
635 MemoryManagerAuthorityRecord {
636 range: MemoryManagerIdRange::new(100, MEMORY_MANAGER_MAX_ID)
637 .expect("app range"),
638 authority: "applications".to_string(),
639 mode: MemoryManagerRangeMode::Allowed,
640 purpose: Some("application stable stores".to_string()),
641 },
642 ]
643 .as_slice()
644 );
645 }
646}