extras refactored

This commit is contained in:
debris 2015-12-17 01:54:24 +01:00
parent 3862d639bc
commit 2251c469b8
3 changed files with 167 additions and 95 deletions

View File

@ -5,6 +5,7 @@ use util::sha3::*;
use blockheader::*;
use state::*;
use transaction::*;
use extras::*;
/// view onto block rlp
pub struct BlockView<'a> {
@ -12,40 +13,73 @@ pub struct BlockView<'a> {
}
impl<'a> BlockView<'a> {
/// Creates new view onto block from raw bytes
pub fn new(bytes: &'a [u8]) -> BlockView<'a> {
BlockView {
rlp: Rlp::new(bytes)
}
}
/// Creates new view onto block from rlp
pub fn new_from_rlp(rlp: Rlp<'a>) -> BlockView<'a> {
BlockView {
rlp: rlp
}
}
pub fn rlp(&self) -> &Rlp<'a> { &self.rlp }
pub fn header(&self) -> Header { self.rlp.val_at(0) }
/// Return reference to underlaying rlp
pub fn rlp(&self) -> &Rlp<'a> {
&self.rlp
}
/// Create new Header object from header rlp
pub fn header(&self) -> Header {
self.rlp.val_at(0)
}
/// Create new header view obto block head rlp
pub fn header_view(&self) -> HeaderView<'a> {
HeaderView::new_from_rlp(self.rlp.at(0))
}
/// Return List of transactions in given block
pub fn transactions(&self) -> Vec<Transaction> {
self.rlp.val_at(1)
}
/// Return transaction hashes
pub fn transaction_hashes(&self) -> Vec<H256> {
self.rlp.at(1).iter().map(|rlp| rlp.raw().sha3()).collect()
}
/// Return list of uncles of given block
pub fn uncles(&self) -> Vec<Header> {
self.rlp.val_at(2)
}
/// Return list of uncle hashes of given block
pub fn uncle_hashes(&self) -> Vec<H256> {
self.rlp.at(2).iter().map(|rlp| rlp.raw().sha3()).collect()
}
/// Return BlockDetaile object of given block
/// note* children is always an empty vector,
/// cause we can't deducate them from rlp.
pub fn block_details(&self) -> BlockDetails {
let header = self.header_view();
BlockDetails {
number: header.number(),
total_difficulty: header.difficulty(),
parent: header.parent_hash(),
children: vec![]
}
}
}
impl<'a> Hashable for BlockView<'a> {
fn sha3(&self) -> H256 {
self.header_view().sha3()
}
}
/// Active model of a block within the blockchain

View File

