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 {