Owning views of blockchain data (#3982)

* owning views of blockchain data

* port blockchain and client traits to owning views

* fix ethcore tests

* use strong headers and bodies in ethcore_light

* port ethsync to use owning views

* port rpc to owning views

* port parity informant and blockchain export
This commit is contained in:
Robert Habermeier
2016-12-28 13:44:51 +01:00
committed by Gav Wood
parent 592a3ac623
commit fe1f542c4f
22 changed files with 493 additions and 212 deletions

View File

@@ -32,7 +32,7 @@ use util::kvdb::*;
// other
use io::*;
use views::{HeaderView, BodyView, BlockView};
use views::BlockView;
use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError};
use header::BlockNumber;
use state::{State, CleanupMode};
@@ -67,10 +67,11 @@ use evm::{Factory as EvmFactory, Schedule};
use miner::{Miner, MinerService};
use snapshot::{self, io as snapshot_io};
use factory::Factories;
use rlp::{decode, View, UntrustedRlp};
use rlp::{View, UntrustedRlp};
use state_db::StateDB;
use rand::OsRng;
use client::registry::Registry;
use encoded;
// re-export
pub use types::blockchain_info::BlockChainInfo;
@@ -303,17 +304,16 @@ impl Client {
/// The env info as of the best block.
fn latest_env_info(&self) -> EnvInfo {
let header_data = self.best_block_header();
let view = HeaderView::new(&header_data);
let header = self.best_block_header();
EnvInfo {
number: view.number(),
author: view.author(),
timestamp: view.timestamp(),
difficulty: view.difficulty(),
last_hashes: self.build_last_hashes(view.hash()),
number: header.number(),
author: header.author(),
timestamp: header.timestamp(),
difficulty: header.difficulty(),
last_hashes: self.build_last_hashes(header.hash()),
gas_used: U256::default(),
gas_limit: view.gas_limit(),
gas_limit: header.gas_limit(),
}
}
@@ -642,8 +642,7 @@ impl Client {
return None;
}
let root = HeaderView::new(&header).state_root();
let root = header.state_root();
State::from_existing(db, root, self.engine.account_start_nonce(), self.factories.clone()).ok()
})
}
@@ -666,7 +665,6 @@ impl Client {
/// Get a copy of the best block's state.
pub fn state(&self) -> State {
let header = self.best_block_header();
let header = HeaderView::new(&header);
State::from_existing(
self.state_db.lock().boxed_clone_canon(&header.hash()),
header.state_root(),
@@ -843,13 +841,12 @@ impl snapshot::DatabaseRestore for Client {
impl BlockChainClient for Client {
fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result<Executed, CallError> {
let header = self.block_header(block).ok_or(CallError::StatePruned)?;
let view = HeaderView::new(&header);
let last_hashes = self.build_last_hashes(view.parent_hash());
let last_hashes = self.build_last_hashes(header.parent_hash());
let env_info = EnvInfo {
number: view.number(),
author: view.author(),
timestamp: view.timestamp(),
difficulty: view.difficulty(),
number: header.number(),
author: header.author(),
timestamp: header.timestamp(),
difficulty: header.difficulty(),
last_hashes: last_hashes,
gas_used: U256::zero(),
gas_limit: U256::max_value(),
@@ -879,26 +876,25 @@ impl BlockChainClient for Client {
fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError> {
let address = self.transaction_address(id).ok_or(CallError::TransactionNotFound)?;
let header_data = self.block_header(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
let body_data = self.block_body(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
let header = self.block_header(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
let body = self.block_body(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
let mut state = self.state_at_beginning(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
let txs = BodyView::new(&body_data).transactions();
let txs = body.transactions();
if address.index >= txs.len() {
return Err(CallError::TransactionNotFound);
}
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
let view = HeaderView::new(&header_data);
let last_hashes = self.build_last_hashes(view.hash());
let last_hashes = self.build_last_hashes(header.hash());
let mut env_info = EnvInfo {
number: view.number(),
author: view.author(),
timestamp: view.timestamp(),
difficulty: view.difficulty(),
number: header.number(),
author: header.author(),
timestamp: header.timestamp(),
difficulty: header.difficulty(),
last_hashes: last_hashes,
gas_used: U256::default(),
gas_limit: view.gas_limit(),
gas_limit: header.gas_limit(),
};
for t in txs.iter().take(address.index) {
match Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, Default::default()) {
@@ -959,11 +955,11 @@ impl BlockChainClient for Client {
}
}
fn best_block_header(&self) -> Bytes {
fn best_block_header(&self) -> encoded::Header {
self.chain.read().best_block_header()
}
fn block_header(&self, id: BlockId) -> Option<Bytes> {
fn block_header(&self, id: BlockId) -> Option<::encoded::Header> {
let chain = self.chain.read();
Self::block_hash(&chain, id).and_then(|hash| chain.block_header_data(&hash))
}
@@ -977,15 +973,15 @@ impl BlockChainClient for Client {
}
}
fn block_body(&self, id: BlockId) -> Option<Bytes> {
fn block_body(&self, id: BlockId) -> Option<encoded::Body> {
let chain = self.chain.read();
Self::block_hash(&chain, id).and_then(|hash| chain.block_body(&hash))
}
fn block(&self, id: BlockId) -> Option<Bytes> {
fn block(&self, id: BlockId) -> Option<encoded::Block> {
if let BlockId::Pending = id {
if let Some(block) = self.miner.pending_block() {
return Some(block.rlp_bytes(Seal::Without));
return Some(encoded::Block::new(block.rlp_bytes(Seal::Without)));
}
}
let chain = self.chain.read();
@@ -1128,9 +1124,10 @@ impl BlockChainClient for Client {
self.transaction_address(id).map(|addr| addr.block_hash)
}
fn uncle(&self, id: UncleId) -> Option<Bytes> {
fn uncle(&self, id: UncleId) -> Option<encoded::Header> {
let index = id.position;
self.block_body(id.block).and_then(|body| BodyView::new(&body).uncle_rlp_at(index))
self.block_body(id.block).and_then(|body| body.view().uncle_rlp_at(index))
.map(encoded::Header::new)
}
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> {
@@ -1138,8 +1135,8 @@ impl BlockChainClient for Client {
self.transaction_address(id)
.and_then(|address| chain.block_number(&address.block_hash).and_then(|block_number| {
let t = chain.block_body(&address.block_hash)
.and_then(|block| {
BodyView::new(&block).localized_transaction_at(&address.block_hash, block_number, address.index)
.and_then(|body| {
body.view().localized_transaction_at(&address.block_hash, block_number, address.index)
});
let tx_and_sender = t.and_then(|tx| tx.sender().ok().map(|sender| (tx, sender)));
@@ -1360,13 +1357,12 @@ impl BlockChainClient for Client {
fn block_extra_info(&self, id: BlockId) -> Option<BTreeMap<String, String>> {
self.block_header(id)
.map(|block| decode(&block))
.map(|header| self.engine.extra_info(&header))
.map(|header| self.engine.extra_info(&header.decode()))
}
fn uncle_extra_info(&self, id: UncleId) -> Option<BTreeMap<String, String>> {
self.uncle(id)
.map(|header| self.engine.extra_info(&decode(&header)))
.map(|header| self.engine.extra_info(&header.decode()))
}
fn pruning_info(&self) -> PruningInfo {

View File

@@ -39,7 +39,6 @@ use miner::{Miner, MinerService, TransactionImportResult};
use spec::Spec;
use types::mode::Mode;
use types::pruning_info::PruningInfo;
use views::BlockView;
use verification::queue::QueueInfo;
use block::{OpenBlock, SealedBlock};
@@ -47,6 +46,7 @@ use executive::Executed;
use error::CallError;
use trace::LocalizedTrace;
use state_db::StateDB;
use encoded;
/// Test client.
pub struct TestBlockChainClient {
@@ -263,7 +263,7 @@ impl TestBlockChainClient {
/// Make a bad block by setting invalid extra data.
pub fn corrupt_block(&self, n: BlockNumber) {
let hash = self.block_hash(BlockId::Number(n)).unwrap();
let mut header: BlockHeader = decode(&self.block_header(BlockId::Number(n)).unwrap());
let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode();
header.set_extra_data(b"This extra data is way too long to be considered valid".to_vec());
let mut rlp = RlpStream::new_list(3);
rlp.append(&header);
@@ -275,7 +275,7 @@ impl TestBlockChainClient {
/// Make a bad block by setting invalid parent hash.
pub fn corrupt_block_parent(&self, n: BlockNumber) {
let hash = self.block_hash(BlockId::Number(n)).unwrap();
let mut header: BlockHeader = decode(&self.block_header(BlockId::Number(n)).unwrap());
let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode();
header.set_parent_hash(H256::from(42));
let mut rlp = RlpStream::new_list(3);
rlp.append(&header);
@@ -458,7 +458,7 @@ impl BlockChainClient for TestBlockChainClient {
None // Simple default.
}
fn uncle(&self, _id: UncleId) -> Option<Bytes> {
fn uncle(&self, _id: UncleId) -> Option<encoded::Header> {
None // Simple default.
}
@@ -487,34 +487,39 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!();
}
fn best_block_header(&self) -> Bytes {
self.block_header(BlockId::Hash(self.chain_info().best_block_hash)).expect("Best block always have header.")
fn best_block_header(&self) -> encoded::Header {
self.block_header(BlockId::Hash(self.chain_info().best_block_hash))
.expect("Best block always has header.")
}
fn block_header(&self, id: BlockId) -> Option<Bytes> {
self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
fn block_header(&self, id: BlockId) -> Option<encoded::Header> {
self.block_hash(id)
.and_then(|hash| self.blocks.read().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
.map(encoded::Header::new)
}
fn block_number(&self, _id: BlockId) -> Option<BlockNumber> {
unimplemented!()
}
fn block_body(&self, id: BlockId) -> Option<Bytes> {
fn block_body(&self, id: BlockId) -> Option<encoded::Body> {
self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| {
let mut stream = RlpStream::new_list(2);
stream.append_raw(Rlp::new(r).at(1).as_raw(), 1);
stream.append_raw(Rlp::new(r).at(2).as_raw(), 1);
stream.out()
encoded::Body::new(stream.out())
}))
}
fn block(&self, id: BlockId) -> Option<Bytes> {
self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).cloned())
fn block(&self, id: BlockId) -> Option<encoded::Block> {
self.block_hash(id)
.and_then(|hash| self.blocks.read().get(&hash).cloned())
.map(encoded::Block::new)
}
fn block_extra_info(&self, id: BlockId) -> Option<BTreeMap<String, String>> {
self.block(id)
.map(|block| BlockView::new(&block).header())
.map(|block| block.view().header())
.map(|header| self.spec.engine.extra_info(&header))
}

View File

@@ -24,7 +24,6 @@ use header::{BlockNumber};
use transaction::{LocalizedTransaction, SignedTransaction, PendingTransaction};
use log_entry::LocalizedLogEntry;
use filter::Filter;
use views::{BlockView};
use error::{ImportResult, CallError};
use receipt::LocalizedReceipt;
use trace::LocalizedTrace;
@@ -40,6 +39,7 @@ use types::blockchain_info::BlockChainInfo;
use types::block_status::BlockStatus;
use types::mode::Mode;
use types::pruning_info::PruningInfo;
use encoded;
#[ipc(client_ident="RemoteClient")]
/// Blockchain database client. Owns and manages a blockchain and a block queue.
@@ -50,17 +50,17 @@ pub trait BlockChainClient : Sync + Send {
fn keep_alive(&self) {}
/// Get raw block header data by block id.
fn block_header(&self, id: BlockId) -> Option<Bytes>;
fn block_header(&self, id: BlockId) -> Option<encoded::Header>;
/// Look up the block number for the given block ID.
fn block_number(&self, id: BlockId) -> Option<BlockNumber>;
/// Get raw block body data by block id.
/// Block body is an RLP list of two items: uncles and transactions.
fn block_body(&self, id: BlockId) -> Option<Bytes>;
fn block_body(&self, id: BlockId) -> Option<encoded::Body>;
/// Get raw block data by block header hash.
fn block(&self, id: BlockId) -> Option<Bytes>;
fn block(&self, id: BlockId) -> Option<encoded::Block>;
/// Get block status by block header hash.
fn block_status(&self, id: BlockId) -> BlockStatus;
@@ -136,7 +136,7 @@ pub trait BlockChainClient : Sync + Send {
fn transaction_block(&self, id: TransactionId) -> Option<H256>;
/// Get uncle with given id.
fn uncle(&self, id: UncleId) -> Option<Bytes>;
fn uncle(&self, id: UncleId) -> Option<encoded::Header>;
/// Get transaction receipt with given hash.
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt>;
@@ -173,7 +173,7 @@ pub trait BlockChainClient : Sync + Send {
fn additional_params(&self) -> BTreeMap<String, String>;
/// Get the best block header.
fn best_block_header(&self) -> Bytes;
fn best_block_header(&self) -> encoded::Header;
/// Returns numbers of blocks containing given bloom.
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option<Vec<BlockNumber>>;
@@ -220,8 +220,7 @@ pub trait BlockChainClient : Sync + Send {
let mut corpus = Vec::new();
while corpus.is_empty() {
for _ in 0..sample_size {
let block_bytes = self.block(BlockId::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
let block = BlockView::new(&block_bytes);
let block = self.block(BlockId::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
let header = block.header_view();
if header.number() == 0 {
return corpus;