Skip to main content

ic_memory/slot/
mod.rs

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}