diff --git a/crates/sc-consensus-subspace/Cargo.toml b/crates/sc-consensus-subspace/Cargo.toml index c8706efc84..1987de8ad5 100644 --- a/crates/sc-consensus-subspace/Cargo.toml +++ b/crates/sc-consensus-subspace/Cargo.toml @@ -8,7 +8,6 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://subspace.network" repository = "https://github.com/subspace/subspace" documentation = "https://docs.rs/sc-consensus-subspace" -readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/crates/sc-consensus-subspace/README.md b/crates/sc-consensus-subspace/README.md deleted file mode 100644 index e73516d8df..0000000000 --- a/crates/sc-consensus-subspace/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Subspace Proof-of-Archival-Storage consensus - -Subspace is a slot-based block production mechanism which uses a Proof-of-Archival-Storage to randomly perform the slot -allocation. On every slot, all the farmers evaluate their disk-based plot. If they have a tag (reflecting a commitment -to a valid encoding) that it is lower than a given threshold (which is proportional to the total space pledged by the -network) they may produce a new block. - -Core inputs to the Proof-of-Archival-Storage, such as global randomness and solution range come from the runtime, -see `pallet-subspace` for details. - -The fork choice rule is weight-based, where weight is derived from the distance between solution proposed in a block and -the local challenge for particular farmer. The heaviest chain (represents a chain with more storage pledged to it) -will be preferred over alternatives or longest chain is in case of a tie. - -For a more in-depth analysis of Subspace consensus can be found in our -[consensus whitepaper](https://subspace.network/news/subspace-network-whitepaper). - -This crate contains following major components: -* worker (`sc-consensus-slots`) for claiming slots (block production) -* block verifier that stateless verification of signature and Proof-of-Space -* block import that verifies Proof-of-Archival-Storage and triggers archiving of the history -* archiver worker triggered by block import that ensures history is archived and segment headers are produced at precisely - the right time before finishing block import - -License: GPL-3.0-or-later WITH Classpath-exception-2.0 diff --git a/crates/sc-consensus-subspace/src/archiver.rs b/crates/sc-consensus-subspace/src/archiver.rs index 31580cd92e..988ec94969 100644 --- a/crates/sc-consensus-subspace/src/archiver.rs +++ b/crates/sc-consensus-subspace/src/archiver.rs @@ -14,10 +14,37 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! Consensus archiver module. +//! Consensus archiver responsible for archival of blockchain history, it is driven by block import +//! pipeline. //! -//! Contains implementation of archiving process in Subspace blockchain that converts blockchain -//! history (blocks) into archived history (pieces). +//! Implements archiving process in Subspace blockchain that converts blockchain history (blocks) +//! into archived history (pieces). +//! +//! The main entry point here is [`create_subspace_archiver`] that will create a task, which while +//! driven will perform the archiving itself. +//! +//! Archiving is triggered by block importing notification ([`SubspaceLink::block_importing_notification_stream`]) +//! and tries to archive the block at [`ChainConstants::confirmation_depth_k`](sp_consensus_subspace::ChainConstants::confirmation_depth_k) +//! depth from the block being imported. Block import will then wait for archiver to acknowledge +//! processing, which is necessary for ensuring that when the next block is imported, inherents will +//! contain segment header of newly archived block (must happen exactly in the next block). +//! +//! Archiving itself will also wait for acknowledgement by various subscribers before proceeding, +//! which includes farmer subscription, in case of reference implementation via RPC +//! (`sc-consensus-subspace-rpc`), but could also be in other ways. +//! +//! [`SegmentHeadersStore`] is maintained as a data structure containing all known (including future +//! in case of syncing) segment headers. This data structure contents is then made available to +//! other parts of the protocol that need to know what correct archival history of the blockchain +//! looks like. For example, it is used during node sync and farmer plotting in order to verify +//! pieces of archival history received from other network participants. +//! +//! [`recreate_genesis_segment`] is a bit of a hack and is useful for deriving of the genesis +//! segment that is special case since we don't have enough data in the blockchain history itself +//! during genesis in order to do the archiving. +//! +//! [`encode_block`] and [`decode_block`] are symmetric encoding/decoding functions turning +//! [`SignedBlock`]s into bytes and back. use crate::block_import::BlockImportingNotification; use crate::slot_worker::SubspaceSyncOracle; @@ -74,7 +101,16 @@ struct SegmentHeadersStoreInner { cache: Mutex>, } -/// Persistent storage of segment headers +/// Persistent storage of segment headers. +/// +/// It maintains all known segment headers. During sync from DSN it is possible that this data structure contains +/// segment headers that from the point of view of the tip of the current chain are "in the future". This is expected +/// and must be accounted for in the archiver and other places. +/// +/// Segment headers are stored in batches (which is more efficient to store and retrieve). Each next batch contains +/// distinct segment headers with monotonically increasing segment indices. During instantiation all previously stored +/// batches will be read and in-memory representation of the whole contents will be created such that queries to this +/// data structure are quick and not involving any disk I/O. #[derive(Debug)] pub struct SegmentHeadersStore { inner: Arc>, @@ -335,7 +371,10 @@ where best_archived_block: (Block::Hash, NumberFor), } -/// Encode block for archiving purposes +/// Encode block for archiving purposes. +/// +/// Only specific Subspace justifications are included in the encoding, determined by result of +/// [`SubspaceJustification::must_be_archived`], other justifications are filtered-out. pub fn encode_block(mut signed_block: SignedBlock) -> Vec where Block: BlockT, @@ -664,6 +703,24 @@ fn finalize_block( /// `store_segment_header` extrinsic). /// /// NOTE: Archiver is doing blocking operations and must run in a dedicated task. +/// +/// Archiver is only able to move forward and doesn't support reorgs. Upon restart it will check +/// [`SegmentHeadersStore`] and chain history to reconstruct "current" state it was in before last +/// shutdown and continue incrementally archiving blockchain history from there. +/// +/// Archiving is triggered by block importing notification ([`SubspaceLink::block_importing_notification_stream`]) +/// and tries to archive the block at [`ChainConstants::confirmation_depth_k`](sp_consensus_subspace::ChainConstants::confirmation_depth_k) +/// depth from the block being imported. Block import will then wait for archiver to acknowledge +/// processing, which is necessary for ensuring that when the next block is imported, inherents will +/// contain segment header of newly archived block (must happen exactly in the next block). +/// +/// Once segment header is archived, notification ([`SubspaceLink::archived_segment_notification_stream`]) +/// will be sent and archiver will be paused until all receivers have provided an acknowledgement +/// for it. +/// +/// Archiving will be incremental during normal operation to decrease impact on block import and +/// non-incremental heavily parallel during sync process since parallel implementation is more +/// efficient overall and during sync only total sync time matters. pub fn create_subspace_archiver( segment_headers_store: SegmentHeadersStore, subspace_link: &SubspaceLink, diff --git a/crates/sc-consensus-subspace/src/block_import.rs b/crates/sc-consensus-subspace/src/block_import.rs index b3c66d7d6a..eeb48a84b0 100644 --- a/crates/sc-consensus-subspace/src/block_import.rs +++ b/crates/sc-consensus-subspace/src/block_import.rs @@ -14,9 +14,18 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! Block import module. +//! Block import for Subspace, which includes stateful verification and corresponding notifications. //! -//! Contains implementation of block import with corresponding checks and notifications. +//! In most cases block import happens after stateless block verification using [`verifier`](crate::verifier), +//! the only exception to that is locally authored blocks. +//! +//! Since [`verifier`](crate::verifier) is stateless, the remaining checks in block import are those +//! that require presence of the parent block or its state in the database. Specifically for Proof +//! of Time individual checkpoints are assumed to be checked already and only PoT inputs need to be +//! checked to correspond to the state of the parent block. +//! +//! After all checks and right before importing the block notification ([`SubspaceLink::block_importing_notification_stream`]) +//! will be sent that [`archiver`](crate::archiver) among other things is subscribed to. use crate::archiver::SegmentHeadersStore; use crate::verifier::VerificationError; diff --git a/crates/sc-consensus-subspace/src/lib.rs b/crates/sc-consensus-subspace/src/lib.rs index b66360076d..4a8b2adad3 100644 --- a/crates/sc-consensus-subspace/src/lib.rs +++ b/crates/sc-consensus-subspace/src/lib.rs @@ -14,7 +14,14 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#![doc = include_str!("../README.md")] +//! `sc-consensus-subspace` is the core of Subspace consensus implementation. +//! +//! You should familiarize yourself with [Subnomicon](https://subnomicon.subspace.network/) and, ideally, protocol +//! specifications. Documentation here assumes decent prior knowledge of the protocol on conceptual level and will not +//! explain how the protocol works, it will instead explain how the protocol is implemented. +//! +//! All of the modules here are crucial for consensus, open each module for specific details. + #![feature(const_option, let_chains, try_blocks)] #![forbid(unsafe_code)] #![warn(missing_docs)] @@ -40,7 +47,7 @@ use std::sync::Arc; use subspace_core_primitives::crypto::kzg::Kzg; use subspace_core_primitives::SegmentHeader; -/// State that must be shared between the import queue and the authoring logic. +/// State that must be shared between various consensus components. #[derive(Clone)] pub struct SubspaceLink { new_slot_notification_sender: SubspaceNotificationSender, diff --git a/crates/sc-consensus-subspace/src/slot_worker.rs b/crates/sc-consensus-subspace/src/slot_worker.rs index aabc1cb7e4..47e5594dde 100644 --- a/crates/sc-consensus-subspace/src/slot_worker.rs +++ b/crates/sc-consensus-subspace/src/slot_worker.rs @@ -14,9 +14,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! Slot worker module. +//! Slot worker drives block and vote production based on slots produced in [`sc_proof_of_time`]. //! -//! Contains implementation of Subspace slot worker that produces block and votes. +//! While slot worker uses [`sc_consensus_slots`], it is not driven by time, but instead by Proof of +//! Time that is produced by [`PotSourceWorker`](sc_proof_of_time::source::PotSourceWorker). +//! +//! Each time a new proof is found, [`PotSlotWorker::on_proof`] is called and corresponding +//! [`SlotInfo`] notification is sent ([`SubspaceLink::new_slot_notification_stream`]) to farmers to +//! do the audit and try to prove they have a solution without actually waiting for the response. +//! [`ChainConstants::block_authoring_delay`](sp_consensus_subspace::ChainConstants::block_authoring_delay) +//! slots later (when corresponding future proof arrives) all the solutions produced by farmers so +//! far are collected and corresponding block and/or votes are produced. In case PoT chain reorg +//! happens, outdated solutions (they are tied to proofs of time) are thrown away. +//! +//! Custom [`SubspaceSyncOracle`] wrapper is introduced due to Subspace-specific changes comparing +//! to the base Substrate behavior where major syncing is assumed to not happen in case authoring is +//! forced. use crate::archiver::SegmentHeadersStore; use crate::SubspaceLink; diff --git a/crates/sc-consensus-subspace/src/verifier.rs b/crates/sc-consensus-subspace/src/verifier.rs index 1df226cdb3..308aae11ac 100644 --- a/crates/sc-consensus-subspace/src/verifier.rs +++ b/crates/sc-consensus-subspace/src/verifier.rs @@ -1,4 +1,17 @@ -//! Subspace block import implementation +//! Stateless and parallelized block verification that happens before block is imported (except for locally produced +//! blocks that are imported directly). +//! +//! The goal of verifier is to check internal consistency of the block, which includes things like +//! solution according to claimed inputs, signature, Proof of Time checkpoints in justifications, +//! etc. +//! +//! This should be the majority of the block verification computation such that all that is left for +//! [`block_import`](crate::block_import) to check is that information in the block corresponds to +//! the state of the parent block, which for the most part is comparing bytes against known good +//! values. +//! +//! This is a significant tradeoff in the protocol: having a smaller header vs being able to verify +//! a lot of things stateless and in parallel. use futures::lock::Mutex; use rand::prelude::*;