@ -8,15 +8,11 @@ use util::hash::*;
use util::uint::*;
use util::rlp::*;
use util::hashdb::*;
use util::overlaydb::*;
use util::sha3::*;
use util::bytes::*;
use util::squeeze::*;
use blockheader::*;
use block::*;
use verifiedblock::*;
use importroute::*;
use account::*;
use genesis::*;
use extras::*;
use transaction::*;
@ -31,24 +27,17 @@ pub struct CacheSize {
}
pub struct BlockChain {
// rlp list of 3
genesis_block: Bytes,
// genesis block header
_genesis_header: Bytes,
genesis_hash: H256,
_genesis_state: HashMap<Address, Account>,
last_block_number: Cell<U256>,
// block cache
blocks: RefCell<HashMap<H256, Bytes>>,
// extra caches
block_details: Extras<H256, BlockDetails>,
block_hashes: Extras<U256, H256>,
transaction_addresses: Extras<H256, TransactionAddress>,
block_logs: Extras<H256, BlockLogBlooms>,
blocks_blooms: Extras<H256, BlocksBlooms>,
block_details: RefCell<HashMap<H256, BlockDetails>>,
block_hashes: RefCell<HashMap<U256, H256>>,
transaction_addresses: RefCell<HashMap<H256, TransactionAddress>>,
block_logs: RefCell<HashMap<H256, BlockLogBlooms>>,
blocks_blooms: RefCell<HashMap<H256, BlocksBlooms>>,
extras_db: DB,
blocks_db: DB
@ -74,24 +63,15 @@ impl BlockChain {
/// let genesis = Genesis::new_frontier();
/// let bc = BlockChain::new(genesis, &dir);
/// let genesis_hash = "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3";
/// assert_eq!(bc.genesis_hash(), &H256::from_str(genesis_hash).unwrap());
/// assert!(bc.is_known(bc.genesis_hash()));
/// assert_eq!(bc.genesis_hash(), &bc.block_hash(&U256::from(0u8)).unwrap());
/// assert_eq!(bc.genesis_hash(), H256::from_str(genesis_hash).unwrap());
/// assert!(bc.is_known(&bc.genesis_hash()));
/// assert_eq!(bc.genesis_hash(), bc.block_hash(&U256::from(0u8)).unwrap());
/// }
/// ```
pub fn new(genesis: Genesis, path: &Path) -> BlockChain {
let (genesis_block, genesis_state) = genesis.drain();
let genesis_header = BlockView::new(&genesis_block).header_view().rlp().raw().to_vec();
let genesis_hash = HeaderView::new(&genesis_header).sha3();
let genesis_details = BlockDetails {
number: U256::from(0u64),
total_difficulty: HeaderView::new(&genesis_header).difficulty(),
parent: H256::new(),
children: vec![]
};
let (genesis_block, _genesis_state) = genesis.drain();
// open dbs
let mut extras_path = path.to_path_buf();
extras_path.push("extras");
let extras_db = DB::open_default(extras_path.to_str().unwrap()).unwrap();
@ -100,34 +80,41 @@ impl BlockChain {
blocks_path.push("blocks");
let blocks_db = DB::open_default(blocks_path.to_str().unwrap()).unwrap();
{
let batch = WriteBatch::new();
batch.put(&genesis_hash.to_extras_slice(ExtrasIndex::BlockDetails), &encode(&genesis_details)).unwrap();
batch.put(&U256::from(0u8).to_extras_slice(ExtrasIndex::BlockHash), &encode(&genesis_hash)).unwrap();
extras_db.write(batch).unwrap();
blocks_db.put(&genesis_hash, &genesis_block).unwrap();
}
BlockChain {
genesis_block: genesis_block,
_genesis_header: genesis_header,
genesis_hash: genesis_hash,
_genesis_state: genesis_state,
let bc = BlockChain {
last_block_number: Cell::new(U256::from(0u8)),
blocks: RefCell::new(HashMap::new()),
block_details: Extras::new(ExtrasIndex::BlockDetails),
block_hashes: Extras::new(ExtrasIndex::BlockHash),
transaction_addresses: Extras::new(ExtrasIndex::TransactionAddress),
block_logs: Extras::new(ExtrasIndex::BlockLogBlooms),
blocks_blooms: Extras::new(ExtrasIndex::BlocksBlooms),
block_details: RefCell::new(HashMap::new()),
block_hashes: RefCell::new(HashMap::new()),
transaction_addresses: RefCell::new(HashMap::new()),
block_logs: RefCell::new(HashMap::new()),
blocks_blooms: RefCell::new(HashMap::new()),
extras_db: extras_db,
blocks_db: blocks_db
}
};
bc.insert_block(&genesis_block);
bc
}
pub fn import_block(&self, _block: &[u8], _db: &OverlayDB) -> ImportRoute {
unimplemented!();
/// Inserts the block into backing cache database.
/// Expects the block to be valid and already verified.
/// If the block is already known, does nothing.
pub fn insert_block(&self, bytes: &[u8]) {
let block = BlockView::new(bytes);
let header = block.header_view();
if self.is_known(&header.sha3()) {
return;
}
let hash = block.sha3();
self.blocks_db.put(&hash, &bytes).unwrap();
let batch = WriteBatch::new();
batch.put_extras(&hash, &block.block_details());
batch.put_extras(&header.number(), &hash);
self.extras_db.write(batch).unwrap();
}
/// Returns true if the given block is known
@ -143,8 +130,8 @@ impl BlockChain {
}
/// Returns reference to genesis hash
pub fn genesis_hash(&self) -> &H256 {
&self.genesis_hash
pub fn genesis_hash(&self) -> H256 {
self.block_hash(&U256::from(0u8)).expect("Genesis hash should always exist")
}
/// Get the partial-header of a block
@ -224,8 +211,8 @@ impl BlockChain {
}
}
fn query_extras<K, T>(&self, hash: &K, cache: &Extras<K, T>) -> Option<T> where
T: Clone + Decodable,
fn query_extras<K, T>(&self, hash: &K, cache: &RefCell<HashMap<K, T>>) -> Option<T> where
T: Clone + Decodable + ExtrasIndexable,
K: ExtrasSliceConvertable + Eq + Hash + Clone {
{
let read = cache.borrow();
@ -235,22 +222,16 @@ impl BlockChain {
}
}
let opt = self.extras_db.get(&hash.to_extras_slice(cache.index()))
.expect("Low level database error. Some issue with disk?");
match opt {
Some(b) => {
let t: T = decode(&b);
self.extras_db.get_extras(hash).map(| t: T | {
let mut write = cache.borrow_mut();
write.insert(hash.clone(), t.clone());
Some(t)
},
None => None
}
t
})
}
fn query_extras_exist<K, T>(&self, hash: &K, cache: &Extras<K, T>) -> bool where
K: ExtrasSliceConvertable + Eq + Hash + Clone {
fn query_extras_exist<K, T>(&self, hash: &K, cache: &RefCell<HashMap<K, T>>) -> bool where
K: ExtrasSliceConvertable + Eq + Hash + Clone,
T: ExtrasIndexable {
{
let read = cache.borrow();
match read.get(hash) {
@ -259,10 +240,7 @@ impl BlockChain {
}
}
let opt = self.extras_db.get(&hash.to_extras_slice(cache.index()))
.expect("Low level database error. Some issue with disk?");
opt.is_some()
self.extras_db.extras_exists::<_, T>(hash)
}
/// Get current cache size

View File

@ -1,13 +1,10 @@
use std::collections::HashMap;
use std::cell::RefCell;
use std::ops::Deref;
use std::hash::Hash;
use heapsize::HeapSizeOf;
use rocksdb::{DB, Writable};
use util::uint::*;
use util::hash::*;
use util::rlp::*;
/// workaround for lack of integer templates in Rust
/// Represents index of extra data in database
#[derive(Copy, Clone)]
pub enum ExtrasIndex {
BlockDetails = 0,
@ -17,26 +14,51 @@ pub enum ExtrasIndex {
BlocksBlooms = 4
}
/// rw locked extra data with slice suffix
// consifer if arc needed here, since blockchain itself will be wrapped
pub struct Extras<K, T>(RefCell<HashMap<K, T>>, ExtrasIndex) where K: Eq + Hash;
impl<K, T> Extras<K, T> where K: Eq + Hash {
pub fn new(i: ExtrasIndex) -> Extras<K, T> {
Extras(RefCell::new(HashMap::new()), i)
}
pub fn index(&self) -> ExtrasIndex { self.1 }
/// trait used to write Extras data to db
pub trait ExtrasWritable {
fn put_extras<K, T>(&self, hash: &K, value: &T) where
T: ExtrasIndexable + Encodable,
K: ExtrasSliceConvertable;
}
impl<K, T> Deref for Extras<K, T> where K : Eq + Hash {
type Target = RefCell<HashMap<K, T>>;
/// trait used to read Extras data from db
pub trait ExtrasReadable {
fn get_extras<K, T>(&self, hash: &K) -> Option<T> where
T: ExtrasIndexable + Decodable,
K: ExtrasSliceConvertable;
fn deref(&self) -> &Self::Target {
&self.0
fn extras_exists<K, T>(&self, hash: &K) -> bool where
T: ExtrasIndexable,
K: ExtrasSliceConvertable;
}
impl<W> ExtrasWritable for W where W: Writable {
fn put_extras<K, T>(&self, hash: &K, value: &T) where
T: ExtrasIndexable + Encodable,
K: ExtrasSliceConvertable {
self.put(&hash.to_extras_slice(T::extras_index()), &encode(value)).unwrap()
}
}
impl ExtrasReadable for DB {
fn get_extras<K, T>(&self, hash: &K) -> Option<T> where
T: ExtrasIndexable + Decodable,
K: ExtrasSliceConvertable {
self.get(&hash.to_extras_slice(T::extras_index())).unwrap()
.map(|v| decode(&v))
}
fn extras_exists<K, T>(&self, hash: &K) -> bool where
T: ExtrasIndexable,
K: ExtrasSliceConvertable {
self.get(&hash.to_extras_slice(T::extras_index())).unwrap().is_some()
}
}
/// Implementations should convert arbitrary type to database key slice
pub trait ExtrasSliceConvertable {
fn to_extras_slice(&self, i: ExtrasIndex) -> H264;
}
@ -55,7 +77,18 @@ impl ExtrasSliceConvertable for U256 {
}
}
/// Types implementing this trait can be indexed in extras database
pub trait ExtrasIndexable {
fn extras_index() -> ExtrasIndex;
}
impl ExtrasIndexable for H256 {
fn extras_index() -> ExtrasIndex {
ExtrasIndex::BlockHash
}
}
/// Familial details concerning a block
#[derive(Clone)]
pub struct BlockDetails {
pub number: U256,
@ -64,6 +97,12 @@ pub struct BlockDetails {
pub children: Vec<H256>
}
impl ExtrasIndexable for BlockDetails {
fn extras_index() -> ExtrasIndex {
ExtrasIndex::BlockDetails
}
}
impl HeapSizeOf for BlockDetails {
fn heap_size_of_children(&self) -> usize {
self.children.heap_size_of_children()
@ -94,11 +133,18 @@ impl Encodable for BlockDetails {
}
}
/// Log blooms of certain block
#[derive(Clone)]
pub struct BlockLogBlooms {
pub blooms: Vec<H2048>
}
impl ExtrasIndexable for BlockLogBlooms {
fn extras_index() -> ExtrasIndex {
ExtrasIndex::BlockLogBlooms
}
}
impl HeapSizeOf for BlockLogBlooms {
fn heap_size_of_children(&self) -> usize {
self.blooms.heap_size_of_children()
@ -121,10 +167,17 @@ impl Encodable for BlockLogBlooms {
}
}
/// Neighboring log blooms on certain level
pub struct BlocksBlooms {
pub blooms: [H2048; 16]
}
impl ExtrasIndexable for BlocksBlooms {
fn extras_index() -> ExtrasIndex {
ExtrasIndex::BlocksBlooms
}
}
impl HeapSizeOf for BlocksBlooms {
fn heap_size_of_children(&self) -> usize { 0 }
}
@ -160,12 +213,19 @@ impl Encodable for BlocksBlooms {
}
}
/// Represents address of certain transaction within block
#[derive(Clone)]
pub struct TransactionAddress {
pub block_hash: H256,
pub index: u64
}
impl ExtrasIndexable for TransactionAddress {
fn extras_index() -> ExtrasIndex {
ExtrasIndex::TransactionAddress
}
}
impl HeapSizeOf for TransactionAddress {
fn heap_size_of_children(&self) -> usize { 0 }
}