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