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