diff --git a/dapps/src/apps/fetcher/mod.rs b/dapps/src/apps/fetcher/mod.rs index ff14def7b..cbc2ab212 100644 --- a/dapps/src/apps/fetcher/mod.rs +++ b/dapps/src/apps/fetcher/mod.rs @@ -48,7 +48,7 @@ pub trait Fetcher: Send + Sync + 'static { } pub struct ContentFetcher { - dapps_path: PathBuf, + cache_path: PathBuf, resolver: R, cache: Arc>, sync: Arc, @@ -61,7 +61,7 @@ pub struct ContentFetcher Drop for ContentFetcher { fn drop(&mut self) { // Clear cache path - let _ = fs::remove_dir_all(&self.dapps_path); + let _ = fs::remove_dir_all(&self.cache_path); } } @@ -73,11 +73,11 @@ impl ContentFetcher { remote: Remote, fetch: F, ) -> Self { - let mut dapps_path = env::temp_dir(); - dapps_path.push(random_filename()); + let mut cache_path = env::temp_dir(); + cache_path.push(random_filename()); ContentFetcher { - dapps_path: dapps_path, + cache_path: cache_path, resolver: resolver, sync: sync_status, cache: Arc::new(Mutex::new(ContentCache::default())), @@ -200,7 +200,7 @@ impl Fetcher for ContentFetcher { control, installers::Dapp::new( content_id.clone(), - self.dapps_path.clone(), + self.cache_path.clone(), Box::new(on_done), self.embeddable_on.clone(), ), @@ -219,7 +219,7 @@ impl Fetcher for ContentFetcher { installers::Content::new( content_id.clone(), content.mime, - self.dapps_path.clone(), + self.cache_path.clone(), Box::new(on_done), ), self.embeddable_on.clone(), diff --git a/ethash/src/compute.rs b/ethash/src/compute.rs index 992578041..fae47c505 100644 --- a/ethash/src/compute.rs +++ b/ethash/src/compute.rs @@ -25,7 +25,7 @@ use std::mem; use std::ptr; use sha3; use std::slice; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::io::{self, Read, Write}; use std::fs::{self, File}; @@ -86,6 +86,7 @@ impl Node { pub type H256 = [u8; 32]; pub struct Light { + cache_dir: PathBuf, block_number: u64, cache: Vec, seed_compute: Mutex, @@ -94,8 +95,8 @@ pub struct Light { /// Light cache structure impl Light { /// Create a new light cache for a given block number - pub fn new(block_number: u64) -> Light { - light_new(block_number) + pub fn new>(cache_dir: T, block_number: u64) -> Light { + light_new(cache_dir, block_number) } /// Calculate the light boundary data @@ -105,17 +106,15 @@ impl Light { light_compute(self, header_hash, nonce) } - pub fn file_path(seed_hash: H256) -> PathBuf { - let mut home = ::std::env::home_dir().unwrap(); - home.push(".ethash"); - home.push("light"); - home.push(to_hex(&seed_hash)); - home + pub fn file_path>(cache_dir: T, seed_hash: H256) -> PathBuf { + let mut cache_dir = cache_dir.as_ref().to_path_buf(); + cache_dir.push(to_hex(&seed_hash)); + cache_dir } - pub fn from_file(block_number: u64) -> io::Result { + pub fn from_file>(cache_dir: T, block_number: u64) -> io::Result { let seed_compute = SeedHashCompute::new(); - let path = Light::file_path(seed_compute.get_seedhash(block_number)); + let path = Light::file_path(&cache_dir, seed_compute.get_seedhash(block_number)); let mut file = File::open(path)?; let cache_size = get_cache_size(block_number); @@ -128,19 +127,22 @@ impl Light { let buf = unsafe { slice::from_raw_parts_mut(nodes.as_mut_ptr() as *mut u8, cache_size) }; file.read_exact(buf)?; Ok(Light { + block_number, + cache_dir: cache_dir.as_ref().to_path_buf(), cache: nodes, - block_number: block_number, seed_compute: Mutex::new(seed_compute), }) } pub fn to_file(&self) -> io::Result { let seed_compute = self.seed_compute.lock(); - let path = Light::file_path(seed_compute.get_seedhash(self.block_number)); + let path = Light::file_path(&self.cache_dir, seed_compute.get_seedhash(self.block_number)); if self.block_number >= ETHASH_EPOCH_LENGTH * 2 { let deprecated = Light::file_path( - seed_compute.get_seedhash(self.block_number - ETHASH_EPOCH_LENGTH * 2)); + &self.cache_dir, + seed_compute.get_seedhash(self.block_number - ETHASH_EPOCH_LENGTH * 2) + ); if deprecated.exists() { debug!(target: "ethash", "removing: {:?}", &deprecated); @@ -341,14 +343,12 @@ fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node { } } -fn light_new(block_number: u64) -> Light { +fn light_new>(cache_dir: T, block_number: u64) -> Light { let seed_compute = SeedHashCompute::new(); let seedhash = seed_compute.get_seedhash(block_number); let cache_size = get_cache_size(block_number); - if cache_size % NODE_BYTES != 0 { - panic!("Unaligned cache size"); - } + assert!(cache_size % NODE_BYTES == 0, "Unaligned cache size"); let num_nodes = cache_size / NODE_BYTES; let mut nodes = Vec::with_capacity(num_nodes); @@ -372,8 +372,9 @@ fn light_new(block_number: u64) -> Light { } Light { + block_number, + cache_dir: cache_dir.as_ref().to_path_buf(), cache: nodes, - block_number: block_number, seed_compute: Mutex::new(seed_compute), } } @@ -432,7 +433,7 @@ fn test_light_compute() { let boundary = [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, 0xe9, 0x7e, 0x53, 0x84]; let nonce = 0xd7b3ac70a301a249; // difficulty = 0x085657254bd9u64; - let light = Light::new(486382); + let light = Light::new(&::std::env::temp_dir(), 486382); let result = light_compute(&light, &hash, nonce); assert_eq!(result.mix_hash[..], mix_hash[..]); assert_eq!(result.value[..], boundary[..]); @@ -471,15 +472,16 @@ fn test_seed_compute_after_newer() { #[test] fn test_drop_old_data() { - let first = Light::new(0).to_file().unwrap(); + let path = ::std::env::temp_dir(); + let first = Light::new(&path, 0).to_file().unwrap(); - let second = Light::new(ETHASH_EPOCH_LENGTH).to_file().unwrap(); + let second = Light::new(&path, ETHASH_EPOCH_LENGTH).to_file().unwrap(); assert!(fs::metadata(&first).is_ok()); - let _ = Light::new(ETHASH_EPOCH_LENGTH * 2).to_file(); + let _ = Light::new(&path, ETHASH_EPOCH_LENGTH * 2).to_file(); assert!(fs::metadata(&first).is_err()); assert!(fs::metadata(&second).is_ok()); - let _ = Light::new(ETHASH_EPOCH_LENGTH * 3).to_file(); + let _ = Light::new(&path, ETHASH_EPOCH_LENGTH * 3).to_file(); assert!(fs::metadata(&second).is_err()); } diff --git a/ethash/src/lib.rs b/ethash/src/lib.rs index 956da5b87..a598af6f7 100644 --- a/ethash/src/lib.rs +++ b/ethash/src/lib.rs @@ -25,6 +25,7 @@ extern crate log; mod compute; use std::mem; +use std::path::{Path, PathBuf}; use compute::Light; pub use compute::{ETHASH_EPOCH_LENGTH, H256, ProofOfWork, SeedHashCompute, quick_get_difficulty, slow_get_seedhash}; @@ -41,12 +42,14 @@ struct LightCache { /// Light/Full cache manager. pub struct EthashManager { cache: Mutex, + cache_dir: PathBuf, } impl EthashManager { /// Create a new new instance of ethash manager - pub fn new() -> EthashManager { + pub fn new>(cache_dir: T) -> EthashManager { EthashManager { + cache_dir: cache_dir.as_ref().to_path_buf(), cache: Mutex::new(LightCache { recent_epoch: None, recent: None, @@ -88,11 +91,11 @@ impl EthashManager { }; match light { None => { - let light = match Light::from_file(block_number) { + let light = match Light::from_file(&self.cache_dir, block_number) { Ok(light) => Arc::new(light), Err(e) => { debug!("Light cache file not found for {}:{}", block_number, e); - let light = Light::new(block_number); + let light = Light::new(&self.cache_dir, block_number); if let Err(e) = light.to_file() { warn!("Light cache file write error: {}", e); } @@ -112,7 +115,7 @@ impl EthashManager { #[test] fn test_lru() { - let ethash = EthashManager::new(); + let ethash = EthashManager::new(&::std::env::temp_dir()); let hash = [0u8; 32]; ethash.compute_light(1, &hash, 1); ethash.compute_light(50000, &hash, 1); diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 4623352d1..44bdbf6c9 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -266,7 +266,7 @@ mod tests { /// Create a new test chain spec with `BasicAuthority` consensus engine. fn new_test_authority() -> Spec { let bytes: &[u8] = include_bytes!("../../res/basic_authority.json"); - Spec::load(bytes).expect("invalid chain spec") + Spec::load(::std::env::temp_dir(), bytes).expect("invalid chain spec") } #[test] diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 632ba08c3..9a25db593 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -386,7 +386,6 @@ pub trait Engine : Sync + Send { } } - /// Common engine utilities pub mod common { use block::ExecutedBlock; diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 3b48ab321..f29c0421a 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::path::Path; use ethash::{quick_get_difficulty, slow_get_seedhash, EthashManager}; use util::*; use block::*; @@ -24,7 +25,7 @@ use header::{Header, BlockNumber}; use state::CleanupMode; use spec::CommonParams; use transaction::UnverifiedTransaction; -use engines::Engine; +use engines::{self, Engine}; use evm::Schedule; use ethjson; use rlp::{self, UntrustedRlp}; @@ -147,12 +148,17 @@ pub struct Ethash { impl Ethash { /// Create a new instance of Ethash engine - pub fn new(params: CommonParams, ethash_params: EthashParams, builtins: BTreeMap) -> Arc { + pub fn new>( + cache_dir: T, + params: CommonParams, + ethash_params: EthashParams, + builtins: BTreeMap, + ) -> Arc { Arc::new(Ethash { - params: params, - ethash_params: ethash_params, - builtins: builtins, - pow: EthashManager::new(), + params, + ethash_params, + builtins, + pow: EthashManager::new(cache_dir), }) } } @@ -165,7 +171,7 @@ impl Ethash { // for any block in the chain. // in the future, we might move the Ethash epoch // caching onto this mechanism as well. -impl ::engines::EpochVerifier for Arc { +impl engines::EpochVerifier for Arc { fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } fn verify_heavy(&self, header: &Header) -> Result<(), Error> { self.verify_block_unordered(header, None) @@ -262,7 +268,7 @@ impl Engine for Arc { _begins_epoch: bool, ) -> Result<(), Error> { let parent_hash = block.fields().header.parent_hash().clone(); - ::engines::common::push_last_hash(block, last_hashes, self, &parent_hash)?; + engines::common::push_last_hash(block, last_hashes, self, &parent_hash)?; if block.fields().header.number() == self.ethash_params.dao_hardfork_transition { let state = block.fields_mut().state; for child in &self.ethash_params.dao_hardfork_accounts { @@ -404,8 +410,8 @@ impl Engine for Arc { Ok(()) } - fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> ::engines::ConstructedVerifier<'a> { - ::engines::ConstructedVerifier::Trusted(Box::new(self.clone())) + fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> engines::ConstructedVerifier<'a> { + engines::ConstructedVerifier::Trusted(Box::new(self.clone())) } fn snapshot_components(&self) -> Option> { @@ -558,13 +564,18 @@ mod tests { use engines::Engine; use error::{BlockError, Error}; use header::Header; + use spec::Spec; use super::super::{new_morden, new_homestead_test}; use super::{Ethash, EthashParams, PARITY_GAS_LIMIT_DETERMINANT, ecip1017_eras_block_reward}; use rlp; + fn test_spec() -> Spec { + new_morden(&::std::env::temp_dir()) + } + #[test] fn on_close_block() { - let spec = new_morden(); + let spec = test_spec(); let engine = &*spec.engine; let genesis_header = spec.genesis_header(); let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); @@ -576,7 +587,7 @@ mod tests { #[test] fn on_close_block_with_uncle() { - let spec = new_morden(); + let spec = test_spec(); let engine = &*spec.engine; let genesis_header = spec.genesis_header(); let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); @@ -594,14 +605,14 @@ mod tests { #[test] fn has_valid_metadata() { - let engine = new_morden().engine; + let engine = test_spec().engine; assert!(!engine.name().is_empty()); assert!(engine.version().major >= 1); } #[test] fn can_return_schedule() { - let engine = new_morden().engine; + let engine = test_spec().engine; let schedule = engine.schedule(10000000); assert!(schedule.stack_limit > 0); @@ -611,8 +622,8 @@ mod tests { #[test] fn can_do_seal_verification_fail() { - let engine = new_morden().engine; - //let engine = Ethash::new_test(new_morden()); + let engine = test_spec().engine; + //let engine = Ethash::new_test(test_spec()); let header: Header = Header::default(); let verify_result = engine.verify_block_basic(&header, None); @@ -626,7 +637,7 @@ mod tests { #[test] fn can_do_difficulty_verification_fail() { - let engine = new_morden().engine; + let engine = test_spec().engine; let mut header: Header = Header::default(); header.set_seal(vec![rlp::encode(&H256::zero()).into_vec(), rlp::encode(&H64::zero()).into_vec()]); @@ -641,7 +652,7 @@ mod tests { #[test] fn can_do_proof_of_work_verification_fail() { - let engine = new_morden().engine; + let engine = test_spec().engine; let mut header: Header = Header::default(); header.set_seal(vec![rlp::encode(&H256::zero()).into_vec(), rlp::encode(&H64::zero()).into_vec()]); header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap()); @@ -657,7 +668,7 @@ mod tests { #[test] fn can_do_seal_unordered_verification_fail() { - let engine = new_morden().engine; + let engine = test_spec().engine; let header: Header = Header::default(); let verify_result = engine.verify_block_unordered(&header, None); @@ -671,7 +682,7 @@ mod tests { #[test] fn can_do_seal256_verification_fail() { - let engine = new_morden().engine; + let engine = test_spec().engine; let mut header: Header = Header::default(); header.set_seal(vec![rlp::encode(&H256::zero()).into_vec(), rlp::encode(&H64::zero()).into_vec()]); let verify_result = engine.verify_block_unordered(&header, None); @@ -685,7 +696,7 @@ mod tests { #[test] fn can_do_proof_of_work_unordered_verification_fail() { - let engine = new_morden().engine; + let engine = test_spec().engine; let mut header: Header = Header::default(); header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")).into_vec(), rlp::encode(&H64::zero()).into_vec()]); header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap()); @@ -701,7 +712,7 @@ mod tests { #[test] fn can_verify_block_family_genesis_fail() { - let engine = new_morden().engine; + let engine = test_spec().engine; let header: Header = Header::default(); let parent_header: Header = Header::default(); @@ -716,7 +727,7 @@ mod tests { #[test] fn can_verify_block_family_difficulty_fail() { - let engine = new_morden().engine; + let engine = test_spec().engine; let mut header: Header = Header::default(); header.set_number(2); let mut parent_header: Header = Header::default(); @@ -733,7 +744,7 @@ mod tests { #[test] fn can_verify_block_family_gas_fail() { - let engine = new_morden().engine; + let engine = test_spec().engine; let mut header: Header = Header::default(); header.set_number(2); header.set_difficulty(U256::from_str("0000000000000000000000000000000000000000000000000000000000020000").unwrap()); @@ -763,7 +774,7 @@ mod tests { fn difficulty_frontier() { let spec = new_homestead_test(); let ethparams = get_default_ethash_params(); - let ethash = Ethash::new(spec.params().clone(), ethparams, BTreeMap::new()); + let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new()); let mut parent_header = Header::default(); parent_header.set_number(1000000); @@ -781,7 +792,7 @@ mod tests { fn difficulty_homestead() { let spec = new_homestead_test(); let ethparams = get_default_ethash_params(); - let ethash = Ethash::new(spec.params().clone(), ethparams, BTreeMap::new()); + let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new()); let mut parent_header = Header::default(); parent_header.set_number(1500000); @@ -838,7 +849,7 @@ mod tests { ecip1010_pause_transition: 3000000, ..get_default_ethash_params() }; - let ethash = Ethash::new(spec.params().clone(), ethparams, BTreeMap::new()); + let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new()); let mut parent_header = Header::default(); parent_header.set_number(3500000); @@ -872,7 +883,7 @@ mod tests { ecip1010_continue_transition: 5000000, ..get_default_ethash_params() }; - let ethash = Ethash::new(spec.params().clone(), ethparams, BTreeMap::new()); + let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new()); let mut parent_header = Header::default(); parent_header.set_number(5000102); @@ -917,7 +928,8 @@ mod tests { #[test] fn gas_limit_is_multiple_of_determinant() { let spec = new_homestead_test(); - let ethash = Ethash::new(spec.params().clone(), get_default_ethash_params(), BTreeMap::new()); + let ethparams = get_default_ethash_params(); + let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new()); let mut parent = Header::new(); let mut header = Header::new(); header.set_number(1); @@ -961,7 +973,7 @@ mod tests { fn difficulty_max_timestamp() { let spec = new_homestead_test(); let ethparams = get_default_ethash_params(); - let ethash = Ethash::new(spec.params().clone(), ethparams, BTreeMap::new()); + let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new()); let mut parent_header = Header::default(); parent_header.set_number(1000000); @@ -989,7 +1001,7 @@ mod tests { header.set_number(parent_header.number() + 1); header.set_gas_limit(100_001.into()); header.set_difficulty(ethparams.minimum_difficulty); - let ethash = Ethash::new(spec.params().clone(), ethparams, BTreeMap::new()); + let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new()); assert!(ethash.verify_block_family(&header, &parent_header, None).is_ok()); parent_header.set_number(9); @@ -1044,7 +1056,7 @@ mod tests { nonce: U256::zero(), }.sign(keypair.secret(), None).into(); - let ethash = Ethash::new(spec.params().clone(), ethparams, BTreeMap::new()); + let ethash = Ethash::new(&::std::env::temp_dir(), spec.params().clone(), ethparams, BTreeMap::new()); assert!(ethash.verify_transaction_basic(&tx1, &header).is_ok()); assert!(ethash.verify_transaction_basic(&tx2, &header).is_ok()); diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index f94c39b33..e731ef7db 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -27,6 +27,7 @@ pub mod denominations; pub use self::ethash::{Ethash}; pub use self::denominations::*; +use std::path::Path; use super::spec::*; /// Most recent fork block that we support on Mainnet. @@ -38,51 +39,56 @@ pub const FORK_SUPPORTED_ROPSTEN: u64 = 10; /// Most recent fork block that we support on Kovan. pub const FORK_SUPPORTED_KOVAN: u64 = 0; -fn load(b: &[u8]) -> Spec { - Spec::load(b).expect("chain spec is invalid") +fn load<'a, T: 'a + Into>>(cache_dir: T, b: &[u8]) -> Spec { + match cache_dir.into() { + Some(path) => Spec::load(path, b), + None => Spec::load(&::std::env::temp_dir(), b) + }.expect("chain spec is invalid") } /// Create a new Foundation Olympic chain spec. -pub fn new_olympic() -> Spec { load(include_bytes!("../../res/ethereum/olympic.json")) } +pub fn new_olympic(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/olympic.json")) } /// Create a new Foundation Mainnet chain spec. -pub fn new_foundation() -> Spec { load(include_bytes!("../../res/ethereum/foundation.json")) } +pub fn new_foundation(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/foundation.json")) } /// Create a new Classic Mainnet chain spec without the DAO hardfork. -pub fn new_classic() -> Spec { load(include_bytes!("../../res/ethereum/classic.json")) } +pub fn new_classic(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/classic.json")) } /// Create a new Expanse mainnet chain spec. -pub fn new_expanse() -> Spec { load(include_bytes!("../../res/ethereum/expanse.json")) } +pub fn new_expanse(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/expanse.json")) } /// Create a new Kovan testnet chain spec. -pub fn new_kovan() -> Spec { load(include_bytes!("../../res/ethereum/kovan.json")) } - -/// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead. -pub fn new_frontier_test() -> Spec { load(include_bytes!("../../res/ethereum/frontier_test.json")) } - -/// Create a new Foundation Homestead-era chain spec as though it never changed from Frontier. -pub fn new_homestead_test() -> Spec { load(include_bytes!("../../res/ethereum/homestead_test.json")) } - -/// Create a new Foundation Homestead-EIP150-era chain spec as though it never changed from Homestead/Frontier. -pub fn new_eip150_test() -> Spec { load(include_bytes!("../../res/ethereum/eip150_test.json")) } - -/// Create a new Foundation Homestead-EIP161-era chain spec as though it never changed from Homestead/Frontier. -pub fn new_eip161_test() -> Spec { load(include_bytes!("../../res/ethereum/eip161_test.json")) } - -/// Create a new Foundation Frontier/Homestead/DAO chain spec with transition points at #5 and #8. -pub fn new_transition_test() -> Spec { load(include_bytes!("../../res/ethereum/transition_test.json")) } - -/// Create a new Foundation Mainnet chain spec without genesis accounts. -pub fn new_mainnet_like() -> Spec { load(include_bytes!("../../res/ethereum/frontier_like_test.json")) } - -/// Create a new Foundation Metropolis era spec. -pub fn new_metropolis_test() -> Spec { load(include_bytes!("../../res/ethereum/metropolis_test.json")) } +pub fn new_kovan(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/kovan.json")) } /// Create a new Foundation Ropsten chain spec. -pub fn new_ropsten() -> Spec { load(include_bytes!("../../res/ethereum/ropsten.json")) } +pub fn new_ropsten(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/ropsten.json")) } /// Create a new Morden chain spec. -pub fn new_morden() -> Spec { load(include_bytes!("../../res/ethereum/morden.json")) } +pub fn new_morden(cache_dir: &Path) -> Spec { load(cache_dir, include_bytes!("../../res/ethereum/morden.json")) } + +// For tests + +/// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead. +pub fn new_frontier_test() -> Spec { load(None, include_bytes!("../../res/ethereum/frontier_test.json")) } + +/// Create a new Foundation Homestead-era chain spec as though it never changed from Frontier. +pub fn new_homestead_test() -> Spec { load(None, include_bytes!("../../res/ethereum/homestead_test.json")) } + +/// Create a new Foundation Homestead-EIP150-era chain spec as though it never changed from Homestead/Frontier. +pub fn new_eip150_test() -> Spec { load(None, include_bytes!("../../res/ethereum/eip150_test.json")) } + +/// Create a new Foundation Homestead-EIP161-era chain spec as though it never changed from Homestead/Frontier. +pub fn new_eip161_test() -> Spec { load(None, include_bytes!("../../res/ethereum/eip161_test.json")) } + +/// Create a new Foundation Frontier/Homestead/DAO chain spec with transition points at #5 and #8. +pub fn new_transition_test() -> Spec { load(None, include_bytes!("../../res/ethereum/transition_test.json")) } + +/// Create a new Foundation Mainnet chain spec without genesis accounts. +pub fn new_mainnet_like() -> Spec { load(None, include_bytes!("../../res/ethereum/frontier_like_test.json")) } + +/// Create a new Foundation Metropolis era spec. +pub fn new_metropolis_test() -> Spec { load(None, include_bytes!("../../res/ethereum/metropolis_test.json")) } #[cfg(test)] mod tests { @@ -94,7 +100,7 @@ mod tests { #[test] fn ensure_db_good() { - let spec = new_morden(); + let spec = new_morden(&::std::env::temp_dir()); let engine = &spec.engine; let genesis_header = spec.genesis_header(); let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); @@ -109,7 +115,7 @@ mod tests { #[test] fn morden() { - let morden = new_morden(); + let morden = new_morden(&::std::env::temp_dir()); assert_eq!(morden.state_root(), "f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9".into()); let genesis = morden.genesis_block(); @@ -120,7 +126,7 @@ mod tests { #[test] fn frontier() { - let frontier = new_foundation(); + let frontier = new_foundation(&::std::env::temp_dir()); assert_eq!(frontier.state_root(), "d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544".into()); let genesis = frontier.genesis_block(); @@ -128,4 +134,23 @@ mod tests { let _ = frontier.engine; } + + #[test] + fn all_spec_files_valid() { + let tmp = ::std::env::temp_dir(); + new_olympic(&tmp); + new_foundation(&tmp); + new_classic(&tmp); + new_expanse(&tmp); + new_kovan(&tmp); + new_ropsten(&tmp); + new_morden(&tmp); + new_frontier_test(); + new_homestead_test(); + new_eip150_test(); + new_eip161_test(); + new_transition_test(); + new_mainnet_like(); + new_metropolis_test(); + } } diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 90bf34f6b..25d131f77 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -32,7 +32,7 @@ //! use ethcore::miner::{Miner, MinerService}; //! //! fn main() { -//! let miner: Miner = Miner::with_spec(ðereum::new_foundation()); +//! let miner: Miner = Miner::with_spec(ðereum::new_foundation(&env::temp_dir())); //! // get status //! assert_eq!(miner.status().transactions_in_pending_queue, 0); //! diff --git a/ethcore/src/snapshot/tests/proof_of_authority.rs b/ethcore/src/snapshot/tests/proof_of_authority.rs index 4428ea2ad..a62dbaf10 100644 --- a/ethcore/src/snapshot/tests/proof_of_authority.rs +++ b/ethcore/src/snapshot/tests/proof_of_authority.rs @@ -59,7 +59,7 @@ lazy_static! { /// `native_contracts::test_contracts::ValidatorSet` provides a native wrapper for the ABi. fn spec_fixed_to_contract() -> Spec { let data = include_bytes!("test_validator_contract.json"); - Spec::load(&data[..]).unwrap() + Spec::load(&::std::env::temp_dir(), &data[..]).unwrap() } // creates an account provider, filling it with accounts from all the given diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index c34652792..3ad23505b 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -158,7 +158,7 @@ pub struct Spec { genesis_state: PodState, } -fn load_from(s: ethjson::spec::Spec) -> Result { +fn load_from>(cache_dir: T, s: ethjson::spec::Spec) -> Result { let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), From::from(p.1))).collect(); let g = Genesis::from(s.genesis); let GenericSeal(seal_rlp) = g.seal.into(); @@ -166,7 +166,7 @@ fn load_from(s: ethjson::spec::Spec) -> Result { let mut s = Spec { name: s.name.clone().into(), - engine: Spec::engine(s.engine, params, builtins), + engine: Spec::engine(cache_dir, s.engine, params, builtins), data_dir: s.data_dir.unwrap_or(s.name).into(), nodes: s.nodes.unwrap_or_else(Vec::new), parent_hash: g.parent_hash, @@ -195,18 +195,26 @@ fn load_from(s: ethjson::spec::Spec) -> Result { macro_rules! load_bundled { ($e:expr) => { - Spec::load(include_bytes!(concat!("../../res/", $e, ".json")) as &[u8]).expect(concat!("Chain spec ", $e, " is invalid.")) + Spec::load( + &::std::env::temp_dir(), + include_bytes!(concat!("../../res/", $e, ".json")) as &[u8] + ).expect(concat!("Chain spec ", $e, " is invalid.")) }; } impl Spec { /// 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(engine_spec: ethjson::spec::Engine, params: CommonParams, builtins: BTreeMap) -> Arc { + fn engine>( + cache_dir: T, + engine_spec: ethjson::spec::Engine, + params: CommonParams, + builtins: BTreeMap, + ) -> Arc { match engine_spec { ethjson::spec::Engine::Null => Arc::new(NullEngine::new(params, builtins)), ethjson::spec::Engine::InstantSeal(instant) => Arc::new(InstantSeal::new(params, instant.params.registrar.map_or_else(Address::new, Into::into), builtins)), - ethjson::spec::Engine::Ethash(ethash) => Arc::new(ethereum::Ethash::new(params, From::from(ethash.params), builtins)), + ethjson::spec::Engine::Ethash(ethash) => Arc::new(ethereum::Ethash::new(cache_dir, params, From::from(ethash.params), builtins)), ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(params, From::from(basic_authority.params), builtins)), ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(params, From::from(authority_round.params), builtins).expect("Failed to start AuthorityRound consensus engine."), ethjson::spec::Engine::Tendermint(tendermint) => Tendermint::new(params, From::from(tendermint.params), builtins).expect("Failed to start the Tendermint consensus engine."), @@ -397,13 +405,13 @@ impl Spec { /// Loads spec from json file. Provide factories for executing contracts and ensuring /// storage goes to the right place. - pub fn load(reader: R) -> Result where R: Read { + pub fn load, R>(cache_dir: T, reader: R) -> Result where R: Read { fn fmt(f: F) -> String { format!("Spec json is invalid: {}", f) } ethjson::spec::Spec::load(reader).map_err(fmt) - .and_then(|x| load_from(x).map_err(fmt)) + .and_then(|x| load_from(cache_dir, x).map_err(fmt)) } /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus. @@ -453,7 +461,7 @@ mod tests { // https://github.com/paritytech/parity/issues/1840 #[test] fn test_load_empty() { - assert!(Spec::load(&[] as &[u8]).is_err()); + assert!(Spec::load(::std::env::temp_dir(), &[] as &[u8]).is_err()); } #[test] diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 6aef92ec5..3b32f9094 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -52,7 +52,7 @@ fn imports_from_empty() { #[test] fn should_return_registrar() { let dir = RandomTempPath::new(); - let spec = ethereum::new_morden(); + let spec = ethereum::new_morden(&dir); let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); let client_db = Arc::new(Database::open(&db_config, dir.as_path().to_str().unwrap()).unwrap()); diff --git a/parity/account.rs b/parity/account.rs index 3b2fd3c2d..ed4f9b40a 100644 --- a/parity/account.rs +++ b/parity/account.rs @@ -71,7 +71,7 @@ pub fn execute(cmd: AccountCmd) -> Result { } fn keys_dir(path: String, spec: SpecType) -> Result { - let spec = spec.spec()?; + let spec = spec.spec(&::std::env::temp_dir())?; let mut path = PathBuf::from(&path); path.push(spec.data_dir); RootDiskDirectory::create(path).map_err(|e| format!("Could not open keys directory: {}", e)) diff --git a/parity/blockchain.rs b/parity/blockchain.rs index 26de04f95..e31f89e45 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -148,7 +148,7 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { let timer = Instant::now(); // load spec file - let spec = cmd.spec.spec()?; + let spec = cmd.spec.spec(&cmd.dirs.cache)?; // load genesis hash let genesis_hash = spec.genesis_header().hash(); @@ -320,7 +320,7 @@ fn start_client( ) -> Result { // load spec file - let spec = spec.spec()?; + let spec = spec.spec(&dirs.cache)?; // load genesis hash let genesis_hash = spec.genesis_header().hash(); @@ -517,7 +517,7 @@ fn execute_export_state(cmd: ExportState) -> Result<(), String> { } pub fn kill_db(cmd: KillBlockchain) -> Result<(), String> { - let spec = cmd.spec.spec()?; + let spec = cmd.spec.spec(&cmd.dirs.cache)?; let genesis_hash = spec.genesis_header().hash(); let db_dirs = cmd.dirs.database(genesis_hash, None, spec.data_dir); let user_defaults_path = db_dirs.user_defaults_path(); diff --git a/parity/configuration.rs b/parity/configuration.rs index 81117db05..b121e2e68 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -34,7 +34,7 @@ use rpc::{IpcConfiguration, HttpConfiguration, WsConfiguration, UiConfiguration} use rpc_apis::ApiSet; use parity_rpc::NetworkSettings; use cache::CacheConfig; -use helpers::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_price, replace_home, replace_home_for_db, +use helpers::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_price, replace_home, replace_home_and_local, geth_ipc_path, parity_ipc_path, to_bootnodes, to_addresses, to_address, to_gas_limit, to_queue_strategy}; use params::{SpecType, ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras, Pruning, Switch}; use ethcore_logger::Config as LogConfig; @@ -893,14 +893,20 @@ impl Configuration { let local_path = default_local_path(); let base_path = self.args.flag_base_path.as_ref().or_else(|| self.args.flag_datadir.as_ref()).map_or_else(|| default_data_path(), |s| s.clone()); let data_path = replace_home("", &base_path); - let base_db_path = if self.args.flag_base_path.is_some() && self.args.flag_db_path.is_none() { - // If base_path is set and db_path is not we default to base path subdir instead of LOCAL. + let is_using_base_path = self.args.flag_base_path.is_some(); + // If base_path is set and db_path is not we default to base path subdir instead of LOCAL. + let base_db_path = if is_using_base_path && self.args.flag_db_path.is_none() { "$BASE/chains" } else { self.args.flag_db_path.as_ref().map_or(dir::CHAINS_PATH, |s| &s) }; + let cache_path = if is_using_base_path { + "$BASE/cache".into() + } else { + replace_home_and_local(&data_path, &local_path, &dir::CACHE_PATH) + }; - let db_path = replace_home_for_db(&data_path, &local_path, &base_db_path); + let db_path = replace_home_and_local(&data_path, &local_path, &base_db_path); let keys_path = replace_home(&data_path, &self.args.flag_keys_path); let dapps_path = replace_home(&data_path, &self.args.flag_dapps_path); let secretstore_path = replace_home(&data_path, &self.args.flag_secretstore_path); @@ -924,6 +930,7 @@ impl Configuration { Directories { keys: keys_path, base: data_path, + cache: cache_path, db: db_path, dapps: dapps_path, signer: ui_path, diff --git a/parity/dir.rs b/parity/dir.rs index 434254774..d254886b9 100644 --- a/parity/dir.rs +++ b/parity/dir.rs @@ -18,7 +18,7 @@ use std::fs; use std::path::{PathBuf, Path}; use util::{H64, H256}; use util::journaldb::Algorithm; -use helpers::{replace_home, replace_home_for_db}; +use helpers::{replace_home, replace_home_and_local}; use app_dirs::{AppInfo, get_app_root, AppDataType}; #[cfg(target_os = "macos")] const AUTHOR: &'static str = "Parity"; @@ -34,6 +34,9 @@ use app_dirs::{AppInfo, get_app_root, AppDataType}; #[cfg(target_os = "windows")] pub const CHAINS_PATH: &'static str = "$LOCAL/chains"; #[cfg(not(target_os = "windows"))] pub const CHAINS_PATH: &'static str = "$BASE/chains"; +#[cfg(target_os = "windows")] pub const CACHE_PATH: &'static str = "$LOCAL/cache"; +#[cfg(not(target_os = "windows"))] pub const CACHE_PATH: &'static str = "$BASE/cache"; + // this const is irrelevent cause we do have migrations now, // but we still use it for backwards compatibility const LEGACY_CLIENT_DB_VER_STR: &'static str = "5.3"; @@ -42,6 +45,7 @@ const LEGACY_CLIENT_DB_VER_STR: &'static str = "5.3"; pub struct Directories { pub base: String, pub db: String, + pub cache: String, pub keys: String, pub signer: String, pub dapps: String, @@ -54,7 +58,8 @@ impl Default for Directories { let local_dir = default_local_path(); Directories { base: replace_home(&data_dir, "$BASE"), - db: replace_home_for_db(&data_dir, &local_dir, CHAINS_PATH), + db: replace_home_and_local(&data_dir, &local_dir, CHAINS_PATH), + cache: replace_home_and_local(&data_dir, &local_dir, CACHE_PATH), keys: replace_home(&data_dir, "$BASE/keys"), signer: replace_home(&data_dir, "$BASE/signer"), dapps: replace_home(&data_dir, "$BASE/dapps"), @@ -67,6 +72,7 @@ impl Directories { pub fn create_dirs(&self, dapps_enabled: bool, signer_enabled: bool, secretstore_enabled: bool) -> Result<(), String> { fs::create_dir_all(&self.base).map_err(|e| e.to_string())?; fs::create_dir_all(&self.db).map_err(|e| e.to_string())?; + fs::create_dir_all(&self.cache).map_err(|e| e.to_string())?; fs::create_dir_all(&self.keys).map_err(|e| e.to_string())?; if signer_enabled { fs::create_dir_all(&self.signer).map_err(|e| e.to_string())?; @@ -231,7 +237,7 @@ pub fn default_hypervisor_path() -> String { #[cfg(test)] mod tests { use super::Directories; - use helpers::{replace_home, replace_home_for_db}; + use helpers::{replace_home, replace_home_and_local}; #[test] fn test_default_directories() { @@ -239,10 +245,14 @@ mod tests { let local_dir = super::default_local_path(); let expected = Directories { base: replace_home(&data_dir, "$BASE"), - db: replace_home_for_db(&data_dir, &local_dir, + db: replace_home_and_local(&data_dir, &local_dir, if cfg!(target_os = "windows") { "$LOCAL/chains" } else { "$BASE/chains" } ), + cache: replace_home_and_local(&data_dir, &local_dir, + if cfg!(target_os = "windows") { "$LOCAL/cache" } + else { "$BASE/cache" } + ), keys: replace_home(&data_dir, "$BASE/keys"), signer: replace_home(&data_dir, "$BASE/signer"), dapps: replace_home(&data_dir, "$BASE/dapps"), diff --git a/parity/helpers.rs b/parity/helpers.rs index 31129f18b..7d28f44fd 100644 --- a/parity/helpers.rs +++ b/parity/helpers.rs @@ -140,7 +140,7 @@ pub fn replace_home(base: &str, arg: &str) -> String { r.replace("/", &::std::path::MAIN_SEPARATOR.to_string()) } -pub fn replace_home_for_db(base: &str, local: &str, arg: &str) -> String { +pub fn replace_home_and_local(base: &str, local: &str, arg: &str) -> String { let r = replace_home(base, arg); r.replace("$LOCAL", local) } diff --git a/parity/params.rs b/parity/params.rs index 20a24ee1c..507d1a9cb 100644 --- a/parity/params.rs +++ b/parity/params.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::{str, fs, fmt}; +use std::{str, fs, fmt, path}; use std::time::Duration; use util::{Address, U256, version_data}; use util::journaldb::Algorithm; @@ -79,19 +79,20 @@ impl fmt::Display for SpecType { } impl SpecType { - pub fn spec(&self) -> Result { + pub fn spec>(&self, cache_dir: T) -> Result { + let cache_dir = cache_dir.as_ref(); match *self { - SpecType::Foundation => Ok(ethereum::new_foundation()), - SpecType::Morden => Ok(ethereum::new_morden()), - SpecType::Ropsten => Ok(ethereum::new_ropsten()), - SpecType::Olympic => Ok(ethereum::new_olympic()), - SpecType::Classic => Ok(ethereum::new_classic()), - SpecType::Expanse => Ok(ethereum::new_expanse()), - SpecType::Kovan => Ok(ethereum::new_kovan()), + SpecType::Foundation => Ok(ethereum::new_foundation(cache_dir)), + SpecType::Morden => Ok(ethereum::new_morden(cache_dir)), + SpecType::Ropsten => Ok(ethereum::new_ropsten(cache_dir)), + SpecType::Olympic => Ok(ethereum::new_olympic(cache_dir)), + SpecType::Classic => Ok(ethereum::new_classic(cache_dir)), + SpecType::Expanse => Ok(ethereum::new_expanse(cache_dir)), + SpecType::Kovan => Ok(ethereum::new_kovan(cache_dir)), SpecType::Dev => Ok(Spec::new_instant()), SpecType::Custom(ref filename) => { - let file = fs::File::open(filename).map_err(|_| "Could not load specification file.")?; - Spec::load(file) + let file = fs::File::open(filename).map_err(|e| format!("Could not load specification file at {}: {}", filename, e))?; + Spec::load(cache_dir, file) } } } diff --git a/parity/run.rs b/parity/run.rs index bcb18aeeb..cc2547c3f 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -168,7 +168,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc) -> use util::RwLock; // load spec - let spec = cmd.spec.spec()?; + let spec = cmd.spec.spec(&cmd.dirs.cache)?; // load genesis hash let genesis_hash = spec.genesis_header().hash(); @@ -352,7 +352,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R } // load spec - let spec = cmd.spec.spec()?; + let spec = cmd.spec.spec(&cmd.dirs.cache)?; // load genesis hash let genesis_hash = spec.genesis_header().hash(); diff --git a/parity/snapshot.rs b/parity/snapshot.rs index 0c4569901..dc786a2ea 100644 --- a/parity/snapshot.rs +++ b/parity/snapshot.rs @@ -133,7 +133,7 @@ impl SnapshotCommand { // shared portion of snapshot commands: start the client service fn start_service(self) -> Result { // load spec file - let spec = self.spec.spec()?; + let spec = self.spec.spec(&self.dirs.cache)?; // load genesis hash let genesis_hash = spec.genesis_header().hash(); diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 5f70271ee..8d2ca7b88 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -15,6 +15,7 @@ // along with Parity. If not, see . //! rpc integration tests. +use std::env; use std::sync::Arc; use std::time::Duration; @@ -318,7 +319,7 @@ const POSITIVE_NONCE_SPEC: &'static [u8] = br#"{ #[test] fn eth_transaction_count() { let secret = "8a283037bb19c4fed7b1c569e40c7dcff366165eb869110a1b11532963eb9cb2".parse().unwrap(); - let tester = EthTester::from_spec(Spec::load(TRANSACTION_COUNT_SPEC).expect("invalid chain spec")); + let tester = EthTester::from_spec(Spec::load(&env::temp_dir(), TRANSACTION_COUNT_SPEC).expect("invalid chain spec")); let address = tester.accounts.insert_account(secret, "").unwrap(); tester.accounts.unlock_account_permanently(address, "".into()).unwrap(); @@ -444,7 +445,7 @@ fn verify_transaction_counts(name: String, chain: BlockChain) { #[test] fn starting_nonce_test() { - let tester = EthTester::from_spec(Spec::load(POSITIVE_NONCE_SPEC).expect("invalid chain spec")); + let tester = EthTester::from_spec(Spec::load(&env::temp_dir(), POSITIVE_NONCE_SPEC).expect("invalid chain spec")); let address = Address::from(10); let sample = tester.handler.handle_request_sync(&(r#"