Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b4004d6
Small refactoring with to reduce code duplication and prepare for PoT…
nazar-pc Sep 4, 2023
95fc104
Support parameters change in `PotSource` and block import/verificatio…
nazar-pc Sep 4, 2023
6cbde16
Make `PotSource` follow canonical fork of the blockchain and rebase P…
nazar-pc Sep 4, 2023
bd5051a
Refactor `update_next_slot_input` to take updated parameters change v…
nazar-pc Sep 4, 2023
96291ce
Small tweaks in `PotSource` to remove unnecessary `.await`
nazar-pc Sep 4, 2023
521878e
Move `update_next_slot_input` into new struct `PotState` and its meth…
nazar-pc Sep 4, 2023
76744ee
Introduce `PotState::try_extend` to generalize more of state management
nazar-pc Sep 4, 2023
efcc84c
Move `gossip` module under `source`
nazar-pc Sep 4, 2023
5010a4f
Modify `PotState` to become sharable data structure with updates usin…
nazar-pc Sep 4, 2023
fd89282
Move `run_timekeeper` function into `timekeeper` submodule
nazar-pc Sep 5, 2023
4932a4b
Make timekeeper rebase to latest proof of time available if necessary
nazar-pc Sep 5, 2023
cddb769
Fix verification
nazar-pc Sep 6, 2023
fcf7e4d
Tiny formatting change
nazar-pc Sep 6, 2023
e49a68a
Remove timekeeper reorg of PoT chain, only blockchain should do reorg…
nazar-pc Sep 6, 2023
105ac8e
Fix initial slot in proof of time chain verification and improve veri…
nazar-pc Sep 7, 2023
42d4b03
Clean up old checkpoints in slot worker in case PoT chain reorg happened
nazar-pc Sep 7, 2023
d63dd4d
Implementation of block import handling without runtime API in `PotSo…
nazar-pc Sep 7, 2023
1b9ed48
Check proof of time in slot worker to ensure bad proofs of time are n…
nazar-pc Sep 7, 2023
7ed2bd1
Relax reorg check in PoT state
nazar-pc Sep 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/pallet-subspace/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ mod pallet {
pub(super) type GlobalRandomnesses<T> =
StorageValue<_, sp_consensus_subspace::GlobalRandomnesses, ValueQuery>;

// TODO: Clarify when this value is updated (when it is updated, right now it is not)
/// Number of iterations for proof of time per slot
#[pallet::storage]
pub(super) type PotSlotIterations<T> = StorageValue<_, NonZeroU32>;
Expand Down
35 changes: 33 additions & 2 deletions crates/sc-consensus-subspace/src/import_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,16 +234,47 @@ where
return Ok(CheckedHeader::Deferred(header, slot));
}

#[cfg(feature = "pot")]
let slot_iterations;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we put these three variables and two following if-statements under the same cfg-block?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did that in some cases, here doing let (slot_iterations, pot_seed, next_slot) = {} would be hardly more readable.

#[cfg(feature = "pot")]
let pot_seed;
#[cfg(feature = "pot")]
let next_slot = slot + Slot::from(1);
#[cfg(feature = "pot")]
// The change to number of iterations might have happened before `next_slot`
if let Some(parameters_change) = subspace_digest_items.pot_parameters_change
&& parameters_change.slot <= next_slot
{
slot_iterations = parameters_change.slot_iterations;
// Only if entropy injection happens exactly on next slot we need to mix it in
if parameters_change.slot == next_slot {
pot_seed = pre_digest
.pot_info()
.proof_of_time()
.seed_with_entropy(&parameters_change.entropy);
} else {
pot_seed = pre_digest.pot_info().proof_of_time().seed();
}
} else {
slot_iterations = subspace_digest_items.pot_slot_iterations;
pot_seed = pre_digest.pot_info().proof_of_time().seed();
}

// TODO: Extend/optimize this check once we have checkpoints in justifications
// Check proof of time between slot of the block and future proof of time
// Here during stateless verification we do not have access to parent block, thus only
// verify proofs after proof of time of at current slot up until future proof of time
// (inclusive), during block import we verify the rest.
#[cfg(feature = "pot")]
if !self
.pot_verifier
.is_proof_valid(
pre_digest.pot_info().proof_of_time().seed(),
subspace_digest_items.pot_slot_iterations,
next_slot,
pot_seed,
slot_iterations,
self.chain_constants.block_authoring_delay(),
pre_digest.pot_info().future_proof_of_time(),
subspace_digest_items.pot_parameters_change,
)
.await
{
Expand Down
39 changes: 37 additions & 2 deletions crates/sc-consensus-subspace/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

#![doc = include_str!("../README.md")]
#![feature(try_blocks)]
#![feature(let_chains, try_blocks)]
#![forbid(unsafe_code)]
#![warn(missing_docs)]

Expand Down Expand Up @@ -401,6 +401,10 @@ where
/// Handle use to report telemetries.
pub telemetry: Option<TelemetryHandle>,

/// Proof of time verifier
#[cfg(feature = "pot")]
pub pot_verifier: PotVerifier,

/// Stream with proof of time slots.
#[cfg(feature = "pot")]
pub pot_slot_info_stream: PotSlotInfoStream,
Expand All @@ -424,6 +428,8 @@ pub fn start_subspace<PosTable, Block, Client, SC, E, I, SO, CIDP, BS, L, AS, Er
max_block_proposal_slot_portion,
telemetry,
#[cfg(feature = "pot")]
pot_verifier,
#[cfg(feature = "pot")]
pot_slot_info_stream,
}: SubspaceParams<Block, Client, SC, E, I, SO, L, CIDP, BS, AS>,
) -> Result<SubspaceWorker, sp_consensus::Error>
Expand Down Expand Up @@ -476,6 +482,8 @@ where
pending_solutions: Default::default(),
#[cfg(feature = "pot")]
pot_checkpoints: Default::default(),
#[cfg(feature = "pot")]
pot_verifier,
_pos_table: PhantomData::<PosTable>,
};

Expand Down Expand Up @@ -745,6 +753,8 @@ where
let correct_global_randomness;
#[cfg(feature = "pot")]
let pot_seed;
#[cfg(feature = "pot")]
let slot_iterations;
let correct_solution_range;

if block_number.is_one() {
Expand All @@ -759,6 +769,11 @@ where
}
#[cfg(feature = "pot")]
{
slot_iterations = self
.client
.runtime_api()
.pot_parameters(parent_hash)?
.slot_iterations();
pot_seed = self.pot_verifier.genesis_seed();
}

Expand All @@ -782,7 +797,19 @@ where
};
}
#[cfg(feature = "pot")]
// In case parameters change in the very first slot after slot of the parent block,
// account for them
if let Some(parameters_change) = subspace_digest_items.pot_parameters_change
&& parameters_change.slot == (parent_slot + Slot::from(1))
{
slot_iterations = parameters_change.slot_iterations;
pot_seed = parent_subspace_digest_items
.pre_digest
.pot_info()
.proof_of_time()
.seed_with_entropy(&parameters_change.entropy);
} else {
slot_iterations = subspace_digest_items.pot_slot_iterations;
pot_seed = parent_subspace_digest_items
.pre_digest
.pot_info()
Expand All @@ -802,13 +829,21 @@ where
}
#[cfg(feature = "pot")]
// TODO: Extend/optimize this check once we have checkpoints in justifications
// Here we check that there is continuity from parent block's proof of time (but not future
// entropy since this block may be produced before slot corresponding to parent block's
// future proof of time) to current block's proof of time. During stateless verification we
// do not have access to parent block, thus only verify proofs after proof of time of at
// current slot up until future proof of time (inclusive), here during block import we
// verify the rest.
if !self
.pot_verifier
.is_proof_valid(
parent_slot + Slot::from(1),
pot_seed,
subspace_digest_items.pot_slot_iterations,
slot_iterations,
slots_since_parent,
subspace_digest_items.pre_digest.pot_info().proof_of_time(),
subspace_digest_items.pot_parameters_change,
)
.await
{
Expand Down
116 changes: 95 additions & 21 deletions crates/sc-consensus-subspace/src/slot_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ use sc_consensus_slots::{
BackoffAuthoringBlocksStrategy, SimpleSlotWorker, SlotInfo, SlotLenienceType, SlotProportion,
};
#[cfg(feature = "pot")]
use sc_proof_of_time::verifier::PotVerifier;
#[cfg(feature = "pot")]
use sc_proof_of_time::PotSlotWorker;
use sc_telemetry::TelemetryHandle;
use sc_utils::mpsc::tracing_unbounded;
Expand Down Expand Up @@ -140,6 +142,8 @@ where
#[cfg(feature = "pot")]
// TODO: Substrate should make `fn claim_slot` take `&mut self`, the we'll not need `Mutex`
pub(super) pot_checkpoints: Mutex<BTreeMap<Slot, PotCheckpoints>>,
#[cfg(feature = "pot")]
pub(super) pot_verifier: PotVerifier,
pub(super) _pos_table: PhantomData<PosTable>,
}

Expand All @@ -153,7 +157,14 @@ where
SO: SyncOracle + Send + Sync,
{
fn on_proof(&mut self, slot: Slot, checkpoints: PotCheckpoints) {
self.pot_checkpoints.lock().insert(slot, checkpoints);
{
let mut pot_checkpoints = self.pot_checkpoints.lock();

// Remove checkpoints from future slots, if present they are out of date anyway
pot_checkpoints.retain(|&stored_slot, _checkpoints| stored_slot < slot);

pot_checkpoints.insert(slot, checkpoints);
}

if self.sync_oracle.is_major_syncing() {
debug!(
Expand Down Expand Up @@ -303,27 +314,90 @@ where

#[cfg(feature = "pot")]
let (proof_of_time, future_proof_of_time, new_checkpoints) = {
let mut pot_checkpoints = self.pot_checkpoints.lock();
// TODO: These variables and code block below are only necessary to work around
// https://github.com/rust-lang/rust/issues/57478
let proof_of_time;
let future_slot;
let future_proof_of_time;
let new_checkpoints;
{
let mut pot_checkpoints = self.pot_checkpoints.lock();

// Remove checkpoints from old slots we will not need anymore
pot_checkpoints.retain(|&stored_slot, _checkpoints| stored_slot > parent_slot);

proof_of_time = pot_checkpoints.get(&slot)?.output();

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

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

// Remove checkpoints from old slots we will not need anymore
pot_checkpoints.retain(|&stored_slot, _checkpoints| stored_slot > parent_slot);

let proof_of_time = pot_checkpoints.get(&slot)?.output();

// 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 parent_future_slot = parent_slot + self.chain_constants.block_authoring_delay();
let future_proof_of_time = 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 = pot_checkpoints
.iter()
.filter_map(|(&stored_slot, &checkpoints)| {
(stored_slot > parent_future_slot && stored_slot <= future_slot)
.then_some(checkpoints)
})
.collect::<Vec<_>>();
let pot_parameters = runtime_api.pot_parameters(parent_hash).ok()?;
let slot_iterations;
let pot_seed;
let after_parent_slot = parent_slot + Slot::from(1);

if parent_header.number().is_zero() {
slot_iterations = pot_parameters.slot_iterations();
pot_seed = self.pot_verifier.genesis_seed();
} else {
let pot_info = parent_pre_digest.pot_info();
// The change to number of iterations might have happened before
// `after_parent_slot`
if let Some(parameters_change) = pot_parameters.next_parameters_change()
&& parameters_change.slot <= after_parent_slot
{
slot_iterations = parameters_change.slot_iterations;
// Only if entropy injection happens exactly after parent slot we need to \
// mix it in
if parameters_change.slot == after_parent_slot {
pot_seed = pot_info
.proof_of_time()
.seed_with_entropy(&parameters_change.entropy);
} else {
pot_seed = pot_info
.proof_of_time().seed();
}
} else {
slot_iterations = pot_parameters.slot_iterations();
pot_seed = pot_info
.proof_of_time()
.seed();
}
};

// Ensure proof of time and future proof of time included in upcoming block are valid
if !self
.pot_verifier
.is_proof_valid(
after_parent_slot,
pot_seed,
slot_iterations,
Slot::from(u64::from(future_slot) - u64::from(parent_slot)),
future_proof_of_time,
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:?}"
);
return None;
}

(proof_of_time, future_proof_of_time, new_checkpoints)
};
Expand Down
3 changes: 2 additions & 1 deletion crates/sc-proof-of-time/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Subspace proof of time implementation.

pub mod gossip;
#![feature(let_chains, stmt_expr_attributes)]

mod slots;
pub mod source;
pub mod verifier;
Expand Down
Loading