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; Self::SIZE]) -> Self {
126 Self(u16::from_le_bytes(bytes))
127 }
128
129 #[inline(always)]
131 pub const fn to_bytes(self) -> [u8; 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 =
179 [0; SectorIndex::SIZE + HistorySize::SIZE as usize + ShardCommitmentHash::SIZE];
180 bytes_to_hash[..SectorIndex::SIZE].copy_from_slice(§or_index.to_bytes());
181 bytes_to_hash[SectorIndex::SIZE..][..HistorySize::SIZE as usize]
182 .copy_from_slice(&history_size.as_non_zero_u64().get().to_le_bytes());
183 bytes_to_hash[SectorIndex::SIZE + HistorySize::SIZE as usize..]
184 .copy_from_slice(shard_commitments_root.as_bytes());
185 Self(Blake3Hash::new(
187 single_block_keyed_hash(public_key_hash, &bytes_to_hash)
188 .expect("Less than a single block worth of bytes; qed"),
189 ))
190 }
191
192 pub fn derive_piece_index(
195 &self,
196 piece_offset: PieceOffset,
197 history_size: HistorySize,
198 max_pieces_in_sector: u16,
199 recent_segments: HistorySize,
200 recent_history_fraction: (HistorySize, HistorySize),
201 ) -> PieceIndex {
202 let recent_segments_in_pieces = recent_segments.in_pieces().get();
203 let min_history_size_in_pieces = recent_segments_in_pieces
206 * recent_history_fraction.1.in_pieces().get()
207 / recent_history_fraction.0.in_pieces();
208 let input_hash = {
209 let piece_offset_bytes = piece_offset.to_bytes();
210 let mut key = [0; 32];
211 key[..piece_offset_bytes.len()].copy_from_slice(&piece_offset_bytes);
212 NanoU256::from_le_bytes(
214 single_block_keyed_hash(&key, self.as_ref())
215 .expect("Less than a single block worth of bytes; qed"),
216 )
217 };
218 let history_size_in_pieces = history_size.in_pieces().get();
219 let num_interleaved_pieces = 1.max(
220 u64::from(max_pieces_in_sector) * recent_history_fraction.0.in_pieces().get()
221 / recent_history_fraction.1.in_pieces()
222 * 2,
223 );
224
225 let piece_index = if history_size_in_pieces > min_history_size_in_pieces
226 && u64::from(piece_offset) < num_interleaved_pieces
227 && u16::from(piece_offset) % 2 == 1
228 {
229 (input_hash % recent_segments_in_pieces)
232 + (history_size_in_pieces - recent_segments_in_pieces)
233 } else {
234 input_hash % history_size_in_pieces
235 };
236
237 PieceIndex::from(piece_index)
238 }
239
240 pub fn derive_sector_slot_challenge(
242 &self,
243 global_challenge: &Blake3Hash,
244 ) -> SectorSlotChallenge {
245 let sector_slot_challenge = Simd::from(*self.0) ^ Simd::from(**global_challenge);
246 SectorSlotChallenge(sector_slot_challenge.to_array().into())
247 }
248
249 pub fn derive_evaluation_seed(&self, piece_offset: PieceOffset) -> PosSeed {
251 let mut bytes_to_hash = [0; Self::SIZE + PieceOffset::SIZE];
252 bytes_to_hash[..Self::SIZE].copy_from_slice(self.as_ref());
253 bytes_to_hash[Self::SIZE..].copy_from_slice(&piece_offset.to_bytes());
254 let evaluation_seed = single_block_hash(&bytes_to_hash)
255 .expect("Less than a single block worth of bytes; qed");
256
257 PosSeed::from(evaluation_seed)
258 }
259
260 pub fn derive_expiration_history_size(
264 &self,
265 history_size: HistorySize,
266 sector_expiration_check_super_segment_root: &SuperSegmentRoot,
267 min_sector_lifetime: HistorySize,
268 ) -> Option<HistorySize> {
269 let sector_expiration_check_history_size = history_size
270 .sector_expiration_check(min_sector_lifetime)?
271 .as_non_zero_u64();
272
273 let input_hash = NanoU256::from_le_bytes(
274 single_block_hash(
275 [*self.0, **sector_expiration_check_super_segment_root].as_flattened(),
276 )
277 .expect("Less than a single block worth of bytes; qed"),
278 );
279
280 let last_possible_expiration = min_sector_lifetime
281 .as_non_zero_u64()
282 .checked_add(history_size.as_non_zero_u64().get().checked_mul(4u64)?)?;
283 let expires_in = input_hash
284 % last_possible_expiration
285 .get()
286 .checked_sub(sector_expiration_check_history_size.get())?;
287
288 let expiration_history_size = sector_expiration_check_history_size.get() + expires_in;
289 let expiration_history_size = NonZeroU64::try_from(expiration_history_size).expect(
290 "History size is not zero, so result is not zero even if expires immediately; qed",
291 );
292 Some(HistorySize::new(expiration_history_size))
293 }
294}
295
296#[derive(
298 Debug,
299 Display,
300 Default,
301 Copy,
302 Clone,
303 Ord,
304 PartialOrd,
305 Eq,
306 PartialEq,
307 Hash,
308 Add,
309 AddAssign,
310 Sub,
311 SubAssign,
312 Mul,
313 MulAssign,
314 Div,
315 DivAssign,
316)]
317#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
318#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
319#[repr(C)]
320pub struct SBucket(u16);
321
322impl Step for SBucket {
323 #[inline]
324 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
325 u16::steps_between(&start.0, &end.0)
326 }
327
328 #[inline]
329 fn forward_checked(start: Self, count: usize) -> Option<Self> {
330 u16::forward_checked(start.0, count).map(Self)
331 }
332
333 #[inline(always)]
334 fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
335 let (n, overflowing) = u16::forward_overflowing(start.0, count);
336 (Self(n), overflowing)
337 }
338
339 #[inline]
340 fn backward_checked(start: Self, count: usize) -> Option<Self> {
341 u16::backward_checked(start.0, count).map(Self)
342 }
343
344 #[inline(always)]
345 fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
346 let (n, overflowing) = u16::backward_overflowing(start.0, count);
347 (Self(n), overflowing)
348 }
349}
350
351const impl From<u16> for SBucket {
352 #[inline(always)]
353 fn from(value: u16) -> Self {
354 Self(value)
355 }
356}
357
358const impl From<SBucket> for u16 {
359 #[inline(always)]
360 fn from(value: SBucket) -> Self {
361 value.0
362 }
363}
364
365impl TryFrom<usize> for SBucket {
366 type Error = TryFromIntError;
367
368 #[inline]
369 fn try_from(value: usize) -> Result<Self, Self::Error> {
370 Ok(Self(u16::try_from(value)?))
371 }
372}
373
374impl From<SBucket> for u32 {
375 #[inline]
376 fn from(original: SBucket) -> Self {
377 u32::from(original.0)
378 }
379}
380
381impl From<SBucket> for usize {
382 #[inline]
383 fn from(original: SBucket) -> Self {
384 usize::from(original.0)
385 }
386}
387
388impl SBucket {
389 pub const ZERO: SBucket = SBucket(0);
391 pub const MAX: SBucket = SBucket((Record::NUM_S_BUCKETS - 1) as u16);
393}