diff --git a/substrate/client/chain-spec/src/chain_spec.rs b/substrate/client/chain-spec/src/chain_spec.rs index fbe7b7e7a8..8e941161ee 100644 --- a/substrate/client/chain-spec/src/chain_spec.rs +++ b/substrate/client/chain-spec/src/chain_spec.rs @@ -16,11 +16,7 @@ //! Substrate chain configurations. -use std::borrow::Cow; -use std::collections::HashMap; -use std::fs::File; -use std::path::PathBuf; -use std::sync::Arc; +use std::{borrow::Cow, fs::File, path::PathBuf, sync::Arc, collections::HashMap}; use serde::{Serialize, Deserialize}; use sp_core::storage::{StorageKey, StorageData, ChildInfo, Storage, StorageChild}; use sp_runtime::BuildStorage; @@ -33,14 +29,16 @@ enum GenesisSource { File(PathBuf), Binary(Cow<'static, [u8]>), Factory(Arc G + Send + Sync>), + Storage(Storage), } impl Clone for GenesisSource { fn clone(&self) -> Self { match *self { - GenesisSource::File(ref path) => GenesisSource::File(path.clone()), - GenesisSource::Binary(ref d) => GenesisSource::Binary(d.clone()), - GenesisSource::Factory(ref f) => GenesisSource::Factory(f.clone()), + Self::File(ref path) => Self::File(path.clone()), + Self::Binary(ref d) => Self::Binary(d.clone()), + Self::Factory(ref f) => Self::Factory(f.clone()), + Self::Storage(ref s) => Self::Storage(s.clone()), } } } @@ -53,19 +51,40 @@ impl GenesisSource { } match self { - GenesisSource::File(path) => { + Self::File(path) => { let file = File::open(path) .map_err(|e| format!("Error opening spec file: {}", e))?; let genesis: GenesisContainer = json::from_reader(file) .map_err(|e| format!("Error parsing spec file: {}", e))?; Ok(genesis.genesis) }, - GenesisSource::Binary(buf) => { + Self::Binary(buf) => { let genesis: GenesisContainer = json::from_reader(buf.as_ref()) .map_err(|e| format!("Error parsing embedded file: {}", e))?; Ok(genesis.genesis) }, - GenesisSource::Factory(f) => Ok(Genesis::Runtime(f())), + Self::Factory(f) => Ok(Genesis::Runtime(f())), + Self::Storage(storage) => { + let top = storage.top + .iter() + .map(|(k, v)| (StorageKey(k.clone()), StorageData(v.clone()))) + .collect(); + + let children_default = storage.children_default + .iter() + .map(|(k, child)| + ( + StorageKey(k.clone()), + child.data + .iter() + .map(|(k, v)| (StorageKey(k.clone()), StorageData(v.clone()))) + .collect() + ) + ) + .collect(); + + Ok(Genesis::Raw(RawGenesis { top, children_default })) + }, } } } @@ -98,15 +117,15 @@ impl BuildStorage for ChainSpec { } } -type GenesisStorage = HashMap; +pub type GenesisStorage = HashMap; +/// Raw storage content for genesis block. #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] -/// Storage content for genesis block. -struct RawGenesis { - top: GenesisStorage, - children_default: HashMap, +pub struct RawGenesis { + pub top: GenesisStorage, + pub children_default: HashMap, } #[derive(Serialize, Deserialize)] @@ -263,7 +282,7 @@ impl ChainSpec { } } -impl ChainSpec { +impl ChainSpec { /// Dump to json string. pub fn as_json(&self, raw: bool) -> Result { #[derive(Serialize, Deserialize)] @@ -303,8 +322,8 @@ impl ChainSpec { impl crate::ChainSpec for ChainSpec where - G: RuntimeGenesis, - E: GetExtension + serde::Serialize + Clone + Send, + G: RuntimeGenesis + 'static, + E: GetExtension + serde::Serialize + Clone + Send + 'static, { fn boot_nodes(&self) -> &[MultiaddrWithPeerId] { ChainSpec::boot_nodes(self) @@ -349,6 +368,14 @@ where fn as_storage_builder(&self) -> &dyn BuildStorage { self } + + fn cloned_box(&self) -> Box { + Box::new(self.clone()) + } + + fn set_storage(&mut self, storage: Storage) { + self.genesis = GenesisSource::Storage(storage); + } } #[cfg(test)] diff --git a/substrate/client/chain-spec/src/lib.rs b/substrate/client/chain-spec/src/lib.rs index de83e170e0..6fb2694261 100644 --- a/substrate/client/chain-spec/src/lib.rs +++ b/substrate/client/chain-spec/src/lib.rs @@ -119,6 +119,7 @@ use serde::{Serialize, de::DeserializeOwned}; use sp_runtime::BuildStorage; use sc_network::config::MultiaddrWithPeerId; use sc_telemetry::TelemetryEndpoints; +use sp_core::storage::Storage; /// A set of traits for the runtime genesis config. pub trait RuntimeGenesis: Serialize + DeserializeOwned + BuildStorage {} @@ -150,4 +151,10 @@ pub trait ChainSpec: BuildStorage + Send { fn as_json(&self, raw: bool) -> Result; /// Return StorageBuilder for this spec. fn as_storage_builder(&self) -> &dyn BuildStorage; + /// Returns a cloned `Box`. + fn cloned_box(&self) -> Box; + /// Set the storage that should be used by this chain spec. + /// + /// This will be used as storage at genesis. + fn set_storage(&mut self, storage: Storage); } diff --git a/substrate/client/cli/src/commands/check_block_cmd.rs b/substrate/client/cli/src/commands/check_block_cmd.rs index ac4fe63da9..d5242cda85 100644 --- a/substrate/client/cli/src/commands/check_block_cmd.rs +++ b/substrate/client/cli/src/commands/check_block_cmd.rs @@ -14,15 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::error; -use crate::params::ImportParams; -use crate::params::SharedParams; -use crate::CliConfiguration; +use crate::{ + CliConfiguration, error, params::{ImportParams, SharedParams, BlockNumberOrHash}, +}; use sc_service::{Configuration, ServiceBuilderCommand}; -use sp_runtime::generic::BlockId; -use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; -use std::fmt::Debug; -use std::str::FromStr; +use sp_runtime::traits::{Block as BlockT, NumberFor}; +use std::{fmt::Debug, str::FromStr}; use structopt::StructOpt; /// The `check-block` command used to validate blocks. @@ -30,7 +27,7 @@ use structopt::StructOpt; pub struct CheckBlockCmd { /// Block hash or number #[structopt(value_name = "HASH or NUMBER")] - pub input: String, + pub input: BlockNumberOrHash, /// The default number of 64KB pages to ever allocate for Wasm execution. /// @@ -57,29 +54,13 @@ impl CheckBlockCmd { where B: FnOnce(Configuration) -> Result, BC: ServiceBuilderCommand + Unpin, - BB: sp_runtime::traits::Block + Debug, - <<::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug, - ::Hash: std::str::FromStr, + BB: BlockT + Debug, + as FromStr>::Err: std::fmt::Debug, + BB::Hash: FromStr, + ::Err: std::fmt::Debug, { - let input = if self.input.starts_with("0x") { - &self.input[2..] - } else { - &self.input[..] - }; - let block_id = match FromStr::from_str(input) { - Ok(hash) => BlockId::hash(hash), - Err(_) => match self.input.parse::() { - Ok(n) => BlockId::number((n as u32).into()), - Err(_) => { - return Err(error::Error::Input( - "Invalid hash or number specified".into(), - )) - } - }, - }; - let start = std::time::Instant::now(); - builder(config)?.check_block(block_id).await?; + builder(config)?.check_block(self.input.parse()?).await?; println!("Completed in {} ms.", start.elapsed().as_millis()); Ok(()) diff --git a/substrate/client/cli/src/commands/export_state_cmd.rs b/substrate/client/cli/src/commands/export_state_cmd.rs new file mode 100644 index 0000000000..db6f81c498 --- /dev/null +++ b/substrate/client/cli/src/commands/export_state_cmd.rs @@ -0,0 +1,81 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +use crate::{ + CliConfiguration, error, params::{PruningParams, SharedParams, BlockNumberOrHash}, +}; +use log::info; +use sc_service::{Configuration, ServiceBuilderCommand}; +use sp_runtime::traits::{Block as BlockT, NumberFor}; +use std::{fmt::Debug, str::FromStr}; +use structopt::StructOpt; + +/// The `export-state` command used to export the state of a given block into +/// a chain spec. +#[derive(Debug, StructOpt, Clone)] +pub struct ExportStateCmd { + /// Block hash or number. + #[structopt(value_name = "HASH or NUMBER")] + pub input: Option, + + #[allow(missing_docs)] + #[structopt(flatten)] + pub shared_params: SharedParams, + + #[allow(missing_docs)] + #[structopt(flatten)] + pub pruning_params: PruningParams, +} + +impl ExportStateCmd { + /// Run the `export-state` command + pub fn run( + &self, + config: Configuration, + builder: B, + ) -> error::Result<()> + where + B: FnOnce(Configuration) -> Result, + BC: ServiceBuilderCommand + Unpin, + BB: BlockT + Debug, + as FromStr>::Err: std::fmt::Debug, + BB::Hash: FromStr, + ::Err: std::fmt::Debug, + { + info!("Exporting raw state..."); + let mut input_spec = config.chain_spec.cloned_box(); + let block_id = self.input.clone().map(|b| b.parse()).transpose()?; + let raw_state = builder(config)?.export_raw_state(block_id)?; + input_spec.set_storage(raw_state); + + info!("Generating new chain spec..."); + let json = sc_service::chain_ops::build_spec(&*input_spec, true)?; + + print!("{}", json); + + Ok(()) + } +} + +impl CliConfiguration for ExportStateCmd { + fn shared_params(&self) -> &SharedParams { + &self.shared_params + } + + fn pruning_params(&self) -> Option<&PruningParams> { + Some(&self.pruning_params) + } +} diff --git a/substrate/client/cli/src/commands/mod.rs b/substrate/client/cli/src/commands/mod.rs index 58ed2b7499..5312336c76 100644 --- a/substrate/client/cli/src/commands/mod.rs +++ b/substrate/client/cli/src/commands/mod.rs @@ -17,18 +17,20 @@ mod build_spec_cmd; mod check_block_cmd; mod export_blocks_cmd; +mod export_state_cmd; mod import_blocks_cmd; mod purge_chain_cmd; mod revert_cmd; -mod runcmd; +mod run_cmd; -pub use crate::commands::build_spec_cmd::BuildSpecCmd; -pub use crate::commands::check_block_cmd::CheckBlockCmd; -pub use crate::commands::export_blocks_cmd::ExportBlocksCmd; -pub use crate::commands::import_blocks_cmd::ImportBlocksCmd; -pub use crate::commands::purge_chain_cmd::PurgeChainCmd; -pub use crate::commands::revert_cmd::RevertCmd; -pub use crate::commands::runcmd::RunCmd; +pub use self::build_spec_cmd::BuildSpecCmd; +pub use self::check_block_cmd::CheckBlockCmd; +pub use self::export_blocks_cmd::ExportBlocksCmd; +pub use self::import_blocks_cmd::ImportBlocksCmd; +pub use self::purge_chain_cmd::PurgeChainCmd; +pub use self::revert_cmd::RevertCmd; +pub use self::run_cmd::RunCmd; +pub use self::export_state_cmd::ExportStateCmd; use std::fmt::Debug; use structopt::StructOpt; @@ -56,6 +58,9 @@ pub enum Subcommand { /// Remove the whole chain data. PurgeChain(PurgeChainCmd), + + /// Export state as raw chain spec. + ExportState(ExportStateCmd), } // TODO: move to config.rs? @@ -339,7 +344,10 @@ macro_rules! substrate_cli_subcommands { } } - fn offchain_worker(&self, role: &::sc_service::Role) -> $crate::Result<::sc_service::config::OffchainWorkerConfig> { + fn offchain_worker( + &self, + role: &::sc_service::Role, + ) -> $crate::Result<::sc_service::config::OffchainWorkerConfig> { match self { $($enum::$variant(cmd) => cmd.offchain_worker(role)),* } @@ -398,5 +406,6 @@ macro_rules! substrate_cli_subcommands { } substrate_cli_subcommands!( - Subcommand => BuildSpec, ExportBlocks, ImportBlocks, CheckBlock, Revert, PurgeChain + Subcommand => BuildSpec, ExportBlocks, ImportBlocks, CheckBlock, Revert, PurgeChain, ExportState ); + diff --git a/substrate/client/cli/src/commands/runcmd.rs b/substrate/client/cli/src/commands/run_cmd.rs similarity index 100% rename from substrate/client/cli/src/commands/runcmd.rs rename to substrate/client/cli/src/commands/run_cmd.rs diff --git a/substrate/client/cli/src/params/mod.rs b/substrate/client/cli/src/params/mod.rs index 79b4e8270a..da236ee165 100644 --- a/substrate/client/cli/src/params/mod.rs +++ b/substrate/client/cli/src/params/mod.rs @@ -24,8 +24,8 @@ mod pruning_params; mod shared_params; mod transaction_pool_params; -use std::fmt::Debug; -use std::str::FromStr; +use std::{fmt::Debug, str::FromStr}; +use sp_runtime::{generic::BlockId, traits::{Block as BlockT, NumberFor}}; pub use crate::params::database_params::*; pub use crate::params::import_params::*; @@ -45,10 +45,10 @@ impl FromStr for BlockNumber { type Err = String; fn from_str(block_number: &str) -> Result { - if block_number.chars().any(|d| !d.is_digit(10)) { + if let Some(pos) = block_number.chars().position(|d| !d.is_digit(10)) { Err(format!( - "Invalid block number: {}, expected decimal formatted unsigned integer", - block_number, + "Expected block number, found illegal digit at position: {}", + pos, )) } else { Ok(Self(block_number.to_owned())) @@ -66,8 +66,89 @@ impl BlockNumber { N: FromStr, N::Err: std::fmt::Debug, { - self.0 - .parse() - .map_err(|e| format!("BlockNumber: {} parsing failed because of {:?}", self.0, e)) + FromStr::from_str(&self.0).map_err(|e| format!("Failed to parse block number: {:?}", e)) + } +} + +/// Wrapper type that is either a `Hash` or the number of a `Block`. +#[derive(Debug, Clone)] +pub struct BlockNumberOrHash(String); + +impl FromStr for BlockNumberOrHash { + type Err = String; + + fn from_str(block_number: &str) -> Result { + if block_number.starts_with("0x") { + if let Some(pos) = &block_number[2..].chars().position(|c| !c.is_ascii_hexdigit()) { + Err(format!( + "Expected block hash, found illegal hex character at position: {}", + 2 + pos, + )) + } else { + Ok(Self(block_number.into())) + } + } else { + BlockNumber::from_str(block_number).map(|v| Self(v.0)) + } + } +} + +impl BlockNumberOrHash { + /// Parse the inner value as `BlockId`. + pub fn parse(&self) -> Result, String> + where + B::Hash: FromStr, + ::Err: std::fmt::Debug, + NumberFor: FromStr, + as FromStr>::Err: std::fmt::Debug, + { + if self.0.starts_with("0x") { + Ok(BlockId::Hash( + FromStr::from_str(&self.0[2..]) + .map_err(|e| format!("Failed to parse block hash: {:?}", e))? + )) + } else { + BlockNumber(self.0.clone()).parse().map(BlockId::Number) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + type Header = sp_runtime::generic::Header; + type Block = sp_runtime::generic::Block; + + #[test] + fn parse_block_number() { + let block_number_or_hash = BlockNumberOrHash::from_str("1234").unwrap(); + let parsed = block_number_or_hash.parse::().unwrap(); + assert_eq!(BlockId::Number(1234), parsed); + } + + #[test] + fn parse_block_hash() { + let hash = sp_core::H256::default(); + let hash_str = format!("{:?}", hash); + let block_number_or_hash = BlockNumberOrHash::from_str(&hash_str).unwrap(); + let parsed = block_number_or_hash.parse::().unwrap(); + assert_eq!(BlockId::Hash(hash), parsed); + } + + #[test] + fn parse_block_hash_fails() { + assert_eq!( + "Expected block hash, found illegal hex character at position: 2", + BlockNumberOrHash::from_str("0xHello").unwrap_err(), + ); + } + + #[test] + fn parse_block_number_fails() { + assert_eq!( + "Expected block number, found illegal digit at position: 3", + BlockNumberOrHash::from_str("345Hello").unwrap_err(), + ); } } diff --git a/substrate/client/cli/src/runner.rs b/substrate/client/cli/src/runner.rs index bd3d2024e6..3b75dc4bc1 100644 --- a/substrate/client/cli/src/runner.rs +++ b/substrate/client/cli/src/runner.rs @@ -26,9 +26,7 @@ use log::info; use sc_service::{AbstractService, Configuration, Role, ServiceBuilderCommand, TaskType}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use sp_utils::metrics::{TOKIO_THREADS_ALIVE, TOKIO_THREADS_TOTAL}; -use std::fmt::Debug; -use std::marker::PhantomData; -use std::sync::Arc; +use std::{str::FromStr, fmt::Debug, marker::PhantomData, sync::Arc}; #[cfg(target_family = "unix")] async fn main(func: F) -> std::result::Result<(), Box> @@ -183,8 +181,9 @@ impl Runner { B: FnOnce(Configuration) -> sc_service::error::Result, BC: ServiceBuilderCommand + Unpin, BB: sp_runtime::traits::Block + Debug, - <<::Header as HeaderT>::Number as std::str::FromStr>::Err: Debug, - ::Hash: std::str::FromStr, + <<::Header as HeaderT>::Number as FromStr>::Err: Debug, + ::Hash: FromStr, + <::Hash as FromStr>::Err: Debug, { match subcommand { Subcommand::BuildSpec(cmd) => cmd.run(self.config), @@ -199,6 +198,7 @@ impl Runner { } Subcommand::Revert(cmd) => cmd.run(self.config, builder), Subcommand::PurgeChain(cmd) => cmd.run(self.config), + Subcommand::ExportState(cmd) => cmd.run(self.config, builder), } } diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index 832fd5b8ea..be9c10631e 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -34,7 +34,7 @@ use futures::{ Future, FutureExt, StreamExt, future::ready, }; -use sc_keystore::{Store as Keystore}; +use sc_keystore::Store as Keystore; use log::{info, warn, error}; use sc_network::config::{Role, FinalityProofProvider, OnDemand, BoxFinalityProofRequestBuilder}; use sc_network::{NetworkService, NetworkStateInfo}; @@ -59,6 +59,7 @@ use sc_client_db::{Backend, DatabaseSettings}; use sp_core::traits::CodeExecutor; use sp_runtime::BuildStorage; use sc_client_api::execution_extensions::ExecutionExtensions; +use sp_core::storage::Storage; pub type BackgroundTask = Pin + Send>>; @@ -771,6 +772,13 @@ pub trait ServiceBuilderCommand { self, block: BlockId ) -> Pin> + Send>>; + + /// Export the raw state at the given `block`. If `block` is `None`, the + /// best block will be used. + fn export_raw_state( + &self, + block: Option>, + ) -> Result; } impl diff --git a/substrate/client/service/src/chain_ops.rs b/substrate/client/service/src/chain_ops.rs index 6303376dc1..612e9310d1 100644 --- a/substrate/client/service/src/chain_ops.rs +++ b/substrate/client/service/src/chain_ops.rs @@ -33,13 +33,14 @@ use sp_consensus::{ import_queue::{IncomingBlock, Link, BlockImportError, BlockImportResult, ImportQueue}, }; use sc_executor::{NativeExecutor, NativeExecutionDispatch}; +use sp_core::storage::{StorageKey, well_known_keys, ChildInfo, Storage, StorageChild, StorageMap}; +use sc_client_api::{StorageProvider, BlockBackend, UsageProvider}; -use std::{io::{Read, Write, Seek}, pin::Pin}; -use sc_client_api::BlockBackend; +use std::{io::{Read, Write, Seek}, pin::Pin, collections::HashMap}; /// Build a chain spec json pub fn build_spec(spec: &dyn ChainSpec, raw: bool) -> error::Result { - Ok(spec.as_json(raw)?) + spec.as_json(raw).map_err(Into::into) } impl< @@ -298,4 +299,45 @@ impl< Err(e) => Box::pin(future::err(format!("Error reading block: {:?}", e).into())), } } + + fn export_raw_state( + &self, + block: Option>, + ) -> Result { + let block = block.unwrap_or_else( + || BlockId::Hash(self.client.usage_info().chain.best_hash) + ); + + let empty_key = StorageKey(Vec::new()); + let mut top_storage = self.client.storage_pairs(&block, &empty_key)?; + let mut children_default = HashMap::new(); + + // Remove all default child storage roots from the top storage and collect the child storage + // pairs. + while let Some(pos) = top_storage + .iter() + .position(|(k, _)| k.0.starts_with(well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX)) { + let (key, _) = top_storage.swap_remove(pos); + + let key = StorageKey( + key.0[well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX.len()..].to_vec(), + ); + let child_info = ChildInfo::new_default(&key.0); + + let keys = self.client.child_storage_keys(&block, &child_info, &empty_key)?; + let mut pairs = StorageMap::new(); + keys.into_iter().try_for_each(|k| { + if let Some(value) = self.client.child_storage(&block, &child_info, &k)? { + pairs.insert(k.0, value.0); + } + + Ok::<_, Error>(()) + })?; + + children_default.insert(key.0, StorageChild { child_info, data: pairs }); + } + + let top = top_storage.into_iter().map(|(k, v)| (k.0, v.0)).collect(); + Ok(Storage { top, children_default }) + } } diff --git a/substrate/client/service/src/client/client.rs b/substrate/client/service/src/client/client.rs index 9758bbe01e..a5a02c85cf 100644 --- a/substrate/client/service/src/client/client.rs +++ b/substrate/client/service/src/client/client.rs @@ -1321,8 +1321,11 @@ impl StorageProvider for Client wher } - fn storage(&self, id: &BlockId, key: &StorageKey) -> sp_blockchain::Result> - { + fn storage( + &self, + id: &BlockId, + key: &StorageKey, + ) -> sp_blockchain::Result> { Ok(self.state_at(id)? .storage(&key.0).map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))? .map(StorageData) @@ -1330,8 +1333,11 @@ impl StorageProvider for Client wher } - fn storage_hash(&self, id: &BlockId, key: &StorageKey) -> sp_blockchain::Result> - { + fn storage_hash( + &self, + id: &BlockId, + key: &StorageKey, + ) -> sp_blockchain::Result> { Ok(self.state_at(id)? .storage_hash(&key.0).map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))? ) diff --git a/substrate/primitives/storage/src/lib.rs b/substrate/primitives/storage/src/lib.rs index d2c4a73e23..ed90c4584b 100644 --- a/substrate/primitives/storage/src/lib.rs +++ b/substrate/primitives/storage/src/lib.rs @@ -92,9 +92,9 @@ pub struct StorageData( #[cfg(feature = "std")] pub type StorageMap = std::collections::BTreeMap, Vec>; +/// Child trie storage data. #[cfg(feature = "std")] #[derive(Debug, PartialEq, Eq, Clone)] -/// Child trie storage data. pub struct StorageChild { /// Child data for storage. pub data: StorageMap, @@ -103,9 +103,9 @@ pub struct StorageChild { pub child_info: ChildInfo, } +/// Struct containing data needed for a storage. #[cfg(feature = "std")] #[derive(Default, Debug, Clone)] -/// Struct containing data needed for a storage. pub struct Storage { /// Top trie storage data. pub top: StorageMap, @@ -148,6 +148,9 @@ pub mod well_known_keys { /// Prefix of child storage keys. pub const CHILD_STORAGE_KEY_PREFIX: &'static [u8] = b":child_storage:"; + /// Prefix of the default child storage keys in the top trie. + pub const DEFAULT_CHILD_STORAGE_KEY_PREFIX: &'static [u8] = b":child_storage:default:"; + /// Whether a key is a child storage key. /// /// This is convenience function which basically checks if the given `key` starts @@ -300,7 +303,7 @@ impl ChildType { /// is one. pub fn parent_prefix(&self) -> &'static [u8] { match self { - &ChildType::ParentKeyId => DEFAULT_CHILD_TYPE_PARENT_PREFIX, + &ChildType::ParentKeyId => well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX, } } } @@ -331,12 +334,10 @@ impl ChildTrieParentKeyId { } } -const DEFAULT_CHILD_TYPE_PARENT_PREFIX: &'static [u8] = b":child_storage:default:"; - #[test] fn test_prefix_default_child_info() { let child_info = ChildInfo::new_default(b"any key"); let prefix = child_info.child_type().parent_prefix(); assert!(prefix.starts_with(well_known_keys::CHILD_STORAGE_KEY_PREFIX)); - assert!(prefix.starts_with(DEFAULT_CHILD_TYPE_PARENT_PREFIX)); + assert!(prefix.starts_with(well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX)); }