subspace_core_primitives/
pot.rs

1//! Proof of time-related data structures.
2
3use crate::Randomness;
4use crate::hashes::{Blake3Hash, blake3_hash, blake3_hash_list};
5use core::num::NonZeroU8;
6use core::str::FromStr;
7use core::{fmt, mem};
8use derive_more::{AsMut, AsRef, Deref, DerefMut, From};
9use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
10use scale_info::TypeInfo;
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13#[cfg(feature = "serde")]
14use serde::{Deserializer, Serializer};
15
16/// Proof of time key(input to the encryption).
17#[derive(
18    Default,
19    Copy,
20    Clone,
21    Eq,
22    PartialEq,
23    From,
24    AsRef,
25    AsMut,
26    Deref,
27    DerefMut,
28    Encode,
29    Decode,
30    TypeInfo,
31    MaxEncodedLen,
32)]
33pub struct PotKey([u8; PotKey::SIZE]);
34
35impl fmt::Debug for PotKey {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        write!(f, "{}", hex::encode(self.0))
38    }
39}
40
41#[cfg(feature = "serde")]
42#[derive(Serialize, Deserialize)]
43#[serde(transparent)]
44struct PotKeyBinary([u8; PotKey::SIZE]);
45
46#[cfg(feature = "serde")]
47#[derive(Serialize, Deserialize)]
48#[serde(transparent)]
49struct PotKeyHex(#[serde(with = "hex")] [u8; PotKey::SIZE]);
50
51#[cfg(feature = "serde")]
52impl Serialize for PotKey {
53    #[inline]
54    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
55    where
56        S: Serializer,
57    {
58        if serializer.is_human_readable() {
59            PotKeyHex(self.0).serialize(serializer)
60        } else {
61            PotKeyBinary(self.0).serialize(serializer)
62        }
63    }
64}
65
66#[cfg(feature = "serde")]
67impl<'de> Deserialize<'de> for PotKey {
68    #[inline]
69    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
70    where
71        D: Deserializer<'de>,
72    {
73        Ok(Self(if deserializer.is_human_readable() {
74            PotKeyHex::deserialize(deserializer)?.0
75        } else {
76            PotKeyBinary::deserialize(deserializer)?.0
77        }))
78    }
79}
80
81impl fmt::Display for PotKey {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        write!(f, "{}", hex::encode(self.0))
84    }
85}
86
87impl FromStr for PotKey {
88    type Err = hex::FromHexError;
89
90    #[inline]
91    fn from_str(s: &str) -> Result<Self, Self::Err> {
92        let mut key = Self::default();
93        hex::decode_to_slice(s, key.as_mut())?;
94
95        Ok(key)
96    }
97}
98
99impl PotKey {
100    /// Size of proof of time key in bytes
101    pub const SIZE: usize = 16;
102}
103
104/// Proof of time seed
105#[derive(
106    Default,
107    Copy,
108    Clone,
109    Eq,
110    PartialEq,
111    Hash,
112    From,
113    AsRef,
114    AsMut,
115    Deref,
116    DerefMut,
117    Encode,
118    Decode,
119    TypeInfo,
120    MaxEncodedLen,
121)]
122pub struct PotSeed([u8; PotSeed::SIZE]);
123
124impl fmt::Debug for PotSeed {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        write!(f, "{}", hex::encode(self.0))
127    }
128}
129
130#[cfg(feature = "serde")]
131#[derive(Serialize, Deserialize)]
132#[serde(transparent)]
133struct PotSeedBinary([u8; PotSeed::SIZE]);
134
135#[cfg(feature = "serde")]
136#[derive(Serialize, Deserialize)]
137#[serde(transparent)]
138struct PotSeedHex(#[serde(with = "hex")] [u8; PotSeed::SIZE]);
139
140#[cfg(feature = "serde")]
141impl Serialize for PotSeed {
142    #[inline]
143    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
144    where
145        S: Serializer,
146    {
147        if serializer.is_human_readable() {
148            PotSeedHex(self.0).serialize(serializer)
149        } else {
150            PotSeedBinary(self.0).serialize(serializer)
151        }
152    }
153}
154
155#[cfg(feature = "serde")]
156impl<'de> Deserialize<'de> for PotSeed {
157    #[inline]
158    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
159    where
160        D: Deserializer<'de>,
161    {
162        Ok(Self(if deserializer.is_human_readable() {
163            PotSeedHex::deserialize(deserializer)?.0
164        } else {
165            PotSeedBinary::deserialize(deserializer)?.0
166        }))
167    }
168}
169
170impl fmt::Display for PotSeed {
171    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172        write!(f, "{}", hex::encode(self.0))
173    }
174}
175
176impl PotSeed {
177    /// Size of proof of time seed in bytes
178    pub const SIZE: usize = 16;
179
180    /// Derive initial PoT seed from genesis block hash
181    #[inline]
182    pub fn from_genesis(genesis_block_hash: &[u8], external_entropy: &[u8]) -> Self {
183        let hash = blake3_hash_list(&[genesis_block_hash, external_entropy]);
184        let mut seed = Self::default();
185        seed.copy_from_slice(&hash[..Self::SIZE]);
186        seed
187    }
188
189    /// Derive key from proof of time seed
190    #[inline]
191    pub fn key(&self) -> PotKey {
192        let mut key = PotKey::default();
193        key.copy_from_slice(&blake3_hash(&self.0)[..Self::SIZE]);
194        key
195    }
196}
197
198/// Proof of time output, can be intermediate checkpoint or final slot output
199#[derive(
200    Default,
201    Copy,
202    Clone,
203    Eq,
204    PartialEq,
205    Hash,
206    From,
207    AsRef,
208    AsMut,
209    Deref,
210    DerefMut,
211    Encode,
212    Decode,
213    TypeInfo,
214    MaxEncodedLen,
215    DecodeWithMemTracking,
216)]
217#[repr(C)]
218pub struct PotOutput([u8; PotOutput::SIZE]);
219
220impl fmt::Debug for PotOutput {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        write!(f, "{}", hex::encode(self.0))
223    }
224}
225
226#[cfg(feature = "serde")]
227#[derive(Serialize, Deserialize)]
228#[serde(transparent)]
229struct PotOutputBinary([u8; PotOutput::SIZE]);
230
231#[cfg(feature = "serde")]
232#[derive(Serialize, Deserialize)]
233#[serde(transparent)]
234struct PotOutputHex(#[serde(with = "hex")] [u8; PotOutput::SIZE]);
235
236#[cfg(feature = "serde")]
237impl Serialize for PotOutput {
238    #[inline]
239    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
240    where
241        S: Serializer,
242    {
243        if serializer.is_human_readable() {
244            PotOutputHex(self.0).serialize(serializer)
245        } else {
246            PotOutputBinary(self.0).serialize(serializer)
247        }
248    }
249}
250
251#[cfg(feature = "serde")]
252impl<'de> Deserialize<'de> for PotOutput {
253    #[inline]
254    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
255    where
256        D: Deserializer<'de>,
257    {
258        Ok(Self(if deserializer.is_human_readable() {
259            PotOutputHex::deserialize(deserializer)?.0
260        } else {
261            PotOutputBinary::deserialize(deserializer)?.0
262        }))
263    }
264}
265
266impl fmt::Display for PotOutput {
267    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268        write!(f, "{}", hex::encode(self.0))
269    }
270}
271
272impl PotOutput {
273    /// Size of proof of time proof in bytes
274    pub const SIZE: usize = 16;
275
276    /// Derives the global randomness from the output
277    #[inline]
278    pub fn derive_global_randomness(&self) -> Randomness {
279        Randomness::from(*blake3_hash(&self.0))
280    }
281
282    /// Derive seed from proof of time in case entropy injection is not needed
283    #[inline]
284    pub fn seed(&self) -> PotSeed {
285        PotSeed(self.0)
286    }
287
288    /// Derive seed from proof of time with entropy injection
289    #[inline]
290    pub fn seed_with_entropy(&self, entropy: &Blake3Hash) -> PotSeed {
291        let hash = blake3_hash_list(&[entropy.as_ref(), &self.0]);
292        let mut seed = PotSeed::default();
293        seed.copy_from_slice(&hash[..Self::SIZE]);
294        seed
295    }
296
297    /// Convenient conversion from slice of underlying representation for efficiency purposes
298    #[inline(always)]
299    pub const fn slice_from_repr(value: &[[u8; Self::SIZE]]) -> &[Self] {
300        // SAFETY: `PotOutput` is `#[repr(C)]` and guaranteed to have the same memory layout
301        unsafe { mem::transmute(value) }
302    }
303
304    /// Convenient conversion to slice of underlying representation for efficiency purposes
305    #[inline(always)]
306    pub const fn repr_from_slice(value: &[Self]) -> &[[u8; Self::SIZE]] {
307        // SAFETY: `PotOutput` is `#[repr(C)]` and guaranteed to have the same memory layout
308        unsafe { mem::transmute(value) }
309    }
310}
311
312/// Proof of time checkpoints, result of proving
313#[derive(
314    Debug,
315    Default,
316    Copy,
317    Clone,
318    Eq,
319    PartialEq,
320    Hash,
321    Deref,
322    DerefMut,
323    Encode,
324    Decode,
325    TypeInfo,
326    MaxEncodedLen,
327)]
328pub struct PotCheckpoints([PotOutput; Self::NUM_CHECKPOINTS.get() as usize]);
329
330impl PotCheckpoints {
331    /// Number of PoT checkpoints produced (used to optimize verification)
332    pub const NUM_CHECKPOINTS: NonZeroU8 = NonZeroU8::new(8).expect("Not zero; qed");
333
334    /// Get proof of time output out of checkpoints (last checkpoint)
335    #[inline]
336    pub fn output(&self) -> PotOutput {
337        self.0[Self::NUM_CHECKPOINTS.get() as usize - 1]
338    }
339}