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_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}