diff --git a/crates/pallet-subspace/src/lib.rs b/crates/pallet-subspace/src/lib.rs index ee6d8faa59..6e9a05678b 100644 --- a/crates/pallet-subspace/src/lib.rs +++ b/crates/pallet-subspace/src/lib.rs @@ -864,7 +864,6 @@ impl Pallet { PorRandomness::::put(por_randomness); // Deposit global randomness data such that light client can validate blocks later. - #[cfg(not(feature = "pot"))] frame_system::Pallet::::deposit_log(DigestItem::global_randomness( GlobalRandomnesses::::get().current, )); diff --git a/crates/sc-consensus-subspace/src/lib.rs b/crates/sc-consensus-subspace/src/lib.rs index cccce5166e..e037aa0731 100644 --- a/crates/sc-consensus-subspace/src/lib.rs +++ b/crates/sc-consensus-subspace/src/lib.rs @@ -102,16 +102,6 @@ pub enum PotVerifyError { slot_number: SlotNumber, }, - /// Parent block has no proof of time digest. - #[error( - "Parent block missing proof of time : {block_number}/{parent_slot_number}/{slot_number}" - )] - ParentMissingPotDigest { - block_number: BlockNumber, - parent_slot_number: SlotNumber, - slot_number: SlotNumber, - }, - /// Verification failed. #[error("Proof of time error: {0}")] PotVerifyBlockProofsError(#[from] PotVerifyBlockProofsError), @@ -1011,6 +1001,9 @@ where }; if subspace_digest_items.global_randomness != correct_global_randomness { + // TODO: There is some kind of mess with PoT randomness right now that doesn't make a + // lot of sense, restore this check once that is resolved + #[cfg(not(feature = "pot"))] return Err(Error::InvalidGlobalRandomness(block_hash)); } @@ -1158,21 +1151,23 @@ where None => return Ok(randomness), }; - let parent_pot_digest = parent_pre_digest.proof_of_time.as_ref().ok_or_else(|| { - PotVerifyError::ParentMissingPotDigest { - block_number: block_number.into(), - parent_slot_number: parent_pre_digest.slot.into(), - slot_number: pre_digest.slot.into(), - } - })?; + let maybe_parent_pot_digest = &parent_pre_digest.proof_of_time; proof_of_time .verify_block_proofs( block_number.into(), - pre_digest.slot.into(), + // TODO: Hack while we don't have PoT-based slot worker, remove once we do + parent_pre_digest + .proof_of_time + .as_ref() + .map(|p| p.proofs().last().slot_number) + .unwrap_or_default() + + SlotNumber::from(parent_pre_digest.slot) + .checked_sub(SlotNumber::from(pre_digest.slot)) + .unwrap_or_default(), pot_digest, parent_pre_digest.slot.into(), - parent_pot_digest, + maybe_parent_pot_digest, ) .map_err(PotVerifyError::PotVerifyBlockProofsError)?; diff --git a/crates/sc-consensus-subspace/src/slot_worker.rs b/crates/sc-consensus-subspace/src/slot_worker.rs index 82a4935d9e..00c63f8c79 100644 --- a/crates/sc-consensus-subspace/src/slot_worker.rs +++ b/crates/sc-consensus-subspace/src/slot_worker.rs @@ -45,8 +45,6 @@ use sp_consensus_subspace::digests::{extract_pre_digest, CompatibleDigestItem, P use sp_consensus_subspace::{FarmerPublicKey, FarmerSignature, SignedVote, SubspaceApi, Vote}; use sp_core::crypto::ByteArray; use sp_core::H256; -#[cfg(feature = "pot")] -use sp_runtime::traits::NumberFor as BlockNumberFor; use sp_runtime::traits::{Block as BlockT, Header, One, Saturating, Zero}; use sp_runtime::DigestItem; use std::future::Future; @@ -66,15 +64,7 @@ use subspace_verification::{ /// Errors while building the block proof of time. #[cfg(feature = "pot")] #[derive(Debug, thiserror::Error)] -pub enum PotCreateError { - /// Parent block has no proof of time digest. - #[error("Parent block missing proof of time : {parent_block_number}/{parent_slot_number}/{slot_number}")] - ParentMissingPotDigest { - parent_block_number: BlockNumberFor, - parent_slot_number: SlotNumber, - slot_number: SlotNumber, - }, - +pub enum PotCreateError { /// Proof creation failed. #[error("Proof of time error: {0}")] PotGetBlockProofsError(#[from] PotGetBlockProofsError), @@ -231,10 +221,6 @@ where let parent_hash = parent_header.hash(); let runtime_api = self.client.runtime_api(); - #[cfg(not(feature = "pot"))] - let global_randomness = - extract_global_randomness_for_block(self.client.as_ref(), parent_hash).ok()?; - // If proof of time is enabled, collect the proofs that go into this // block and derive randomness from the last proof. #[cfg(feature = "pot")] @@ -245,7 +231,15 @@ where proof_of_time.as_ref(), parent_header, &parent_pre_digest, - slot.into(), + // TODO: Hack while we don't have PoT-based slot worker, remove once we do + parent_pre_digest + .proof_of_time + .as_ref() + .map(|p| p.proofs().last().slot_number) + .unwrap_or_default() + + SlotNumber::from(parent_slot) + .checked_sub(SlotNumber::from(slot)) + .unwrap_or_default(), ) .await .ok()?; @@ -258,6 +252,12 @@ where ) }; + // TODO: There is some kind of mess with PoT randomness right now that doesn't make a + // lot of sense, restore this check once that is resolved + // #[cfg(not(feature = "pot"))] + let global_randomness = + extract_global_randomness_for_block(self.client.as_ref(), parent_hash).ok()?; + let (solution_range, voting_solution_range) = extract_solution_ranges_for_block(self.client.as_ref(), parent_hash).ok()?; @@ -624,30 +624,14 @@ where parent_header: &Block::Header, parent_pre_digest: &PreDigest, slot_number: SlotNumber, - ) -> Result> { + ) -> Result { let block_number = *parent_header.number() + One::one(); - // Block 1 does not have proofs - if block_number.is_one() { - return Ok(PotPreDigest::FirstBlock(slot_number)); - } - - // Block 2 onwards. - // Get the start slot number for the proofs in the new block. - let parent_pot_digest = parent_pre_digest.proof_of_time.as_ref().ok_or_else(|| { - // PoT needs to be present in the block if feature is enabled. - PotCreateError::ParentMissingPotDigest { - parent_block_number: *parent_header.number(), - parent_slot_number: parent_pre_digest.slot.into(), - slot_number, - } - })?; - proof_of_time .get_block_proofs( block_number.into(), slot_number, - parent_pot_digest, + &parent_pre_digest.proof_of_time, Some(self.subspace_link.slot_duration().as_duration()), ) .await diff --git a/crates/sc-proof-of-time/src/lib.rs b/crates/sc-proof-of-time/src/lib.rs index cedefaff9d..e0c5e66b48 100644 --- a/crates/sc-proof-of-time/src/lib.rs +++ b/crates/sc-proof-of-time/src/lib.rs @@ -21,7 +21,7 @@ pub use time_keeper::TimeKeeper; #[derive(Debug, Clone)] pub struct PotConfig { /// PoT key used initially when PoT chain starts. - // TODO: Use this field + // TODO: Also add seed field here pub initial_key: PotKey, /// Frequency of entropy injection from consensus. @@ -52,6 +52,10 @@ pub struct PotConfig { /// Components initialized during the new_partial() phase of set up. #[derive(Debug)] pub struct PotComponents { + /// PoT key used initially when PoT chain starts. + // TODO: Remove this from here, shouldn't be necessary eventually + pub(crate) initial_key: PotKey, + /// If the role is time keeper or node client. is_time_keeper: bool, @@ -71,9 +75,11 @@ impl PotComponents { let proof_of_time = ProofOfTime::new(config.pot_iterations, config.num_checkpoints) // TODO: Proper error handling or proof .expect("Failed to initialize proof of time"); + let initial_key = config.initial_key; let (protocol_state, consensus_state) = init_pot_state(config, proof_of_time); Self { + initial_key, is_time_keeper, proof_of_time, protocol_state, diff --git a/crates/sc-proof-of-time/src/state_manager.rs b/crates/sc-proof-of-time/src/state_manager.rs index 7f07c3910f..777e3eed47 100644 --- a/crates/sc-proof-of-time/src/state_manager.rs +++ b/crates/sc-proof-of-time/src/state_manager.rs @@ -53,14 +53,6 @@ pub(crate) enum PotProtocolStateError { /// Error codes for PotConsensusState::get_block_proofs(). #[derive(Debug, thiserror::Error)] pub enum PotGetBlockProofsError { - #[error("Failed to get start slot: {summary:?}/{block_number}/{proof_slot}/{current_slot}")] - StartSlotMissing { - summary: PotStateSummary, - block_number: BlockNumber, - proof_slot: SlotNumber, - current_slot: SlotNumber, - }, - #[error( "Invalid slot range: {summary:?}/{block_number}/{start_slot}/{proof_slot}/{current_slot}" )] @@ -84,22 +76,6 @@ pub enum PotGetBlockProofsError { /// Error codes for PotConsensusState::verify_block_proofs(). #[derive(Debug, thiserror::Error)] pub enum PotVerifyBlockProofsError { - #[error("Block has no proofs: {summary:?}/{block_number}/{slot}/{parent_slot}")] - NoProofs { - summary: PotStateSummary, - block_number: BlockNumber, - slot: SlotNumber, - parent_slot: SlotNumber, - }, - - #[error("Failed to get start slot: {summary:?}/{block_number}/{slot}/{parent_slot}")] - StartSlotMissing { - summary: PotStateSummary, - block_number: BlockNumber, - slot: SlotNumber, - parent_slot: SlotNumber, - }, - #[error("Unexpected slot number: {summary:?}/{block_number}/{slot}/{parent_slot}/{expected_slot}/{actual_slot}")] UnexpectedSlot { summary: PotStateSummary, @@ -211,7 +187,7 @@ impl PotChain { if let Some(tip) = self.entries.back() { // This is a debug assert for now, as this should not happen. // Change to return error if needed. - debug_assert!((tip.slot_number + 1) == proof.slot_number); + debug_assert_eq!((tip.slot_number + 1), proof.slot_number); } if self.entries.len() == self.max_entries { // Evict the oldest entry if full @@ -361,6 +337,13 @@ impl InternalState { let tip = match self.chain.tip() { Some(tip) => tip.clone(), None => { + if proof.key != self.config.initial_key { + return Err(PotProtocolStateError::InvalidKey { + expected: self.config.initial_key, + actual: proof.key, + }); + } + // TODO: Check initial seed // Chain is empty, possible first proof. return Ok(()); } @@ -477,18 +460,22 @@ impl InternalState { &self, block_number: BlockNumber, current_slot: SlotNumber, - parent_pre_digest: &PotPreDigest, + maybe_parent_pre_digest: &Option, ) -> Result, PotGetBlockProofsError> { let summary = self.summary(); - let proof_slot = current_slot - self.config.global_randomness_reveal_lag_slots; - let start_slot = parent_pre_digest.next_block_initial_slot().ok_or_else(|| { - PotGetBlockProofsError::StartSlotMissing { - summary: summary.clone(), - block_number, - proof_slot, - current_slot, - } - })?; + // TODO: Saturating sub is a hack here to make things work for now, this will need to change + let proof_slot = + current_slot.saturating_sub(self.config.global_randomness_reveal_lag_slots); + // Get the expected slot of the first proof in this block. + let start_slot = maybe_parent_pre_digest + .as_ref() + .map(|parent_pre_digest| parent_pre_digest.next_block_initial_slot()) + // TODO: This fallback slot does not make sense and is just used to make things "run" + // for now + .unwrap_or(0); + + // TODO: This hack does not make sense and is just used to make things "run" for now + let proof_slot = proof_slot.max(start_slot); if start_slot > proof_slot { return Err(PotGetBlockProofsError::InvalidRange { @@ -505,7 +492,7 @@ impl InternalState { let mut iter = self.chain.iter().skip_while(|p| p.slot_number < start_slot); for slot in start_slot..=proof_slot { if let Some(proof) = iter.next() { - debug_assert!(proof.slot_number == slot); + debug_assert_eq!(proof.slot_number, slot); proofs.push(proof.clone()); } else { return Err(PotGetBlockProofsError::ProofUnavailable { @@ -527,27 +514,18 @@ impl InternalState { slot_number: SlotNumber, pre_digest: &PotPreDigest, parent_slot_number: SlotNumber, - parent_pre_digest: &PotPreDigest, + maybe_parent_pre_digest: &Option, ) -> Result<(), PotVerifyBlockProofsError> { let summary = self.summary(); - let block_proofs = pre_digest - .proofs() - .ok_or(PotVerifyBlockProofsError::NoProofs { - summary: summary.clone(), - block_number, - slot: slot_number, - parent_slot: parent_slot_number, - })?; + let block_proofs = pre_digest.proofs(); // Get the expected slot of the first proof in this block. - let start_slot = parent_pre_digest.next_block_initial_slot().ok_or_else(|| { - PotVerifyBlockProofsError::StartSlotMissing { - summary: summary.clone(), - block_number, - slot: slot_number, - parent_slot: parent_slot_number, - } - })?; + let start_slot = maybe_parent_pre_digest + .as_ref() + .map(|parent_pre_digest| parent_pre_digest.next_block_initial_slot()) + // TODO: This fallback slot does not make sense and is just used to make things "run" + // for now + .unwrap_or(0); // Since we check the first proof starts with the parent.last_proof.slot + 1, // and we already verified the seed/key of the proofs in the chain were was @@ -680,6 +658,19 @@ impl StateManager { err, } })?; + } else { + // TODO: This is ugly, but we need initial key here right now + let initial_key = self.state.lock().config.initial_key; + if proof.key != initial_key { + return Err(PotVerifyBlockProofsError::UnexpectedKey { + summary: summary.clone(), + block_number, + slot: slot_number, + parent_slot: parent_slot_number, + error_slot: proof.slot_number, + }); + } + // TODO: Check initial seed } to_add.push(proof.clone()); prev_proof = Some(proof.clone()); @@ -773,7 +764,7 @@ pub trait PotConsensusState: fmt::Debug + Send + Sync { &self, block_number: BlockNumber, current_slot: SlotNumber, - parent_pre_digest: &PotPreDigest, + maybe_parent_pre_digest: &Option, wait_time: Option, ) -> Result, PotGetBlockProofsError>; @@ -785,7 +776,7 @@ pub trait PotConsensusState: fmt::Debug + Send + Sync { slot_number: SlotNumber, pre_digest: &PotPreDigest, parent_slot_number: SlotNumber, - parent_pre_digest: &PotPreDigest, + maybe_parent_pre_digest: &Option, ) -> Result<(), PotVerifyBlockProofsError>; } @@ -795,7 +786,7 @@ impl PotConsensusState for StateManager { &self, block_number: BlockNumber, current_slot: SlotNumber, - parent_pre_digest: &PotPreDigest, + maybe_parent_pre_digest: &Option, wait_time: Option, ) -> Result, PotGetBlockProofsError> { let start_ts = Instant::now(); @@ -807,10 +798,11 @@ impl PotConsensusState for StateManager { let retry_delay = Duration::from_millis(200); let mut retries = 0; loop { - let result = - self.state - .lock() - .get_block_proofs(block_number, current_slot, parent_pre_digest); + let result = self.state.lock().get_block_proofs( + block_number, + current_slot, + maybe_parent_pre_digest, + ); match result { Ok(_) => return result, Err(PotGetBlockProofsError::ProofUnavailable { .. }) => { @@ -838,32 +830,31 @@ impl PotConsensusState for StateManager { slot_number: SlotNumber, pre_digest: &PotPreDigest, parent_slot_number: SlotNumber, - parent_pre_digest: &PotPreDigest, + maybe_parent_pre_digest: &Option, ) -> Result<(), PotVerifyBlockProofsError> { - if let Some(block_proofs) = pre_digest.proofs() { - // Opportunistically try to extend the chain with - // the proofs from the block. - // TODO: this also is done when the chain is empty and - // we don't have a previous proof to verify against. - // This needs to be revisited as part of the node sync. - let (tip, summary) = { - let state = self.state.lock(); - (state.tip(), state.summary()) - }; - let should_append = tip - .as_ref() - .map(|tip| (tip.slot_number + 1) == block_proofs.first().slot_number) - .unwrap_or(true); - if should_append { - return self.verify_and_append( - block_number, - slot_number, - block_proofs, - parent_slot_number, - tip, - summary, - ); - } + let block_proofs = pre_digest.proofs(); + // Opportunistically try to extend the chain with + // the proofs from the block. + // TODO: this also is done when the chain is empty and + // we don't have a previous proof to verify against. + // This needs to be revisited as part of the node sync. + let (tip, summary) = { + let state = self.state.lock(); + (state.tip(), state.summary()) + }; + let should_append = tip + .as_ref() + .map(|tip| (tip.slot_number + 1) == block_proofs.first().slot_number) + .unwrap_or(true); + if should_append { + return self.verify_and_append( + block_number, + slot_number, + block_proofs, + parent_slot_number, + tip, + summary, + ); } self.state.lock().verify_block_proofs( @@ -871,7 +862,7 @@ impl PotConsensusState for StateManager { slot_number, pre_digest, parent_slot_number, - parent_pre_digest, + maybe_parent_pre_digest, ) } } diff --git a/crates/sc-proof-of-time/src/time_keeper.rs b/crates/sc-proof-of-time/src/time_keeper.rs index 85804ad929..1dd68b9fa7 100644 --- a/crates/sc-proof-of-time/src/time_keeper.rs +++ b/crates/sc-proof-of-time/src/time_keeper.rs @@ -3,8 +3,8 @@ use crate::state_manager::PotProtocolState; use crate::PotComponents; use futures::channel::mpsc; -use futures::{SinkExt, StreamExt}; -use sc_client_api::BlockchainEvents; +use futures::SinkExt; +use sc_client_api::BlockBackend; use sp_blockchain::HeaderBackend; use sp_consensus_subspace::digests::extract_pre_digest; use sp_core::H256; @@ -13,7 +13,7 @@ use std::marker::PhantomData; use std::sync::Arc; use std::thread; use std::time::{Duration, Instant}; -use subspace_core_primitives::{NonEmptyVec, PotProof, PotSeed}; +use subspace_core_primitives::{NonEmptyVec, PotKey, PotProof, PotSeed}; use subspace_proof_of_time::ProofOfTime; use tokio::sync::mpsc::{channel, Receiver, Sender}; use tracing::{debug, error, trace, warn}; @@ -25,6 +25,8 @@ const PROOFS_CHANNEL_SIZE: usize = 12; // 2 * reveal lag. /// The time keeper manages the protocol: periodic proof generation/verification, gossip. pub struct TimeKeeper { + // TODO: Remove this from here, shouldn't be necessary eventually + initial_key: PotKey, proof_of_time: ProofOfTime, pot_state: Arc, client: Arc, @@ -39,7 +41,7 @@ pub struct TimeKeeper { impl TimeKeeper where Block: BlockT, - Client: HeaderBackend + BlockchainEvents, + Client: HeaderBackend + BlockBackend, { /// Creates the time keeper instance. pub fn new( @@ -49,6 +51,7 @@ where gossip_sender: mpsc::Sender, ) -> Self { Self { + initial_key: components.initial_key, proof_of_time: components.proof_of_time, pot_state: Arc::clone(&components.protocol_state), client, @@ -74,61 +77,61 @@ where /// Initializes the chain state from the consensus tip info. async fn initialize(&self) { - debug!("Waiting for initialization"); - - // Wait for the first block import. - let mut block_import = self.client.import_notification_stream(); - while let Some(incoming_block) = block_import.next().await { - let pre_digest = match extract_pre_digest(&incoming_block.header) { - Ok(pre_digest) => pre_digest, - Err(error) => { - warn!( - %error, - block_hash = %incoming_block.hash, - origin = ?incoming_block.origin, - "Failed to get pre_digest", - ); - continue; - } - }; - - let pot_pre_digest = match pre_digest.pot_pre_digest() { - Some(pot_pre_digest) => pot_pre_digest, - None => { - warn!( - block_hash = %incoming_block.hash, - origin = ?incoming_block.origin, - "Failed to get pot_pre_digest", - ); - continue; - } - }; + debug!("Initializing timekeeper"); + + let best_hash = self.client.info().best_hash; + let best_block = match self.client.block(best_hash) { + Ok(maybe_best_header) => maybe_best_header.expect("Best block must exist; qed").block, + Err(error) => { + // TODO: This is very bad, initialization must become fallible + error!( + %error, + %best_hash, + "Failed to get best block", + ); + return; + } + }; + + let pre_digest = match extract_pre_digest(best_block.header()) { + Ok(pre_digest) => pre_digest, + Err(error) => { + // TODO: This is very bad, initialization must become fallible + error!( + %error, + %best_hash, + "Failed to get pre_digest", + ); + return; + } + }; - debug!( - block_hash = %incoming_block.hash, - origin = ?incoming_block.origin, - ?pot_pre_digest, - "Initialization complete", - ); - let proofs = pot_pre_digest.proofs().cloned().unwrap_or_else(|| { - // Producing proofs starting from (genesis_slot + 1). - // TODO: Proper error handling or proof - let block_hash = incoming_block.hash.into(); + let maybe_pot_pre_digest = pre_digest.pot_pre_digest(); + + let proofs = match maybe_pot_pre_digest { + Some(pot_pre_digest) => pot_pre_digest.proofs().clone(), + None => { + // TODO: We shouldn't need to generate proofs here, but current state expects parent + // proofs to exist + // No proof of time means genesis block, produce the very first proof let proof = self.proof_of_time.create( - PotSeed::from_genesis_block_hash(block_hash), - Default::default(), // TODO: key from cmd line or BTC - pot_pre_digest - .next_block_initial_slot() - .expect("Initial slot number should be available for block_number >= 1"), - block_hash, + PotSeed::from_genesis_block_hash(best_hash.into()), + self.initial_key, + 0, + best_hash.into(), ); - debug!(%proof, "Creating first proof"); + debug!(%proof, "Created the first proof"); NonEmptyVec::new_with_entry(proof) - }); + } + }; - self.pot_state.reset(proofs); - return; - } + self.pot_state.reset(proofs); + + debug!( + %best_hash, + ?maybe_pot_pre_digest, + "Initialization complete", + ); } /// Starts the thread to produce the proofs. diff --git a/crates/sp-consensus-subspace/src/digests.rs b/crates/sp-consensus-subspace/src/digests.rs index 09b0181356..46dfcbe4a8 100644 --- a/crates/sp-consensus-subspace/src/digests.rs +++ b/crates/sp-consensus-subspace/src/digests.rs @@ -67,14 +67,6 @@ impl PreDigest { /// versioning added on the proof side #[derive(Clone, Encode, Decode)] pub enum PotPreDigest { - /// The block was produced in the bootstrapping phase, where - /// the genesis slot has not yet been determined and the proof - /// production has not started. - Bootstrapping, - - /// Genesis slot determined by the bootstrap node. - FirstBlock(SlotNumber), - /// V0 proof. V0(NonEmptyVec), } @@ -86,50 +78,31 @@ impl PotPreDigest { } /// Returns a reference to the proofs. - pub fn proofs(&self) -> Option<&NonEmptyVec> { + pub fn proofs(&self) -> &NonEmptyVec { match self { - Self::Bootstrapping | Self::FirstBlock(_) => None, - Self::V0(proofs) => Some(proofs), + Self::V0(proofs) => proofs, } } /// Returns the starting slot number for the proofs in the next /// block. - pub fn next_block_initial_slot(&self) -> Option { + pub fn next_block_initial_slot(&self) -> SlotNumber { match self { - Self::Bootstrapping => None, - Self::FirstBlock(slot_number) => Some(slot_number + 1), - Self::V0(proofs) => Some(proofs.last().slot_number + 1), + Self::V0(proofs) => proofs.last().slot_number + 1, } } /// Returns the global randomness from the proofs in the block pre digest. pub fn derive_global_randomness(&self) -> Randomness { match self { - Self::Bootstrapping | Self::FirstBlock(_) => Randomness::default(), Self::V0(proofs) => proofs.last().derive_global_randomness().into(), } } } -impl Default for PotPreDigest { - fn default() -> Self { - Self::Bootstrapping - } -} - impl fmt::Debug for PotPreDigest { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Bootstrapping => { - write!(f, "PotPreDigest::Bootstrapping") - } - Self::FirstBlock(slot_number) => { - write!( - f, - "PotPreDigest::FirstBlock => genesis_slot = {slot_number}" - ) - } Self::V0(proofs) => { write!( f, @@ -640,17 +613,6 @@ where } } - // If global randomness was not in the logs, try to get it from the POT - #[cfg(feature = "pot")] - if maybe_global_randomness.is_none() { - maybe_global_randomness = maybe_pre_digest.as_ref().and_then(|pre_digest| { - pre_digest - .proof_of_time - .as_ref() - .map(|pot| pot.derive_global_randomness()) - }); - } - Ok(SubspaceDigestItems { pre_digest: maybe_pre_digest.ok_or(Error::Missing(ErrorDigestType::PreDigest))?, signature: maybe_seal,