junobuild_storage/
runtime.rs

1use crate::certification::types::certified::CertifiedAssetHashes;
2use crate::memory::STATE;
3use crate::strategies::StorageCertificateStrategy;
4use crate::types::config::StorageConfig;
5use crate::types::runtime_state::{
6    BatchId, Batches, ChunkId, Chunks, RuntimeState, StorageRuntimeState,
7};
8use crate::types::store::{Asset, Batch, BatchExpiry, Chunk};
9use ic_cdk::api::time;
10use ic_certification::Hash;
11use junobuild_collections::types::core::CollectionKey;
12use junobuild_shared::rate::types::RateConfig;
13use junobuild_shared::rate::utils::increment_and_assert_rate_store;
14use std::collections::HashMap;
15
16// ---------------------------------------------------------
17// Certified assets
18// ---------------------------------------------------------
19
20pub fn init_certified_assets(
21    asset_hashes: &CertifiedAssetHashes,
22    certificate: &impl StorageCertificateStrategy,
23) {
24    // 1. Init all asset in tree
25    STATE.with(|state| {
26        init_certified_assets_impl(asset_hashes, &mut state.borrow_mut().runtime.storage)
27    });
28
29    // 2. Update the root hash and the canister certified data
30    certificate.update_certified_data();
31}
32
33pub fn update_certified_asset(
34    asset: &Asset,
35    config: &StorageConfig,
36    certificate: &impl StorageCertificateStrategy,
37) {
38    // 1. Replace or insert the new asset in tree
39    STATE.with(|state| update_certified_asset_impl(asset, config, &mut state.borrow_mut().runtime));
40
41    // 2. Update the root hash and the canister certified data
42    certificate.update_certified_data();
43}
44
45pub fn delete_certified_asset(asset: &Asset, certificate: &impl StorageCertificateStrategy) {
46    // 1. Remove the asset in tree
47    STATE.with(|state| delete_certified_asset_impl(asset, &mut state.borrow_mut().runtime));
48
49    // 2. Update the root hash and the canister certified data
50    certificate.update_certified_data();
51}
52
53pub fn certified_assets_root_hash() -> Hash {
54    STATE.with(|state| certified_assets_root_hash_impl(&state.borrow().runtime.storage))
55}
56
57fn init_certified_assets_impl(
58    asset_hashes: &CertifiedAssetHashes,
59    storage: &mut StorageRuntimeState,
60) {
61    storage.asset_hashes = asset_hashes.clone();
62}
63
64fn update_certified_asset_impl(asset: &Asset, config: &StorageConfig, runtime: &mut RuntimeState) {
65    runtime.storage.asset_hashes.insert(asset, config);
66}
67
68fn delete_certified_asset_impl(asset: &Asset, runtime: &mut RuntimeState) {
69    runtime.storage.asset_hashes.delete(asset);
70}
71
72fn certified_assets_root_hash_impl(storage: &StorageRuntimeState) -> Hash {
73    storage.asset_hashes.root_hash()
74}
75
76// ---------------------------------------------------------
77// Batch
78// ---------------------------------------------------------
79
80pub fn get_batch(batch_id: &BatchId) -> Option<Batch> {
81    STATE.with(|state| {
82        state
83            .borrow()
84            .runtime
85            .storage
86            .batches
87            .get(batch_id)
88            .cloned()
89    })
90}
91
92pub fn insert_batch(batch_id: &BatchId, batch: Batch) {
93    STATE.with(|state| {
94        insert_batch_impl(
95            batch_id,
96            batch,
97            &mut state.borrow_mut().runtime.storage.batches,
98        )
99    })
100}
101
102pub fn clear_expired_batches() {
103    STATE.with(|state| clear_expired_batches_impl(&mut state.borrow_mut().runtime.storage.batches));
104}
105
106pub fn clear_batch(batch_id: &BatchId, chunk_ids: &[ChunkId]) {
107    STATE.with(|state| {
108        clear_batch_impl(batch_id, chunk_ids, &mut state.borrow_mut().runtime.storage)
109    });
110}
111
112fn insert_batch_impl(batch_id: &BatchId, batch: Batch, batches: &mut Batches) {
113    batches.insert(*batch_id, batch);
114}
115
116fn clear_expired_batches_impl(batches: &mut Batches) {
117    clear_expired_items(batches);
118}
119
120fn clear_expired_items<K, V>(batches: &mut HashMap<K, V>)
121where
122    K: Clone + Eq + std::hash::Hash,
123    V: BatchExpiry,
124{
125    let now = time();
126
127    let expired_ids: Vec<K> = batches
128        .iter()
129        .filter_map(|(id, item)| {
130            if now > item.expires_at() {
131                Some(id.clone())
132            } else {
133                None
134            }
135        })
136        .collect();
137
138    for id in expired_ids {
139        batches.remove(&id);
140    }
141}
142
143fn clear_batch_impl(batch_id: &BatchId, chunk_ids: &[ChunkId], state: &mut StorageRuntimeState) {
144    for chunk_id in chunk_ids.iter() {
145        state.chunks.remove(chunk_id);
146    }
147
148    state.batches.remove(batch_id);
149}
150
151// ---------------------------------------------------------
152// Chunks
153// ---------------------------------------------------------
154
155pub fn get_chunk(chunk_id: &ChunkId) -> Option<Chunk> {
156    STATE.with(|state| {
157        let chunks = state.borrow().runtime.storage.chunks.clone();
158        let chunk = chunks.get(chunk_id);
159        chunk.cloned()
160    })
161}
162
163pub fn clear_expired_chunks() {
164    STATE.with(|state| clear_expired_chunks_impl(&mut state.borrow_mut().runtime.storage));
165}
166
167pub fn insert_chunk(chunk_id: &ChunkId, chunk: Chunk) {
168    STATE.with(|state| {
169        insert_chunk_impl(
170            chunk_id,
171            chunk,
172            &mut state.borrow_mut().runtime.storage.chunks,
173        )
174    })
175}
176
177fn clear_expired_chunks_impl(state: &mut StorageRuntimeState) {
178    let cloned_chunks = state.chunks.clone();
179
180    for (chunk_id, chunk) in cloned_chunks.iter() {
181        if !state.batches.contains_key(&chunk.batch_id) {
182            state.chunks.remove(chunk_id);
183        }
184    }
185}
186
187fn insert_chunk_impl(chunk_id: &ChunkId, chunk: Chunk, chunks: &mut Chunks) {
188    chunks.insert(*chunk_id, chunk);
189}
190
191// ---------------------------------------------------------
192// Rates
193// ---------------------------------------------------------
194
195pub fn increment_and_assert_rate(
196    collection: &CollectionKey,
197    config: &Option<RateConfig>,
198) -> Result<(), String> {
199    STATE.with(|state| {
200        increment_and_assert_rate_store(
201            collection,
202            config,
203            &mut state.borrow_mut().runtime.storage.rate_tokens,
204        )
205    })
206}