Skip to content

Commit

Permalink
Merge pull request #2038 from subspace/pot-fix-slot-worker-after-pot-…
Browse files Browse the repository at this point in the history
…reorg

Fix slot worker after PoT reorg
  • Loading branch information
nazar-pc authored Oct 2, 2023
2 parents 787039b + d45a9aa commit 3abb09a
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 59 deletions.
97 changes: 55 additions & 42 deletions crates/sc-consensus-subspace/src/slot_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ where
parent_slot + self.chain_constants.block_authoring_delay()
};

let (proof_of_time, future_proof_of_time, new_checkpoints) = {
let (proof_of_time, future_proof_of_time, pot_justification) = {
// Remove checkpoints from old slots we will not need anymore
self.pot_checkpoints
.retain(|&stored_slot, _checkpoints| stored_slot > parent_slot);
Expand All @@ -307,18 +307,6 @@ where

// Future slot for which proof must be available before authoring block at this slot
let future_slot = slot + self.chain_constants.block_authoring_delay();
let future_proof_of_time = self.pot_checkpoints.get(&future_slot)?.output();

// New checkpoints that were produced since parent block's future slot up to current
// future slot (inclusive)
let new_checkpoints = self
.pot_checkpoints
.iter()
.filter_map(|(&stored_slot, &checkpoints)| {
(stored_slot > parent_future_slot && stored_slot <= future_slot)
.then_some(checkpoints)
})
.collect::<Vec<_>>();

let pot_input = if parent_header.number().is_zero() {
PotNextSlotInput {
Expand All @@ -335,26 +323,73 @@ where
)
};

// Ensure proof of time and future proof of time included in upcoming block are valid
// Ensure proof of time is valid according to parent block
if !self
.pot_verifier
.is_output_valid(
pot_input,
Slot::from(u64::from(future_slot) - u64::from(parent_slot)),
future_proof_of_time,
Slot::from(u64::from(slot) - u64::from(parent_slot)),
proof_of_time,
parent_pot_parameters.next_parameters_change(),
)
.await
{
warn!(
target: "subspace",
"Proof of time or future proof of time is invalid, skipping block \
production at slot {slot:?}"
"Proof of time is invalid, skipping block authoring at slot {slot:?}"
);
return None;
}

(proof_of_time, future_proof_of_time, new_checkpoints)
let mut checkpoints_pot_input = if parent_header.number().is_zero() {
PotNextSlotInput {
slot: parent_slot + Slot::from(1),
slot_iterations: parent_pot_parameters.slot_iterations(),
seed: self.pot_verifier.genesis_seed(),
}
} else {
let parent_pot_info = parent_pre_digest.pot_info();

PotNextSlotInput::derive(
parent_pot_parameters.slot_iterations(),
parent_future_slot,
parent_pot_info.future_proof_of_time(),
&parent_pot_parameters.next_parameters_change(),
)
};
let seed = checkpoints_pot_input.seed;

let mut checkpoints = Vec::with_capacity((*future_slot - *parent_future_slot) as usize);

for slot in *parent_future_slot + 1..=*future_slot {
let slot = Slot::from(slot);
let maybe_slot_checkpoints_fut = self.pot_verifier.get_checkpoints(
checkpoints_pot_input.slot_iterations,
checkpoints_pot_input.seed,
);
let Some(slot_checkpoints) = maybe_slot_checkpoints_fut.await else {
warn!("Proving failed during block authoring");
return None;
};

checkpoints.push(slot_checkpoints);

checkpoints_pot_input = PotNextSlotInput::derive(
checkpoints_pot_input.slot_iterations,
slot,
slot_checkpoints.output(),
&parent_pot_parameters.next_parameters_change(),
);
}

let future_proof_of_time = checkpoints
.last()
.expect("Never empty, there is at least one slot between blocks; qed")
.output();

let pot_justification = SubspaceJustification::PotCheckpoints { seed, checkpoints };

(proof_of_time, future_proof_of_time, pot_justification)
};

let mut solution_receiver = {
Expand Down Expand Up @@ -515,29 +550,7 @@ where
}
}

let pot_seed = if parent_header.number().is_zero() {
self.pot_verifier.genesis_seed()
} else {
let parent_pot_info = parent_pre_digest.pot_info();

PotNextSlotInput::derive(
parent_pot_parameters.slot_iterations(),
parent_future_slot,
parent_pot_info.future_proof_of_time(),
&parent_pot_parameters.next_parameters_change(),
)
.seed
};

maybe_pre_digest.map(|pre_digest| {
(
pre_digest,
SubspaceJustification::PotCheckpoints {
seed: pot_seed,
checkpoints: new_checkpoints,
},
)
})
maybe_pre_digest.map(|pre_digest| (pre_digest, pot_justification))
}

