Fork choice and metadata framework for Engine (#8401)
* Add light client TODO item * Move existing total-difficulty-based fork choice check to Engine * Abstract total difficulty and block provider as Machine::BlockMetadata and Machine::BlockProvider * Decouple "generate_metadata" logic to Engine * Use fixed BlockMetadata and BlockProvider type for null and instantseal In this way they can use total difficulty fork choice check * Extend blockdetails with metadatas and finalized info * Extra data update: mark_finalized and update_metadatas * Check finalized block in Blockchain * Fix a test constructor in verification mod * Add total difficulty trait * Fix type import * Db migration to V13 with metadata column * Address grumbles * metadatas -> metadata * Use generic type for update_metadata to avoid passing HashMap all around * Remove metadata in blockdetails * [WIP] Implement a generic metadata architecture * [WIP] Metadata insertion logic in BlockChain * typo: Value -> Self::Value * [WIP] Temporarily remove Engine::is_new_best interface So that we don't have too many type errors. * [WIP] Fix more type errors * [WIP] ExtendedHeader::PartialEq * [WIP] Change metadata type Option<Vec<u8>> to Vec<u8> * [WIP] Remove Metadata Error * [WIP] Clean up error conversion * [WIP] finalized -> is_finalized * [WIP] Mark all fields in ExtrasInsert as pub * [WIP] Remove unused import * [WIP] Keep only local metadata info * Mark metadata as optional * [WIP] Revert metadata db change in BlockChain * [WIP] Put finalization in unclosed state * Use metadata interface in BlockDetail * [WIP] Fix current build failures * [WIP] Remove unused blockmetadata struct * Remove DB migration info * [WIP] Typo * Use ExtendedHeader to implement fork choice check * Implement is_new_best using Ancestry iterator * Use expect instead of panic * [WIP] Add ancestry Engine support via on_new_block * Fix tests * Emission of ancestry actions * use_short_version should take account of metadata * Engine::is_new_best -> Engine::fork_choice * Use proper expect format as defined in #1026 * panic -> expect * ancestry_header -> ancestry_with_metadata * Boxed iterator -> &mut iterator * Fix tests * is_new_best -> primitive_fork_choice * Document how fork_choice works * Engine::fork_choice -> Engine::primitive_fork_choice * comment: clarify types of finalization where Engine::primitive_fork_choice works * Expose FinalizationInfo to Engine * Fix tests due to merging * Remove TotalDifficulty trait * Do not pass FinalizationInfo to Engine If there's finalized blocks in from route, choose the old branch without calling `Engine::fork_choice`. * Fix compile * Fix unused import * Remove is_to_route_finalized When no block reorg passes a finalized block, this variable is always false. * Address format grumbles * Fix docs: mark_finalized returns None if block hash is not found `blockchain` mod does not yet have an Error type, so we still temporarily use None here. * Fix inaccurate tree_route None expect description
This commit is contained in:
parent
3bb5ad7204
commit
0ecbb3ec02
@ -445,6 +445,7 @@ impl HeaderChain {
|
||||
let raw = header.encoded().into_inner();
|
||||
transaction.put_vec(self.col, &hash[..], raw);
|
||||
|
||||
// TODO: For engines when required, use cryptoeconomic guarantees.
|
||||
let (best_num, is_new_best) = {
|
||||
let cur_best = self.best_block.read();
|
||||
if cur_best.total_difficulty < total_difficulty {
|
||||
|
@ -31,7 +31,7 @@ use vm::{EnvInfo, LastHashes};
|
||||
use engines::EthEngine;
|
||||
use error::{Error, BlockError};
|
||||
use factory::Factories;
|
||||
use header::Header;
|
||||
use header::{Header, ExtendedHeader};
|
||||
use receipt::{Receipt, TransactionOutcome};
|
||||
use state::State;
|
||||
use state_db::StateDB;
|
||||
@ -94,6 +94,8 @@ pub struct ExecutedBlock {
|
||||
state: State<StateDB>,
|
||||
traces: Tracing,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
is_finalized: bool,
|
||||
metadata: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl ExecutedBlock {
|
||||
@ -112,6 +114,8 @@ impl ExecutedBlock {
|
||||
Tracing::Disabled
|
||||
},
|
||||
last_hashes: last_hashes,
|
||||
is_finalized: false,
|
||||
metadata: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,6 +210,26 @@ impl ::parity_machine::Transactions for ExecutedBlock {
|
||||
}
|
||||
}
|
||||
|
||||
impl ::parity_machine::Finalizable for ExecutedBlock {
|
||||
fn is_finalized(&self) -> bool {
|
||||
self.is_finalized
|
||||
}
|
||||
|
||||
fn mark_finalized(&mut self) {
|
||||
self.is_finalized = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl ::parity_machine::WithMetadata for ExecutedBlock {
|
||||
fn metadata(&self) -> Option<&[u8]> {
|
||||
self.metadata.as_ref().map(|v| v.as_ref())
|
||||
}
|
||||
|
||||
fn set_metadata(&mut self, value: Option<Vec<u8>>) {
|
||||
self.metadata = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Block that is ready for transactions to be added.
|
||||
///
|
||||
/// It's a bit like a Vec<Transaction>, except that whenever a transaction is pushed, we execute it and
|
||||
@ -224,6 +248,8 @@ pub struct ClosedBlock {
|
||||
block: ExecutedBlock,
|
||||
uncle_bytes: Bytes,
|
||||
unclosed_state: State<StateDB>,
|
||||
unclosed_finalization_state: bool,
|
||||
unclosed_metadata: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
/// Just like `ClosedBlock` except that we can't reopen it and it's faster.
|
||||
@ -245,7 +271,7 @@ pub struct SealedBlock {
|
||||
|
||||
impl<'x> OpenBlock<'x> {
|
||||
/// Create a new `OpenBlock` ready for transaction pushing.
|
||||
pub fn new(
|
||||
pub fn new<'a>(
|
||||
engine: &'x EthEngine,
|
||||
factories: Factories,
|
||||
tracing: bool,
|
||||
@ -256,6 +282,7 @@ impl<'x> OpenBlock<'x> {
|
||||
gas_range_target: (U256, U256),
|
||||
extra_data: Bytes,
|
||||
is_epoch_begin: bool,
|
||||
ancestry: &mut Iterator<Item=ExtendedHeader>,
|
||||
) -> Result<Self, Error> {
|
||||
let number = parent.number() + 1;
|
||||
let state = State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(number), factories)?;
|
||||
@ -277,7 +304,7 @@ impl<'x> OpenBlock<'x> {
|
||||
engine.populate_from_parent(&mut r.block.header, parent);
|
||||
|
||||
engine.machine().on_new_block(&mut r.block)?;
|
||||
engine.on_new_block(&mut r.block, is_epoch_begin)?;
|
||||
engine.on_new_block(&mut r.block, is_epoch_begin, ancestry)?;
|
||||
|
||||
Ok(r)
|
||||
}
|
||||
@ -387,6 +414,8 @@ impl<'x> OpenBlock<'x> {
|
||||
let mut s = self;
|
||||
|
||||
let unclosed_state = s.block.state.clone();
|
||||
let unclosed_metadata = s.block.metadata.clone();
|
||||
let unclosed_finalization_state = s.block.is_finalized;
|
||||
|
||||
if let Err(e) = s.engine.on_close_block(&mut s.block) {
|
||||
warn!("Encountered error on closing the block: {}", e);
|
||||
@ -410,6 +439,8 @@ impl<'x> OpenBlock<'x> {
|
||||
block: s.block,
|
||||
uncle_bytes,
|
||||
unclosed_state,
|
||||
unclosed_metadata,
|
||||
unclosed_finalization_state,
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,6 +514,8 @@ impl ClosedBlock {
|
||||
// revert rewards (i.e. set state back at last transaction's state).
|
||||
let mut block = self.block;
|
||||
block.state = self.unclosed_state;
|
||||
block.metadata = self.unclosed_metadata;
|
||||
block.is_finalized = self.unclosed_finalization_state;
|
||||
OpenBlock {
|
||||
block: block,
|
||||
engine: engine,
|
||||
@ -588,6 +621,7 @@ fn enact(
|
||||
last_hashes: Arc<LastHashes>,
|
||||
factories: Factories,
|
||||
is_epoch_begin: bool,
|
||||
ancestry: &mut Iterator<Item=ExtendedHeader>,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
{
|
||||
if ::log::max_log_level() >= ::log::LogLevel::Trace {
|
||||
@ -608,6 +642,7 @@ fn enact(
|
||||
(3141562.into(), 31415620.into()),
|
||||
vec![],
|
||||
is_epoch_begin,
|
||||
ancestry,
|
||||
)?;
|
||||
|
||||
b.populate_from(&header);
|
||||
@ -630,6 +665,7 @@ pub fn enact_verified(
|
||||
last_hashes: Arc<LastHashes>,
|
||||
factories: Factories,
|
||||
is_epoch_begin: bool,
|
||||
ancestry: &mut Iterator<Item=ExtendedHeader>,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
let view = view!(BlockView, &block.bytes);
|
||||
|
||||
@ -644,6 +680,7 @@ pub fn enact_verified(
|
||||
last_hashes,
|
||||
factories,
|
||||
is_epoch_begin,
|
||||
ancestry,
|
||||
)
|
||||
}
|
||||
|
||||
@ -701,6 +738,7 @@ mod tests {
|
||||
(3141562.into(), 31415620.into()),
|
||||
vec![],
|
||||
false,
|
||||
&mut Vec::new().into_iter(),
|
||||
)?;
|
||||
|
||||
b.populate_from(&header);
|
||||
@ -734,7 +772,7 @@ mod tests {
|
||||
let genesis_header = spec.genesis_header();
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
let _ = b.seal(&*spec.engine, vec![]);
|
||||
}
|
||||
@ -748,7 +786,7 @@ mod tests {
|
||||
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap()
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap()
|
||||
.close_and_lock().seal(engine, vec![]).unwrap();
|
||||
let orig_bytes = b.rlp_bytes();
|
||||
let orig_db = b.drain();
|
||||
@ -772,7 +810,7 @@ mod tests {
|
||||
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let mut open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let mut open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let mut uncle1_header = Header::new();
|
||||
uncle1_header.set_extra_data(b"uncle1".to_vec());
|
||||
let mut uncle2_header = Header::new();
|
||||
@ -797,4 +835,3 @@ mod tests {
|
||||
assert!(orig_db.journal_db().keys().iter().filter(|k| orig_db.journal_db().get(k.0) != db.journal_db().get(k.0)).next() == None);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,11 +38,12 @@ use blockchain::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainD
|
||||
use blockchain::extras::{BlockReceipts, BlockDetails, TransactionAddress, EPOCH_KEY_PREFIX, EpochTransitions};
|
||||
use types::blockchain_info::BlockChainInfo;
|
||||
use types::tree_route::TreeRoute;
|
||||
use blockchain::update::ExtrasUpdate;
|
||||
use blockchain::update::{ExtrasUpdate, ExtrasInsert};
|
||||
use blockchain::{CacheSize, ImportRoute, Config};
|
||||
use db::{self, Writable, Readable, CacheUpdatePolicy};
|
||||
use cache_manager::CacheManager;
|
||||
use encoded;
|
||||
use engines::ForkChoice;
|
||||
use engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition};
|
||||
use rayon::prelude::*;
|
||||
use ansi_term::Colour;
|
||||
@ -417,6 +418,42 @@ impl<'a> Iterator for AncestryIter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator which walks the blockchain towards the genesis, with metadata information.
|
||||
pub struct AncestryWithMetadataIter<'a> {
|
||||
current: H256,
|
||||
chain: &'a BlockChain,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for AncestryWithMetadataIter<'a> {
|
||||
type Item = ExtendedHeader;
|
||||
fn next(&mut self) -> Option<ExtendedHeader> {
|
||||
if self.current.is_zero() {
|
||||
None
|
||||
} else {
|
||||
let details = self.chain.block_details(&self.current);
|
||||
let header = self.chain.block_header_data(&self.current)
|
||||
.map(|h| h.decode().expect("Stored block header data is valid RLP; qed"));
|
||||
|
||||
match (details, header) {
|
||||
(Some(details), Some(header)) => {
|
||||
self.current = details.parent;
|
||||
Some(ExtendedHeader {
|
||||
parent_total_difficulty: details.total_difficulty - *header.difficulty(),
|
||||
is_finalized: details.is_finalized,
|
||||
metadata: details.metadata,
|
||||
|
||||
header: header,
|
||||
})
|
||||
},
|
||||
_ => {
|
||||
self.current = H256::default();
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator which walks all epoch transitions.
|
||||
/// Returns epoch transitions.
|
||||
pub struct EpochTransitionIter<'a> {
|
||||
@ -510,6 +547,8 @@ impl BlockChain {
|
||||
total_difficulty: header.difficulty(),
|
||||
parent: header.parent_hash(),
|
||||
children: vec![],
|
||||
is_finalized: false,
|
||||
metadata: None,
|
||||
};
|
||||
|
||||
let mut batch = DBTransaction::new();
|
||||
@ -649,6 +688,7 @@ 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 from_details = self.block_details(&from)?;
|
||||
@ -661,6 +701,7 @@ 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.is_finalized;
|
||||
}
|
||||
|
||||
while to_details.number > from_details.number {
|
||||
@ -676,6 +717,7 @@ 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.is_finalized;
|
||||
|
||||
to_branch.push(current_to);
|
||||
current_to = to_details.parent.clone();
|
||||
@ -689,7 +731,8 @@ impl BlockChain {
|
||||
Some(TreeRoute {
|
||||
blocks: from_branch,
|
||||
ancestor: current_from,
|
||||
index: index
|
||||
index: index,
|
||||
is_from_route_finalized: is_from_route_finalized,
|
||||
})
|
||||
}
|
||||
|
||||
@ -733,7 +776,7 @@ impl BlockChain {
|
||||
|
||||
self.prepare_update(batch, ExtrasUpdate {
|
||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||
block_details: self.prepare_block_details_update(bytes, &info),
|
||||
block_details: self.prepare_block_details_update(bytes, &info, false, None),
|
||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||
@ -769,11 +812,14 @@ impl BlockChain {
|
||||
location: BlockLocation::CanonChain,
|
||||
};
|
||||
|
||||
// TODO [sorpaas] support warp sync insertion of finalization and metadata.
|
||||
let block_details = BlockDetails {
|
||||
number: header.number(),
|
||||
total_difficulty: info.total_difficulty,
|
||||
parent: header.parent_hash(),
|
||||
children: Vec::new(),
|
||||
is_finalized: false,
|
||||
metadata: None,
|
||||
};
|
||||
|
||||
let mut update = HashMap::new();
|
||||
@ -898,7 +944,22 @@ impl BlockChain {
|
||||
/// 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, batch: &mut DBTransaction, bytes: &[u8], receipts: Vec<Receipt>) -> ImportRoute {
|
||||
pub fn insert_block(&self, batch: &mut DBTransaction, bytes: &[u8], receipts: Vec<Receipt>, extras: ExtrasInsert) -> ImportRoute {
|
||||
let block = view!(BlockView, bytes);
|
||||
let header = block.header_view();
|
||||
|
||||
let parent_hash = header.parent_hash();
|
||||
let best_hash = self.best_block_hash();
|
||||
|
||||
let route = self.tree_route(best_hash, parent_hash).expect("forks are only kept when it has common ancestors; tree route from best to prospective's parent always exists; qed");
|
||||
|
||||
self.insert_block_with_route(batch, bytes, receipts, route, extras)
|
||||
}
|
||||
|
||||
/// Inserts the block into backing cache database with already generated route information.
|
||||
/// Expects the block to be valid and already verified and route is tree route information from current best block to new block's parent.
|
||||
/// If the block is already known, does nothing.
|
||||
pub fn insert_block_with_route(&self, batch: &mut DBTransaction, bytes: &[u8], receipts: Vec<Receipt>, route: TreeRoute, extras: ExtrasInsert) -> ImportRoute {
|
||||
// create views onto rlp
|
||||
let block = view!(BlockView, bytes);
|
||||
let header = block.header_view();
|
||||
@ -917,7 +978,7 @@ impl BlockChain {
|
||||
batch.put(db::COL_HEADERS, &hash, &compressed_header);
|
||||
batch.put(db::COL_BODIES, &hash, &compressed_body);
|
||||
|
||||
let info = self.block_info(&header);
|
||||
let info = self.block_info(&header, route, &extras);
|
||||
|
||||
if let BlockLocation::BranchBecomingCanonChain(ref d) = info.location {
|
||||
info!(target: "reorg", "Reorg to {} ({} {} {})",
|
||||
@ -930,7 +991,7 @@ impl BlockChain {
|
||||
|
||||
self.prepare_update(batch, ExtrasUpdate {
|
||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||
block_details: self.prepare_block_details_update(bytes, &info),
|
||||
block_details: self.prepare_block_details_update(bytes, &info, extras.is_finalized, extras.metadata),
|
||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||
@ -942,27 +1003,21 @@ impl BlockChain {
|
||||
}
|
||||
|
||||
/// Get inserted block info which is critical to prepare extras updates.
|
||||
fn block_info(&self, header: &HeaderView) -> BlockInfo {
|
||||
fn block_info(&self, header: &HeaderView, route: TreeRoute, extras: &ExtrasInsert) -> BlockInfo {
|
||||
let hash = header.hash();
|
||||
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 = parent_details.total_difficulty + header.difficulty() > self.best_block_total_difficulty();
|
||||
|
||||
BlockInfo {
|
||||
hash: hash,
|
||||
number: number,
|
||||
total_difficulty: parent_details.total_difficulty + header.difficulty(),
|
||||
location: if is_new_best {
|
||||
// on new best block we need to make sure that all ancestors
|
||||
location: match extras.fork_choice {
|
||||
ForkChoice::New => {
|
||||
// On new best block we need to make sure that all ancestors
|
||||
// are moved to "canon chain"
|
||||
// find the route between old best block and the new one
|
||||
let best_hash = self.best_block_hash();
|
||||
let route = self.tree_route(best_hash, parent_hash)
|
||||
.expect("blocks being imported always within recent history; qed");
|
||||
|
||||
assert_eq!(number, parent_details.number + 1);
|
||||
|
||||
match route.blocks.len() {
|
||||
0 => BlockLocation::CanonChain,
|
||||
_ => {
|
||||
@ -975,10 +1030,30 @@ impl BlockChain {
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BlockLocation::Branch
|
||||
},
|
||||
ForkChoice::Old => BlockLocation::Branch,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark a block to be considered finalized. Returns `Some(())` if the operation succeeds, and `None` if the block
|
||||
/// hash is not found.
|
||||
pub fn mark_finalized(&self, batch: &mut DBTransaction, block_hash: H256) -> Option<()> {
|
||||
let mut block_details = self.block_details(&block_hash)?;
|
||||
block_details.is_finalized = true;
|
||||
|
||||
self.update_block_details(batch, block_hash, block_details);
|
||||
Some(())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@ -1101,6 +1176,18 @@ impl BlockChain {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator that lists `first` and then all of `first`'s ancestors, by extended header.
|
||||
pub fn ancestry_with_metadata_iter<'a>(&'a self, first: H256) -> AncestryWithMetadataIter {
|
||||
AncestryWithMetadataIter {
|
||||
current: if self.is_known(&first) {
|
||||
first
|
||||
} else {
|
||||
H256::default() // zero hash
|
||||
},
|
||||
chain: self
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a block's `parent`, find every block header which represents a valid possible uncle.
|
||||
pub fn find_uncle_headers(&self, parent: &H256, uncle_generations: usize) -> Option<Vec<encoded::Header>> {
|
||||
self.find_uncle_hashes(parent, uncle_generations)
|
||||
@ -1166,7 +1253,7 @@ impl BlockChain {
|
||||
|
||||
/// This function returns modified block details.
|
||||
/// Uses the given parent details or attempts to load them from the database.
|
||||
fn prepare_block_details_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap<H256, BlockDetails> {
|
||||
fn prepare_block_details_update(&self, block_bytes: &[u8], info: &BlockInfo, is_finalized: bool, metadata: Option<Vec<u8>>) -> HashMap<H256, BlockDetails> {
|
||||
let block = view!(BlockView, block_bytes);
|
||||
let header = block.header_view();
|
||||
let parent_hash = header.parent_hash();
|
||||
@ -1181,6 +1268,8 @@ impl BlockChain {
|
||||
total_difficulty: info.total_difficulty,
|
||||
parent: parent_hash,
|
||||
children: vec![],
|
||||
is_finalized: is_finalized,
|
||||
metadata: metadata,
|
||||
};
|
||||
|
||||
// write to batch
|
||||
@ -1418,7 +1507,7 @@ mod tests {
|
||||
use std::sync::Arc;
|
||||
use rustc_hex::FromHex;
|
||||
use hash::keccak;
|
||||
use kvdb::KeyValueDB;
|
||||
use kvdb::{KeyValueDB, DBTransaction};
|
||||
use kvdb_memorydb;
|
||||
use ethereum_types::*;
|
||||
use receipt::{Receipt, TransactionOutcome};
|
||||
@ -1441,6 +1530,42 @@ mod tests {
|
||||
BlockChain::new(Config::default(), genesis, db)
|
||||
}
|
||||
|
||||
fn insert_block(db: &Arc<KeyValueDB>, bc: &BlockChain, bytes: &[u8], receipts: Vec<Receipt>) -> ImportRoute {
|
||||
insert_block_commit(db, bc, bytes, receipts, true)
|
||||
}
|
||||
|
||||
fn insert_block_commit(db: &Arc<KeyValueDB>, bc: &BlockChain, bytes: &[u8], receipts: Vec<Receipt>, commit: bool) -> ImportRoute {
|
||||
let mut batch = db.transaction();
|
||||
let res = insert_block_batch(&mut batch, bc, bytes, receipts);
|
||||
db.write(batch).unwrap();
|
||||
if commit {
|
||||
bc.commit();
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn insert_block_batch(batch: &mut DBTransaction, bc: &BlockChain, bytes: &[u8], receipts: Vec<Receipt>) -> ImportRoute {
|
||||
use views::BlockView;
|
||||
use blockchain::ExtrasInsert;
|
||||
|
||||
let block = view!(BlockView, bytes);
|
||||
let header = block.header_view();
|
||||
let parent_hash = header.parent_hash();
|
||||
let parent_details = bc.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash));
|
||||
let block_total_difficulty = parent_details.total_difficulty + header.difficulty();
|
||||
let fork_choice = if block_total_difficulty > bc.best_block_total_difficulty() {
|
||||
::engines::ForkChoice::New
|
||||
} else {
|
||||
::engines::ForkChoice::Old
|
||||
};
|
||||
|
||||
bc.insert_block(batch, bytes, receipts, ExtrasInsert {
|
||||
fork_choice: fork_choice,
|
||||
is_finalized: false,
|
||||
metadata: None
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_cache_best_block() {
|
||||
// given
|
||||
@ -1452,8 +1577,7 @@ mod tests {
|
||||
assert_eq!(bc.best_block_number(), 0);
|
||||
|
||||
// when
|
||||
let mut batch = db.transaction();
|
||||
bc.insert_block(&mut batch, &first.last().encoded(), vec![]);
|
||||
insert_block_commit(&db, &bc, &first.last().encoded(), vec![], false);
|
||||
assert_eq!(bc.best_block_number(), 0);
|
||||
bc.commit();
|
||||
// NOTE no db.write here (we want to check if best block is cached)
|
||||
@ -1483,7 +1607,7 @@ mod tests {
|
||||
assert_eq!(bc.block_details(&genesis_hash).unwrap().children, vec![]);
|
||||
|
||||
let mut batch = db.transaction();
|
||||
bc.insert_block(&mut batch, &first.encoded(), vec![]);
|
||||
insert_block_batch(&mut batch, &bc, &first.encoded(), vec![]);
|
||||
db.write(batch).unwrap();
|
||||
bc.commit();
|
||||
|
||||
@ -1509,7 +1633,7 @@ mod tests {
|
||||
let mut batch = db.transaction();
|
||||
for block in generator {
|
||||
block_hashes.push(block.hash());
|
||||
bc.insert_block(&mut batch, &block.encoded(), vec![]);
|
||||
insert_block_batch(&mut batch, &bc, &block.encoded(), vec![]);
|
||||
bc.commit();
|
||||
}
|
||||
db.write(batch).unwrap();
|
||||
@ -1549,14 +1673,10 @@ mod tests {
|
||||
let db = new_db();
|
||||
let bc = new_chain(&genesis.last().encoded(), db.clone());
|
||||
|
||||
let mut batch = db.transaction();
|
||||
for b in generator {
|
||||
bc.insert_block(&mut batch, &b.encoded(), vec![]);
|
||||
bc.commit();
|
||||
insert_block(&db, &bc, &b.encoded(), vec![]);
|
||||
}
|
||||
|
||||
db.write(batch).unwrap();
|
||||
|
||||
assert_eq!(uncle_headers, bc.find_uncle_headers(&b4a_hash, 3).unwrap());
|
||||
// TODO: insert block that already includes one of them as an uncle to check it's not allowed.
|
||||
}
|
||||
@ -1590,9 +1710,9 @@ mod tests {
|
||||
let bc = new_chain(&genesis.last().encoded(), db.clone());
|
||||
|
||||
let mut batch = db.transaction();
|
||||
let _ = bc.insert_block(&mut batch, &b1a.last().encoded(), vec![]);
|
||||
let _ = insert_block_batch(&mut batch, &bc, &b1a.last().encoded(), vec![]);
|
||||
bc.commit();
|
||||
let _ = bc.insert_block(&mut batch, &b1b.last().encoded(), vec![]);
|
||||
let _ = insert_block_batch(&mut batch, &bc, &b1b.last().encoded(), vec![]);
|
||||
bc.commit();
|
||||
db.write(batch).unwrap();
|
||||
|
||||
@ -1604,7 +1724,7 @@ mod tests {
|
||||
|
||||
// now let's make forked chain the canon chain
|
||||
let mut batch = db.transaction();
|
||||
let _ = bc.insert_block(&mut batch, &b2.last().encoded(), vec![]);
|
||||
let _ = insert_block_batch(&mut batch, &bc, &b2.last().encoded(), vec![]);
|
||||
bc.commit();
|
||||
db.write(batch).unwrap();
|
||||
|
||||
@ -1665,9 +1785,9 @@ mod tests {
|
||||
let bc = new_chain(&genesis.last().encoded(), db.clone());
|
||||
|
||||
let mut batch = db.transaction();
|
||||
let _ = bc.insert_block(&mut batch, &b1a.last().encoded(), vec![]);
|
||||
let _ = insert_block_batch(&mut batch, &bc, &b1a.last().encoded(), vec![]);
|
||||
bc.commit();
|
||||
let _ = bc.insert_block(&mut batch, &b1b.last().encoded(), vec![]);
|
||||
let _ = insert_block_batch(&mut batch, &bc, &b1b.last().encoded(), vec![]);
|
||||
bc.commit();
|
||||
db.write(batch).unwrap();
|
||||
|
||||
@ -1683,7 +1803,7 @@ mod tests {
|
||||
|
||||
// now let's make forked chain the canon chain
|
||||
let mut batch = db.transaction();
|
||||
let _ = bc.insert_block(&mut batch, &b2.last().encoded(), vec![]);
|
||||
let _ = insert_block_batch(&mut batch, &bc, &b2.last().encoded(), vec![]);
|
||||
bc.commit();
|
||||
db.write(batch).unwrap();
|
||||
|
||||
@ -1723,16 +1843,16 @@ mod tests {
|
||||
let bc = new_chain(&genesis.last().encoded(), db.clone());
|
||||
|
||||
let mut batch = db.transaction();
|
||||
let ir1 = bc.insert_block(&mut batch, &b1.last().encoded(), vec![]);
|
||||
let ir1 = insert_block_batch(&mut batch, &bc, &b1.last().encoded(), vec![]);
|
||||
bc.commit();
|
||||
let ir2 = bc.insert_block(&mut batch, &b2.last().encoded(), vec![]);
|
||||
let ir2 = insert_block_batch(&mut batch, &bc, &b2.last().encoded(), vec![]);
|
||||
bc.commit();
|
||||
let ir3b = bc.insert_block(&mut batch, &b3b.last().encoded(), vec![]);
|
||||
let ir3b = insert_block_batch(&mut batch, &bc, &b3b.last().encoded(), vec![]);
|
||||
bc.commit();
|
||||
db.write(batch).unwrap();
|
||||
assert_eq!(bc.block_hash(3).unwrap(), b3b_hash);
|
||||
let mut batch = db.transaction();
|
||||
let ir3a = bc.insert_block(&mut batch, &b3a.last().encoded(), vec![]);
|
||||
let ir3a = insert_block_batch(&mut batch, &bc, &b3a.last().encoded(), vec![]);
|
||||
bc.commit();
|
||||
db.write(batch).unwrap();
|
||||
|
||||
@ -1837,7 +1957,7 @@ mod tests {
|
||||
let bc = new_chain(&genesis.last().encoded(), db.clone());
|
||||
assert_eq!(bc.best_block_hash(), genesis_hash);
|
||||
let mut batch = db.transaction();
|
||||
bc.insert_block(&mut batch, &first.last().encoded(), vec![]);
|
||||
insert_block_batch(&mut batch, &bc, &first.last().encoded(), vec![]);
|
||||
db.write(batch).unwrap();
|
||||
bc.commit();
|
||||
assert_eq!(bc.best_block_hash(), first_hash);
|
||||
@ -1896,7 +2016,7 @@ mod tests {
|
||||
let db = new_db();
|
||||
let bc = new_chain(&genesis, db.clone());
|
||||
let mut batch =db.transaction();
|
||||
bc.insert_block(&mut batch, &b1, vec![]);
|
||||
insert_block_batch(&mut batch, &bc, &b1, vec![]);
|
||||
db.write(batch).unwrap();
|
||||
bc.commit();
|
||||
|
||||
@ -1907,14 +2027,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_block(db: &Arc<KeyValueDB>, bc: &BlockChain, bytes: &[u8], receipts: Vec<Receipt>) -> ImportRoute {
|
||||
let mut batch = db.transaction();
|
||||
let res = bc.insert_block(&mut batch, bytes, receipts);
|
||||
db.write(batch).unwrap();
|
||||
bc.commit();
|
||||
res
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_logs() {
|
||||
let t1 = Transaction {
|
||||
@ -2198,12 +2310,12 @@ mod tests {
|
||||
let mut batch = db.transaction();
|
||||
// create a longer fork
|
||||
for block in generator {
|
||||
bc.insert_block(&mut batch, &block.encoded(), vec![]);
|
||||
insert_block_batch(&mut batch, &bc, &block.encoded(), vec![]);
|
||||
bc.commit();
|
||||
}
|
||||
|
||||
assert_eq!(bc.best_block_number(), 5);
|
||||
bc.insert_block(&mut batch, &uncle.last().encoded(), vec![]);
|
||||
insert_block_batch(&mut batch, &bc, &uncle.last().encoded(), vec![]);
|
||||
db.write(batch).unwrap();
|
||||
bc.commit();
|
||||
}
|
||||
@ -2230,7 +2342,7 @@ mod tests {
|
||||
// create a longer fork
|
||||
for (i, block) in generator.into_iter().enumerate() {
|
||||
|
||||
bc.insert_block(&mut batch, &block.encoded(), vec![]);
|
||||
insert_block_batch(&mut batch, &bc, &block.encoded(), vec![]);
|
||||
bc.insert_epoch_transition(&mut batch, i as u64, EpochTransition {
|
||||
block_hash: block.hash(),
|
||||
block_number: i as u64 + 1,
|
||||
@ -2241,7 +2353,7 @@ mod tests {
|
||||
|
||||
assert_eq!(bc.best_block_number(), 5);
|
||||
|
||||
bc.insert_block(&mut batch, &uncle.last().encoded(), vec![]);
|
||||
insert_block_batch(&mut batch, &bc, &uncle.last().encoded(), vec![]);
|
||||
bc.insert_epoch_transition(&mut batch, 999, EpochTransition {
|
||||
block_hash: uncle.last().hash(),
|
||||
block_number: 1,
|
||||
@ -2291,11 +2403,7 @@ mod tests {
|
||||
// and a non-canonical fork of 8 from genesis.
|
||||
let fork_hash = {
|
||||
for block in fork_generator {
|
||||
let mut batch = db.transaction();
|
||||
|
||||
bc.insert_block(&mut batch, &block.encoded(), vec![]);
|
||||
bc.commit();
|
||||
db.write(batch).unwrap();
|
||||
insert_block(&db, &bc, &block.encoded(), vec![]);
|
||||
}
|
||||
|
||||
assert_eq!(bc.best_block_number(), 7);
|
||||
@ -2303,11 +2411,7 @@ mod tests {
|
||||
};
|
||||
|
||||
for block in next_generator {
|
||||
let mut batch = db.transaction();
|
||||
bc.insert_block(&mut batch, &block.encoded(), vec![]);
|
||||
bc.commit();
|
||||
|
||||
db.write(batch).unwrap();
|
||||
insert_block(&db, &bc, &block.encoded(), vec![]);
|
||||
}
|
||||
|
||||
assert_eq!(bc.best_block_number(), 10);
|
||||
|
@ -23,6 +23,7 @@ use db::Key;
|
||||
use engines::epoch::{Transition as EpochTransition};
|
||||
use header::BlockNumber;
|
||||
use receipt::Receipt;
|
||||
use rlp;
|
||||
|
||||
use heapsize::HeapSizeOf;
|
||||
use ethereum_types::{H256, H264, U256};
|
||||
@ -167,7 +168,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,
|
||||
@ -177,6 +178,57 @@ pub struct BlockDetails {
|
||||
pub parent: H256,
|
||||
/// List of children block hashes
|
||||
pub children: Vec<H256>,
|
||||
/// Whether the block is considered finalized
|
||||
pub is_finalized: bool,
|
||||
/// Additional block metadata
|
||||
pub metadata: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl rlp::Encodable for BlockDetails {
|
||||
fn rlp_append(&self, stream: &mut rlp::RlpStream) {
|
||||
let use_short_version = self.metadata.is_none() && !self.is_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.is_finalized);
|
||||
stream.append(&self.metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl rlp::Decodable for BlockDetails {
|
||||
fn decode(rlp: &rlp::Rlp) -> 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)?,
|
||||
is_finalized: if use_short_version {
|
||||
false
|
||||
} else {
|
||||
rlp.val_at(4)?
|
||||
},
|
||||
metadata: if use_short_version {
|
||||
None
|
||||
} else {
|
||||
rlp.val_at(5)?
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapSizeOf for BlockDetails {
|
||||
|
@ -33,4 +33,5 @@ pub use self::cache::CacheSize;
|
||||
pub use self::config::Config;
|
||||
pub use self::extras::{BlockReceipts, BlockDetails, TransactionAddress};
|
||||
pub use self::import_route::ImportRoute;
|
||||
pub use self::update::ExtrasInsert;
|
||||
pub use types::tree_route::TreeRoute;
|
||||
|
@ -22,3 +22,13 @@ pub struct ExtrasUpdate<'a> {
|
||||
/// Modified transaction addresses (None signifies removed transactions).
|
||||
pub transactions_addresses: HashMap<H256, Option<TransactionAddress>>,
|
||||
}
|
||||
|
||||
/// Extra information in block insertion.
|
||||
pub struct ExtrasInsert {
|
||||
/// The primitive fork choice before applying finalization rules.
|
||||
pub fork_choice: ::engines::ForkChoice,
|
||||
/// Is the inserted block considered finalized.
|
||||
pub is_finalized: bool,
|
||||
/// New block local metadata.
|
||||
pub metadata: Option<Vec<u8>>,
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ use util_error::UtilError;
|
||||
// other
|
||||
use ethereum_types::{H256, Address, U256};
|
||||
use block::{IsBlock, LockedBlock, Drain, ClosedBlock, OpenBlock, enact_verified, SealedBlock};
|
||||
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute, TransactionAddress};
|
||||
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert};
|
||||
use client::ancient_import::AncientVerifier;
|
||||
use client::Error as ClientError;
|
||||
use client::{
|
||||
@ -50,13 +50,13 @@ use client::{
|
||||
IoClient,
|
||||
};
|
||||
use encoded;
|
||||
use engines::{EthEngine, EpochTransition};
|
||||
use engines::{EthEngine, EpochTransition, ForkChoice};
|
||||
use error::{ImportErrorKind, BlockImportErrorKind, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError};
|
||||
use vm::{EnvInfo, LastHashes};
|
||||
use evm::Schedule;
|
||||
use executive::{Executive, Executed, TransactOptions, contract_address};
|
||||
use factory::{Factories, VmFactory};
|
||||
use header::{BlockNumber, Header};
|
||||
use header::{BlockNumber, Header, ExtendedHeader};
|
||||
use io::{IoChannel, IoError};
|
||||
use log_entry::LocalizedLogEntry;
|
||||
use miner::{Miner, MinerService};
|
||||
@ -73,10 +73,12 @@ use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Databa
|
||||
use transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Transaction, Action};
|
||||
use types::filter::Filter;
|
||||
use types::mode::Mode as IpcMode;
|
||||
use types::ancestry_action::AncestryAction;
|
||||
use verification;
|
||||
use verification::{PreverifiedBlock, Verifier};
|
||||
use verification::queue::BlockQueue;
|
||||
use views::BlockView;
|
||||
use parity_machine::{Finalizable, WithMetadata};
|
||||
|
||||
// re-export
|
||||
pub use types::blockchain_info::BlockChainInfo;
|
||||
@ -396,6 +398,7 @@ impl Importer {
|
||||
last_hashes,
|
||||
client.factories.clone(),
|
||||
is_epoch_begin,
|
||||
&mut chain.ancestry_with_metadata_iter(*header.parent_hash()),
|
||||
);
|
||||
|
||||
let mut locked_block = enact_result.map_err(|e| {
|
||||
@ -467,6 +470,44 @@ impl Importer {
|
||||
|
||||
let mut batch = DBTransaction::new();
|
||||
|
||||
let ancestry_actions = self.engine.ancestry_actions(block.block(), &mut chain.ancestry_with_metadata_iter(*parent));
|
||||
|
||||
let best_hash = chain.best_block_hash();
|
||||
let metadata = block.block().metadata().map(Into::into);
|
||||
let is_finalized = block.block().is_finalized();
|
||||
|
||||
let new = ExtendedHeader {
|
||||
header: header.clone(),
|
||||
is_finalized: is_finalized,
|
||||
metadata: metadata,
|
||||
parent_total_difficulty: chain.block_details(&parent).expect("Parent block is in the database; qed").total_difficulty
|
||||
};
|
||||
|
||||
let best = {
|
||||
let hash = best_hash;
|
||||
let header = chain.block_header_data(&hash)
|
||||
.expect("Best block is in the database; qed")
|
||||
.decode()
|
||||
.expect("Stored block header is valid RLP; qed");
|
||||
let details = chain.block_details(&hash)
|
||||
.expect("Best block is in the database; qed");
|
||||
|
||||
ExtendedHeader {
|
||||
parent_total_difficulty: details.total_difficulty - *header.difficulty(),
|
||||
is_finalized: details.is_finalized,
|
||||
metadata: details.metadata,
|
||||
|
||||
header: header,
|
||||
}
|
||||
};
|
||||
|
||||
let route = chain.tree_route(best_hash, *parent).expect("forks are only kept when it has common ancestors; tree route from best to prospective's parent always exists; qed");
|
||||
let fork_choice = if route.is_from_route_finalized {
|
||||
ForkChoice::Old
|
||||
} else {
|
||||
self.engine.fork_choice(&new, &best)
|
||||
};
|
||||
|
||||
// CHECK! I *think* this is fine, even if the state_root is equal to another
|
||||
// already-imported block of the same number.
|
||||
// TODO: Prove it with a test.
|
||||
@ -485,7 +526,17 @@ impl Importer {
|
||||
);
|
||||
|
||||
state.journal_under(&mut batch, number, hash).expect("DB commit failed");
|
||||
let route = chain.insert_block(&mut batch, block_data, receipts.clone());
|
||||
|
||||
for ancestry_action in ancestry_actions {
|
||||
let AncestryAction::MarkFinalized(ancestry) = ancestry_action;
|
||||
chain.mark_finalized(&mut batch, ancestry).expect("Engine's ancestry action must be known blocks; qed");
|
||||
}
|
||||
|
||||
let route = chain.insert_block(&mut batch, block_data, receipts.clone(), ExtrasInsert {
|
||||
fork_choice: fork_choice,
|
||||
is_finalized: is_finalized,
|
||||
metadata: new.metadata,
|
||||
});
|
||||
|
||||
client.tracedb.read().import(&mut batch, TraceImportRequest {
|
||||
traces: traces.into(),
|
||||
@ -2069,6 +2120,7 @@ impl PrepareOpenBlock for Client {
|
||||
gas_range_target,
|
||||
extra_data,
|
||||
is_epoch_begin,
|
||||
&mut chain.ancestry_with_metadata_iter(best_header.hash()),
|
||||
).expect("OpenBlock::new only fails if parent state root invalid; state root of best block's header is never invalid; qed");
|
||||
|
||||
// Add uncles
|
||||
@ -2284,6 +2336,7 @@ mod tests {
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use kvdb::DBTransaction;
|
||||
use blockchain::ExtrasInsert;
|
||||
|
||||
let client = generate_dummy_client(0);
|
||||
let genesis = client.chain_info().best_block_hash;
|
||||
@ -2296,7 +2349,11 @@ mod tests {
|
||||
let another_client = client.clone();
|
||||
thread::spawn(move || {
|
||||
let mut batch = DBTransaction::new();
|
||||
another_client.chain.read().insert_block(&mut batch, &new_block, Vec::new());
|
||||
another_client.chain.read().insert_block(&mut batch, &new_block, Vec::new(), ExtrasInsert {
|
||||
fork_choice: ::engines::ForkChoice::New,
|
||||
is_finalized: false,
|
||||
metadata: None,
|
||||
});
|
||||
go_thread.store(true, Ordering::SeqCst);
|
||||
});
|
||||
go
|
||||
|
@ -392,6 +392,7 @@ impl PrepareOpenBlock for TestBlockChainClient {
|
||||
gas_range_target,
|
||||
extra_data,
|
||||
false,
|
||||
&mut Vec::new().into_iter(),
|
||||
).expect("Opening block for tests will not fail.");
|
||||
// TODO [todr] Override timestamp for predictability
|
||||
open_block.set_timestamp(*self.latest_block_timestamp.read());
|
||||
@ -739,7 +740,8 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
}
|
||||
}
|
||||
if adding { Vec::new() } else { blocks }
|
||||
}
|
||||
},
|
||||
is_from_route_finalized: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ use error::{Error, BlockError};
|
||||
use ethjson;
|
||||
use machine::{AuxiliaryData, Call, EthereumMachine};
|
||||
use hash::keccak;
|
||||
use header::{Header, BlockNumber};
|
||||
use header::{Header, BlockNumber, ExtendedHeader};
|
||||
|
||||
use super::signer::EngineSigner;
|
||||
use super::validator_set::{ValidatorSet, SimpleList, new_validator_set};
|
||||
@ -957,6 +957,7 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
||||
&self,
|
||||
block: &mut ExecutedBlock,
|
||||
epoch_begin: bool,
|
||||
_ancestry: &mut Iterator<Item=ExtendedHeader>,
|
||||
) -> Result<(), Error> {
|
||||
// with immediate transitions, we don't use the epoch mechanism anyway.
|
||||
// the genesis is always considered an epoch, but we ignore it intentionally.
|
||||
@ -1348,6 +1349,10 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
||||
Some(Box::new(::snapshot::PoaSnapshot))
|
||||
}
|
||||
}
|
||||
|
||||
fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice {
|
||||
super::total_difficulty_fork_choice(new, current)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -1407,9 +1412,9 @@ mod tests {
|
||||
let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b1 = b1.close_and_lock();
|
||||
let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b2 = b2.close_and_lock();
|
||||
|
||||
engine.set_signer(tap.clone(), addr1, "1".into());
|
||||
@ -1441,9 +1446,9 @@ mod tests {
|
||||
let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b1 = b1.close_and_lock();
|
||||
let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b2 = b2.close_and_lock();
|
||||
|
||||
engine.set_signer(tap.clone(), addr1, "1".into());
|
||||
@ -1696,7 +1701,7 @@ mod tests {
|
||||
let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b1 = b1.close_and_lock();
|
||||
|
||||
let client = generate_dummy_client(0);
|
||||
@ -1731,7 +1736,7 @@ mod tests {
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
|
||||
// step 2
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b1 = b1.close_and_lock();
|
||||
|
||||
// since the block is empty it isn't sealed and we generate empty steps
|
||||
@ -1740,7 +1745,7 @@ mod tests {
|
||||
engine.step();
|
||||
|
||||
// step 3
|
||||
let mut b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr2, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let mut b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr2, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
b2.push_transaction(Transaction {
|
||||
action: Action::Create,
|
||||
nonce: U256::from(0),
|
||||
@ -1779,7 +1784,7 @@ mod tests {
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
|
||||
// step 2
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b1 = b1.close_and_lock();
|
||||
|
||||
// since the block is empty it isn't sealed and we generate empty steps
|
||||
@ -1788,7 +1793,7 @@ mod tests {
|
||||
engine.step();
|
||||
|
||||
// step 3
|
||||
let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr2, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr2, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b2 = b2.close_and_lock();
|
||||
engine.set_signer(tap.clone(), addr2, "0".into());
|
||||
assert_eq!(engine.generate_seal(b2.block(), &genesis_header), Seal::None);
|
||||
@ -1796,7 +1801,7 @@ mod tests {
|
||||
|
||||
// step 4
|
||||
// the spec sets the maximum_empty_steps to 2 so we will now seal an empty block and include the empty step messages
|
||||
let b3 = OpenBlock::new(engine, Default::default(), false, db3, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b3 = OpenBlock::new(engine, Default::default(), false, db3, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b3 = b3.close_and_lock();
|
||||
|
||||
engine.set_signer(tap.clone(), addr1, "1".into());
|
||||
@ -1829,7 +1834,7 @@ mod tests {
|
||||
engine.register_client(Arc::downgrade(&client) as _);
|
||||
|
||||
// step 2
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b1 = b1.close_and_lock();
|
||||
|
||||
// since the block is empty it isn't sealed and we generate empty steps
|
||||
@ -1839,7 +1844,7 @@ mod tests {
|
||||
|
||||
// step 3
|
||||
// the signer of the accumulated empty step message should be rewarded
|
||||
let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let addr1_balance = b2.block().state().balance(&addr1).unwrap();
|
||||
|
||||
// after closing the block `addr1` should be reward twice, one for the included empty step message and another for block creation
|
||||
@ -1962,6 +1967,7 @@ mod tests {
|
||||
(3141562.into(), 31415620.into()),
|
||||
vec![],
|
||||
false,
|
||||
&mut Vec::new().into_iter(),
|
||||
).unwrap();
|
||||
let b1 = b1.close_and_lock();
|
||||
|
||||
@ -1983,6 +1989,7 @@ mod tests {
|
||||
(3141562.into(), 31415620.into()),
|
||||
vec![],
|
||||
false,
|
||||
&mut Vec::new().into_iter(),
|
||||
).unwrap();
|
||||
let addr1_balance = b2.block().state().balance(&addr1).unwrap();
|
||||
|
||||
|
@ -25,7 +25,7 @@ use block::*;
|
||||
use engines::{Engine, Seal, ConstructedVerifier, EngineError};
|
||||
use error::{BlockError, Error};
|
||||
use ethjson;
|
||||
use header::Header;
|
||||
use header::{Header, ExtendedHeader};
|
||||
use client::EngineClient;
|
||||
use machine::{AuxiliaryData, Call, EthereumMachine};
|
||||
use super::signer::EngineSigner;
|
||||
@ -191,6 +191,10 @@ impl Engine<EthereumMachine> for BasicAuthority {
|
||||
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice {
|
||||
super::total_difficulty_fork_choice(new, current)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -247,7 +251,7 @@ mod tests {
|
||||
let genesis_header = spec.genesis_header();
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
if let Seal::Regular(seal) = engine.generate_seal(b.block(), &genesis_header) {
|
||||
assert!(b.try_seal(engine, seal).is_ok());
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use engines::{Engine, Seal};
|
||||
use parity_machine::{Machine, Transactions};
|
||||
use parity_machine::{Machine, Transactions, TotalScoredHeader};
|
||||
|
||||
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
|
||||
/// Only seals blocks which have transactions.
|
||||
@ -33,7 +33,9 @@ impl<M> InstantSeal<M> {
|
||||
}
|
||||
|
||||
impl<M: Machine> Engine<M> for InstantSeal<M>
|
||||
where M::LiveBlock: Transactions
|
||||
where M::LiveBlock: Transactions,
|
||||
M::ExtendedHeader: TotalScoredHeader,
|
||||
<M::ExtendedHeader as TotalScoredHeader>::Value: Ord
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
"InstantSeal"
|
||||
@ -61,6 +63,10 @@ impl<M: Machine> Engine<M> for InstantSeal<M>
|
||||
fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool {
|
||||
header_timestamp >= parent_timestamp
|
||||
}
|
||||
|
||||
fn fork_choice(&self, new: &M::ExtendedHeader, current: &M::ExtendedHeader) -> super::ForkChoice {
|
||||
super::total_difficulty_fork_choice(new, current)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -80,7 +86,7 @@ mod tests {
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let genesis_header = spec.genesis_header();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
if let Seal::Regular(seal) = engine.generate_seal(b.block(), &genesis_header) {
|
||||
assert!(b.try_seal(engine, seal).is_ok());
|
||||
|
@ -52,15 +52,25 @@ use spec::CommonParams;
|
||||
use transaction::{self, UnverifiedTransaction, SignedTransaction};
|
||||
|
||||
use ethkey::Signature;
|
||||
use parity_machine::{Machine, LocalizedMachine as Localized};
|
||||
use parity_machine::{Machine, LocalizedMachine as Localized, TotalScoredHeader};
|
||||
use ethereum_types::{H256, U256, Address};
|
||||
use unexpected::{Mismatch, OutOfBounds};
|
||||
use bytes::Bytes;
|
||||
use types::ancestry_action::AncestryAction;
|
||||
|
||||
/// Default EIP-210 contract code.
|
||||
/// As defined in https://github.com/ethereum/EIPs/pull/210
|
||||
pub const DEFAULT_BLOCKHASH_CONTRACT: &'static str = "73fffffffffffffffffffffffffffffffffffffffe33141561006a5760014303600035610100820755610100810715156100455760003561010061010083050761010001555b6201000081071515610064576000356101006201000083050761020001555b5061013e565b4360003512151561008457600060405260206040f361013d565b61010060003543031315156100a857610100600035075460605260206060f361013c565b6101006000350715156100c55762010000600035430313156100c8565b60005b156100ea576101006101006000350507610100015460805260206080f361013b565b620100006000350715156101095763010000006000354303131561010c565b60005b1561012f57610100620100006000350507610200015460a052602060a0f361013a565b600060c052602060c0f35b5b5b5b5b";
|
||||
|
||||
/// Fork choice.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ForkChoice {
|
||||
/// Choose the new block.
|
||||
New,
|
||||
/// Choose the current best block.
|
||||
Old,
|
||||
}
|
||||
|
||||
/// Voting errors.
|
||||
#[derive(Debug)]
|
||||
pub enum EngineError {
|
||||
@ -208,6 +218,7 @@ pub trait Engine<M: Machine>: Sync + Send {
|
||||
&self,
|
||||
_block: &mut M::LiveBlock,
|
||||
_epoch_begin: bool,
|
||||
_ancestry: &mut Iterator<Item=M::ExtendedHeader>,
|
||||
) -> Result<(), M::Error> {
|
||||
Ok(())
|
||||
}
|
||||
@ -348,6 +359,24 @@ pub trait Engine<M: Machine>: Sync + Send {
|
||||
fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool {
|
||||
header_timestamp > parent_timestamp
|
||||
}
|
||||
|
||||
/// Gather all ancestry actions. Called at the last stage when a block is committed. The Engine must guarantee that
|
||||
/// the ancestry exists.
|
||||
fn ancestry_actions(&self, _block: &M::LiveBlock, _ancestry: &mut Iterator<Item=M::ExtendedHeader>) -> Vec<AncestryAction> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
/// Check whether the given new block is the best block, after finalization check.
|
||||
fn fork_choice(&self, new: &M::ExtendedHeader, best: &M::ExtendedHeader) -> ForkChoice;
|
||||
}
|
||||
|
||||
/// Check whether a given block is the best block based on the default total difficulty rule.
|
||||
pub fn total_difficulty_fork_choice<T: TotalScoredHeader>(new: &T, best: &T) -> ForkChoice where <T as TotalScoredHeader>::Value: Ord {
|
||||
if new.total_score() > best.total_score() {
|
||||
ForkChoice::New
|
||||
} else {
|
||||
ForkChoice::Old
|
||||
}
|
||||
}
|
||||
|
||||
/// Common type alias for an engine coupled with an Ethereum-like state machine.
|
||||
|
@ -19,7 +19,7 @@ use engines::Engine;
|
||||
use engines::block_reward::{self, RewardKind};
|
||||
use header::BlockNumber;
|
||||
use machine::WithRewards;
|
||||
use parity_machine::{Header, LiveBlock, WithBalances};
|
||||
use parity_machine::{Header, LiveBlock, WithBalances, TotalScoredHeader};
|
||||
|
||||
/// Params for a null engine.
|
||||
#[derive(Clone, Default)]
|
||||
@ -58,7 +58,10 @@ impl<M: Default> Default for NullEngine<M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: WithBalances + WithRewards> Engine<M> for NullEngine<M> {
|
||||
impl<M: WithBalances + WithRewards> Engine<M> for NullEngine<M>
|
||||
where M::ExtendedHeader: TotalScoredHeader,
|
||||
<M::ExtendedHeader as TotalScoredHeader>::Value: Ord
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
"NullEngine"
|
||||
}
|
||||
@ -101,4 +104,8 @@ impl<M: WithBalances + WithRewards> Engine<M> for NullEngine<M> {
|
||||
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {
|
||||
Some(Box::new(::snapshot::PowSnapshot::new(10000, 10000)))
|
||||
}
|
||||
|
||||
fn fork_choice(&self, new: &M::ExtendedHeader, current: &M::ExtendedHeader) -> super::ForkChoice {
|
||||
super::total_difficulty_fork_choice(new, current)
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ use unexpected::{OutOfBounds, Mismatch};
|
||||
use client::EngineClient;
|
||||
use bytes::Bytes;
|
||||
use error::{Error, BlockError};
|
||||
use header::{Header, BlockNumber};
|
||||
use header::{Header, BlockNumber, ExtendedHeader};
|
||||
use rlp::Rlp;
|
||||
use ethkey::{self, Message, Signature};
|
||||
use account_provider::AccountProvider;
|
||||
@ -530,7 +530,7 @@ impl Engine<EthereumMachine> for Tendermint {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_new_block(&self, block: &mut ExecutedBlock, epoch_begin: bool) -> Result<(), Error> {
|
||||
fn on_new_block(&self, block: &mut ExecutedBlock, epoch_begin: bool, _ancestry: &mut Iterator<Item=ExtendedHeader>) -> Result<(), Error> {
|
||||
if !epoch_begin { return Ok(()) }
|
||||
|
||||
// genesis is never a new block, but might as well check.
|
||||
@ -765,6 +765,10 @@ impl Engine<EthereumMachine> for Tendermint {
|
||||
*self.client.write() = Some(client.clone());
|
||||
self.validators.register_client(client);
|
||||
}
|
||||
|
||||
fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice {
|
||||
super::total_difficulty_fork_choice(new, current)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -800,7 +804,7 @@ mod tests {
|
||||
let db = spec.ensure_db_good(db, &Default::default()).unwrap();
|
||||
let genesis_header = spec.genesis_header();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b = b.close();
|
||||
if let Seal::Proposal(seal) = spec.engine.generate_seal(b.block(), &genesis_header) {
|
||||
(b, seal)
|
||||
|
@ -25,7 +25,7 @@ use ethereum_types::{H256, H64, U256, Address};
|
||||
use unexpected::{OutOfBounds, Mismatch};
|
||||
use block::*;
|
||||
use error::{BlockError, Error};
|
||||
use header::{Header, BlockNumber};
|
||||
use header::{Header, BlockNumber, ExtendedHeader};
|
||||
use engines::{self, Engine};
|
||||
use ethjson;
|
||||
use rlp::Rlp;
|
||||
@ -222,14 +222,6 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
|
||||
header.set_difficulty(difficulty);
|
||||
}
|
||||
|
||||
fn on_new_block(
|
||||
&self,
|
||||
_block: &mut ExecutedBlock,
|
||||
_begins_epoch: bool,
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Apply the block reward on finalisation of the block.
|
||||
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
|
||||
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
|
||||
@ -364,6 +356,10 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
|
||||
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {
|
||||
Some(Box::new(::snapshot::PowSnapshot::new(SNAPSHOT_BLOCKS, MAX_SNAPSHOT_BLOCKS)))
|
||||
}
|
||||
|
||||
fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> engines::ForkChoice {
|
||||
engines::total_difficulty_fork_choice(new, current)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ethash {
|
||||
@ -538,7 +534,7 @@ mod tests {
|
||||
let genesis_header = spec.genesis_header();
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b = b.close();
|
||||
assert_eq!(b.state().balance(&Address::zero()).unwrap(), U256::from_str("4563918244f40000").unwrap());
|
||||
}
|
||||
@ -587,7 +583,7 @@ mod tests {
|
||||
let genesis_header = spec.genesis_header();
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let mut uncle = Header::new();
|
||||
let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into();
|
||||
uncle.set_author(uncle_author);
|
||||
@ -605,7 +601,7 @@ mod tests {
|
||||
let genesis_header = spec.genesis_header();
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
|
||||
let b = b.close();
|
||||
|
||||
let ubi_contract: Address = "00efdd5883ec628983e9063c7d969fe268bbf310".into();
|
||||
|
@ -34,6 +34,19 @@ enum Seal {
|
||||
Without,
|
||||
}
|
||||
|
||||
/// Extended block header, wrapping `Header` with finalized and total difficulty information.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ExtendedHeader {
|
||||
/// The actual header.
|
||||
pub header: Header,
|
||||
/// Whether the block underlying this header is considered finalized.
|
||||
pub is_finalized: bool,
|
||||
/// The parent block difficulty.
|
||||
pub parent_total_difficulty: U256,
|
||||
/// The block metadata information.
|
||||
pub metadata: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
/// A block header.
|
||||
///
|
||||
/// Reflects the specific RLP fields of a block in the chain with additional room for the seal
|
||||
@ -368,21 +381,48 @@ impl HeapSizeOf for Header {
|
||||
|
||||
impl ::parity_machine::Header for Header {
|
||||
fn bare_hash(&self) -> H256 { Header::bare_hash(self) }
|
||||
|
||||
fn hash(&self) -> H256 { Header::hash(self) }
|
||||
|
||||
fn seal(&self) -> &[Vec<u8>] { Header::seal(self) }
|
||||
|
||||
fn author(&self) -> &Address { Header::author(self) }
|
||||
|
||||
fn number(&self) -> BlockNumber { Header::number(self) }
|
||||
}
|
||||
|
||||
impl ::parity_machine::ScoredHeader for Header {
|
||||
type Value = U256;
|
||||
|
||||
fn score(&self) -> &U256 { self.difficulty() }
|
||||
fn set_score(&mut self, score: U256) { self.set_difficulty(score) }
|
||||
}
|
||||
|
||||
impl ::parity_machine::Header for ExtendedHeader {
|
||||
fn bare_hash(&self) -> H256 { self.header.bare_hash() }
|
||||
fn hash(&self) -> H256 { self.header.hash() }
|
||||
fn seal(&self) -> &[Vec<u8>] { self.header.seal() }
|
||||
fn author(&self) -> &Address { self.header.author() }
|
||||
fn number(&self) -> BlockNumber { self.header.number() }
|
||||
}
|
||||
|
||||
impl ::parity_machine::ScoredHeader for ExtendedHeader {
|
||||
type Value = U256;
|
||||
|
||||
fn score(&self) -> &U256 { self.header.difficulty() }
|
||||
fn set_score(&mut self, score: U256) { self.header.set_difficulty(score) }
|
||||
}
|
||||
|
||||
impl ::parity_machine::TotalScoredHeader for ExtendedHeader {
|
||||
type Value = U256;
|
||||
|
||||
fn total_score(&self) -> U256 { self.parent_total_difficulty + *self.header.difficulty() }
|
||||
}
|
||||
|
||||
impl ::parity_machine::FinalizableHeader for ExtendedHeader {
|
||||
fn is_finalized(&self) -> bool { self.is_finalized }
|
||||
}
|
||||
|
||||
impl ::parity_machine::WithMetadataHeader for ExtendedHeader {
|
||||
fn metadata(&self) -> Option<&[u8]> { self.metadata.as_ref().map(|v| v.as_ref()) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rustc_hex::FromHex;
|
||||
|
@ -25,7 +25,7 @@ use builtin::Builtin;
|
||||
use client::{BlockInfo, CallContract};
|
||||
use error::Error;
|
||||
use executive::Executive;
|
||||
use header::{BlockNumber, Header};
|
||||
use header::{BlockNumber, Header, ExtendedHeader};
|
||||
use spec::CommonParams;
|
||||
use state::{CleanupMode, Substate};
|
||||
use trace::{NoopTracer, NoopVMTracer, Tracer, ExecutiveTracer, RewardType, Tracing};
|
||||
@ -422,10 +422,12 @@ pub enum AuxiliaryRequest {
|
||||
|
||||
impl ::parity_machine::Machine for EthereumMachine {
|
||||
type Header = Header;
|
||||
type ExtendedHeader = ExtendedHeader;
|
||||
|
||||
type LiveBlock = ExecutedBlock;
|
||||
type EngineClient = ::client::EngineClient;
|
||||
type AuxiliaryRequest = AuxiliaryRequest;
|
||||
type AncestryAction = ::types::ancestry_action::AncestryAction;
|
||||
|
||||
type Error = Error;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ use tempdir::TempDir;
|
||||
use error::{Error, ErrorKind};
|
||||
|
||||
use blockchain::generator::{BlockGenerator, BlockBuilder};
|
||||
use blockchain::BlockChain;
|
||||
use blockchain::{BlockChain, ExtrasInsert};
|
||||
use snapshot::{chunk_secondary, Error as SnapshotError, Progress, SnapshotComponents};
|
||||
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
|
||||
|
||||
@ -49,7 +49,11 @@ fn chunk_and_restore(amount: u64) {
|
||||
// build the blockchain.
|
||||
let mut batch = DBTransaction::new();
|
||||
for block in generator {
|
||||
bc.insert_block(&mut batch, &block.encoded(), vec![]);
|
||||
bc.insert_block(&mut batch, &block.encoded(), vec![], ExtrasInsert {
|
||||
fork_choice: ::engines::ForkChoice::New,
|
||||
is_finalized: false,
|
||||
metadata: None,
|
||||
});
|
||||
bc.commit();
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
use account_provider::AccountProvider;
|
||||
use ethereum_types::{H256, U256, Address};
|
||||
use block::{OpenBlock, Drain};
|
||||
use blockchain::{BlockChain, Config as BlockChainConfig};
|
||||
use blockchain::{BlockChain, Config as BlockChainConfig, ExtrasInsert};
|
||||
use bytes::Bytes;
|
||||
use client::{Client, ClientConfig, ChainInfo, ImportBlock, ChainNotify, ChainMessageType, PrepareOpenBlock};
|
||||
use ethkey::KeyPair;
|
||||
@ -148,6 +148,7 @@ pub fn generate_dummy_client_with_spec_accounts_and_data<F>(test_spec: F, accoun
|
||||
(3141562.into(), 31415620.into()),
|
||||
vec![],
|
||||
false,
|
||||
&mut Vec::new().into_iter(),
|
||||
).unwrap();
|
||||
rolling_timestamp += 10;
|
||||
b.set_timestamp(rolling_timestamp);
|
||||
@ -265,7 +266,12 @@ pub fn generate_dummy_blockchain(block_number: u32) -> BlockChain {
|
||||
|
||||
let mut batch = db.transaction();
|
||||
for block_order in 1..block_number {
|
||||
bc.insert_block(&mut batch, &create_unverifiable_block(block_order, bc.best_block_hash()), vec![]);
|
||||
// Total difficulty is always 0 here.
|
||||
bc.insert_block(&mut batch, &create_unverifiable_block(block_order, bc.best_block_hash()), vec![], ExtrasInsert {
|
||||
fork_choice: ::engines::ForkChoice::New,
|
||||
is_finalized: false,
|
||||
metadata: None,
|
||||
});
|
||||
bc.commit();
|
||||
}
|
||||
db.write(batch).unwrap();
|
||||
@ -280,7 +286,12 @@ pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> BlockChain {
|
||||
|
||||
let mut batch = db.transaction();
|
||||
for block_order in 1..block_number {
|
||||
bc.insert_block(&mut batch, &create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None), vec![]);
|
||||
// Total difficulty is always 0 here.
|
||||
bc.insert_block(&mut batch, &create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None), vec![], ExtrasInsert {
|
||||
fork_choice: ::engines::ForkChoice::New,
|
||||
is_finalized: false,
|
||||
metadata: None,
|
||||
});
|
||||
bc.commit();
|
||||
}
|
||||
db.write(batch).unwrap();
|
||||
|
@ -87,6 +87,7 @@ fn can_trace_block_and_uncle_reward() {
|
||||
(3141562.into(), 31415620.into()),
|
||||
vec![],
|
||||
false,
|
||||
&mut Vec::new().into_iter(),
|
||||
).unwrap();
|
||||
rolling_timestamp += 10;
|
||||
root_block.set_timestamp(rolling_timestamp);
|
||||
@ -115,6 +116,7 @@ fn can_trace_block_and_uncle_reward() {
|
||||
(3141562.into(), 31415620.into()),
|
||||
vec![],
|
||||
false,
|
||||
&mut Vec::new().into_iter(),
|
||||
).unwrap();
|
||||
rolling_timestamp += 10;
|
||||
parent_block.set_timestamp(rolling_timestamp);
|
||||
@ -141,7 +143,8 @@ fn can_trace_block_and_uncle_reward() {
|
||||
author.clone(),
|
||||
(3141562.into(), 31415620.into()),
|
||||
vec![],
|
||||
false
|
||||
false,
|
||||
&mut Vec::new().into_iter(),
|
||||
).unwrap();
|
||||
rolling_timestamp += 10;
|
||||
block.set_timestamp(rolling_timestamp);
|
||||
|
@ -455,6 +455,8 @@ mod tests {
|
||||
total_difficulty: header.difficulty().clone(),
|
||||
parent: header.parent_hash().clone(),
|
||||
children: Vec::new(),
|
||||
is_finalized: false,
|
||||
metadata: None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
27
ethcore/types/src/ancestry_action.rs
Normal file
27
ethcore/types/src/ancestry_action.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2015-2018 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/>.
|
||||
|
||||
//! Actions on ancestry blocks when working on a new block.
|
||||
|
||||
use ethereum_types::H256;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
/// Actions on a live block's parent block. Only committed when the live block is committed. Those actions here must
|
||||
/// respect the normal blockchain reorganization rules.
|
||||
pub enum AncestryAction {
|
||||
/// Mark an ancestry block as finalized.
|
||||
MarkFinalized(H256),
|
||||
}
|
@ -46,6 +46,7 @@ pub mod state_diff;
|
||||
pub mod trace_filter;
|
||||
pub mod tree_route;
|
||||
pub mod verification_queue_info;
|
||||
pub mod ancestry_action;
|
||||
|
||||
/// Type for block number.
|
||||
pub type BlockNumber = u64;
|
||||
|
@ -27,4 +27,6 @@ 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,
|
||||
}
|
||||
|
@ -40,13 +40,35 @@ pub trait Header {
|
||||
fn number(&self) -> u64;
|
||||
}
|
||||
|
||||
/// a header with an associated score (difficulty in PoW terms)
|
||||
/// A header with an associated score (difficulty in PoW terms)
|
||||
pub trait ScoredHeader: Header {
|
||||
type Value;
|
||||
|
||||
/// Get the score of this header.
|
||||
fn score(&self) -> &U256;
|
||||
fn score(&self) -> &Self::Value;
|
||||
|
||||
/// Set the score of this header.
|
||||
fn set_score(&mut self, score: U256);
|
||||
fn set_score(&mut self, score: Self::Value);
|
||||
}
|
||||
|
||||
/// A header with associated total score.
|
||||
pub trait TotalScoredHeader: Header {
|
||||
type Value;
|
||||
|
||||
/// Get the total score of this header.
|
||||
fn total_score(&self) -> Self::Value;
|
||||
}
|
||||
|
||||
/// A header with finalized information.
|
||||
pub trait FinalizableHeader: Header {
|
||||
/// Get whether this header is considered finalized, so that it will never be replaced in reorganization.
|
||||
fn is_finalized(&self) -> bool;
|
||||
}
|
||||
|
||||
/// A header with metadata information.
|
||||
pub trait WithMetadataHeader: Header {
|
||||
/// Get the current header metadata.
|
||||
fn metadata(&self) -> Option<&[u8]>;
|
||||
}
|
||||
|
||||
/// A "live" block is one which is in the process of the transition.
|
||||
@ -73,16 +95,36 @@ pub trait Transactions: LiveBlock {
|
||||
fn transactions(&self) -> &[Self::Transaction];
|
||||
}
|
||||
|
||||
/// Trait for blocks which have finalized information.
|
||||
pub trait Finalizable: LiveBlock {
|
||||
/// Get whether the block is finalized.
|
||||
fn is_finalized(&self) -> bool;
|
||||
/// Mark the block as finalized.
|
||||
fn mark_finalized(&mut self);
|
||||
}
|
||||
|
||||
/// A state machine with block metadata.
|
||||
pub trait WithMetadata: LiveBlock {
|
||||
/// Get the current live block metadata.
|
||||
fn metadata(&self) -> Option<&[u8]>;
|
||||
/// Set the current live block metadata.
|
||||
fn set_metadata(&mut self, value: Option<Vec<u8>>);
|
||||
}
|
||||
|
||||
/// Generalization of types surrounding blockchain-suitable state machines.
|
||||
pub trait Machine: for<'a> LocalizedMachine<'a> {
|
||||
/// The block header type.
|
||||
type Header: Header;
|
||||
/// The live block type.
|
||||
type LiveBlock: LiveBlock<Header=Self::Header>;
|
||||
/// Block header with metadata information.
|
||||
type ExtendedHeader: Header;
|
||||
/// A handle to a blockchain client for this machine.
|
||||
type EngineClient: ?Sized;
|
||||
/// A description of needed auxiliary data.
|
||||
type AuxiliaryRequest;
|
||||
/// Actions taken on ancestry blocks when commiting a new block.
|
||||
type AncestryAction;
|
||||
|
||||
/// Errors which can occur when querying or interacting with the machine.
|
||||
type Error;
|
||||
|
Loading…
Reference in New Issue
Block a user