ab_core_primitives/
pot.rs

1//! Proof of time-related data structures.
2
3use crate::block::BlockRoot;
4use crate::hashes::Blake3Hash;
5use crate::pieces::RecordChunk;
6use crate::solutions::ShardMembershipEntropy;
7use ab_blake3::single_block_hash;
8use ab_io_type::trivial_type::TrivialType;
9use core::iter::Step;
10use core::num::{NonZeroU8, NonZeroU32};
11use core::str::FromStr;
12use core::time::Duration;
13use core::{fmt, mem};
14use derive_more::{
15    Add, AddAssign, AsMut, AsRef, Deref, DerefMut, Display, Div, DivAssign, From, Into, Mul,
16    MulAssign, Sub, SubAssign,
17};
18#[cfg(feature = "scale-codec")]
19use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
20#[cfg(feature = "serde")]
21use serde::{Deserialize, Serialize};
22#[cfg(feature = "serde")]
23use serde::{Deserializer, Serializer};
24
25/// Slot duration
26#[derive(
27    Debug, Display, Default, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, From, Into,
28)]
29#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
30#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
31#[repr(C)]
32pub struct SlotDuration(u16);
33
34impl SlotDuration {
35    /// Size in bytes
36    pub const SIZE: usize = size_of::<u16>();
37
38    /// Create a new instance
39    #[inline(always)]
40    pub const fn from_millis(ms: u16) -> Self {
41        Self(ms)
42    }
43
44    /// Get internal representation
45    #[inline(always)]
46    pub const fn as_millis(self) -> u16 {
47        self.0
48    }
49
50    /// Get the value as [`Duration`] instance
51    #[inline(always)]
52    pub const fn as_duration(self) -> Duration {
53        Duration::from_millis(self.as_millis() as u64)
54    }
55}
56
57/// Slot number
58#[derive(
59    Debug,
60    Display,
61    Default,
62    Copy,
63    Clone,
64    Ord,
65    PartialOrd,
66    Eq,
67    PartialEq,
68    Hash,
69    From,
70    Into,
71    Add,
72    AddAssign,
73    Sub,
74    SubAssign,
75    Mul,
76    MulAssign,
77    Div,
78    DivAssign,
79    TrivialType,
80)]
81#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
82#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
83#[repr(C)]
84pub struct SlotNumber(u64);
85
86impl Step for SlotNumber {
87    #[inline(always)]
88    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
89        u64::steps_between(&start.0, &end.0)
90    }
91
92    #[inline(always)]
93    fn forward_checked(start: Self, count: usize) -> Option<Self> {
94        u64::forward_checked(start.0, count).map(Self)
95    }
96
97    #[inline(always)]
98    fn backward_checked(start: Self, count: usize) -> Option<Self> {
99        u64::backward_checked(start.0, count).map(Self)
100    }
101}
102
103impl From<SlotNumber> for u128 {
104    #[inline(always)]
105    fn from(original: SlotNumber) -> Self {
106        u128::from(original.0)
107    }
108}
109
110impl SlotNumber {
111    /// Size in bytes
112    pub const SIZE: usize = size_of::<u64>();
113    /// Slot 0
114    pub const ZERO: Self = Self(0);
115    /// Slot 1
116    pub const ONE: Self = Self(1);
117    /// Max slot
118    pub const MAX: Self = Self(u64::MAX);
119
120    /// Create a new instance
121    #[inline(always)]
122    pub const fn new(n: u64) -> Self {
123        Self(n)
124    }
125
126    /// Get internal representation
127    #[inline(always)]
128    pub const fn as_u64(self) -> u64 {
129        self.0
130    }
131
132    /// Create slot number from bytes
133    #[inline(always)]
134    pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self {
135        Self(u64::from_le_bytes(bytes))
136    }
137
138    /// Convert slot number to bytes
139    #[inline(always)]
140    pub const fn to_bytes(self) -> [u8; Self::SIZE] {
141        self.0.to_le_bytes()
142    }
143
144    /// Checked addition, returns `None` on overflow
145    #[inline(always)]
146    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
147        if let Some(n) = self.0.checked_add(rhs.0) {
148            Some(Self(n))
149        } else {
150            None
151        }
152    }
153
154    /// Saturating addition
155    #[inline(always)]
156    pub const fn saturating_add(self, rhs: Self) -> Self {
157        Self(self.0.saturating_add(rhs.0))
158    }
159
160    /// Checked subtraction, returns `None` on underflow
161    #[inline(always)]
162    pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
163        if let Some(n) = self.0.checked_sub(rhs.0) {
164            Some(Self(n))
165        } else {
166            None
167        }
168    }
169
170    /// Saturating subtraction
171    #[inline(always)]
172    pub const fn saturating_sub(self, rhs: Self) -> Self {
173        Self(self.0.saturating_sub(rhs.0))
174    }
175}
176
177/// Proof of time key(input to the encryption).
178#[derive(Default, Copy, Clone, Eq, PartialEq, From, Into, AsRef, AsMut, Deref, DerefMut)]
179#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
180pub struct PotKey([u8; PotKey::SIZE]);
181
182impl fmt::Debug for PotKey {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        for byte in self.0 {
185            write!(f, "{byte:02x}")?;
186        }
187        Ok(())
188    }
189}
190
191#[cfg(feature = "serde")]
192#[derive(Serialize, Deserialize)]
193#[serde(transparent)]
194struct PotKeyBinary([u8; PotKey::SIZE]);
195
196#[cfg(feature = "serde")]
197#[derive(Serialize, Deserialize)]
198#[serde(transparent)]
199struct PotKeyHex(#[serde(with = "hex")] [u8; PotKey::SIZE]);
200
201#[cfg(feature = "serde")]
202impl Serialize for PotKey {
203    #[inline]
204    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
205    where
206        S: Serializer,
207    {
208        if serializer.is_human_readable() {
209            PotKeyHex(self.0).serialize(serializer)
210        } else {
211            PotKeyBinary(self.0).serialize(serializer)
212        }
213    }
214}
215
216#[cfg(feature = "serde")]
217impl<'de> Deserialize<'de> for PotKey {
218    #[inline]
219    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
220    where
221        D: Deserializer<'de>,
222    {
223        Ok(Self(if deserializer.is_human_readable() {
224            PotKeyHex::deserialize(deserializer)?.0
225        } else {
226            PotKeyBinary::deserialize(deserializer)?.0
227        }))
228    }
229}
230
231impl fmt::Display for PotKey {
232    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233        for byte in self.0 {
234            write!(f, "{byte:02x}")?;
235        }
236        Ok(())
237    }
238}
239
240impl FromStr for PotKey {
241    type Err = hex::FromHexError;
242
243    #[inline]
244    fn from_str(s: &str) -> Result<Self, Self::Err> {
245        let mut key = Self::default();
246        hex::decode_to_slice(s, key.as_mut())?;
247
248        Ok(key)
249    }
250}
251
252impl PotKey {
253    /// Size in bytes
254    pub const SIZE: usize = 16;
255}
256
257/// Proof of time seed
258#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, From, Into, AsRef, AsMut, Deref, DerefMut)]
259#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
260pub struct PotSeed([u8; PotSeed::SIZE]);
261
262impl fmt::Debug for PotSeed {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        for byte in self.0 {
265            write!(f, "{byte:02x}")?;
266        }
267        Ok(())
268    }
269}
270
271#[cfg(feature = "serde")]
272#[derive(Serialize, Deserialize)]
273#[serde(transparent)]
274struct PotSeedBinary([u8; PotSeed::SIZE]);
275
276#[cfg(feature = "serde")]
277#[derive(Serialize, Deserialize)]
278#[serde(transparent)]
279struct PotSeedHex(#[serde(with = "hex")] [u8; PotSeed::SIZE]);
280
281#[cfg(feature = "serde")]
282impl Serialize for PotSeed {
283    #[inline]
284    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
285    where
286        S: Serializer,
287    {
288        if serializer.is_human_readable() {
289            PotSeedHex(self.0).serialize(serializer)
290        } else {
291            PotSeedBinary(self.0).serialize(serializer)
292        }
293    }
294}
295
296#[cfg(feature = "serde")]
297impl<'de> Deserialize<'de> for PotSeed {
298    #[inline]
299    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
300    where
301        D: Deserializer<'de>,
302    {
303        Ok(Self(if deserializer.is_human_readable() {
304            PotSeedHex::deserialize(deserializer)?.0
305        } else {
306            PotSeedBinary::deserialize(deserializer)?.0
307        }))
308    }
309}
310
311impl fmt::Display for PotSeed {
312    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313        for byte in self.0 {
314            write!(f, "{byte:02x}")?;
315        }
316        Ok(())
317    }
318}
319
320impl PotSeed {
321    /// Size in bytes
322    pub const SIZE: usize = 16;
323
324    /// Derive initial PoT seed from genesis block root
325    #[inline]
326    pub fn from_genesis(genesis_block_root: &BlockRoot, external_entropy: &[u8]) -> Self {
327        let mut hasher = blake3::Hasher::new();
328        hasher.update(genesis_block_root.as_ref());
329        hasher.update(external_entropy);
330        let hash = hasher.finalize();
331        let mut seed = Self::default();
332        seed.copy_from_slice(&hash.as_bytes()[..Self::SIZE]);
333        seed
334    }
335
336    /// Derive key from proof of time seed
337    #[inline]
338    pub fn key(&self) -> PotKey {
339        let mut key = PotKey::default();
340        key.copy_from_slice(
341            &single_block_hash(&self.0).expect("Less than a single block worth of bytes; qed")
342                [..Self::SIZE],
343        );
344        key
345    }
346}
347
348/// Proof of time output, can be intermediate checkpoint or final slot output
349#[derive(
350    Default,
351    Copy,
352    Clone,
353    Eq,
354    PartialEq,
355    Hash,
356    From,
357    Into,
358    AsRef,
359    AsMut,
360    Deref,
361    DerefMut,
362    TrivialType,
363)]
364#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
365#[repr(C)]
366pub struct PotOutput([u8; PotOutput::SIZE]);
367
368impl fmt::Debug for PotOutput {
369    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370        for byte in self.0 {
371            write!(f, "{byte:02x}")?;
372        }
373        Ok(())
374    }
375}
376
377#[cfg(feature = "serde")]
378#[derive(Serialize, Deserialize)]
379#[serde(transparent)]
380struct PotOutputBinary([u8; PotOutput::SIZE]);
381
382#[cfg(feature = "serde")]
383#[derive(Serialize, Deserialize)]
384#[serde(transparent)]
385struct PotOutputHex(#[serde(with = "hex")] [u8; PotOutput::SIZE]);
386
387#[cfg(feature = "serde")]
388impl Serialize for PotOutput {
389    #[inline]
390    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
391    where
392        S: Serializer,
393    {
394        if serializer.is_human_readable() {
395            PotOutputHex(self.0).serialize(serializer)
396        } else {
397            PotOutputBinary(self.0).serialize(serializer)
398        }
399    }
400}
401
402#[cfg(feature = "serde")]
403impl<'de> Deserialize<'de> for PotOutput {
404    #[inline]
405    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
406    where
407        D: Deserializer<'de>,
408    {
409        Ok(Self(if deserializer.is_human_readable() {
410            PotOutputHex::deserialize(deserializer)?.0
411        } else {
412            PotOutputBinary::deserialize(deserializer)?.0
413        }))
414    }
415}
416
417impl fmt::Display for PotOutput {
418    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
419        for byte in self.0 {
420            write!(f, "{byte:02x}")?;
421        }
422        Ok(())
423    }
424}
425
426impl PotOutput {
427    /// Size in bytes
428    pub const SIZE: usize = 16;
429
430    /// Derives the global challenge from the output and slot
431    #[inline]
432    pub fn derive_global_challenge(&self, slot: SlotNumber) -> Blake3Hash {
433        let mut bytes_to_hash = [0; Self::SIZE + SlotNumber::SIZE];
434        bytes_to_hash[..Self::SIZE].copy_from_slice(&self.0);
435        bytes_to_hash[Self::SIZE..].copy_from_slice(&slot.to_bytes());
436        Blake3Hash::new(
437            single_block_hash(&bytes_to_hash)
438                .expect("Less than a single block worth of bytes; qed"),
439        )
440    }
441
442    /// Derive seed from proof of time in case entropy injection is not needed
443    #[inline]
444    pub fn seed(&self) -> PotSeed {
445        PotSeed(self.0)
446    }
447
448    /// Derive seed from proof of time with entropy injection
449    #[inline]
450    pub fn seed_with_entropy(&self, entropy: &Blake3Hash) -> PotSeed {
451        let mut bytes_to_hash = [0; Blake3Hash::SIZE + Self::SIZE];
452        bytes_to_hash[..Blake3Hash::SIZE].copy_from_slice(entropy.as_ref());
453        bytes_to_hash[Blake3Hash::SIZE..].copy_from_slice(&self.0);
454        let hash = single_block_hash(&bytes_to_hash)
455            .expect("Less than a single block worth of bytes; qed");
456        let mut seed = PotSeed::default();
457        seed.copy_from_slice(&hash[..Self::SIZE]);
458        seed
459    }
460
461    /// Derive proof of time entropy from chunk and proof of time for injection purposes
462    #[inline]
463    pub fn derive_pot_entropy(&self, solution_chunk: &RecordChunk) -> Blake3Hash {
464        let mut bytes_to_hash = [0; RecordChunk::SIZE + Self::SIZE];
465        bytes_to_hash[..RecordChunk::SIZE].copy_from_slice(solution_chunk.as_ref());
466        bytes_to_hash[RecordChunk::SIZE..].copy_from_slice(&self.0);
467        Blake3Hash::new(
468            single_block_hash(&bytes_to_hash)
469                .expect("Less than a single block worth of bytes; qed"),
470        )
471    }
472
473    /// Derive shard membership entropy
474    #[inline]
475    pub fn shard_membership_entropy(&self) -> ShardMembershipEntropy {
476        ShardMembershipEntropy::new(self.0)
477    }
478
479    /// Convenient conversion from slice of underlying representation for efficiency purposes
480    #[inline(always)]
481    pub const fn slice_from_repr(value: &[[u8; Self::SIZE]]) -> &[Self] {
482        // SAFETY: `PotOutput` is `#[repr(C)]` and guaranteed to have the same memory layout
483        unsafe { mem::transmute(value) }
484    }
485
486    /// Convenient conversion to slice of underlying representation for efficiency purposes
487    #[inline(always)]
488    pub const fn repr_from_slice(value: &[Self]) -> &[[u8; Self::SIZE]] {
489        // SAFETY: `PotOutput` is `#[repr(C)]` and guaranteed to have the same memory layout
490        unsafe { mem::transmute(value) }
491    }
492}
493
494/// Proof of time checkpoints, result of proving
495#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Deref, DerefMut, TrivialType)]
496#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
497#[repr(C)]
498pub struct PotCheckpoints([PotOutput; PotCheckpoints::NUM_CHECKPOINTS.get() as usize]);
499
500impl PotCheckpoints {
501    /// Size in bytes
502    pub const SIZE: usize = PotOutput::SIZE * Self::NUM_CHECKPOINTS.get() as usize;
503    /// Number of PoT checkpoints produced (used to optimize verification)
504    pub const NUM_CHECKPOINTS: NonZeroU8 = NonZeroU8::new(8).expect("Not zero; qed");
505
506    /// Get proof of time output out of checkpoints (last checkpoint)
507    #[inline]
508    pub fn output(&self) -> PotOutput {
509        self.0[Self::NUM_CHECKPOINTS.get() as usize - 1]
510    }
511
512    /// Convenient conversion from slice of underlying representation for efficiency purposes
513    #[inline(always)]
514    pub const fn slice_from_bytes(value: &[[u8; Self::SIZE]]) -> &[Self] {
515        // SAFETY: `PotOutput` and `PotCheckpoints` are `#[repr(C)]` and guaranteed to have the same
516        // memory layout
517        unsafe { mem::transmute(value) }
518    }
519
520    /// Convenient conversion to slice of underlying representation for efficiency purposes
521    #[inline(always)]
522    pub const fn bytes_from_slice(value: &[Self]) -> &[[u8; Self::SIZE]] {
523        // SAFETY: `PotOutput` and `PotCheckpoints` are `#[repr(C)]` and guaranteed to have the same
524        // memory layout
525        unsafe { mem::transmute(value) }
526    }
527}
528
529/// Change of parameters to apply to the proof of time chain.
530///
531/// Corresponds to scheduled PoT parameters change, which is applied after the slot of this block.
532/// It is carried into the next blocks until it is applied on or before slot of the current block.
533#[derive(Debug, Copy, Clone, PartialEq, Eq)]
534#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
535#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
536#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
537pub struct PotParametersChange {
538    // TODO: Reduce this to `u16` or even `u8` since it is always an offset relatively to current
539    //  block's slot number
540    /// At which slot change of parameters takes effect
541    pub slot: SlotNumber,
542    /// New number of slot iterations
543    pub slot_iterations: NonZeroU32,
544    /// Entropy that should be injected at this time
545    pub entropy: Blake3Hash,
546}