// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum 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. // Parity Ethereum 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 Parity Ethereum. If not, see . //! Parameters for a block chain. use std::{ collections::BTreeMap, convert::TryFrom, fmt, io::Read, path::Path, sync::Arc, }; use common_types::{ BlockNumber, header::Header, encoded, engines::{OptimizeFor, params::CommonParams}, errors::EthcoreError as Error, transaction::{Action, Transaction}, }; use account_state::{Backend, State, backend::Basic as BasicBackend}; use authority_round::AuthorityRound; use basic_authority::BasicAuthority; use bytes::Bytes; use builtin::Builtin; use clique::Clique; use engine::Engine; use ethash_engine::Ethash; use ethereum_types::{H256, Bloom, U256, Address}; use ethjson; use instant_seal::{InstantSeal, InstantSealParams}; use keccak_hash::{KECCAK_NULL_RLP, keccak}; use log::{trace, warn}; use machine::{executive::Executive, Machine, substate::Substate}; use null_engine::NullEngine; use pod::PodState; use rlp::{Rlp, RlpStream}; use trace::{NoopTracer, NoopVMTracer}; use trie_vm_factories::Factories; use vm::{EnvInfo, ActionType, ActionValue, ActionParams, ParamsType}; use crate::{ Genesis, seal::Generic as GenericSeal, }; /// Runtime parameters for the spec that are related to how the software should run the chain, /// rather than integral properties of the chain itself. pub struct SpecParams<'a> { /// The path to the folder used to cache nodes. This is typically /tmp/ on Unix-like systems pub cache_dir: &'a Path, /// Whether to run slower at the expense of better memory usage, or run faster while using /// more /// memory. This may get more fine-grained in the future but for now is simply a binary /// option. pub optimization_setting: Option, } impl<'a> SpecParams<'a> { /// Create from a cache path, with null values for the other fields pub fn from_path(path: &'a Path) -> Self { SpecParams { cache_dir: path, optimization_setting: None, } } /// Create from a cache path and an optimization setting pub fn new(path: &'a Path, optimization: OptimizeFor) -> Self { SpecParams { cache_dir: path, optimization_setting: Some(optimization), } } } impl<'a, T: AsRef> From<&'a T> for SpecParams<'a> { fn from(path: &'a T) -> Self { Self::from_path(path.as_ref()) } } /// given a pre-constructor state, run all the given constructors and produce a new state and /// state root. fn run_constructors( genesis_state: &PodState, constructors: &[(Address, Bytes)], engine: &dyn Engine, author: Address, timestamp: u64, difficulty: U256, factories: &Factories, mut db: T ) -> Result<(H256, T), Error> { let mut root = KECCAK_NULL_RLP; // basic accounts in spec. { let mut t = factories.trie.create(db.as_hash_db_mut(), &mut root); for (address, account) in genesis_state.get().iter() { t.insert(address.as_bytes(), &account.rlp())?; } } for (address, account) in genesis_state.get().iter() { db.note_non_null_account(address); account.insert_additional( &mut *factories.accountdb.create( db.as_hash_db_mut(), keccak(address), ), &factories.trie, ); } let start_nonce = engine.account_start_nonce(0); let mut state = State::from_existing(db, root, start_nonce, factories.clone())?; if constructors.is_empty() { state.populate_from(genesis_state.clone()); let _ = state.commit()?; } else { // Execute contract constructors. let env_info = EnvInfo { number: 0, author, timestamp, difficulty, last_hashes: Default::default(), gas_used: U256::zero(), gas_limit: U256::max_value(), }; let from = Address::zero(); for &(ref address, ref constructor) in constructors.iter() { trace!(target: "spec", "run_constructors: Creating a contract at {}.", address); trace!(target: "spec", " .. root before = {}", state.root()); let params = ActionParams { code_address: address.clone(), code_hash: Some(keccak(constructor)), code_version: U256::zero(), address: address.clone(), sender: from.clone(), origin: from.clone(), gas: U256::max_value(), gas_price: Default::default(), value: ActionValue::Transfer(Default::default()), code: Some(Arc::new(constructor.clone())), data: None, action_type: ActionType::Create, params_type: ParamsType::Embedded, }; let mut substate = Substate::new(); { let machine = engine.machine(); let schedule = machine.schedule(env_info.number); let mut exec = Executive::new(&mut state, &env_info, &machine, &schedule); // failing create is not a bug if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) { warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e); } } let _ = state.commit()?; } } Ok(state.drop()) } /// Parameters for a block chain; includes both those intrinsic to the design of the /// chain and those to be interpreted by the active chain engine. pub struct Spec { /// User friendly spec name. pub name: String, /// Engine specified by json file. pub engine: Arc, /// Name of the subdir inside the main data dir to use for chain data and settings. pub data_dir: String, /// Known nodes on the network in enode format. pub nodes: Vec, /// The genesis block's parent hash field. pub parent_hash: H256, /// The genesis block's author field. pub author: Address, /// The genesis block's difficulty field. pub difficulty: U256, /// The genesis block's gas limit field. pub gas_limit: U256, /// The genesis block's gas used field. pub gas_used: U256, /// The genesis block's timestamp field. pub timestamp: u64, /// Transactions root of the genesis block. Should be KECCAK_NULL_RLP. pub transactions_root: H256, /// Receipts root of the genesis block. Should be KECCAK_NULL_RLP. pub receipts_root: H256, /// The genesis block's extra data field. pub extra_data: Bytes, /// Each seal field, expressed as RLP, concatenated. pub seal_rlp: Bytes, /// Hardcoded synchronization. Allows the light client to immediately jump to a specific block. pub hardcoded_sync: Option, /// Contract constructors to be executed on genesis. pub constructors: Vec<(Address, Bytes)>, /// May be pre-populated if we know this in advance. pub state_root: H256, /// Genesis state as plain old data. pub genesis_state: PodState, } /// Part of `Spec`. Describes the hardcoded synchronization parameters. pub struct SpecHardcodedSync { /// Header of the block to jump to for hardcoded sync, and total difficulty. pub header: encoded::Header, /// Total difficulty of the block to jump to. pub total_difficulty: U256, /// List of hardcoded CHTs, in order. If `hardcoded_sync` is set, the CHTs should include the /// header of `hardcoded_sync`. pub chts: Vec, } impl From for SpecHardcodedSync { fn from(sync: ethjson::spec::HardcodedSync) -> Self { SpecHardcodedSync { header: encoded::Header::new(sync.header.into()), total_difficulty: sync.total_difficulty.into(), chts: sync.chts.into_iter().map(Into::into).collect(), } } } impl fmt::Display for SpecHardcodedSync { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "{{")?; writeln!(f, r#"header": "{:?},"#, self.header)?; writeln!(f, r#"total_difficulty": "{:?},"#, self.total_difficulty)?; writeln!(f, r#"chts": {:#?}"#, self.chts.iter().map(|x| format!(r#"{:?}"#, x)).collect::>())?; writeln!(f, "}}") } } fn convert_json_to_spec( (address, builtin): (ethjson::hash::Address, ethjson::spec::builtin::Builtin), ) -> Result<(Address, Builtin), Error> { let builtin = Builtin::try_from(builtin)?; Ok((address.into(), builtin)) } /// Load from JSON object. fn load_from(spec_params: SpecParams, s: ethjson::spec::Spec) -> Result { let builtins: Result, _> = s .accounts .builtins() .into_iter() .map(convert_json_to_spec) .collect(); let builtins = builtins?; let g = Genesis::from(s.genesis); let GenericSeal(seal_rlp) = g.seal.into(); let params = CommonParams::from(s.params); let hardcoded_sync = s.hardcoded_sync.map(Into::into); let engine = Spec::engine(spec_params, s.engine, params, builtins); let author = g.author; let timestamp = g.timestamp; let difficulty = g.difficulty; let constructors: Vec<_> = s.accounts .constructors() .into_iter() .map(|(a, c)| (a.into(), c.into())) .collect(); let genesis_state: PodState = s.accounts.into(); let (state_root, _) = run_constructors( &genesis_state, &constructors, &*engine, author, timestamp, difficulty, &Default::default(), BasicBackend(journaldb::new_memory_db()), )?; let s = Spec { engine, name: s.name.clone().into(), data_dir: s.data_dir.unwrap_or(s.name).into(), nodes: s.nodes.unwrap_or_else(Vec::new), parent_hash: g.parent_hash, transactions_root: g.transactions_root, receipts_root: g.receipts_root, author, difficulty, gas_limit: g.gas_limit, gas_used: g.gas_used, timestamp, extra_data: g.extra_data, seal_rlp, hardcoded_sync, constructors, genesis_state, state_root, }; Ok(s) } impl Spec { // create an instance of an Ethereum state machine, minus consensus logic. fn machine( engine_spec: ðjson::spec::Engine, params: CommonParams, builtins: BTreeMap, ) -> Machine { if let ethjson::spec::Engine::Ethash(ref ethash) = *engine_spec { Machine::with_ethash_extensions(params, builtins, ethash.params.clone().into()) } else { Machine::regular(params, builtins) } } /// Convert engine spec into a arc'd Engine of the right underlying type. /// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead. fn engine( spec_params: SpecParams, engine_spec: ethjson::spec::Engine, params: CommonParams, builtins: BTreeMap, ) -> Arc { let machine = Self::machine(&engine_spec, params, builtins); match engine_spec { ethjson::spec::Engine::Null(null) => Arc::new(NullEngine::new(null.params.into(), machine)), ethjson::spec::Engine::Ethash(ethash) => Arc::new(Ethash::new(spec_params.cache_dir, ethash.params.into(), machine, spec_params.optimization_setting)), ethjson::spec::Engine::InstantSeal(Some(instant_seal)) => Arc::new(InstantSeal::new(instant_seal.params.into(), machine)), ethjson::spec::Engine::InstantSeal(None) => Arc::new(InstantSeal::new(InstantSealParams::default(), machine)), ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(basic_authority.params.into(), machine)), ethjson::spec::Engine::Clique(clique) => Clique::new(clique.params.into(), machine) .expect("Failed to start Clique consensus engine."), ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(authority_round.params.into(), machine) .expect("Failed to start AuthorityRound consensus engine."), } } /// Get common blockchain parameters. pub fn params(&self) -> &CommonParams { &self.engine.params() } /// Get the configured Network ID. pub fn network_id(&self) -> u64 { self.params().network_id } /// Get the chain ID used for signing. pub fn chain_id(&self) -> u64 { self.params().chain_id } /// Get the configured subprotocol name. pub fn subprotocol_name(&self) -> String { self.params().subprotocol_name.clone() } /// Get the configured network fork block. pub fn fork_block(&self) -> Option<(BlockNumber, H256)> { self.params().fork_block } /// Get the header of the genesis block. pub fn genesis_header(&self) -> Header { let mut header: Header = Default::default(); header.set_parent_hash(self.parent_hash.clone()); header.set_timestamp(self.timestamp); header.set_number(0); header.set_author(self.author.clone()); header.set_transactions_root(self.transactions_root.clone()); header.set_uncles_hash(keccak(RlpStream::new_list(0).out())); header.set_extra_data(self.extra_data.clone()); header.set_state_root(self.state_root); header.set_receipts_root(self.receipts_root.clone()); header.set_log_bloom(Bloom::default()); header.set_gas_used(self.gas_used.clone()); header.set_gas_limit(self.gas_limit.clone()); header.set_difficulty(self.difficulty.clone()); header.set_seal({ let r = Rlp::new(&self.seal_rlp); r.iter().map(|f| f.as_raw().to_vec()).collect() }); trace!(target: "spec", "Header hash is {}", header.hash()); header } /// Compose the genesis block for this chain. pub fn genesis_block(&self) -> Bytes { let empty_list = RlpStream::new_list(0).out(); let header = self.genesis_header(); let mut ret = RlpStream::new_list(3); ret.append(&header); ret.append_raw(&empty_list, 1); ret.append_raw(&empty_list, 1); ret.out() } /// Overwrite the genesis components. pub fn overwrite_genesis_params(&mut self, g: Genesis) { let GenericSeal(seal_rlp) = g.seal.into(); self.parent_hash = g.parent_hash; self.transactions_root = g.transactions_root; self.receipts_root = g.receipts_root; self.author = g.author; self.difficulty = g.difficulty; self.gas_limit = g.gas_limit; self.gas_used = g.gas_used; self.timestamp = g.timestamp; self.extra_data = g.extra_data; self.seal_rlp = seal_rlp; } /// Alter the value of the genesis state. pub fn set_genesis_state(&mut self, s: PodState) -> Result<(), Error> { self.genesis_state = s; let (root, _) = run_constructors( &self.genesis_state, &self.constructors, &*self.engine, self.author, self.timestamp, self.difficulty, &Default::default(), BasicBackend(journaldb::new_memory_db()), )?; self.state_root = root; Ok(()) } /// Ensure that the given state DB has the trie nodes in for the genesis state. pub fn ensure_db_good(&self, db: T, factories: &Factories) -> Result { if db.as_hash_db().contains(&self.state_root, hash_db::EMPTY_PREFIX) { return Ok(db); } // TODO: could optimize so we don't re-run, but `ensure_db_good` is barely ever // called anyway. let (root, db) = run_constructors( &self.genesis_state, &self.constructors, &*self.engine, self.author, self.timestamp, self.difficulty, factories, db )?; assert_eq!(root, self.state_root, "Spec's state root has not been precomputed correctly."); Ok(db) } /// Loads just the state machine from a json file. pub fn load_machine(reader: R) -> Result { ethjson::spec::Spec::load(reader) .map_err(|e| Error::Msg(e.to_string())) .and_then(|s| { let builtins: Result, _> = s .accounts .builtins() .into_iter() .map(convert_json_to_spec) .collect(); let builtins = builtins?; let params = CommonParams::from(s.params); Ok(Spec::machine(&s.engine, params, builtins)) }) } /// Loads spec from json file. Provide factories for executing contracts and ensuring /// storage goes to the right place. pub fn load<'a, T: Into>, R: Read>(params: T, reader: R) -> Result { ethjson::spec::Spec::load(reader) .map_err(|e| Error::Msg(e.to_string())) .and_then(|x| load_from(params.into(), x)) } /// initialize genesis epoch data, using in-memory database for /// constructor. pub fn genesis_epoch_data(&self) -> Result, String> { let genesis = self.genesis_header(); let factories = Default::default(); let mut db = journaldb::new( Arc::new(kvdb_memorydb::create(1)), journaldb::Algorithm::Archive, 0, ); self.ensure_db_good(BasicBackend(db.as_hash_db_mut()), &factories) .map_err(|e| format!("Unable to initialize genesis state: {}", e))?; let call = |a, d| { let mut db = db.boxed_clone(); let env_info = evm::EnvInfo { number: 0, author: *genesis.author(), timestamp: genesis.timestamp(), difficulty: *genesis.difficulty(), gas_limit: U256::max_value(), last_hashes: Arc::new(Vec::new()), gas_used: 0.into(), }; let from = Address::zero(); let tx = Transaction { nonce: self.engine.account_start_nonce(0), action: Action::Call(a), gas: U256::max_value(), gas_price: U256::default(), value: U256::default(), data: d, }.fake_sign(from); executive_state::prove_transaction_virtual( db.as_hash_db_mut(), *genesis.state_root(), &tx, self.engine.machine(), &env_info, factories.clone(), ).ok_or_else(|| "Failed to prove call: insufficient state".into()) }; self.engine.genesis_epoch_data(&genesis, &call) } } #[cfg(test)] mod tests { use std::str::FromStr; use account_state::State; use common_types::{view, views::BlockView}; use ethereum_types::{Address, H256}; use ethcore::test_helpers::get_temp_state_db; use tempdir::TempDir; use super::Spec; #[test] fn test_load_empty() { let tempdir = TempDir::new("").unwrap(); assert!(Spec::load(&tempdir.path(), &[] as &[u8]).is_err()); } #[test] fn test_chain() { let test_spec = crate::new_test(); assert_eq!( test_spec.state_root, H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap() ); let genesis = test_spec.genesis_block(); assert_eq!( view!(BlockView, &genesis).header_view().hash(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap() ); } #[test] fn genesis_constructor() { let _ = ::env_logger::try_init(); let spec = crate::new_test_constructor(); let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()) .unwrap(); let state = State::from_existing( db.boxed_clone(), spec.state_root, spec.engine.account_start_nonce(0), Default::default(), ).unwrap(); let expected = H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(); let address = Address::from_str("0000000000000000000000000000000000001337").unwrap(); assert_eq!(state.storage_at(&address, &H256::zero()).unwrap(), expected); assert_eq!(state.balance(&address).unwrap(), 1.into()); } }