Remove RefCell from Header (#8227)

* Cache RLP and header hashes.

* Refactor header - WiP

* Avoid decoding laster header.

* Pre-compute hashes for Sealed/Locked block.

* Use accrue bloom. Closes ##8241
This commit is contained in:
Tomasz Drwięga
2018-04-03 10:01:28 +02:00
committed by Marek Kotewicz
parent d477670cb9
commit 9f775a7673
20 changed files with 300 additions and 233 deletions

View File

@@ -362,16 +362,15 @@ impl Importer {
let engine = &*self.engine;
let header = &block.header;
let chain = client.chain.read();
// Check the block isn't so old we won't be able to enact it.
let best_block_number = chain.best_block_number();
let best_block_number = client.chain.read().best_block_number();
if client.pruning_info().earliest_state > header.number() {
warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number);
return Err(());
}
// Check if parent is in chain
let parent = match chain.block_header(header.parent_hash()) {
let parent = match client.block_header_decoded(BlockId::Hash(*header.parent_hash())) {
Some(h) => h,
None => {
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash());
@@ -379,6 +378,7 @@ impl Importer {
}
};
let chain = client.chain.read();
// Verify Block Family
let verify_family_result = self.verifier.verify_block_family(
header,
@@ -408,6 +408,7 @@ impl Importer {
let db = client.state_db.read().boxed_clone_canon(header.parent_hash());
let is_epoch_begin = chain.epoch_transition(parent.number(), *header.parent_hash()).is_some();
let strip_receipts = header.number() < engine.params().validate_receipts_transition;
let enact_result = enact_verified(block,
engine,
client.tracedb.read().tracing_enabled(),
@@ -416,15 +417,13 @@ impl Importer {
last_hashes,
client.factories.clone(),
is_epoch_begin,
strip_receipts,
);
let mut locked_block = enact_result.map_err(|e| {
let locked_block = enact_result.map_err(|e| {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
})?;
if header.number() < engine.params().validate_receipts_transition && header.receipts_root() != locked_block.block().header().receipts_root() {
locked_block = locked_block.strip_receipts();
}
// Final Verification
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
warn!(target: "client", "Stage 5 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
@@ -655,7 +654,7 @@ impl Importer {
fn check_epoch_end<'a>(&self, header: &'a Header, chain: &BlockChain, client: &Client) {
let is_epoch_end = self.engine.is_epoch_end(
header,
&(|hash| chain.block_header(&hash)),
&(|hash| client.block_header_decoded(BlockId::Hash(hash))),
&(|hash| chain.get_pending_transition(hash)), // TODO: limit to current epoch.
);
@@ -724,7 +723,7 @@ impl Client {
config.history
};
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(h.state_root())) {
if !chain.block_header_data(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(&h.state_root())) {
warn!("State root not found for block #{} ({:x})", chain.best_block_number(), chain.best_block_hash());
}
@@ -1000,7 +999,7 @@ impl Client {
let header = self.best_block_header();
State::from_existing(
self.state_db.read().boxed_clone_canon(&header.hash()),
header.state_root(),
*header.state_root(),
self.engine.account_start_nonce(header.number()),
self.factories.clone()
)
@@ -1260,6 +1259,23 @@ impl Client {
BlockId::Latest => Some(self.chain.read().best_block_number()),
}
}
/// Retrieve a decoded header given `BlockId`
///
/// This method optimizes access patterns for latest block header
/// to avoid excessive RLP encoding, decoding and hashing.
fn block_header_decoded(&self, id: BlockId) -> Option<Header> {
match id {
BlockId::Latest
=> Some(self.chain.read().best_block_header()),
BlockId::Hash(ref hash) if hash == &self.chain.read().best_block_hash()
=> Some(self.chain.read().best_block_header()),
BlockId::Number(number) if number == self.chain.read().best_block_number()
=> Some(self.chain.read().best_block_header()),
_
=> self.block_header(id).map(|h| h.decode()),
}
}
}
impl snapshot::DatabaseRestore for Client {
@@ -1315,16 +1331,14 @@ impl BlockInfo for Client {
Self::block_hash(&chain, id).and_then(|hash| chain.block_header_data(&hash))
}
fn best_block_header(&self) -> encoded::Header {
fn best_block_header(&self) -> Header {
self.chain.read().best_block_header()
}
fn block(&self, id: BlockId) -> Option<encoded::Block> {
let chain = self.chain.read();
Self::block_hash(&chain, id).and_then(|hash| {
chain.block(&hash)
})
Self::block_hash(&chain, id).and_then(|hash| chain.block(&hash))
}
fn code_hash(&self, address: &Address, id: BlockId) -> Option<H256> {
@@ -1360,11 +1374,11 @@ impl CallContract for Client {
fn call_contract(&self, block_id: BlockId, address: Address, data: Bytes) -> Result<Bytes, String> {
let state_pruned = || CallError::StatePruned.to_string();
let state = &mut self.state_at(block_id).ok_or_else(&state_pruned)?;
let header = self.block_header(block_id).ok_or_else(&state_pruned)?;
let header = self.block_header_decoded(block_id).ok_or_else(&state_pruned)?;
let transaction = self.contract_call_tx(block_id, address, data);
self.call(&transaction, Default::default(), state, &header.decode())
self.call(&transaction, Default::default(), state, &header)
.map_err(|e| format!("{:?}", e))
.map(|executed| executed.output)
}
@@ -1918,8 +1932,8 @@ impl BlockChainClient for Client {
}
fn block_extra_info(&self, id: BlockId) -> Option<BTreeMap<String, String>> {
self.block_header(id)
.map(|header| self.engine.extra_info(&header.decode()))
self.block_header_decoded(id)
.map(|header| self.engine.extra_info(&header))
}
fn uncle_extra_info(&self, id: UncleId) -> Option<BTreeMap<String, String>> {
@@ -1973,8 +1987,8 @@ impl ReopenBlock for Client {
for h in uncles {
if !block.uncles().iter().any(|header| header.hash() == h) {
let uncle = chain.block_header(&h).expect("find_uncle_hashes only returns hashes for existing headers; qed");
block.push_uncle(uncle).expect("pushing up to maximum_uncle_count;
let uncle = chain.block_header_data(&h).expect("find_uncle_hashes only returns hashes for existing headers; qed");
block.push_uncle(uncle.decode()).expect("pushing up to maximum_uncle_count;
push_uncle is not ok only if more than maximum_uncle_count is pushed;
so all push_uncle are Ok;
qed");
@@ -1991,9 +2005,8 @@ impl PrepareOpenBlock for Client {
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
let engine = &*self.engine;
let chain = self.chain.read();
let h = chain.best_block_hash();
let best_header = &chain.block_header(&h)
.expect("h is best block hash: so its header must exist: qed");
let best_header = chain.best_block_header();
let h = best_header.hash();
let is_epoch_begin = chain.epoch_transition(best_header.number(), h).is_some();
let mut open_block = OpenBlock::new(
@@ -2001,7 +2014,7 @@ impl PrepareOpenBlock for Client {
self.factories.clone(),
self.tracedb.read().tracing_enabled(),
self.state_db.read().boxed_clone_canon(&h),
best_header,
&best_header,
self.build_last_hashes(&h),
author,
gas_range_target,
@@ -2016,7 +2029,7 @@ impl PrepareOpenBlock for Client {
.into_iter()
.take(engine.maximum_uncle_count(open_block.header().number()))
.foreach(|h| {
open_block.push_uncle(h).expect("pushing maximum_uncle_count;
open_block.push_uncle(h.decode()).expect("pushing maximum_uncle_count;
open_block was just created;
push_uncle is not ok only if more than maximum_uncle_count is pushed;
so all push_uncle are Ok;

View File

@@ -474,9 +474,10 @@ impl BlockInfo for TestBlockChainClient {
.map(encoded::Header::new)
}
fn best_block_header(&self) -> encoded::Header {
fn best_block_header(&self) -> Header {
self.block_header(BlockId::Hash(self.chain_info().best_block_hash))
.expect("Best block always has header.")
.decode()
}
fn block(&self, id: BlockId) -> Option<encoded::Block> {

View File

@@ -121,7 +121,7 @@ pub trait BlockInfo {
fn block_header(&self, id: BlockId) -> Option<encoded::Header>;
/// Get the best block header.
fn best_block_header(&self) -> encoded::Header;
fn best_block_header(&self) -> Header;
/// Get raw block data by block header hash.
fn block(&self, id: BlockId) -> Option<encoded::Block>;