Chain scoring (#4218)

* chain scoring

* use current steps
This commit is contained in:
keorn 2017-01-23 15:27:11 +01:00 committed by Nikolay Volf
parent 2034595652
commit b7f9b304c1
10 changed files with 24 additions and 84 deletions

View File

@ -34,7 +34,6 @@ use blockchain::update::ExtrasUpdate;
use blockchain::{CacheSize, ImportRoute, Config}; use blockchain::{CacheSize, ImportRoute, Config};
use db::{self, Writable, Readable, CacheUpdatePolicy}; use db::{self, Writable, Readable, CacheUpdatePolicy};
use cache_manager::CacheManager; use cache_manager::CacheManager;
use engines::Engine;
use encoded; use encoded;
const LOG_BLOOMS_LEVELS: usize = 3; const LOG_BLOOMS_LEVELS: usize = 3;
@ -200,9 +199,6 @@ pub struct BlockChain {
pending_block_hashes: RwLock<HashMap<BlockNumber, H256>>, pending_block_hashes: RwLock<HashMap<BlockNumber, H256>>,
pending_block_details: RwLock<HashMap<H256, BlockDetails>>, pending_block_details: RwLock<HashMap<H256, BlockDetails>>,
pending_transaction_addresses: RwLock<HashMap<H256, Option<TransactionAddress>>>, pending_transaction_addresses: RwLock<HashMap<H256, Option<TransactionAddress>>>,
// Used for block ordering.
engine: Arc<Engine>,
} }
impl BlockProvider for BlockChain { impl BlockProvider for BlockChain {
@ -424,8 +420,8 @@ impl<'a> Iterator for AncestryIter<'a> {
} }
impl BlockChain { impl BlockChain {
/// Create new instance of blockchain from given Genesis and block picking rules of Engine. /// Create new instance of blockchain from given Genesis.
pub fn new(config: Config, genesis: &[u8], db: Arc<Database>, engine: Arc<Engine>) -> BlockChain { pub fn new(config: Config, genesis: &[u8], db: Arc<Database>) -> BlockChain {
// 400 is the avarage size of the key // 400 is the avarage size of the key
let cache_man = CacheManager::new(config.pref_cache_size, config.max_cache_size, 400); let cache_man = CacheManager::new(config.pref_cache_size, config.max_cache_size, 400);
@ -450,7 +446,6 @@ impl BlockChain {
pending_block_hashes: RwLock::new(HashMap::new()), pending_block_hashes: RwLock::new(HashMap::new()),
pending_block_details: RwLock::new(HashMap::new()), pending_block_details: RwLock::new(HashMap::new()),
pending_transaction_addresses: RwLock::new(HashMap::new()), pending_transaction_addresses: RwLock::new(HashMap::new()),
engine: engine,
}; };
// load best block // load best block
@ -867,7 +862,7 @@ impl BlockChain {
let number = header.number(); let number = header.number();
let parent_hash = header.parent_hash(); let parent_hash = header.parent_hash();
let parent_details = self.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); let parent_details = self.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash));
let is_new_best = self.engine.is_new_best_block(self.best_block_total_difficulty(), self.best_block_header().view(), &parent_details, header); let is_new_best = parent_details.total_difficulty + header.difficulty() > self.best_block_total_difficulty();
BlockInfo { BlockInfo {
hash: hash, hash: hash,
@ -1328,7 +1323,6 @@ mod tests {
use views::BlockView; use views::BlockView;
use transaction::{Transaction, Action}; use transaction::{Transaction, Action};
use log_entry::{LogEntry, LocalizedLogEntry}; use log_entry::{LogEntry, LocalizedLogEntry};
use spec::Spec;
use ethkey::Secret; use ethkey::Secret;
fn new_db(path: &str) -> Arc<Database> { fn new_db(path: &str) -> Arc<Database> {
@ -1336,7 +1330,7 @@ mod tests {
} }
fn new_chain(genesis: &[u8], db: Arc<Database>) -> BlockChain { fn new_chain(genesis: &[u8], db: Arc<Database>) -> BlockChain {
BlockChain::new(Config::default(), genesis, db, Spec::new_null().engine) BlockChain::new(Config::default(), genesis, db)
} }
#[test] #[test]

View File

@ -192,7 +192,7 @@ impl Client {
} }
let gb = spec.genesis_block(); let gb = spec.genesis_block();
let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone(), spec.engine.clone())); let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone()));
let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone())); let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone()));
trace!("Cleanup journal: DB Earliest = {:?}, Latest = {:?}", state_db.journal_db().earliest_era(), state_db.journal_db().latest_era()); trace!("Cleanup journal: DB Earliest = {:?}, Latest = {:?}", state_db.journal_db().earliest_era(), state_db.journal_db().latest_era());
@ -857,7 +857,7 @@ impl snapshot::DatabaseRestore for Client {
let cache_size = state_db.cache_size(); let cache_size = state_db.cache_size();
*state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE), cache_size); *state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE), cache_size);
*chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone(), self.engine.clone())); *chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone()));
*tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone()); *tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone());
Ok(()) Ok(())
} }

