subspace_data_retrieval/object_fetcher/
segment_header.rsuse crate::object_fetcher::{decode_data_length, Error};
use parity_scale_codec::{Decode, Encode, Input, IoReader};
use std::io::Cursor;
use subspace_archiving::archiver::SegmentItem;
use subspace_core_primitives::hashes::Blake3Hash;
use subspace_core_primitives::objects::GlobalObject;
use subspace_core_primitives::segments::{
ArchivedBlockProgress, LastArchivedBlock, SegmentCommitment, SegmentHeader, SegmentIndex,
};
use subspace_runtime_primitives::MAX_BLOCK_LENGTH;
pub const MAX_SEGMENT_PADDING: usize = 3;
const SEGMENT_VERSION_VARIANT: u8 = 0;
const BLOCK_CONTINUATION_VARIANT: u8 = 3;
#[inline]
pub fn min_segment_header_encoded_size() -> usize {
let min_segment_header = SegmentHeader::V0 {
segment_index: 0.into(),
segment_commitment: SegmentCommitment::default(),
prev_segment_header_hash: Blake3Hash::default(),
last_archived_block: LastArchivedBlock {
number: 0,
archived_progress: ArchivedBlockProgress::Complete,
},
};
min_segment_header.encoded_size()
}
#[inline]
pub fn max_segment_header_encoded_size() -> usize {
let max_segment_header = SegmentHeader::V0 {
segment_index: u64::MAX.into(),
segment_commitment: SegmentCommitment::default(),
prev_segment_header_hash: Blake3Hash::default(),
last_archived_block: LastArchivedBlock {
number: u32::MAX,
archived_progress: ArchivedBlockProgress::Partial(u32::MAX),
},
};
max_segment_header.encoded_size()
}
pub fn strip_segment_header(
piece_data: Vec<u8>,
segment_index: SegmentIndex,
mapping: GlobalObject,
) -> Result<(Vec<u8>, usize), Error> {
let mut piece_data = IoReader(Cursor::new(piece_data));
let segment_variant = piece_data
.read_byte()
.map_err(|source| Error::SegmentDecoding {
source,
segment_index,
mapping,
})?;
if segment_variant != SEGMENT_VERSION_VARIANT {
return Err(Error::UnknownSegmentVariant {
segment_variant,
segment_index,
mapping,
});
}
let segment_item =
SegmentItem::decode(&mut piece_data).map_err(|source| Error::SegmentDecoding {
source,
segment_index,
mapping,
})?;
let SegmentItem::ParentSegmentHeader(_) = segment_item else {
return Err(Error::UnexpectedSegmentItem {
segment_progress: piece_data.0.position() as usize,
segment_index,
segment_item: Box::new(segment_item),
mapping,
});
};
let segment_item_variant = piece_data
.read_byte()
.map_err(|source| Error::SegmentDecoding {
source,
segment_index,
mapping,
})?;
let header_bytes = piece_data.0.position() as usize;
let mut piece_data = piece_data.0.into_inner().split_off(header_bytes);
let segment_item_lengths = decode_data_length(&piece_data, MAX_BLOCK_LENGTH as usize, mapping)?;
if segment_item_variant != BLOCK_CONTINUATION_VARIANT || segment_item_lengths.is_none() {
return Err(Error::UnexpectedSegmentItemVariant {
segment_progress: header_bytes,
segment_index,
segment_item_variant,
segment_item_lengths,
mapping,
});
}
let (segment_item_prefix_len, segment_item_data_len) =
segment_item_lengths.expect("just checked length is Some; qed");
let mut piece_data = piece_data.split_off(segment_item_prefix_len);
piece_data.truncate(segment_item_data_len);
Ok((piece_data, segment_item_data_len))
}
#[cfg(test)]
mod test {
use super::*;
use parity_scale_codec::{Compact, CompactLen};
use subspace_archiving::archiver::Segment;
use subspace_core_primitives::objects::BlockObjectMapping;
use subspace_runtime_primitives::MAX_BLOCK_LENGTH;
#[test]
fn max_segment_padding_constant() {
assert_eq!(
MAX_SEGMENT_PADDING,
Compact::compact_len(&MAX_BLOCK_LENGTH) - Compact::<u32>::compact_len(&1)
);
}
#[test]
fn segment_header_length_constants() {
assert!(
min_segment_header_encoded_size() < max_segment_header_encoded_size(),
"min_segment_header_encoded_size: {} must be less than max_segment_header_encoded_size: {}",
min_segment_header_encoded_size(),
max_segment_header_encoded_size()
);
}
#[test]
fn segment_version_variant_constant() {
let segment = Segment::V0 { items: Vec::new() };
let segment = segment.encode();
assert_eq!(segment[0], SEGMENT_VERSION_VARIANT);
}
#[test]
fn block_continuation_variant_constant() {
let block_continuation = SegmentItem::BlockContinuation {
bytes: Vec::new(),
object_mapping: BlockObjectMapping::default(),
};
let block_continuation = block_continuation.encode();
assert_eq!(block_continuation[0], BLOCK_CONTINUATION_VARIANT);
}
}