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