blockchain.rs 80.5 KiB
Newer Older
Gav Wood's avatar
Gav Wood committed
// Copyright 2015-2017 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 <http://www.gnu.org/licenses/>.

//! Blockchain database.
Marek Kotewicz's avatar
Marek Kotewicz committed
use std::collections::{HashMap, HashSet};
Marek Kotewicz's avatar
Marek Kotewicz committed
use std::sync::Arc;
Marek Kotewicz's avatar
Marek Kotewicz committed
use std::mem;
Marek Kotewicz's avatar
Marek Kotewicz committed
use bloomchain as bc;
use heapsize::HeapSizeOf;
Marek Kotewicz's avatar
Marek Kotewicz committed
use ethereum_types::{H256, Bloom, U256};
use parking_lot::{Mutex, RwLock};
asynchronous rob's avatar
asynchronous rob committed
use rlp::*;
use header::*;
Marek Kotewicz's avatar
Marek Kotewicz committed
use super::extras::*;
use transaction::*;
Marek Kotewicz's avatar
Marek Kotewicz committed
use views::*;
use log_entry::{LogEntry, LocalizedLogEntry};
Marek Kotewicz's avatar
Marek Kotewicz committed
use blooms::BloomGroup;
Nikolay Volf's avatar
Nikolay Volf committed
use blockchain::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData};
use blockchain::best_block::{BestBlock, BestAncientBlock};
use types::blockchain_info::BlockChainInfo;
use blockchain::update::ExtrasUpdate;
Marek Kotewicz's avatar
Marek Kotewicz committed
use blockchain::{CacheSize, ImportRoute, Config};
use db::{self, Writable, Readable, CacheUpdatePolicy};
use cache_manager::CacheManager;
use engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition};
use rayon::prelude::*;
use ansi_term::Colour;
use kvdb::{DBTransaction, KeyValueDB};
Marek Kotewicz's avatar
Marek Kotewicz committed
const LOG_BLOOMS_LEVELS: usize = 3;
const LOG_BLOOMS_ELEMENTS_PER_INDEX: usize = 16;
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
/// Interface for querying blocks by hash and by number.
pub trait BlockProvider {
	/// Returns true if the given block is known
	/// (though not necessarily a part of the canon chain).
	fn is_known(&self, hash: &H256) -> bool;

	/// Get the first block of the best part of the chain.
	/// Return `None` if there is no gap and the first block is the genesis.
	/// Any queries of blocks which precede this one are not guaranteed to
	/// succeed.
	fn first_block(&self) -> Option<H256>;

