1#[cfg(test)]
4mod tests;
5
6use crate::hashes::Blake3Hash;
7use crate::nano_u256::NanoU256;
8use crate::pieces::{PieceIndex, PieceOffset, Record};
9use crate::pos::PosSeed;
10use crate::segments::{HistorySize, SuperSegmentRoot};
11use crate::solutions::ShardCommitmentHash;
12use ab_blake3::{single_block_hash, single_block_keyed_hash};
13use ab_io_type::trivial_type::TrivialType;
14use core::hash::Hash;
15use core::iter::Step;
16use core::num::{NonZeroU64, TryFromIntError};
17use core::simd::Simd;
18use derive_more::{Add, AddAssign, Deref, Display, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
19#[cfg(feature = "scale-codec")]
20use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
21#[cfg(feature = "serde")]
22use serde::{Deserialize, Serialize};
23
24#[derive(
26 Debug,
27 Display,
28 Default,
29 Copy,
30 Clone,
31 Ord,
32 PartialOrd,
33 Eq,
34 PartialEq,
35 Hash,
36 Add,
37 AddAssign,
38 Sub,
39 SubAssign,
40 Mul,
41 MulAssign,
42 Div,
43 DivAssign,
44 TrivialType,
45)]
46#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
47#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
48#[repr(C)]
49pub struct SectorIndex(u16);
50
51impl Step for SectorIndex {
52 #[inline(always)]
53 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
54 u16::steps_between(&start.0, &end.0)
55 }
56
57 #[inline(always)]
58 fn forward_checked(start: Self, count: usize) -> Option<Self> {
59 u16::forward_checked(start.0, count).map(Self)
60 }
61
62 #[inline(always)]
63 fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
64 let (n, overflowing) = u16::forward_overflowing(start.0, count);
65 (Self(n), overflowing)
66 }
67
68 #[inline(always)]
69 fn backward_checked(start: Self, count: usize) -> Option<Self> {
70 u16::backward_checked(start.0, count).map(Self)
71 }
72
73 #[inline(always)]
74 fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
75 let (n, overflowing) = u16::backward_overflowing(start.0, count);
76 (Self(n), overflowing)
77 }
78}
79
80const impl From<u16> for SectorIndex {
81 #[inline(always)]
82 fn from(value: u16) -> Self {
83 Self(value)
84 }
85}
86
87const impl From<SectorIndex> for u16 {
88 #[inline(always)]
89 fn from(value: SectorIndex) -> Self {
90 value.0
91 }
92}
93
94const impl From<SectorIndex> for u32 {
95 #[inline(always)]
96 fn from(original: SectorIndex) -> Self {
97 u32::from(original.0)
98 }
99}
100
101const impl From<SectorIndex> for u64 {
102 #[inline(always)]
103 fn from(original: SectorIndex) -> Self {
104 u64::from(original.0)
105 }
106}
107
108const impl From<SectorIndex> for usize {
109 #[inline(always)]
110 fn from(original: SectorIndex) -> Self {
111 usize::from(original.0)
112 }
113}
114
115impl SectorIndex {
116 pub const SIZE: usize = size_of::<u16>();
118 pub const ZERO: Self = Self(0);
120 pub const MAX: Self = Self(u16::MAX);
122
123 #[inline(always)]
125 pub const fn from_bytes(bytes: [u8; const { Self::SIZE }]) -> Self {
126 Self(u16::from_le_bytes(bytes))
127 }
128
129 #[inline(always)]
131 pub const fn to_bytes(self) -> [u8; const { Self::SIZE }] {
132 self.0.to_le_bytes()
133 }
134}
135
136#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deref)]
138pub struct SectorSlotChallenge(Blake3Hash);
139
140impl SectorSlotChallenge {
141 #[inline]
143 pub fn s_bucket_audit_index(&self) -> SBucket {
144 const {
147 assert!(Record::NUM_S_BUCKETS == 1 << u16::BITS as usize);
148 }
149 SBucket::from(u16::from_le_bytes([self.0[0], self.0[1]]))
150 }
151}
152
153#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
155#[cfg_attr(feature = "scale-codec", derive(Encode, Decode))]
156#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
157pub struct SectorId(Blake3Hash);
158
159impl AsRef<[u8]> for SectorId {
160 #[inline]
161 fn as_ref(&self) -> &[u8] {
162 self.0.as_ref()
163 }
164}
165
166impl SectorId {
167 const SIZE: usize = Blake3Hash::SIZE;
169
170 #[inline]
172 pub fn new(
173 public_key_hash: &Blake3Hash,
174 shard_commitments_root: &ShardCommitmentHash,
175 sector_index: SectorIndex,
176 history_size: HistorySize,
177 ) -> Self {
178 let mut bytes_to_hash = [0; const {
179 SectorIndex::SIZE + HistorySize::SIZE as usize + ShardCommitmentHash::SIZE
180 }];
181 bytes_to_hash[..SectorIndex::SIZE].copy_from_slice(§or_index.to_bytes());
182 bytes_to_hash[SectorIndex::SIZE..][..HistorySize::SIZE as usize]
183 .copy_from_slice(&history_size.as_non_zero_u64().get().to_le_bytes());
184 bytes_to_hash[SectorIndex::SIZE + HistorySize::SIZE as usize..]
185 .copy_from_slice(shard_commitments_root.as_bytes());
186 Self(Blake3Hash::new(
188 single_block_keyed_hash(public_key_hash, &bytes_to_hash)
189 .expect("Less than a single block worth of bytes; qed"),
190 ))
191 }
192
193 pub fn derive_piece_index(
196 &self,
197 piece_offset: PieceOffset,
198 history_size: HistorySize,
199 max_pieces_in_sector: u16,
200 recent_segments: HistorySize,
201 recent_history_fraction: (HistorySize, HistorySize),
202 ) -> PieceIndex {
203 let recent_segments_in_pieces = recent_segments.in_pieces().get();
204 let min_history_size_in_pieces = recent_segments_in_pieces
207 * recent_history_fraction.1.in_pieces().get()
208 / recent_history_fraction.0.in_pieces().get();
209 let input_hash = {
210 let piece_offset_bytes = piece_offset.to_bytes();
211 let mut key = [0; 32];
212 key[..piece_offset_bytes.len()].copy_from_slice(&piece_offset_bytes);
213 NanoU256::from_le_bytes(
215 single_block_keyed_hash(&key, self.as_ref())
216 .expect("Less than a single block worth of bytes; qed"),
217 )
218 };
219 let history_size_in_pieces = history_size.in_pieces().get();
220 let num_interleaved_pieces = 1.max(
221 u64::from(max_pieces_in_sector) * recent_history_fraction.0.in_pieces().get()
222 / recent_history_fraction.1.in_pieces().get()
223 * 2,
224 );
225
226 let piece_index = if history_size_in_pieces > min_history_size_in_pieces
227 && u64::from(piece_offset) < num_interleaved_pieces
228 && u16::from(piece_offset) % 2 == 1
229 {
230 (input_hash % recent_segments_in_pieces)
233 + (history_size_in_pieces - recent_segments_in_pieces)
234 } else {
235 input_hash % history_size_in_pieces
236 };
237
238 PieceIndex::from(piece_index)
239 }
240
241 pub fn derive_sector_slot_challenge(
243 &self,
244 global_challenge: &Blake3Hash,
245 ) -> SectorSlotChallenge {
246 let sector_slot_challenge = Simd::from(*self.0) ^ Simd::from(**global_challenge);
247 SectorSlotChallenge(sector_slot_challenge.to_array().into())
248 }
249
250 pub fn derive_evaluation_seed(&self, piece_offset: PieceOffset) -> PosSeed {
252 let mut bytes_to_hash = [0; const { Self::SIZE + PieceOffset::SIZE }];
253 bytes_to_hash[..Self::SIZE].copy_from_slice(self.as_ref());
254 bytes_to_hash[Self::SIZE..].copy_from_slice(&piece_offset.to_bytes());
255 let evaluation_seed = single_block_hash(&bytes_to_hash)
256 .expect("Less than a single block worth of bytes; qed");
257
258 PosSeed::from(evaluation_seed)
259 }
260
261 pub fn derive_expiration_history_size(
265 &self,
266 history_size: HistorySize,
267 sector_expiration_check_super_segment_root: &SuperSegmentRoot,
268 min_sector_lifetime: HistorySize,
269 ) -> Option<HistorySize> {
270 let sector_expiration_check_history_size = history_size
271 .sector_expiration_check(min_sector_lifetime)?
272 .as_non_zero_u64();
273
274 let input_hash = NanoU256::from_le_bytes(
275 single_block_hash(
276 [*self.0, **sector_expiration_check_super_segment_root].as_flattened(),
277 )
278 .expect("Less than a single block worth of bytes; qed"),
279 );
280
281 let last_possible_expiration = min_sector_lifetime
282 .as_non_zero_u64()
283 .checked_add(history_size.as_non_zero_u64().get().checked_mul(4u64)?)?;
284 let expires_in = input_hash
285 % last_possible_expiration
286 .get()
287 .checked_sub(sector_expiration_check_history_size.get())?;
288
289 let expiration_history_size = sector_expiration_check_history_size.get() + expires_in;
290 let expiration_history_size = NonZeroU64::try_from(expiration_history_size).expect(
291 "History size is not zero, so result is not zero even if expires immediately; qed",
292 );
293 Some(HistorySize::new(expiration_history_size))
294 }
295}
296
297#[derive(
299 Debug,
300 Display,
301 Default,
302 Copy,
303 Clone,
304 Ord,
305 PartialOrd,
306 Eq,
307 PartialEq,
308 Hash,
309 Add,
310 AddAssign,
311 Sub,
312 SubAssign,
313 Mul,
314 MulAssign,
315 Div,
316 DivAssign,
317)]
318#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
319#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
320#[repr(C)]
321pub struct SBucket(u16);
322
323impl Step for SBucket {
324 #[inline]
325 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
326 u16::steps_between(&start.0, &end.0)
327 }
328
329 #[inline]
330 fn forward_checked(start: Self, count: usize) -> Option<Self> {
331 u16::forward_checked(start.0, count).map(Self)
332 }
333
334 #[inline(always)]
335 fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
336 let (n, overflowing) = u16::forward_overflowing(start.0, count);
337 (Self(n), overflowing)
338 }
339
340 #[inline]
341 fn backward_checked(start: Self, count: usize) -> Option<Self> {
342 u16::backward_checked(start.0, count).map(Self)
343 }
344
345 #[inline(always)]
346 fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
347 let (n, overflowing) = u16::backward_overflowing(start.0, count);
348 (Self(n), overflowing)
349 }
350}
351
352const impl From<u16> for SBucket {
353 #[inline(always)]
354 fn from(value: u16) -> Self {
355 Self(value)
356 }
357}
358
359const impl From<SBucket> for u16 {
360 #[inline(always)]
361 fn from(value: SBucket) -> Self {
362 value.0
363 }
364}
365
366impl TryFrom<usize> for SBucket {
367 type Error = TryFromIntError;
368
369 #[inline]
370 fn try_from(value: usize) -> Result<Self, Self::Error> {
371 Ok(Self(u16::try_from(value)?))
372 }
373}
374
375impl From<SBucket> for u32 {
376 #[inline]
377 fn from(original: SBucket) -> Self {
378 u32::from(original.0)
379 }
380}
381
382impl From<SBucket> for usize {
383 #[inline]
384 fn from(original: SBucket) -> Self {
385 usize::from(original.0)
386 }
387}
388
389impl SBucket {
390 pub const ZERO: SBucket = SBucket(0);
392 pub const MAX: SBucket = SBucket((Record::NUM_S_BUCKETS - 1) as u16);
394}