Skip to content
This repository was archived by the owner on Nov 6, 2020. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 61 additions & 11 deletions ethcore/src/blockchain/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use types::blockchain_info::BlockChainInfo;
use types::tree_route::TreeRoute;
use blockchain::update::ExtrasUpdate;
use blockchain::{CacheSize, ImportRoute, Config};
use error::MetadataError;
use db::{self, Writable, Readable, CacheUpdatePolicy};
use cache_manager::CacheManager;
use encoded;
Expand Down Expand Up @@ -508,6 +509,8 @@ impl BlockChain {
total_difficulty: header.difficulty(),
parent: header.parent_hash(),
children: vec![],
finalized: false,
metadata: HashMap::new(),
};

let mut batch = DBTransaction::new();
Expand Down Expand Up @@ -647,7 +650,9 @@ impl BlockChain {
/// `None` is returned.
pub fn tree_route(&self, from: H256, to: H256) -> Option<TreeRoute> {
let mut from_branch = vec![];
let mut is_from_route_finalized = false;
let mut to_branch = vec![];
let mut is_to_route_finalized = false;

let mut from_details = self.block_details(&from)?;
let mut to_details = self.block_details(&to)?;
Expand All @@ -659,12 +664,14 @@ impl BlockChain {
from_branch.push(current_from);
current_from = from_details.parent.clone();
from_details = self.block_details(&from_details.parent)?;
is_from_route_finalized = is_from_route_finalized || from_details.finalized;
}

while to_details.number > from_details.number {
to_branch.push(current_to);
current_to = to_details.parent.clone();
to_details = self.block_details(&to_details.parent)?;
is_to_route_finalized = is_to_route_finalized || to_details.finalized;
}

assert_eq!(from_details.number, to_details.number);
Expand All @@ -674,10 +681,12 @@ impl BlockChain {
from_branch.push(current_from);
current_from = from_details.parent.clone();
from_details = self.block_details(&from_details.parent)?;
is_from_route_finalized = is_from_route_finalized || from_details.finalized;

to_branch.push(current_to);
current_to = to_details.parent.clone();
to_details = self.block_details(&to_details.parent)?;
is_to_route_finalized = is_to_route_finalized || to_details.finalized;
}

let index = from_branch.len();
Expand All @@ -687,7 +696,9 @@ impl BlockChain {
Some(TreeRoute {
blocks: from_branch,
ancestor: current_from,
index: index
index: index,
is_from_route_finalized: is_from_route_finalized,
is_to_route_finalized: is_to_route_finalized,
})
}

Expand Down Expand Up @@ -772,6 +783,8 @@ impl BlockChain {
total_difficulty: info.total_difficulty,
parent: header.parent_hash(),
children: Vec::new(),
finalized: false,
metadata: HashMap::new(),
};

let mut update = HashMap::new();
Expand Down Expand Up @@ -961,16 +974,20 @@ impl BlockChain {

assert_eq!(number, parent_details.number + 1);

match route.blocks.len() {
0 => BlockLocation::CanonChain,
_ => {
let retracted = route.blocks.iter().take(route.index).cloned().collect::<Vec<_>>().into_iter().collect::<Vec<_>>();
let enacted = route.blocks.into_iter().skip(route.index).collect::<Vec<_>>();
BlockLocation::BranchBecomingCanonChain(BranchBecomingCanonChainData {
ancestor: route.ancestor,
enacted: enacted,
retracted: retracted,
})
if route.is_from_route_finalized {
BlockLocation::Branch
} else {
match route.blocks.len() {
0 => BlockLocation::CanonChain,
_ => {
let retracted = route.blocks.iter().take(route.index).cloned().collect::<Vec<_>>().into_iter().collect::<Vec<_>>();
let enacted = route.blocks.into_iter().skip(route.index).collect::<Vec<_>>();
BlockLocation::BranchBecomingCanonChain(BranchBecomingCanonChainData {
ancestor: route.ancestor,
enacted: enacted,
retracted: retracted,
})
}
}
}
} else {
Expand All @@ -979,6 +996,37 @@ impl BlockChain {
}
}

/// Mark a block to be considered finalized.
pub fn mark_finalized(&self, batch: &mut DBTransaction, block_hash: H256) -> Result<(), MetadataError> {
let mut block_details = self.block_details(&block_hash).ok_or(MetadataError::UnknownBlock)?;
block_details.finalized = true;

self.update_block_details(batch, block_hash, block_details);
Ok(())
}

/// Update metadata detail for an existing block.
pub fn update_metadata<T: Into<HashMap<Bytes, Bytes>>>(&self, batch: &mut DBTransaction, block_hash: H256, metadata: T) -> Result<(), MetadataError> {
let mut block_details = self.block_details(&block_hash).ok_or(MetadataError::UnknownBlock)?;
let metadata: HashMap<Bytes, Bytes> = metadata.into();
for (key, value) in metadata {
block_details.metadata.insert(key, value);
}

self.update_block_details(batch, block_hash, block_details);
Ok(())
}

/// Prepares extras block detail update.
fn update_block_details(&self, batch: &mut DBTransaction, block_hash: H256, block_details: BlockDetails) {
let mut details_map = HashMap::new();
details_map.insert(block_hash, block_details);

// We're only updating one existing value. So it shouldn't suffer from cache decoherence problem.
let mut write_details = self.pending_block_details.write();
batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, details_map, CacheUpdatePolicy::Overwrite);
}

/// Prepares extras update.
fn prepare_update(&self, batch: &mut DBTransaction, update: ExtrasUpdate, is_best: bool) {

Expand Down Expand Up @@ -1179,6 +1227,8 @@ impl BlockChain {
total_difficulty: info.total_difficulty,
parent: parent_hash,
children: vec![],
finalized: false,
metadata: HashMap::new(),
};

// write to batch
Expand Down
71 changes: 70 additions & 1 deletion ethcore/src/blockchain/extras.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@

use std::ops;
use std::io::Write;
use std::collections::HashMap;
use blooms::{GroupPosition, BloomGroup};
use db::Key;
use engines::epoch::{Transition as EpochTransition};
use header::BlockNumber;
use receipt::Receipt;
use rlp;
use bytes::Bytes;

use heapsize::HeapSizeOf;
use ethereum_types::{H256, H264, U256};
Expand Down Expand Up @@ -167,7 +170,7 @@ impl Key<EpochTransitions> for u64 {
}

/// Familial details concerning a block
#[derive(Debug, Clone, RlpEncodable, RlpDecodable)]
#[derive(Debug, Clone)]
pub struct BlockDetails {
/// Block number
pub number: BlockNumber,
Expand All @@ -177,6 +180,63 @@ pub struct BlockDetails {
pub parent: H256,
/// List of children block hashes
pub children: Vec<H256>,
/// Whether the block is considered finalized
pub finalized: bool,
/// Metadata information
pub metadata: HashMap<Bytes, Bytes>,
}

impl rlp::Encodable for BlockDetails {
fn rlp_append(&self, stream: &mut rlp::RlpStream) {
let use_short_version = self.metadata.len() == 0 && !self.finalized;

match use_short_version {
true => { stream.begin_list(4); },
false => { stream.begin_list(6); },
}

stream.append(&self.number);
stream.append(&self.total_difficulty);
stream.append(&self.parent);
stream.append_list(&self.children);
if !use_short_version {
stream.append(&self.finalized);

let metadata: Vec<BlockMetadata> = self.metadata.clone().into_iter().map(|(key, value)| {
BlockMetadata { key, value }
}).collect();
stream.append_list(&metadata);
}
}
}

impl rlp::Decodable for BlockDetails {
fn decode(rlp: &rlp::UntrustedRlp) -> Result<Self, rlp::DecoderError> {
let use_short_version = match rlp.item_count()? {
4 => true,
6 => false,
_ => return Err(rlp::DecoderError::RlpIncorrectListLen),
};

Ok(BlockDetails {
number: rlp.val_at(0)?,
total_difficulty: rlp.val_at(1)?,
parent: rlp.val_at(2)?,
children: rlp.list_at(3)?,
finalized: if use_short_version {
false
} else {
rlp.val_at(4)?
},
metadata: if use_short_version {
HashMap::new()
} else {
let metadatas: Vec<BlockMetadata> = rlp.list_at(5)?;

metadatas.into_iter().map(|metadata| (metadata.key, metadata.value)).collect()
},
})
}
}

impl HeapSizeOf for BlockDetails {
Expand All @@ -185,6 +245,15 @@ impl HeapSizeOf for BlockDetails {
}
}

/// Metadata key and value
#[derive(Debug, Clone, RlpEncodable, RlpDecodable)]
struct BlockMetadata {
/// Key of the metadata
pub key: Bytes,
/// Value of the metadata
pub value: Bytes,
}

/// Represents address of certain transaction within block
#[derive(Debug, PartialEq, Clone, RlpEncodable, RlpDecodable)]
pub struct TransactionAddress {
Expand Down
4 changes: 3 additions & 1 deletion ethcore/src/client/test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,9 @@ impl BlockChainClient for TestBlockChainClient {
}
}
if adding { Vec::new() } else { blocks }
}
},
is_from_route_finalized: false,
is_to_route_finalized: false,
})
}

