// Copyright 2017 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 . //! Substrate chain configurations. use std::collections::HashMap; use std::fs::File; use std::path::PathBuf; use primitives::storage::{StorageKey, StorageData}; use runtime_primitives::{BuildStorage, StorageMap}; use serde_json as json; use components::RuntimeGenesis; enum GenesisSource { File(PathBuf), Embedded(&'static [u8]), Factory(fn() -> G), } impl Clone for GenesisSource { fn clone(&self) -> Self { match *self { GenesisSource::File(ref path) => GenesisSource::File(path.clone()), GenesisSource::Embedded(d) => GenesisSource::Embedded(d), GenesisSource::Factory(f) => GenesisSource::Factory(f), } } } impl GenesisSource { fn resolve(&self) -> Result, String> { #[derive(Serialize, Deserialize)] struct GenesisContainer { genesis: Genesis, } match *self { GenesisSource::File(ref 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::Embedded(buf) => { let genesis: GenesisContainer = json::from_reader(buf).map_err(|e| format!("Error parsing embedded file: {}", e))?; Ok(genesis.genesis) }, GenesisSource::Factory(f) => Ok(Genesis::Runtime(f())), } } } impl<'a, G: RuntimeGenesis> BuildStorage for &'a ChainSpec { fn build_storage(self) -> Result { match self.genesis.resolve()? { Genesis::Runtime(gc) => gc.build_storage(), Genesis::Raw(map) => Ok(map.into_iter().map(|(k, v)| (k.0, v.0)).collect()), } } } #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] enum Genesis { Runtime(G), Raw(HashMap), } #[derive(Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] struct ChainSpecFile { pub name: String, pub id: String, pub boot_nodes: Vec, pub telemetry_url: Option, pub protocol_id: Option, } /// A configuration of a chain. Can be used to build a genesis block. pub struct ChainSpec { spec: ChainSpecFile, genesis: GenesisSource, } impl Clone for ChainSpec { fn clone(&self) -> Self { ChainSpec { spec: self.spec.clone(), genesis: self.genesis.clone(), } } } impl ChainSpec { pub fn boot_nodes(&self) -> &[String] { &self.spec.boot_nodes } pub fn name(&self) -> &str { &self.spec.name } pub fn id(&self) -> &str { &self.spec.id } pub fn telemetry_url(&self) -> Option<&str> { self.spec.telemetry_url.as_ref().map(String::as_str) } pub fn protocol_id(&self) -> Option<&str> { self.spec.protocol_id.as_ref().map(String::as_str) } /// Parse json content into a `ChainSpec` pub fn from_embedded(json: &'static [u8]) -> Result { let spec = json::from_slice(json).map_err(|e| format!("Error parsing spec file: {}", e))?; Ok(ChainSpec { spec, genesis: GenesisSource::Embedded(json), }) } /// Parse json file into a `ChainSpec` pub fn from_json_file(path: PathBuf) -> Result { let file = File::open(&path).map_err(|e| format!("Error opening spec file: {}", e))?; let spec = json::from_reader(file).map_err(|e| format!("Error parsing spec file: {}", e))?; Ok(ChainSpec { spec, genesis: GenesisSource::File(path), }) } /// Create hardcoded spec. pub fn from_genesis( name: &str, id: &str, constructor: fn() -> G, boot_nodes: Vec, telemetry_url: Option<&str>, protocol_id: Option<&str>, ) -> Self { let spec = ChainSpecFile { name: name.to_owned(), id: id.to_owned(), boot_nodes: boot_nodes, telemetry_url: telemetry_url.map(str::to_owned), protocol_id: protocol_id.map(str::to_owned), }; ChainSpec { spec, genesis: GenesisSource::Factory(constructor), } } /// Dump to json string. pub fn to_json(self, raw: bool) -> Result { #[derive(Serialize, Deserialize)] struct Container { #[serde(flatten)] spec: ChainSpecFile, genesis: Genesis, }; let genesis = match (raw, self.genesis.resolve()?) { (true, Genesis::Runtime(g)) => { let storage = g.build_storage()?.into_iter() .map(|(k, v)| (StorageKey(k), StorageData(v))) .collect(); Genesis::Raw(storage) }, (_, genesis) => genesis, }; let spec = Container { spec: self.spec, genesis, }; json::to_string_pretty(&spec).map_err(|e| format!("Error generating spec json: {}", e)) } }