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:
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user