ab_core_primitives/
pot.rs

1//! Proof of time-related data structures.
2
3use crate::hashes::{Blake3Hash, blake3_hash, blake3_hash_list};
4use crate::pieces::RecordChunk;
5use core::fmt;
6use core::iter::Step;
7use core::num::NonZeroU8;
8use core::str::FromStr;
9use core::time::Duration;
10use derive_more::{
11    Add, AddAssign, AsMut, AsRef, Deref, DerefMut, Display, Div, DivAssign, From, Into, Mul,
12    MulAssign, Sub, SubAssign,
13};
14#[cfg(feature = "scale-codec")]
15use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
16#[cfg(feature = "scale-codec")]
17use scale_info::TypeInfo;
18#[cfg(feature = "serde")]
19use serde::{Deserialize, Serialize};
20#[cfg(feature = "serde")]
21use serde::{Deserializer, Serializer};
22
23/// Slot duration
24#[derive(
25    Debug, Display, Default, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, From, Into,
26)]
27#[cfg_attr(
28    feature = "scale-codec",
29    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
30)]
31#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32#[repr(transparent)]
33pub struct SlotDuration(u16);
34
35impl SlotDuration {
36    /// Size in bytes
37    pub const SIZE: usize = size_of::<u16>();
38
39    /// Create new instance
40    #[inline(always)]
41    pub const fn from_millis(n: u16) -> Self {
42        Self(n)
43    }
44
45    /// Get internal representation
46    #[inline(always)]
47    pub const fn as_millis(self) -> u16 {
48        self.0
49    }
50
51    /// Get the value as [`Duration`] instance
52    #[inline(always)]
53    pub const fn as_duration(self) -> Duration {
54        Duration::from_millis(self.as_millis() as u64)
55    }
56}
57
58/// Slot number
59#[derive(
60    Debug,
61    Display,
62    Default,
63    Copy,
64    Clone,
65    Ord,
66    PartialOrd,
67    Eq,
68    PartialEq,
69    Hash,
70    From,
71    Into,
72    Add,
73    AddAssign,
74    Sub,
75    SubAssign,
76    Mul,
77    MulAssign,
78    Div,
79    DivAssign,
80)]
81#[cfg_attr(
82    feature = "scale-codec",
83    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
84)]
85#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
86#[repr(transparent)]
87pub struct SlotNumber(u64);
88
89impl Step for SlotNumber {
90    #[inline(always)]
91    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
92        u64::steps_between(&start.0, &end.0)
93    }
94
95    #[inline(always)]
96    fn forward_checked(start: Self, count: usize) -> Option<Self> {
97        u64::forward_checked(start.0, count).map(Self)
98    }
99
100    #[inline(always)]
101    fn backward_checked(start: Self, count: usize) -> Option<Self> {
102        u64::backward_checked(start.0, count).map(Self)
103    }
104}
105
106impl From<SlotNumber> for u128 {
107    #[inline(always)]
108    fn from(original: SlotNumber) -> Self {
109        u128::from(original.0)
110    }
111}
112
113impl SlotNumber {
114    /// Size in bytes
115    pub const SIZE: usize = size_of::<u64>();
116    /// Slot 0
117    pub const ZERO: Self = Self(0);
118    /// Slot 1
119    pub const ONE: Self = Self(1);
120    /// Max slot
121    pub const MAX: Self = Self(u64::MAX);
122
123    /// Create new instance
124    #[inline(always)]
125    pub const fn new(n: u64) -> Self {
126        Self(n)
127    }
128
129    /// Get internal representation
130    #[inline(always)]
131    pub const fn as_u64(self) -> u64 {
132        self.0
133    }
134
135    /// Create slot number from bytes
136    #[inline(always)]
137    pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self {
138        Self(u64::from_le_bytes(bytes))
139    }
140
141    /// Convert slot number to bytes
142    #[inline(always)]
143    pub const fn to_bytes(self) -> [u8; Self::SIZE] {
144        self.0.to_le_bytes()
145    }
146
147    /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred
148    #[inline]
149    pub fn checked_add(self, rhs: Self) -> Option<Self> {
150        self.0.checked_add(rhs.0).map(Self)
151    }
152
153    /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred
154    #[inline]
155    pub fn checked_sub(self, rhs: Self) -> Option<Self> {
156        self.0.checked_sub(rhs.0).map(Self)
157    }
158}
159
160/// Proof of time key(input to the encryption).
161#[derive(Default, Copy, Clone, Eq, PartialEq, From, Into, AsRef, AsMut, Deref, DerefMut)]
162#[cfg_attr(
163    feature = "scale-codec",
164    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
165)]
166pub struct PotKey([u8; PotKey::SIZE]);
167
168impl fmt::Debug for PotKey {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        for byte in self.0 {
171            write!(f, "{byte:02x}")?;
172        }
173        Ok(())
174    }
175}
176
177#[cfg(feature = "serde")]
178#[derive(Serialize, Deserialize)]
179#[serde(transparent)]
180struct PotKeyBinary([u8; PotKey::SIZE]);
181
182#[cfg(feature = "serde")]
183#[derive(Serialize, Deserialize)]
184#[serde(transparent)]
185struct PotKeyHex(#[serde(with = "hex")] [u8; PotKey::SIZE]);
186
187#[cfg(feature = "serde")]
188impl Serialize for PotKey {
189    #[inline]
190    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
191    where
192        S: Serializer,
193    {
194        if serializer.is_human_readable() {
195            PotKeyHex(self.0).serialize(serializer)
196        } else {
197            PotKeyBinary(self.0).serialize(serializer)
198        }
199    }
200}
201
202#[cfg(feature = "serde")]
203impl<'de> Deserialize<'de> for PotKey {
204    #[inline]
205    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
206    where
207        D: Deserializer<'de>,
208    {
209        Ok(Self(if deserializer.is_human_readable() {
210            PotKeyHex::deserialize(deserializer)?.0
211        } else {
212            PotKeyBinary::deserialize(deserializer)?.0
213        }))
214    }
215}
216
217impl fmt::Display for PotKey {
218    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219        for byte in self.0 {
220            write!(f, "{byte:02x}")?;
221        }
222        Ok(())
223    }
224}
225
226impl FromStr for PotKey {
227    type Err = hex::FromHexError;
228
229    #[inline]
230    fn from_str(s: &str) -> Result<Self, Self::Err> {
231        let mut key = Self::default();
232        hex::decode_to_slice(s, key.as_mut())?;
233
234        Ok(key)
235    }
236}
237
238impl PotKey {
239    /// Size in bytes
240    pub const SIZE: usize = 16;
241}
242
243/// Proof of time seed
244#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, From, Into, AsRef, AsMut, Deref, DerefMut)]
245#[cfg_attr(
246    feature = "scale-codec",
247    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
248)]
249pub struct PotSeed([u8; PotSeed::SIZE]);
250
251impl fmt::Debug for PotSeed {
252    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253        for byte in self.0 {
254            write!(f, "{byte:02x}")?;
255        }
256        Ok(())
257    }
258}
259
260#[cfg(feature = "serde")]
261#[derive(Serialize, Deserialize)]
262#[serde(transparent)]
263struct PotSeedBinary([u8; PotSeed::SIZE]);
264
265#[cfg(feature = "serde")]
266#[derive(Serialize, Deserialize)]
267#[serde(transparent)]
268struct PotSeedHex(#[serde(with = "hex")] [u8; PotSeed::SIZE]);
269
270#[cfg(feature = "serde")]
271impl Serialize for PotSeed {
272    #[inline]
273    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
274    where
275        S: Serializer,
276    {
277        if serializer.is_human_readable() {
278            PotSeedHex(self.0).serialize(serializer)
279        } else {
280            PotSeedBinary(self.0).serialize(serializer)
281        }
282    }
283}
284
285#[cfg(feature = "serde")]
286impl<'de> Deserialize<'de> for PotSeed {
287    #[inline]
288    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
289    where
290        D: Deserializer<'de>,
291    {
292        Ok(Self(if deserializer.is_human_readable() {
293            PotSeedHex::deserialize(deserializer)?.0
294        } else {
295            PotSeedBinary::deserialize(deserializer)?.0
296        }))
297    }
298}
299
300impl fmt::Display for PotSeed {
301    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302        for byte in self.0 {
303            write!(f, "{byte:02x}")?;
304        }
305        Ok(())
306    }
307}
308
309impl PotSeed {
310    /// Size in bytes
311    pub const SIZE: usize = 16;
312
313    /// Derive initial PoT seed from genesis block hash
314    #[inline]
315    pub fn from_genesis(genesis_block_hash: &[u8], external_entropy: &[u8]) -> Self {
316        let hash = blake3_hash_list(&[genesis_block_hash, external_entropy]);
317        let mut seed = Self::default();
318        seed.copy_from_slice(&hash[..Self::SIZE]);
319        seed
320    }
321
322    /// Derive key from proof of time seed
323    #[inline]
324    pub fn key(&self) -> PotKey {
325        let mut key = PotKey::default();
326        key.copy_from_slice(&blake3_hash(&self.0)[..Self::SIZE]);
327        key
328    }
329}
330
331/// Proof of time output, can be intermediate checkpoint or final slot output
332#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, From, Into, AsRef, AsMut, Deref, DerefMut)]
333#[cfg_attr(
334    feature = "scale-codec",
335    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
336)]
337pub struct PotOutput([u8; PotOutput::SIZE]);
338
339impl fmt::Debug for PotOutput {
340    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341        for byte in self.0 {
342            write!(f, "{byte:02x}")?;
343        }
344        Ok(())
345    }
346}
347
348#[cfg(feature = "serde")]
349#[derive(Serialize, Deserialize)]
350#[serde(transparent)]
351struct PotOutputBinary([u8; PotOutput::SIZE]);
352
353#[cfg(feature = "serde")]
354#[derive(Serialize, Deserialize)]
355#[serde(transparent)]
356struct PotOutputHex(#[serde(with = "hex")] [u8; PotOutput::SIZE]);
357
358#[cfg(feature = "serde")]
359impl Serialize for PotOutput {
360    #[inline]
361    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
362    where
363        S: Serializer,
364    {
365        if serializer.is_human_readable() {
366            PotOutputHex(self.0).serialize(serializer)
367        } else {
368            PotOutputBinary(self.0).serialize(serializer)
369        }
370    }
371}
372
373#[cfg(feature = "serde")]
374impl<'de> Deserialize<'de> for PotOutput {
375    #[inline]
376    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
377    where
378        D: Deserializer<'de>,
379    {
380        Ok(Self(if deserializer.is_human_readable() {
381            PotOutputHex::deserialize(deserializer)?.0
382        } else {
383            PotOutputBinary::deserialize(deserializer)?.0
384        }))
385    }
386}
387
388impl fmt::Display for PotOutput {
389    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390        for byte in self.0 {
391            write!(f, "{byte:02x}")?;
392        }
393        Ok(())
394    }
395}
396
397impl PotOutput {
398    /// Size in bytes
399    pub const SIZE: usize = 16;
400
401    /// Derives the global challenge from the output and slot
402    #[inline]
403    pub fn derive_global_challenge(&self, slot: SlotNumber) -> Blake3Hash {
404        blake3_hash_list(&[&self.0, &slot.to_bytes()])
405    }
406
407    /// Derive seed from proof of time in case entropy injection is not needed
408    #[inline]
409    pub fn seed(&self) -> PotSeed {
410        PotSeed(self.0)
411    }
412
413    /// Derive seed from proof of time with entropy injection
414    #[inline]
415    pub fn seed_with_entropy(&self, entropy: &Blake3Hash) -> PotSeed {
416        let hash = blake3_hash_list(&[entropy.as_ref(), &self.0]);
417        let mut seed = PotSeed::default();
418        seed.copy_from_slice(&hash[..Self::SIZE]);
419        seed
420    }
421
422    /// Derive proof of time entropy from chunk and proof of time for injection purposes
423    #[inline]
424    pub fn derive_pot_entropy(&self, solution_chunk: &RecordChunk) -> Blake3Hash {
425        blake3_hash_list(&[solution_chunk.as_ref(), &self.0])
426    }
427}
428
429/// Proof of time checkpoints, result of proving
430#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Deref, DerefMut)]
431#[cfg_attr(
432    feature = "scale-codec",
433    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
434)]
435pub struct PotCheckpoints([PotOutput; PotCheckpoints::NUM_CHECKPOINTS.get() as usize]);
436
437impl PotCheckpoints {
438    /// Number of PoT checkpoints produced (used to optimize verification)
439    pub const NUM_CHECKPOINTS: NonZeroU8 = NonZeroU8::new(8).expect("Not zero; qed");
440
441    /// Get proof of time output out of checkpoints (last checkpoint)
442    #[inline]
443    pub fn output(&self) -> PotOutput {
444        self.0[Self::NUM_CHECKPOINTS.get() as usize - 1]
445    }
446}