subspace_farmer_components/
sector.rs1use bitvec::prelude::*;
10use parity_scale_codec::{Decode, Encode};
11use rayon::prelude::*;
12use std::mem::ManuallyDrop;
13use std::ops::{Deref, DerefMut};
14use std::{mem, slice};
15use subspace_core_primitives::checksum::Blake3Checksummed;
16use subspace_core_primitives::hashes::{Blake3Hash, blake3_hash};
17use subspace_core_primitives::pieces::{PieceOffset, Record, RecordCommitment, RecordWitness};
18use subspace_core_primitives::sectors::{SBucket, SectorIndex};
19use subspace_core_primitives::segments::{HistorySize, SegmentIndex};
20use thiserror::Error;
21use tracing::debug;
22
23#[inline]
27pub const fn sector_record_chunks_size(pieces_in_sector: u16) -> usize {
28 pieces_in_sector as usize * Record::SIZE
29}
30
31#[inline]
35pub const fn sector_record_metadata_size(pieces_in_sector: u16) -> usize {
36 pieces_in_sector as usize * RecordMetadata::encoded_size()
37}
38
39#[inline]
47pub const fn sector_size(pieces_in_sector: u16) -> usize {
48 sector_record_chunks_size(pieces_in_sector)
49 + sector_record_metadata_size(pieces_in_sector)
50 + SectorContentsMap::encoded_size(pieces_in_sector)
51 + Blake3Hash::SIZE
52}
53
54#[derive(Debug, Encode, Decode, Clone)]
56pub struct SectorMetadata {
57 pub sector_index: SectorIndex,
59 pub pieces_in_sector: u16,
61 pub s_bucket_sizes: Box<[u16; Record::NUM_S_BUCKETS]>,
63 pub history_size: HistorySize,
65}
66
67impl SectorMetadata {
68 pub fn s_bucket_offsets(&self) -> Box<[u32; Record::NUM_S_BUCKETS]> {
70 let s_bucket_offsets = self
71 .s_bucket_sizes
72 .iter()
73 .map({
74 let mut base_offset = 0;
75
76 move |s_bucket_size| {
77 let offset = base_offset;
78 base_offset += u32::from(*s_bucket_size);
79 offset
80 }
81 })
82 .collect::<Box<_>>();
83
84 assert_eq!(s_bucket_offsets.len(), Record::NUM_S_BUCKETS);
85 let mut s_bucket_offsets = ManuallyDrop::new(s_bucket_offsets);
86 unsafe { Box::from_raw(s_bucket_offsets.as_mut_ptr() as *mut [u32; Record::NUM_S_BUCKETS]) }
88 }
89}
90
91#[derive(Debug, Clone, Encode, Decode)]
93pub struct SectorMetadataChecksummed(Blake3Checksummed<SectorMetadata>);
94
95impl From<SectorMetadata> for SectorMetadataChecksummed {
96 #[inline]
97 fn from(value: SectorMetadata) -> Self {
98 Self(Blake3Checksummed(value))
99 }
100}
101
102impl Deref for SectorMetadataChecksummed {
103 type Target = SectorMetadata;
104
105 #[inline]
106 fn deref(&self) -> &Self::Target {
107 &self.0.0
108 }
109}
110
111impl DerefMut for SectorMetadataChecksummed {
112 #[inline]
113 fn deref_mut(&mut self) -> &mut Self::Target {
114 &mut self.0.0
115 }
116}
117
118impl SectorMetadataChecksummed {
119 #[inline]
123 pub fn encoded_size() -> usize {
124 let default = SectorMetadataChecksummed::from(SectorMetadata {
125 sector_index: 0,
126 pieces_in_sector: 0,
127 s_bucket_sizes: unsafe { Box::new_zeroed().assume_init() },
130 history_size: HistorySize::from(SegmentIndex::ZERO),
131 });
132
133 default.encoded_size()
134 }
135}
136
137#[derive(Debug, Default, Clone, Encode, Decode)]
139pub(crate) struct RecordMetadata {
140 pub(crate) commitment: RecordCommitment,
142 pub(crate) witness: RecordWitness,
144 pub(crate) piece_checksum: Blake3Hash,
146}
147
148impl RecordMetadata {
149 pub(crate) const fn encoded_size() -> usize {
150 RecordWitness::SIZE + RecordCommitment::SIZE + Blake3Hash::SIZE
151 }
152}
153
154#[derive(Debug, Clone)]
156pub(crate) struct RawSector {
157 pub(crate) records: Vec<Record>,
159 pub(crate) metadata: Vec<RecordMetadata>,
161}
162
163impl RawSector {
164 pub(crate) fn new(pieces_in_sector: u16) -> Self {
166 Self {
167 records: Record::new_zero_vec(usize::from(pieces_in_sector)),
168 metadata: vec![RecordMetadata::default(); usize::from(pieces_in_sector)],
169 }
170 }
171}
172
173type SingleRecordBitArray = BitArray<[u8; Record::NUM_S_BUCKETS / u8::BITS as usize]>;
175
176const SINGLE_RECORD_BIT_ARRAY_SIZE: usize = mem::size_of::<SingleRecordBitArray>();
177
178#[derive(Debug)]
185pub struct EncodedChunksUsed<'a> {
186 encoded_record_chunks_used: &'a mut SingleRecordBitArray,
187 num_encoded_record_chunks: &'a mut SBucket,
188 potentially_updated: bool,
189}
190
191impl Drop for EncodedChunksUsed<'_> {
192 fn drop(&mut self) {
193 if self.potentially_updated {
194 let num_encoded_record_chunks = self.encoded_record_chunks_used.count_ones();
195 assert!(num_encoded_record_chunks <= SBucket::MAX.into());
196 *self.num_encoded_record_chunks = SBucket::try_from(num_encoded_record_chunks)
197 .expect("Checked with explicit assert above; qed");
198 }
199 }
200}
201
202impl EncodedChunksUsed<'_> {
203 pub fn iter(&self) -> impl ExactSizeIterator<Item = impl Deref<Target = bool> + '_> + '_ {
205 self.encoded_record_chunks_used.iter()
206 }
207
208 pub fn iter_mut(
210 &mut self,
211 ) -> impl ExactSizeIterator<Item = impl DerefMut<Target = bool> + '_> + '_ {
212 self.potentially_updated = true;
213 self.encoded_record_chunks_used.iter_mut()
214 }
215}
216
217#[derive(Debug, Error, Copy, Clone, Eq, PartialEq)]
219pub enum SectorContentsMapFromBytesError {
220 #[error("Invalid bytes length, expected {expected}, actual {actual}")]
222 InvalidBytesLength {
223 expected: usize,
225 actual: usize,
227 },
228 #[error("Invalid number of encoded record chunks: {actual}")]
230 InvalidEncodedRecordChunks {
231 actual: usize,
233 max: usize,
235 },
236 #[error("Checksum mismatch")]
238 ChecksumMismatch,
239}
240
241#[derive(Debug, Error, Copy, Clone, Eq, PartialEq)]
243pub enum SectorContentsMapEncodeIntoError {
244 #[error("Invalid bytes length, expected {expected}, actual {actual}")]
246 InvalidBytesLength {
247 expected: usize,
249 actual: usize,
251 },
252}
253
254#[derive(Debug, Error, Copy, Clone, Eq, PartialEq)]
256pub enum SectorContentsMapIterationError {
257 #[error("S-bucket provided {provided} is out of range, max {max}")]
259 SBucketOutOfRange {
260 provided: usize,
262 max: usize,
264 },
265}
266
267#[derive(Debug, Clone, Eq, PartialEq)]
274pub struct SectorContentsMap {
275 num_encoded_record_chunks: Vec<SBucket>,
280 encoded_record_chunks_used: Vec<SingleRecordBitArray>,
283}
284
285impl SectorContentsMap {
286 pub fn new(pieces_in_sector: u16) -> Self {
289 Self {
290 num_encoded_record_chunks: vec![SBucket::default(); usize::from(pieces_in_sector)],
291 encoded_record_chunks_used: vec![
292 SingleRecordBitArray::default();
293 usize::from(pieces_in_sector)
294 ],
295 }
296 }
297
298 pub fn from_bytes(
303 bytes: &[u8],
304 pieces_in_sector: u16,
305 ) -> Result<Self, SectorContentsMapFromBytesError> {
306 if bytes.len() != Self::encoded_size(pieces_in_sector) {
307 return Err(SectorContentsMapFromBytesError::InvalidBytesLength {
308 expected: Self::encoded_size(pieces_in_sector),
309 actual: bytes.len(),
310 });
311 }
312
313 let (single_records_bit_arrays, expected_checksum) =
314 bytes.split_at(bytes.len() - Blake3Hash::SIZE);
315 let actual_checksum = blake3_hash(single_records_bit_arrays);
317 if *actual_checksum != *expected_checksum {
318 debug!(
319 actual_checksum = %hex::encode(actual_checksum),
320 expected_checksum = %hex::encode(expected_checksum),
321 "Hash doesn't match, corrupted bytes"
322 );
323
324 return Err(SectorContentsMapFromBytesError::ChecksumMismatch);
325 }
326
327 let mut encoded_record_chunks_used =
328 vec![SingleRecordBitArray::default(); pieces_in_sector.into()];
329
330 let num_encoded_record_chunks = encoded_record_chunks_used
331 .iter_mut()
332 .zip(
333 single_records_bit_arrays
334 .as_chunks::<{ SINGLE_RECORD_BIT_ARRAY_SIZE }>()
335 .0,
336 )
337 .map(|(encoded_record_chunks_used, bytes)| {
338 encoded_record_chunks_used
339 .as_raw_mut_slice()
340 .copy_from_slice(bytes);
341 let num_encoded_record_chunks = encoded_record_chunks_used.count_ones();
342 if num_encoded_record_chunks > Record::NUM_CHUNKS {
343 return Err(
344 SectorContentsMapFromBytesError::InvalidEncodedRecordChunks {
345 actual: num_encoded_record_chunks,
346 max: Record::NUM_CHUNKS,
347 },
348 );
349 }
350 Ok(SBucket::try_from(num_encoded_record_chunks).expect("Verified above; qed"))
351 })
352 .collect::<Result<Vec<_>, _>>()?;
353
354 Ok(Self {
355 num_encoded_record_chunks,
356 encoded_record_chunks_used,
357 })
358 }
359
360 pub const fn encoded_size(pieces_in_sector: u16) -> usize {
363 SINGLE_RECORD_BIT_ARRAY_SIZE * pieces_in_sector as usize + Blake3Hash::SIZE
364 }
365
366 pub fn encode_into(&self, output: &mut [u8]) -> Result<(), SectorContentsMapEncodeIntoError> {
368 if output.len() != Self::encoded_size(self.encoded_record_chunks_used.len() as u16) {
369 return Err(SectorContentsMapEncodeIntoError::InvalidBytesLength {
370 expected: Self::encoded_size(self.encoded_record_chunks_used.len() as u16),
371 actual: output.len(),
372 });
373 }
374
375 let slice = self.encoded_record_chunks_used.as_slice();
376 let slice = unsafe {
378 slice::from_raw_parts(
379 slice.as_ptr() as *const u8,
380 slice.len() * SINGLE_RECORD_BIT_ARRAY_SIZE,
381 )
382 };
383
384 output[..slice.len()].copy_from_slice(slice);
386 output[slice.len()..].copy_from_slice(blake3_hash(slice).as_ref());
387
388 Ok(())
389 }
390
391 pub fn num_encoded_record_chunks(&self) -> &[SBucket] {
393 &self.num_encoded_record_chunks
394 }
395
396 pub fn iter_record_bitfields(&self) -> &[SingleRecordBitArray] {
398 &self.encoded_record_chunks_used
399 }
400
401 pub fn iter_record_bitfields_mut(
403 &mut self,
404 ) -> impl ExactSizeIterator<Item = EncodedChunksUsed<'_>> + '_ {
405 self.encoded_record_chunks_used
406 .iter_mut()
407 .zip(&mut self.num_encoded_record_chunks)
408 .map(
409 |(encoded_record_chunks_used, num_encoded_record_chunks)| EncodedChunksUsed {
410 encoded_record_chunks_used,
411 num_encoded_record_chunks,
412 potentially_updated: false,
413 },
414 )
415 }
416
417 pub fn s_bucket_sizes(&self) -> Box<[u16; Record::NUM_S_BUCKETS]> {
419 let s_bucket_sizes = (u16::from(SBucket::ZERO)..=u16::from(SBucket::MAX))
421 .into_par_iter()
422 .map(SBucket::from)
423 .map(|s_bucket| {
424 self.iter_s_bucket_records(s_bucket)
425 .expect("S-bucket guaranteed to be in range; qed")
426 .count() as u16
427 })
428 .collect::<Box<_>>();
429
430 assert_eq!(s_bucket_sizes.len(), Record::NUM_S_BUCKETS);
431 let mut s_bucket_sizes = ManuallyDrop::new(s_bucket_sizes);
432 unsafe { Box::from_raw(s_bucket_sizes.as_mut_ptr() as *mut [u16; Record::NUM_S_BUCKETS]) }
434 }
435
436 pub fn iter_record_chunk_to_plot(
441 &self,
442 piece_offset: PieceOffset,
443 ) -> impl Iterator<Item = (SBucket, bool, usize)> + '_ {
444 (SBucket::ZERO..=SBucket::MAX)
446 .flat_map(|s_bucket| {
448 self.iter_s_bucket_records(s_bucket)
449 .expect("S-bucket guaranteed to be in range; qed")
450 .map(move |(current_piece_offset, encoded_chunk_used)| {
451 (s_bucket, current_piece_offset, encoded_chunk_used)
452 })
453 })
454 .enumerate()
457 .filter_map(
459 move |(chunk_location, (s_bucket, current_piece_offset, encoded_chunk_used))| {
460 (current_piece_offset == piece_offset).then_some((
462 s_bucket,
463 encoded_chunk_used,
464 chunk_location,
465 ))
466 },
467 )
468 .take(Record::NUM_CHUNKS)
470 }
471
472 pub fn par_iter_record_chunk_to_plot(
480 &self,
481 piece_offset: PieceOffset,
482 ) -> impl IndexedParallelIterator<Item = Option<(usize, bool)>> + '_ {
483 let piece_offset = usize::from(piece_offset);
484 (u16::from(SBucket::ZERO)..=u16::from(SBucket::MAX))
485 .into_par_iter()
486 .map(SBucket::from)
487 .map(move |s_bucket| {
489 let encoded_chunk_used = record_has_s_bucket_chunk(
490 s_bucket.into(),
491 &self.encoded_record_chunks_used[piece_offset],
492 usize::from(self.num_encoded_record_chunks[piece_offset]),
493 )?;
494
495 let chunk_offset = self
498 .encoded_record_chunks_used
499 .iter()
500 .zip(&self.num_encoded_record_chunks)
501 .take(piece_offset)
502 .filter(move |(record_bitfields, num_encoded_record_chunks)| {
503 record_has_s_bucket_chunk(
504 s_bucket.into(),
505 record_bitfields,
506 usize::from(**num_encoded_record_chunks),
507 )
508 .is_some()
509 })
510 .count();
511
512 Some((chunk_offset, encoded_chunk_used))
513 })
514 }
515
516 pub fn iter_s_bucket_records(
522 &self,
523 s_bucket: SBucket,
524 ) -> Result<impl Iterator<Item = (PieceOffset, bool)> + '_, SectorContentsMapIterationError>
525 {
526 let s_bucket = usize::from(s_bucket);
527
528 if s_bucket >= Record::NUM_S_BUCKETS {
529 return Err(SectorContentsMapIterationError::SBucketOutOfRange {
530 provided: s_bucket,
531 max: Record::NUM_S_BUCKETS,
532 });
533 }
534
535 Ok((PieceOffset::ZERO..)
536 .zip(
537 self.encoded_record_chunks_used
538 .iter()
539 .zip(&self.num_encoded_record_chunks),
540 )
541 .filter_map(
542 move |(piece_offset, (record_bitfields, num_encoded_record_chunks))| {
543 let encoded_chunk_used = record_has_s_bucket_chunk(
544 s_bucket,
545 record_bitfields,
546 usize::from(*num_encoded_record_chunks),
547 )?;
548
549 Some((piece_offset, encoded_chunk_used))
550 },
551 ))
552 }
553
554 pub fn iter_s_bucket_encoded_record_chunks_used(
560 &self,
561 s_bucket: SBucket,
562 ) -> Result<impl Iterator<Item = bool> + '_, SectorContentsMapIterationError> {
563 let s_bucket = usize::from(s_bucket);
564
565 if s_bucket >= Record::NUM_S_BUCKETS {
566 return Err(SectorContentsMapIterationError::SBucketOutOfRange {
567 provided: s_bucket,
568 max: Record::NUM_S_BUCKETS,
569 });
570 }
571
572 Ok(self
573 .encoded_record_chunks_used
574 .iter()
575 .map(move |record_bitfields| record_bitfields[s_bucket]))
576 }
577}
578
579fn record_has_s_bucket_chunk(
583 s_bucket: usize,
584 record_bitfields: &SingleRecordBitArray,
585 num_encoded_record_chunks: usize,
586) -> Option<bool> {
587 if record_bitfields[s_bucket] {
588 Some(true)
590 } else if num_encoded_record_chunks == Record::NUM_CHUNKS {
591 None
592 } else {
593 let encoded_before = record_bitfields[..s_bucket].count_ones();
595 let unencoded_before = s_bucket - encoded_before;
596 let unencoded_total = Record::NUM_CHUNKS.saturating_sub(num_encoded_record_chunks);
599
600 if unencoded_before < unencoded_total {
601 Some(false)
604 } else {
605 None
606 }
607 }
608}