Skip to main content

ic_memory/runtime/
policy.rs

1use super::{RuntimeBootstrapError, RuntimePolicyError};
2use crate::{
3    AllocationPolicy, AllocationSlotDescriptor, PolicyIdentity, PolicyIdentityError,
4    RuntimeBootstrapPolicy, StableKey,
5    registry::SealedDeclarationSnapshot,
6    slot::{
7        IC_MEMORY_AUTHORITY_OWNER, IC_MEMORY_LEDGER_STABLE_KEY, MemoryManagerRangeAuthorityError,
8    },
9};
10use std::convert::Infallible;
11
12pub(super) fn runtime_bootstrap_error_from_bootstrap<P>(
13    err: crate::BootstrapError<RuntimePolicyError<P>>,
14) -> RuntimeBootstrapError<P> {
15    match err {
16        crate::BootstrapError::Ledger(err) => RuntimeBootstrapError::LedgerCommit(err),
17        crate::BootstrapError::Validation(err) => RuntimeBootstrapError::Validation(err),
18        crate::BootstrapError::Staging(err) => RuntimeBootstrapError::Staging(err),
19    }
20}
21
22pub(super) struct RuntimeMemoryManagerPolicy<'a, P> {
23    pub(super) declarations: &'a SealedDeclarationSnapshot,
24    pub(super) custom_policy: &'a P,
25}
26
27impl<P: AllocationPolicy> AllocationPolicy for RuntimeMemoryManagerPolicy<'_, P> {
28    type Error = RuntimePolicyError<P::Error>;
29
30    fn validate_key(&self, key: &StableKey) -> Result<(), Self::Error> {
31        let authority = self.declaration_authority(key);
32        if authority == IC_MEMORY_AUTHORITY_OWNER {
33            return Ok(());
34        }
35        self.custom_policy
36            .validate_key(key)
37            .map_err(RuntimePolicyError::Custom)
38    }
39
40    fn validate_slot(
41        &self,
42        key: &StableKey,
43        slot: &AllocationSlotDescriptor,
44    ) -> Result<(), Self::Error> {
45        let authority = self.declaration_authority(key);
46        self.validate_runtime_range(authority, slot)?;
47        if authority == IC_MEMORY_AUTHORITY_OWNER {
48            return Ok(());
49        }
50        self.custom_policy
51            .validate_slot(key, slot)
52            .map_err(RuntimePolicyError::Custom)
53    }
54
55    fn validate_reserved_slot(
56        &self,
57        key: &StableKey,
58        slot: &AllocationSlotDescriptor,
59    ) -> Result<(), Self::Error> {
60        let authority = self.declaration_authority(key);
61        self.validate_runtime_range(authority, slot)?;
62        if authority == IC_MEMORY_AUTHORITY_OWNER {
63            return Ok(());
64        }
65        self.custom_policy
66            .validate_reserved_slot(key, slot)
67            .map_err(RuntimePolicyError::Custom)
68    }
69}
70
71impl<P: AllocationPolicy> RuntimeMemoryManagerPolicy<'_, P> {
72    fn declaration_authority(&self, key: &StableKey) -> &str {
73        if key.as_str() == IC_MEMORY_LEDGER_STABLE_KEY {
74            return IC_MEMORY_AUTHORITY_OWNER;
75        }
76        // Bootstrap and diagnostics validate the allocation snapshot from this
77        // same immutable resolved snapshot, so every external key is registered.
78        self.declarations
79            .registered_declaration(key)
80            .expect("validated declaration belongs to the resolved snapshot")
81            .authority()
82    }
83
84    fn validate_runtime_range(
85        &self,
86        authority: &str,
87        slot: &AllocationSlotDescriptor,
88    ) -> Result<(), RuntimePolicyError<P::Error>> {
89        match self
90            .declarations
91            .range_authority()
92            .validate_slot_authority(slot, authority)
93        {
94            // Fixed external claims can defer unclaimed IDs to custom policy
95            // only when no user ranges exist. Claimed IDs always check ownership.
96            Err(MemoryManagerRangeAuthorityError::UnclaimedId { .. })
97                if authority != IC_MEMORY_AUTHORITY_OWNER
98                    && !self.declarations.user_ranges_registered() =>
99            {
100                Ok(())
101            }
102            result => result.map(|_| ()).map_err(RuntimePolicyError::Range),
103        }
104    }
105}
106
107///
108/// GenericRangePolicy
109///
110/// Built-in bootstrap policy used by the no-argument default-runtime helpers.
111/// The runtime enforces registered range ownership and internal reservations;
112/// this policy adds no application-specific restrictions. Passing it directly
113/// to allocation validation outside the runtime does not enforce those ranges.
114///
115/// Use with configured bootstrap when the host does not require a custom
116/// policy. It retains the built-in policy identity and does not authorize
117/// replacing a different policy already bound to the runtime.
118///
119pub struct GenericRangePolicy;
120
121impl AllocationPolicy for GenericRangePolicy {
122    type Error = Infallible;
123
124    fn validate_key(&self, _key: &StableKey) -> Result<(), Self::Error> {
125        Ok(())
126    }
127
128    fn validate_slot(
129        &self,
130        _key: &StableKey,
131        _slot: &AllocationSlotDescriptor,
132    ) -> Result<(), Self::Error> {
133        Ok(())
134    }
135
136    fn validate_reserved_slot(
137        &self,
138        _key: &StableKey,
139        _slot: &AllocationSlotDescriptor,
140    ) -> Result<(), Self::Error> {
141        Ok(())
142    }
143}
144
145impl RuntimeBootstrapPolicy for GenericRangePolicy {
146    fn runtime_bootstrap_identity(&self) -> Result<PolicyIdentity, PolicyIdentityError> {
147        PolicyIdentity::new("ic-memory.noop-policy", 1)
148    }
149}