massa_storage/
block_indexes.rs

1use std::{collections::btree_map, collections::hash_map, collections::BTreeMap, ops::RangeBounds};
2
3use massa_models::{
4    address::Address,
5    block::SecureShareBlock,
6    block_id::BlockId,
7    endorsement::EndorsementId,
8    operation::OperationId,
9    prehash::{PreHashMap, PreHashSet},
10    slot::Slot,
11};
12
13/// Container for all blocks and different indexes.
14/// Note: The structure can evolve and store more indexes.
15#[derive(Default)]
16pub struct BlockIndexes {
17    /// Blocks structure container
18    blocks: PreHashMap<BlockId, Box<SecureShareBlock>>,
19    /// Structure mapping creators with the created blocks
20    index_by_creator: PreHashMap<Address, PreHashSet<BlockId>>,
21    /// Structure mapping slot with their block id
22    index_by_slot: BTreeMap<Slot, PreHashSet<BlockId>>,
23    /// Structure mapping operation id with ids of blocks they are contained in
24    index_by_op: PreHashMap<OperationId, PreHashSet<BlockId>>,
25    /// Structure mapping endorsement id with ids of blocks they are contained in
26    index_by_endorsement: PreHashMap<EndorsementId, PreHashSet<BlockId>>,
27}
28
29impl BlockIndexes {
30    /// Insert a block and populate the indexes.
31    /// Arguments:
32    /// - block: the block to insert
33    pub(crate) fn insert(&mut self, block: SecureShareBlock) {
34        if let hash_map::Entry::Vacant(vac) = self.blocks.entry(block.id) {
35            let block = vac.insert(Box::new(block));
36            // update creator index
37            self.index_by_creator
38                .entry(block.content_creator_address)
39                .or_default()
40                .insert(block.id);
41
42            // update slot index
43            self.index_by_slot
44                .entry(block.content.header.content.slot)
45                .or_default()
46                .insert(block.id);
47
48            // update index_by_op
49            for op in &block.content.operations {
50                self.index_by_op.entry(*op).or_default().insert(block.id);
51            }
52
53            // update index_by_endorsement
54            for ed in &block.content.header.content.endorsements {
55                self.index_by_endorsement
56                    .entry(ed.id)
57                    .or_default()
58                    .insert(block.id);
59            }
60
61            massa_metrics::set_blocks_counter(self.blocks.len());
62        }
63    }
64
65    /// Remove a block, remove from the indexes and do some clean-up in indexes if necessary.
66    /// Arguments:
67    /// * `block_id`: the block id to remove
68    pub(crate) fn remove(&mut self, block_id: &BlockId) -> Option<Box<SecureShareBlock>> {
69        if let Some(b) = self.blocks.remove(block_id) {
70            // update creator index
71            if let hash_map::Entry::Occupied(mut occ) =
72                self.index_by_creator.entry(b.content_creator_address)
73            {
74                occ.get_mut().remove(&b.id);
75                if occ.get().is_empty() {
76                    occ.remove();
77                }
78            }
79
80            // update slot index
81            if let btree_map::Entry::Occupied(mut occ) =
82                self.index_by_slot.entry(b.content.header.content.slot)
83            {
84                occ.get_mut().remove(&b.id);
85                if occ.get().is_empty() {
86                    occ.remove();
87                }
88            }
89
90            // update index_by_op
91            for op in &b.content.operations {
92                if let hash_map::Entry::Occupied(mut occ) = self.index_by_op.entry(*op) {
93                    occ.get_mut().remove(&b.id);
94                    if occ.get().is_empty() {
95                        occ.remove();
96                    }
97                }
98            }
99
100            // update index_by_endorsement
101            for ed in &b.content.header.content.endorsements {
102                if let hash_map::Entry::Occupied(mut occ) = self.index_by_endorsement.entry(ed.id) {
103                    occ.get_mut().remove(&b.id);
104                    if occ.get().is_empty() {
105                        occ.remove();
106                    }
107                }
108            }
109            massa_metrics::set_blocks_counter(self.blocks.len());
110            return Some(b);
111        }
112        None
113    }
114
115    /// Get a block reference by its ID
116    /// Arguments:
117    /// - id: ID of the block to retrieve
118    ///
119    /// Returns:
120    /// - a reference to the block, or None if not found
121    pub fn get(&self, id: &BlockId) -> Option<&SecureShareBlock> {
122        self.blocks.get(id).map(|v| v.as_ref())
123    }
124
125    /// Checks whether a block exists in global storage.
126    pub fn contains(&self, id: &BlockId) -> bool {
127        self.blocks.contains_key(id)
128    }
129
130    /// Get the block ids created by an address.
131    /// Arguments:
132    /// - address: the address to get the blocks created by
133    ///
134    /// Returns:
135    /// - a reference to the block ids created by the address
136    pub fn get_blocks_created_by(&self, address: &Address) -> Option<&PreHashSet<BlockId>> {
137        self.index_by_creator.get(address)
138    }
139
140    /// Get the block ids of the blocks at a given slot.
141    /// Arguments:
142    /// - slot: the slot to get the block id of
143    ///
144    /// Returns:
145    /// - the block ids of the blocks at the slot if any, None otherwise
146    pub fn get_blocks_by_slot(&self, slot: &Slot) -> Option<&PreHashSet<BlockId>> {
147        self.index_by_slot.get(slot)
148    }
149
150    /// Aggregate block IDs by slot range.
151    /// Arguments:
152    /// - slot_range: the slot range of interest
153    ///
154    /// Returns:
155    /// - a copy of the block ids of the blocks within the slot range
156    pub fn aggregate_blocks_by_slot_range<R>(&self, slot_range: R) -> PreHashSet<BlockId>
157    where
158        R: RangeBounds<Slot>,
159    {
160        self.index_by_slot.range(slot_range).fold(
161            PreHashSet::default(),
162            |mut acc: PreHashSet<BlockId>, (_, v)| {
163                acc.extend(v);
164                acc
165            },
166        )
167    }
168
169    /// Get the block ids of the blocks containing a given operation.
170    /// Arguments:
171    /// - id: the ID of the operation
172    ///
173    /// Returns:
174    /// - the block ids containing the operation if any, None otherwise
175    pub fn get_blocks_by_operation(&self, id: &OperationId) -> Option<&PreHashSet<BlockId>> {
176        self.index_by_op.get(id)
177    }
178
179    /// Get the block ids of the blocks containing a given endorsement.
180    /// Arguments:
181    /// - id: the ID of the endorsement
182    ///
183    /// Returns:
184    /// - the block ids containing the endorsement if any, None otherwise
185    pub fn get_blocks_by_endorsement(&self, id: &EndorsementId) -> Option<&PreHashSet<BlockId>> {
186        self.index_by_endorsement.get(id)
187    }
188}