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