diff --git a/Cargo.toml b/Cargo.toml index 29dfa6942..8450bf1b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,11 @@ authors = ["Ethcore "] log = "0.3" env_logger = "0.3" ethcore-util = "0.1.0" -evmjit = { path = "rust-evmjit", optional = true } rustc-serialize = "0.3" flate2 = "0.2" +rocksdb = "0.2.1" + +evmjit = { path = "rust-evmjit", optional = true } [features] jit = ["evmjit"] diff --git a/src/blockchain.rs b/src/blockchain.rs index 9e6d02089..a81f3d857 100644 --- a/src/blockchain.rs +++ b/src/blockchain.rs @@ -1,5 +1,8 @@ use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, RwLock}; +use std::path::Path; +use std::hash::Hash; +use rocksdb::{DB, WriteBatch, Writable}; use util::hash::*; use util::uint::*; use util::rlp::*; @@ -23,10 +26,12 @@ pub struct BlockChain { genesis_hash: H256, genesis_state: HashMap, + last_block_number: U256, + // extras - // TODO: is arc really needed here, since blockchain itself will be wrapped - // into `Arc`? - block_details: Arc>> + blocks_details: Extras, + blocks_hashes: Extras, + extras_db: DB } impl BlockChain { @@ -35,19 +40,26 @@ impl BlockChain { /// ```rust /// extern crate ethcore_util as util; /// extern crate ethcore; + /// use std::env; /// use std::str::FromStr; /// use ethcore::genesis::*; /// use ethcore::blockchain::*; /// use util::hash::*; + /// use util::uint::*; /// /// fn main() { + /// let mut dir = env::temp_dir(); + /// dir.push(H32::random().hex()); + /// /// let genesis = Genesis::new_frontier(); - /// let bc = BlockChain::new(genesis); + /// let bc = BlockChain::new(genesis, &dir); /// let genesis_hash = "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"; /// assert_eq!(bc.genesis_hash(), &H256::from_str(genesis_hash).unwrap()); + /// assert!(bc.is_known(bc.genesis_hash())); + /// assert_eq!(bc.genesis_hash(), &bc.number_hash(&U256::from(0u8)).unwrap()); /// } /// ``` - pub fn new(genesis: Genesis) -> BlockChain { + pub fn new(genesis: Genesis, path: &Path) -> BlockChain { let (genesis_block, genesis_state) = genesis.drain(); let genesis_header = Rlp::new(&genesis_block).at(0).raw().to_vec(); @@ -60,19 +72,25 @@ impl BlockChain { children: vec![] }; - // TODO: also insert into backing db - let mut block_details = HashMap::new(); - block_details.insert(genesis_hash.clone(), genesis_details); + let db = DB::open_default(path.to_str().unwrap()).unwrap(); - let bc = BlockChain { + { + let mut batch = WriteBatch::new(); + batch.put(&genesis_hash.to_extras_slice(ExtrasIndex::BlockDetails), &encode(&genesis_details)); + batch.put(&U256::from(0u8).to_extras_slice(ExtrasIndex::BlockHash), &encode(&genesis_hash)); + db.write(batch); + } + + BlockChain { genesis_block: genesis_block, genesis_header: genesis_header, genesis_hash: genesis_hash, genesis_state: genesis_state, - block_details: Arc::new(Mutex::new(block_details)) - }; - - bc + last_block_number: U256::from(0u8), + blocks_details: Extras::new(ExtrasIndex::BlockDetails), + blocks_hashes: Extras::new(ExtrasIndex::BlockHash), + extras_db: db + } } pub fn genesis_hash(&self) -> &H256 { @@ -114,11 +132,62 @@ impl BlockChain { unimplemented!(); } + /// Get the hash of given block's number + pub fn number_hash(&self, hash: &U256) -> Option { + self.query_extras(hash, &self.blocks_hashes) + } + /// Returns true if the given block is known /// (though not necessarily a part of the canon chain). pub fn is_known(&self, hash: &H256) -> bool { - return false; - //unimplemented!() - // TODO: check is hash exist in hashes + // TODO: first do lookup in blocks_db for given hash + + // TODO: consider taking into account current block + match self.query_extras(hash, &self.blocks_details) { + None => false, + Some(details) => details.number <= self.last_block_number + } + } + + pub fn query_extras(&self, hash: &K, cache: &Extras) -> Option where + T: Clone + Decodable, + K: ExtrasSliceConvertable + Eq + Hash + Clone { + { + let read = cache.read().unwrap(); + match read.get(hash) { + Some(v) => return Some(v.clone()), + None => () + } + } + + let opt = self.extras_db.get(&hash.to_extras_slice(cache.index())) + .expect("Low level database error. Some issue with disk?"); + + match opt { + Some(b) => { + let t: T = decode(&b); + let mut write = cache.write().unwrap(); + write.insert(hash.clone(), t.clone()); + Some(t) + }, + None => None + } + } + + pub fn query_extras_exist(&self, hash: &K, cache: &Extras) -> bool where + K: ExtrasSliceConvertable + Eq + Hash + Clone { + { + let read = cache.read().unwrap(); + match read.get(hash) { + Some(_) => return true, + None => () + } + } + + let opt = self.extras_db.get(&hash.to_extras_slice(cache.index())) + .expect("Low level database error. Some issue with disk?"); + + opt.is_some() } } + diff --git a/src/extras.rs b/src/extras.rs index d16c1e9c0..d9a0fa08b 100644 --- a/src/extras.rs +++ b/src/extras.rs @@ -1,7 +1,58 @@ +use std::collections::HashMap; +use std::sync::RwLock; +use std::ops::Deref; +use std::hash::Hash; use util::uint::*; use util::hash::*; use util::rlp::*; +/// workaround for lack of integer templates in Rust +#[derive(Copy, Clone)] +pub enum ExtrasIndex { + BlockDetails = 0, + BlockHash = 1, +} + +/// rw locked extra data with slice suffix +// consifer if arc needed here, since blockchain itself will be wrapped +pub struct Extras(RwLock>, ExtrasIndex) where K: Eq + Hash; + +impl Extras where K: Eq + Hash { + pub fn new(i: ExtrasIndex) -> Extras { + Extras(RwLock::new(HashMap::new()), i) + } + + pub fn index(&self) -> ExtrasIndex { self.1 } +} + +impl Deref for Extras where K : Eq + Hash { + type Target = RwLock>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub trait ExtrasSliceConvertable { + fn to_extras_slice(&self, i: ExtrasIndex) -> H264; +} + +impl ExtrasSliceConvertable for H256 { + fn to_extras_slice(&self, i: ExtrasIndex) -> H264 { + let mut slice = H264::from_slice(self); + slice[32] = i as u8; + slice + } +} + +impl ExtrasSliceConvertable for U256 { + fn to_extras_slice(&self, i: ExtrasIndex) -> H264 { + H256::from(self).to_extras_slice(i) + } +} + + +#[derive(Clone)] pub struct BlockDetails { pub number: U256, pub total_difficulty: U256, diff --git a/src/lib.rs b/src/lib.rs index bd6e5c33f..cc1240233 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,6 +73,7 @@ extern crate log; extern crate rustc_serialize; extern crate flate2; +extern crate rocksdb; extern crate env_logger; #[cfg(feature = "jit" )]