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