Skip to content

Commit

Permalink
Merge pull request #1613 from subspace/custom-genesis-block-builder
Browse files Browse the repository at this point in the history
Introduce custom Subspace genesis block builder
  • Loading branch information
nazar-pc committed Jun 29, 2023
2 parents fe018b0 + 1d1a3c8 commit 25ae19b
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 52 deletions.
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

0 comments on commit 25ae19b

Please sign in to comment.