subspace_core_primitives/
sectors.rs1#[cfg(test)]
4mod tests;
5
6use crate::hashes::{
7 blake3_hash_list, blake3_hash_list_with_key, blake3_hash_with_key, Blake3Hash,
8};
9use crate::pieces::{PieceIndex, PieceOffset, Record};
10use crate::pos::PosSeed;
11use crate::segments::{HistorySize, SegmentCommitment};
12use crate::U256;
13use core::hash::Hash;
14use core::iter::Step;
15use core::num::{NonZeroU64, TryFromIntError};
16use core::simd::Simd;
17use derive_more::{
18 Add, AddAssign, AsRef, Deref, Display, Div, DivAssign, From, Into, Mul, MulAssign, Sub,
19 SubAssign,
20};
21use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
22use scale_info::TypeInfo;
23#[cfg(feature = "serde")]
24use serde::{Deserialize, Serialize};
25use static_assertions::const_assert_eq;
26
27pub type SectorIndex = u16;
29
30#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deref)]
32pub struct SectorSlotChallenge(Blake3Hash);
33
34impl SectorSlotChallenge {
35 #[inline]
37 pub fn s_bucket_audit_index(&self) -> SBucket {
38 const_assert_eq!(Record::NUM_S_BUCKETS, 1 << u16::BITS as usize);
41 SBucket::from(u16::from_le_bytes([self.0[0], self.0[1]]))
42 }
43}
44
45#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Encode, Decode, TypeInfo)]
47#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
48pub struct SectorId(Blake3Hash);
49
50impl AsRef<[u8]> for SectorId {
51 #[inline]
52 fn as_ref(&self) -> &[u8] {
53 self.0.as_ref()
54 }
55}
56
57impl SectorId {
58 pub fn new(
60 public_key_hash: Blake3Hash,
61 sector_index: SectorIndex,
62 history_size: HistorySize,
63 ) -> Self {
64 Self(blake3_hash_list_with_key(
65 &public_key_hash,
66 &[
67 §or_index.to_le_bytes(),
68 &history_size.get().to_le_bytes(),
69 ],
70 ))
71 }
72
73 pub fn derive_piece_index(
76 &self,
77 piece_offset: PieceOffset,
78 history_size: HistorySize,
79 max_pieces_in_sector: u16,
80 recent_segments: HistorySize,
81 recent_history_fraction: (HistorySize, HistorySize),
82 ) -> PieceIndex {
83 let recent_segments_in_pieces = recent_segments.in_pieces().get();
84 let min_history_size_in_pieces = recent_segments_in_pieces
87 * recent_history_fraction.1.in_pieces().get()
88 / recent_history_fraction.0.in_pieces().get();
89 let input_hash = {
90 let piece_offset_bytes = piece_offset.to_bytes();
91 let mut key = [0; 32];
92 key[..piece_offset_bytes.len()].copy_from_slice(&piece_offset_bytes);
93 U256::from_le_bytes(*blake3_hash_with_key(&key, self.as_ref()))
94 };
95 let history_size_in_pieces = history_size.in_pieces().get();
96 let num_interleaved_pieces = 1.max(
97 u64::from(max_pieces_in_sector) * recent_history_fraction.0.in_pieces().get()
98 / recent_history_fraction.1.in_pieces().get()
99 * 2,
100 );
101
102 let piece_index = if history_size_in_pieces > min_history_size_in_pieces
103 && u64::from(piece_offset) < num_interleaved_pieces
104 && u16::from(piece_offset) % 2 == 1
105 {
106 input_hash % U256::from(recent_segments_in_pieces)
109 + U256::from(history_size_in_pieces - recent_segments_in_pieces)
110 } else {
111 input_hash % U256::from(history_size_in_pieces)
112 };
113
114 PieceIndex::from(u64::try_from(piece_index).expect(
115 "Remainder of division by PieceIndex is guaranteed to fit into PieceIndex; qed",
116 ))
117 }
118
119 pub fn derive_sector_slot_challenge(
121 &self,
122 global_challenge: &Blake3Hash,
123 ) -> SectorSlotChallenge {
124 let sector_slot_challenge = Simd::from(*self.0) ^ Simd::from(**global_challenge);
125 SectorSlotChallenge(sector_slot_challenge.to_array().into())
126 }
127
128 pub fn derive_evaluation_seed(&self, piece_offset: PieceOffset) -> PosSeed {
130 let evaluation_seed = blake3_hash_list(&[self.as_ref(), &piece_offset.to_bytes()]);
131
132 PosSeed::from(*evaluation_seed)
133 }
134
135 pub fn derive_expiration_history_size(
139 &self,
140 history_size: HistorySize,
141 sector_expiration_check_segment_commitment: &SegmentCommitment,
142 min_sector_lifetime: HistorySize,
143 ) -> Option<HistorySize> {
144 let sector_expiration_check_history_size =
145 history_size.sector_expiration_check(min_sector_lifetime)?;
146
147 let input_hash = U256::from_le_bytes(*blake3_hash_list(&[
148 self.as_ref(),
149 sector_expiration_check_segment_commitment.as_ref(),
150 ]));
151
152 let last_possible_expiration =
153 min_sector_lifetime.checked_add(history_size.get().checked_mul(4u64)?)?;
154 let expires_in = input_hash
155 % U256::from(
156 last_possible_expiration
157 .get()
158 .checked_sub(sector_expiration_check_history_size.get())?,
159 );
160 let expires_in = u64::try_from(expires_in).expect("Number modulo u64 fits into u64; qed");
161
162 let expiration_history_size = sector_expiration_check_history_size.get() + expires_in;
163 let expiration_history_size = NonZeroU64::try_from(expiration_history_size).expect(
164 "History size is not zero, so result is not zero even if expires immediately; qed",
165 );
166 Some(HistorySize::from(expiration_history_size))
167 }
168}
169
170#[derive(
172 Debug,
173 Display,
174 Default,
175 Copy,
176 Clone,
177 Ord,
178 PartialOrd,
179 Eq,
180 PartialEq,
181 Hash,
182 Encode,
183 Decode,
184 Add,
185 AddAssign,
186 Sub,
187 SubAssign,
188 Mul,
189 MulAssign,
190 Div,
191 DivAssign,
192 TypeInfo,
193 MaxEncodedLen,
194)]
195#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
196#[repr(transparent)]
197pub struct SBucket(u16);
198
199impl Step for SBucket {
200 #[inline]
201 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
202 u16::steps_between(&start.0, &end.0)
203 }
204
205 #[inline]
206 fn forward_checked(start: Self, count: usize) -> Option<Self> {
207 u16::forward_checked(start.0, count).map(Self)
208 }
209
210 #[inline]
211 fn backward_checked(start: Self, count: usize) -> Option<Self> {
212 u16::backward_checked(start.0, count).map(Self)
213 }
214}
215
216impl TryFrom<usize> for SBucket {
217 type Error = TryFromIntError;
218
219 #[inline]
220 fn try_from(value: usize) -> Result<Self, Self::Error> {
221 Ok(Self(u16::try_from(value)?))
222 }
223}
224
225impl From<u16> for SBucket {
226 #[inline]
227 fn from(original: u16) -> Self {
228 Self(original)
229 }
230}
231
232impl From<SBucket> for u16 {
233 #[inline]
234 fn from(original: SBucket) -> Self {
235 original.0
236 }
237}
238
239impl From<SBucket> for u32 {
240 #[inline]
241 fn from(original: SBucket) -> Self {
242 u32::from(original.0)
243 }
244}
245
246impl From<SBucket> for usize {
247 #[inline]
248 fn from(original: SBucket) -> Self {
249 usize::from(original.0)
250 }
251}
252
253impl SBucket {
254 pub const ZERO: SBucket = SBucket(0);
256 pub const MAX: SBucket = SBucket((Record::NUM_S_BUCKETS - 1) as u16);
258}