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
use std::collections::BTreeSet;
use massa_consensus_exports::{
block_status::{BlockStatus, DiscardReason, HeaderOrBlock},
error::ConsensusError,
};
use massa_logging::massa_trace;
use massa_models::{
block_header::SecuredHeader, block_id::BlockId, denunciation::DenunciationPrecursor, slot::Slot,
};
use massa_storage::Storage;
use massa_time::MassaTime;
use tracing::debug;
use super::ConsensusState;
impl ConsensusState {
/// Register a block header in the graph. Ignore genesis hashes.
///
/// # Arguments:
/// * `block_id`: the block id
/// * `header`: the header to register
/// * `current_slot`: the slot when this function is called
///
/// # Returns:
/// Success or error if the header is invalid or too old
pub fn register_block_header(
&mut self,
block_id: BlockId,
header: SecuredHeader,
current_slot: Option<Slot>,
) -> Result<(), ConsensusError> {
// ignore genesis blocks
if self.genesis_hashes.contains(&block_id) {
return Ok(());
}
let de_p = DenunciationPrecursor::from(&header);
self.channels
.pool_controller
.add_denunciation_precursor(de_p);
debug!(
"received header {} for slot {}",
block_id, header.content.slot
);
massa_trace!("consensus.block_graph.incoming_header", {"block_id": block_id, "header": header});
let mut to_ack: BTreeSet<(Slot, BlockId)> = BTreeSet::new();
self.blocks_state
.transition_map(&block_id, |block_status, _| match block_status {
None => {
to_ack.insert((header.content.slot, block_id));
Some(BlockStatus::Incoming(HeaderOrBlock::Header(header.clone())))
}
Some(block_status) => Some(block_status),
});
// process
self.rec_process(to_ack, current_slot)?;
Ok(())
}
/// Register a new full block in the graph. Ignore genesis hashes.
///
/// # Arguments:
/// * `block_id`: the block id
/// * `slot`: the slot of the block
/// * `current_slot`: the slot when this function is called
/// * `storage`: Storage containing the whole content of the block
/// * `created`: is the block created by the node or received from the network
///
/// # Returns:
/// Success or error if the block is invalid or too old
pub fn register_block(
&mut self,
block_id: BlockId,
slot: Slot,
current_slot: Option<Slot>,
storage: Storage,
created: bool,
) -> Result<(), ConsensusError> {
// ignore genesis blocks
if self.genesis_hashes.contains(&block_id) {
return Ok(());
}
if let Some(verifiable_block) = storage.read_blocks().get(&block_id) {
let de_p = DenunciationPrecursor::from(&verifiable_block.content.header);
self.channels
.pool_controller
.add_denunciation_precursor(de_p);
}
// Block is coming from protocol mark it for desync calculation
if !created {
let now = MassaTime::now();
self.protocol_blocks.push_back((now, block_id));
}
debug!("received block {} for slot {}", block_id, slot);
let mut to_ack: BTreeSet<(Slot, BlockId)> = BTreeSet::new();
self.blocks_state
.transition_map(&block_id, |block_status, _| {
match block_status {
None => {
to_ack.insert((slot, block_id));
Some(BlockStatus::Incoming(HeaderOrBlock::Block {
id: block_id,
slot,
storage,
}))
}
Some(block_status) => {
Some(match block_status {
BlockStatus::WaitingForSlot(_) => {
// promote to full block
BlockStatus::WaitingForSlot(HeaderOrBlock::Block {
id: block_id,
slot,
storage,
})
}
BlockStatus::WaitingForDependencies {
header_or_block: _,
mut unsatisfied_dependencies,
sequence_number,
} => {
// promote to full block and satisfy self-dependency
if unsatisfied_dependencies.remove(&block_id) {
// a dependency was satisfied: process
to_ack.insert((slot, block_id));
}
BlockStatus::WaitingForDependencies {
header_or_block: HeaderOrBlock::Block {
id: block_id,
slot,
storage,
},
unsatisfied_dependencies,
sequence_number,
}
}
_ => block_status,
})
}
}
});
// process
self.rec_process(to_ack, current_slot)?;
Ok(())
}
/// Mark a block that is in the graph as invalid.
///
/// # Arguments:
/// * `block_id`: Block id of the block to mark as invalid
/// * `header`: Header of the block to mark as invalid
pub fn mark_invalid_block(&mut self, block_id: &BlockId, header: SecuredHeader) {
let reason = DiscardReason::Invalid("invalid".to_string());
self.maybe_note_attack_attempt(&reason, block_id);
massa_trace!("consensus.block_graph.process.invalid_block", {"block_id": block_id, "reason": reason});
let sequence_number = self.blocks_state.sequence_counter();
self.blocks_state.transition_map(block_id, |_, _| {
Some(BlockStatus::Discarded {
slot: header.content.slot,
creator: header.content_creator_address,
parents: header.content.parents,
reason,
sequence_number,
})
});
}
}