Skip to main content

ic_memory/slot/
mod.rs

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}