fn pre_digest_data(
Expand Down
4 changes: 2 additions & 2 deletions crates/sc-proof-of-time/src/source/gossip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ where

if let Some(verified_checkpoints) = self
.pot_verifier
.try_get_checkpoints(proof.seed, proof.slot_iterations)
.try_get_checkpoints(proof.slot_iterations, proof.seed)
{
if verified_checkpoints != proof.checkpoints {
trace!(
Expand Down Expand Up @@ -334,7 +334,7 @@ where
if proof.slot != next_slot_input.slot {
let invalid_proof = self
.pot_verifier
.try_get_checkpoints(proof.seed, proof.slot_iterations)
.try_get_checkpoints(proof.slot_iterations, proof.seed)
.map(|verified_checkpoints| verified_checkpoints != proof.checkpoints)
.unwrap_or_default();

Expand Down
4 changes: 2 additions & 2 deletions crates/sc-proof-of-time/src/source/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ impl InnerState {

// Advance further as far as possible using previously verified proofs/checkpoints
if let Some(checkpoints) = pot_verifier.try_get_checkpoints(
self.next_slot_input.seed,
self.next_slot_input.slot_iterations,
self.next_slot_input.seed,
) {
best_slot = self.next_slot_input.slot;
best_output = checkpoints.output();
Expand Down Expand Up @@ -163,7 +163,7 @@ impl PotState {
{
let slot = Slot::from(slot);

let Some(checkpoints) = self.verifier.try_get_checkpoints(seed, slot_iterations)
let Some(checkpoints) = self.verifier.try_get_checkpoints(slot_iterations, seed)
else {
break;
};
Expand Down
55 changes: 42 additions & 13 deletions crates/sc-proof-of-time/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,40 @@ impl PotVerifier {
self.genesis_seed
}

/// Try to get checkpoints for provided seed and slot iterations, returns `None` if proving
/// fails internally.
pub async fn get_checkpoints(
&self,
slot_iterations: NonZeroU32,
seed: PotSeed,
) -> Option<PotCheckpoints> {
// TODO: This "proxy" is a workaround for https://github.com/rust-lang/rust/issues/57478
let (result_sender, result_receiver) = oneshot::channel();
tokio::task::spawn_blocking({
let verifier = self.clone();

move || {
futures::executor::block_on({
async move {
// Result doesn't matter here
let _ = result_sender.send(
verifier
.calculate_checkpoints(slot_iterations, seed, true)
.await,
);
}
});
}
});

result_receiver.await.unwrap_or_default()
}

/// Try to get checkpoints quickly without waiting for potentially locked async mutex or proving
pub fn try_get_checkpoints(
&self,
seed: PotSeed,
slot_iterations: NonZeroU32,
seed: PotSeed,
) -> Option<PotCheckpoints> {
let cache_key = CacheKey {
seed,
Expand Down Expand Up @@ -136,9 +165,9 @@ impl PotVerifier {
// Result doesn't matter here
let _ = result_sender.send(
verifier
.calculate_output(
input.seed,
.calculate_checkpoints(
input.slot_iterations,
input.seed,
do_proving_if_necessary,
)
.await,
Expand All @@ -148,20 +177,21 @@ impl PotVerifier {
}
});

let Ok(Some(calculated_proof)) = result_receiver.await else {
let Ok(Some(calculated_checkpoints)) = result_receiver.await else {
return false;
};
let calculated_output = calculated_checkpoints.output();

slots -= 1;

if slots == 0 {
return output == calculated_proof;
return output == calculated_output;
}

input = PotNextSlotInput::derive(
input.slot_iterations,
input.slot,
calculated_proof,
calculated_output,
&maybe_parameters_change,
);
}
Expand All @@ -171,12 +201,12 @@ impl PotVerifier {
// TODO: False-positive, lock is not actually held over await point, remove suppression once
// fixed upstream
#[allow(clippy::await_holding_lock)]
async fn calculate_output(
async fn calculate_checkpoints(
&self,
seed: PotSeed,
slot_iterations: NonZeroU32,
seed: PotSeed,
do_proving_if_necessary: bool,
) -> Option<PotOutput> {
) -> Option<PotCheckpoints> {
let cache_key = CacheKey {
seed,
slot_iterations,
Expand All @@ -188,8 +218,8 @@ impl PotVerifier {
if let Some(cache_value) = maybe_cache_value {
drop(cache);
let correct_checkpoints = cache_value.checkpoints.lock().await;
if let Some(correct_checkpoints) = correct_checkpoints.as_ref() {
return Some(correct_checkpoints.output());
if let Some(correct_checkpoints) = *correct_checkpoints {
return Some(correct_checkpoints);
}

// There was another verification for these inputs and it wasn't successful, retry
Expand Down Expand Up @@ -242,9 +272,8 @@ impl PotVerifier {
return None;
};

let proof = generated_checkpoints.output();
checkpoints.replace(generated_checkpoints);
return Some(proof);
return Some(generated_checkpoints);
}
}

Expand Down

0 comments on commit 3abb09a

Please sign in to comment.