massa_storage/
lib.rs

1//! Copyright (c) 2022 MASSA LABS <info@massa.net>
2//!
3//! This crate is used to store shared objects (blocks, operations...) across different modules.
4//! The cloneable `Storage` structure has thread-safe shared access to the stored objects.
5//!
6//! The `Storage` structure also has lists of object references held by the current instance of `Storage`.
7//! When no instance of `Storage` claims a reference to a given object anymore, that object is automatically removed from storage.
8
9#![warn(missing_docs)]
10
11mod block_indexes;
12mod endorsement_indexes;
13mod operation_indexes;
14
15#[cfg(test)]
16mod tests;
17
18use block_indexes::BlockIndexes;
19use endorsement_indexes::EndorsementIndexes;
20use massa_models::prehash::{CapacityAllocator, PreHashMap, PreHashSet, PreHashed};
21use massa_models::secure_share::Id;
22use massa_models::{
23    block::SecureShareBlock,
24    block_id::BlockId,
25    endorsement::{EndorsementId, SecureShareEndorsement},
26    operation::{OperationId, SecureShareOperation},
27};
28use operation_indexes::OperationIndexes;
29use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
30use std::fmt::Debug;
31use std::hash::Hash;
32use std::{collections::hash_map, sync::Arc};
33
34/// A storage system for objects (blocks, operations...), shared by various components.
35pub struct Storage {
36    /// global block storage
37    blocks: Arc<RwLock<BlockIndexes>>,
38    /// global operation storage
39    operations: Arc<RwLock<OperationIndexes>>,
40    /// global operation storage
41    endorsements: Arc<RwLock<EndorsementIndexes>>,
42
43    /// global block reference counter
44    block_owners: Arc<RwLock<PreHashMap<BlockId, usize>>>,
45    /// global operation reference counter
46    operation_owners: Arc<RwLock<PreHashMap<OperationId, usize>>>,
47    /// global endorsement reference counter
48    endorsement_owners: Arc<RwLock<PreHashMap<EndorsementId, usize>>>,
49
50    /// locally used block references
51    local_used_blocks: PreHashSet<BlockId>,
52    /// locally used operation references
53    local_used_ops: PreHashSet<OperationId>,
54    /// locally used endorsement references
55    local_used_endorsements: PreHashSet<EndorsementId>,
56}
57
58impl Debug for Storage {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        // TODO format storage
61        f.write_str("")
62    }
63}
64
65impl Clone for Storage {
66    fn clone(&self) -> Self {
67        let mut res = Self::clone_without_refs(self);
68
69        // claim one more user of the op refs
70        Storage::internal_claim_refs(
71            &self.local_used_ops.clone(),
72            &mut res.operation_owners.write(),
73            &mut res.local_used_ops,
74        );
75
76        // claim one more user of the block refs
77        Storage::internal_claim_refs(
78            &self.local_used_blocks.clone(),
79            &mut res.block_owners.write(),
80            &mut res.local_used_blocks,
81        );
82
83        // claim one more user of the endorsement refs
84        Storage::internal_claim_refs(
85            &self.local_used_endorsements.clone(),
86            &mut res.endorsement_owners.write(),
87            &mut res.local_used_endorsements,
88        );
89
90        res
91    }
92}
93
94impl Storage {
95    /// Creates a new `Storage` instance. Must be called only one time in the execution:
96    /// - In the main for the node
97    /// - At the top of the test in tests
98    ///
99    /// All others instances of Storage must be cloned from this one using `clone()` or `clone_without_refs()`.
100    pub fn create_root() -> Storage {
101        Storage {
102            blocks: Default::default(),
103            operations: Default::default(),
104            endorsements: Default::default(),
105            block_owners: Default::default(),
106            operation_owners: Default::default(),
107            endorsement_owners: Default::default(),
108            local_used_blocks: Default::default(),
109            local_used_ops: Default::default(),
110            local_used_endorsements: Default::default(),
111        }
112    }
113
114    /// Clones the object to a new one that has no references
115    pub fn clone_without_refs(&self) -> Self {
116        Self {
117            blocks: self.blocks.clone(),
118            operations: self.operations.clone(),
119            endorsements: self.endorsements.clone(),
120
121            operation_owners: self.operation_owners.clone(),
122            block_owners: self.block_owners.clone(),
123            endorsement_owners: self.endorsement_owners.clone(),
124
125            // do not clone local ref lists
126            local_used_ops: Default::default(),
127            local_used_blocks: Default::default(),
128            local_used_endorsements: Default::default(),
129        }
130    }
131
132    /// replace the current storage with a copy of the other one
133    pub fn replace_with(&mut self, other: &Storage) {
134        // compute the diff between the two storages in terms of operations
135        {
136            let added_ops = other.get_op_refs() - self.get_op_refs();
137            let removed_ops = self.get_op_refs() - other.get_op_refs();
138            {
139                let owners = &mut self.operation_owners.write();
140                // removed objects
141                let orphaned_ids =
142                    Self::internal_drop_refs(&removed_ops, owners, &mut self.local_used_ops);
143                // if there are orphaned objects, remove them from storage
144                if !orphaned_ids.is_empty() {
145                    let mut ops = self.operations.write();
146                    for op_id in orphaned_ids {
147                        ops.remove(&op_id);
148                    }
149                }
150                // claim the added objects
151                Self::internal_claim_refs(&added_ops, owners, &mut self.local_used_ops);
152            }
153        }
154
155        // compute the diff between the two storages in terms of blocks
156        {
157            let added_blocks = other.get_block_refs() - self.get_block_refs();
158            let removed_blocks = self.get_block_refs() - other.get_block_refs();
159            {
160                let owners = &mut self.block_owners.write();
161                // removed objects
162                let orphaned_ids =
163                    Self::internal_drop_refs(&removed_blocks, owners, &mut self.local_used_blocks);
164                // if there are orphaned objects, remove them from storage
165                if !orphaned_ids.is_empty() {
166                    let mut blocks = self.blocks.write();
167                    for block_id in orphaned_ids {
168                        blocks.remove(&block_id);
169                    }
170                }
171                // claim the added objects
172                Self::internal_claim_refs(&added_blocks, owners, &mut self.local_used_blocks);
173            }
174        }
175
176        // compute the diff between the two storages in terms of endorsements
177        {
178            let added_endorsements = other.get_endorsement_refs() - self.get_endorsement_refs();
179            let removed_endorsements = self.get_endorsement_refs() - other.get_endorsement_refs();
180            {
181                let owners = &mut self.endorsement_owners.write();
182                // removed objects
183                let orphaned_ids = Self::internal_drop_refs(
184                    &removed_endorsements,
185                    owners,
186                    &mut self.local_used_endorsements,
187                );
188                // if there are orphaned objects, remove them from storage
189                if !orphaned_ids.is_empty() {
190                    let mut endorsements = self.endorsements.write();
191                    for endorsement_id in orphaned_ids {
192                        endorsements.remove(&endorsement_id);
193                    }
194                }
195                // claim the added objects
196                Self::internal_claim_refs(
197                    &added_endorsements,
198                    owners,
199                    &mut self.local_used_endorsements,
200                );
201            }
202        }
203    }
204
205    /// Efficiently extends the current Storage by consuming the refs of another storage.
206    pub fn extend(&mut self, mut other: Storage) {
207        // Take ownership of `other`'s references.
208        // Objects owned by both require a counter decrement and are handled when `other` is dropped.
209        other
210            .local_used_ops
211            .retain(|id| !self.local_used_ops.insert(*id));
212
213        other
214            .local_used_blocks
215            .retain(|id| !self.local_used_blocks.insert(*id));
216
217        other
218            .local_used_endorsements
219            .retain(|id| !self.local_used_endorsements.insert(*id));
220    }
221
222    /// Efficiently splits off a subset of the reference ownership into a new Storage object.
223    /// Panics if some of the refs are not owned by the source.
224    pub fn split_off(
225        &mut self,
226        blocks: &PreHashSet<BlockId>,
227        operations: &PreHashSet<OperationId>,
228        endorsements: &PreHashSet<EndorsementId>,
229    ) -> Storage {
230        // Make a clone of self, which has no ref ownership.
231        let mut res = self.clone_without_refs();
232
233        // Define the ref ownership of the new Storage as all the listed objects that we managed to remove from `self`.
234        // Note that this does not require updating counters.
235
236        res.local_used_blocks = blocks
237            .iter()
238            .map(|id| {
239                self.local_used_blocks
240                    .take(id)
241                    .expect("split block ref not owned by source")
242            })
243            .collect();
244
245        res.local_used_ops = operations
246            .iter()
247            .map(|id| {
248                self.local_used_ops
249                    .take(id)
250                    .expect("split op ref not owned by source")
251            })
252            .collect();
253
254        res.local_used_endorsements = endorsements
255            .iter()
256            .map(|id| {
257                self.local_used_endorsements
258                    .take(id)
259                    .expect("split endorsement ref not owned by source")
260            })
261            .collect();
262
263        res
264    }
265
266    /// internal helper to locally claim a reference to an object
267    fn internal_claim_refs<IdT: Id + PartialEq + Eq + Hash + PreHashed + Copy>(
268        ids: &PreHashSet<IdT>,
269        owners: &mut RwLockWriteGuard<PreHashMap<IdT, usize>>,
270        local_used_ids: &mut PreHashSet<IdT>,
271    ) {
272        for &id in ids {
273            if local_used_ids.insert(id) {
274                owners.entry(id).and_modify(|v| *v += 1).or_insert(1);
275            }
276        }
277    }
278
279    /// get the block reference ownership
280    pub fn get_block_refs(&self) -> &PreHashSet<BlockId> {
281        &self.local_used_blocks
282    }
283
284    /// Claim block references.
285    /// Returns the set of block refs that were found and claimed.
286    pub fn claim_block_refs(&mut self, ids: &PreHashSet<BlockId>) -> PreHashSet<BlockId> {
287        let mut claimed = PreHashSet::with_capacity(ids.len());
288
289        if ids.is_empty() {
290            return claimed;
291        }
292
293        let owners = &mut self.block_owners.write();
294
295        // check that all IDs are owned
296        claimed.extend(ids.iter().filter(|id| owners.contains_key(id)));
297
298        // effectively add local ownership on the refs
299        Storage::internal_claim_refs(&claimed, owners, &mut self.local_used_blocks);
300
301        claimed
302    }
303
304    fn internal_drop_refs<IdT: Id + PartialEq + Eq + Hash + PreHashed + Copy>(
305        ids: &PreHashSet<IdT>,
306        owners: &mut RwLockWriteGuard<PreHashMap<IdT, usize>>,
307        local_used_ids: &mut PreHashSet<IdT>,
308    ) -> Vec<IdT> {
309        let mut orphaned_ids = Vec::new();
310        for id in ids {
311            if !local_used_ids.remove(id) {
312                // the object was already not referenced locally
313                continue;
314            }
315            match owners.entry(*id) {
316                hash_map::Entry::Occupied(mut occ) => {
317                    let res_count = {
318                        let cnt = occ.get_mut();
319                        *cnt = cnt
320                            .checked_sub(1)
321                            .expect("less than 1 owner on storage object reference drop");
322                        *cnt
323                    };
324                    if res_count == 0 {
325                        orphaned_ids.push(*id);
326                        occ.remove();
327                    }
328                }
329                hash_map::Entry::Vacant(_vac) => {
330                    panic!("missing object in storage on storage object reference drop");
331                }
332            }
333        }
334        orphaned_ids
335    }
336
337    /// Drop block references
338    pub fn drop_block_refs(&mut self, ids: &PreHashSet<BlockId>) {
339        if ids.is_empty() {
340            return;
341        }
342        let mut owners = self.block_owners.write();
343        let orphaned_ids = Self::internal_drop_refs(ids, &mut owners, &mut self.local_used_blocks);
344        // if there are orphaned objects, remove them from storage
345        if !orphaned_ids.is_empty() {
346            let mut blocks = self.blocks.write();
347            for b_id in orphaned_ids {
348                blocks.remove(&b_id);
349            }
350        }
351    }
352
353    /// Store a block
354    /// Note that this also claims a local reference to the block
355    pub fn store_block(&mut self, block: SecureShareBlock) {
356        let id = block.id;
357        let mut owners = self.block_owners.write();
358        let mut blocks = self.blocks.write();
359        blocks.insert(block);
360        // update local reference counters
361        Storage::internal_claim_refs(
362            &vec![id].into_iter().collect(),
363            &mut owners,
364            &mut self.local_used_blocks,
365        );
366    }
367
368    /// Claim operation references.
369    /// Returns the set of operation refs that were found and claimed.
370    pub fn claim_operation_refs(
371        &mut self,
372        ids: &PreHashSet<OperationId>,
373    ) -> PreHashSet<OperationId> {
374        let mut claimed = PreHashSet::with_capacity(ids.len());
375
376        if ids.is_empty() {
377            return claimed;
378        }
379
380        let owners = &mut self.operation_owners.write();
381
382        // check that all IDs are owned
383        claimed.extend(ids.iter().filter(|id| owners.contains_key(id)));
384
385        // effectively add local ownership on the refs
386        Storage::internal_claim_refs(&claimed, owners, &mut self.local_used_ops);
387
388        claimed
389    }
390
391    /// get the operation reference ownership
392    pub fn get_op_refs(&self) -> &PreHashSet<OperationId> {
393        &self.local_used_ops
394    }
395
396    /// Drop local operation references.
397    /// Ignores already-absent refs.
398    pub fn drop_operation_refs(&mut self, ids: &PreHashSet<OperationId>) {
399        if ids.is_empty() {
400            return;
401        }
402        let mut owners = self.operation_owners.write();
403        let orphaned_ids = Self::internal_drop_refs(ids, &mut owners, &mut self.local_used_ops);
404        // if there are orphaned objects, remove them from storage
405        if !orphaned_ids.is_empty() {
406            let mut ops = self.operations.write();
407            for op_id in orphaned_ids {
408                ops.remove(&op_id);
409            }
410        }
411    }
412
413    /// Store operations
414    /// Claims a local reference to the added operation
415    pub fn store_operations(&mut self, operations: Vec<SecureShareOperation>) {
416        if operations.is_empty() {
417            return;
418        }
419        let mut owners = self.operation_owners.write();
420        let mut op_store = self.operations.write();
421        let ids: PreHashSet<OperationId> = operations.iter().map(|op| op.id).collect();
422        for op in operations {
423            op_store.insert(op);
424        }
425        Storage::internal_claim_refs(&ids, &mut owners, &mut self.local_used_ops);
426    }
427
428    /// Gets a read reference to the operations index
429    pub fn read_operations(&self) -> RwLockReadGuard<'_, OperationIndexes> {
430        self.operations.read()
431    }
432
433    /// Gets a read reference to the endorsements index
434    pub fn read_endorsements(&self) -> RwLockReadGuard<'_, EndorsementIndexes> {
435        self.endorsements.read()
436    }
437
438    /// Gets a read reference to the blocks index
439    pub fn read_blocks(&self) -> RwLockReadGuard<'_, BlockIndexes> {
440        self.blocks.read()
441    }
442
443    /// Claim endorsement references.
444    /// Returns the set of operation refs that were found and claimed.
445    pub fn claim_endorsement_refs(
446        &mut self,
447        ids: &PreHashSet<EndorsementId>,
448    ) -> PreHashSet<EndorsementId> {
449        let mut claimed = PreHashSet::with_capacity(ids.len());
450
451        if ids.is_empty() {
452            return claimed;
453        }
454
455        let owners = &mut self.endorsement_owners.write();
456
457        // check that all IDs are owned
458        claimed.extend(ids.iter().filter(|id| owners.contains_key(id)));
459
460        // effectively add local ownership on the refs
461        Storage::internal_claim_refs(&claimed, owners, &mut self.local_used_endorsements);
462        claimed
463    }
464
465    /// get the endorsement reference ownership
466    pub fn get_endorsement_refs(&self) -> &PreHashSet<EndorsementId> {
467        &self.local_used_endorsements
468    }
469
470    /// Drop local endorsement references.
471    /// Ignores already-absent refs.
472    pub fn drop_endorsement_refs(&mut self, ids: &PreHashSet<EndorsementId>) {
473        if ids.is_empty() {
474            return;
475        }
476        let mut owners = self.endorsement_owners.write();
477        let orphaned_ids =
478            Self::internal_drop_refs(ids, &mut owners, &mut self.local_used_endorsements);
479        // if there are orphaned objects, remove them from storage
480        if !orphaned_ids.is_empty() {
481            let mut endorsements = self.endorsements.write();
482            for e_id in orphaned_ids {
483                endorsements.remove(&e_id);
484            }
485        }
486    }
487
488    /// Store endorsements
489    /// Claims local references to the added endorsements
490    pub fn store_endorsements(&mut self, endorsements: Vec<SecureShareEndorsement>) {
491        if endorsements.is_empty() {
492            return;
493        }
494        let mut owners = self.endorsement_owners.write();
495        let mut endo_store = self.endorsements.write();
496        let ids: PreHashSet<EndorsementId> = endorsements.iter().map(|op| op.id).collect();
497        for endorsement in endorsements {
498            endo_store.insert(endorsement);
499        }
500        Storage::internal_claim_refs(&ids, &mut owners, &mut self.local_used_endorsements);
501    }
502}
503
504impl Drop for Storage {
505    /// cleanup on Storage instance drop
506    fn drop(&mut self) {
507        // release all blocks
508        self.drop_block_refs(&self.local_used_blocks.clone());
509
510        // release all ops
511        self.drop_operation_refs(&self.local_used_ops.clone());
512
513        // release all endorsements
514        self.drop_endorsement_refs(&self.local_used_endorsements.clone());
515    }
516}