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