From 2251c469b8df9a7a4f5369b2ece2fe65936e5d2f Mon Sep 17 00:00:00 2001 From: debris Date: Thu, 17 Dec 2015 01:54:24 +0100 Subject: [PATCH] extras refactored --- src/block.rs | 38 +++++++++++++- src/blockchain.rs | 126 +++++++++++++++++++--------------------------- src/extras.rs | 98 +++++++++++++++++++++++++++++------- 3 files changed, 167 insertions(+), 95 deletions(-) diff --git a/src/block.rs b/src/block.rs index 30768ed0e..3aa498ff7 100644 --- a/src/block.rs +++ b/src/block.rs @@ -5,6 +5,7 @@ use util::sha3::*; use blockheader::*; use state::*; use transaction::*; +use extras::*; /// view onto block rlp pub struct BlockView<'a> { @@ -12,40 +13,73 @@ pub struct BlockView<'a> { } impl<'a> BlockView<'a> { + /// Creates new view onto block from raw bytes pub fn new(bytes: &'a [u8]) -> BlockView<'a> { BlockView { rlp: Rlp::new(bytes) } } + /// Creates new view onto block from rlp pub fn new_from_rlp(rlp: Rlp<'a>) -> BlockView<'a> { BlockView { rlp: rlp } } - pub fn rlp(&self) -> &Rlp<'a> { &self.rlp } - pub fn header(&self) -> Header { self.rlp.val_at(0) } + /// Return reference to underlaying rlp + pub fn rlp(&self) -> &Rlp<'a> { + &self.rlp + } + /// Create new Header object from header rlp + pub fn header(&self) -> Header { + self.rlp.val_at(0) + } + + /// Create new header view obto block head rlp pub fn header_view(&self) -> HeaderView<'a> { HeaderView::new_from_rlp(self.rlp.at(0)) } + /// Return List of transactions in given block pub fn transactions(&self) -> Vec { self.rlp.val_at(1) } + /// Return transaction hashes pub fn transaction_hashes(&self) -> Vec { self.rlp.at(1).iter().map(|rlp| rlp.raw().sha3()).collect() } + /// Return list of uncles of given block pub fn uncles(&self) -> Vec
{ self.rlp.val_at(2) } + /// Return list of uncle hashes of given block pub fn uncle_hashes(&self) -> Vec { self.rlp.at(2).iter().map(|rlp| rlp.raw().sha3()).collect() } + + /// Return BlockDetaile object of given block + /// note* children is always an empty vector, + /// cause we can't deducate them from rlp. + pub fn block_details(&self) -> BlockDetails { + let header = self.header_view(); + BlockDetails { + number: header.number(), + total_difficulty: header.difficulty(), + parent: header.parent_hash(), + children: vec![] + } + } +} + +impl<'a> Hashable for BlockView<'a> { + fn sha3(&self) -> H256 { + self.header_view().sha3() + } } /// Active model of a block within the blockchain diff --git a/src/blockchain.rs b/src/blockchain.rs index 6a48e12af..4a498ec69 100644 --- a/src/blockchain.rs +++ b/src/blockchain.rs @@ -8,15 +8,11 @@ use util::hash::*; use util::uint::*; use util::rlp::*; use util::hashdb::*; -use util::overlaydb::*; use util::sha3::*; use util::bytes::*; use util::squeeze::*; use blockheader::*; use block::*; -use verifiedblock::*; -use importroute::*; -use account::*; use genesis::*; use extras::*; use transaction::*; @@ -31,24 +27,17 @@ pub struct CacheSize { } pub struct BlockChain { - // rlp list of 3 - genesis_block: Bytes, - // genesis block header - _genesis_header: Bytes, - genesis_hash: H256, - _genesis_state: HashMap, - last_block_number: Cell, // block cache blocks: RefCell>, // extra caches - block_details: Extras, - block_hashes: Extras, - transaction_addresses: Extras, - block_logs: Extras, - blocks_blooms: Extras, + block_details: RefCell>, + block_hashes: RefCell>, + transaction_addresses: RefCell>, + block_logs: RefCell>, + blocks_blooms: RefCell>, extras_db: DB, blocks_db: DB @@ -74,24 +63,15 @@ impl BlockChain { /// let genesis = Genesis::new_frontier(); /// 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.block_hash(&U256::from(0u8)).unwrap()); + /// assert_eq!(bc.genesis_hash(), H256::from_str(genesis_hash).unwrap()); + /// assert!(bc.is_known(&bc.genesis_hash())); + /// assert_eq!(bc.genesis_hash(), bc.block_hash(&U256::from(0u8)).unwrap()); /// } /// ``` pub fn new(genesis: Genesis, path: &Path) -> BlockChain { - let (genesis_block, genesis_state) = genesis.drain(); - - let genesis_header = BlockView::new(&genesis_block).header_view().rlp().raw().to_vec(); - let genesis_hash = HeaderView::new(&genesis_header).sha3(); - - let genesis_details = BlockDetails { - number: U256::from(0u64), - total_difficulty: HeaderView::new(&genesis_header).difficulty(), - parent: H256::new(), - children: vec![] - }; + let (genesis_block, _genesis_state) = genesis.drain(); + // open dbs let mut extras_path = path.to_path_buf(); extras_path.push("extras"); let extras_db = DB::open_default(extras_path.to_str().unwrap()).unwrap(); @@ -100,34 +80,41 @@ impl BlockChain { blocks_path.push("blocks"); let blocks_db = DB::open_default(blocks_path.to_str().unwrap()).unwrap(); - { - let batch = WriteBatch::new(); - batch.put(&genesis_hash.to_extras_slice(ExtrasIndex::BlockDetails), &encode(&genesis_details)).unwrap(); - batch.put(&U256::from(0u8).to_extras_slice(ExtrasIndex::BlockHash), &encode(&genesis_hash)).unwrap(); - extras_db.write(batch).unwrap(); - - blocks_db.put(&genesis_hash, &genesis_block).unwrap(); - } - - BlockChain { - genesis_block: genesis_block, - _genesis_header: genesis_header, - genesis_hash: genesis_hash, - _genesis_state: genesis_state, + let bc = BlockChain { last_block_number: Cell::new(U256::from(0u8)), blocks: RefCell::new(HashMap::new()), - block_details: Extras::new(ExtrasIndex::BlockDetails), - block_hashes: Extras::new(ExtrasIndex::BlockHash), - transaction_addresses: Extras::new(ExtrasIndex::TransactionAddress), - block_logs: Extras::new(ExtrasIndex::BlockLogBlooms), - blocks_blooms: Extras::new(ExtrasIndex::BlocksBlooms), + block_details: RefCell::new(HashMap::new()), + block_hashes: RefCell::new(HashMap::new()), + transaction_addresses: RefCell::new(HashMap::new()), + block_logs: RefCell::new(HashMap::new()), + blocks_blooms: RefCell::new(HashMap::new()), extras_db: extras_db, blocks_db: blocks_db - } + }; + + bc.insert_block(&genesis_block); + bc } - pub fn import_block(&self, _block: &[u8], _db: &OverlayDB) -> ImportRoute { - unimplemented!(); + /// Inserts the block into backing cache database. + /// Expects the block to be valid and already verified. + /// If the block is already known, does nothing. + pub fn insert_block(&self, bytes: &[u8]) { + let block = BlockView::new(bytes); + let header = block.header_view(); + + if self.is_known(&header.sha3()) { + return; + } + + let hash = block.sha3(); + + self.blocks_db.put(&hash, &bytes).unwrap(); + + let batch = WriteBatch::new(); + batch.put_extras(&hash, &block.block_details()); + batch.put_extras(&header.number(), &hash); + self.extras_db.write(batch).unwrap(); } /// Returns true if the given block is known @@ -143,8 +130,8 @@ impl BlockChain { } /// Returns reference to genesis hash - pub fn genesis_hash(&self) -> &H256 { - &self.genesis_hash + pub fn genesis_hash(&self) -> H256 { + self.block_hash(&U256::from(0u8)).expect("Genesis hash should always exist") } /// Get the partial-header of a block @@ -224,8 +211,8 @@ impl BlockChain { } } - fn query_extras(&self, hash: &K, cache: &Extras) -> Option where - T: Clone + Decodable, + fn query_extras(&self, hash: &K, cache: &RefCell>) -> Option where + T: Clone + Decodable + ExtrasIndexable, K: ExtrasSliceConvertable + Eq + Hash + Clone { { let read = cache.borrow(); @@ -235,22 +222,16 @@ impl BlockChain { } } - 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.borrow_mut(); - write.insert(hash.clone(), t.clone()); - Some(t) - }, - None => None - } + self.extras_db.get_extras(hash).map(| t: T | { + let mut write = cache.borrow_mut(); + write.insert(hash.clone(), t.clone()); + t + }) } - fn query_extras_exist(&self, hash: &K, cache: &Extras) -> bool where - K: ExtrasSliceConvertable + Eq + Hash + Clone { + fn query_extras_exist(&self, hash: &K, cache: &RefCell>) -> bool where + K: ExtrasSliceConvertable + Eq + Hash + Clone, + T: ExtrasIndexable { { let read = cache.borrow(); match read.get(hash) { @@ -259,10 +240,7 @@ impl BlockChain { } } - let opt = self.extras_db.get(&hash.to_extras_slice(cache.index())) - .expect("Low level database error. Some issue with disk?"); - - opt.is_some() + self.extras_db.extras_exists::<_, T>(hash) } /// Get current cache size diff --git a/src/extras.rs b/src/extras.rs index d0cfffd89..fbdfb2ae8 100644 --- a/src/extras.rs +++ b/src/extras.rs @@ -1,13 +1,10 @@ -use std::collections::HashMap; -use std::cell::RefCell; -use std::ops::Deref; -use std::hash::Hash; use heapsize::HeapSizeOf; +use rocksdb::{DB, Writable}; use util::uint::*; use util::hash::*; use util::rlp::*; -/// workaround for lack of integer templates in Rust +/// Represents index of extra data in database #[derive(Copy, Clone)] pub enum ExtrasIndex { BlockDetails = 0, @@ -17,26 +14,51 @@ pub enum ExtrasIndex { BlocksBlooms = 4 } -/// rw locked extra data with slice suffix -// consifer if arc needed here, since blockchain itself will be wrapped -pub struct Extras(RefCell>, ExtrasIndex) where K: Eq + Hash; - -impl Extras where K: Eq + Hash { - pub fn new(i: ExtrasIndex) -> Extras { - Extras(RefCell::new(HashMap::new()), i) - } - - pub fn index(&self) -> ExtrasIndex { self.1 } +/// trait used to write Extras data to db +pub trait ExtrasWritable { + fn put_extras(&self, hash: &K, value: &T) where + T: ExtrasIndexable + Encodable, + K: ExtrasSliceConvertable; } -impl Deref for Extras where K : Eq + Hash { - type Target = RefCell>; +/// trait used to read Extras data from db +pub trait ExtrasReadable { + fn get_extras(&self, hash: &K) -> Option where + T: ExtrasIndexable + Decodable, + K: ExtrasSliceConvertable; - fn deref(&self) -> &Self::Target { - &self.0 + fn extras_exists(&self, hash: &K) -> bool where + T: ExtrasIndexable, + K: ExtrasSliceConvertable; +} + +impl ExtrasWritable for W where W: Writable { + fn put_extras(&self, hash: &K, value: &T) where + T: ExtrasIndexable + Encodable, + K: ExtrasSliceConvertable { + + self.put(&hash.to_extras_slice(T::extras_index()), &encode(value)).unwrap() } } +impl ExtrasReadable for DB { + fn get_extras(&self, hash: &K) -> Option where + T: ExtrasIndexable + Decodable, + K: ExtrasSliceConvertable { + + self.get(&hash.to_extras_slice(T::extras_index())).unwrap() + .map(|v| decode(&v)) + } + + fn extras_exists(&self, hash: &K) -> bool where + T: ExtrasIndexable, + K: ExtrasSliceConvertable { + + self.get(&hash.to_extras_slice(T::extras_index())).unwrap().is_some() + } +} + +/// Implementations should convert arbitrary type to database key slice pub trait ExtrasSliceConvertable { fn to_extras_slice(&self, i: ExtrasIndex) -> H264; } @@ -55,7 +77,18 @@ impl ExtrasSliceConvertable for U256 { } } +/// Types implementing this trait can be indexed in extras database +pub trait ExtrasIndexable { + fn extras_index() -> ExtrasIndex; +} +impl ExtrasIndexable for H256 { + fn extras_index() -> ExtrasIndex { + ExtrasIndex::BlockHash + } +} + +/// Familial details concerning a block #[derive(Clone)] pub struct BlockDetails { pub number: U256, @@ -64,6 +97,12 @@ pub struct BlockDetails { pub children: Vec } +impl ExtrasIndexable for BlockDetails { + fn extras_index() -> ExtrasIndex { + ExtrasIndex::BlockDetails + } +} + impl HeapSizeOf for BlockDetails { fn heap_size_of_children(&self) -> usize { self.children.heap_size_of_children() @@ -94,11 +133,18 @@ impl Encodable for BlockDetails { } } +/// Log blooms of certain block #[derive(Clone)] pub struct BlockLogBlooms { pub blooms: Vec } +impl ExtrasIndexable for BlockLogBlooms { + fn extras_index() -> ExtrasIndex { + ExtrasIndex::BlockLogBlooms + } +} + impl HeapSizeOf for BlockLogBlooms { fn heap_size_of_children(&self) -> usize { self.blooms.heap_size_of_children() @@ -121,10 +167,17 @@ impl Encodable for BlockLogBlooms { } } +/// Neighboring log blooms on certain level pub struct BlocksBlooms { pub blooms: [H2048; 16] } +impl ExtrasIndexable for BlocksBlooms { + fn extras_index() -> ExtrasIndex { + ExtrasIndex::BlocksBlooms + } +} + impl HeapSizeOf for BlocksBlooms { fn heap_size_of_children(&self) -> usize { 0 } } @@ -160,12 +213,19 @@ impl Encodable for BlocksBlooms { } } +/// Represents address of certain transaction within block #[derive(Clone)] pub struct TransactionAddress { pub block_hash: H256, pub index: u64 } +impl ExtrasIndexable for TransactionAddress { + fn extras_index() -> ExtrasIndex { + ExtrasIndex::TransactionAddress + } +} + impl HeapSizeOf for TransactionAddress { fn heap_size_of_children(&self) -> usize { 0 } }