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