Expand Down
26 changes: 26 additions & 0 deletions ethcore/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,23 @@ impl fmt::Display for BlockError {
}
}

#[derive(Debug, Clone, Copy, PartialEq)]
/// Errors related to metadata operations
pub enum MetadataError {
/// The metadata block trying to set is unknown.
UnknownBlock,
}

impl fmt::Display for MetadataError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let msg = match *self {
MetadataError::UnknownBlock => "unknown block",
};

f.write_fmt(format_args!("Block metadata error ({})", msg))
}
}

#[derive(Debug, Clone, Copy, PartialEq)]
/// Import to the block queue result
pub enum ImportError {
Expand Down Expand Up @@ -247,6 +264,8 @@ pub enum Error {
Ethkey(EthkeyError),
/// Account Provider error.
AccountProvider(AccountsError),
/// Block metadata error.
Metadata(MetadataError),
}

impl fmt::Display for Error {
Expand All @@ -271,6 +290,7 @@ impl fmt::Display for Error {
Error::Engine(ref err) => err.fmt(f),
Error::Ethkey(ref err) => err.fmt(f),
Error::AccountProvider(ref err) => err.fmt(f),
Error::Metadata(ref err) => err.fmt(f),
}
}
}
Expand All @@ -285,6 +305,12 @@ impl error::Error for Error {
/// Result of import block operation.
pub type ImportResult = Result<H256, Error>;

impl From<MetadataError> for Error {
fn from(err: MetadataError) -> Error {
Error::Metadata(err)
}
}

impl From<ClientError> for Error {
fn from(err: ClientError) -> Error {
match err {
Expand Down
2 changes: 2 additions & 0 deletions ethcore/src/verification/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,8 @@ mod tests {
total_difficulty: header.difficulty().clone(),
parent: header.parent_hash().clone(),
children: Vec::new(),
finalized: false,
metadata: Default::default(),
}
})
}
Expand Down
4 changes: 4 additions & 0 deletions ethcore/types/src/tree_route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ pub struct TreeRoute {
pub ancestor: H256,
/// An index where best common ancestor would be.
pub index: usize,
/// Whether it has finalized blocks from `from` (inclusive) to `ancestor` (exclusive).
pub is_from_route_finalized: bool,
/// Whether it has finalized blocks from `ancestor` (exclusive) to `to` (inclusive).
pub is_to_route_finalized: bool,
}