diff --git a/crates/sc-proof-of-time/src/source.rs b/crates/sc-proof-of-time/src/source.rs index d8b4d96030..d78436b58f 100644 --- a/crates/sc-proof-of-time/src/source.rs +++ b/crates/sc-proof-of-time/src/source.rs @@ -3,7 +3,7 @@ mod state; mod timekeeper; use crate::source::gossip::{GossipProof, PotGossipWorker, ToGossipMessage}; -use crate::source::state::{NextSlotInput, PotState}; +use crate::source::state::{NextSlotInput, PotState, PotStateUpdateOutcome}; use crate::source::timekeeper::{run_timekeeper, TimekeeperProof}; use crate::verifier::PotVerifier; use core_affinity::CoreId; @@ -27,7 +27,7 @@ use std::marker::PhantomData; use std::sync::Arc; use std::thread; use subspace_core_primitives::PotCheckpoints; -use tracing::{debug, error, warn}; +use tracing::{debug, error, trace, warn}; const LOCAL_PROOFS_CHANNEL_CAPACITY: usize = 10; const SLOTS_CHANNEL_CAPACITY: usize = 10; @@ -335,24 +335,54 @@ where // This will do one of 3 things depending on circumstances: // * if block import is ahead of timekeeper and gossip, it will update next slot input // * if block import is on a different PoT chain, it will update next slot input to the - // correct fork + // correct fork (reorg) // * if block import is on the same PoT chain this will essentially do nothing - if let Some(next_slot_input) = self.state.update( + match self.state.update( best_slot, best_proof, Some(subspace_digest_items.pot_parameters_change), ) { - warn!("Proof of time chain reorg happened"); + PotStateUpdateOutcome::NoChange => { + trace!( + %best_slot, + "Block import didn't result in proof of time chain changes", + ); + } + PotStateUpdateOutcome::Extension { from, to } => { + warn!( + from_next_slot = %from.slot, + to_next_slot = %to.slot, + "Proof of time chain was extended from block import", + ); - if self - .to_gossip_sender - .try_send(ToGossipMessage::NextSlotInput(next_slot_input)) - .is_err() - { - debug!( - next_slot = %next_slot_input.slot, - "Gossip is not able to keep-up with slot production (block import)", + if self + .to_gossip_sender + .try_send(ToGossipMessage::NextSlotInput(to)) + .is_err() + { + debug!( + next_slot = %to.slot, + "Gossip is not able to keep-up with slot production (block import)", + ); + } + } + PotStateUpdateOutcome::Reorg { from, to } => { + warn!( + from_next_slot = %from.slot, + to_next_slot = %to.slot, + "Proof of time chain reorg happened", ); + + if self + .to_gossip_sender + .try_send(ToGossipMessage::NextSlotInput(to)) + .is_err() + { + debug!( + next_slot = %to.slot, + "Gossip is not able to keep-up with slot production (block import)", + ); + } } } } diff --git a/crates/sc-proof-of-time/src/source/state.rs b/crates/sc-proof-of-time/src/source/state.rs index eadcb369a0..718f5bdcff 100644 --- a/crates/sc-proof-of-time/src/source/state.rs +++ b/crates/sc-proof-of-time/src/source/state.rs @@ -73,6 +73,19 @@ impl InnerState { } } +#[derive(Debug)] +pub(super) enum PotStateUpdateOutcome { + NoChange, + Extension { + from: NextSlotInput, + to: NextSlotInput, + }, + Reorg { + from: NextSlotInput, + to: NextSlotInput, + }, +} + #[derive(Debug)] pub(super) struct PotState { inner_state: Atomic, @@ -145,7 +158,7 @@ impl PotState { best_slot: Slot, best_output: PotOutput, maybe_updated_parameters_change: Option>, - ) -> Option { + ) -> PotStateUpdateOutcome { let mut best_state = None; // Use `fetch_update` such that we don't accidentally downgrade best slot to smaller value let previous_best_state = self @@ -163,7 +176,51 @@ impl PotState { .expect("Callback always returns `Some`; qed"); let best_state = best_state.expect("Replaced with `Some` above; qed"); - (previous_best_state.next_slot_input != best_state.next_slot_input) - .then_some(best_state.next_slot_input) + if previous_best_state.next_slot_input == best_state.next_slot_input { + return PotStateUpdateOutcome::NoChange; + } + + if previous_best_state.next_slot_input.slot < best_state.next_slot_input.slot { + let mut slot_iterations = previous_best_state.next_slot_input.slot_iterations; + let mut seed = previous_best_state.next_slot_input.seed; + + for slot in u64::from(previous_best_state.next_slot_input.slot) + ..u64::from(best_state.next_slot_input.slot) + { + let slot = Slot::from(slot); + + let Some(checkpoints) = self.verifier.try_get_checkpoints(seed, slot_iterations) + else { + break; + }; + + let next_slot = slot + Slot::from(1); + + // In case parameters change in the next slot, account for them + if let Some(Some(parameters_change)) = maybe_updated_parameters_change + && parameters_change.slot == next_slot + { + slot_iterations = parameters_change.slot_iterations; + seed = checkpoints.output().seed_with_entropy(¶meters_change.entropy); + } else { + seed = checkpoints.output().seed(); + } + + if next_slot == best_state.next_slot_input.slot + && slot_iterations == best_state.next_slot_input.slot_iterations + && seed == best_state.next_slot_input.seed + { + return PotStateUpdateOutcome::Extension { + from: previous_best_state.next_slot_input, + to: best_state.next_slot_input, + }; + } + } + } + + PotStateUpdateOutcome::Reorg { + from: previous_best_state.next_slot_input, + to: best_state.next_slot_input, + } } }