From 1d1a3c8821e537e92ef069c657ae955e35be94d9 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Wed, 28 Jun 2023 23:41:37 +0800 Subject: [PATCH] Introduce custom Subspace genesis block builder Close #1033 --- crates/subspace-runtime-primitives/src/lib.rs | 54 +---------- .../src/genesis_block_builder.rs | 97 +++++++++++++++++++ crates/subspace-service/src/lib.rs | 19 +++- 3 files changed, 118 insertions(+), 52 deletions(-) create mode 100644 crates/subspace-service/src/genesis_block_builder.rs diff --git a/crates/subspace-runtime-primitives/src/lib.rs b/crates/subspace-runtime-primitives/src/lib.rs index e8cc64dbf2..187de5e438 100644 --- a/crates/subspace-runtime-primitives/src/lib.rs +++ b/crates/subspace-runtime-primitives/src/lib.rs @@ -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; /// 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, - } - - impl BlockT for Block { - type Extrinsic = OpaqueExtrinsic; - type Header = Header; - type Hash =
::Hash; - - fn header(&self) -> &Self::Header { - &self.header - } - fn extrinsics(&self) -> &[Self::Extrinsic] { - &self.extrinsics[..] - } - fn deconstruct(self) -> (Self::Header, Vec) { - (self.header, self.extrinsics) - } - fn new(mut header: Self::Header, extrinsics: Vec) -> 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 { - (header, extrinsics).encode() - } - } + pub type Block = generic::Block; } /// A trait for finding the address for a block reward based on the `PreRuntime` digests contained within it. diff --git a/crates/subspace-service/src/genesis_block_builder.rs b/crates/subspace-service/src/genesis_block_builder.rs new file mode 100644 index 0000000000..3d4ab89746 --- /dev/null +++ b/crates/subspace-service/src/genesis_block_builder.rs @@ -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 { + genesis_storage: Storage, + commit_genesis_state: bool, + backend: Arc, + executor: E, + _phantom: PhantomData, +} + +impl, E: RuntimeVersionOf> + SubspaceGenesisBlockBuilder +{ + /// Constructs a new instance of [`SubspaceGenesisBlockBuilder`]. + pub fn new( + build_genesis_storage: &dyn BuildStorage, + commit_genesis_state: bool, + backend: Arc, + executor: E, + ) -> sp_blockchain::Result { + 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::, + }) + } +} + +impl, E: RuntimeVersionOf> BuildGenesisBlock + for SubspaceGenesisBlockBuilder +{ + type BlockImportOperation = >::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::(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( + state_root: Block::Hash, + state_version: StateVersion, +) -> Block { + let extrinsics_root = <<::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( + <::Header as HeaderT>::new( + Zero::zero(), + extrinsics_root, + state_root, + Default::default(), + digest, + ), + Default::default(), + ) +} diff --git a/crates/subspace-service/src/lib.rs b/crates/subspace-service/src/lib.rs index 5ed20f4df0..72b89dd7d5 100644 --- a/crates/subspace-service/src/lib.rs +++ b/crates/subspace-service/src/lib.rs @@ -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; @@ -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}; @@ -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}; @@ -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::( + sc_service::new_full_parts_with_genesis_builder::( config, telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), executor.clone(), + backend, + genesis_block_builder, )?; let kzg = Kzg::new(embedded_kzg_settings());