Skip to main content

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