ic_memory/runtime/
bootstrap.rs1use super::{
3 BootstrapAdmission, CommittedRuntime, MemoryRuntime, RuntimeBootstrapBinding,
4 RuntimeBootstrapError, RuntimeLifecycle, RuntimeMemory,
5};
6use crate::{
7 LedgerCommitStore, PolicyIdentity, RuntimeBootstrapPolicy, STABLE_CELL_VALUE_OFFSET,
8 SealedDeclarationSnapshot,
9 slot::MEMORY_MANAGER_LEDGER_ID,
10 stable_cell::{EncodedLedgerRecord, decode_stable_cell_commit_store_from_memory},
11};
12use ic_platform::stable_structures::{Cell, Memory};
13
14impl<M: Memory> MemoryRuntime<M> {
15 pub fn bootstrap<P: RuntimeBootstrapPolicy>(
30 &mut self,
31 declarations: &SealedDeclarationSnapshot,
32 pool: &crate::MemoryAllocationPool,
33 policy: &P,
34 ) -> Result<u64, RuntimeBootstrapError<P::Error>> {
35 match &self.lifecycle {
36 RuntimeLifecycle::Unbootstrapped => self.bootstrap_unbootstrapped(
37 declarations,
38 pool,
39 policy,
40 policy.runtime_bootstrap_identity(),
41 ),
42 RuntimeLifecycle::Bootstrapped(_) => self.verify_bootstrap(declarations, pool, policy),
43 }
44 }
45
46 pub fn verify_bootstrap<P: RuntimeBootstrapPolicy>(
55 &self,
56 declarations: &SealedDeclarationSnapshot,
57 pool: &crate::MemoryAllocationPool,
58 policy: &P,
59 ) -> Result<u64, RuntimeBootstrapError<P::Error>> {
60 let committed = self
61 .committed()
62 .map_err(|_| RuntimeBootstrapError::NotBootstrapped)?;
63 committed
64 .binding
65 .validate(declarations, pool, &policy.runtime_bootstrap_identity())?;
66 Ok(committed.generation)
67 }
68
69 pub(super) fn validate_pool_custody(
70 &self,
71 occupied: &[bool; crate::constants::MAX_ALLOCATIONS],
72 pool: &crate::MemoryAllocationPool,
73 ) -> Result<(), super::MemoryResolutionError> {
74 for id in 0..crate::MEMORY_MANAGER_INVALID_ID {
75 if pool.contains(id) && self.memory_size_pages(id) != 0 && !occupied[usize::from(id)] {
76 return Err(super::MemoryResolutionError::UnmanagedAllocation { id });
77 }
78 }
79 Ok(())
80 }
81
82 fn bootstrap_unbootstrapped<P: RuntimeBootstrapPolicy>(
83 &mut self,
84 declarations: &SealedDeclarationSnapshot,
85 pool: &crate::MemoryAllocationPool,
86 policy: &P,
87 policy_identity: PolicyIdentity,
88 ) -> Result<u64, RuntimeBootstrapError<P::Error>> {
89 self.growth.verify()?;
90 let mut memory = self
91 .memory(MEMORY_MANAGER_LEDGER_ID)
92 .expect("governance memory is not publicly leasable");
93 let mut record = decode_stable_cell_commit_store_from_memory(&memory)?;
94 let recovered = record.recover_or_genesis()?;
95 let occupied = recovered.ledger().occupied_slots();
96 self.validate_pool_custody(&occupied, pool)?;
97 if memory.size() == 0 {
98 memory = write_ledger_cell(memory, &LedgerCommitStore::default())?;
101 }
102 let mut admission = BootstrapAdmission::new(recovered.ledger(), declarations, pool);
103 let preparation = policy.prepare_bootstrap(&mut admission);
104 let historical = admission.complete()?;
105 preparation.map_err(RuntimeBootstrapError::AdmissionPolicy)?;
106 let resolved = declarations.resolve(&recovered, &occupied, historical, pool)?;
107 let (generation, resolved) = {
108 let (staged, resolved) = crate::ledger::stage_generation(recovered, resolved, policy)?;
109 record.commit_generation(&staged)?;
110 (staged.current_generation(), resolved)
111 };
112 drop(write_ledger_cell(memory, &record)?);
113 let mut allocations = resolved;
115 allocations.retain(|declaration| {
116 !crate::is_ic_memory_stable_key(declaration.stable_key().as_str())
117 });
118 self.lifecycle = RuntimeLifecycle::Bootstrapped(CommittedRuntime {
119 allocations,
120 generation,
121 binding: RuntimeBootstrapBinding {
122 source: declarations.clone(),
123 policy_identity,
124 pool: pool.clone(),
125 },
126 });
127 Ok(generation)
128 }
129}
130
131impl RuntimeBootstrapBinding {
132 pub(super) fn validate<P>(
133 &self,
134 declarations: &SealedDeclarationSnapshot,
135 pool: &crate::MemoryAllocationPool,
136 policy_identity: &PolicyIdentity,
137 ) -> Result<(), RuntimeBootstrapError<P>> {
138 if &self.source != declarations {
139 return Err(RuntimeBootstrapError::DeclarationSnapshotMismatch);
140 }
141 if &self.pool != pool {
142 return Err(RuntimeBootstrapError::AllocationPoolMismatch);
143 }
144 if &self.policy_identity != policy_identity {
145 return Err(RuntimeBootstrapError::PolicyIdentityMismatch {
146 established: self.policy_identity,
147 requested: *policy_identity,
148 });
149 }
150 Ok(())
151 }
152}
153
154fn write_ledger_cell<M: Memory, P>(
155 memory: RuntimeMemory<M>,
156 record: &LedgerCommitStore,
157) -> Result<RuntimeMemory<M>, RuntimeBootstrapError<P>> {
158 let encoded =
159 EncodedLedgerRecord::new(record).map_err(RuntimeBootstrapError::StableCellLedgerEncode)?;
160 let value_size = encoded.len();
161 let required_bytes = STABLE_CELL_VALUE_OFFSET + value_size as u64;
163 let available_bytes = memory.size().saturating_mul(crate::WASM_PAGE_SIZE_BYTES);
164 if required_bytes > available_bytes {
165 let grow_by = (required_bytes - available_bytes).div_ceil(crate::WASM_PAGE_SIZE_BYTES);
166 memory.grow(grow_by)?;
167 }
168 Ok(Cell::new(memory, encoded).into_memory())
169}