View File

@ -21,15 +21,13 @@ use std::sync::Weak;
use std::time::{UNIX_EPOCH, Duration}; use std::time::{UNIX_EPOCH, Duration};
use util::*; use util::*;
use ethkey::{verify_address, Signature}; use ethkey::{verify_address, Signature};
use rlp::{UntrustedRlp, Rlp, View, encode}; use rlp::{UntrustedRlp, View, encode};
use account_provider::AccountProvider; use account_provider::AccountProvider;
use block::*; use block::*;
use spec::CommonParams; use spec::CommonParams;
use engines::{Engine, Seal, EngineError}; use engines::{Engine, Seal, EngineError};
use header::Header; use header::Header;
use error::{Error, BlockError}; use error::{Error, BlockError};
use blockchain::extras::BlockDetails;
use views::HeaderView;
use evm::Schedule; use evm::Schedule;
use ethjson; use ethjson;
use io::{IoContext, IoHandler, TimerToken, IoService}; use io::{IoContext, IoHandler, TimerToken, IoService};
@ -208,7 +206,9 @@ impl Engine for AuthorityRound {
} }
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
header.set_difficulty(parent.difficulty().clone()); // Chain scoring: total weight is sqrt(U256::max_value())*height - step
let new_difficulty = U256::from(U128::max_value()) + header_step(parent).expect("Header has been verified; qed").into() - self.step.load(AtomicOrdering::SeqCst).into();
header.set_difficulty(new_difficulty);
header.set_gas_limit({ header.set_gas_limit({
let gas_limit = parent.gas_limit().clone(); let gas_limit = parent.gas_limit().clone();
let bound_divisor = self.gas_limit_bound_divisor; let bound_divisor = self.gas_limit_bound_divisor;
@ -309,19 +309,6 @@ impl Engine for AuthorityRound {
Ok(()) Ok(())
} }
fn is_new_best_block(&self, _best_total_difficulty: U256, best_header: HeaderView, _parent_details: &BlockDetails, new_header: &HeaderView) -> bool {
let new_number = new_header.number();
let best_number = best_header.number();
if new_number != best_number {
new_number > best_number
} else {
// Take the oldest step at given height.
let new_step: usize = Rlp::new(&new_header.seal()[0]).as_val();
let best_step: usize = Rlp::new(&best_header.seal()[0]).as_val();
new_step < best_step
}
}
fn register_client(&self, client: Weak<Client>) { fn register_client(&self, client: Weak<Client>) {
*self.client.write() = Some(client.clone()); *self.client.write() = Some(client.clone());
self.validators.register_call_contract(client); self.validators.register_call_contract(client);

View File

@ -41,9 +41,6 @@ use spec::CommonParams;
use evm::Schedule; use evm::Schedule;
use header::Header; use header::Header;
use transaction::{UnverifiedTransaction, SignedTransaction}; use transaction::{UnverifiedTransaction, SignedTransaction};
use ethereum::ethash;
use blockchain::extras::BlockDetails;
use views::HeaderView;
use client::Client; use client::Client;
/// Voting errors. /// Voting errors.
@ -177,7 +174,7 @@ pub trait Engine : Sync + Send {
} }
/// Populate a header's fields based on its parent's header. /// Populate a header's fields based on its parent's header.
/// Takes gas floor and ceiling targets. /// Usually implements the chain scoring rule based on weight.
/// The gas floor target must not be lower than the engine's minimum gas limit. /// The gas floor target must not be lower than the engine's minimum gas limit.
fn populate_from_parent(&self, header: &mut Header, parent: &Header, _gas_floor_target: U256, _gas_ceil_target: U256) { fn populate_from_parent(&self, header: &mut Header, parent: &Header, _gas_floor_target: U256, _gas_ceil_target: U256) {
header.set_difficulty(parent.difficulty().clone()); header.set_difficulty(parent.difficulty().clone());
@ -203,11 +200,6 @@ pub trait Engine : Sync + Send {
self.builtins().get(a).expect("attempted to execute nonexistent builtin").execute(input, output); self.builtins().get(a).expect("attempted to execute nonexistent builtin").execute(input, output);
} }
/// Check if new block should be chosen as the one in chain.
fn is_new_best_block(&self, best_total_difficulty: U256, _best_header: HeaderView, parent_details: &BlockDetails, new_header: &HeaderView) -> bool {
ethash::is_new_best_block(best_total_difficulty, parent_details, new_header)
}
/// Find out if the block is a proposal block and should not be inserted into the DB. /// Find out if the block is a proposal block and should not be inserted into the DB.
/// Takes a header of a fully verified block. /// Takes a header of a fully verified block.
fn is_proposal(&self, _verified_header: &Header) -> bool { false } fn is_proposal(&self, _verified_header: &Header) -> bool { false }

View File

@ -53,7 +53,8 @@ impl VoteStep {
} }
} }
fn consensus_round(header: &Header) -> Result<Round, ::rlp::DecoderError> { /// Header consensus round.
pub fn consensus_round(header: &Header) -> Result<Round, ::rlp::DecoderError> {
let round_rlp = header.seal().get(0).expect("seal passed basic verification; seal has 3 fields; qed"); let round_rlp = header.seal().get(0).expect("seal passed basic verification; seal has 3 fields; qed");
UntrustedRlp::new(round_rlp.as_slice()).as_val() UntrustedRlp::new(round_rlp.as_slice()).as_val()
} }

View File

@ -41,8 +41,6 @@ use account_provider::AccountProvider;
use block::*; use block::*;
use spec::CommonParams; use spec::CommonParams;
use engines::{Engine, Seal, EngineError}; use engines::{Engine, Seal, EngineError};
use blockchain::extras::BlockDetails;
use views::HeaderView;
use evm::Schedule; use evm::Schedule;
use state::CleanupMode; use state::CleanupMode;
use io::IoService; use io::IoService;
@ -397,7 +395,9 @@ impl Engine for Tendermint {
} }
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
header.set_difficulty(parent.difficulty().clone()); // Chain scoring: total weight is sqrt(U256::max_value())*height - round
let new_difficulty = U256::from(U128::max_value()) + consensus_round(parent).expect("Header has been verified; qed").into() - self.round.load(AtomicOrdering::SeqCst).into();
header.set_difficulty(new_difficulty);
header.set_gas_limit({ header.set_gas_limit({
let gas_limit = parent.gas_limit().clone(); let gas_limit = parent.gas_limit().clone();
let bound_divisor = self.gas_limit_bound_divisor; let bound_divisor = self.gas_limit_bound_divisor;
@ -565,27 +565,6 @@ impl Engine for Tendermint {
self.step_service.stop() self.step_service.stop()
} }
fn is_new_best_block(&self, _best_total_difficulty: U256, best_header: HeaderView, _parent_details: &BlockDetails, new_header: &HeaderView) -> bool {
let new_number = new_header.number();
let best_number = best_header.number();
trace!(target: "poa", "new_header: {}, best_header: {}", new_number, best_number);
if new_number != best_number {
new_number > best_number
} else {
let new_seal = new_header.seal();
let best_seal = best_header.seal();
let new_signatures = new_seal.get(2).expect("Tendermint seal should have three elements.").len();
let best_signatures = best_seal.get(2).expect("Tendermint seal should have three elements.").len();
if new_signatures > best_signatures {
true
} else {
let new_round: Round = ::rlp::Rlp::new(&new_seal.get(0).expect("Tendermint seal should have three elements.")).as_val();
let best_round: Round = ::rlp::Rlp::new(&best_seal.get(0).expect("Tendermint seal should have three elements.")).as_val();
new_round > best_round
}
}
}
fn is_proposal(&self, header: &Header) -> bool { fn is_proposal(&self, header: &Header) -> bool {
let signatures_len = header.seal()[2].len(); let signatures_len = header.seal()[2].len();
// Signatures have to be an empty list rlp. // Signatures have to be an empty list rlp.

View File

@ -21,7 +21,6 @@ use builtin::Builtin;
use env_info::EnvInfo; use env_info::EnvInfo;
use error::{BlockError, TransactionError, Error}; use error::{BlockError, TransactionError, Error};
use header::Header; use header::Header;
use views::HeaderView;
use state::CleanupMode; use state::CleanupMode;
use spec::CommonParams; use spec::CommonParams;
use transaction::UnverifiedTransaction; use transaction::UnverifiedTransaction;
@ -29,7 +28,6 @@ use engines::Engine;
use evm::Schedule; use evm::Schedule;
use ethjson; use ethjson;
use rlp::{self, UntrustedRlp, View}; use rlp::{self, UntrustedRlp, View};
use blockchain::extras::BlockDetails;
/// Parity tries to round block.gas_limit to multiple of this constant /// Parity tries to round block.gas_limit to multiple of this constant
pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]); pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]);
@ -335,15 +333,6 @@ impl Engine for Ethash {
Ok(()) Ok(())
} }
fn is_new_best_block(&self, best_total_difficulty: U256, _best_header: HeaderView, parent_details: &BlockDetails, new_header: &HeaderView) -> bool {
is_new_best_block(best_total_difficulty, parent_details, new_header)
}
}
/// Check if a new block should replace the best blockchain block.
pub fn is_new_best_block(best_total_difficulty: U256, parent_details: &BlockDetails, new_header: &HeaderView) -> bool {
parent_details.total_difficulty + new_header.difficulty() > best_total_difficulty
} }
// Try to round gas_limit a bit so that: // Try to round gas_limit a bit so that:

