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 integer addition. Computes `self + rhs`, returning `None` if overflow occurred
151    #[inline]
152    pub fn checked_add(self, rhs: Self) -> Option<Self> {
153        self.0.checked_add(rhs.0).map(Self)
154    }
155
156    /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred
157    #[inline]
158    pub fn checked_sub(self, rhs: Self) -> Option<Self> {
159        self.0.checked_sub(rhs.0).map(Self)
160    }
161}
162
163/// Proof of time key(input to the encryption).
164#[derive(Default, Copy, Clone, Eq, PartialEq, From, Into, AsRef, AsMut, Deref, DerefMut)]
165#[cfg_attr(
166    feature = "scale-codec",
167    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
168)]
169pub struct PotKey([u8; PotKey::SIZE]);
170
171impl fmt::Debug for PotKey {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        for byte in self.0 {
174            write!(f, "{byte:02x}")?;
175        }
176        Ok(())
177    }
178}
179
180#[cfg(feature = "serde")]
181#[derive(Serialize, Deserialize)]
182#[serde(transparent)]
183struct PotKeyBinary([u8; PotKey::SIZE]);
184
185#[cfg(feature = "serde")]
186#[derive(Serialize, Deserialize)]
187#[serde(transparent)]
188struct PotKeyHex(#[serde(with = "hex")] [u8; PotKey::SIZE]);
189
190#[cfg(feature = "serde")]
191impl Serialize for PotKey {
192    #[inline]
193    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
194    where
195        S: Serializer,
196    {
197        if serializer.is_human_readable() {
198            PotKeyHex(self.0).serialize(serializer)
199        } else {
200            PotKeyBinary(self.0).serialize(serializer)
201        }
202    }
203}
204
205#[cfg(feature = "serde")]
206impl<'de> Deserialize<'de> for PotKey {
207    #[inline]
208    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
209    where
210        D: Deserializer<'de>,
211    {
212        Ok(Self(if deserializer.is_human_readable() {
213            PotKeyHex::deserialize(deserializer)?.0
214        } else {
215            PotKeyBinary::deserialize(deserializer)?.0
216        }))
217    }
218}
219
220impl fmt::Display for PotKey {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        for byte in self.0 {
223            write!(f, "{byte:02x}")?;
224        }
225        Ok(())
226    }
227}
228
229impl FromStr for PotKey {
230    type Err = hex::FromHexError;
231
232    #[inline]
233    fn from_str(s: &str) -> Result<Self, Self::Err> {
234        let mut key = Self::default();
235        hex::decode_to_slice(s, key.as_mut())?;
236
237        Ok(key)
238    }
239}
240
241impl PotKey {
242    /// Size in bytes
243    pub const SIZE: usize = 16;
244}
245
246/// Proof of time seed
247#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, From, Into, AsRef, AsMut, Deref, DerefMut)]
248#[cfg_attr(
249    feature = "scale-codec",
250    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
251)]
252pub struct PotSeed([u8; PotSeed::SIZE]);
253
254impl fmt::Debug for PotSeed {
255    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256        for byte in self.0 {
257            write!(f, "{byte:02x}")?;
258        }
259        Ok(())
260    }
261}
262
263#[cfg(feature = "serde")]
264#[derive(Serialize, Deserialize)]
265#[serde(transparent)]
266struct PotSeedBinary([u8; PotSeed::SIZE]);
267
268#[cfg(feature = "serde")]
269#[derive(Serialize, Deserialize)]
270#[serde(transparent)]
271struct PotSeedHex(#[serde(with = "hex")] [u8; PotSeed::SIZE]);
272
273#[cfg(feature = "serde")]
274impl Serialize for PotSeed {
275    #[inline]
276    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
277    where
278        S: Serializer,
279    {
280        if serializer.is_human_readable() {
281            PotSeedHex(self.0).serialize(serializer)
282        } else {
283            PotSeedBinary(self.0).serialize(serializer)
284        }
285    }
286}
287
288#[cfg(feature = "serde")]
289impl<'de> Deserialize<'de> for PotSeed {
290    #[inline]
291    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
292    where
293        D: Deserializer<'de>,
294    {
295        Ok(Self(if deserializer.is_human_readable() {
296            PotSeedHex::deserialize(deserializer)?.0
297        } else {
298            PotSeedBinary::deserialize(deserializer)?.0
299        }))
300    }
301}
302
303impl fmt::Display for PotSeed {
304    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305        for byte in self.0 {
306            write!(f, "{byte:02x}")?;
307        }
308        Ok(())
309    }
310}
311
312impl PotSeed {
313    /// Size in bytes
314    pub const SIZE: usize = 16;
315
316    /// Derive initial PoT seed from genesis block root
317    #[inline]
318    pub fn from_genesis(genesis_block_root: &BlockRoot, external_entropy: &[u8]) -> Self {
319        let mut hasher = blake3::Hasher::new();
320        hasher.update(genesis_block_root.as_ref());
321        hasher.update(external_entropy);
322        let hash = hasher.finalize();
323        let mut seed = Self::default();
324        seed.copy_from_slice(&hash.as_bytes()[..Self::SIZE]);
325        seed
326    }
327
328    /// Derive key from proof of time seed
329    #[inline]
330    pub fn key(&self) -> PotKey {
331        let mut key = PotKey::default();
332        key.copy_from_slice(&blake3::hash(&self.0).as_bytes()[..Self::SIZE]);
333        key
334    }
335}
336
337/// Proof of time output, can be intermediate checkpoint or final slot output
338#[derive(
339    Default,
340    Copy,
341    Clone,
342    Eq,
343    PartialEq,
344    Hash,
345    From,
346    Into,
347    AsRef,
348    AsMut,
349    Deref,
350    DerefMut,
351    TrivialType,
352)]
353#[cfg_attr(
354    feature = "scale-codec",
355    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
356)]
357#[repr(C)]
358pub struct PotOutput([u8; PotOutput::SIZE]);
359
360impl fmt::Debug for PotOutput {
361    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362        for byte in self.0 {
363            write!(f, "{byte:02x}")?;
364        }
365        Ok(())
366    }
367}
368
369#[cfg(feature = "serde")]
370#[derive(Serialize, Deserialize)]
371#[serde(transparent)]
372struct PotOutputBinary([u8; PotOutput::SIZE]);
373
374#[cfg(feature = "serde")]
375#[derive(Serialize, Deserialize)]
376#[serde(transparent)]
377struct PotOutputHex(#[serde(with = "hex")] [u8; PotOutput::SIZE]);
378
379#[cfg(feature = "serde")]
380impl Serialize for PotOutput {
381    #[inline]
382    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
383    where
384        S: Serializer,
385    {
386        if serializer.is_human_readable() {
387            PotOutputHex(self.0).serialize(serializer)
388        } else {
389            PotOutputBinary(self.0).serialize(serializer)
390        }
391    }
392}
393
394#[cfg(feature = "serde")]
395impl<'de> Deserialize<'de> for PotOutput {
396    #[inline]
397    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
398    where
399        D: Deserializer<'de>,
400    {
401        Ok(Self(if deserializer.is_human_readable() {
402            PotOutputHex::deserialize(deserializer)?.0
403        } else {
404            PotOutputBinary::deserialize(deserializer)?.0
405        }))
406    }
407}
408
409impl fmt::Display for PotOutput {
410    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
411        for byte in self.0 {
412            write!(f, "{byte:02x}")?;
413        }
414        Ok(())
415    }
416}
417
418impl PotOutput {
419    /// Size in bytes
420    pub const SIZE: usize = 16;
421
422    /// Derives the global challenge from the output and slot
423    #[inline]
424    pub fn derive_global_challenge(&self, slot: SlotNumber) -> Blake3Hash {
425        let mut bytes_to_hash = [0; Self::SIZE + SlotNumber::SIZE];
426        bytes_to_hash[..Self::SIZE].copy_from_slice(&self.0);
427        bytes_to_hash[Self::SIZE..].copy_from_slice(&slot.to_bytes());
428        blake3::hash(&bytes_to_hash).into()
429    }
430
431    /// Derive seed from proof of time in case entropy injection is not needed
432    #[inline]
433    pub fn seed(&self) -> PotSeed {
434        PotSeed(self.0)
435    }
436
437    /// Derive seed from proof of time with entropy injection
438    #[inline]
439    pub fn seed_with_entropy(&self, entropy: &Blake3Hash) -> PotSeed {
440        let mut bytes_to_hash = [0; Blake3Hash::SIZE + Self::SIZE];
441        bytes_to_hash[..Blake3Hash::SIZE].copy_from_slice(entropy.as_ref());
442        bytes_to_hash[Blake3Hash::SIZE..].copy_from_slice(&self.0);
443        let hash = blake3::hash(&bytes_to_hash);
444        let mut seed = PotSeed::default();
445        seed.copy_from_slice(&hash.as_bytes()[..Self::SIZE]);
446        seed
447    }
448
449    /// Derive proof of time entropy from chunk and proof of time for injection purposes
450    #[inline]
451    pub fn derive_pot_entropy(&self, solution_chunk: &RecordChunk) -> Blake3Hash {
452        let mut bytes_to_hash = [0; RecordChunk::SIZE + Self::SIZE];
453        bytes_to_hash[..RecordChunk::SIZE].copy_from_slice(solution_chunk.as_ref());
454        bytes_to_hash[RecordChunk::SIZE..].copy_from_slice(&self.0);
455        blake3::hash(&bytes_to_hash).into()
456    }
457
458    /// Convenient conversion from slice of underlying representation for efficiency purposes
459    #[inline(always)]
460    pub const fn slice_from_repr(value: &[[u8; Self::SIZE]]) -> &[Self] {
461        // SAFETY: `PotOutput` is `#[repr(C)]` and guaranteed to have the same memory layout
462        unsafe { mem::transmute(value) }
463    }
464
465    /// Convenient conversion to slice of underlying representation for efficiency purposes
466    #[inline(always)]
467    pub const fn repr_from_slice(value: &[Self]) -> &[[u8; Self::SIZE]] {
468        // SAFETY: `PotOutput` is `#[repr(C)]` and guaranteed to have the same memory layout
469        unsafe { mem::transmute(value) }
470    }
471}
472
473/// Proof of time checkpoints, result of proving
474#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Deref, DerefMut, TrivialType)]
475#[cfg_attr(
476    feature = "scale-codec",
477    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
478)]
479#[repr(C)]
480pub struct PotCheckpoints([PotOutput; PotCheckpoints::NUM_CHECKPOINTS.get() as usize]);
481
482impl PotCheckpoints {
483    /// Size in bytes
484    pub const SIZE: usize = PotOutput::SIZE * Self::NUM_CHECKPOINTS.get() as usize;
485    /// Number of PoT checkpoints produced (used to optimize verification)
486    pub const NUM_CHECKPOINTS: NonZeroU8 = NonZeroU8::new(8).expect("Not zero; qed");
487
488    /// Get proof of time output out of checkpoints (last checkpoint)
489    #[inline]
490    pub fn output(&self) -> PotOutput {
491        self.0[Self::NUM_CHECKPOINTS.get() as usize - 1]
492    }
493
494    /// Convenient conversion from slice of underlying representation for efficiency purposes
495    #[inline(always)]
496    pub const fn slice_from_bytes(value: &[[u8; Self::SIZE]]) -> &[Self] {
497        // SAFETY: `PotOutput` and `PotCheckpoints` are `#[repr(C)]` and guaranteed to have the same
498        // memory layout
499        unsafe { mem::transmute(value) }
500    }
501
502    /// Convenient conversion to slice of underlying representation for efficiency purposes
503    #[inline(always)]
504    pub const fn bytes_from_slice(value: &[Self]) -> &[[u8; Self::SIZE]] {
505        // SAFETY: `PotOutput` and `PotCheckpoints` are `#[repr(C)]` and guaranteed to have the same
506        // memory layout
507        unsafe { mem::transmute(value) }
508    }
509}
510
511/// Change of parameters to apply to the proof of time chain
512#[derive(Debug, Copy, Clone, PartialEq, Eq)]
513#[cfg_attr(
514    feature = "scale-codec",
515    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
516)]
517#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
518#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
519pub struct PotParametersChange {
520    // TODO: Reduce this to `u16` or even `u8` since it is always an offset relatively to current
521    //  block's slot number
522    /// At which slot change of parameters takes effect
523    pub slot: SlotNumber,
524    /// New number of slot iterations
525    pub slot_iterations: NonZeroU32,
526    /// Entropy that should be injected at this time
527    pub entropy: Blake3Hash,
528}