Backports for beta 2.2.2 (#9976)
* version: bump beta to 2.2.2 * Add experimental RPCs flag (#9928) * WiP * Enable experimental RPCs. * Keep existing blocks when restoring a Snapshot (#8643) * Rename db_restore => client * First step: make it compile! * Second step: working implementation! * Refactoring * Fix tests * PR Grumbles * PR Grumbles WIP * Migrate ancient blocks interating backward * Early return in block migration if snapshot is aborted * Remove RwLock getter (PR Grumble I) * Remove dependency on `Client`: only used Traits * Add test for recovering aborted snapshot recovery * Add test for migrating old blocks * Fix build * PR Grumble I * PR Grumble II * PR Grumble III * PR Grumble IV * PR Grumble V * PR Grumble VI * Fix one test * Fix test * PR Grumble * PR Grumbles * PR Grumbles II * Fix tests * Release RwLock earlier * Revert Cargo.lock * Update _update ancient block_ logic: set local in `commit` * Update typo in ethcore/src/snapshot/service.rs Co-Authored-By: ngotchac <ngotchac@gmail.com> * Adjust requests costs for light client (#9925) * PIP Table Cost relative to average peers instead of max peers * Add tracing in PIP new_cost_table * Update stat peer_count * Use number of leeching peers for Light serve costs * Fix test::light_params_load_share_depends_on_max_peers (wrong type) * Remove (now) useless test * Remove `load_share` from LightParams.Config Prevent div. by 0 * Add LEECHER_COUNT_FACTOR * PR Grumble: u64 to u32 for f64 casting * Prevent u32 overflow for avg_peer_count * Add tests for LightSync::Statistics * Fix empty steps (#9939) * Don't send empty step twice or empty step then block. * Perform basic validation of locally sealed blocks. * Don't include empty step twice. * prevent silent errors in daemon mode, closes #9367 (#9946) * Fix a deadlock (#9952) * Update informant: - decimal in Mgas/s - print every 5s (not randomly between 5s and 10s) * Fix dead-lock in `blockchain.rs` * Update locks ordering * Fix light client informant while syncing (#9932) * Add `is_idle` to LightSync to check importing status * Use SyncStateWrapper to make sure is_idle gets updates * Update is_major_import to use verified queue size as well * Add comment for `is_idle` * Add Debug to `SyncStateWrapper` * `fn get` -> `fn into_inner` * ci: rearrange pipeline by logic (#9970) * ci: rearrange pipeline by logic * ci: rename docs script * fix docker build (#9971) * Deny unknown fields for chainspec (#9972) * Add deny_unknown_fields to chainspec * Add tests and fix existing one * Remove serde_ignored dependency for chainspec * Fix rpc test eth chain spec * Fix starting_nonce_test spec * Improve block and transaction propagation (#9954) * Refactor sync to add priority tasks. * Send priority tasks notifications. * Propagate blocks, optimize transactions. * Implement transaction propagation. Use sync_channel. * Tone down info. * Prevent deadlock by not waiting forever for sync lock. * Fix lock order. * Don't use sync_channel to prevent deadlocks. * Fix tests. * Fix unstable peers and slowness in sync (#9967) * Don't sync all peers after each response * Update formating * Fix tests: add `continue_sync` to `Sync_step` * Update ethcore/sync/src/chain/mod.rs Co-Authored-By: ngotchac <ngotchac@gmail.com> * fix rpc middlewares * fix Cargo.lock * json: resolve merge in spec * rpc: fix starting_nonce_test * ci: allow nightl job to fail
This commit is contained in:
@@ -28,7 +28,7 @@ use parking_lot::{Mutex, RwLock};
|
||||
use provider::Provider;
|
||||
use request::{Request, NetworkRequests as Requests, Response};
|
||||
use rlp::{RlpStream, Rlp};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::fmt;
|
||||
use std::ops::{BitOr, BitAnd, Not};
|
||||
use std::sync::Arc;
|
||||
@@ -38,7 +38,7 @@ use std::time::{Duration, Instant};
|
||||
use self::request_credits::{Credits, FlowParams};
|
||||
use self::context::{Ctx, TickCtx};
|
||||
use self::error::Punishment;
|
||||
use self::load_timer::{LoadDistribution, NullStore};
|
||||
use self::load_timer::{LoadDistribution, NullStore, MOVING_SAMPLE_SIZE};
|
||||
use self::request_set::RequestSet;
|
||||
use self::id_guard::IdGuard;
|
||||
|
||||
@@ -70,6 +70,16 @@ const PROPAGATE_TIMEOUT_INTERVAL: Duration = Duration::from_secs(5);
|
||||
const RECALCULATE_COSTS_TIMEOUT: TimerToken = 3;
|
||||
const RECALCULATE_COSTS_INTERVAL: Duration = Duration::from_secs(60 * 60);
|
||||
|
||||
const STATISTICS_TIMEOUT: TimerToken = 4;
|
||||
const STATISTICS_INTERVAL: Duration = Duration::from_secs(15);
|
||||
|
||||
/// Maximum load share for the light server
|
||||
pub const MAX_LIGHTSERV_LOAD: f64 = 0.5;
|
||||
|
||||
/// Factor to multiply leecher count to cater for
|
||||
/// extra sudden connections (should be >= 1.0)
|
||||
pub const LEECHER_COUNT_FACTOR: f64 = 1.25;
|
||||
|
||||
// minimum interval between updates.
|
||||
const UPDATE_INTERVAL: Duration = Duration::from_millis(5000);
|
||||
|
||||
@@ -256,18 +266,18 @@ pub trait Handler: Send + Sync {
|
||||
pub struct Config {
|
||||
/// How many stored seconds of credits peers should be able to accumulate.
|
||||
pub max_stored_seconds: u64,
|
||||
/// How much of the total load capacity each peer should be allowed to take.
|
||||
pub load_share: f64,
|
||||
/// The network config median peers (used as default peer count)
|
||||
pub median_peers: f64,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
const LOAD_SHARE: f64 = 1.0 / 25.0;
|
||||
const MEDIAN_PEERS: f64 = 25.0;
|
||||
const MAX_ACCUMULATED: u64 = 60 * 5; // only charge for 5 minutes.
|
||||
|
||||
Config {
|
||||
max_stored_seconds: MAX_ACCUMULATED,
|
||||
load_share: LOAD_SHARE,
|
||||
median_peers: MEDIAN_PEERS,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,6 +345,42 @@ mod id_guard {
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides various statistics that could
|
||||
/// be used to compute costs
|
||||
pub struct Statistics {
|
||||
/// Samples of peer count
|
||||
peer_counts: VecDeque<usize>,
|
||||
}
|
||||
|
||||
impl Statistics {
|
||||
/// Create a new Statistics instance
|
||||
pub fn new() -> Self {
|
||||
Statistics {
|
||||
peer_counts: VecDeque::with_capacity(MOVING_SAMPLE_SIZE),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a new peer_count sample
|
||||
pub fn add_peer_count(&mut self, peer_count: usize) {
|
||||
while self.peer_counts.len() >= MOVING_SAMPLE_SIZE {
|
||||
self.peer_counts.pop_front();
|
||||
}
|
||||
self.peer_counts.push_back(peer_count);
|
||||
}
|
||||
|
||||
/// Get the average peer count from previous samples. Is always >= 1.0
|
||||
pub fn avg_peer_count(&self) -> f64 {
|
||||
let len = self.peer_counts.len();
|
||||
if len == 0 {
|
||||
return 1.0;
|
||||
}
|
||||
let avg = self.peer_counts.iter()
|
||||
.fold(0, |sum: u32, &v| sum.saturating_add(v as u32)) as f64
|
||||
/ len as f64;
|
||||
avg.max(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// This is an implementation of the light ethereum network protocol, abstracted
|
||||
/// over a `Provider` of data and a p2p network.
|
||||
///
|
||||
@@ -359,6 +405,7 @@ pub struct LightProtocol {
|
||||
req_id: AtomicUsize,
|
||||
sample_store: Box<SampleStore>,
|
||||
load_distribution: LoadDistribution,
|
||||
statistics: RwLock<Statistics>,
|
||||
}
|
||||
|
||||
impl LightProtocol {
|
||||
@@ -369,9 +416,11 @@ impl LightProtocol {
|
||||
let genesis_hash = provider.chain_info().genesis_hash;
|
||||
let sample_store = params.sample_store.unwrap_or_else(|| Box::new(NullStore));
|
||||
let load_distribution = LoadDistribution::load(&*sample_store);
|
||||
// Default load share relative to median peers
|
||||
let load_share = MAX_LIGHTSERV_LOAD / params.config.median_peers;
|
||||
let flow_params = FlowParams::from_request_times(
|
||||
|kind| load_distribution.expected_time(kind),
|
||||
params.config.load_share,
|
||||
load_share,
|
||||
Duration::from_secs(params.config.max_stored_seconds),
|
||||
);
|
||||
|
||||
@@ -389,6 +438,7 @@ impl LightProtocol {
|
||||
req_id: AtomicUsize::new(0),
|
||||
sample_store,
|
||||
load_distribution,
|
||||
statistics: RwLock::new(Statistics::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,6 +458,16 @@ impl LightProtocol {
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the number of active light peers downloading from the
|
||||
/// node
|
||||
pub fn leecher_count(&self) -> usize {
|
||||
let credit_limit = *self.flow_params.read().limit();
|
||||
// Count the number of peers that used some credit
|
||||
self.peers.read().iter()
|
||||
.filter(|(_, p)| p.lock().local_credits.current() < credit_limit)
|
||||
.count()
|
||||
}
|
||||
|
||||
/// Make a request to a peer.
|
||||
///
|
||||
/// Fails on: nonexistent peer, network error, peer not server,
|
||||
@@ -772,12 +832,16 @@ impl LightProtocol {
|
||||
fn begin_new_cost_period(&self, io: &IoContext) {
|
||||
self.load_distribution.end_period(&*self.sample_store);
|
||||
|
||||
let avg_peer_count = self.statistics.read().avg_peer_count();
|
||||
// Load share relative to average peer count +LEECHER_COUNT_FACTOR%
|
||||
let load_share = MAX_LIGHTSERV_LOAD / (avg_peer_count * LEECHER_COUNT_FACTOR);
|
||||
let new_params = Arc::new(FlowParams::from_request_times(
|
||||
|kind| self.load_distribution.expected_time(kind),
|
||||
self.config.load_share,
|
||||
load_share,
|
||||
Duration::from_secs(self.config.max_stored_seconds),
|
||||
));
|
||||
*self.flow_params.write() = new_params.clone();
|
||||
trace!(target: "pip", "New cost period: avg_peers={} ; cost_table:{:?}", avg_peer_count, new_params.cost_table());
|
||||
|
||||
let peers = self.peers.read();
|
||||
let now = Instant::now();
|
||||
@@ -797,6 +861,11 @@ impl LightProtocol {
|
||||
peer_info.awaiting_acknowledge = Some((now, new_params.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
fn tick_statistics(&self) {
|
||||
let leecher_count = self.leecher_count();
|
||||
self.statistics.write().add_peer_count(leecher_count);
|
||||
}
|
||||
}
|
||||
|
||||
impl LightProtocol {
|
||||
@@ -1099,6 +1168,8 @@ impl NetworkProtocolHandler for LightProtocol {
|
||||
.expect("Error registering sync timer.");
|
||||
io.register_timer(RECALCULATE_COSTS_TIMEOUT, RECALCULATE_COSTS_INTERVAL)
|
||||
.expect("Error registering request timer interval token.");
|
||||
io.register_timer(STATISTICS_TIMEOUT, STATISTICS_INTERVAL)
|
||||
.expect("Error registering statistics timer.");
|
||||
}
|
||||
|
||||
fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) {
|
||||
@@ -1119,6 +1190,7 @@ impl NetworkProtocolHandler for LightProtocol {
|
||||
TICK_TIMEOUT => self.tick_handlers(&io),
|
||||
PROPAGATE_TIMEOUT => self.propagate_transactions(&io),
|
||||
RECALCULATE_COSTS_TIMEOUT => self.begin_new_cost_period(&io),
|
||||
STATISTICS_TIMEOUT => self.tick_statistics(),
|
||||
_ => warn!(target: "pip", "received timeout on unknown token {}", timer),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,10 @@ use ethcore::client::{EachBlockWith, TestBlockChainClient};
|
||||
use ethcore::encoded;
|
||||
use ethcore::ids::BlockId;
|
||||
use ethereum_types::{H256, U256, Address};
|
||||
use net::{LightProtocol, Params, packet, Peer};
|
||||
use net::{LightProtocol, Params, packet, Peer, Statistics};
|
||||
use net::context::IoContext;
|
||||
use net::status::{Capabilities, Status};
|
||||
use net::load_timer::MOVING_SAMPLE_SIZE;
|
||||
use network::{PeerId, NodeId};
|
||||
use provider::Provider;
|
||||
use request;
|
||||
@@ -780,3 +781,34 @@ fn get_transaction_index() {
|
||||
let expected = Expect::Respond(packet::RESPONSE, response);
|
||||
proto.handle_packet(&expected, 1, packet::REQUEST, &request_body);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_statistics() {
|
||||
let mut stats = Statistics::new();
|
||||
|
||||
// Empty set should return 1.0
|
||||
assert_eq!(stats.avg_peer_count(), 1.0);
|
||||
|
||||
// Average < 1.0 should return 1.0
|
||||
stats.add_peer_count(0);
|
||||
assert_eq!(stats.avg_peer_count(), 1.0);
|
||||
|
||||
stats = Statistics::new();
|
||||
|
||||
const N: f64 = 50.0;
|
||||
|
||||
for i in 1..(N as usize + 1) {
|
||||
stats.add_peer_count(i);
|
||||
}
|
||||
|
||||
// Compute the average for the sum 1..N
|
||||
assert_eq!(stats.avg_peer_count(), N * (N + 1.0) / 2.0 / N);
|
||||
|
||||
for _ in 1..(MOVING_SAMPLE_SIZE + 1) {
|
||||
stats.add_peer_count(40);
|
||||
}
|
||||
|
||||
// Test that it returns the average of the last
|
||||
// `MOVING_SAMPLE_SIZE` values
|
||||
assert_eq!(stats.avg_peer_count(), 40.0);
|
||||
}
|
||||
|
||||
@@ -176,8 +176,8 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
|
||||
}
|
||||
|
||||
fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse> {
|
||||
BlockChainClient::encoded_block_receipts(self, &req.hash)
|
||||
.map(|x| ::request::ReceiptsResponse { receipts: ::rlp::decode_list(&x) })
|
||||
BlockChainClient::block_receipts(self, &req.hash)
|
||||
.map(|x| ::request::ReceiptsResponse { receipts: x.receipts })
|
||||
}
|
||||
|
||||
fn account_proof(&self, req: request::CompleteAccountRequest) -> Option<request::AccountResponse> {
|
||||
|
||||
Reference in New Issue
Block a user