View File

@ -81,7 +81,6 @@ struct Restoration {
struct RestorationParams<'a> { struct RestorationParams<'a> {
manifest: ManifestData, // manifest to base restoration on. manifest: ManifestData, // manifest to base restoration on.
pruning: Algorithm, // pruning algorithm for the database. pruning: Algorithm, // pruning algorithm for the database.
engine: Arc<Engine>, // consensus engine of the chain.
db_path: PathBuf, // database path db_path: PathBuf, // database path
db_config: &'a DatabaseConfig, // configuration for the database. db_config: &'a DatabaseConfig, // configuration for the database.
writer: Option<LooseWriter>, // writer for recovered snapshot. writer: Option<LooseWriter>, // writer for recovered snapshot.
@ -100,7 +99,7 @@ impl Restoration {
let raw_db = Arc::new(Database::open(params.db_config, &*params.db_path.to_string_lossy()) let raw_db = Arc::new(Database::open(params.db_config, &*params.db_path.to_string_lossy())
.map_err(UtilError::SimpleString)?); .map_err(UtilError::SimpleString)?);
let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone(), params.engine); let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone());
let blocks = BlockRebuilder::new(chain, raw_db.clone(), &manifest)?; let blocks = BlockRebuilder::new(chain, raw_db.clone(), &manifest)?;
let root = manifest.state_root.clone(); let root = manifest.state_root.clone();
@ -421,7 +420,6 @@ impl Service {
let params = RestorationParams { let params = RestorationParams {
manifest: manifest, manifest: manifest,
pruning: self.pruning, pruning: self.pruning,
engine: self.engine.clone(),
db_path: self.restoration_db(), db_path: self.restoration_db(),
db_config: &self.db_config, db_config: &self.db_config,
writer: writer, writer: writer,

View File

@ -44,7 +44,7 @@ fn chunk_and_restore(amount: u64) {
snapshot_path.push("SNAP"); snapshot_path.push("SNAP");
let old_db = Arc::new(Database::open(&db_cfg, orig_path.as_str()).unwrap()); let old_db = Arc::new(Database::open(&db_cfg, orig_path.as_str()).unwrap());
let bc = BlockChain::new(Default::default(), &genesis, old_db.clone(), engine.clone()); let bc = BlockChain::new(Default::default(), &genesis, old_db.clone());
// build the blockchain. // build the blockchain.
let mut batch = old_db.transaction(); let mut batch = old_db.transaction();
@ -74,7 +74,7 @@ fn chunk_and_restore(amount: u64) {
// restore it. // restore it.
let new_db = Arc::new(Database::open(&db_cfg, new_path.as_str()).unwrap()); let new_db = Arc::new(Database::open(&db_cfg, new_path.as_str()).unwrap());
let new_chain = BlockChain::new(Default::default(), &genesis, new_db.clone(), engine.clone()); let new_chain = BlockChain::new(Default::default(), &genesis, new_db.clone());
let mut rebuilder = BlockRebuilder::new(new_chain, new_db.clone(), &manifest).unwrap(); let mut rebuilder = BlockRebuilder::new(new_chain, new_db.clone(), &manifest).unwrap();
let reader = PackedReader::new(&snapshot_path).unwrap().unwrap(); let reader = PackedReader::new(&snapshot_path).unwrap().unwrap();
let flag = AtomicBool::new(true); let flag = AtomicBool::new(true);
@ -87,7 +87,7 @@ fn chunk_and_restore(amount: u64) {
rebuilder.finalize(HashMap::new()).unwrap(); rebuilder.finalize(HashMap::new()).unwrap();
// and test it. // and test it.
let new_chain = BlockChain::new(Default::default(), &genesis, new_db, engine); let new_chain = BlockChain::new(Default::default(), &genesis, new_db);
assert_eq!(new_chain.best_block_hash(), best_hash); assert_eq!(new_chain.best_block_hash(), best_hash);
} }
@ -122,7 +122,7 @@ fn checks_flag() {
let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
let db = Arc::new(Database::open(&db_cfg, path.as_str()).unwrap()); let db = Arc::new(Database::open(&db_cfg, path.as_str()).unwrap());
let engine = Arc::new(::engines::NullEngine::default()); let engine = Arc::new(::engines::NullEngine::default());
let chain = BlockChain::new(Default::default(), &genesis, db.clone(), engine.clone()); let chain = BlockChain::new(Default::default(), &genesis, db.clone());
let manifest = ::snapshot::ManifestData { let manifest = ::snapshot::ManifestData {
state_hashes: Vec::new(), state_hashes: Vec::new(),

View File

@ -294,7 +294,7 @@ fn new_db(path: &str) -> Arc<Database> {
pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult<BlockChain> { pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult<BlockChain> {
let temp = RandomTempPath::new(); let temp = RandomTempPath::new();
let db = new_db(temp.as_str()); let db = new_db(temp.as_str());
let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone(), Spec::new_null().engine); let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone());
let mut batch = db.transaction(); let mut batch = db.transaction();
for block_order in 1..block_number { for block_order in 1..block_number {
@ -312,7 +312,7 @@ pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult<BlockCh
pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempResult<BlockChain> { pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempResult<BlockChain> {
let temp = RandomTempPath::new(); let temp = RandomTempPath::new();
let db = new_db(temp.as_str()); let db = new_db(temp.as_str());
let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone(), Spec::new_null().engine); let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone());
let mut batch = db.transaction(); let mut batch = db.transaction();
@ -331,7 +331,7 @@ pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempRes
pub fn generate_dummy_empty_blockchain() -> GuardedTempResult<BlockChain> { pub fn generate_dummy_empty_blockchain() -> GuardedTempResult<BlockChain> {
let temp = RandomTempPath::new(); let temp = RandomTempPath::new();
let db = new_db(temp.as_str()); let db = new_db(temp.as_str());
let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone(), Spec::new_null().engine); let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone());
GuardedTempResult::<BlockChain> { GuardedTempResult::<BlockChain> {
_temp: temp, _temp: temp,