diff --git a/crates/sc-consensus-subspace/src/authorship.rs b/crates/sc-consensus-subspace/src/authorship.rs
deleted file mode 100644
index 2e538aa9159b1..0000000000000
--- a/crates/sc-consensus-subspace/src/authorship.rs
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright (C) 2021 Subspace Labs, Inc.
-// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
-
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
-
-use crate::slot_worker::SubspaceSlotWorker;
-use crate::{subspace_err, verification, Epoch, Error, NewSlotInfo, NewSlotNotification};
-use futures::StreamExt;
-use log::{debug, trace, warn};
-use sc_consensus::block_import::BlockImport;
-use sc_consensus_epochs::ViableEpochDescriptor;
-use sc_consensus_slots::BackoffAuthoringBlocksStrategy;
-use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver};
-use schnorrkel::SecretKey;
-use sp_api::{NumberFor, ProvideRuntimeApi};
-use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata, ProvideCache};
-use sp_consensus::{Environment, Error as ConsensusError, Proposer, SyncOracle};
-use sp_consensus_slots::Slot;
-use sp_consensus_subspace::digests::{
- CompatibleDigestItem, NextSaltDescriptor, NextSolutionRangeDescriptor, PreDigest, Solution,
-};
-use sp_consensus_subspace::{ConsensusLog, SubspaceApi, SUBSPACE_ENGINE_ID};
-use sp_core::sr25519::Pair;
-use sp_runtime::generic::{BlockId, OpaqueDigestItemId};
-use sp_runtime::traits::{Block as BlockT, DigestItemFor, Header, Zero};
-pub use subspace_archiving::archiver::ArchivedSegment;
-use subspace_core_primitives::{Randomness, Salt};
-
-type EpochData = ViableEpochDescriptor<::Hash, NumberFor, Epoch>;
-
-type Claim = (PreDigest, Pair);
-
-/// Extract the next Subspace solution range digest from the given header if it exists.
-fn find_next_solution_range_digest(
- header: &B::Header,
-) -> Result, Error>
-where
- DigestItemFor: CompatibleDigestItem,
-{
- let mut next_solution_range_digest: Option<_> = None;
- for log in header.digest().logs() {
- trace!(target: "subspace", "Checking log {:?}, looking for next solution range digest.", log);
- let log = log.try_to::(OpaqueDigestItemId::Consensus(&SUBSPACE_ENGINE_ID));
- match (log, next_solution_range_digest.is_some()) {
- (Some(ConsensusLog::NextSolutionRangeData(_)), true) => {
- return Err(subspace_err(Error::MultipleNextSolutionRangeDigests))
- }
- (Some(ConsensusLog::NextSolutionRangeData(solution_range)), false) => {
- next_solution_range_digest = Some(solution_range)
- }
- _ => trace!(target: "subspace", "Ignoring digest not meant for us"),
- }
- }
-
- Ok(next_solution_range_digest)
-}
-
-/// Extract the next Subspace salt digest from the given header if it exists.
-fn find_next_salt_digest(
- header: &B::Header,
-) -> Result, Error>
-where
- DigestItemFor: CompatibleDigestItem,
-{
- let mut next_salt_digest: Option<_> = None;
- for log in header.digest().logs() {
- trace!(target: "subspace", "Checking log {:?}, looking for salt digest.", log);
- let log = log.try_to::(OpaqueDigestItemId::Consensus(&SUBSPACE_ENGINE_ID));
- match (log, next_salt_digest.is_some()) {
- (Some(ConsensusLog::NextSaltData(_)), true) => {
- return Err(subspace_err(Error::MultipleSaltDigests))
- }
- (Some(ConsensusLog::NextSaltData(salt)), false) => next_salt_digest = Some(salt),
- _ => trace!(target: "subspace", "Ignoring digest not meant for us"),
- }
- }
-
- Ok(next_salt_digest)
-}
-
-pub(crate) async fn claim_slot(
- worker: &SubspaceSlotWorker,
- parent_header: &B::Header,
- slot: Slot,
- epoch_descriptor: &EpochData,
-) -> Option
-where
- B: BlockT,
- C: ProvideRuntimeApi
- + ProvideCache
- + HeaderBackend
- + HeaderMetadata
- + 'static,
- C::Api: SubspaceApi,
- E: Environment + Send + Sync,
- E::Proposer: Proposer>,
- I: BlockImport> + Send + Sync + 'static,
- SO: SyncOracle + Send + Sync + Clone,
- L: sc_consensus::JustificationSyncLink,
- BS: BackoffAuthoringBlocksStrategy> + Send + Sync,
- Error: Send + From + From + 'static,
-{
- debug!(target: "subspace", "Attempting to claim slot {}", slot);
-
- struct PreparedData {
- block_id: BlockId,
- solution_range: u64,
- epoch_randomness: Randomness,
- salt: Salt,
- solution_receiver: TracingUnboundedReceiver<(Solution, Vec)>,
- }
-
- let parent_block_id = BlockId::Hash(parent_header.hash());
- let maybe_prepared_data: Option> = try {
- let epoch_changes = worker.subspace_link.epoch_changes.shared_data();
- let epoch = epoch_changes.viable_epoch(epoch_descriptor, |slot| {
- Epoch::genesis(&worker.subspace_link.config, slot)
- })?;
- let epoch_randomness = epoch.as_ref().randomness;
- // Here we always use parent block as the source of information, thus on the edge of the
- // era the very first block of the era still uses solution range from the previous one,
- // but the block after it uses "next" solution range deposited in the first block.
- let solution_range = find_next_solution_range_digest::(parent_header)
- .ok()?
- .map(|d| d.solution_range)
- .or_else(|| {
- // We use runtime API as it will fallback to default value for genesis when
- // there is no solution range stored yet
- worker
- .client
- .runtime_api()
- .solution_range(&parent_block_id)
- .ok()
- })?;
- // Here we always use parent block as the source of information, thus on the edge of the
- // eon the very first block of the eon still uses salt from the previous one, but the
- // block after it uses "next" salt deposited in the first block.
- let salt = find_next_salt_digest::(parent_header)
- .ok()?
- .map(|d| d.salt)
- .or_else(|| {
- // We use runtime API as it will fallback to default value for genesis when
- // there is no salt stored yet
- worker.client.runtime_api().salt(&parent_block_id).ok()
- })?;
-
- let new_slot_info = NewSlotInfo {
- slot,
- global_challenge: subspace_solving::derive_global_challenge(&epoch_randomness, slot),
- salt: salt.to_le_bytes(),
- // TODO: This will not be the correct way in the future once salt is no longer
- // just an incremented number
- next_salt: Some((salt + 1).to_le_bytes()),
- solution_range,
- };
- let (solution_sender, solution_receiver) =
- tracing_unbounded("subspace_slot_solution_stream");
-
- worker
- .subspace_link
- .new_slot_notification_sender
- .notify(|| NewSlotNotification {
- new_slot_info,
- solution_sender,
- });
-
- PreparedData {
- block_id: parent_block_id,
- solution_range,
- epoch_randomness,
- salt: salt.to_le_bytes(),
- solution_receiver,
- }
- };
-
- let client = worker.client.clone();
- let signing_context = worker.signing_context.clone();
-
- let PreparedData {
- block_id,
- solution_range,
- epoch_randomness,
- salt,
- mut solution_receiver,
- } = maybe_prepared_data?;
-
- while let Some((solution, secret_key)) = solution_receiver.next().await {
- // TODO: We need also need to check for equivocation of farmers connected to *this node*
- // during block import, currently farmers connected to this node are considered trusted
- if client
- .runtime_api()
- .is_in_block_list(&block_id, &solution.public_key)
- .ok()?
- {
- warn!(
- target: "subspace",
- "Ignoring solution for slot {} provided by farmer in block list: {}",
- slot,
- solution.public_key,
- );
-
- continue;
- }
-
- let record_size = worker
- .client
- .runtime_api()
- .record_size(&parent_block_id)
- .ok()?;
- let recorded_history_segment_size = worker
- .client
- .runtime_api()
- .recorded_history_segment_size(&parent_block_id)
- .ok()?;
- let merkle_num_leaves = u64::from(recorded_history_segment_size / record_size * 2);
- let segment_index = solution.piece_index / merkle_num_leaves;
- let position = solution.piece_index % merkle_num_leaves;
- let mut maybe_records_root = worker
- .client
- .runtime_api()
- .records_root(&parent_block_id, segment_index)
- .ok()?;
-
- // This is not a very nice hack due to the fact that at the time first block is produced
- // extrinsics with root blocks are not yet in runtime.
- if maybe_records_root.is_none() && parent_header.number().is_zero() {
- maybe_records_root = worker.subspace_link.root_blocks.lock().iter().find_map(
- |(_block_number, root_blocks)| {
- root_blocks.iter().find_map(|root_block| {
- if root_block.segment_index() == segment_index {
- Some(root_block.records_root())
- } else {
- None
- }
- })
- },
- );
- }
-
- let records_root = match maybe_records_root {
- Some(records_root) => records_root,
- None => {
- warn!(
- target: "subspace",
- "Records root for segment index {} not found (slot {})",
- segment_index,
- slot,
- );
- continue;
- }
- };
-
- let secret_key = SecretKey::from_bytes(&secret_key).ok()?;
-
- match verification::verify_solution::(
- &solution,
- verification::VerifySolutionParams {
- epoch_randomness: &epoch_randomness,
- solution_range,
- slot,
- salt,
- records_root: &records_root,
- position,
- record_size,
- signing_context: &signing_context,
- },
- ) {
- Ok(_) => {
- debug!(target: "subspace", "Claimed slot {}", slot);
-
- return Some((PreDigest { solution, slot }, secret_key.into()));
- }
- Err(error) => {
- warn!(target: "subspace", "Invalid solution received for slot {}: {:?}", slot, error);
- }
- }
- }
-
- None
-}
diff --git a/crates/sc-consensus-subspace/src/lib.rs b/crates/sc-consensus-subspace/src/lib.rs
index 231c86ac5b9b5..0cbbf3cb565de 100644
--- a/crates/sc-consensus-subspace/src/lib.rs
+++ b/crates/sc-consensus-subspace/src/lib.rs
@@ -49,7 +49,6 @@
#![warn(missing_docs)]
mod archiver;
-mod authorship;
pub mod aux_schema;
pub mod notification;
mod slot_worker;
diff --git a/crates/sc-consensus-subspace/src/slot_worker.rs b/crates/sc-consensus-subspace/src/slot_worker.rs
index 530f9c1575d33..1827dbdcae6a3 100644
--- a/crates/sc-consensus-subspace/src/slot_worker.rs
+++ b/crates/sc-consensus-subspace/src/slot_worker.rs
@@ -16,27 +16,36 @@
// along with this program. If not, see .
use crate::{
- authorship, find_pre_digest, Epoch, SubspaceIntermediate, SubspaceLink, INTERMEDIATE_KEY,
+ find_pre_digest, subspace_err, verification, Epoch, Error, NewSlotInfo, NewSlotNotification,
+ SubspaceIntermediate, SubspaceLink, INTERMEDIATE_KEY,
};
+use futures::StreamExt;
use futures::TryFutureExt;
+use log::{debug, trace, warn};
use sc_consensus::block_import::{BlockImport, BlockImportParams, StateAction};
use sc_consensus_epochs::{descendent_query, ViableEpochDescriptor};
use sc_consensus_slots::{
BackoffAuthoringBlocksStrategy, SimpleSlotWorker, SlotInfo, SlotProportion, StorageChanges,
};
use sc_telemetry::TelemetryHandle;
+use sc_utils::mpsc::tracing_unbounded;
use schnorrkel::context::SigningContext;
+use schnorrkel::SecretKey;
use sp_api::{NumberFor, ProvideRuntimeApi};
use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata, ProvideCache};
use sp_consensus::{BlockOrigin, Environment, Error as ConsensusError, Proposer, SyncOracle};
use sp_consensus_slots::Slot;
-use sp_consensus_subspace::digests::{CompatibleDigestItem, PreDigest};
-use sp_consensus_subspace::SubspaceApi;
+use sp_consensus_subspace::digests::{
+ CompatibleDigestItem, NextSaltDescriptor, NextSolutionRangeDescriptor, PreDigest,
+};
+use sp_consensus_subspace::{ConsensusLog, SubspaceApi, SUBSPACE_ENGINE_ID};
use sp_core::sr25519::Pair;
use sp_core::Pair as _;
-use sp_runtime::traits::{Block as BlockT, DigestItemFor, Header};
+use sp_runtime::generic::{BlockId, OpaqueDigestItemId};
+use sp_runtime::traits::{Block as BlockT, DigestItemFor, Header, Zero};
use std::future::Future;
use std::{borrow::Cow, pin::Pin, sync::Arc};
+pub use subspace_archiving::archiver::ArchivedSegment;
pub(super) struct SubspaceSlotWorker {
pub(super) client: Arc,
@@ -112,7 +121,148 @@ where
slot: Slot,
epoch_descriptor: &Self::EpochData,
) -> Option {
- authorship::claim_slot(self, parent_header, slot, epoch_descriptor).await
+ debug!(target: "subspace", "Attempting to claim slot {}", slot);
+
+ let parent_block_id = BlockId::Hash(parent_header.hash());
+ let runtime_api = self.client.runtime_api();
+
+ let epoch_randomness = self
+ .subspace_link
+ .epoch_changes
+ .shared_data()
+ .viable_epoch(epoch_descriptor, |slot| {
+ Epoch::genesis(&self.subspace_link.config, slot)
+ })?
+ .as_ref()
+ .randomness;
+
+ // Here we always use parent block as the source of information, thus on the edge of the
+ // era the very first block of the era still uses solution range from the previous one,
+ // but the block after it uses "next" solution range deposited in the first block.
+ let solution_range = find_next_solution_range_digest::(parent_header)
+ .ok()?
+ .map(|d| d.solution_range)
+ .or_else(|| {
+ // We use runtime API as it will fallback to default value for genesis when
+ // there is no solution range stored yet
+ runtime_api.solution_range(&parent_block_id).ok()
+ })?;
+ // Here we always use parent block as the source of information, thus on the edge of the
+ // eon the very first block of the eon still uses salt from the previous one, but the
+ // block after it uses "next" salt deposited in the first block.
+ let salt = find_next_salt_digest::(parent_header)
+ .ok()?
+ .map(|d| d.salt)
+ .or_else(|| {
+ // We use runtime API as it will fallback to default value for genesis when
+ // there is no salt stored yet
+ runtime_api.salt(&parent_block_id).ok()
+ })?;
+
+ let new_slot_info = NewSlotInfo {
+ slot,
+ global_challenge: subspace_solving::derive_global_challenge(&epoch_randomness, slot),
+ salt: salt.to_le_bytes(),
+ // TODO: This will not be the correct way in the future once salt is no longer
+ // just an incremented number
+ next_salt: Some((salt + 1).to_le_bytes()),
+ solution_range,
+ };
+ let (solution_sender, mut solution_receiver) =
+ tracing_unbounded("subspace_slot_solution_stream");
+
+ self.subspace_link
+ .new_slot_notification_sender
+ .notify(|| NewSlotNotification {
+ new_slot_info,
+ solution_sender,
+ });
+
+ while let Some((solution, secret_key)) = solution_receiver.next().await {
+ // TODO: We need also need to check for equivocation of farmers connected to *this node*
+ // during block import, currently farmers connected to this node are considered trusted
+ if runtime_api
+ .is_in_block_list(&parent_block_id, &solution.public_key)
+ .ok()?
+ {
+ warn!(
+ target: "subspace",
+ "Ignoring solution for slot {} provided by farmer in block list: {}",
+ slot,
+ solution.public_key,
+ );
+
+ continue;
+ }
+
+ let record_size = runtime_api.record_size(&parent_block_id).ok()?;
+ let recorded_history_segment_size = runtime_api
+ .recorded_history_segment_size(&parent_block_id)
+ .ok()?;
+ let merkle_num_leaves = u64::from(recorded_history_segment_size / record_size * 2);
+ let segment_index = solution.piece_index / merkle_num_leaves;
+ let position = solution.piece_index % merkle_num_leaves;
+ let mut maybe_records_root = runtime_api
+ .records_root(&parent_block_id, segment_index)
+ .ok()?;
+
+ // This is not a very nice hack due to the fact that at the time first block is produced
+ // extrinsics with root blocks are not yet in runtime.
+ if maybe_records_root.is_none() && parent_header.number().is_zero() {
+ maybe_records_root = self.subspace_link.root_blocks.lock().iter().find_map(
+ |(_block_number, root_blocks)| {
+ root_blocks.iter().find_map(|root_block| {
+ if root_block.segment_index() == segment_index {
+ Some(root_block.records_root())
+ } else {
+ None
+ }
+ })
+ },
+ );
+ }
+
+ let records_root = match maybe_records_root {
+ Some(records_root) => records_root,
+ None => {
+ warn!(
+ target: "subspace",
+ "Records root for segment index {} not found (slot {})",
+ segment_index,
+ slot,
+ );
+ continue;
+ }
+ };
+
+ match verification::verify_solution::(
+ &solution,
+ verification::VerifySolutionParams {
+ epoch_randomness: &epoch_randomness,
+ solution_range,
+ slot,
+ salt: salt.to_le_bytes(),
+ records_root: &records_root,
+ position,
+ record_size,
+ signing_context: &self.signing_context,
+ },
+ ) {
+ Ok(_) => {
+ debug!(target: "subspace", "Claimed slot {}", slot);
+
+ return Some((
+ PreDigest { solution, slot },
+ SecretKey::from_bytes(&secret_key).ok()?.into(),
+ ));
+ }
+ Err(error) => {
+ warn!(target: "subspace", "Invalid solution received for slot {}: {:?}", slot, error);
+ }
+ }
+ }
+
+ None
}
fn pre_digest_data(
@@ -230,3 +380,51 @@ where
Some(2)
}
}
+
+/// Extract the next Subspace solution range digest from the given header if it exists.
+fn find_next_solution_range_digest(
+ header: &B::Header,
+) -> Result, Error>
+where
+ DigestItemFor: CompatibleDigestItem,
+{
+ let mut next_solution_range_digest: Option<_> = None;
+ for log in header.digest().logs() {
+ trace!(target: "subspace", "Checking log {:?}, looking for next solution range digest.", log);
+ let log = log.try_to::(OpaqueDigestItemId::Consensus(&SUBSPACE_ENGINE_ID));
+ match (log, next_solution_range_digest.is_some()) {
+ (Some(ConsensusLog::NextSolutionRangeData(_)), true) => {
+ return Err(subspace_err(Error::MultipleNextSolutionRangeDigests))
+ }
+ (Some(ConsensusLog::NextSolutionRangeData(solution_range)), false) => {
+ next_solution_range_digest = Some(solution_range)
+ }
+ _ => trace!(target: "subspace", "Ignoring digest not meant for us"),
+ }
+ }
+
+ Ok(next_solution_range_digest)
+}
+
+/// Extract the next Subspace salt digest from the given header if it exists.
+fn find_next_salt_digest(
+ header: &B::Header,
+) -> Result, Error>
+where
+ DigestItemFor: CompatibleDigestItem,
+{
+ let mut next_salt_digest: Option<_> = None;
+ for log in header.digest().logs() {
+ trace!(target: "subspace", "Checking log {:?}, looking for salt digest.", log);
+ let log = log.try_to::(OpaqueDigestItemId::Consensus(&SUBSPACE_ENGINE_ID));
+ match (log, next_salt_digest.is_some()) {
+ (Some(ConsensusLog::NextSaltData(_)), true) => {
+ return Err(subspace_err(Error::MultipleSaltDigests))
+ }
+ (Some(ConsensusLog::NextSaltData(salt)), false) => next_salt_digest = Some(salt),
+ _ => trace!(target: "subspace", "Ignoring digest not meant for us"),
+ }
+ }
+
+ Ok(next_salt_digest)
+}