	/// Get the number of the first block.
	fn first_block_number(&self) -> Option<BlockNumber> {
		self.first_block().map(|b| self.block_number(&b).expect("First block is always set to an existing block or `None`. Existing block always has a number; qed"))
	/// Get the best block of an first block sequence if there is a gap.
	fn best_ancient_block(&self) -> Option<H256>;

	/// Get the number of the first block.
	fn best_ancient_number(&self) -> Option<BlockNumber> {
		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"))
	}
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	/// Get raw block data
	fn block(&self, hash: &H256) -> Option<encoded::Block>;
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed

	/// Get the familial details concerning a block.
	fn block_details(&self, hash: &H256) -> Option<BlockDetails>;

	/// Get the hash of given block's number.
	fn block_hash(&self, index: BlockNumber) -> Option<H256>;

	/// Get the address of transaction with given hash.
	fn transaction_address(&self, hash: &H256) -> Option<TransactionAddress>;

	/// Get receipts of block with given hash.
	fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts>;

Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	/// Get the partial-header of a block.
	fn block_header(&self, hash: &H256) -> Option<Header> {
		self.block_header_data(hash).map(|header| header.decode())
Tomasz Drwięga's avatar
Tomasz Drwięga committed
	/// Get the header RLP of a block.
	fn block_header_data(&self, hash: &H256) -> Option<encoded::Header>;
Tomasz Drwięga's avatar
Tomasz Drwięga committed

	/// Get the block body (uncles and transactions).
	fn block_body(&self, hash: &H256) -> Option<encoded::Body>;
Tomasz Drwięga's avatar
Tomasz Drwięga committed

Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	/// Get a list of uncles for a given block.
Gav Wood's avatar
Gav Wood committed
	/// Returns None if block does not exist.
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	fn uncles(&self, hash: &H256) -> Option<Vec<Header>> {
		self.block_body(hash).map(|body| body.uncles())
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	}

	/// Get a list of uncle hashes for a given block.
	/// Returns None if block does not exist.
	fn uncle_hashes(&self, hash: &H256) -> Option<Vec<H256>> {
		self.block_body(hash).map(|body| body.uncle_hashes())
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	}

	/// Get the number of given block's hash.
	fn block_number(&self, hash: &H256) -> Option<BlockNumber> {
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		self.block_details(hash).map(|details| details.number)
	/// Get transaction with given transaction hash.
	fn transaction(&self, address: &TransactionAddress) -> Option<LocalizedTransaction> {
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		self.block_body(&address.block_hash)
			.and_then(|body| self.block_number(&address.block_hash)
			.and_then(|n| body.view().localized_transaction_at(&address.block_hash, n, address.index)))
Marek Kotewicz's avatar
Marek Kotewicz committed
	/// Get transaction receipt.
	fn transaction_receipt(&self, address: &TransactionAddress) -> Option<Receipt> {
		self.block_receipts(&address.block_hash).and_then(|br| br.receipts.into_iter().nth(address.index))
	}

Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	/// Get a list of transactions for a given block.
	/// Returns None if block does not exist.
Marek Kotewicz's avatar
Marek Kotewicz committed
	fn transactions(&self, hash: &H256) -> Option<Vec<LocalizedTransaction>> {
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		self.block_body(hash)
			.and_then(|body| self.block_number(hash)
			.map(|n| body.view().localized_transactions(hash, n)))
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	}

	/// Returns reference to genesis hash.
	fn genesis_hash(&self) -> H256 {
		self.block_hash(0).expect("Genesis hash should always exist")
	}

	/// Returns the header of the genesis block.
	fn genesis_header(&self) -> Header {
		self.block_header(&self.genesis_hash())
			.expect("Genesis header always stored; qed")

	/// Returns numbers of blocks containing given bloom.
Marek Kotewicz's avatar
Marek Kotewicz committed
	fn blocks_with_bloom(&self, bloom: &Bloom, from_block: BlockNumber, to_block: BlockNumber) -> Vec<BlockNumber>;

	/// Returns logs matching given filter.
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	fn logs<F>(&self, blocks: Vec<BlockNumber>, matches: F, limit: Option<usize>) -> Vec<LocalizedLogEntry>
		where F: Fn(&LogEntry) -> bool + Send + Sync, Self: Sized;
macro_rules! otry {
	($e:expr) => { match $e { Some(x) => x, None => return None } }
}

#[derive(Debug, Hash, Eq, PartialEq, Clone)]
enum CacheId {
Tomasz Drwięga's avatar
Tomasz Drwięga committed
	BlockHeader(H256),
	BlockBody(H256),
Marek Kotewicz's avatar
Marek Kotewicz committed
	BlockDetails(H256),
	BlockHashes(BlockNumber),
	TransactionAddresses(H256),
	BlocksBlooms(LogGroupPosition),
	BlockReceipts(H256),
Marek Kotewicz's avatar
Marek Kotewicz committed
impl bc::group::BloomGroupDatabase for BlockChain {
	fn blooms_at(&self, position: &bc::group::GroupPosition) -> Option<bc::group::BloomGroup> {
		let position = LogGroupPosition::from(position.clone());
		let result = self.db.read_with_cache(db::COL_EXTRA, &self.blocks_blooms, &position).map(Into::into);
		self.cache_man.lock().note_used(CacheId::BlocksBlooms(position));
/// Structure providing fast access to blockchain data.
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
///
Marek Kotewicz's avatar
Marek Kotewicz committed
/// **Does not do input data verification.**
Marek Kotewicz's avatar
Marek Kotewicz committed
pub struct BlockChain {
	// All locks must be captured in the order declared here.
Marek Kotewicz's avatar
Marek Kotewicz committed
	blooms_config: bc::Config,
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	best_block: RwLock<BestBlock>,
	// Stores best block of the first uninterrupted sequence of blocks. `None` if there are no gaps.
	// Only updated with `insert_unordered_block`.
	best_ancient_block: RwLock<Option<BestAncientBlock>>,
	// Stores the last block of the last sequence of blocks. `None` if there are no gaps.
	// This is calculated on start and does not get updated.
	first_block: Option<H256>,
Marek Kotewicz's avatar
Marek Kotewicz committed

Marek Kotewicz's avatar
Marek Kotewicz committed
	// block cache
Tomasz Drwięga's avatar
Tomasz Drwięga committed
	block_headers: RwLock<HashMap<H256, Bytes>>,
	block_bodies: RwLock<HashMap<H256, Bytes>>,
Loading full blame...