use crate::{decode_entries, Result};
use solana_entry::{block_component::VersionedBlockMarker, entry::Entry};
const ENTRY_COUNT_LEN: usize = 8;
const BLOCK_MARKER_VERSION_V1: u16 = 1;
const BLOCK_MARKER_HEADER_LEN: usize = 13;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum BlockMarkerKind {
BlockFooter,
BlockHeader,
UpdateParent,
GenesisCertificate,
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BlockComponentPayload {
Entries(Vec<Entry>),
BlockMarker {
kind: BlockMarkerKind,
marker: Option<VersionedBlockMarker>,
},
}
pub fn classify_block_marker(payload: &[u8]) -> Option<BlockMarkerKind> {
if payload.len() < ENTRY_COUNT_LEN || payload[..ENTRY_COUNT_LEN] != [0u8; ENTRY_COUNT_LEN] {
return None;
}
if payload[ENTRY_COUNT_LEN..].iter().all(|byte| *byte == 0) {
return None;
}
let well_formed = payload.len() >= BLOCK_MARKER_HEADER_LEN
&& payload[8..10] == BLOCK_MARKER_VERSION_V1.to_le_bytes()
&& marker_end(payload) <= payload.len()
&& payload[marker_end(payload)..].iter().all(|byte| *byte == 0);
if !well_formed {
return Some(BlockMarkerKind::Unknown);
}
Some(match payload[10] {
0 => BlockMarkerKind::BlockFooter,
1 => BlockMarkerKind::BlockHeader,
2 => BlockMarkerKind::UpdateParent,
3 => BlockMarkerKind::GenesisCertificate,
_ => BlockMarkerKind::Unknown,
})
}
fn marker_end(payload: &[u8]) -> usize {
BLOCK_MARKER_HEADER_LEN + u16::from_le_bytes([payload[11], payload[12]]) as usize
}
pub fn decode_block_component(payload: &[u8]) -> Result<BlockComponentPayload> {
if let Some(kind) = classify_block_marker(payload) {
let marker = if kind == BlockMarkerKind::Unknown {
None
} else {
wincode::deserialize::<VersionedBlockMarker>(
&payload[ENTRY_COUNT_LEN..marker_end(payload)],
)
.ok()
};
return Ok(BlockComponentPayload::BlockMarker { kind, marker });
}
if payload.len() >= ENTRY_COUNT_LEN && payload.iter().all(|byte| *byte == 0) {
return Ok(BlockComponentPayload::Entries(Vec::new()));
}
Ok(BlockComponentPayload::Entries(decode_entries(payload)?))
}