blockchain.rs 44.9 KiB
Newer Older
// Copyright 2015, 2016 Ethcore (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.
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
use header::*;
Marek Kotewicz's avatar
Marek Kotewicz committed
use extras::*;
use transaction::*;
Marek Kotewicz's avatar
Marek Kotewicz committed
use views::*;
use chainfilter::{ChainFilter, BloomIndex, FilterDataSource};
Nikolay Volf's avatar
Nikolay Volf committed
use blockchain::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData};
use blockchain::best_block::BestBlock;
use blockchain::bloom_indexer::BloomIndexer;
use blockchain::update::ExtrasUpdate;
Marek Kotewicz's avatar
Marek Kotewicz committed
use blockchain::{CacheSize, ImportRoute};
use db::{Writable, Readable, Key, CacheUpdatePolicy};

const BLOOM_INDEX_SIZE: usize = 16;
const BLOOM_LEVELS: u8 = 3;
Marek Kotewicz's avatar
Marek Kotewicz committed

Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
/// Blockchain configuration.
#[derive(Debug)]
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
pub struct BlockChainConfig {
	/// Preferred cache size in bytes.
	pub pref_cache_size: usize,
	/// Maximum cache size in bytes.
	pub max_cache_size: usize,
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
impl Default for BlockChainConfig {
	fn default() -> Self {
		BlockChainConfig {
			pref_cache_size: 1 << 14,
			max_cache_size: 1 << 20,
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 raw block data
	fn block(&self, hash: &H256) -> Option<Bytes>;

	/// 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(hash).map(|bytes| BlockView::new(&bytes).header())
	}

	/// 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(hash).map(|bytes| BlockView::new(&bytes).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<Vec<H256>> {
		self.block(hash).map(|bytes| BlockView::new(&bytes).uncle_hashes())
	}

	/// Get the number of given block's hash.
	fn block_number(&self, hash: &H256) -> Option<BlockNumber> {
		self.block(hash).map(|bytes| BlockView::new(&bytes).header_view().number())
	}

	/// Get transaction with given transaction hash.
	fn transaction(&self, address: &TransactionAddress) -> Option<LocalizedTransaction> {
		self.block(&address.block_hash).and_then(|bytes| BlockView::new(&bytes).localized_transaction_at(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>> {
		self.block(hash).map(|bytes| BlockView::new(&bytes).localized_transactions())
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()).unwrap()
	}

	/// Returns numbers of blocks containing given bloom.
	fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockNumber, to_block: BlockNumber) -> Vec<BlockNumber>;
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
Gav Wood's avatar
Gav Wood committed
enum CacheID {
	Block(H256),
	Extras(ExtrasIndex, H256),
}

struct CacheManager {
	cache_usage: VecDeque<HashSet<CacheID>>,
	in_use: HashSet<CacheID>,
}

/// 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.
	pref_cache_size: AtomicUsize,
	max_cache_size: AtomicUsize,
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	best_block: RwLock<BestBlock>,
Marek Kotewicz's avatar
Marek Kotewicz committed

Marek Kotewicz's avatar
Marek Kotewicz committed
	// block cache
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	blocks: RwLock<HashMap<H256, Bytes>>,
Marek Kotewicz's avatar
Marek Kotewicz committed
	// extra caches
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	block_details: RwLock<HashMap<H256, BlockDetails>>,
	block_hashes: RwLock<HashMap<BlockNumber, H256>>,
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	transaction_addresses: RwLock<HashMap<H256, TransactionAddress>>,
	block_logs: RwLock<HashMap<H256, BlockLogBlooms>>,
	blocks_blooms: RwLock<HashMap<H256, BlocksBlooms>>,
	block_receipts: RwLock<HashMap<H256, BlockReceipts>>,
Marek Kotewicz's avatar
Marek Kotewicz committed

	extras_db: Database,
	blocks_db: Database,

	cache_man: RwLock<CacheManager>,
Marek Kotewicz's avatar
Marek Kotewicz committed
	bloom_indexer: BloomIndexer,
	insert_lock: Mutex<()>
Marek Kotewicz's avatar
Marek Kotewicz committed
}

impl FilterDataSource for BlockChain {
	fn bloom_at_index(&self, bloom_index: &BloomIndex) -> Option<H2048> {
		let location = self.bloom_indexer.location(bloom_index);
		self.blocks_blooms(&location.hash).and_then(|blooms| blooms.blooms.into_iter().nth(location.index).cloned())
	}
Marek Kotewicz's avatar
Marek Kotewicz committed
}

Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
impl BlockProvider for BlockChain {
	/// Returns true if the given block is known
	/// (though not necessarily a part of the canon chain).
	fn is_known(&self, hash: &H256) -> bool {
		self.extras_db.exists_with_cache(&self.block_details, hash)
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	}

	/// Get raw block data
	fn block(&self, hash: &H256) -> Option<Bytes> {
		{
			let read = self.blocks.read().unwrap();
			if let Some(v) = read.get(hash) {
				return Some(v.clone());
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
			}
		}

		let opt = self.blocks_db.get(hash)
			.expect("Low level database error. Some issue with disk?");

Gav Wood's avatar
Gav Wood committed
		self.note_used(CacheID::Block(hash.clone()));

Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		match opt {
Loading full blame...