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        if authority == IC_MEMORY_AUTHORITY_OWNER || self.declarations.user_ranges_registered() {
90            self.declarations
91                .range_authority()
92                .validate_slot_authority(slot, authority)?;
93            return Ok(());
94        }
95
96        let id = slot
97            .memory_manager_id()
98            .map_err(MemoryManagerRangeAuthorityError::Slot)?;
99        if self
100            .declarations
101            .range_authority()
102            .authority_for_id(id)?
103            .is_some()
104        {
105            self.declarations
106                .range_authority()
107                .validate_slot_authority(slot, authority)?;
108        }
109        Ok(())
110    }
111}
112
113///
114/// GenericRangePolicy
115///
116/// Built-in bootstrap policy used by the no-argument default-runtime helpers.
117/// The runtime enforces registered range ownership and internal reservations;
118/// this policy adds no application-specific restrictions. Passing it directly
119/// to allocation validation outside the runtime does not enforce those ranges.
120///
121/// Use with configured bootstrap when the host does not require a custom
122/// policy. It retains the built-in policy identity and does not authorize
123/// replacing a different policy already bound to the runtime.
124///
125pub struct GenericRangePolicy;
126
127impl AllocationPolicy for GenericRangePolicy {
128    type Error = Infallible;
129
130    fn validate_key(&self, _key: &StableKey) -> Result<(), Self::Error> {
131        Ok(())
132    }
133
134    fn validate_slot(
135        &self,
136        _key: &StableKey,
137        _slot: &AllocationSlotDescriptor,
138    ) -> Result<(), Self::Error> {
139        Ok(())
140    }
141
142    fn validate_reserved_slot(
143        &self,
144        _key: &StableKey,
145        _slot: &AllocationSlotDescriptor,
146    ) -> Result<(), Self::Error> {
147        Ok(())
148    }
149}
150
151impl RuntimeBootstrapPolicy for GenericRangePolicy {
152    fn runtime_bootstrap_identity(&self) -> Result<PolicyIdentity, PolicyIdentityError> {
153        PolicyIdentity::new("ic-memory.noop-policy", 1)
154    }
155}