a0a2beddfe
* parity-version: bump beta to 2.0.2 * remove ssl from dockerfiles, closes #8880 (#9195) * snap: remove ssl dependencies from snapcraft definition (#9222) * parity-version: bump beta to 2.0.3 * Remove all dapp permissions related settings (#9120) * Completely remove all dapps struct from rpc * Remove unused pub use * Remove dapp policy/permission func in ethcore * Remove all dapps settings from rpc * Fix rpc tests * Use both origin and user_agent * Address grumbles * Address grumbles * Fix tests * Check if synced when using eth_getWork (#9193) (#9210) * Check if synced when using eth_getWork (#9193) * Don't use fn syncing * Fix identation * Fix typo * Don't check for warping * rpc: avoid calling queue_info twice on eth_getWork * Fix potential as_usize overflow when casting from U256 in miner (#9221) * Allow old blocks from peers with lower difficulty (#9226) Previously we only allow downloading of old blocks if the peer difficulty was greater than our syncing difficulty. This change allows downloading of blocks from peers where the difficulty is greater then the last downloaded old block. * Update Dockerfile (#9242) * Update Dockerfile fix Docker build * fix dockerfile paths: parity -> parity-ethereum (#9248) * Propagate transactions for next 4 blocks. (#9265) Closes #9255 This PR also removes the limit of max 64 transactions per packet, currently we only attempt to prevent the packet size to go over 8MB. This will only be the case for super-large transactions or high-block-gas-limit chains. Patching this is important only for chains that have blocks that can fit more than 4k transactions (over 86M block gas limit) For mainnet, we should actually see a tiny bit faster propagation since instead of computing 4k pending set, we only need `4 * 8M / 21k = 1523` transactions. Running some tests on `dekompile` node right now, to check how it performs in the wild. * Update tobalaba.json (#9313) * Fix load share (#9321) * fix(light_sync): calculate `load_share` properly * refactor(api.rs): extract `light_params` fn, add test * style(api.rs): add trailing commas * ethcore: fix pow difficulty validation (#9328) * ethcore: fix pow difficulty validation * ethcore: validate difficulty is not zero * ethcore: add issue link to regression test * ethcore: fix tests * ethcore: move difficulty_to_boundary to ethash crate * ethcore: reuse difficulty_to_boundary and boundary_to_difficulty * ethcore: fix grumbles in difficulty_to_boundary_aux * Light client `Provide default nonce in transactions when it´s missing` (#9370) * Provide `default_nonce` in tx`s when it´s missing When `nonce` is missing in a `EthTransaction` will cause it to fall in these cases provide `default_nonce` value instead! * Changed http:// to https:// on Yasm link (#9369) Changed http:// to https:// on Yasm link in README.md * Provide `default_nonce` in tx`s when it´s missing When `nonce` is missing in a `EthTransaction` will cause it to fall in these cases provide `default_nonce` value instead! * Address grumbles * ethcore: kovan: delay activation of strict score validation (#9406) * Better support for eth_getLogs in light mode (#9186) * Light client on-demand request for headers range. * Cache headers in HeaderWithAncestors response. Also fulfills request locally if all headers are in cache. * LightFetch::logs fetches missing headers on demand. * LightFetch::logs limit the number of headers requested at a time. * LightFetch::logs refactor header fetching logic. * Enforce limit on header range length in light client logs request. * Fix light request tests after struct change. * Respond to review comments. * Add update docs script to CI (#9219) * Add update docs script to CI Added a script to CI that will use the jsonrpc tool to update rpc documentation then commit and push those to the wiki repo. * fix gitlab ci lint * Only apply jsonrpc docs update on tags * Update gitlab-rpc-docs.sh * Copy correct parity repo to jsonrpc folder Copy correct parity repo to jsonrpc folder before attempting to build docs since the CI runner clones the repo as parity and not parity-ethereum. * Fix JSONRPC docs CI job Update remote config in wiki repo before pushing changes using a github token for authentication. Add message to wiki tag when pushing changes. Use project directory to correctly copy parity code base into the jsonrpc repo for doc generation. * Fix set_remote_wiki function call in CI * Prevent blockchain & miner racing when accessing pending block. (#9310) * Prevent blockchain & miner racing when accessing pending block. * Fix unavailability of pending block during reseal. * Prevent sync restart if import queue full (#9381) * Add POA Networks: Core and Sokol (#9413) * ethcore: add poa network and sokol chainspecs * rpc: simplify chain spec docs * cli: rearrange networks by main/test and size/range * parity: don't blacklist 0x00a328 on sokol testnet * parity: add sokol and poanet to params and clean up a bit, add tests * ethcore: add the poa networks and clean up a bit * ethcore: fix path to poacore chain spec * parity: rename poa networks to poacore and poasokol * parity: fix configuration tests * parity: fix parameter tests * ethcore: rename POA Core and POA Sokol * Update tobalaba.json (#9419) * Update hardcoded sync (#9421) - Update foundation hardcoded header to block 6219777 - Update ropsten hardcoded header to block 3917825 - Update kovan hardcoded header to block 8511489
956 lines
29 KiB
Rust
956 lines
29 KiB
Rust
// 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/>.
|
|
|
|
use std::sync::Arc;
|
|
use std::collections::{HashMap, BTreeMap};
|
|
use std::io;
|
|
use std::ops::Range;
|
|
use std::time::Duration;
|
|
use bytes::Bytes;
|
|
use devp2p::NetworkService;
|
|
use network::{NetworkProtocolHandler, NetworkContext, PeerId, ProtocolId,
|
|
NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, Error, ErrorKind,
|
|
ConnectionFilter};
|
|
|
|
use types::pruning_info::PruningInfo;
|
|
use ethereum_types::{H256, H512, U256};
|
|
use io::{TimerToken};
|
|
use ethcore::ethstore::ethkey::Secret;
|
|
use ethcore::client::{BlockChainClient, ChainNotify, ChainRoute, ChainMessageType};
|
|
use ethcore::snapshot::SnapshotService;
|
|
use ethcore::header::BlockNumber;
|
|
use sync_io::NetSyncIo;
|
|
use chain::{ChainSync, SyncStatus as EthSyncStatus};
|
|
use std::net::{SocketAddr, AddrParseError};
|
|
use std::str::FromStr;
|
|
use parking_lot::RwLock;
|
|
use chain::{ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_62,
|
|
PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3};
|
|
use light::client::AsLightClient;
|
|
use light::Provider;
|
|
use light::net::{
|
|
self as light_net, LightProtocol, Params as LightParams,
|
|
Capabilities, Handler as LightHandler, EventContext, SampleStore,
|
|
};
|
|
use network::IpFilter;
|
|
use private_tx::PrivateTxHandler;
|
|
use transaction::UnverifiedTransaction;
|
|
|
|
/// Parity sync protocol
|
|
pub const WARP_SYNC_PROTOCOL_ID: ProtocolId = *b"par";
|
|
/// Ethereum sync protocol
|
|
pub const ETH_PROTOCOL: ProtocolId = *b"eth";
|
|
/// Ethereum light protocol
|
|
pub const LIGHT_PROTOCOL: ProtocolId = *b"pip";
|
|
|
|
/// Determine warp sync status.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum WarpSync {
|
|
/// Warp sync is enabled.
|
|
Enabled,
|
|
/// Warp sync is disabled.
|
|
Disabled,
|
|
/// Only warp sync is allowed (no regular sync) and only after given block number.
|
|
OnlyAndAfter(BlockNumber),
|
|
}
|
|
|
|
impl WarpSync {
|
|
/// Returns true if warp sync is enabled.
|
|
pub fn is_enabled(&self) -> bool {
|
|
match *self {
|
|
WarpSync::Enabled => true,
|
|
WarpSync::OnlyAndAfter(_) => true,
|
|
WarpSync::Disabled => false,
|
|
}
|
|
}
|
|
|
|
/// Returns `true` if we are in warp-only mode.
|
|
///
|
|
/// i.e. we will never fall back to regular sync
|
|
/// until given block number is reached by
|
|
/// successfuly finding and restoring from a snapshot.
|
|
pub fn is_warp_only(&self) -> bool {
|
|
if let WarpSync::OnlyAndAfter(_) = *self {
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Sync configuration
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct SyncConfig {
|
|
/// Max blocks to download ahead
|
|
pub max_download_ahead_blocks: usize,
|
|
/// Enable ancient block download.
|
|
pub download_old_blocks: bool,
|
|
/// Network ID
|
|
pub network_id: u64,
|
|
/// Main "eth" subprotocol name.
|
|
pub subprotocol_name: [u8; 3],
|
|
/// Light subprotocol name.
|
|
pub light_subprotocol_name: [u8; 3],
|
|
/// Fork block to check
|
|
pub fork_block: Option<(BlockNumber, H256)>,
|
|
/// Enable snapshot sync
|
|
pub warp_sync: WarpSync,
|
|
/// Enable light client server.
|
|
pub serve_light: bool,
|
|
}
|
|
|
|
impl Default for SyncConfig {
|
|
fn default() -> SyncConfig {
|
|
SyncConfig {
|
|
max_download_ahead_blocks: 20000,
|
|
download_old_blocks: true,
|
|
network_id: 1,
|
|
subprotocol_name: ETH_PROTOCOL,
|
|
light_subprotocol_name: LIGHT_PROTOCOL,
|
|
fork_block: None,
|
|
warp_sync: WarpSync::Disabled,
|
|
serve_light: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Current sync status
|
|
pub trait SyncProvider: Send + Sync {
|
|
/// Get sync status
|
|
fn status(&self) -> EthSyncStatus;
|
|
|
|
/// Get peers information
|
|
fn peers(&self) -> Vec<PeerInfo>;
|
|
|
|
/// Get the enode if available.
|
|
fn enode(&self) -> Option<String>;
|
|
|
|
/// Returns propagation count for pending transactions.
|
|
fn transactions_stats(&self) -> BTreeMap<H256, TransactionStats>;
|
|
}
|
|
|
|
/// Transaction stats
|
|
#[derive(Debug)]
|
|
pub struct TransactionStats {
|
|
/// Block number where this TX was first seen.
|
|
pub first_seen: u64,
|
|
/// Peers it was propagated to.
|
|
pub propagated_to: BTreeMap<H512, usize>,
|
|
}
|
|
|
|
/// Peer connection information
|
|
#[derive(Debug)]
|
|
pub struct PeerInfo {
|
|
/// Public node id
|
|
pub id: Option<String>,
|
|
/// Node client ID
|
|
pub client_version: String,
|
|
/// Capabilities
|
|
pub capabilities: Vec<String>,
|
|
/// Remote endpoint address
|
|
pub remote_address: String,
|
|
/// Local endpoint address
|
|
pub local_address: String,
|
|
/// Eth protocol info.
|
|
pub eth_info: Option<EthProtocolInfo>,
|
|
/// Light protocol info.
|
|
pub pip_info: Option<PipProtocolInfo>,
|
|
}
|
|
|
|
/// Ethereum protocol info.
|
|
#[derive(Debug)]
|
|
pub struct EthProtocolInfo {
|
|
/// Protocol version
|
|
pub version: u32,
|
|
/// SHA3 of peer best block hash
|
|
pub head: H256,
|
|
/// Peer total difficulty if known
|
|
pub difficulty: Option<U256>,
|
|
}
|
|
|
|
/// PIP protocol info.
|
|
#[derive(Debug)]
|
|
pub struct PipProtocolInfo {
|
|
/// Protocol version
|
|
pub version: u32,
|
|
/// SHA3 of peer best block hash
|
|
pub head: H256,
|
|
/// Peer total difficulty if known
|
|
pub difficulty: U256,
|
|
}
|
|
|
|
impl From<light_net::Status> for PipProtocolInfo {
|
|
fn from(status: light_net::Status) -> Self {
|
|
PipProtocolInfo {
|
|
version: status.protocol_version,
|
|
head: status.head_hash,
|
|
difficulty: status.head_td,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Configuration to attach alternate protocol handlers.
|
|
/// Only works when IPC is disabled.
|
|
pub struct AttachedProtocol {
|
|
/// The protocol handler in question.
|
|
pub handler: Arc<NetworkProtocolHandler + Send + Sync>,
|
|
/// 3-character ID for the protocol.
|
|
pub protocol_id: ProtocolId,
|
|
/// Supported versions and their packet counts.
|
|
pub versions: &'static [(u8, u8)],
|
|
}
|
|
|
|
impl AttachedProtocol {
|
|
fn register(&self, network: &NetworkService) {
|
|
let res = network.register_protocol(
|
|
self.handler.clone(),
|
|
self.protocol_id,
|
|
self.versions
|
|
);
|
|
|
|
if let Err(e) = res {
|
|
warn!(target: "sync", "Error attaching protocol {:?}: {:?}", self.protocol_id, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// EthSync initialization parameters.
|
|
pub struct Params {
|
|
/// Configuration.
|
|
pub config: SyncConfig,
|
|
/// Blockchain client.
|
|
pub chain: Arc<BlockChainClient>,
|
|
/// Snapshot service.
|
|
pub snapshot_service: Arc<SnapshotService>,
|
|
/// Private tx service.
|
|
pub private_tx_handler: Arc<PrivateTxHandler>,
|
|
/// Light data provider.
|
|
pub provider: Arc<::light::Provider>,
|
|
/// Network layer configuration.
|
|
pub network_config: NetworkConfiguration,
|
|
/// Other protocols to attach.
|
|
pub attached_protos: Vec<AttachedProtocol>,
|
|
}
|
|
|
|
/// Ethereum network protocol handler
|
|
pub struct EthSync {
|
|
/// Network service
|
|
network: NetworkService,
|
|
/// Main (eth/par) protocol handler
|
|
eth_handler: Arc<SyncProtocolHandler>,
|
|
/// Light (pip) protocol handler
|
|
light_proto: Option<Arc<LightProtocol>>,
|
|
/// Other protocols to attach.
|
|
attached_protos: Vec<AttachedProtocol>,
|
|
/// The main subprotocol name
|
|
subprotocol_name: [u8; 3],
|
|
/// Light subprotocol name.
|
|
light_subprotocol_name: [u8; 3],
|
|
}
|
|
|
|
fn light_params(
|
|
network_id: u64,
|
|
max_peers: u32,
|
|
pruning_info: PruningInfo,
|
|
sample_store: Option<Box<SampleStore>>,
|
|
) -> LightParams {
|
|
const MAX_LIGHTSERV_LOAD: f64 = 0.5;
|
|
|
|
let mut light_params = LightParams {
|
|
network_id: network_id,
|
|
config: Default::default(),
|
|
capabilities: Capabilities {
|
|
serve_headers: true,
|
|
serve_chain_since: Some(pruning_info.earliest_chain),
|
|
serve_state_since: Some(pruning_info.earliest_state),
|
|
tx_relay: true,
|
|
},
|
|
sample_store: sample_store,
|
|
};
|
|
|
|
let max_peers = ::std::cmp::max(max_peers, 1);
|
|
light_params.config.load_share = MAX_LIGHTSERV_LOAD / max_peers as f64;
|
|
|
|
light_params
|
|
}
|
|
|
|
impl EthSync {
|
|
/// Creates and register protocol with the network service
|
|
pub fn new(params: Params, connection_filter: Option<Arc<ConnectionFilter>>) -> Result<Arc<EthSync>, Error> {
|
|
let pruning_info = params.chain.pruning_info();
|
|
let light_proto = match params.config.serve_light {
|
|
false => None,
|
|
true => Some({
|
|
let sample_store = params.network_config.net_config_path
|
|
.clone()
|
|
.map(::std::path::PathBuf::from)
|
|
.map(|mut p| { p.push("request_timings"); light_net::FileStore(p) })
|
|
.map(|store| Box::new(store) as Box<_>);
|
|
|
|
let light_params = light_params(
|
|
params.config.network_id,
|
|
params.network_config.max_peers,
|
|
pruning_info,
|
|
sample_store,
|
|
);
|
|
|
|
let mut light_proto = LightProtocol::new(params.provider, light_params);
|
|
light_proto.add_handler(Arc::new(TxRelay(params.chain.clone())));
|
|
|
|
Arc::new(light_proto)
|
|
})
|
|
};
|
|
|
|
let chain_sync = ChainSync::new(params.config, &*params.chain, params.private_tx_handler.clone());
|
|
let service = NetworkService::new(params.network_config.clone().into_basic()?, connection_filter)?;
|
|
|
|
let sync = Arc::new(EthSync {
|
|
network: service,
|
|
eth_handler: Arc::new(SyncProtocolHandler {
|
|
sync: RwLock::new(chain_sync),
|
|
chain: params.chain,
|
|
snapshot_service: params.snapshot_service,
|
|
overlay: RwLock::new(HashMap::new()),
|
|
}),
|
|
light_proto: light_proto,
|
|
subprotocol_name: params.config.subprotocol_name,
|
|
light_subprotocol_name: params.config.light_subprotocol_name,
|
|
attached_protos: params.attached_protos,
|
|
});
|
|
|
|
Ok(sync)
|
|
}
|
|
}
|
|
|
|
impl SyncProvider for EthSync {
|
|
/// Get sync status
|
|
fn status(&self) -> EthSyncStatus {
|
|
self.eth_handler.sync.read().status()
|
|
}
|
|
|
|
/// Get sync peers
|
|
fn peers(&self) -> Vec<PeerInfo> {
|
|
self.network.with_context_eval(self.subprotocol_name, |ctx| {
|
|
let peer_ids = self.network.connected_peers();
|
|
let eth_sync = self.eth_handler.sync.read();
|
|
let light_proto = self.light_proto.as_ref();
|
|
|
|
peer_ids.into_iter().filter_map(|peer_id| {
|
|
let session_info = match ctx.session_info(peer_id) {
|
|
None => return None,
|
|
Some(info) => info,
|
|
};
|
|
|
|
Some(PeerInfo {
|
|
id: session_info.id.map(|id| format!("{:x}", id)),
|
|
client_version: session_info.client_version,
|
|
capabilities: session_info.peer_capabilities.into_iter().map(|c| c.to_string()).collect(),
|
|
remote_address: session_info.remote_address,
|
|
local_address: session_info.local_address,
|
|
eth_info: eth_sync.peer_info(&peer_id),
|
|
pip_info: light_proto.as_ref().and_then(|lp| lp.peer_status(&peer_id)).map(Into::into),
|
|
})
|
|
}).collect()
|
|
}).unwrap_or_else(Vec::new)
|
|
}
|
|
|
|
fn enode(&self) -> Option<String> {
|
|
self.network.external_url()
|
|
}
|
|
|
|
fn transactions_stats(&self) -> BTreeMap<H256, TransactionStats> {
|
|
let sync = self.eth_handler.sync.read();
|
|
sync.transactions_stats()
|
|
.iter()
|
|
.map(|(hash, stats)| (*hash, stats.into()))
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
const PEERS_TIMER: TimerToken = 0;
|
|
const SYNC_TIMER: TimerToken = 1;
|
|
const TX_TIMER: TimerToken = 2;
|
|
|
|
struct SyncProtocolHandler {
|
|
/// Shared blockchain client.
|
|
chain: Arc<BlockChainClient>,
|
|
/// Shared snapshot service.
|
|
snapshot_service: Arc<SnapshotService>,
|
|
/// Sync strategy
|
|
sync: RwLock<ChainSync>,
|
|
/// Chain overlay used to cache data such as fork block.
|
|
overlay: RwLock<HashMap<BlockNumber, Bytes>>,
|
|
}
|
|
|
|
impl NetworkProtocolHandler for SyncProtocolHandler {
|
|
fn initialize(&self, io: &NetworkContext) {
|
|
if io.subprotocol_name() != WARP_SYNC_PROTOCOL_ID {
|
|
io.register_timer(PEERS_TIMER, Duration::from_millis(700)).expect("Error registering peers timer");
|
|
io.register_timer(SYNC_TIMER, Duration::from_millis(1100)).expect("Error registering sync timer");
|
|
io.register_timer(TX_TIMER, Duration::from_millis(1300)).expect("Error registering transactions timer");
|
|
}
|
|
}
|
|
|
|
fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) {
|
|
ChainSync::dispatch_packet(&self.sync, &mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer, packet_id, data);
|
|
}
|
|
|
|
fn connected(&self, io: &NetworkContext, peer: &PeerId) {
|
|
trace_time!("sync::connected");
|
|
// If warp protocol is supported only allow warp handshake
|
|
let warp_protocol = io.protocol_version(WARP_SYNC_PROTOCOL_ID, *peer).unwrap_or(0) != 0;
|
|
let warp_context = io.subprotocol_name() == WARP_SYNC_PROTOCOL_ID;
|
|
if warp_protocol == warp_context {
|
|
self.sync.write().on_peer_connected(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer);
|
|
}
|
|
}
|
|
|
|
fn disconnected(&self, io: &NetworkContext, peer: &PeerId) {
|
|
trace_time!("sync::disconnected");
|
|
if io.subprotocol_name() != WARP_SYNC_PROTOCOL_ID {
|
|
self.sync.write().on_peer_aborting(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer);
|
|
}
|
|
}
|
|
|
|
fn timeout(&self, io: &NetworkContext, timer: TimerToken) {
|
|
trace_time!("sync::timeout");
|
|
let mut io = NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay);
|
|
match timer {
|
|
PEERS_TIMER => self.sync.write().maintain_peers(&mut io),
|
|
SYNC_TIMER => self.sync.write().maintain_sync(&mut io),
|
|
TX_TIMER => {
|
|
self.sync.write().propagate_new_transactions(&mut io);
|
|
},
|
|
_ => warn!("Unknown timer {} triggered.", timer),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ChainNotify for EthSync {
|
|
fn new_blocks(&self,
|
|
imported: Vec<H256>,
|
|
invalid: Vec<H256>,
|
|
route: ChainRoute,
|
|
sealed: Vec<H256>,
|
|
proposed: Vec<Bytes>,
|
|
_duration: Duration)
|
|
{
|
|
use light::net::Announcement;
|
|
|
|
self.network.with_context(self.subprotocol_name, |context| {
|
|
let mut sync_io = NetSyncIo::new(context, &*self.eth_handler.chain, &*self.eth_handler.snapshot_service,
|
|
&self.eth_handler.overlay);
|
|
self.eth_handler.sync.write().chain_new_blocks(
|
|
&mut sync_io,
|
|
&imported,
|
|
&invalid,
|
|
route.enacted(),
|
|
route.retracted(),
|
|
&sealed,
|
|
&proposed);
|
|
});
|
|
|
|
self.network.with_context(self.light_subprotocol_name, |context| {
|
|
let light_proto = match self.light_proto.as_ref() {
|
|
Some(lp) => lp,
|
|
None => return,
|
|
};
|
|
|
|
let chain_info = self.eth_handler.chain.chain_info();
|
|
light_proto.make_announcement(&context, Announcement {
|
|
head_hash: chain_info.best_block_hash,
|
|
head_num: chain_info.best_block_number,
|
|
head_td: chain_info.total_difficulty,
|
|
reorg_depth: 0, // recalculated on a per-peer basis.
|
|
serve_headers: false, // these fields consist of _changes_ in capability.
|
|
serve_state_since: None,
|
|
serve_chain_since: None,
|
|
tx_relay: false,
|
|
})
|
|
})
|
|
}
|
|
|
|
fn start(&self) {
|
|
match self.network.start() {
|
|
Err((err, listen_address)) => {
|
|
match err.into() {
|
|
ErrorKind::Io(ref e) if e.kind() == io::ErrorKind::AddrInUse => {
|
|
warn!("Network port {:?} is already in use, make sure that another instance of an Ethereum client is not running or change the port using the --port option.", listen_address.expect("Listen address is not set."))
|
|
},
|
|
err => warn!("Error starting network: {}", err),
|
|
}
|
|
},
|
|
_ => {},
|
|
}
|
|
|
|
self.network.register_protocol(self.eth_handler.clone(), self.subprotocol_name, &[ETH_PROTOCOL_VERSION_62, ETH_PROTOCOL_VERSION_63])
|
|
.unwrap_or_else(|e| warn!("Error registering ethereum protocol: {:?}", e));
|
|
// register the warp sync subprotocol
|
|
self.network.register_protocol(self.eth_handler.clone(), WARP_SYNC_PROTOCOL_ID, &[PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3])
|
|
.unwrap_or_else(|e| warn!("Error registering snapshot sync protocol: {:?}", e));
|
|
|
|
// register the light protocol.
|
|
if let Some(light_proto) = self.light_proto.as_ref().map(|x| x.clone()) {
|
|
self.network.register_protocol(light_proto, self.light_subprotocol_name, ::light::net::PROTOCOL_VERSIONS)
|
|
.unwrap_or_else(|e| warn!("Error registering light client protocol: {:?}", e));
|
|
}
|
|
|
|
// register any attached protocols.
|
|
for proto in &self.attached_protos { proto.register(&self.network) }
|
|
}
|
|
|
|
fn stop(&self) {
|
|
self.eth_handler.snapshot_service.abort_restore();
|
|
self.network.stop();
|
|
}
|
|
|
|
fn broadcast(&self, message_type: ChainMessageType) {
|
|
self.network.with_context(WARP_SYNC_PROTOCOL_ID, |context| {
|
|
let mut sync_io = NetSyncIo::new(context, &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, &self.eth_handler.overlay);
|
|
match message_type {
|
|
ChainMessageType::Consensus(message) => self.eth_handler.sync.write().propagate_consensus_packet(&mut sync_io, message),
|
|
ChainMessageType::PrivateTransaction(message) => self.eth_handler.sync.write().propagate_private_transaction(&mut sync_io, message),
|
|
ChainMessageType::SignedPrivateTransaction(message) => self.eth_handler.sync.write().propagate_signed_private_transaction(&mut sync_io, message),
|
|
}
|
|
});
|
|
}
|
|
|
|
fn transactions_received(&self, txs: &[UnverifiedTransaction], peer_id: PeerId) {
|
|
let mut sync = self.eth_handler.sync.write();
|
|
sync.transactions_received(txs, peer_id);
|
|
}
|
|
}
|
|
|
|
/// PIP event handler.
|
|
/// Simply queues transactions from light client peers.
|
|
struct TxRelay(Arc<BlockChainClient>);
|
|
|
|
impl LightHandler for TxRelay {
|
|
fn on_transactions(&self, ctx: &EventContext, relay: &[::transaction::UnverifiedTransaction]) {
|
|
trace!(target: "pip", "Relaying {} transactions from peer {}", relay.len(), ctx.peer());
|
|
self.0.queue_transactions(relay.iter().map(|tx| ::rlp::encode(tx).into_vec()).collect(), ctx.peer())
|
|
}
|
|
}
|
|
|
|
/// Trait for managing network
|
|
pub trait ManageNetwork : Send + Sync {
|
|
/// Set to allow unreserved peers to connect
|
|
fn accept_unreserved_peers(&self);
|
|
/// Set to deny unreserved peers to connect
|
|
fn deny_unreserved_peers(&self);
|
|
/// Remove reservation for the peer
|
|
fn remove_reserved_peer(&self, peer: String) -> Result<(), String>;
|
|
/// Add reserved peer
|
|
fn add_reserved_peer(&self, peer: String) -> Result<(), String>;
|
|
/// Start network
|
|
fn start_network(&self);
|
|
/// Stop network
|
|
fn stop_network(&self);
|
|
/// Returns the minimum and maximum peers.
|
|
/// Note that `range.end` is *exclusive*.
|
|
// TODO: Range should be changed to RangeInclusive once stable (https://github.com/rust-lang/rust/pull/50758)
|
|
fn num_peers_range(&self) -> Range<u32>;
|
|
/// Get network context for protocol.
|
|
fn with_proto_context(&self, proto: ProtocolId, f: &mut FnMut(&NetworkContext));
|
|
}
|
|
|
|
impl ManageNetwork for EthSync {
|
|
fn accept_unreserved_peers(&self) {
|
|
self.network.set_non_reserved_mode(NonReservedPeerMode::Accept);
|
|
}
|
|
|
|
fn deny_unreserved_peers(&self) {
|
|
self.network.set_non_reserved_mode(NonReservedPeerMode::Deny);
|
|
}
|
|
|
|
fn remove_reserved_peer(&self, peer: String) -> Result<(), String> {
|
|
self.network.remove_reserved_peer(&peer).map_err(|e| format!("{:?}", e))
|
|
}
|
|
|
|
fn add_reserved_peer(&self, peer: String) -> Result<(), String> {
|
|
self.network.add_reserved_peer(&peer).map_err(|e| format!("{:?}", e))
|
|
}
|
|
|
|
fn start_network(&self) {
|
|
self.start();
|
|
}
|
|
|
|
fn stop_network(&self) {
|
|
self.network.with_context(self.subprotocol_name, |context| {
|
|
let mut sync_io = NetSyncIo::new(context, &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, &self.eth_handler.overlay);
|
|
self.eth_handler.sync.write().abort(&mut sync_io);
|
|
});
|
|
|
|
if let Some(light_proto) = self.light_proto.as_ref() {
|
|
light_proto.abort();
|
|
}
|
|
|
|
self.stop();
|
|
}
|
|
|
|
fn num_peers_range(&self) -> Range<u32> {
|
|
self.network.num_peers_range()
|
|
}
|
|
|
|
fn with_proto_context(&self, proto: ProtocolId, f: &mut FnMut(&NetworkContext)) {
|
|
self.network.with_context_eval(proto, f);
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
/// Network service configuration
|
|
pub struct NetworkConfiguration {
|
|
/// Directory path to store general network configuration. None means nothing will be saved
|
|
pub config_path: Option<String>,
|
|
/// Directory path to store network-specific configuration. None means nothing will be saved
|
|
pub net_config_path: Option<String>,
|
|
/// IP address to listen for incoming connections. Listen to all connections by default
|
|
pub listen_address: Option<String>,
|
|
/// IP address to advertise. Detected automatically if none.
|
|
pub public_address: Option<String>,
|
|
/// Port for UDP connections, same as TCP by default
|
|
pub udp_port: Option<u16>,
|
|
/// Enable NAT configuration
|
|
pub nat_enabled: bool,
|
|
/// Enable discovery
|
|
pub discovery_enabled: bool,
|
|
/// List of initial node addresses
|
|
pub boot_nodes: Vec<String>,
|
|
/// Use provided node key instead of default
|
|
pub use_secret: Option<Secret>,
|
|
/// Max number of connected peers to maintain
|
|
pub max_peers: u32,
|
|
/// Min number of connected peers to maintain
|
|
pub min_peers: u32,
|
|
/// Max pending peers.
|
|
pub max_pending_peers: u32,
|
|
/// Reserved snapshot sync peers.
|
|
pub snapshot_peers: u32,
|
|
/// List of reserved node addresses.
|
|
pub reserved_nodes: Vec<String>,
|
|
/// The non-reserved peer mode.
|
|
pub allow_non_reserved: bool,
|
|
/// IP Filtering
|
|
pub ip_filter: IpFilter,
|
|
/// Client version string
|
|
pub client_version: String,
|
|
}
|
|
|
|
impl NetworkConfiguration {
|
|
/// Create a new default config.
|
|
pub fn new() -> Self {
|
|
From::from(BasicNetworkConfiguration::new())
|
|
}
|
|
|
|
/// Create a new local config.
|
|
pub fn new_local() -> Self {
|
|
From::from(BasicNetworkConfiguration::new_local())
|
|
}
|
|
|
|
/// Attempt to convert this config into a BasicNetworkConfiguration.
|
|
pub fn into_basic(self) -> Result<BasicNetworkConfiguration, AddrParseError> {
|
|
Ok(BasicNetworkConfiguration {
|
|
config_path: self.config_path,
|
|
net_config_path: self.net_config_path,
|
|
listen_address: match self.listen_address { None => None, Some(addr) => Some(SocketAddr::from_str(&addr)?) },
|
|
public_address: match self.public_address { None => None, Some(addr) => Some(SocketAddr::from_str(&addr)?) },
|
|
udp_port: self.udp_port,
|
|
nat_enabled: self.nat_enabled,
|
|
discovery_enabled: self.discovery_enabled,
|
|
boot_nodes: self.boot_nodes,
|
|
use_secret: self.use_secret,
|
|
max_peers: self.max_peers,
|
|
min_peers: self.min_peers,
|
|
max_handshakes: self.max_pending_peers,
|
|
reserved_protocols: hash_map![WARP_SYNC_PROTOCOL_ID => self.snapshot_peers],
|
|
reserved_nodes: self.reserved_nodes,
|
|
ip_filter: self.ip_filter,
|
|
non_reserved_mode: if self.allow_non_reserved { NonReservedPeerMode::Accept } else { NonReservedPeerMode::Deny },
|
|
client_version: self.client_version,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl From<BasicNetworkConfiguration> for NetworkConfiguration {
|
|
fn from(other: BasicNetworkConfiguration) -> Self {
|
|
NetworkConfiguration {
|
|
config_path: other.config_path,
|
|
net_config_path: other.net_config_path,
|
|
listen_address: other.listen_address.and_then(|addr| Some(format!("{}", addr))),
|
|
public_address: other.public_address.and_then(|addr| Some(format!("{}", addr))),
|
|
udp_port: other.udp_port,
|
|
nat_enabled: other.nat_enabled,
|
|
discovery_enabled: other.discovery_enabled,
|
|
boot_nodes: other.boot_nodes,
|
|
use_secret: other.use_secret,
|
|
max_peers: other.max_peers,
|
|
min_peers: other.min_peers,
|
|
max_pending_peers: other.max_handshakes,
|
|
snapshot_peers: *other.reserved_protocols.get(&WARP_SYNC_PROTOCOL_ID).unwrap_or(&0),
|
|
reserved_nodes: other.reserved_nodes,
|
|
ip_filter: other.ip_filter,
|
|
allow_non_reserved: match other.non_reserved_mode { NonReservedPeerMode::Accept => true, _ => false } ,
|
|
client_version: other.client_version,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Configuration for IPC service.
|
|
#[derive(Debug, Clone)]
|
|
pub struct ServiceConfiguration {
|
|
/// Sync config.
|
|
pub sync: SyncConfig,
|
|
/// Network configuration.
|
|
pub net: NetworkConfiguration,
|
|
/// IPC path.
|
|
pub io_path: String,
|
|
}
|
|
|
|
/// Numbers of peers (max, min, active).
|
|
#[derive(Debug, Clone)]
|
|
pub struct PeerNumbers {
|
|
/// Number of connected peers.
|
|
pub connected: usize,
|
|
/// Number of active peers.
|
|
pub active: usize,
|
|
/// Max peers.
|
|
pub max: usize,
|
|
/// Min peers.
|
|
pub min: usize,
|
|
}
|
|
|
|
/// Light synchronization.
|
|
pub trait LightSyncProvider {
|
|
/// Get peer numbers.
|
|
fn peer_numbers(&self) -> PeerNumbers;
|
|
|
|
/// Get peers information
|
|
fn peers(&self) -> Vec<PeerInfo>;
|
|
|
|
/// Get network id.
|
|
fn network_id(&self) -> u64;
|
|
|
|
/// Get the enode if available.
|
|
fn enode(&self) -> Option<String>;
|
|
|
|
/// Returns propagation count for pending transactions.
|
|
fn transactions_stats(&self) -> BTreeMap<H256, TransactionStats>;
|
|
}
|
|
|
|
/// Configuration for the light sync.
|
|
pub struct LightSyncParams<L> {
|
|
/// Network configuration.
|
|
pub network_config: BasicNetworkConfiguration,
|
|
/// Light client to sync to.
|
|
pub client: Arc<L>,
|
|
/// Network ID.
|
|
pub network_id: u64,
|
|
/// Subprotocol name.
|
|
pub subprotocol_name: [u8; 3],
|
|
/// Other handlers to attach.
|
|
pub handlers: Vec<Arc<LightHandler>>,
|
|
/// Other subprotocols to run.
|
|
pub attached_protos: Vec<AttachedProtocol>,
|
|
}
|
|
|
|
/// Service for light synchronization.
|
|
pub struct LightSync {
|
|
proto: Arc<LightProtocol>,
|
|
sync: Arc<::light_sync::SyncInfo + Sync + Send>,
|
|
attached_protos: Vec<AttachedProtocol>,
|
|
network: NetworkService,
|
|
subprotocol_name: [u8; 3],
|
|
network_id: u64,
|
|
}
|
|
|
|
impl LightSync {
|
|
/// Create a new light sync service.
|
|
pub fn new<L>(params: LightSyncParams<L>) -> Result<Self, Error>
|
|
where L: AsLightClient + Provider + Sync + Send + 'static
|
|
{
|
|
use light_sync::LightSync as SyncHandler;
|
|
|
|
// initialize light protocol handler and attach sync module.
|
|
let (sync, light_proto) = {
|
|
let light_params = LightParams {
|
|
network_id: params.network_id,
|
|
config: Default::default(),
|
|
capabilities: Capabilities {
|
|
serve_headers: false,
|
|
serve_chain_since: None,
|
|
serve_state_since: None,
|
|
tx_relay: false,
|
|
},
|
|
sample_store: None,
|
|
};
|
|
|
|
let mut light_proto = LightProtocol::new(params.client.clone(), light_params);
|
|
let sync_handler = Arc::new(SyncHandler::new(params.client.clone())?);
|
|
light_proto.add_handler(sync_handler.clone());
|
|
|
|
for handler in params.handlers {
|
|
light_proto.add_handler(handler);
|
|
}
|
|
|
|
(sync_handler, Arc::new(light_proto))
|
|
};
|
|
|
|
let service = NetworkService::new(params.network_config, None)?;
|
|
|
|
Ok(LightSync {
|
|
proto: light_proto,
|
|
sync: sync,
|
|
attached_protos: params.attached_protos,
|
|
network: service,
|
|
subprotocol_name: params.subprotocol_name,
|
|
network_id: params.network_id,
|
|
})
|
|
}
|
|
|
|
/// Execute a closure with a protocol context.
|
|
pub fn with_context<F, T>(&self, f: F) -> Option<T>
|
|
where F: FnOnce(&::light::net::BasicContext) -> T
|
|
{
|
|
self.network.with_context_eval(
|
|
self.subprotocol_name,
|
|
move |ctx| self.proto.with_context(&ctx, f),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl ::std::ops::Deref for LightSync {
|
|
type Target = ::light_sync::SyncInfo;
|
|
|
|
fn deref(&self) -> &Self::Target { &*self.sync }
|
|
}
|
|
|
|
impl ManageNetwork for LightSync {
|
|
fn accept_unreserved_peers(&self) {
|
|
self.network.set_non_reserved_mode(NonReservedPeerMode::Accept);
|
|
}
|
|
|
|
fn deny_unreserved_peers(&self) {
|
|
self.network.set_non_reserved_mode(NonReservedPeerMode::Deny);
|
|
}
|
|
|
|
fn remove_reserved_peer(&self, peer: String) -> Result<(), String> {
|
|
self.network.remove_reserved_peer(&peer).map_err(|e| format!("{:?}", e))
|
|
}
|
|
|
|
fn add_reserved_peer(&self, peer: String) -> Result<(), String> {
|
|
self.network.add_reserved_peer(&peer).map_err(|e| format!("{:?}", e))
|
|
}
|
|
|
|
fn start_network(&self) {
|
|
match self.network.start() {
|
|
Err((err, listen_address)) => {
|
|
match err.into() {
|
|
ErrorKind::Io(ref e) if e.kind() == io::ErrorKind::AddrInUse => {
|
|
warn!("Network port {:?} is already in use, make sure that another instance of an Ethereum client is not running or change the port using the --port option.", listen_address.expect("Listen address is not set."))
|
|
},
|
|
err => warn!("Error starting network: {}", err),
|
|
}
|
|
},
|
|
_ => {},
|
|
}
|
|
|
|
let light_proto = self.proto.clone();
|
|
|
|
self.network.register_protocol(light_proto, self.subprotocol_name, ::light::net::PROTOCOL_VERSIONS)
|
|
.unwrap_or_else(|e| warn!("Error registering light client protocol: {:?}", e));
|
|
|
|
for proto in &self.attached_protos { proto.register(&self.network) }
|
|
}
|
|
|
|
fn stop_network(&self) {
|
|
self.proto.abort();
|
|
self.network.stop();
|
|
}
|
|
|
|
fn num_peers_range(&self) -> Range<u32> {
|
|
self.network.num_peers_range()
|
|
}
|
|
|
|
fn with_proto_context(&self, proto: ProtocolId, f: &mut FnMut(&NetworkContext)) {
|
|
self.network.with_context_eval(proto, f);
|
|
}
|
|
}
|
|
|
|
impl LightSyncProvider for LightSync {
|
|
fn peer_numbers(&self) -> PeerNumbers {
|
|
let (connected, active) = self.proto.peer_count();
|
|
let peers_range = self.num_peers_range();
|
|
debug_assert!(peers_range.end > peers_range.start);
|
|
PeerNumbers {
|
|
connected: connected,
|
|
active: active,
|
|
max: peers_range.end as usize - 1,
|
|
min: peers_range.start as usize,
|
|
}
|
|
}
|
|
|
|
fn peers(&self) -> Vec<PeerInfo> {
|
|
self.network.with_context_eval(self.subprotocol_name, |ctx| {
|
|
let peer_ids = self.network.connected_peers();
|
|
|
|
peer_ids.into_iter().filter_map(|peer_id| {
|
|
let session_info = match ctx.session_info(peer_id) {
|
|
None => return None,
|
|
Some(info) => info,
|
|
};
|
|
|
|
Some(PeerInfo {
|
|
id: session_info.id.map(|id| format!("{:x}", id)),
|
|
client_version: session_info.client_version,
|
|
capabilities: session_info.peer_capabilities.into_iter().map(|c| c.to_string()).collect(),
|
|
remote_address: session_info.remote_address,
|
|
local_address: session_info.local_address,
|
|
eth_info: None,
|
|
pip_info: self.proto.peer_status(&peer_id).map(Into::into),
|
|
})
|
|
}).collect()
|
|
}).unwrap_or_else(Vec::new)
|
|
}
|
|
|
|
fn enode(&self) -> Option<String> {
|
|
self.network.external_url()
|
|
}
|
|
|
|
fn network_id(&self) -> u64 {
|
|
self.network_id
|
|
}
|
|
|
|
fn transactions_stats(&self) -> BTreeMap<H256, TransactionStats> {
|
|
Default::default() // TODO
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn light_params_load_share_depends_on_max_peers() {
|
|
let pruning_info = PruningInfo {
|
|
earliest_chain: 0,
|
|
earliest_state: 0,
|
|
};
|
|
let params1 = light_params(0, 10, pruning_info.clone(), None);
|
|
let params2 = light_params(0, 20, pruning_info, None);
|
|
assert!(params1.config.load_share > params2.config.load_share)
|
|
}
|
|
}
|