diff --git a/ethcore/src/blockchain/best_block.rs b/ethcore/src/blockchain/best_block.rs new file mode 100644 index 000000000..cbb219617 --- /dev/null +++ b/ethcore/src/blockchain/best_block.rs @@ -0,0 +1,30 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity 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 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. If not, see . + +use util::hash::H256; +use util::uint::U256; +use header::BlockNumber; + +/// Best block info. +#[derive(Default)] +pub struct BestBlock { + /// Best block hash. + pub hash: H256, + /// Best block number. + pub number: BlockNumber, + /// Best block total difficulty. + pub total_difficulty: U256 +} diff --git a/ethcore/src/blockchain/block_info.rs b/ethcore/src/blockchain/block_info.rs new file mode 100644 index 000000000..98dac648d --- /dev/null +++ b/ethcore/src/blockchain/block_info.rs @@ -0,0 +1,48 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity 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 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. If not, see . + +use util::hash::H256; +use util::uint::U256; +use header::BlockNumber; + +/// Brief info about inserted block. +pub struct BlockInfo { + /// Block hash. + pub hash: H256, + /// Block number. + pub number: BlockNumber, + /// Total block difficulty. + pub total_difficulty: U256, + /// Block location in blockchain. + pub location: BlockLocation +} + +/// Describes location of newly inserted block. +pub enum BlockLocation { + /// It's part of the canon chain. + CanonChain, + /// It's not a part of the canon chain. + Branch, + /// It's part of the fork which should become canon chain, + /// because it's total difficulty is higher than current + /// canon chain difficulty. + BranchBecomingCanonChain { + /// Hash of the newest common ancestor with old canon chain. + ancestor: H256, + /// Hashes of the blocks between ancestor and this block. + route: Vec + } +} diff --git a/ethcore/src/blockchain.rs b/ethcore/src/blockchain/blockchain.rs similarity index 86% rename from ethcore/src/blockchain.rs rename to ethcore/src/blockchain/blockchain.rs index 30f039600..8aa0fffdc 100644 --- a/ethcore/src/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -23,6 +23,12 @@ use transaction::*; use views::*; use receipt::Receipt; use chainfilter::{ChainFilter, BloomIndex, FilterDataSource}; +use blockchain::block_info::{BlockInfo, BlockLocation}; +use blockchain::best_block::BestBlock; +use blockchain::bloom_indexer::BloomIndexer; +use blockchain::tree_route::TreeRoute; +use blockchain::update::ExtrasUpdate; +use blockchain::CacheSize; const BLOOM_INDEX_SIZE: usize = 16; const BLOOM_LEVELS: u8 = 3; @@ -45,109 +51,6 @@ impl Default for BlockChainConfig { } } -/// Represents a tree route between `from` block and `to` block: -pub struct TreeRoute { - /// A vector of hashes of all blocks, ordered from `from` to `to`. - pub blocks: Vec, - /// Best common ancestor of these blocks. - pub ancestor: H256, - /// An index where best common ancestor would be. - pub index: usize, -} - -/// Represents blockchain's in-memory cache size in bytes. -#[derive(Debug)] -pub struct CacheSize { - /// Blocks cache size. - pub blocks: usize, - /// BlockDetails cache size. - pub block_details: usize, - /// Transaction addresses cache size. - pub transaction_addresses: usize, - /// Logs cache size. - pub block_logs: usize, - /// Blooms cache size. - pub blocks_blooms: usize, - /// Block receipts size. - pub block_receipts: usize, -} - -struct BloomIndexer { - index_size: usize, - levels: u8, -} - -impl BloomIndexer { - fn new(index_size: usize, levels: u8) -> Self { - BloomIndexer { - index_size: index_size, - levels: levels - } - } - - /// Calculates bloom's position in database. - fn location(&self, bloom_index: &BloomIndex) -> BlocksBloomLocation { - use std::{mem, ptr}; - - let hash = unsafe { - let mut hash: H256 = mem::zeroed(); - ptr::copy(&[bloom_index.index / self.index_size] as *const usize as *const u8, hash.as_mut_ptr(), 8); - hash[8] = bloom_index.level; - hash.reverse(); - hash - }; - - BlocksBloomLocation { - hash: hash, - index: bloom_index.index % self.index_size - } - } - - fn index_size(&self) -> usize { - self.index_size - } - - fn levels(&self) -> u8 { - self.levels - } -} - -/// Blockchain update info. -struct ExtrasUpdate { - /// Block hash. - hash: H256, - /// DB update batch. - batch: DBTransaction, - /// Inserted block familial details. - details: BlockDetails, - /// New best block (if it has changed). - new_best: Option, - /// Changed blocks bloom location hashes. - bloom_hashes: HashSet, -} - -impl CacheSize { - /// Total amount used by the cache. - pub fn total(&self) -> usize { self.blocks + self.block_details + self.transaction_addresses + self.block_logs + self.blocks_blooms } -} - -/// Information about best block gathered together -struct BestBlock { - pub hash: H256, - pub number: BlockNumber, - pub total_difficulty: U256 -} - -impl BestBlock { - fn new() -> BestBlock { - BestBlock { - hash: H256::new(), - number: 0, - total_difficulty: U256::from(0) - } - } -} - /// Interface for querying blocks by hash and by number. pub trait BlockProvider { /// Returns true if the given block is known @@ -343,7 +246,7 @@ impl BlockChain { let bc = BlockChain { pref_cache_size: config.pref_cache_size, max_cache_size: config.max_cache_size, - best_block: RwLock::new(BestBlock::new()), + best_block: RwLock::new(BestBlock::default()), blocks: RwLock::new(HashMap::new()), block_details: RwLock::new(HashMap::new()), block_hashes: RwLock::new(HashMap::new()), @@ -444,40 +347,26 @@ impl BlockChain { /// ```json /// { blocks: [B4, B3, A3, A4], ancestor: A2, index: 2 } /// ``` - pub fn tree_route(&self, from: H256, to: H256) -> Option { - let from_details = match self.block_details(&from) { - Some(h) => h, - None => return None, - }; - let to_details = match self.block_details(&to) { - Some(h) => h, - None => return None, - }; - Some(self.tree_route_aux((&from_details, &from), (&to_details, &to))) - } - - /// Similar to `tree_route` function, but can be used to return a route - /// between blocks which may not be in database yet. - fn tree_route_aux(&self, from: (&BlockDetails, &H256), to: (&BlockDetails, &H256)) -> TreeRoute { + pub fn tree_route(&self, from: H256, to: H256) -> TreeRoute { let mut from_branch = vec![]; let mut to_branch = vec![]; - let mut from_details = from.0.clone(); - let mut to_details = to.0.clone(); - let mut current_from = from.1.clone(); - let mut current_to = to.1.clone(); + let mut from_details = self.block_details(&from).expect(&format!("0. Expected to find details for block {:?}", from)); + let mut to_details = self.block_details(&to).expect(&format!("1. Expected to find details for block {:?}", to)); + let mut current_from = from; + let mut current_to = to; // reset from && to to the same level while from_details.number > to_details.number { from_branch.push(current_from); current_from = from_details.parent.clone(); - from_details = self.block_details(&from_details.parent).expect(&format!("1. Expected to find details for block {:?}", from_details.parent)); + from_details = self.block_details(&from_details.parent).expect(&format!("2. Expected to find details for block {:?}", from_details.parent)); } while to_details.number > from_details.number { to_branch.push(current_to); current_to = to_details.parent.clone(); - to_details = self.block_details(&to_details.parent).expect(&format!("2. Expected to find details for block {:?}", to_details.parent)); + to_details = self.block_details(&to_details.parent).expect(&format!("3. Expected to find details for block {:?}", to_details.parent)); } assert_eq!(from_details.number, to_details.number); @@ -486,11 +375,11 @@ impl BlockChain { while current_from != current_to { from_branch.push(current_from); current_from = from_details.parent.clone(); - from_details = self.block_details(&from_details.parent).expect(&format!("3. Expected to find details for block {:?}", from_details.parent)); + from_details = self.block_details(&from_details.parent).expect(&format!("4. Expected to find details for block {:?}", from_details.parent)); to_branch.push(current_to); current_to = to_details.parent.clone(); - to_details = self.block_details(&to_details.parent).expect(&format!("4. Expected to find details for block {:?}", from_details.parent)); + to_details = self.block_details(&to_details.parent).expect(&format!("5. Expected to find details for block {:?}", from_details.parent)); } let index = from_branch.len(); @@ -519,165 +408,237 @@ impl BlockChain { // store block in db self.blocks_db.put(&hash, &bytes).unwrap(); - let update = self.block_to_extras_update(bytes, receipts); - self.apply_update(update); + + let info = self.block_info(bytes); + + self.apply_update(ExtrasUpdate { + block_hashes: self.prepare_block_hashes_update(bytes, &info), + block_details: self.prepare_block_details_update(bytes, &info), + block_receipts: self.prepare_block_receipts_update(receipts, &info), + transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), + blocks_blooms: self.prepare_block_blooms_update(bytes, &info), + info: info + }); } /// Applies extras update. fn apply_update(&self, update: ExtrasUpdate) { + let batch = DBTransaction::new(); + batch.put(b"best", &update.info.hash).unwrap(); + // update best block let mut best_block = self.best_block.write().unwrap(); - if let Some(b) = update.new_best { - *best_block = b; + match update.info.location { + BlockLocation::Branch => (), + _ => { + *best_block = BestBlock { + hash: update.info.hash, + number: update.info.number, + total_difficulty: update.info.total_difficulty + }; + } } - // update details cache - let mut write_details = self.block_details.write().unwrap(); - write_details.remove(&update.details.parent); - write_details.insert(update.hash.clone(), update.details); - self.note_used(CacheID::Block(update.hash)); + let mut write_hashes = self.block_hashes.write().unwrap(); + for (number, hash) in &update.block_hashes { + batch.put_extras(number, hash); + write_hashes.remove(number); + } + + let mut write_details = self.block_details.write().unwrap(); + for (hash, details) in update.block_details.into_iter() { + batch.put_extras(&hash, &details); + write_details.insert(hash, details); + } + + let mut write_receipts = self.block_receipts.write().unwrap(); + for (hash, receipt) in &update.block_receipts { + batch.put_extras(hash, receipt); + write_receipts.remove(hash); + } + + let mut write_txs = self.transaction_addresses.write().unwrap(); + for (hash, tx_address) in &update.transactions_addresses { + batch.put_extras(hash, tx_address); + write_txs.remove(hash); + } - // update blocks blooms cache let mut write_blocks_blooms = self.blocks_blooms.write().unwrap(); - for bloom_hash in &update.bloom_hashes { + for (bloom_hash, blocks_bloom) in &update.blocks_blooms { + batch.put_extras(bloom_hash, blocks_bloom); write_blocks_blooms.remove(bloom_hash); } // update extras database - self.extras_db.write(update.batch).unwrap(); + self.extras_db.write(batch).unwrap(); } - /// Transforms block into WriteBatch that may be written into database - /// Additionally, if it's new best block it returns new best block object. - fn block_to_extras_update(&self, bytes: &[u8], receipts: Vec) -> ExtrasUpdate { - // create views onto rlp - let block = BlockView::new(bytes); + /// Get inserted block info which is critical to preapre extras updates. + fn block_info(&self, block_bytes: &[u8]) -> BlockInfo { + let block = BlockView::new(block_bytes); let header = block.header_view(); - - // prepare variables let hash = block.sha3(); - let mut parent_details = self.block_details(&header.parent_hash()).expect(format!("Invalid parent hash: {:?}", header.parent_hash()).as_ref()); + let number = header.number(); + let parent_hash = header.parent_hash(); + let parent_details = self.block_details(&parent_hash).expect(format!("Invalid parent hash: {:?}", parent_hash).as_ref()); let total_difficulty = parent_details.total_difficulty + header.difficulty(); let is_new_best = total_difficulty > self.best_block_total_difficulty(); + + BlockInfo { + hash: hash, + number: number, + total_difficulty: total_difficulty, + location: if is_new_best { + // on new best block we need to make sure that all ancestors + // are moved to "canon chain" + // find the route between old best block and the new one + let best_hash = self.best_block_hash(); + let route = self.tree_route(best_hash, parent_hash); + + assert_eq!(number, parent_details.number + 1); + + match route.blocks.len() { + 0 => BlockLocation::CanonChain, + _ => BlockLocation::BranchBecomingCanonChain { + ancestor: route.ancestor, + route: route.blocks.into_iter().skip(route.index).collect() + } + } + } else { + BlockLocation::Branch + } + } + } + + /// This function returns modified block hashes. + fn prepare_block_hashes_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap { + let mut block_hashes = HashMap::new(); + let block = BlockView::new(block_bytes); + let header = block.header_view(); + let number = header.number(); + + match info.location { + BlockLocation::Branch => (), + BlockLocation::CanonChain => { + block_hashes.insert(number, info.hash.clone()); + }, + BlockLocation::BranchBecomingCanonChain { ref ancestor, ref route } => { + let ancestor_number = self.block_number(ancestor).unwrap(); + let start_number = ancestor_number + 1; + + for (index, hash) in route.iter().cloned().enumerate() { + block_hashes.insert(start_number + index as BlockNumber, hash); + } + + block_hashes.insert(number, info.hash.clone()); + } + } + + block_hashes + } + + /// This function returns modified block details. + fn prepare_block_details_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap { + let block = BlockView::new(block_bytes); + let header = block.header_view(); let parent_hash = header.parent_hash(); + // update parent + let mut parent_details = self.block_details(&parent_hash).expect(format!("Invalid parent hash: {:?}", parent_hash).as_ref()); + parent_details.children.push(info.hash.clone()); + // create current block details let details = BlockDetails { number: header.number(), - total_difficulty: total_difficulty, + total_difficulty: info.total_difficulty, parent: parent_hash.clone(), children: vec![] }; - // prepare the batch - let batch = DBTransaction::new(); + // write to batch + let mut block_details = HashMap::new(); + block_details.insert(parent_hash, parent_details); + block_details.insert(info.hash.clone(), details); + block_details + } - // insert new block details - batch.put_extras(&hash, &details); + /// This function returns modified block receipts. + fn prepare_block_receipts_update(&self, receipts: Vec, info: &BlockInfo) -> HashMap { + let mut block_receipts = HashMap::new(); + block_receipts.insert(info.hash.clone(), BlockReceipts::new(receipts)); + block_receipts + } - // update parent details - parent_details.children.push(hash.clone()); - batch.put_extras(&parent_hash, &parent_details); + /// This function returns modified transaction addresses. + fn prepare_transaction_addresses_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap { + let block = BlockView::new(block_bytes); + let transaction_hashes = block.transaction_hashes(); - // update transaction addresses - for (i, tx_hash) in block.transaction_hashes().iter().enumerate() { - batch.put_extras(tx_hash, &TransactionAddress { - block_hash: hash.clone(), - index: i - }); - } + transaction_hashes.into_iter() + .enumerate() + .fold(HashMap::new(), |mut acc, (i ,tx_hash)| { + acc.insert(tx_hash, TransactionAddress { + block_hash: info.hash.clone(), + index: i + }); + acc + }) + } - // update block receipts - batch.put_extras(&hash, &BlockReceipts::new(receipts)); + /// This functions returns modified blocks blooms. + /// + /// To accelerate blooms lookups, blomms are stored in multiple + /// layers (BLOOM_LEVELS, currently 3). + /// ChainFilter is responsible for building and rebuilding these layers. + /// It returns them in HashMap, where values are Blooms and + /// keys are BloomIndexes. BloomIndex represents bloom location on one + /// of these layers. + /// + /// To reduce number of queries to databse, block blooms are stored + /// in BlocksBlooms structure which contains info about several + /// (BLOOM_INDEX_SIZE, currently 16) consecutive blocks blooms. + /// + /// Later, BloomIndexer is used to map bloom location on filter layer (BloomIndex) + /// to bloom location in database (BlocksBloomLocation). + /// + fn prepare_block_blooms_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap { + let block = BlockView::new(block_bytes); + let header = block.header_view(); - // if it's not new best block, just return - if !is_new_best { - return ExtrasUpdate { - hash: hash.clone(), - batch: batch, - details: details, - new_best: None, - bloom_hashes: HashSet::new() - }; - } - - // if its new best block we need to make sure that all ancestors - // are moved to "canon chain" - // find the route between old best block and the new one - let best_hash = self.best_block_hash(); - let best_details = self.block_details(&best_hash).expect("best block hash is invalid!"); - let route = self.tree_route_aux((&best_details, &best_hash), (&details, &hash)); - - let modified_blooms; - - match route.blocks.len() { - // its our parent - 1 => { - batch.put_extras(&header.number(), &hash); - - // update block blooms - modified_blooms = ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels()) - .add_bloom(&header.log_bloom(), header.number() as usize); + let modified_blooms = match info.location { + BlockLocation::Branch => HashMap::new(), + BlockLocation::CanonChain => { + ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels()) + .add_bloom(&header.log_bloom(), header.number() as usize) }, - // it is a fork - i if i > 1 => { - let ancestor_number = self.block_number(&route.ancestor).unwrap(); + BlockLocation::BranchBecomingCanonChain { ref ancestor, ref route } => { + let ancestor_number = self.block_number(ancestor).unwrap(); let start_number = ancestor_number + 1; - for (index, hash) in route.blocks.iter().skip(route.index).enumerate() { - batch.put_extras(&(start_number + index as BlockNumber), hash); - } - // get all blocks that are not part of canon chain (TODO: optimize it to one query) - let blooms: Vec = route.blocks.iter() - .skip(route.index) + let mut blooms: Vec = route.iter() .map(|hash| self.block(hash).unwrap()) .map(|bytes| BlockView::new(&bytes).header_view().log_bloom()) .collect(); - // reset blooms chain head - modified_blooms = ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels()) - .reset_chain_head(&blooms, start_number as usize, self.best_block_number() as usize); - }, - // route.blocks.len() could be 0 only if inserted block is best block, - // and this is not possible at this stage - _ => { unreachable!(); } + blooms.push(header.log_bloom()); + + ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels()) + .reset_chain_head(&blooms, start_number as usize, self.best_block_number() as usize) + } }; - let bloom_hashes = modified_blooms.iter() - .map(|(bloom_index, _)| self.bloom_indexer.location(&bloom_index).hash) - .collect(); - - for (bloom_hash, blocks_blooms) in modified_blooms.into_iter() + modified_blooms.into_iter() .fold(HashMap::new(), | mut acc, (bloom_index, bloom) | { { let location = self.bloom_indexer.location(&bloom_index); let mut blocks_blooms = acc .entry(location.hash.clone()) .or_insert_with(|| self.blocks_blooms(&location.hash).unwrap_or_else(BlocksBlooms::new)); - assert_eq!(self.bloom_indexer.index_size, blocks_blooms.blooms.len()); + assert_eq!(self.bloom_indexer.index_size(), blocks_blooms.blooms.len()); blocks_blooms.blooms[location.index] = bloom; } acc - }) { - batch.put_extras(&bloom_hash, &blocks_blooms); - } - - // this is new best block - batch.put(b"best", &hash).unwrap(); - - let best_block = BestBlock { - hash: hash.clone(), - number: header.number(), - total_difficulty: total_difficulty - }; - - ExtrasUpdate { - hash: hash, - batch: batch, - new_best: Some(best_block), - details: details, - bloom_hashes: bloom_hashes - } + }) } /// Get best block hash. @@ -878,52 +839,52 @@ mod tests { assert_eq!(bc.block_hash(3).unwrap(), b3a_hash); // test trie route - let r0_1 = bc.tree_route(genesis_hash.clone(), b1_hash.clone()).unwrap(); + let r0_1 = bc.tree_route(genesis_hash.clone(), b1_hash.clone()); assert_eq!(r0_1.ancestor, genesis_hash); assert_eq!(r0_1.blocks, [b1_hash.clone()]); assert_eq!(r0_1.index, 0); - let r0_2 = bc.tree_route(genesis_hash.clone(), b2_hash.clone()).unwrap(); + let r0_2 = bc.tree_route(genesis_hash.clone(), b2_hash.clone()); assert_eq!(r0_2.ancestor, genesis_hash); assert_eq!(r0_2.blocks, [b1_hash.clone(), b2_hash.clone()]); assert_eq!(r0_2.index, 0); - let r1_3a = bc.tree_route(b1_hash.clone(), b3a_hash.clone()).unwrap(); + let r1_3a = bc.tree_route(b1_hash.clone(), b3a_hash.clone()); assert_eq!(r1_3a.ancestor, b1_hash); assert_eq!(r1_3a.blocks, [b2_hash.clone(), b3a_hash.clone()]); assert_eq!(r1_3a.index, 0); - let r1_3b = bc.tree_route(b1_hash.clone(), b3b_hash.clone()).unwrap(); + let r1_3b = bc.tree_route(b1_hash.clone(), b3b_hash.clone()); assert_eq!(r1_3b.ancestor, b1_hash); assert_eq!(r1_3b.blocks, [b2_hash.clone(), b3b_hash.clone()]); assert_eq!(r1_3b.index, 0); - let r3a_3b = bc.tree_route(b3a_hash.clone(), b3b_hash.clone()).unwrap(); + let r3a_3b = bc.tree_route(b3a_hash.clone(), b3b_hash.clone()); assert_eq!(r3a_3b.ancestor, b2_hash); assert_eq!(r3a_3b.blocks, [b3a_hash.clone(), b3b_hash.clone()]); assert_eq!(r3a_3b.index, 1); - let r1_0 = bc.tree_route(b1_hash.clone(), genesis_hash.clone()).unwrap(); + let r1_0 = bc.tree_route(b1_hash.clone(), genesis_hash.clone()); assert_eq!(r1_0.ancestor, genesis_hash); assert_eq!(r1_0.blocks, [b1_hash.clone()]); assert_eq!(r1_0.index, 1); - let r2_0 = bc.tree_route(b2_hash.clone(), genesis_hash.clone()).unwrap(); + let r2_0 = bc.tree_route(b2_hash.clone(), genesis_hash.clone()); assert_eq!(r2_0.ancestor, genesis_hash); assert_eq!(r2_0.blocks, [b2_hash.clone(), b1_hash.clone()]); assert_eq!(r2_0.index, 2); - let r3a_1 = bc.tree_route(b3a_hash.clone(), b1_hash.clone()).unwrap(); + let r3a_1 = bc.tree_route(b3a_hash.clone(), b1_hash.clone()); assert_eq!(r3a_1.ancestor, b1_hash); assert_eq!(r3a_1.blocks, [b3a_hash.clone(), b2_hash.clone()]); assert_eq!(r3a_1.index, 2); - let r3b_1 = bc.tree_route(b3b_hash.clone(), b1_hash.clone()).unwrap(); + let r3b_1 = bc.tree_route(b3b_hash.clone(), b1_hash.clone()); assert_eq!(r3b_1.ancestor, b1_hash); assert_eq!(r3b_1.blocks, [b3b_hash.clone(), b2_hash.clone()]); assert_eq!(r3b_1.index, 2); - let r3b_3a = bc.tree_route(b3b_hash.clone(), b3a_hash.clone()).unwrap(); + let r3b_3a = bc.tree_route(b3b_hash.clone(), b3a_hash.clone()); assert_eq!(r3b_3a.ancestor, b2_hash); assert_eq!(r3b_3a.blocks, [b3b_hash.clone(), b3a_hash.clone()]); assert_eq!(r3b_3a.index, 1); @@ -1081,30 +1042,5 @@ mod tests { assert_eq!(blocks_ba, vec![3]); } - #[test] - fn test_bloom_indexer() { - use chainfilter::BloomIndex; - use blockchain::BloomIndexer; - use extras::BlocksBloomLocation; - let bi = BloomIndexer::new(16, 3); - - let index = BloomIndex::new(0, 0); - assert_eq!(bi.location(&index), BlocksBloomLocation { - hash: H256::new(), - index: 0 - }); - - let index = BloomIndex::new(1, 0); - assert_eq!(bi.location(&index), BlocksBloomLocation { - hash: H256::from_str("0000000000000000000000000000000000000000000000010000000000000000").unwrap(), - index: 0 - }); - - let index = BloomIndex::new(0, 299_999); - assert_eq!(bi.location(&index), BlocksBloomLocation { - hash: H256::from_str("000000000000000000000000000000000000000000000000000000000000493d").unwrap(), - index: 15 - }); - } } diff --git a/ethcore/src/blockchain/bloom_indexer.rs b/ethcore/src/blockchain/bloom_indexer.rs new file mode 100644 index 000000000..74c679ba8 --- /dev/null +++ b/ethcore/src/blockchain/bloom_indexer.rs @@ -0,0 +1,102 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity 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 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. If not, see . + +use util::hash::H256; +use chainfilter::BloomIndex; + +/// Represents location of block bloom in extras database. +#[derive(Debug, PartialEq)] +pub struct BlocksBloomLocation { + /// Unique hash of BlocksBloom + pub hash: H256, + /// Index within BlocksBloom + pub index: usize, +} + +/// Should be used to localize blocks blooms in extras database. +pub struct BloomIndexer { + index_size: usize, + levels: u8, +} + +impl BloomIndexer { + /// Plain constructor. + pub fn new(index_size: usize, levels: u8) -> Self { + BloomIndexer { + index_size: index_size, + levels: levels + } + } + + /// Calculates bloom's position in database. + pub fn location(&self, bloom_index: &BloomIndex) -> BlocksBloomLocation { + use std::{mem, ptr}; + + let hash = unsafe { + let mut hash: H256 = mem::zeroed(); + ptr::copy(&[bloom_index.index / self.index_size] as *const usize as *const u8, hash.as_mut_ptr(), 8); + hash[8] = bloom_index.level; + hash.reverse(); + hash + }; + + BlocksBloomLocation { + hash: hash, + index: bloom_index.index % self.index_size + } + } + + /// Returns index size. + pub fn index_size(&self) -> usize { + self.index_size + } + + /// Returns number of cache levels. + pub fn levels(&self) -> u8 { + self.levels + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use util::hash::{H256, FixedHash}; + use chainfilter::BloomIndex; + use blockchain::bloom_indexer::{BloomIndexer, BlocksBloomLocation}; + + #[test] + fn test_bloom_indexer() { + let bi = BloomIndexer::new(16, 3); + + let index = BloomIndex::new(0, 0); + assert_eq!(bi.location(&index), BlocksBloomLocation { + hash: H256::new(), + index: 0 + }); + + let index = BloomIndex::new(1, 0); + assert_eq!(bi.location(&index), BlocksBloomLocation { + hash: H256::from_str("0000000000000000000000000000000000000000000000010000000000000000").unwrap(), + index: 0 + }); + + let index = BloomIndex::new(0, 299_999); + assert_eq!(bi.location(&index), BlocksBloomLocation { + hash: H256::from_str("000000000000000000000000000000000000000000000000000000000000493d").unwrap(), + index: 15 + }); + } +} diff --git a/ethcore/src/blockchain/cache.rs b/ethcore/src/blockchain/cache.rs new file mode 100644 index 000000000..722f83c16 --- /dev/null +++ b/ethcore/src/blockchain/cache.rs @@ -0,0 +1,37 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity 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 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. If not, see . + +/// Represents blockchain's in-memory cache size in bytes. +#[derive(Debug)] +pub struct CacheSize { + /// Blocks cache size. + pub blocks: usize, + /// BlockDetails cache size. + pub block_details: usize, + /// Transaction addresses cache size. + pub transaction_addresses: usize, + /// Logs cache size. + pub block_logs: usize, + /// Blooms cache size. + pub blocks_blooms: usize, + /// Block receipts size. + pub block_receipts: usize, +} + +impl CacheSize { + /// Total amount used by the cache. + pub fn total(&self) -> usize { self.blocks + self.block_details + self.transaction_addresses + self.block_logs + self.blocks_blooms } +} diff --git a/ethcore/src/blockchain/mod.rs b/ethcore/src/blockchain/mod.rs new file mode 100644 index 000000000..c1046d960 --- /dev/null +++ b/ethcore/src/blockchain/mod.rs @@ -0,0 +1,29 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity 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 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. If not, see . + +//! Blockchain database. + +pub mod blockchain; +mod best_block; +mod block_info; +mod bloom_indexer; +mod cache; +mod tree_route; +mod update; + +pub use self::blockchain::{BlockProvider, BlockChain, BlockChainConfig}; +pub use self::cache::CacheSize; +pub use self::tree_route::TreeRoute; diff --git a/ethcore/src/blockchain/tree_route.rs b/ethcore/src/blockchain/tree_route.rs new file mode 100644 index 000000000..1bd0e6f75 --- /dev/null +++ b/ethcore/src/blockchain/tree_route.rs @@ -0,0 +1,29 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity 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 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. If not, see . + +use util::hash::H256; + +/// Represents a tree route between `from` block and `to` block: +#[derive(Debug)] +pub struct TreeRoute { + /// A vector of hashes of all blocks, ordered from `from` to `to`. + pub blocks: Vec, + /// Best common ancestor of these blocks. + pub ancestor: H256, + /// An index where best common ancestor would be. + pub index: usize, +} + diff --git a/ethcore/src/blockchain/update.rs b/ethcore/src/blockchain/update.rs new file mode 100644 index 000000000..f8ca06e66 --- /dev/null +++ b/ethcore/src/blockchain/update.rs @@ -0,0 +1,21 @@ +use std::collections::HashMap; +use util::hash::H256; +use header::BlockNumber; +use blockchain::block_info::BlockInfo; +use extras::{BlockDetails, BlockReceipts, TransactionAddress, BlocksBlooms}; + +/// Block extras update info. +pub struct ExtrasUpdate { + /// Block info. + pub info: BlockInfo, + /// Modified block hashes. + pub block_hashes: HashMap, + /// Modified block details. + pub block_details: HashMap, + /// Modified block receipts. + pub block_receipts: HashMap, + /// Modified transaction addresses. + pub transactions_addresses: HashMap, + /// Modified blocks blooms. + pub blocks_blooms: HashMap, +} diff --git a/ethcore/src/client.rs b/ethcore/src/client.rs index 5d71eed84..f2894decb 100644 --- a/ethcore/src/client.rs +++ b/ethcore/src/client.rs @@ -461,7 +461,11 @@ impl BlockChainClient for Client { } fn tree_route(&self, from: &H256, to: &H256) -> Option { - self.chain.read().unwrap().tree_route(from.clone(), to.clone()) + let chain = self.chain.read().unwrap(); + match chain.is_known(from) && chain.is_known(to) { + true => Some(chain.tree_route(from.clone(), to.clone())), + false => None + } } fn state_data(&self, _hash: &H256) -> Option { diff --git a/ethcore/src/extras.rs b/ethcore/src/extras.rs index 128357576..f4759b040 100644 --- a/ethcore/src/extras.rs +++ b/ethcore/src/extras.rs @@ -262,15 +262,6 @@ impl Encodable for BlocksBlooms { } } -/// Represents location of bloom in database. -#[derive(Debug, PartialEq)] -pub struct BlocksBloomLocation { - /// Unique hash of BlocksBloom - pub hash: H256, - /// Index within BlocksBloom - pub index: usize, -} - /// Represents address of certain transaction within block #[derive(Clone)] pub struct TransactionAddress {