Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce custom Subspace genesis block builder #1613

Merged
merged 1 commit into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
54 changes: 4 additions & 50 deletions crates/subspace-runtime-primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,60 +67,14 @@ pub type Moment = u64;
/// to even the core data structures.
pub mod opaque {
use super::BlockNumber;
use parity_scale_codec::{Decode, Encode};
use serde::{Deserialize, Serialize};
use sp_runtime::traits::{BlakeTwo256, Block as BlockT, Header as HeaderT};
use sp_runtime::{generic, DigestItem, OpaqueExtrinsic};
use sp_std::prelude::*;
use subspace_core_primitives::RecordedHistorySegment;
use sp_runtime::generic;
use sp_runtime::traits::BlakeTwo256;
pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;

/// Opaque block header type.
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// Opaque block type.

/// Abstraction over a substrate block.
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
pub struct Block {
/// The block header.
pub header: Header,
/// The accompanying extrinsics.
pub extrinsics: Vec<OpaqueExtrinsic>,
}

impl BlockT for Block {
type Extrinsic = OpaqueExtrinsic;
type Header = Header;
type Hash = <Header as HeaderT>::Hash;

fn header(&self) -> &Self::Header {
&self.header
}
fn extrinsics(&self) -> &[Self::Extrinsic] {
&self.extrinsics[..]
}
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
(self.header, self.extrinsics)
}
fn new(mut header: Self::Header, extrinsics: Vec<Self::Extrinsic>) -> Self {
if header.number == 0 {
// This check is necessary in case block was deconstructed and constructed again.
if header.digest.logs.is_empty() {
// We fill genesis block with extra data such that the very first archived
// segment can be produced right away, bootstrapping the farming process.
let ballast = vec![0; RecordedHistorySegment::SIZE];
header.digest.logs.push(DigestItem::Other(ballast));
}
Block { header, extrinsics }
} else {
Block { header, extrinsics }
}
}
fn encode_from(header: &Self::Header, extrinsics: &[Self::Extrinsic]) -> Vec<u8> {
(header, extrinsics).encode()
}
}
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
}

/// A trait for finding the address for a block reward based on the `PreRuntime` digests contained within it.
Expand Down
97 changes: 97 additions & 0 deletions crates/subspace-service/src/genesis_block_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use sc_client_api::backend::Backend;
use sc_client_api::BlockImportOperation;
use sc_executor::RuntimeVersionOf;
use sc_service::{resolve_state_version_from_wasm, BuildGenesisBlock};
use sp_core::storage::{StateVersion, Storage};
use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, Zero};
use sp_runtime::{BuildStorage, Digest, DigestItem};
use std::marker::PhantomData;
use std::sync::Arc;
use subspace_core_primitives::RecordedHistorySegment;

/// Custom genesis block builder for Subspace.
pub struct SubspaceGenesisBlockBuilder<Block: BlockT, B, E> {
genesis_storage: Storage,
commit_genesis_state: bool,
backend: Arc<B>,
executor: E,
_phantom: PhantomData<Block>,
}

impl<Block: BlockT, B: Backend<Block>, E: RuntimeVersionOf>
SubspaceGenesisBlockBuilder<Block, B, E>
{
/// Constructs a new instance of [`SubspaceGenesisBlockBuilder`].
pub fn new(
build_genesis_storage: &dyn BuildStorage,
commit_genesis_state: bool,
backend: Arc<B>,
executor: E,
) -> sp_blockchain::Result<Self> {
let genesis_storage = build_genesis_storage
.build_storage()
.map_err(sp_blockchain::Error::Storage)?;
Ok(Self {
genesis_storage,
commit_genesis_state,
backend,
executor,
_phantom: PhantomData::<Block>,
})
}
}

impl<Block: BlockT, B: Backend<Block>, E: RuntimeVersionOf> BuildGenesisBlock<Block>
for SubspaceGenesisBlockBuilder<Block, B, E>
{
type BlockImportOperation = <B as Backend<Block>>::BlockImportOperation;

fn build_genesis_block(self) -> sp_blockchain::Result<(Block, Self::BlockImportOperation)> {
let Self {
genesis_storage,
commit_genesis_state,
backend,
executor,
_phantom,
} = self;

let genesis_state_version = resolve_state_version_from_wasm(&genesis_storage, &executor)?;
let mut op = backend.begin_operation()?;
let state_root =
op.set_genesis_state(genesis_storage, commit_genesis_state, genesis_state_version)?;
let genesis_block = construct_genesis_block::<Block>(state_root, genesis_state_version);

Ok((genesis_block, op))
}
}

/// Create a custom Subspace genesis block, given the initial storage.
///
/// We have a non-empty digest in comparision to the default Substrate genesis block.
fn construct_genesis_block<Block: BlockT>(
state_root: Block::Hash,
state_version: StateVersion,
) -> Block {
let extrinsics_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
Vec::new(),
state_version,
);

// We fill genesis block with extra data such that the very first archived
// segment can be produced right away, bootstrapping the farming process.
let ballast = vec![0; RecordedHistorySegment::SIZE];
let digest = Digest {
logs: vec![DigestItem::Other(ballast)],
};

Block::new(
<<Block as BlockT>::Header as HeaderT>::new(
Zero::zero(),
extrinsics_root,
state_root,
Default::default(),
digest,
),
Default::default(),
)
}
19 changes: 17 additions & 2 deletions crates/subspace-service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#![feature(type_alias_impl_trait, type_changing_struct_update)]

pub mod dsn;
mod genesis_block_builder;
mod metrics;
pub mod piece_cache;
pub mod rpc;
Expand All @@ -27,6 +28,7 @@ pub mod tx_pre_validator;

use crate::dsn::import_blocks::initial_block_import_from_dsn;
use crate::dsn::{create_dsn_instance, DsnConfigurationError};
use crate::genesis_block_builder::SubspaceGenesisBlockBuilder;
use crate::metrics::NodeMetrics;
use crate::piece_cache::PieceCache;
use crate::segment_headers::{start_segment_header_archiver, SegmentHeaderCache};
Expand Down Expand Up @@ -55,7 +57,9 @@ use sc_consensus_subspace::{
use sc_executor::{NativeElseWasmExecutor, NativeExecutionDispatch};
use sc_network::NetworkService;
use sc_service::error::Error as ServiceError;
use sc_service::{Configuration, NetworkStarter, PartialComponents, SpawnTasksParams, TaskManager};
use sc_service::{
new_db_backend, Configuration, NetworkStarter, PartialComponents, SpawnTasksParams, TaskManager,
};
use sc_subspace_block_relay::{build_consensus_relay, NetworkWrapper};
use sc_telemetry::{Telemetry, TelemetryWorker};
use sp_api::{ApiExt, ConstructRuntimeApi, HeaderT, Metadata, ProvideRuntimeApi, TransactionFor};
Expand Down Expand Up @@ -295,11 +299,22 @@ where

let executor = sc_service::new_native_or_wasm_executor(config);

let backend = new_db_backend(config.db_config())?;

let genesis_block_builder = SubspaceGenesisBlockBuilder::new(
config.chain_spec.as_storage_builder(),
!config.no_genesis(),
backend.clone(),
executor.clone(),
)?;

let (client, backend, keystore_container, task_manager) =
sc_service::new_full_parts::<Block, RuntimeApi, _>(
sc_service::new_full_parts_with_genesis_builder::<Block, RuntimeApi, _, _>(
config,
telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
executor.clone(),
backend,
genesis_block_builder,
)?;

let kzg = Kzg::new(embedded_kzg_settings());
Expand Down