From fe1f542c4ffaed8e028f0b3a0e0b7c1fdbf2ee8b Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 28 Dec 2016 13:44:51 +0100 Subject: [PATCH] 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 --- ethcore/light/src/client.rs | 7 +- ethcore/light/src/net/mod.rs | 9 +- ethcore/light/src/net/tests/mod.rs | 11 +- ethcore/light/src/provider.rs | 25 +-- ethcore/src/blockchain/blockchain.rs | 72 +++--- ethcore/src/client/client.rs | 78 +++---- ethcore/src/client/test_client.rs | 31 +-- ethcore/src/client/traits.rs | 15 +- ethcore/src/miner/miner.rs | 7 +- ethcore/src/snapshot/mod.rs | 5 +- ethcore/src/snapshot/watcher.rs | 3 +- ethcore/src/tests/client.rs | 10 +- ethcore/src/types/encoded.rs | 273 +++++++++++++++++++++++ ethcore/src/types/mod.rs.in | 3 +- ethcore/src/verification/verification.rs | 19 +- ethcore/src/views/block.rs | 4 +- ethcore/src/views/body.rs | 4 +- parity/blockchain.rs | 22 +- parity/informant.rs | 18 +- rpc/src/v1/impls/eth.rs | 29 ++- sync/src/blocks.rs | 14 +- sync/src/chain.rs | 46 ++-- 22 files changed, 493 insertions(+), 212 deletions(-) create mode 100644 ethcore/src/types/encoded.rs diff --git a/ethcore/light/src/client.rs b/ethcore/light/src/client.rs index 9a594f3dd..14d032821 100644 --- a/ethcore/light/src/client.rs +++ b/ethcore/light/src/client.rs @@ -26,6 +26,7 @@ use ethcore::block_status::BlockStatus; use ethcore::verification::queue::{HeaderQueue, QueueInfo}; use ethcore::transaction::{SignedTransaction, PendingTransaction}; use ethcore::blockchain_info::BlockChainInfo; +use ethcore::encoded; use io::IoChannel; use util::hash::{H256, H256FastMap}; @@ -90,11 +91,11 @@ impl Provider for Client { None } - fn block_header(&self, _id: BlockId) -> Option { + fn block_header(&self, _id: BlockId) -> Option { None } - fn block_body(&self, _id: BlockId) -> Option { + fn block_body(&self, _id: BlockId) -> Option { None } @@ -110,7 +111,7 @@ impl Provider for Client { Vec::new() } - fn header_proof(&self, _req: request::HeaderProof) -> Option<(Bytes, Vec)> { + fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec)> { None } diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index e76487b6d..18df0f899 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -706,7 +706,7 @@ impl LightProtocol { stream.append(&req_id).append(&cur_buffer).begin_list(response.len()); for header in response { - stream.append_raw(&header, 1); + stream.append_raw(&header.into_inner(), 1); } stream.out() @@ -757,7 +757,7 @@ impl LightProtocol { let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Bodies, req.block_hashes.len())?; let response = self.provider.block_bodies(req); - let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count(); + let response_len = response.iter().filter(|x| x.is_some()).count(); let actual_cost = self.flow_params.compute_cost(request::Kind::Bodies, response_len); assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); @@ -768,7 +768,10 @@ impl LightProtocol { stream.append(&req_id).append(&cur_buffer).begin_list(response.len()); for body in response { - stream.append_raw(&body, 1); + match body { + Some(body) => stream.append_raw(&body.into_inner(), 1), + None => stream.append_empty_data(), + }; } stream.out() diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 0cfc8cac7..56ff32b54 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -21,6 +21,7 @@ use ethcore::blockchain_info::BlockChainInfo; use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient}; use ethcore::ids::BlockId; use ethcore::transaction::PendingTransaction; +use ethcore::encoded; use network::{PeerId, NodeId}; use net::buffer_flow::FlowParams; @@ -94,11 +95,11 @@ impl Provider for TestProvider { None } - fn block_header(&self, id: BlockId) -> Option { + fn block_header(&self, id: BlockId) -> Option { self.0.client.block_header(id) } - fn block_body(&self, id: BlockId) -> Option { + fn block_body(&self, id: BlockId) -> Option { self.0.client.block_body(id) } @@ -122,7 +123,7 @@ impl Provider for TestProvider { req.account_key.iter().chain(req.account_key.iter()).cloned().collect() } - fn header_proof(&self, _req: request::HeaderProof) -> Option<(Bytes, Vec)> { + fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec)> { None } @@ -273,7 +274,7 @@ fn get_block_headers() { response_stream.append(&req_id).append(&new_buf).begin_list(10); for header in headers { - response_stream.append_raw(&header, 1); + response_stream.append_raw(&header.into_inner(), 1); } response_stream.out() @@ -320,7 +321,7 @@ fn get_block_bodies() { response_stream.append(&req_id).append(&new_buf).begin_list(10); for body in bodies { - response_stream.append_raw(&body, 1); + response_stream.append_raw(&body.into_inner(), 1); } response_stream.out() diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index afc5294fa..0b94077ab 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -21,6 +21,7 @@ use ethcore::blockchain_info::BlockChainInfo; use ethcore::client::{BlockChainClient, ProvingBlockChainClient}; use ethcore::transaction::PendingTransaction; use ethcore::ids::BlockId; +use ethcore::encoded; use util::{Bytes, H256}; @@ -52,9 +53,8 @@ pub trait Provider: Send + Sync { /// /// The returned vector may have any length in the range [0, `max`], but the /// results within must adhere to the `skip` and `reverse` parameters. - fn block_headers(&self, req: request::Headers) -> Vec { + fn block_headers(&self, req: request::Headers) -> Vec { use request::HashOrNumber; - use ethcore::views::HeaderView; if req.max == 0 { return Vec::new() } @@ -67,9 +67,9 @@ pub trait Provider: Send + Sync { return Vec::new(); } Some(header) => { - let num = HeaderView::new(&header).number(); + let num = header.number(); let canon_hash = self.block_header(BlockId::Number(num)) - .map(|h| HeaderView::new(&h).hash()); + .map(|h| h.hash()); if req.max == 1 || canon_hash != Some(hash) { // Non-canonical header or single header requested. @@ -92,19 +92,18 @@ pub trait Provider: Send + Sync { } /// Get a block header by id. - fn block_header(&self, id: BlockId) -> Option; + fn block_header(&self, id: BlockId) -> Option; /// Provide as many as possible of the requested blocks (minus the headers) encoded /// in RLP format. - fn block_bodies(&self, req: request::Bodies) -> Vec { + fn block_bodies(&self, req: request::Bodies) -> Vec> { req.block_hashes.into_iter() .map(|hash| self.block_body(BlockId::Hash(hash))) - .map(|body| body.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec())) .collect() } /// Get a block body by id. - fn block_body(&self, id: BlockId) -> Option; + fn block_body(&self, id: BlockId) -> Option; /// Provide the receipts as many as possible of the requested blocks. /// Returns a vector of RLP-encoded lists of receipts. @@ -169,7 +168,7 @@ pub trait Provider: Send + Sync { None => rlp::EMPTY_LIST_RLP.to_vec(), Some((header, proof)) => { let mut stream = RlpStream::new_list(2); - stream.append_raw(&header, 1).begin_list(proof.len()); + stream.append_raw(&header.into_inner(), 1).begin_list(proof.len()); for node in proof { stream.append_raw(&node, 1); @@ -184,7 +183,7 @@ pub trait Provider: Send + Sync { /// Provide a header proof from a given Canonical Hash Trie as well as the /// corresponding header. The first element is the block header and the /// second is a merkle proof of the CHT. - fn header_proof(&self, req: request::HeaderProof) -> Option<(Bytes, Vec)>; + fn header_proof(&self, req: request::HeaderProof) -> Option<(encoded::Header, Vec)>; /// Provide pending transactions. fn ready_transactions(&self) -> Vec; @@ -204,11 +203,11 @@ impl Provider for T { Some(self.pruning_info().earliest_state) } - fn block_header(&self, id: BlockId) -> Option { + fn block_header(&self, id: BlockId) -> Option { BlockChainClient::block_header(self, id) } - fn block_body(&self, id: BlockId) -> Option { + fn block_body(&self, id: BlockId) -> Option { BlockChainClient::block_body(self, id) } @@ -227,7 +226,7 @@ impl Provider for T { self.code_by_hash(req.account_key, BlockId::Hash(req.block_hash)) } - fn header_proof(&self, _req: request::HeaderProof) -> Option<(Bytes, Vec)> { + fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec)> { None } diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 47f3a38c4..1226a5abe 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -35,6 +35,7 @@ use blockchain::{CacheSize, ImportRoute, Config}; use db::{self, Writable, Readable, CacheUpdatePolicy}; use cache_manager::CacheManager; use engines::Engine; +use encoded; const LOG_BLOOMS_LEVELS: usize = 3; const LOG_BLOOMS_ELEMENTS_PER_INDEX: usize = 16; @@ -64,7 +65,7 @@ pub trait BlockProvider { self.best_ancient_block().map(|h| self.block_number(&h).expect("Ancient block is always set to an existing block or `None`. Existing block always has a number; qed")) } /// Get raw block data - fn block(&self, hash: &H256) -> Option; + fn block(&self, hash: &H256) -> Option; /// Get the familial details concerning a block. fn block_details(&self, hash: &H256) -> Option; @@ -80,25 +81,25 @@ pub trait BlockProvider { /// Get the partial-header of a block. fn block_header(&self, hash: &H256) -> Option
{ - self.block_header_data(hash).map(|header| decode(&header)) + self.block_header_data(hash).map(|header| header.decode()) } /// Get the header RLP of a block. - fn block_header_data(&self, hash: &H256) -> Option; + fn block_header_data(&self, hash: &H256) -> Option; /// Get the block body (uncles and transactions). - fn block_body(&self, hash: &H256) -> Option; + fn block_body(&self, hash: &H256) -> Option; /// Get a list of uncles for a given block. /// Returns None if block does not exist. fn uncles(&self, hash: &H256) -> Option> { - self.block_body(hash).map(|bytes| BodyView::new(&bytes).uncles()) + self.block_body(hash).map(|body| body.uncles()) } /// Get a list of uncle hashes for a given block. /// Returns None if block does not exist. fn uncle_hashes(&self, hash: &H256) -> Option> { - self.block_body(hash).map(|bytes| BodyView::new(&bytes).uncle_hashes()) + self.block_body(hash).map(|body| body.uncle_hashes()) } /// Get the number of given block's hash. @@ -109,8 +110,8 @@ pub trait BlockProvider { /// Get transaction with given transaction hash. fn transaction(&self, address: &TransactionAddress) -> Option { self.block_body(&address.block_hash) - .and_then(|bytes| self.block_number(&address.block_hash) - .and_then(|n| BodyView::new(&bytes).localized_transaction_at(&address.block_hash, n, address.index))) + .and_then(|body| self.block_number(&address.block_hash) + .and_then(|n| body.view().localized_transaction_at(&address.block_hash, n, address.index))) } /// Get transaction receipt. @@ -122,8 +123,8 @@ pub trait BlockProvider { /// Returns None if block does not exist. fn transactions(&self, hash: &H256) -> Option> { self.block_body(hash) - .and_then(|bytes| self.block_number(hash) - .map(|n| BodyView::new(&bytes).localized_transactions(hash, n))) + .and_then(|body| self.block_number(hash) + .map(|n| body.view().localized_transactions(hash, n))) } /// Returns reference to genesis hash. @@ -224,27 +225,27 @@ impl BlockProvider for BlockChain { } /// Get raw block data - fn block(&self, hash: &H256) -> Option { + fn block(&self, hash: &H256) -> Option { match (self.block_header_data(hash), self.block_body(hash)) { (Some(header), Some(body)) => { let mut block = RlpStream::new_list(3); - let body_rlp = Rlp::new(&body); - block.append_raw(&header, 1); + let body_rlp = body.rlp(); + block.append_raw(header.rlp().as_raw(), 1); block.append_raw(body_rlp.at(0).as_raw(), 1); block.append_raw(body_rlp.at(1).as_raw(), 1); - Some(block.out()) + Some(encoded::Block::new(block.out())) }, _ => None, } } /// Get block header data - fn block_header_data(&self, hash: &H256) -> Option { + fn block_header_data(&self, hash: &H256) -> Option { // Check cache first { let read = self.block_headers.read(); if let Some(v) = read.get(hash) { - return Some(v.clone()); + return Some(encoded::Header::new(v.clone())); } } @@ -252,7 +253,9 @@ impl BlockProvider for BlockChain { { let best_block = self.best_block.read(); if &best_block.hash == hash { - return Some(Rlp::new(&best_block.block).at(0).as_raw().to_vec()); + return Some(encoded::Header::new( + Rlp::new(&best_block.block).at(0).as_raw().to_vec() + )) } } @@ -265,7 +268,7 @@ impl BlockProvider for BlockChain { let bytes: Bytes = UntrustedRlp::new(&b).decompress(RlpType::Blocks).to_vec(); let mut write = self.block_headers.write(); write.insert(hash.clone(), bytes.clone()); - Some(bytes) + Some(encoded::Header::new(bytes)) }, None => None }; @@ -275,12 +278,12 @@ impl BlockProvider for BlockChain { } /// Get block body data - fn block_body(&self, hash: &H256) -> Option { + fn block_body(&self, hash: &H256) -> Option { // Check cache first { let read = self.block_bodies.read(); if let Some(v) = read.get(hash) { - return Some(v.clone()); + return Some(encoded::Body::new(v.clone())); } } @@ -288,7 +291,7 @@ impl BlockProvider for BlockChain { { let best_block = self.best_block.read(); if &best_block.hash == hash { - return Some(Self::block_to_body(&best_block.block)); + return Some(encoded::Body::new(Self::block_to_body(&best_block.block))); } } @@ -301,7 +304,7 @@ impl BlockProvider for BlockChain { let bytes: Bytes = UntrustedRlp::new(&b).decompress(RlpType::Blocks).to_vec(); let mut write = self.block_bodies.write(); write.insert(hash.clone(), bytes.clone()); - Some(bytes) + Some(encoded::Body::new(bytes)) }, None => None }; @@ -358,7 +361,7 @@ impl BlockProvider for BlockChain { let mut logs = blocks.into_iter() .filter_map(|number| self.block_hash(number).map(|hash| (number, hash))) .filter_map(|(number, hash)| self.block_receipts(&hash).map(|r| (number, hash, r.receipts))) - .filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, BodyView::new(b).transaction_hashes()))) + .filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, b.transaction_hashes()))) .flat_map(|(number, hash, mut receipts, mut hashes)| { if receipts.len() != hashes.len() { warn!("Block {} ({}) has different number of receipts ({}) to transactions ({}). Database corrupt?", number, hash, receipts.len(), hashes.len()); @@ -484,7 +487,7 @@ impl BlockChain { // Fetch best block details let best_block_number = bc.block_number(&best_block_hash).unwrap(); let best_block_total_difficulty = bc.block_details(&best_block_hash).unwrap().total_difficulty; - let best_block_rlp = bc.block(&best_block_hash).unwrap(); + let best_block_rlp = bc.block(&best_block_hash).unwrap().into_inner(); let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map(|v| v.to_vec()); let mut best_ancient = bc.db.get(db::COL_EXTRA, b"ancient").unwrap().map(|h| H256::from_slice(&h)); @@ -578,7 +581,7 @@ impl BlockChain { batch.put(db::COL_EXTRA, b"best", &hash); let best_block_total_difficulty = self.block_details(&hash).unwrap().total_difficulty; - let best_block_rlp = self.block(&hash).unwrap(); + let best_block_rlp = self.block(&hash).unwrap().into_inner(); let mut best_block = self.best_block.write(); *best_block = BestBlock { @@ -862,7 +865,7 @@ impl BlockChain { let number = header.number(); let parent_hash = header.parent_hash(); let parent_details = self.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); - let is_new_best = self.engine.is_new_best_block(self.best_block_total_difficulty(), HeaderView::new(&self.best_block_header()), &parent_details, header); + let is_new_best = self.engine.is_new_best_block(self.best_block_total_difficulty(), self.best_block_header().view(), &parent_details, header); BlockInfo { hash: hash, @@ -1108,8 +1111,8 @@ impl BlockChain { BlockLocation::BranchBecomingCanonChain(ref data) => { let addresses = data.enacted.iter() .flat_map(|hash| { - let bytes = self.block_body(hash).expect("Enacted block must be in database."); - let hashes = BodyView::new(&bytes).transaction_hashes(); + let body = self.block_body(hash).expect("Enacted block must be in database."); + let hashes = body.transaction_hashes(); hashes.into_iter() .enumerate() .map(|(i, tx_hash)| (tx_hash, Some(TransactionAddress { @@ -1129,8 +1132,8 @@ impl BlockChain { }); let retracted = data.retracted.iter().flat_map(|hash| { - let bytes = self.block_body(hash).expect("Retracted block must be in database."); - let hashes = BodyView::new(&bytes).transaction_hashes(); + let body = self.block_body(hash).expect("Retracted block must be in database."); + let hashes = body.transaction_hashes(); hashes.into_iter().map(|hash| (hash, None)).collect::>>() }); @@ -1179,7 +1182,7 @@ impl BlockChain { let mut blooms: Vec = data.enacted.iter() .map(|hash| self.block_header_data(hash).unwrap()) - .map(|bytes| HeaderView::new(&bytes).log_bloom()) + .map(|h| h.log_bloom()) .map(Bloom::from) .map(Into::into) .collect(); @@ -1212,9 +1215,10 @@ impl BlockChain { } /// Get best block header - pub fn best_block_header(&self) -> Bytes { + pub fn best_block_header(&self) -> encoded::Header { let block = self.best_block.read(); - BlockView::new(&block.block).header_view().rlp().as_raw().to_vec() + let raw = BlockView::new(&block.block).header_view().rlp().as_raw().to_vec(); + encoded::Header::new(raw) } /// Get current cache size. @@ -1329,7 +1333,7 @@ mod tests { } fn new_chain(genesis: &[u8], db: Arc) -> BlockChain { - BlockChain::new(Config::default(), genesis, db, Spec::new_null().engine) + BlockChain::new(Config::default(), genesis, db, Spec::new_null().engine) } #[test] diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index cfe5dc527..48ff03095 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -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 { 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 { 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 { + 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 { + fn block_body(&self, id: BlockId) -> Option { let chain = self.chain.read(); Self::block_hash(&chain, id).and_then(|hash| chain.block_body(&hash)) } - fn block(&self, id: BlockId) -> Option { + fn block(&self, id: BlockId) -> Option { 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 { + fn uncle(&self, id: UncleId) -> Option { 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 { @@ -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> { 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> { self.uncle(id) - .map(|header| self.engine.extra_info(&decode(&header))) + .map(|header| self.engine.extra_info(&header.decode())) } fn pruning_info(&self) -> PruningInfo { diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 075f3f11c..da7425a3d 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -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 { + fn uncle(&self, _id: UncleId) -> Option { 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 { - 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 { + 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 { unimplemented!() } - fn block_body(&self, id: BlockId) -> Option { + fn block_body(&self, id: BlockId) -> Option { 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 { - self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).cloned()) + fn block(&self, id: BlockId) -> Option { + 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> { self.block(id) - .map(|block| BlockView::new(&block).header()) + .map(|block| block.view().header()) .map(|header| self.spec.engine.extra_info(&header)) } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 2d7d882ee..7e39d5002 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -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; + fn block_header(&self, id: BlockId) -> Option; /// Look up the block number for the given block ID. fn block_number(&self, id: BlockId) -> Option; /// 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; + fn block_body(&self, id: BlockId) -> Option; /// Get raw block data by block header hash. - fn block(&self, id: BlockId) -> Option; + fn block(&self, id: BlockId) -> Option; /// 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; /// Get uncle with given id. - fn uncle(&self, id: UncleId) -> Option; + fn uncle(&self, id: UncleId) -> Option; /// Get transaction receipt with given hash. fn transaction_receipt(&self, id: TransactionId) -> Option; @@ -173,7 +173,7 @@ pub trait BlockChainClient : Sync + Send { fn additional_params(&self) -> BTreeMap; /// 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>; @@ -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; diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 198170735..c11f34ecb 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -20,8 +20,6 @@ use std::time::{Instant, Duration}; use util::*; use util::using_queue::{UsingQueue, GetAction}; use account_provider::{AccountProvider, Error as AccountError}; -use views::{BlockView, HeaderView}; -use header::Header; use state::{State, CleanupMode}; use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockId, CallAnalytics, TransactionId}; use client::TransactionImportResult; @@ -536,7 +534,7 @@ impl Miner { } fn update_gas_limit(&self, chain: &MiningBlockChainClient) { - let gas_limit = HeaderView::new(&chain.best_block_header()).gas_limit(); + let gas_limit = chain.best_block_header().gas_limit(); let mut queue = self.transaction_queue.lock(); queue.set_gas_limit(gas_limit); if let GasLimit::Auto = self.options.tx_queue_gas_limit { @@ -598,7 +596,7 @@ impl Miner { let schedule = chain.latest_schedule(); let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into(); - let best_block_header: Header = ::rlp::decode(&chain.best_block_header()); + let best_block_header = chain.best_block_header().decode(); transactions.into_iter() .map(|tx| { if chain.transaction_block(TransactionId::Hash(tx.hash())).is_some() { @@ -1127,7 +1125,6 @@ impl MinerService for Miner { .map(|hash| { let block = chain.block(BlockId::Hash(*hash)) .expect("Client is sending message after commit to db and inserting to chain; the block is available; qed"); - let block = BlockView::new(&block); let txs = block.transactions(); // populate sender for tx in &txs { diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 557f5160a..56fd09200 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -190,8 +190,7 @@ impl<'a> BlockChunker<'a> { .and_then(|b| self.chain.block_receipts(&self.current_hash).map(|r| (b, r))) .ok_or(Error::BlockNotFound(self.current_hash))?; - let view = BlockView::new(&block); - let abridged_rlp = AbridgedBlock::from_block_view(&view).into_inner(); + let abridged_rlp = AbridgedBlock::from_block_view(&block.view()).into_inner(); let pair = { let mut pair_stream = RlpStream::new_list(2); @@ -213,7 +212,7 @@ impl<'a> BlockChunker<'a> { self.rlps.push_front(pair); last = self.current_hash; - self.current_hash = view.header_view().parent_hash(); + self.current_hash = block.header_view().parent_hash(); } if loaded_size != 0 { diff --git a/ethcore/src/snapshot/watcher.rs b/ethcore/src/snapshot/watcher.rs index 91d94174e..0dda4ba93 100644 --- a/ethcore/src/snapshot/watcher.rs +++ b/ethcore/src/snapshot/watcher.rs @@ -20,7 +20,6 @@ use util::Mutex; use client::{BlockChainClient, Client, ChainNotify}; use ids::BlockId; use service::ClientIoMessage; -use views::HeaderView; use io::IoChannel; use util::{H256, Bytes}; @@ -43,7 +42,7 @@ impl Oracle for StandardOracle where F: Send + Sync + Fn() -> bool { fn to_number(&self, hash: H256) -> Option { - self.client.block_header(BlockId::Hash(hash)).map(|h| HeaderView::new(&h).number()) + self.client.block_header(BlockId::Hash(hash)).map(|h| h.number()) } fn is_major_importing(&self) -> bool { diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index fa5522d7f..c6a9d8157 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -24,7 +24,7 @@ use types::filter::Filter; use util::*; use devtools::*; use miner::Miner; -use rlp::{Rlp, View}; +use rlp::View; use spec::Spec; use views::BlockView; use util::stats::Histogram; @@ -103,7 +103,7 @@ fn imports_good_block() { client.import_verified_blocks(); let block = client.block_header(BlockId::Number(1)).unwrap(); - assert!(!block.is_empty()); + assert!(!block.into_inner().is_empty()); } #[test] @@ -128,7 +128,7 @@ fn query_none_block() { fn query_bad_block() { let client_result = get_test_client_with_blocks(vec![get_bad_state_dummy_block()]); let client = client_result.reference(); - let bad_block:Option = client.block_header(BlockId::Number(1)); + let bad_block: Option<_> = client.block_header(BlockId::Number(1)); assert!(bad_block.is_none()); } @@ -180,7 +180,7 @@ fn returns_block_body() { let client = client_result.reference(); let block = BlockView::new(&dummy_block); let body = client.block_body(BlockId::Hash(block.header().hash())).unwrap(); - let body = Rlp::new(&body); + let body = body.rlp(); assert_eq!(body.item_count(), 2); assert_eq!(body.at(0).as_raw()[..], block.rlp().at(1).as_raw()[..]); assert_eq!(body.at(1).as_raw()[..], block.rlp().at(2).as_raw()[..]); @@ -192,7 +192,7 @@ fn imports_block_sequence() { let client = client_result.reference(); let block = client.block_header(BlockId::Number(5)).unwrap(); - assert!(!block.is_empty()); + assert!(!block.into_inner().is_empty()); } #[test] diff --git a/ethcore/src/types/encoded.rs b/ethcore/src/types/encoded.rs new file mode 100644 index 000000000..41db96fcf --- /dev/null +++ b/ethcore/src/types/encoded.rs @@ -0,0 +1,273 @@ +// Copyright 2015, 2016 Parity Technologies (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 . + +//! Lazily-decoded owning views of RLP-encoded blockchain objects. +//! These views are meant to contain _trusted_ data -- without encoding +//! errors or inconsistencies. +//! +//! In general these views are useful when only a few fields of an object +//! are relevant. In these cases it's more efficient to decode the object piecemeal. +//! When the entirety of the object is needed, it's better to upgrade it to a fully +//! decoded object where parts like the hash can be saved. + +use block::Block as FullBlock; +use header::{BlockNumber, Header as FullHeader}; +use transaction::SignedTransaction; +use views; + +use util::{Address, Hashable, H256, H2048, U256}; +use rlp::{Rlp, View}; + +/// Owning header view. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", binary)] +pub struct Header(Vec); + +impl Header { + /// Create a new owning header view. + /// Expects the data to be an RLP-encoded header -- any other case will likely lead to + /// panics further down the line. + pub fn new(encoded: Vec) -> Self { Header(encoded) } + + /// Upgrade this encoded view to a fully owned `Header` object. + pub fn decode(&self) -> FullHeader { ::rlp::decode(&self.0) } + + /// Get a borrowed header view onto the data. + #[inline] + pub fn view(&self) -> views::HeaderView { views::HeaderView::new(&self.0) } + + /// Get the rlp of the header. + #[inline] + pub fn rlp(&self) -> Rlp { Rlp::new(&self.0) } + + /// Consume the view and return the raw bytes. + pub fn into_inner(self) -> Vec { self.0 } +} + +// forwarders to borrowed view. +impl Header { + /// Returns the header hash. + pub fn hash(&self) -> H256 { self.sha3() } + + /// Returns the parent hash. + pub fn parent_hash(&self) -> H256 { self.view().parent_hash() } + + /// Returns the uncles hash. + pub fn uncles_hash(&self) -> H256 { self.view().uncles_hash() } + + /// Returns the author. + pub fn author(&self) -> Address { self.view().author() } + + /// Returns the state root. + pub fn state_root(&self) -> H256 { self.view().state_root() } + + /// Returns the transaction trie root. + pub fn transactions_root(&self) -> H256 { self.view().transactions_root() } + + /// Returns the receipts trie root + pub fn receipts_root(&self) -> H256 { self.view().receipts_root() } + + /// Returns the block log bloom + pub fn log_bloom(&self) -> H2048 { self.view().log_bloom() } + + /// Difficulty of this block + pub fn difficulty(&self) -> U256 { self.view().difficulty() } + + /// Number of this block. + pub fn number(&self) -> BlockNumber { self.view().number() } + + /// Time this block was produced. + pub fn timestamp(&self) -> u64 { self.view().timestamp() } + + /// Gas limit of this block. + pub fn gas_limit(&self) -> U256 { self.view().gas_limit() } + + /// Total gas used in this block. + pub fn gas_used(&self) -> U256 { self.view().gas_used() } + + /// Block extra data. + pub fn extra_data(&self) -> Vec { self.view().extra_data() } + + /// Engine-specific seal fields. + pub fn seal(&self) -> Vec> { self.view().seal() } +} + +impl Hashable for Header { + fn sha3(&self) -> H256 { + self.0.sha3() + } +} + +/// Owning block body view. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", binary)] +pub struct Body(Vec); + +impl Body { + /// Create a new owning block body view. The raw bytes passed in must be an rlp-encoded block + /// body. + pub fn new(raw: Vec) -> Self { Body(raw) } + + /// Get a borrowed view of the data within. + #[inline] + pub fn view(&self) -> views::BodyView { views::BodyView::new(&self.0) } + + /// Fully decode this block body. + pub fn decode(&self) -> (Vec, Vec) { + (self.view().transactions(), self.view().uncles()) + } + + /// Get the RLP of this block body. + #[inline] + pub fn rlp(&self) -> Rlp { + Rlp::new(&self.0) + } + + /// Consume the view and return the raw bytes. + pub fn into_inner(self) -> Vec { self.0 } +} + +// forwarders to borrowed view. +impl Body { + /// Get a vector of all transactions. + pub fn transactions(&self) -> Vec { self.view().transactions() } + + /// Number of transactions in the block. + pub fn transactions_count(&self) -> usize { self.view().transactions_count() } + + /// A view over each transaction in the block. + pub fn transaction_views(&self) -> Vec { self.view().transaction_views() } + + /// The hash of each transaction in the block. + pub fn transaction_hashes(&self) -> Vec { self.view().transaction_hashes() } + + /// Decode uncle headers. + pub fn uncles(&self) -> Vec { self.view().uncles() } + + /// Number of uncles. + pub fn uncles_count(&self) -> usize { self.view().uncles_count() } + + /// Borrowed view over each uncle. + pub fn uncle_views(&self) -> Vec { self.view().uncle_views() } + + /// Hash of each uncle. + pub fn uncle_hashes(&self) -> Vec { self.view().uncle_hashes() } +} + +/// Owning block view. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", binary)] +pub struct Block(Vec); + +impl Block { + /// Create a new owning block view. The raw bytes passed in must be an rlp-encoded block. + pub fn new(raw: Vec) -> Self { Block(raw) } + + /// Get a borrowed view of the whole block. + #[inline] + pub fn view(&self) -> views::BlockView { views::BlockView::new(&self.0) } + + /// Get a borrowed view of the block header. + #[inline] + pub fn header_view(&self) -> views::HeaderView { self.view().header_view() } + + /// Decode to a full block. + pub fn decode(&self) -> FullBlock { ::rlp::decode(&self.0) } + + /// Get the rlp of this block. + #[inline] + pub fn rlp(&self) -> Rlp { + Rlp::new(&self.0) + } + + /// Consume the view and return the raw bytes. + pub fn into_inner(self) -> Vec { self.0 } +} + +// forwarders to borrowed header view. +impl Block { + /// Returns the header hash. + pub fn hash(&self) -> H256 { self.header_view().sha3() } + + /// Returns the parent hash. + pub fn parent_hash(&self) -> H256 { self.header_view().parent_hash() } + + /// Returns the uncles hash. + pub fn uncles_hash(&self) -> H256 { self.header_view().uncles_hash() } + + /// Returns the author. + pub fn author(&self) -> Address { self.header_view().author() } + + /// Returns the state root. + pub fn state_root(&self) -> H256 { self.header_view().state_root() } + + /// Returns the transaction trie root. + pub fn transactions_root(&self) -> H256 { self.header_view().transactions_root() } + + /// Returns the receipts trie root + pub fn receipts_root(&self) -> H256 { self.header_view().receipts_root() } + + /// Returns the block log bloom + pub fn log_bloom(&self) -> H2048 { self.header_view().log_bloom() } + + /// Difficulty of this block + pub fn difficulty(&self) -> U256 { self.header_view().difficulty() } + + /// Number of this block. + pub fn number(&self) -> BlockNumber { self.header_view().number() } + + /// Time this block was produced. + pub fn timestamp(&self) -> u64 { self.header_view().timestamp() } + + /// Gas limit of this block. + pub fn gas_limit(&self) -> U256 { self.header_view().gas_limit() } + + /// Total gas used in this block. + pub fn gas_used(&self) -> U256 { self.header_view().gas_used() } + + /// Block extra data. + pub fn extra_data(&self) -> Vec { self.header_view().extra_data() } + + /// Engine-specific seal fields. + pub fn seal(&self) -> Vec> { self.header_view().seal() } +} + +// forwarders to body view. +impl Block { + /// Get a vector of all transactions. + pub fn transactions(&self) -> Vec { self.view().transactions() } + + /// Number of transactions in the block. + pub fn transactions_count(&self) -> usize { self.view().transactions_count() } + + /// A view over each transaction in the block. + pub fn transaction_views(&self) -> Vec { self.view().transaction_views() } + + /// The hash of each transaction in the block. + pub fn transaction_hashes(&self) -> Vec { self.view().transaction_hashes() } + + /// Decode uncle headers. + pub fn uncles(&self) -> Vec { self.view().uncles() } + + /// Number of uncles. + pub fn uncles_count(&self) -> usize { self.view().uncles_count() } + + /// Borrowed view over each uncle. + pub fn uncle_views(&self) -> Vec { self.view().uncle_views() } + + /// Hash of each uncle. + pub fn uncle_hashes(&self) -> Vec { self.view().uncle_hashes() } +} diff --git a/ethcore/src/types/mod.rs.in b/ethcore/src/types/mod.rs.in index a5a8fb4ef..567c6fff9 100644 --- a/ethcore/src/types/mod.rs.in +++ b/ethcore/src/types/mod.rs.in @@ -35,4 +35,5 @@ pub mod restoration_status; pub mod snapshot_manifest; pub mod mode; pub mod pruning_info; -pub mod security_level; \ No newline at end of file +pub mod security_level; +pub mod encoded; diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index bbdf3c0c1..2ff4c8963 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -117,7 +117,7 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: & excluded.insert(details.parent.clone()); let b = bc.block(&hash) .expect("parent already known to be stored; qed"); - excluded.extend(BlockView::new(&b).uncle_hashes()); + excluded.extend(b.uncle_hashes()); hash = details.parent; } None => break @@ -264,6 +264,7 @@ mod tests { use types::log_entry::{LogEntry, LocalizedLogEntry}; use rlp::View; use time::get_time; + use encoded; fn check_ok(result: Result<(), Error>) { result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e)); @@ -322,16 +323,20 @@ mod tests { } /// Get raw block data - fn block(&self, hash: &H256) -> Option { - self.blocks.get(hash).cloned() + fn block(&self, hash: &H256) -> Option { + self.blocks.get(hash).cloned().map(encoded::Block::new) } - fn block_header_data(&self, hash: &H256) -> Option { - self.block(hash).map(|b| BlockView::new(&b).header_rlp().as_raw().to_vec()) + fn block_header_data(&self, hash: &H256) -> Option { + self.block(hash) + .map(|b| b.header_view().rlp().as_raw().to_vec()) + .map(encoded::Header::new) } - fn block_body(&self, hash: &H256) -> Option { - self.block(hash).map(|b| BlockChain::block_to_body(&b)) + fn block_body(&self, hash: &H256) -> Option { + self.block(hash) + .map(|b| BlockChain::block_to_body(&b.into_inner())) + .map(encoded::Body::new) } fn best_ancient_block(&self) -> Option { diff --git a/ethcore/src/views/block.rs b/ethcore/src/views/block.rs index ede5b1985..879c2b611 100644 --- a/ethcore/src/views/block.rs +++ b/ethcore/src/views/block.rs @@ -94,7 +94,7 @@ impl<'a> BlockView<'a> { } /// Return List of transactions in given block. - pub fn transaction_views(&self) -> Vec { + pub fn transaction_views(&self) -> Vec> { self.rlp.at(1).iter().map(TransactionView::new_from_rlp).collect() } @@ -132,7 +132,7 @@ impl<'a> BlockView<'a> { } /// Return List of transactions in given block. - pub fn uncle_views(&self) -> Vec { + pub fn uncle_views(&self) -> Vec> { self.rlp.at(2).iter().map(HeaderView::new_from_rlp).collect() } diff --git a/ethcore/src/views/body.rs b/ethcore/src/views/body.rs index c6168f6d8..34ab9679b 100644 --- a/ethcore/src/views/body.rs +++ b/ethcore/src/views/body.rs @@ -71,7 +71,7 @@ impl<'a> BodyView<'a> { } /// Return List of transactions in given block. - pub fn transaction_views(&self) -> Vec { + pub fn transaction_views(&self) -> Vec> { self.rlp.at(0).iter().map(TransactionView::new_from_rlp).collect() } @@ -106,7 +106,7 @@ impl<'a> BodyView<'a> { } /// Return List of transactions in given block. - pub fn uncle_views(&self) -> Vec { + pub fn uncle_views(&self) -> Vec> { self.rlp.at(1).iter().map(HeaderView::new_from_rlp).collect() } diff --git a/parity/blockchain.rs b/parity/blockchain.rs index 0b1af4706..e5a5ddb3f 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -186,16 +186,16 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { // prepare client config let mut client_config = to_client_config( - &cmd.cache_config, - Mode::Active, - tracing, - fat_db, - cmd.compaction, - cmd.wal, - cmd.vm_type, - "".into(), - algorithm, - cmd.pruning_history, + &cmd.cache_config, + Mode::Active, + tracing, + fat_db, + cmd.compaction, + cmd.wal, + cmd.vm_type, + "".into(), + algorithm, + cmd.pruning_history, cmd.check_seal ); @@ -390,7 +390,7 @@ fn execute_export(cmd: ExportBlockchain) -> Result<(), String> { if i % 10000 == 0 { info!("#{}", i); } - let b = client.block(BlockId::Number(i)).ok_or("Error exporting incomplete chain")?; + let b = client.block(BlockId::Number(i)).ok_or("Error exporting incomplete chain")?.into_inner(); match format { DataFormat::Binary => { out.write(&b).expect("Couldn't write to stream."); } DataFormat::Hex => { out.write_fmt(format_args!("{}", b.pretty())).expect("Couldn't write to stream."); } diff --git a/parity/informant.rs b/parity/informant.rs index 0ff13e2dc..ff83ef586 100644 --- a/parity/informant.rs +++ b/parity/informant.rs @@ -27,11 +27,11 @@ use ethsync::{SyncProvider, ManageNetwork}; use util::{Uint, RwLock, Mutex, H256, Colour, Bytes}; use ethcore::client::*; use ethcore::service::ClientIoMessage; -use ethcore::views::BlockView; use ethcore::snapshot::service::Service as SnapshotService; use ethcore::snapshot::{RestorationStatus, SnapshotService as SS}; use number_prefix::{binary_prefix, Standalone, Prefixed}; use ethcore_rpc::is_major_importing; +use rlp::View; pub struct Informant { report: RwLock>, @@ -186,21 +186,19 @@ impl ChainNotify for Informant { let txs_imported = imported.iter() .take(imported.len().saturating_sub(if ripe { 1 } else { 0 })) .filter_map(|h| self.client.block(BlockId::Hash(*h))) - .map(|b| BlockView::new(&b).transactions_count()) + .map(|b| b.transactions_count()) .sum(); if ripe { if let Some(block) = imported.last().and_then(|h| self.client.block(BlockId::Hash(*h))) { - let view = BlockView::new(&block); - let header = view.header(); - let tx_count = view.transactions_count(); - let size = block.len(); + let header_view = block.header_view(); + let size = block.rlp().as_raw().len(); let (skipped, skipped_txs) = (self.skipped.load(AtomicOrdering::Relaxed) + imported.len() - 1, self.skipped_txs.load(AtomicOrdering::Relaxed) + txs_imported); info!(target: "import", "Imported {} {} ({} txs, {} Mgas, {} ms, {} KiB){}", - Colour::White.bold().paint(format!("#{}", header.number())), - Colour::White.bold().paint(format!("{}", header.hash())), - Colour::Yellow.bold().paint(format!("{}", tx_count)), - Colour::Yellow.bold().paint(format!("{:.2}", header.gas_used().low_u64() as f32 / 1000000f32)), + Colour::White.bold().paint(format!("#{}", header_view.number())), + Colour::White.bold().paint(format!("{}", header_view.hash())), + Colour::Yellow.bold().paint(format!("{}", block.transactions_count())), + Colour::Yellow.bold().paint(format!("{:.2}", header_view.gas_used().low_u64() as f32 / 1000000f32)), Colour::Purple.bold().paint(format!("{:.2}", duration as f32 / 1000000f32)), Colour::Blue.bold().paint(format!("{:.2}", size as f32 / 1024f32)), if skipped > 0 { diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 953669089..a11edc84e 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -36,7 +36,6 @@ use ethcore::account_provider::AccountProvider; use ethcore::client::{MiningBlockChainClient, BlockId, TransactionId, UncleId}; use ethcore::header::{Header as BlockHeader, BlockNumber as EthBlockNumber}; use ethcore::block::IsBlock; -use ethcore::views::*; use ethcore::ethereum::Ethash; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, PendingTransaction, Action}; use ethcore::log_entry::LogEntry; @@ -122,13 +121,12 @@ impl EthClient where fn block(&self, id: BlockId, include_txs: bool) -> Result, Error> { let client = take_weak!(self.client); match (client.block(id.clone()), client.block_total_difficulty(id)) { - (Some(bytes), Some(total_difficulty)) => { - let block_view = BlockView::new(&bytes); - let view = block_view.header_view(); - let block = RichBlock { + (Some(block), Some(total_difficulty)) => { + let view = block.header_view(); + Ok(Some(RichBlock { block: Block { hash: Some(view.sha3().into()), - size: Some(bytes.len().into()), + size: Some(block.rlp().as_raw().len().into()), parent_hash: view.parent_hash().into(), uncles_hash: view.uncles_hash().into(), author: view.author().into(), @@ -144,16 +142,15 @@ impl EthClient where difficulty: view.difficulty().into(), total_difficulty: total_difficulty.into(), seal_fields: view.seal().into_iter().map(|f| rlp::decode(&f)).map(Bytes::new).collect(), - uncles: block_view.uncle_hashes().into_iter().map(Into::into).collect(), + uncles: block.uncle_hashes().into_iter().map(Into::into).collect(), transactions: match include_txs { - true => BlockTransactions::Full(block_view.localized_transactions().into_iter().map(Into::into).collect()), - false => BlockTransactions::Hashes(block_view.transaction_hashes().into_iter().map(Into::into).collect()), + true => BlockTransactions::Full(block.view().localized_transactions().into_iter().map(Into::into).collect()), + false => BlockTransactions::Hashes(block.transaction_hashes().into_iter().map(Into::into).collect()), }, extra_data: Bytes::new(view.extra_data()), }, extra_info: client.block_extra_info(id.clone()).expect(EXTRA_INFO_PROOF), - }; - Ok(Some(block)) + })) }, _ => Ok(None) } @@ -169,7 +166,7 @@ impl EthClient where fn uncle(&self, id: UncleId) -> Result, Error> { let client = take_weak!(self.client); let uncle: BlockHeader = match client.uncle(id) { - Some(rlp) => rlp::decode(&rlp), + Some(hdr) => hdr.decode(), None => { return Ok(None); } }; let parent_difficulty = match client.block_total_difficulty(BlockId::Hash(uncle.parent_hash().clone())) { @@ -419,7 +416,7 @@ impl Eth for EthClient where self.active()?; Ok( take_weak!(self.client).block(BlockId::Hash(hash.into())) - .map(|bytes| BlockView::new(&bytes).transactions_count().into()) + .map(|block| block.transactions_count().into()) ) } @@ -432,7 +429,7 @@ impl Eth for EthClient where )), _ => Ok( take_weak!(self.client).block(num.into()) - .map(|bytes| BlockView::new(&bytes).transactions_count().into()) + .map(|block| block.transactions_count().into()) ) } } @@ -442,7 +439,7 @@ impl Eth for EthClient where Ok( take_weak!(self.client).block(BlockId::Hash(hash.into())) - .map(|bytes| BlockView::new(&bytes).uncles_count().into()) + .map(|block| block.uncles_count().into()) ) } @@ -453,7 +450,7 @@ impl Eth for EthClient where BlockNumber::Pending => Ok(Some(0.into())), _ => Ok( take_weak!(self.client).block(num.into()) - .map(|bytes| BlockView::new(&bytes).uncles_count().into()) + .map(|block| block.uncles_count().into()) ), } } diff --git a/sync/src/blocks.rs b/sync/src/blocks.rs index 57d9c7f23..5d6b132df 100644 --- a/sync/src/blocks.rs +++ b/sync/src/blocks.rs @@ -17,7 +17,7 @@ use util::*; use rlp::*; use network::NetworkError; -use ethcore::header::{ Header as BlockHeader}; +use ethcore::header::Header as BlockHeader; known_heap_size!(0, HeaderId); @@ -511,7 +511,9 @@ mod test { let client = TestBlockChainClient::new(); let nblocks = 200; client.add_blocks(nblocks, EachBlockWith::Nothing); - let blocks: Vec<_> = (0 .. nblocks).map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap()).collect(); + let blocks: Vec<_> = (0..nblocks) + .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) + .collect(); let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect(); let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).sha3()).collect(); let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(h.clone()) } else { None }).collect(); @@ -564,7 +566,9 @@ mod test { let client = TestBlockChainClient::new(); let nblocks = 200; client.add_blocks(nblocks, EachBlockWith::Nothing); - let blocks: Vec<_> = (0 .. nblocks).map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap()).collect(); + let blocks: Vec<_> = (0..nblocks) + .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) + .collect(); let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect(); let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).sha3()).collect(); let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(h.clone()) } else { None }).collect(); @@ -586,7 +590,9 @@ mod test { let client = TestBlockChainClient::new(); let nblocks = 200; client.add_blocks(nblocks, EachBlockWith::Nothing); - let blocks: Vec<_> = (0 .. nblocks).map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap()).collect(); + let blocks: Vec<_> = (0..nblocks) + .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) + .collect(); let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect(); let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).sha3()).collect(); let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(h.clone()) } else { None }).collect(); diff --git a/sync/src/chain.rs b/sync/src/chain.rs index b38244dc1..1a4dc4197 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -92,7 +92,6 @@ use util::*; use rlp::*; use network::*; -use ethcore::views::{HeaderView}; use ethcore::header::{BlockNumber, Header as BlockHeader}; use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo, BlockImportError, BlockQueueInfo}; use ethcore::error::*; @@ -1493,14 +1492,15 @@ impl ChainSync { trace!(target: "sync", "{} -> GetBlockHeaders (hash: {}, max: {}, skip: {}, reverse:{})", peer_id, hash, max_headers, skip, reverse); match io.chain().block_header(BlockId::Hash(hash)) { Some(hdr) => { - let number = From::from(HeaderView::new(&hdr).number()); - debug_assert_eq!(HeaderView::new(&hdr).sha3(), hash); + let number = hdr.number().into(); + debug_assert_eq!(hdr.sha3(), hash); + if max_headers == 1 || io.chain().block_hash(BlockId::Number(number)) != Some(hash) { // Non canonical header or single header requested // TODO: handle single-step reverse hashchains of non-canon hashes trace!(target:"sync", "Returning single header: {:?}", hash); let mut rlp = RlpStream::new_list(1); - rlp.append_raw(&hdr, 1); + rlp.append_raw(&hdr.into_inner(), 1); return Ok(Some((BLOCK_HEADERS_PACKET, rlp))); } number @@ -1528,8 +1528,8 @@ impl ChainSync { trace!(target: "sync", "{}: Returning cached fork header", peer_id); data.extend_from_slice(hdr); count += 1; - } else if let Some(mut hdr) = io.chain().block_header(BlockId::Number(number)) { - data.append(&mut hdr); + } else if let Some(hdr) = io.chain().block_header(BlockId::Number(number)) { + data.append(&mut hdr.into_inner()); count += 1; } else { // No required block. @@ -1562,8 +1562,8 @@ impl ChainSync { let mut added = 0usize; let mut data = Bytes::new(); for i in 0..count { - if let Some(mut hdr) = io.chain().block_body(BlockId::Hash(r.val_at::(i)?)) { - data.append(&mut hdr); + if let Some(body) = io.chain().block_body(BlockId::Hash(r.val_at::(i)?)) { + data.append(&mut body.into_inner()); added += 1; } } @@ -1585,8 +1585,8 @@ impl ChainSync { let mut added = 0usize; let mut data = Vec::new(); for i in 0..count { - if let Some(hdr) = io.chain().state_data(&r.val_at::(i)?) { - data.push(hdr); + if let Some(node) = io.chain().state_data(&r.val_at::(i)?) { + data.push(node); added += 1; } } @@ -1819,8 +1819,8 @@ impl ChainSync { let mut rlp_stream = RlpStream::new_list(blocks.len()); for block_hash in blocks { let mut hash_rlp = RlpStream::new_list(2); - let number = HeaderView::new(&chain.block_header(BlockId::Hash(block_hash.clone())) - .expect("chain.tree_route and chain.find_uncles only return hahses of blocks that are in the blockchain. qed.")).number(); + let number = chain.block_header(BlockId::Hash(block_hash.clone())) + .expect("chain.tree_route and chain.find_uncles only return hahses of blocks that are in the blockchain. qed.").number(); hash_rlp.append(&block_hash); hash_rlp.append(&number); rlp_stream.append_raw(hash_rlp.as_raw(), 1); @@ -1844,7 +1844,8 @@ impl ChainSync { /// creates latest block rlp for the given client fn create_latest_block_rlp(chain: &BlockChainClient) -> Bytes { ChainSync::create_block_rlp( - &chain.block(BlockId::Hash(chain.chain_info().best_block_hash)).expect("Best block always exists"), + &chain.block(BlockId::Hash(chain.chain_info().best_block_hash)) + .expect("Best block always exists").into_inner(), chain.chain_info().total_difficulty ) } @@ -1852,7 +1853,7 @@ impl ChainSync { /// creates given hash block rlp for the given client fn create_new_block_rlp(chain: &BlockChainClient, hash: &H256) -> Bytes { ChainSync::create_block_rlp( - &chain.block(BlockId::Hash(hash.clone())).expect("Block has just been sealed; qed"), + &chain.block(BlockId::Hash(hash.clone())).expect("Block has just been sealed; qed").into_inner(), chain.block_total_difficulty(BlockId::Hash(hash.clone())).expect("Block has just been sealed; qed.") ) } @@ -1915,8 +1916,7 @@ impl ChainSync { fn propagate_new_hashes(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo, peers: &[PeerId]) -> usize { trace!(target: "sync", "Sending NewHashes to {:?}", peers); let mut sent = 0; - let last_parent = HeaderView::new(&io.chain().block_header(BlockId::Hash(chain_info.best_block_hash.clone())) - .expect("Best block always exists")).parent_hash(); + let last_parent = &io.chain().best_block_header().parent_hash(); for peer_id in peers { sent += match ChainSync::create_new_hashes_rlp(io.chain(), &last_parent, &chain_info.best_block_hash) { Some(rlp) => { @@ -2099,7 +2099,6 @@ mod tests { use super::*; use ::SyncConfig; use super::{PeerInfo, PeerAsking}; - use ethcore::views::BlockView; use ethcore::header::*; use ethcore::client::*; use ethcore::miner::MinerService; @@ -2253,7 +2252,8 @@ mod tests { let mut client = TestBlockChainClient::new(); client.add_blocks(100, EachBlockWith::Nothing); - let blocks: Vec<_> = (0 .. 100).map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap()).collect(); + let blocks: Vec<_> = (0 .. 100) + .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).map(|b| b.into_inner()).unwrap()).collect(); let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect(); let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).sha3()).collect(); @@ -2444,7 +2444,7 @@ mod tests { let mut client = TestBlockChainClient::new(); client.add_blocks(2, EachBlockWith::Uncle); let queue = RwLock::new(VecDeque::new()); - let block = client.block(BlockId::Latest).unwrap(); + let block = client.block(BlockId::Latest).unwrap().into_inner(); let mut sync = ChainSync::new(SyncConfig::default(), &client); sync.peers.insert(0, PeerInfo { @@ -2722,9 +2722,8 @@ mod tests { // Add some balance to clients and reset nonces for h in &[good_blocks[0], retracted_blocks[0]] { let block = client.block(BlockId::Hash(*h)).unwrap(); - let view = BlockView::new(&block); - client.set_balance(view.transactions()[0].sender().unwrap(), U256::from(1_000_000_000)); - client.set_nonce(view.transactions()[0].sender().unwrap(), U256::from(0)); + client.set_balance(block.transactions()[0].sender().unwrap(), U256::from(1_000_000_000)); + client.set_nonce(block.transactions()[0].sender().unwrap(), U256::from(0)); } @@ -2741,8 +2740,7 @@ mod tests { // We need to update nonce status (because we say that the block has been imported) for h in &[good_blocks[0]] { let block = client.block(BlockId::Hash(*h)).unwrap(); - let view = BlockView::new(&block); - client.set_nonce(view.transactions()[0].sender().unwrap(), U256::from(1)); + client.set_nonce(block.transactions()[0].sender().unwrap(), U256::from(1)); } { let queue = RwLock::new(VecDeque::new());