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