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:
committed by
Marek Kotewicz
parent
d477670cb9
commit
9f775a7673
@@ -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;
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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>;
|
||||
|
||||
Reference in New Issue
Block a user