Reformat the source code

This commit is contained in:
Artem Vorotnikov
2020-08-05 07:08:03 +03:00
parent 253ff3f37b
commit 610d9baba4
742 changed files with 175791 additions and 141379 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -17,138 +17,221 @@
use block_sync::BlockRequest;
use bytes::Bytes;
use ethereum_types::H256;
use network::{PeerId};
use network::PeerId;
use rlp::RlpStream;
use std::time::Instant;
use sync_io::SyncIo;
use types::BlockNumber;
use super::sync_packet::SyncPacket;
use super::sync_packet::SyncPacket::{
GetBlockHeadersPacket,
GetBlockBodiesPacket,
GetReceiptsPacket,
GetSnapshotManifestPacket,
GetSnapshotDataPacket,
use super::sync_packet::{
SyncPacket,
SyncPacket::{
GetBlockBodiesPacket, GetBlockHeadersPacket, GetReceiptsPacket, GetSnapshotDataPacket,
GetSnapshotManifestPacket,
},
};
use super::{
BlockSet,
ChainSync,
PeerAsking,
};
use super::{BlockSet, ChainSync, PeerAsking};
/// The Chain Sync Requester: requesting data to other peers
pub struct SyncRequester;
impl SyncRequester {
/// Perform block download request`
pub fn request_blocks(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, request: BlockRequest, block_set: BlockSet) {
match request {
BlockRequest::Headers { start, count, skip } => {
SyncRequester::request_headers_by_hash(sync, io, peer_id, &start, count, skip, false, block_set);
},
BlockRequest::Bodies { hashes } => {
SyncRequester::request_bodies(sync, io, peer_id, hashes, block_set);
},
BlockRequest::Receipts { hashes } => {
SyncRequester::request_receipts(sync, io, peer_id, hashes, block_set);
},
}
}
/// Perform block download request`
pub fn request_blocks(
sync: &mut ChainSync,
io: &mut SyncIo,
peer_id: PeerId,
request: BlockRequest,
block_set: BlockSet,
) {
match request {
BlockRequest::Headers { start, count, skip } => {
SyncRequester::request_headers_by_hash(
sync, io, peer_id, &start, count, skip, false, block_set,
);
}
BlockRequest::Bodies { hashes } => {
SyncRequester::request_bodies(sync, io, peer_id, hashes, block_set);
}
BlockRequest::Receipts { hashes } => {
SyncRequester::request_receipts(sync, io, peer_id, hashes, block_set);
}
}
}
/// Request block bodies from a peer
fn request_bodies(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, hashes: Vec<H256>, set: BlockSet) {
let mut rlp = RlpStream::new_list(hashes.len());
trace!(target: "sync", "{} <- GetBlockBodies: {} entries starting from {:?}, set = {:?}", peer_id, hashes.len(), hashes.first(), set);
for h in &hashes {
rlp.append(&h.clone());
}
SyncRequester::send_request(sync, io, peer_id, PeerAsking::BlockBodies, GetBlockBodiesPacket, rlp.out());
let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed");
peer.asking_blocks = hashes;
peer.block_set = Some(set);
}
/// Request block bodies from a peer
fn request_bodies(
sync: &mut ChainSync,
io: &mut SyncIo,
peer_id: PeerId,
hashes: Vec<H256>,
set: BlockSet,
) {
let mut rlp = RlpStream::new_list(hashes.len());
trace!(target: "sync", "{} <- GetBlockBodies: {} entries starting from {:?}, set = {:?}", peer_id, hashes.len(), hashes.first(), set);
for h in &hashes {
rlp.append(&h.clone());
}
SyncRequester::send_request(
sync,
io,
peer_id,
PeerAsking::BlockBodies,
GetBlockBodiesPacket,
rlp.out(),
);
let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed");
peer.asking_blocks = hashes;
peer.block_set = Some(set);
}
/// Request headers from a peer by block number
pub fn request_fork_header(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, n: BlockNumber) {
trace!(target: "sync", "{} <- GetForkHeader: at {}", peer_id, n);
let mut rlp = RlpStream::new_list(4);
rlp.append(&n);
rlp.append(&1u32);
rlp.append(&0u32);
rlp.append(&0u32);
SyncRequester::send_request(sync, io, peer_id, PeerAsking::ForkHeader, GetBlockHeadersPacket, rlp.out());
}
/// Request headers from a peer by block number
pub fn request_fork_header(
sync: &mut ChainSync,
io: &mut SyncIo,
peer_id: PeerId,
n: BlockNumber,
) {
trace!(target: "sync", "{} <- GetForkHeader: at {}", peer_id, n);
let mut rlp = RlpStream::new_list(4);
rlp.append(&n);
rlp.append(&1u32);
rlp.append(&0u32);
rlp.append(&0u32);
SyncRequester::send_request(
sync,
io,
peer_id,
PeerAsking::ForkHeader,
GetBlockHeadersPacket,
rlp.out(),
);
}
/// Find some headers or blocks to download for a peer.
pub fn request_snapshot_data(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId) {
// find chunk data to download
if let Some(hash) = sync.snapshot.needed_chunk() {
if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) {
peer.asking_snapshot_data = Some(hash.clone());
}
SyncRequester::request_snapshot_chunk(sync, io, peer_id, &hash);
}
}
/// Find some headers or blocks to download for a peer.
pub fn request_snapshot_data(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId) {
// find chunk data to download
if let Some(hash) = sync.snapshot.needed_chunk() {
if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) {
peer.asking_snapshot_data = Some(hash.clone());
}
SyncRequester::request_snapshot_chunk(sync, io, peer_id, &hash);
}
}
/// Request snapshot manifest from a peer.
pub fn request_snapshot_manifest(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId) {
trace!(target: "sync", "{} <- GetSnapshotManifest", peer_id);
let rlp = RlpStream::new_list(0);
SyncRequester::send_request(sync, io, peer_id, PeerAsking::SnapshotManifest, GetSnapshotManifestPacket, rlp.out());
}
/// Request snapshot manifest from a peer.
pub fn request_snapshot_manifest(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId) {
trace!(target: "sync", "{} <- GetSnapshotManifest", peer_id);
let rlp = RlpStream::new_list(0);
SyncRequester::send_request(
sync,
io,
peer_id,
PeerAsking::SnapshotManifest,
GetSnapshotManifestPacket,
rlp.out(),
);
}
/// Request headers from a peer by block hash
fn request_headers_by_hash(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, h: &H256, count: u64, skip: u64, reverse: bool, set: BlockSet) {
trace!(target: "sync", "{} <- GetBlockHeaders: {} entries starting from {}, set = {:?}", peer_id, count, h, set);
let mut rlp = RlpStream::new_list(4);
rlp.append(h);
rlp.append(&count);
rlp.append(&skip);
rlp.append(&if reverse {1u32} else {0u32});
SyncRequester::send_request(sync, io, peer_id, PeerAsking::BlockHeaders, GetBlockHeadersPacket, rlp.out());
let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed");
peer.asking_hash = Some(h.clone());
peer.block_set = Some(set);
}
/// Request headers from a peer by block hash
fn request_headers_by_hash(
sync: &mut ChainSync,
io: &mut SyncIo,
peer_id: PeerId,
h: &H256,
count: u64,
skip: u64,
reverse: bool,
set: BlockSet,
) {
trace!(target: "sync", "{} <- GetBlockHeaders: {} entries starting from {}, set = {:?}", peer_id, count, h, set);
let mut rlp = RlpStream::new_list(4);
rlp.append(h);
rlp.append(&count);
rlp.append(&skip);
rlp.append(&if reverse { 1u32 } else { 0u32 });
SyncRequester::send_request(
sync,
io,
peer_id,
PeerAsking::BlockHeaders,
GetBlockHeadersPacket,
rlp.out(),
);
let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed");
peer.asking_hash = Some(h.clone());
peer.block_set = Some(set);
}
/// Request block receipts from a peer
fn request_receipts(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, hashes: Vec<H256>, set: BlockSet) {
let mut rlp = RlpStream::new_list(hashes.len());
trace!(target: "sync", "{} <- GetBlockReceipts: {} entries starting from {:?}, set = {:?}", peer_id, hashes.len(), hashes.first(), set);
for h in &hashes {
rlp.append(&h.clone());
}
SyncRequester::send_request(sync, io, peer_id, PeerAsking::BlockReceipts, GetReceiptsPacket, rlp.out());
let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed");
peer.asking_blocks = hashes;
peer.block_set = Some(set);
}
/// Request block receipts from a peer
fn request_receipts(
sync: &mut ChainSync,
io: &mut SyncIo,
peer_id: PeerId,
hashes: Vec<H256>,
set: BlockSet,
) {
let mut rlp = RlpStream::new_list(hashes.len());
trace!(target: "sync", "{} <- GetBlockReceipts: {} entries starting from {:?}, set = {:?}", peer_id, hashes.len(), hashes.first(), set);
for h in &hashes {
rlp.append(&h.clone());
}
SyncRequester::send_request(
sync,
io,
peer_id,
PeerAsking::BlockReceipts,
GetReceiptsPacket,
rlp.out(),
);
let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed");
peer.asking_blocks = hashes;
peer.block_set = Some(set);
}
/// Request snapshot chunk from a peer.
fn request_snapshot_chunk(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, chunk: &H256) {
trace!(target: "sync", "{} <- GetSnapshotData {:?}", peer_id, chunk);
let mut rlp = RlpStream::new_list(1);
rlp.append(chunk);
SyncRequester::send_request(sync, io, peer_id, PeerAsking::SnapshotData, GetSnapshotDataPacket, rlp.out());
}
/// Request snapshot chunk from a peer.
fn request_snapshot_chunk(
sync: &mut ChainSync,
io: &mut SyncIo,
peer_id: PeerId,
chunk: &H256,
) {
trace!(target: "sync", "{} <- GetSnapshotData {:?}", peer_id, chunk);
let mut rlp = RlpStream::new_list(1);
rlp.append(chunk);
SyncRequester::send_request(
sync,
io,
peer_id,
PeerAsking::SnapshotData,
GetSnapshotDataPacket,
rlp.out(),
);
}
/// Generic request sender
fn send_request(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, asking: PeerAsking, packet_id: SyncPacket, packet: Bytes) {
if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) {
if peer.asking != PeerAsking::Nothing {
warn!(target:"sync", "Asking {:?} while requesting {:?}", peer.asking, asking);
}
peer.asking = asking;
peer.ask_time = Instant::now();
/// Generic request sender
fn send_request(
sync: &mut ChainSync,
io: &mut SyncIo,
peer_id: PeerId,
asking: PeerAsking,
packet_id: SyncPacket,
packet: Bytes,
) {
if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) {
if peer.asking != PeerAsking::Nothing {
warn!(target:"sync", "Asking {:?} while requesting {:?}", peer.asking, asking);
}
peer.asking = asking;
peer.ask_time = Instant::now();
let result = io.send(peer_id, packet_id, packet);
let result = io.send(peer_id, packet_id, packet);
if let Err(e) = result {
debug!(target:"sync", "Error sending request: {:?}", e);
io.disconnect_peer(peer_id);
}
}
}
if let Err(e) = result {
debug!(target:"sync", "Error sending request: {:?}", e);
io.disconnect_peer(peer_id);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -34,27 +34,27 @@ use network::{PacketId, ProtocolId};
enum_from_primitive! {
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum SyncPacket {
StatusPacket = 0x00,
NewBlockHashesPacket = 0x01,
TransactionsPacket = 0x02,
GetBlockHeadersPacket = 0x03,
BlockHeadersPacket = 0x04,
GetBlockBodiesPacket = 0x05,
BlockBodiesPacket = 0x06,
NewBlockPacket = 0x07,
StatusPacket = 0x00,
NewBlockHashesPacket = 0x01,
TransactionsPacket = 0x02,
GetBlockHeadersPacket = 0x03,
BlockHeadersPacket = 0x04,
GetBlockBodiesPacket = 0x05,
BlockBodiesPacket = 0x06,
NewBlockPacket = 0x07,
GetNodeDataPacket = 0x0d,
NodeDataPacket = 0x0e,
GetReceiptsPacket = 0x0f,
ReceiptsPacket = 0x10,
GetNodeDataPacket = 0x0d,
NodeDataPacket = 0x0e,
GetReceiptsPacket = 0x0f,
ReceiptsPacket = 0x10,
GetSnapshotManifestPacket = 0x11,
SnapshotManifestPacket = 0x12,
GetSnapshotDataPacket = 0x13,
SnapshotDataPacket = 0x14,
ConsensusDataPacket = 0x15,
PrivateTransactionPacket = 0x16,
SignedPrivateTransactionPacket = 0x17,
GetSnapshotManifestPacket = 0x11,
SnapshotManifestPacket = 0x12,
GetSnapshotDataPacket = 0x13,
SnapshotDataPacket = 0x14,
ConsensusDataPacket = 0x15,
PrivateTransactionPacket = 0x16,
SignedPrivateTransactionPacket = 0x17,
}
}
@@ -63,79 +63,73 @@ use self::SyncPacket::*;
/// Provide both subprotocol and packet id information within the
/// same object.
pub trait PacketInfo {
fn id(&self) -> PacketId;
fn protocol(&self) -> ProtocolId;
fn id(&self) -> PacketId;
fn protocol(&self) -> ProtocolId;
}
// The mechanism to match packet ids and protocol may be improved
// through some macro magic, but for now this works.
impl PacketInfo for SyncPacket {
fn protocol(&self) -> ProtocolId {
match self {
StatusPacket |
NewBlockHashesPacket |
TransactionsPacket |
GetBlockHeadersPacket |
BlockHeadersPacket |
GetBlockBodiesPacket |
BlockBodiesPacket |
NewBlockPacket |
fn protocol(&self) -> ProtocolId {
match self {
StatusPacket
| NewBlockHashesPacket
| TransactionsPacket
| GetBlockHeadersPacket
| BlockHeadersPacket
| GetBlockBodiesPacket
| BlockBodiesPacket
| NewBlockPacket
| GetNodeDataPacket
| NodeDataPacket
| GetReceiptsPacket
| ReceiptsPacket => ETH_PROTOCOL,
GetNodeDataPacket|
NodeDataPacket |
GetReceiptsPacket |
ReceiptsPacket
GetSnapshotManifestPacket
| SnapshotManifestPacket
| GetSnapshotDataPacket
| SnapshotDataPacket
| ConsensusDataPacket
| PrivateTransactionPacket
| SignedPrivateTransactionPacket => WARP_SYNC_PROTOCOL_ID,
}
}
=> ETH_PROTOCOL,
GetSnapshotManifestPacket|
SnapshotManifestPacket |
GetSnapshotDataPacket |
SnapshotDataPacket |
ConsensusDataPacket |
PrivateTransactionPacket |
SignedPrivateTransactionPacket
=> WARP_SYNC_PROTOCOL_ID,
}
}
fn id(&self) -> PacketId {
(*self) as PacketId
}
fn id(&self) -> PacketId {
(*self) as PacketId
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::*;
use enum_primitive::FromPrimitive;
use enum_primitive::FromPrimitive;
#[test]
fn packet_ids_from_u8_when_from_primitive_zero_then_equals_status_packet() {
assert_eq!(SyncPacket::from_u8(0x00), Some(StatusPacket));
}
#[test]
fn packet_ids_from_u8_when_from_primitive_zero_then_equals_status_packet() {
assert_eq!(SyncPacket::from_u8(0x00), Some(StatusPacket));
}
#[test]
fn packet_ids_from_u8_when_from_primitive_eleven_then_equals_get_snapshot_manifest_packet() {
assert_eq!(SyncPacket::from_u8(0x11), Some(GetSnapshotManifestPacket));
}
#[test]
fn packet_ids_from_u8_when_from_primitive_eleven_then_equals_get_snapshot_manifest_packet() {
assert_eq!(SyncPacket::from_u8(0x11), Some(GetSnapshotManifestPacket));
}
#[test]
fn packet_ids_from_u8_when_invalid_packet_id_then_none() {
assert!(SyncPacket::from_u8(0x99).is_none());
}
#[test]
fn packet_ids_from_u8_when_invalid_packet_id_then_none() {
assert!(SyncPacket::from_u8(0x99).is_none());
}
#[test]
fn when_status_packet_then_id_and_protocol_match() {
assert_eq!(StatusPacket.id(), StatusPacket as PacketId);
assert_eq!(StatusPacket.protocol(), ETH_PROTOCOL);
}
#[test]
fn when_status_packet_then_id_and_protocol_match() {
assert_eq!(StatusPacket.id(), StatusPacket as PacketId);
assert_eq!(StatusPacket.protocol(), ETH_PROTOCOL);
}
#[test]
fn when_consensus_data_packet_then_id_and_protocol_match() {
assert_eq!(ConsensusDataPacket.id(), ConsensusDataPacket as PacketId);
assert_eq!(ConsensusDataPacket.protocol(), WARP_SYNC_PROTOCOL_ID);
}
#[test]
fn when_consensus_data_packet_then_id_and_protocol_match() {
assert_eq!(ConsensusDataPacket.id(), ConsensusDataPacket as PacketId);
assert_eq!(ConsensusDataPacket.protocol(), WARP_SYNC_PROTOCOL_ID);
}
}

View File

@@ -39,10 +39,14 @@ extern crate triehash_ethereum;
extern crate ethcore_light as light;
#[cfg(test)] extern crate env_logger;
#[cfg(test)] extern crate ethcore_private_tx;
#[cfg(test)] extern crate kvdb_memorydb;
#[cfg(test)] extern crate rustc_hex;
#[cfg(test)]
extern crate env_logger;
#[cfg(test)]
extern crate ethcore_private_tx;
#[cfg(test)]
extern crate kvdb_memorydb;
#[cfg(test)]
extern crate rustc_hex;
#[macro_use]
extern crate enum_primitive;
@@ -55,12 +59,12 @@ extern crate heapsize;
#[macro_use]
extern crate trace_time;
mod chain;
mod blocks;
mod block_sync;
mod sync_io;
mod blocks;
mod chain;
mod private_tx;
mod snapshot;
mod sync_io;
mod transactions_stats;
pub mod light_sync;
@@ -71,7 +75,7 @@ mod tests;
mod api;
pub use api::*;
pub use chain::{SyncStatus, SyncState};
pub use chain::{SyncState, SyncStatus};
pub use devp2p::validate_node_url;
pub use network::{NonReservedPeerMode, Error, ErrorKind, ConnectionFilter, ConnectionDirection};
pub use private_tx::{PrivateTxHandler, NoopPrivateTxHandler, SimplePrivateTxHandler};
pub use network::{ConnectionDirection, ConnectionFilter, Error, ErrorKind, NonReservedPeerMode};
pub use private_tx::{NoopPrivateTxHandler, PrivateTxHandler, SimplePrivateTxHandler};

File diff suppressed because it is too large Load Diff

View File

@@ -16,79 +16,85 @@
//! Helpers for decoding and verifying responses for headers.
use types::{encoded, header::Header};
use ethereum_types::H256;
use light::request::{HashOrNumber, CompleteHeadersRequest as HeadersRequest};
use light::request::{CompleteHeadersRequest as HeadersRequest, HashOrNumber};
use rlp::DecoderError;
use std::fmt;
use types::{encoded, header::Header};
/// Errors found when decoding headers and verifying with basic constraints.
#[derive(Debug, PartialEq)]
pub enum BasicError {
/// Wrong skip value: expected, found (if any).
WrongSkip(u64, Option<u64>),
/// Wrong start number.
WrongStartNumber(u64, u64),
/// Wrong start hash.
WrongStartHash(H256, H256),
/// Too many headers.
TooManyHeaders(usize, usize),
/// Decoder error.
Decoder(DecoderError),
/// Wrong skip value: expected, found (if any).
WrongSkip(u64, Option<u64>),
/// Wrong start number.
WrongStartNumber(u64, u64),
/// Wrong start hash.
WrongStartHash(H256, H256),
/// Too many headers.
TooManyHeaders(usize, usize),
/// Decoder error.
Decoder(DecoderError),
}
impl From<DecoderError> for BasicError {
fn from(err: DecoderError) -> Self {
BasicError::Decoder(err)
}
fn from(err: DecoderError) -> Self {
BasicError::Decoder(err)
}
}
impl fmt::Display for BasicError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Header response verification error: ")?;
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Header response verification error: ")?;
match *self {
BasicError::WrongSkip(ref exp, ref got)
=> write!(f, "wrong skip (expected {}, got {:?})", exp, got),
BasicError::WrongStartNumber(ref exp, ref got)
=> write!(f, "wrong start number (expected {}, got {})", exp, got),
BasicError::WrongStartHash(ref exp, ref got)
=> write!(f, "wrong start hash (expected {}, got {})", exp, got),
BasicError::TooManyHeaders(ref max, ref got)
=> write!(f, "too many headers (max {}, got {})", max, got),
BasicError::Decoder(ref err)
=> write!(f, "{}", err),
}
}
match *self {
BasicError::WrongSkip(ref exp, ref got) => {
write!(f, "wrong skip (expected {}, got {:?})", exp, got)
}
BasicError::WrongStartNumber(ref exp, ref got) => {
write!(f, "wrong start number (expected {}, got {})", exp, got)
}
BasicError::WrongStartHash(ref exp, ref got) => {
write!(f, "wrong start hash (expected {}, got {})", exp, got)
}
BasicError::TooManyHeaders(ref max, ref got) => {
write!(f, "too many headers (max {}, got {})", max, got)
}
BasicError::Decoder(ref err) => write!(f, "{}", err),
}
}
}
/// Request verification constraint.
pub trait Constraint {
type Error;
type Error;
/// Verify headers against this.
fn verify(&self, headers: &[Header], reverse: bool) -> Result<(), Self::Error>;
/// Verify headers against this.
fn verify(&self, headers: &[Header], reverse: bool) -> Result<(), Self::Error>;
}
/// Do basic verification of provided headers against a request.
pub fn verify(headers: &[encoded::Header], request: &HeadersRequest) -> Result<Vec<Header>, BasicError> {
let headers: Result<Vec<_>, _> = headers.iter().map(|h| h.decode() ).collect();
match headers {
Ok(headers) => {
let reverse = request.reverse;
pub fn verify(
headers: &[encoded::Header],
request: &HeadersRequest,
) -> Result<Vec<Header>, BasicError> {
let headers: Result<Vec<_>, _> = headers.iter().map(|h| h.decode()).collect();
match headers {
Ok(headers) => {
let reverse = request.reverse;
Max(request.max as usize).verify(&headers, reverse)?;
match request.start {
HashOrNumber::Number(ref num) => StartsAtNumber(*num).verify(&headers, reverse)?,
HashOrNumber::Hash(ref hash) => StartsAtHash(*hash).verify(&headers, reverse)?,
}
Max(request.max as usize).verify(&headers, reverse)?;
match request.start {
HashOrNumber::Number(ref num) => StartsAtNumber(*num).verify(&headers, reverse)?,
HashOrNumber::Hash(ref hash) => StartsAtHash(*hash).verify(&headers, reverse)?,
}
SkipsBetween(request.skip).verify(&headers, reverse)?;
SkipsBetween(request.skip).verify(&headers, reverse)?;
Ok(headers)
},
Err(e) => Err(e.into())
}
Ok(headers)
}
Err(e) => Err(e.into()),
}
}
struct StartsAtNumber(u64);
@@ -97,162 +103,189 @@ struct SkipsBetween(u64);
struct Max(usize);
impl Constraint for StartsAtNumber {
type Error = BasicError;
type Error = BasicError;
fn verify(&self, headers: &[Header], _reverse: bool) -> Result<(), BasicError> {
headers.first().map_or(Ok(()), |h| {
if h.number() == self.0 {
Ok(())
} else {
Err(BasicError::WrongStartNumber(self.0, h.number()))
}
})
}
fn verify(&self, headers: &[Header], _reverse: bool) -> Result<(), BasicError> {
headers.first().map_or(Ok(()), |h| {
if h.number() == self.0 {
Ok(())
} else {
Err(BasicError::WrongStartNumber(self.0, h.number()))
}
})
}
}
impl Constraint for StartsAtHash {
type Error = BasicError;
type Error = BasicError;
fn verify(&self, headers: &[Header], _reverse: bool) -> Result<(), BasicError> {
headers.first().map_or(Ok(()), |h| {
if h.hash() == self.0 {
Ok(())
} else {
Err(BasicError::WrongStartHash(self.0, h.hash()))
}
})
}
fn verify(&self, headers: &[Header], _reverse: bool) -> Result<(), BasicError> {
headers.first().map_or(Ok(()), |h| {
if h.hash() == self.0 {
Ok(())
} else {
Err(BasicError::WrongStartHash(self.0, h.hash()))
}
})
}
}
impl Constraint for SkipsBetween {
type Error = BasicError;
type Error = BasicError;
fn verify(&self, headers: &[Header], reverse: bool) -> Result<(), BasicError> {
for pair in headers.windows(2) {
let (low, high) = if reverse { (&pair[1], &pair[0]) } else { (&pair[0], &pair[1]) };
if low.number() >= high.number() { return Err(BasicError::WrongSkip(self.0, None)) }
fn verify(&self, headers: &[Header], reverse: bool) -> Result<(), BasicError> {
for pair in headers.windows(2) {
let (low, high) = if reverse {
(&pair[1], &pair[0])
} else {
(&pair[0], &pair[1])
};
if low.number() >= high.number() {
return Err(BasicError::WrongSkip(self.0, None));
}
let skip = (high.number() - low.number()) - 1;
if skip != self.0 { return Err(BasicError::WrongSkip(self.0, Some(skip))) }
}
let skip = (high.number() - low.number()) - 1;
if skip != self.0 {
return Err(BasicError::WrongSkip(self.0, Some(skip)));
}
}
Ok(())
}
Ok(())
}
}
impl Constraint for Max {
type Error = BasicError;
type Error = BasicError;
fn verify(&self, headers: &[Header], _reverse: bool) -> Result<(), BasicError> {
match headers.len() > self.0 {
true => Err(BasicError::TooManyHeaders(self.0, headers.len())),
false => Ok(())
}
}
fn verify(&self, headers: &[Header], _reverse: bool) -> Result<(), BasicError> {
match headers.len() > self.0 {
true => Err(BasicError::TooManyHeaders(self.0, headers.len())),
false => Ok(()),
}
}
}
#[cfg(test)]
mod tests {
use types::encoded;
use types::header::Header;
use light::request::CompleteHeadersRequest as HeadersRequest;
use light::request::CompleteHeadersRequest as HeadersRequest;
use types::{encoded, header::Header};
use super::*;
use super::*;
#[test]
fn sequential_forward() {
let request = HeadersRequest {
start: 10.into(),
max: 30,
skip: 0,
reverse: false,
};
#[test]
fn sequential_forward() {
let request = HeadersRequest {
start: 10.into(),
max: 30,
skip: 0,
reverse: false,
};
let mut parent_hash = None;
let headers: Vec<_> = (0..25).map(|x| x + 10).map(|x| {
let mut header = Header::default();
header.set_number(x);
let mut parent_hash = None;
let headers: Vec<_> = (0..25)
.map(|x| x + 10)
.map(|x| {
let mut header = Header::default();
header.set_number(x);
if let Some(parent_hash) = parent_hash {
header.set_parent_hash(parent_hash);
}
if let Some(parent_hash) = parent_hash {
header.set_parent_hash(parent_hash);
}
parent_hash = Some(header.hash());
parent_hash = Some(header.hash());
encoded::Header::new(::rlp::encode(&header))
}).collect();
encoded::Header::new(::rlp::encode(&header))
})
.collect();
assert!(verify(&headers, &request).is_ok());
}
assert!(verify(&headers, &request).is_ok());
}
#[test]
fn sequential_backward() {
let request = HeadersRequest {
start: 34.into(),
max: 30,
skip: 0,
reverse: true,
};
#[test]
fn sequential_backward() {
let request = HeadersRequest {
start: 34.into(),
max: 30,
skip: 0,
reverse: true,
};
let mut parent_hash = None;
let headers: Vec<_> = (0..25).map(|x| x + 10).rev().map(|x| {
let mut header = Header::default();
header.set_number(x);
let mut parent_hash = None;
let headers: Vec<_> = (0..25)
.map(|x| x + 10)
.rev()
.map(|x| {
let mut header = Header::default();
header.set_number(x);
if let Some(parent_hash) = parent_hash {
header.set_parent_hash(parent_hash);
}
if let Some(parent_hash) = parent_hash {
header.set_parent_hash(parent_hash);
}
parent_hash = Some(header.hash());
parent_hash = Some(header.hash());
encoded::Header::new(::rlp::encode(&header))
}).collect();
encoded::Header::new(::rlp::encode(&header))
})
.collect();
assert!(verify(&headers, &request).is_ok());
}
assert!(verify(&headers, &request).is_ok());
}
#[test]
fn too_many() {
let request = HeadersRequest {
start: 10.into(),
max: 20,
skip: 0,
reverse: false,
};
#[test]
fn too_many() {
let request = HeadersRequest {
start: 10.into(),
max: 20,
skip: 0,
reverse: false,
};
let mut parent_hash = None;
let headers: Vec<_> = (0..25).map(|x| x + 10).map(|x| {
let mut header = Header::default();
header.set_number(x);
let mut parent_hash = None;
let headers: Vec<_> = (0..25)
.map(|x| x + 10)
.map(|x| {
let mut header = Header::default();
header.set_number(x);
if let Some(parent_hash) = parent_hash {
header.set_parent_hash(parent_hash);
}
if let Some(parent_hash) = parent_hash {
header.set_parent_hash(parent_hash);
}
parent_hash = Some(header.hash());
parent_hash = Some(header.hash());
encoded::Header::new(::rlp::encode(&header))
}).collect();
encoded::Header::new(::rlp::encode(&header))
})
.collect();
assert_eq!(verify(&headers, &request), Err(BasicError::TooManyHeaders(20, 25)));
}
assert_eq!(
verify(&headers, &request),
Err(BasicError::TooManyHeaders(20, 25))
);
}
#[test]
fn wrong_skip() {
let request = HeadersRequest {
start: 10.into(),
max: 30,
skip: 5,
reverse: false,
};
#[test]
fn wrong_skip() {
let request = HeadersRequest {
start: 10.into(),
max: 30,
skip: 5,
reverse: false,
};
let headers: Vec<_> = (0..25).map(|x| x * 3).map(|x| x + 10).map(|x| {
let mut header = Header::default();
header.set_number(x);
let headers: Vec<_> = (0..25)
.map(|x| x * 3)
.map(|x| x + 10)
.map(|x| {
let mut header = Header::default();
header.set_number(x);
encoded::Header::new(::rlp::encode(&header))
}).collect();
encoded::Header::new(::rlp::encode(&header))
})
.collect();
assert_eq!(verify(&headers, &request), Err(BasicError::WrongSkip(5, Some(2))));
}
assert_eq!(
verify(&headers, &request),
Err(BasicError::WrongSkip(5, Some(2)))
);
}
}

View File

@@ -16,18 +16,18 @@
//! Header download state machine.
use std::cmp::Ordering;
use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
use std::fmt;
use std::{
cmp::Ordering,
collections::{BinaryHeap, HashMap, HashSet, VecDeque},
fmt,
};
use types::encoded;
use types::header::Header;
use types::{encoded, header::Header};
use light::net::ReqId;
use light::request::CompleteHeadersRequest as HeadersRequest;
use light::{net::ReqId, request::CompleteHeadersRequest as HeadersRequest};
use network::PeerId;
use ethereum_types::H256;
use network::PeerId;
use super::response;
@@ -36,277 +36,295 @@ const SCAFFOLD_ATTEMPTS: usize = 3;
/// Context for a headers response.
pub trait ResponseContext {
/// Get the peer who sent this response.
fn responder(&self) -> PeerId;
/// Get the request ID this response corresponds to.
fn req_id(&self) -> &ReqId;
/// Get the (unverified) response data.
fn data(&self) -> &[encoded::Header];
/// Punish the responder.
fn punish_responder(&self);
/// Get the peer who sent this response.
fn responder(&self) -> PeerId;
/// Get the request ID this response corresponds to.
fn req_id(&self) -> &ReqId;
/// Get the (unverified) response data.
fn data(&self) -> &[encoded::Header];
/// Punish the responder.
fn punish_responder(&self);
}
/// Reasons for sync round abort.
#[derive(Debug, Clone)]
pub enum AbortReason {
/// Bad sparse header chain along with a list of peers who contributed to it.
BadScaffold(Vec<PeerId>),
/// No incoming data.
NoResponses,
/// Sync rounds completed.
TargetReached,
/// Bad sparse header chain along with a list of peers who contributed to it.
BadScaffold(Vec<PeerId>),
/// No incoming data.
NoResponses,
/// Sync rounds completed.
TargetReached,
}
// A request for headers with a known starting header hash.
// and a known parent hash for the last block.
#[derive(PartialEq, Eq)]
struct SubchainRequest {
subchain_parent: (u64, H256),
headers_request: HeadersRequest,
subchain_end: (u64, H256),
downloaded: VecDeque<Header>,
subchain_parent: (u64, H256),
headers_request: HeadersRequest,
subchain_end: (u64, H256),
downloaded: VecDeque<Header>,
}
// ordered by subchain parent number so pending requests towards the
// front of the round are dispatched first.
impl PartialOrd for SubchainRequest {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.subchain_parent.0
.partial_cmp(&other.subchain_parent.0)
.map(Ordering::reverse)
}
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.subchain_parent
.0
.partial_cmp(&other.subchain_parent.0)
.map(Ordering::reverse)
}
}
impl Ord for SubchainRequest {
fn cmp(&self, other: &Self) -> Ordering {
self.subchain_parent.0.cmp(&other.subchain_parent.0).reverse()
}
fn cmp(&self, other: &Self) -> Ordering {
self.subchain_parent
.0
.cmp(&other.subchain_parent.0)
.reverse()
}
}
/// Manages downloading of interior blocks of a sparse header chain.
pub struct Fetcher {
sparse: VecDeque<Header>, // sparse header chain.
requests: BinaryHeap<SubchainRequest>,
complete_requests: HashMap<H256, SubchainRequest>,
pending: HashMap<ReqId, SubchainRequest>,
scaffold_contributors: Vec<PeerId>,
ready: VecDeque<Header>,
end: (u64, H256),
target: (u64, H256),
sparse: VecDeque<Header>, // sparse header chain.
requests: BinaryHeap<SubchainRequest>,
complete_requests: HashMap<H256, SubchainRequest>,
pending: HashMap<ReqId, SubchainRequest>,
scaffold_contributors: Vec<PeerId>,
ready: VecDeque<Header>,
end: (u64, H256),
target: (u64, H256),
}
impl Fetcher {
// Produce a new fetcher given a sparse headerchain, in ascending order along
// with a list of peers who helped produce the chain.
// The headers must be valid RLP at this point and must have a consistent
// non-zero gap between them. Will abort the round if found wrong.
fn new(sparse_headers: Vec<Header>, contributors: Vec<PeerId>, target: (u64, H256)) -> SyncRound {
let mut requests = BinaryHeap::with_capacity(sparse_headers.len() - 1);
// Produce a new fetcher given a sparse headerchain, in ascending order along
// with a list of peers who helped produce the chain.
// The headers must be valid RLP at this point and must have a consistent
// non-zero gap between them. Will abort the round if found wrong.
fn new(
sparse_headers: Vec<Header>,
contributors: Vec<PeerId>,
target: (u64, H256),
) -> SyncRound {
let mut requests = BinaryHeap::with_capacity(sparse_headers.len() - 1);
for pair in sparse_headers.windows(2) {
let low_rung = &pair[0];
let high_rung = &pair[1];
for pair in sparse_headers.windows(2) {
let low_rung = &pair[0];
let high_rung = &pair[1];
let diff = high_rung.number() - low_rung.number();
let diff = high_rung.number() - low_rung.number();
// should never happen as long as we verify the gaps
// gotten from SyncRound::Start
if diff < 2 { continue }
// should never happen as long as we verify the gaps
// gotten from SyncRound::Start
if diff < 2 {
continue;
}
let needed_headers = HeadersRequest {
start: high_rung.parent_hash().clone().into(),
max: diff - 1,
skip: 0,
reverse: true,
};
let needed_headers = HeadersRequest {
start: high_rung.parent_hash().clone().into(),
max: diff - 1,
skip: 0,
reverse: true,
};
requests.push(SubchainRequest {
headers_request: needed_headers,
subchain_end: (high_rung.number() - 1, *high_rung.parent_hash()),
downloaded: VecDeque::new(),
subchain_parent: (low_rung.number(), low_rung.hash()),
});
}
requests.push(SubchainRequest {
headers_request: needed_headers,
subchain_end: (high_rung.number() - 1, *high_rung.parent_hash()),
downloaded: VecDeque::new(),
subchain_parent: (low_rung.number(), low_rung.hash()),
});
}
let end = match sparse_headers.last().map(|h| (h.number(), h.hash())) {
Some(end) => end,
None => return SyncRound::abort(AbortReason::BadScaffold(contributors), VecDeque::new()),
};
let end = match sparse_headers.last().map(|h| (h.number(), h.hash())) {
Some(end) => end,
None => {
return SyncRound::abort(AbortReason::BadScaffold(contributors), VecDeque::new())
}
};
SyncRound::Fetch(Fetcher {
sparse: sparse_headers.into(),
requests: requests,
complete_requests: HashMap::new(),
pending: HashMap::new(),
scaffold_contributors: contributors,
ready: VecDeque::new(),
end: end,
target: target,
})
}
SyncRound::Fetch(Fetcher {
sparse: sparse_headers.into(),
requests: requests,
complete_requests: HashMap::new(),
pending: HashMap::new(),
scaffold_contributors: contributors,
ready: VecDeque::new(),
end: end,
target: target,
})
}
// collect complete requests and their subchain from the sparse header chain
// into the ready set in order.
fn collect_ready(&mut self) {
loop {
let start_hash = match self.sparse.front() {
Some(first) => first.hash(),
None => break,
};
// collect complete requests and their subchain from the sparse header chain
// into the ready set in order.
fn collect_ready(&mut self) {
loop {
let start_hash = match self.sparse.front() {
Some(first) => first.hash(),
None => break,
};
match self.complete_requests.remove(&start_hash) {
None => break,
Some(complete_req) => {
self.ready.push_back(self.sparse.pop_front().expect("first known to exist; qed"));
self.ready.extend(complete_req.downloaded);
}
}
}
match self.complete_requests.remove(&start_hash) {
None => break,
Some(complete_req) => {
self.ready
.push_back(self.sparse.pop_front().expect("first known to exist; qed"));
self.ready.extend(complete_req.downloaded);
}
}
}
// frames are between two sparse headers and keyed by subchain parent, so the last
// remaining will be the last header.
if self.sparse.len() == 1 {
self.ready.push_back(self.sparse.pop_back().expect("sparse known to have one entry; qed"))
}
// frames are between two sparse headers and keyed by subchain parent, so the last
// remaining will be the last header.
if self.sparse.len() == 1 {
self.ready.push_back(
self.sparse
.pop_back()
.expect("sparse known to have one entry; qed"),
)
}
trace!(target: "sync", "{} headers ready to drain", self.ready.len());
}
trace!(target: "sync", "{} headers ready to drain", self.ready.len());
}
fn process_response<R: ResponseContext>(mut self, ctx: &R) -> SyncRound {
let mut request = match self.pending.remove(ctx.req_id()) {
Some(request) => request,
None => return SyncRound::Fetch(self),
};
fn process_response<R: ResponseContext>(mut self, ctx: &R) -> SyncRound {
let mut request = match self.pending.remove(ctx.req_id()) {
Some(request) => request,
None => return SyncRound::Fetch(self),
};
trace!(target: "sync", "Received response for subchain ({} -> {})",
trace!(target: "sync", "Received response for subchain ({} -> {})",
request.subchain_parent.0, request.subchain_end.0);
let headers = ctx.data();
let headers = ctx.data();
if headers.is_empty() {
trace!(target: "sync", "Punishing peer {} for empty response", ctx.responder());
ctx.punish_responder();
if headers.is_empty() {
trace!(target: "sync", "Punishing peer {} for empty response", ctx.responder());
ctx.punish_responder();
self.requests.push(request);
return SyncRound::Fetch(self);
}
self.requests.push(request);
return SyncRound::Fetch(self);
}
match response::verify(headers, &request.headers_request) {
Err(e) => {
trace!(target: "sync", "Punishing peer {} for invalid response ({})", ctx.responder(), e);
ctx.punish_responder();
match response::verify(headers, &request.headers_request) {
Err(e) => {
trace!(target: "sync", "Punishing peer {} for invalid response ({})", ctx.responder(), e);
ctx.punish_responder();
// TODO: track number of attempts per request,
// abort if failure rate too high.
self.requests.push(request);
SyncRound::Fetch(self)
}
Ok(headers) => {
let mut parent_hash = None;
for header in headers {
if let Some(hash) = parent_hash.as_ref() {
if *hash != header.hash() {
trace!(target: "sync", "Punishing peer {} for parent mismatch", ctx.responder());
ctx.punish_responder();
self.requests.push(request);
return SyncRound::Fetch(self);
}
}
// incrementally update the frame request as we go so we can
// return at any time in the loop.
parent_hash = Some(*header.parent_hash());
request.headers_request.start = header.parent_hash().clone().into();
request.headers_request.max -= 1;
request.downloaded.push_front(header);
}
// TODO: track number of attempts per request,
// abort if failure rate too high.
self.requests.push(request);
SyncRound::Fetch(self)
}
Ok(headers) => {
let mut parent_hash = None;
for header in headers {
if let Some(hash) = parent_hash.as_ref() {
if *hash != header.hash() {
trace!(target: "sync", "Punishing peer {} for parent mismatch", ctx.responder());
ctx.punish_responder();
self.requests.push(request);
return SyncRound::Fetch(self);
}
}
// incrementally update the frame request as we go so we can
// return at any time in the loop.
parent_hash = Some(*header.parent_hash());
request.headers_request.start = header.parent_hash().clone().into();
request.headers_request.max -= 1;
request.downloaded.push_front(header);
}
let subchain_parent = request.subchain_parent.1;
let subchain_parent = request.subchain_parent.1;
// check if the subchain portion has been completely filled.
if request.headers_request.max == 0 {
if parent_hash.map_or(true, |hash| hash != subchain_parent) {
let abort = AbortReason::BadScaffold(self.scaffold_contributors);
return SyncRound::abort(abort, self.ready);
}
// check if the subchain portion has been completely filled.
if request.headers_request.max == 0 {
if parent_hash.map_or(true, |hash| hash != subchain_parent) {
let abort = AbortReason::BadScaffold(self.scaffold_contributors);
return SyncRound::abort(abort, self.ready);
}
self.complete_requests.insert(subchain_parent, request);
self.collect_ready();
}
self.complete_requests.insert(subchain_parent, request);
self.collect_ready();
}
// state transition not triggered until drain is finished.
(SyncRound::Fetch(self))
}
}
}
// state transition not triggered until drain is finished.
(SyncRound::Fetch(self))
}
}
}
fn requests_abandoned(mut self, abandoned: &[ReqId]) -> SyncRound {
trace!(target: "sync", "Abandonned requests {:?}", abandoned);
fn requests_abandoned(mut self, abandoned: &[ReqId]) -> SyncRound {
trace!(target: "sync", "Abandonned requests {:?}", abandoned);
for abandoned in abandoned {
match self.pending.remove(abandoned) {
None => {},
Some(req) => self.requests.push(req),
}
}
for abandoned in abandoned {
match self.pending.remove(abandoned) {
None => {}
Some(req) => self.requests.push(req),
}
}
// TODO: track failure rate and potentially abort.
SyncRound::Fetch(self)
}
// TODO: track failure rate and potentially abort.
SyncRound::Fetch(self)
}
fn dispatch_requests<D>(mut self, mut dispatcher: D) -> SyncRound
where D: FnMut(HeadersRequest) -> Option<ReqId>
{
while let Some(pending_req) = self.requests.pop() {
match dispatcher(pending_req.headers_request.clone()) {
Some(req_id) => {
trace!(target: "sync", "Assigned request {} for subchain ({} -> {})",
fn dispatch_requests<D>(mut self, mut dispatcher: D) -> SyncRound
where
D: FnMut(HeadersRequest) -> Option<ReqId>,
{
while let Some(pending_req) = self.requests.pop() {
match dispatcher(pending_req.headers_request.clone()) {
Some(req_id) => {
trace!(target: "sync", "Assigned request {} for subchain ({} -> {})",
req_id, pending_req.subchain_parent.0, pending_req.subchain_end.0);
self.pending.insert(req_id, pending_req);
}
None => {
trace!(target: "sync", "Failed to assign request for subchain ({} -> {})",
self.pending.insert(req_id, pending_req);
}
None => {
trace!(target: "sync", "Failed to assign request for subchain ({} -> {})",
pending_req.subchain_parent.0, pending_req.subchain_end.0);
self.requests.push(pending_req);
break;
}
}
}
self.requests.push(pending_req);
break;
}
}
}
SyncRound::Fetch(self)
}
SyncRound::Fetch(self)
}
fn drain(mut self, headers: &mut Vec<Header>, max: Option<usize>) -> SyncRound {
let max = ::std::cmp::min(max.unwrap_or(usize::max_value()), self.ready.len());
headers.extend(self.ready.drain(0..max));
fn drain(mut self, headers: &mut Vec<Header>, max: Option<usize>) -> SyncRound {
let max = ::std::cmp::min(max.unwrap_or(usize::max_value()), self.ready.len());
headers.extend(self.ready.drain(0..max));
if self.sparse.is_empty() && self.ready.is_empty() {
trace!(target: "sync", "sync round complete. Starting anew from {:?}", self.end);
SyncRound::begin(self.end, self.target)
} else {
SyncRound::Fetch(self)
}
}
if self.sparse.is_empty() && self.ready.is_empty() {
trace!(target: "sync", "sync round complete. Starting anew from {:?}", self.end);
SyncRound::begin(self.end, self.target)
} else {
SyncRound::Fetch(self)
}
}
}
// Compute scaffold parameters from non-zero distance between start and target block: (skip, pivots).
fn scaffold_params(diff: u64) -> (u64, u64) {
// default parameters.
// amount of blocks between each scaffold pivot.
const ROUND_SKIP: u64 = 255;
// amount of scaffold pivots: these are the Xs in "X___X___X"
const ROUND_PIVOTS: u64 = 256;
// default parameters.
// amount of blocks between each scaffold pivot.
const ROUND_SKIP: u64 = 255;
// amount of scaffold pivots: these are the Xs in "X___X___X"
const ROUND_PIVOTS: u64 = 256;
let rem = diff % (ROUND_SKIP + 1);
if diff <= ROUND_SKIP {
// just request headers from the start to the target.
(0, rem)
} else {
// the number of pivots necessary to exactly hit or overshoot the target.
let pivots_to_target = (diff / (ROUND_SKIP + 1)) + if rem == 0 { 0 } else { 1 };
let num_pivots = ::std::cmp::min(pivots_to_target, ROUND_PIVOTS);
(ROUND_SKIP, num_pivots)
}
let rem = diff % (ROUND_SKIP + 1);
if diff <= ROUND_SKIP {
// just request headers from the start to the target.
(0, rem)
} else {
// the number of pivots necessary to exactly hit or overshoot the target.
let pivots_to_target = (diff / (ROUND_SKIP + 1)) + if rem == 0 { 0 } else { 1 };
let num_pivots = ::std::cmp::min(pivots_to_target, ROUND_PIVOTS);
(ROUND_SKIP, num_pivots)
}
}
/// Round started: get stepped header chain.
@@ -316,242 +334,252 @@ fn scaffold_params(diff: u64) -> (u64, u64) {
/// only those blocks. If the sync target is within (ROUND_SKIP + 1) * (ROUND_PIVOTS - 1) of
/// the start, we reduce the number of pivots so the target is outside it.
pub struct RoundStart {
start_block: (u64, H256),
target: (u64, H256),
pending_req: Option<(ReqId, HeadersRequest)>,
sparse_headers: Vec<Header>,
contributors: HashSet<PeerId>,
attempt: usize,
skip: u64,
pivots: u64,
start_block: (u64, H256),
target: (u64, H256),
pending_req: Option<(ReqId, HeadersRequest)>,
sparse_headers: Vec<Header>,
contributors: HashSet<PeerId>,
attempt: usize,
skip: u64,
pivots: u64,
}
impl RoundStart {
fn new(start: (u64, H256), target: (u64, H256)) -> Self {
let (skip, pivots) = scaffold_params(target.0 - start.0);
fn new(start: (u64, H256), target: (u64, H256)) -> Self {
let (skip, pivots) = scaffold_params(target.0 - start.0);
trace!(target: "sync", "Beginning sync round: {} pivots and {} skip from block {}",
trace!(target: "sync", "Beginning sync round: {} pivots and {} skip from block {}",
pivots, skip, start.0);
RoundStart {
start_block: start,
target: target,
pending_req: None,
sparse_headers: Vec::new(),
contributors: HashSet::new(),
attempt: 0,
skip: skip,
pivots: pivots,
}
}
RoundStart {
start_block: start,
target: target,
pending_req: None,
sparse_headers: Vec::new(),
contributors: HashSet::new(),
attempt: 0,
skip: skip,
pivots: pivots,
}
}
// called on failed attempt. may trigger a transition after a number of attempts.
// a failed attempt is defined as any time a peer returns invalid or incomplete response
fn failed_attempt(mut self) -> SyncRound {
self.attempt += 1;
// called on failed attempt. may trigger a transition after a number of attempts.
// a failed attempt is defined as any time a peer returns invalid or incomplete response
fn failed_attempt(mut self) -> SyncRound {
self.attempt += 1;
if self.attempt >= SCAFFOLD_ATTEMPTS {
return if self.sparse_headers.len() > 1 {
Fetcher::new(self.sparse_headers, self.contributors.into_iter().collect(), self.target)
} else {
let fetched_headers = if self.skip == 0 {
self.sparse_headers.into()
} else {
VecDeque::new()
};
if self.attempt >= SCAFFOLD_ATTEMPTS {
return if self.sparse_headers.len() > 1 {
Fetcher::new(
self.sparse_headers,
self.contributors.into_iter().collect(),
self.target,
)
} else {
let fetched_headers = if self.skip == 0 {
self.sparse_headers.into()
} else {
VecDeque::new()
};
SyncRound::abort(AbortReason::NoResponses, fetched_headers)
}
} else {
SyncRound::Start(self)
}
}
SyncRound::abort(AbortReason::NoResponses, fetched_headers)
};
} else {
SyncRound::Start(self)
}
}
fn process_response<R: ResponseContext>(mut self, ctx: &R) -> SyncRound {
let req = match self.pending_req.take() {
Some((id, ref req)) if ctx.req_id() == &id => { req.clone() }
other => {
self.pending_req = other;
return SyncRound::Start(self);
}
};
fn process_response<R: ResponseContext>(mut self, ctx: &R) -> SyncRound {
let req = match self.pending_req.take() {
Some((id, ref req)) if ctx.req_id() == &id => req.clone(),
other => {
self.pending_req = other;
return SyncRound::Start(self);
}
};
match response::verify(ctx.data(), &req) {
Ok(headers) => {
if self.sparse_headers.is_empty()
&& headers.get(0).map_or(false, |x| x.parent_hash() != &self.start_block.1) {
trace!(target: "sync", "Wrong parent for first header in round");
ctx.punish_responder(); // or should we reset?
}
match response::verify(ctx.data(), &req) {
Ok(headers) => {
if self.sparse_headers.is_empty()
&& headers
.get(0)
.map_or(false, |x| x.parent_hash() != &self.start_block.1)
{
trace!(target: "sync", "Wrong parent for first header in round");
ctx.punish_responder(); // or should we reset?
}
self.contributors.insert(ctx.responder());
self.sparse_headers.extend(headers);
self.contributors.insert(ctx.responder());
self.sparse_headers.extend(headers);
if self.sparse_headers.len() as u64 == self.pivots {
return if self.skip == 0 {
SyncRound::abort(AbortReason::TargetReached, self.sparse_headers.into())
} else {
trace!(target: "sync", "Beginning fetch of blocks between {} sparse headers",
if self.sparse_headers.len() as u64 == self.pivots {
return if self.skip == 0 {
SyncRound::abort(AbortReason::TargetReached, self.sparse_headers.into())
} else {
trace!(target: "sync", "Beginning fetch of blocks between {} sparse headers",
self.sparse_headers.len());
Fetcher::new(
self.sparse_headers,
self.contributors.into_iter().collect(),
self.target
)
}
}
}
Err(e) => {
trace!(target: "sync", "Punishing peer {} for malformed response ({})", ctx.responder(), e);
ctx.punish_responder();
}
};
Fetcher::new(
self.sparse_headers,
self.contributors.into_iter().collect(),
self.target,
)
};
}
}
Err(e) => {
trace!(target: "sync", "Punishing peer {} for malformed response ({})", ctx.responder(), e);
ctx.punish_responder();
}
};
self.failed_attempt()
}
self.failed_attempt()
}
fn requests_abandoned(mut self, abandoned: &[ReqId]) -> SyncRound {
match self.pending_req.take() {
Some((id, req)) => {
if abandoned.iter().any(|r| r == &id) {
self.pending_req = None;
self.failed_attempt()
} else {
self.pending_req = Some((id, req));
SyncRound::Start(self)
}
}
None => SyncRound::Start(self),
}
}
fn requests_abandoned(mut self, abandoned: &[ReqId]) -> SyncRound {
match self.pending_req.take() {
Some((id, req)) => {
if abandoned.iter().any(|r| r == &id) {
self.pending_req = None;
self.failed_attempt()
} else {
self.pending_req = Some((id, req));
SyncRound::Start(self)
}
}
None => SyncRound::Start(self),
}
}
fn dispatch_requests<D>(mut self, mut dispatcher: D) -> SyncRound
where D: FnMut(HeadersRequest) -> Option<ReqId>
{
if self.pending_req.is_none() {
// beginning offset + first block expected after last header we have.
let start = (self.start_block.0 + 1)
+ self.sparse_headers.len() as u64 * (self.skip + 1);
fn dispatch_requests<D>(mut self, mut dispatcher: D) -> SyncRound
where
D: FnMut(HeadersRequest) -> Option<ReqId>,
{
if self.pending_req.is_none() {
// beginning offset + first block expected after last header we have.
let start =
(self.start_block.0 + 1) + self.sparse_headers.len() as u64 * (self.skip + 1);
let max = self.pivots - self.sparse_headers.len() as u64;
let max = self.pivots - self.sparse_headers.len() as u64;
let headers_request = HeadersRequest {
start: start.into(),
max: max,
skip: self.skip,
reverse: false,
};
let headers_request = HeadersRequest {
start: start.into(),
max: max,
skip: self.skip,
reverse: false,
};
if let Some(req_id) = dispatcher(headers_request.clone()) {
trace!(target: "sync", "Requesting scaffold: {} headers forward from {}, skip={}",
if let Some(req_id) = dispatcher(headers_request.clone()) {
trace!(target: "sync", "Requesting scaffold: {} headers forward from {}, skip={}",
max, start, self.skip);
self.pending_req = Some((req_id, headers_request));
}
}
self.pending_req = Some((req_id, headers_request));
}
}
SyncRound::Start(self)
}
SyncRound::Start(self)
}
}
/// Sync round state machine.
pub enum SyncRound {
/// Beginning a sync round.
Start(RoundStart),
/// Fetching intermediate blocks during a sync round.
Fetch(Fetcher),
/// Aborted + Sequential headers
Abort(AbortReason, VecDeque<Header>),
/// Beginning a sync round.
Start(RoundStart),
/// Fetching intermediate blocks during a sync round.
Fetch(Fetcher),
/// Aborted + Sequential headers
Abort(AbortReason, VecDeque<Header>),
}
impl SyncRound {
fn abort(reason: AbortReason, remaining: VecDeque<Header>) -> Self {
trace!(target: "sync", "Aborting sync round: {:?}. To drain: {}", reason, remaining.len());
fn abort(reason: AbortReason, remaining: VecDeque<Header>) -> Self {
trace!(target: "sync", "Aborting sync round: {:?}. To drain: {}", reason, remaining.len());
SyncRound::Abort(reason, remaining)
}
SyncRound::Abort(reason, remaining)
}
/// Begin sync rounds from a starting block, but not to go past a given target
pub fn begin(start: (u64, H256), target: (u64, H256)) -> Self {
if target.0 <= start.0 {
SyncRound::abort(AbortReason::TargetReached, VecDeque::new())
} else {
SyncRound::Start(RoundStart::new(start, target))
}
}
/// Begin sync rounds from a starting block, but not to go past a given target
pub fn begin(start: (u64, H256), target: (u64, H256)) -> Self {
if target.0 <= start.0 {
SyncRound::abort(AbortReason::TargetReached, VecDeque::new())
} else {
SyncRound::Start(RoundStart::new(start, target))
}
}
/// Process an answer to a request. Unknown requests will be ignored.
pub fn process_response<R: ResponseContext>(self, ctx: &R) -> Self {
match self {
SyncRound::Start(round_start) => round_start.process_response(ctx),
SyncRound::Fetch(fetcher) => fetcher.process_response(ctx),
other => other,
}
}
/// Process an answer to a request. Unknown requests will be ignored.
pub fn process_response<R: ResponseContext>(self, ctx: &R) -> Self {
match self {
SyncRound::Start(round_start) => round_start.process_response(ctx),
SyncRound::Fetch(fetcher) => fetcher.process_response(ctx),
other => other,
}
}
/// Return unfulfilled requests from disconnected peer. Unknown requests will be ignored.
pub fn requests_abandoned(self, abandoned: &[ReqId]) -> Self {
match self {
SyncRound::Start(round_start) => round_start.requests_abandoned(abandoned),
SyncRound::Fetch(fetcher) => fetcher.requests_abandoned(abandoned),
other => other,
}
}
/// Return unfulfilled requests from disconnected peer. Unknown requests will be ignored.
pub fn requests_abandoned(self, abandoned: &[ReqId]) -> Self {
match self {
SyncRound::Start(round_start) => round_start.requests_abandoned(abandoned),
SyncRound::Fetch(fetcher) => fetcher.requests_abandoned(abandoned),
other => other,
}
}
/// Dispatch pending requests. The dispatcher provided will attempt to
/// find a suitable peer to serve the request.
// TODO: have dispatcher take capabilities argument? and return an error as
// to why no suitable peer can be found? (no buffer, no chain heads that high, etc)
pub fn dispatch_requests<D>(self, dispatcher: D) -> Self
where D: FnMut(HeadersRequest) -> Option<ReqId>
{
match self {
SyncRound::Start(round_start) => round_start.dispatch_requests(dispatcher),
SyncRound::Fetch(fetcher) => fetcher.dispatch_requests(dispatcher),
other => other,
}
}
/// Dispatch pending requests. The dispatcher provided will attempt to
/// find a suitable peer to serve the request.
// TODO: have dispatcher take capabilities argument? and return an error as
// to why no suitable peer can be found? (no buffer, no chain heads that high, etc)
pub fn dispatch_requests<D>(self, dispatcher: D) -> Self
where
D: FnMut(HeadersRequest) -> Option<ReqId>,
{
match self {
SyncRound::Start(round_start) => round_start.dispatch_requests(dispatcher),
SyncRound::Fetch(fetcher) => fetcher.dispatch_requests(dispatcher),
other => other,
}
}
/// Drain up to a maximum number (None -> all) of headers (continuous, starting with a child of
/// the round start block) from the round, starting a new one once finished.
pub fn drain(self, v: &mut Vec<Header>, max: Option<usize>) -> Self {
match self {
SyncRound::Fetch(fetcher) => fetcher.drain(v, max),
SyncRound::Abort(reason, mut remaining) => {
let len = ::std::cmp::min(max.unwrap_or(usize::max_value()), remaining.len());
v.extend(remaining.drain(..len));
SyncRound::Abort(reason, remaining)
}
other => other,
}
}
/// Drain up to a maximum number (None -> all) of headers (continuous, starting with a child of
/// the round start block) from the round, starting a new one once finished.
pub fn drain(self, v: &mut Vec<Header>, max: Option<usize>) -> Self {
match self {
SyncRound::Fetch(fetcher) => fetcher.drain(v, max),
SyncRound::Abort(reason, mut remaining) => {
let len = ::std::cmp::min(max.unwrap_or(usize::max_value()), remaining.len());
v.extend(remaining.drain(..len));
SyncRound::Abort(reason, remaining)
}
other => other,
}
}
}
impl fmt::Debug for SyncRound {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SyncRound::Start(ref state) => write!(f, "Scaffolding from {:?}", state.start_block),
SyncRound::Fetch(ref fetcher) => write!(f, "Filling scaffold up to {:?}", fetcher.end),
SyncRound::Abort(ref reason, ref remaining) =>
write!(f, "Aborted: {:?}, {} remain", reason, remaining.len()),
}
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SyncRound::Start(ref state) => write!(f, "Scaffolding from {:?}", state.start_block),
SyncRound::Fetch(ref fetcher) => write!(f, "Filling scaffold up to {:?}", fetcher.end),
SyncRound::Abort(ref reason, ref remaining) => {
write!(f, "Aborted: {:?}, {} remain", reason, remaining.len())
}
}
}
}
#[cfg(test)]
mod tests {
use super::scaffold_params;
use super::scaffold_params;
#[test]
fn scaffold_config() {
// within a certain distance of the head, we download
// sequentially.
assert_eq!(scaffold_params(1), (0, 1));
assert_eq!(scaffold_params(6), (0, 6));
#[test]
fn scaffold_config() {
// within a certain distance of the head, we download
// sequentially.
assert_eq!(scaffold_params(1), (0, 1));
assert_eq!(scaffold_params(6), (0, 6));
// when scaffolds are useful, download enough frames to get
// within a close distance of the goal.
assert_eq!(scaffold_params(1000), (255, 4));
assert_eq!(scaffold_params(1024), (255, 4));
}
// when scaffolds are useful, download enough frames to get
// within a close distance of the goal.
assert_eq!(scaffold_params(1000), (255, 4));
assert_eq!(scaffold_params(1024), (255, 4));
}
}

View File

@@ -16,48 +16,61 @@
use tests::helpers::TestNet;
use ethcore::client::{BlockInfo, BlockId, EachBlockWith};
use ethcore::client::{BlockId, BlockInfo, EachBlockWith};
mod test_net;
#[test]
fn basic_sync() {
let mut net = TestNet::light(1, 2);
net.peer(1).chain().add_blocks(5000, EachBlockWith::Nothing);
net.peer(2).chain().add_blocks(6000, EachBlockWith::Nothing);
let mut net = TestNet::light(1, 2);
net.peer(1).chain().add_blocks(5000, EachBlockWith::Nothing);
net.peer(2).chain().add_blocks(6000, EachBlockWith::Nothing);
net.sync();
net.sync();
assert!(net.peer(0).light_chain().block_header(BlockId::Number(6000)).is_some());
assert!(net
.peer(0)
.light_chain()
.block_header(BlockId::Number(6000))
.is_some());
}
#[test]
fn fork_post_cht() {
const CHAIN_LENGTH: u64 = 50; // shouldn't be longer than ::light::cht::size();
const CHAIN_LENGTH: u64 = 50; // shouldn't be longer than ::light::cht::size();
let mut net = TestNet::light(1, 2);
let mut net = TestNet::light(1, 2);
// peer 2 is on a higher TD chain.
net.peer(1).chain().add_blocks(CHAIN_LENGTH as usize, EachBlockWith::Nothing);
net.peer(2).chain().add_blocks(CHAIN_LENGTH as usize + 1, EachBlockWith::Uncle);
// peer 2 is on a higher TD chain.
net.peer(1)
.chain()
.add_blocks(CHAIN_LENGTH as usize, EachBlockWith::Nothing);
net.peer(2)
.chain()
.add_blocks(CHAIN_LENGTH as usize + 1, EachBlockWith::Uncle);
// get the light peer on peer 1's chain.
for id in (0..CHAIN_LENGTH).map(|x| x + 1).map(BlockId::Number) {
let (light_peer, full_peer) = (net.peer(0), net.peer(1));
let light_chain = light_peer.light_chain();
let header = full_peer.chain().block_header(id).unwrap().decode().expect("decoding failure");
let _ = light_chain.import_header(header);
light_chain.flush_queue();
light_chain.import_verified();
assert!(light_chain.block_header(id).is_some());
}
// get the light peer on peer 1's chain.
for id in (0..CHAIN_LENGTH).map(|x| x + 1).map(BlockId::Number) {
let (light_peer, full_peer) = (net.peer(0), net.peer(1));
let light_chain = light_peer.light_chain();
let header = full_peer
.chain()
.block_header(id)
.unwrap()
.decode()
.expect("decoding failure");
let _ = light_chain.import_header(header);
light_chain.flush_queue();
light_chain.import_verified();
assert!(light_chain.block_header(id).is_some());
}
net.sync();
net.sync();
for id in (0..CHAIN_LENGTH).map(|x| x + 1).map(BlockId::Number) {
assert_eq!(
net.peer(0).light_chain().block_header(id).unwrap(),
net.peer(2).chain().block_header(id).unwrap()
);
}
for id in (0..CHAIN_LENGTH).map(|x| x + 1).map(BlockId::Number) {
assert_eq!(
net.peer(0).light_chain().block_header(id).unwrap(),
net.peer(2).chain().block_header(id).unwrap()
);
}
}

View File

@@ -16,236 +16,255 @@
//! TestNet peer definition.
use std::collections::{HashSet, VecDeque};
use std::sync::Arc;
use std::{
collections::{HashSet, VecDeque},
sync::Arc,
};
use light_sync::*;
use tests::helpers::{TestNet, Peer as PeerLike, TestPacket};
use tests::helpers::{Peer as PeerLike, TestNet, TestPacket};
use ethcore::client::TestBlockChainClient;
use ethcore::spec::Spec;
use ethcore::{client::TestBlockChainClient, spec::Spec};
use io::IoChannel;
use kvdb_memorydb;
use light::client::fetch::{self, Unavailable};
use light::net::{LightProtocol, IoContext, Capabilities, Params as LightParams};
use light::provider::LightProvider;
use light::{
client::fetch::{self, Unavailable},
net::{Capabilities, IoContext, LightProtocol, Params as LightParams},
provider::LightProvider,
};
use network::{NodeId, PeerId};
use parking_lot::RwLock;
use std::time::Duration;
use light::cache::Cache;
use std::time::Duration;
const NETWORK_ID: u64 = 0xcafebabe;
pub type LightClient = ::light::client::Client<Unavailable>;
struct TestIoContext<'a> {
queue: &'a RwLock<VecDeque<TestPacket>>,
sender: Option<PeerId>,
to_disconnect: RwLock<HashSet<PeerId>>,
queue: &'a RwLock<VecDeque<TestPacket>>,
sender: Option<PeerId>,
to_disconnect: RwLock<HashSet<PeerId>>,
}
impl<'a> IoContext for TestIoContext<'a> {
fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec<u8>) {
self.queue.write().push_back(TestPacket {
data: packet_body,
packet_id: packet_id,
recipient: peer,
})
}
fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec<u8>) {
self.queue.write().push_back(TestPacket {
data: packet_body,
packet_id: packet_id,
recipient: peer,
})
}
fn respond(&self, packet_id: u8, packet_body: Vec<u8>) {
if let Some(sender) = self.sender {
self.send(sender, packet_id, packet_body);
}
}
fn respond(&self, packet_id: u8, packet_body: Vec<u8>) {
if let Some(sender) = self.sender {
self.send(sender, packet_id, packet_body);
}
}
fn disconnect_peer(&self, peer: PeerId) {
self.to_disconnect.write().insert(peer);
}
fn disconnect_peer(&self, peer: PeerId) {
self.to_disconnect.write().insert(peer);
}
fn disable_peer(&self, peer: PeerId) { self.disconnect_peer(peer) }
fn protocol_version(&self, _peer: PeerId) -> Option<u8> { Some(::light::net::MAX_PROTOCOL_VERSION) }
fn disable_peer(&self, peer: PeerId) {
self.disconnect_peer(peer)
}
fn protocol_version(&self, _peer: PeerId) -> Option<u8> {
Some(::light::net::MAX_PROTOCOL_VERSION)
}
fn persistent_peer_id(&self, _peer: PeerId) -> Option<NodeId> { unimplemented!() }
fn is_reserved_peer(&self, _peer: PeerId) -> bool { false }
fn persistent_peer_id(&self, _peer: PeerId) -> Option<NodeId> {
unimplemented!()
}
fn is_reserved_peer(&self, _peer: PeerId) -> bool {
false
}
}
// peer-specific data.
enum PeerData {
Light(Arc<LightSync<LightClient>>, Arc<LightClient>),
Full(Arc<TestBlockChainClient>)
Light(Arc<LightSync<LightClient>>, Arc<LightClient>),
Full(Arc<TestBlockChainClient>),
}
// test peer type.
// Either a full peer or a light peer.
pub struct Peer {
proto: LightProtocol,
queue: RwLock<VecDeque<TestPacket>>,
data: PeerData,
proto: LightProtocol,
queue: RwLock<VecDeque<TestPacket>>,
data: PeerData,
}
impl Peer {
// create a new full-client peer for light client peers to sync to.
// buffer flow is made negligible.
pub fn new_full(chain: Arc<TestBlockChainClient>) -> Self {
let params = LightParams {
network_id: NETWORK_ID,
config: Default::default(),
capabilities: Capabilities {
serve_headers: true,
serve_chain_since: None,
serve_state_since: None,
tx_relay: true,
},
sample_store: None,
};
// create a new full-client peer for light client peers to sync to.
// buffer flow is made negligible.
pub fn new_full(chain: Arc<TestBlockChainClient>) -> Self {
let params = LightParams {
network_id: NETWORK_ID,
config: Default::default(),
capabilities: Capabilities {
serve_headers: true,
serve_chain_since: None,
serve_state_since: None,
tx_relay: true,
},
sample_store: None,
};
let proto = LightProtocol::new(chain.clone(), params);
Peer {
proto: proto,
queue: RwLock::new(VecDeque::new()),
data: PeerData::Full(chain),
}
}
let proto = LightProtocol::new(chain.clone(), params);
Peer {
proto: proto,
queue: RwLock::new(VecDeque::new()),
data: PeerData::Full(chain),
}
}
// create a new light-client peer to sync to full peers.
pub fn new_light(chain: Arc<LightClient>) -> Self {
let sync = Arc::new(LightSync::new(chain.clone()).unwrap());
let params = LightParams {
network_id: NETWORK_ID,
config: Default::default(),
capabilities: Capabilities {
serve_headers: false,
serve_chain_since: None,
serve_state_since: None,
tx_relay: false,
},
sample_store: None,
};
// create a new light-client peer to sync to full peers.
pub fn new_light(chain: Arc<LightClient>) -> Self {
let sync = Arc::new(LightSync::new(chain.clone()).unwrap());
let params = LightParams {
network_id: 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 provider = LightProvider::new(chain.clone(), Arc::new(RwLock::new(Default::default())));
let mut proto = LightProtocol::new(Arc::new(provider), params);
proto.add_handler(sync.clone());
Peer {
proto: proto,
queue: RwLock::new(VecDeque::new()),
data: PeerData::Light(sync, chain),
}
}
let provider = LightProvider::new(chain.clone(), Arc::new(RwLock::new(Default::default())));
let mut proto = LightProtocol::new(Arc::new(provider), params);
proto.add_handler(sync.clone());
Peer {
proto: proto,
queue: RwLock::new(VecDeque::new()),
data: PeerData::Light(sync, chain),
}
}
// get the chain from the client, asserting that it is a full node.
pub fn chain(&self) -> &TestBlockChainClient {
match self.data {
PeerData::Full(ref chain) => &*chain,
_ => panic!("Attempted to access full chain on light peer."),
}
}
// get the chain from the client, asserting that it is a full node.
pub fn chain(&self) -> &TestBlockChainClient {
match self.data {
PeerData::Full(ref chain) => &*chain,
_ => panic!("Attempted to access full chain on light peer."),
}
}
// get the light chain from the peer, asserting that it is a light node.
pub fn light_chain(&self) -> &LightClient {
match self.data {
PeerData::Light(_, ref chain) => &*chain,
_ => panic!("Attempted to access light chain on full peer."),
}
}
// get the light chain from the peer, asserting that it is a light node.
pub fn light_chain(&self) -> &LightClient {
match self.data {
PeerData::Light(_, ref chain) => &*chain,
_ => panic!("Attempted to access light chain on full peer."),
}
}
// get a test Io context based on
fn io(&self, sender: Option<PeerId>) -> TestIoContext {
TestIoContext {
queue: &self.queue,
sender: sender,
to_disconnect: RwLock::new(HashSet::new()),
}
}
// get a test Io context based on
fn io(&self, sender: Option<PeerId>) -> TestIoContext {
TestIoContext {
queue: &self.queue,
sender: sender,
to_disconnect: RwLock::new(HashSet::new()),
}
}
}
impl PeerLike for Peer {
type Message = TestPacket;
type Message = TestPacket;
fn on_connect(&self, other: PeerId) {
let io = self.io(Some(other));
self.proto.on_connect(other, &io);
}
fn on_connect(&self, other: PeerId) {
let io = self.io(Some(other));
self.proto.on_connect(other, &io);
}
fn on_disconnect(&self, other: PeerId){
let io = self.io(Some(other));
self.proto.on_disconnect(other, &io);
}
fn on_disconnect(&self, other: PeerId) {
let io = self.io(Some(other));
self.proto.on_disconnect(other, &io);
}
fn receive_message(&self, from: PeerId, msg: TestPacket) -> HashSet<PeerId> {
let io = self.io(Some(from));
self.proto.handle_packet(&io, from, msg.packet_id, &msg.data);
io.to_disconnect.into_inner()
}
fn receive_message(&self, from: PeerId, msg: TestPacket) -> HashSet<PeerId> {
let io = self.io(Some(from));
self.proto
.handle_packet(&io, from, msg.packet_id, &msg.data);
io.to_disconnect.into_inner()
}
fn pending_message(&self) -> Option<TestPacket> {
self.queue.write().pop_front()
}
fn pending_message(&self) -> Option<TestPacket> {
self.queue.write().pop_front()
}
fn is_done(&self) -> bool {
self.queue.read().is_empty() && match self.data {
PeerData::Light(_, ref client) => {
// should create a test light client which just imports
// headers directly and doesn't have a queue to drain.
client.import_verified();
client.queue_info().is_empty()
}
_ => true,
}
}
fn is_done(&self) -> bool {
self.queue.read().is_empty()
&& match self.data {
PeerData::Light(_, ref client) => {
// should create a test light client which just imports
// headers directly and doesn't have a queue to drain.
client.import_verified();
client.queue_info().is_empty()
}
_ => true,
}
}
fn sync_step(&self) {
if let PeerData::Light(_, ref client) = self.data {
client.flush_queue();
fn sync_step(&self) {
if let PeerData::Light(_, ref client) = self.data {
client.flush_queue();
while !client.queue_info().is_empty() {
client.import_verified()
}
}
}
while !client.queue_info().is_empty() {
client.import_verified()
}
}
}
fn restart_sync(&self) { }
fn restart_sync(&self) {}
fn process_all_io_messages(&self) { }
fn process_all_io_messages(&self) {}
fn process_all_new_block_messages(&self) { }
fn process_all_new_block_messages(&self) {}
}
impl TestNet<Peer> {
/// Create a new `TestNet` for testing light synchronization.
/// The first parameter is the number of light nodes,
/// the second is the number of full nodes.
pub fn light(n_light: usize, n_full: usize) -> Self {
let mut peers = Vec::with_capacity(n_light + n_full);
for _ in 0..n_light {
let mut config = ::light::client::Config::default();
/// Create a new `TestNet` for testing light synchronization.
/// The first parameter is the number of light nodes,
/// the second is the number of full nodes.
pub fn light(n_light: usize, n_full: usize) -> Self {
let mut peers = Vec::with_capacity(n_light + n_full);
for _ in 0..n_light {
let mut config = ::light::client::Config::default();
// skip full verification because the blocks are bad.
config.verify_full = false;
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600))));
let db = kvdb_memorydb::create(0);
let client = LightClient::new(
config,
Arc::new(db),
None,
&Spec::new_test(),
fetch::unavailable(), // TODO: allow fetch from full nodes.
IoChannel::disconnected(),
cache
).expect("New DB creation infallible; qed");
// skip full verification because the blocks are bad.
config.verify_full = false;
let cache = Arc::new(Mutex::new(Cache::new(
Default::default(),
Duration::from_secs(6 * 3600),
)));
let db = kvdb_memorydb::create(0);
let client = LightClient::new(
config,
Arc::new(db),
None,
&Spec::new_test(),
fetch::unavailable(), // TODO: allow fetch from full nodes.
IoChannel::disconnected(),
cache,
)
.expect("New DB creation infallible; qed");
peers.push(Arc::new(Peer::new_light(Arc::new(client))))
}
peers.push(Arc::new(Peer::new_light(Arc::new(client))))
}
for _ in 0..n_full {
peers.push(Arc::new(Peer::new_full(Arc::new(TestBlockChainClient::new()))))
}
for _ in 0..n_full {
peers.push(Arc::new(Peer::new_full(Arc::new(
TestBlockChainClient::new(),
))))
}
TestNet {
peers: peers,
started: false,
disconnect_events: Vec::new(),
}
}
TestNet {
peers: peers,
started: false,
disconnect_events: Vec::new(),
}
}
}

View File

@@ -14,50 +14,50 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use parking_lot::Mutex;
use ethereum_types::H256;
use parking_lot::Mutex;
/// Trait which should be implemented by a private transaction handler.
pub trait PrivateTxHandler: Send + Sync + 'static {
/// Function called on new private transaction received.
/// Returns the hash of the imported transaction
fn import_private_transaction(&self, rlp: &[u8]) -> Result<H256, String>;
/// Function called on new private transaction received.
/// Returns the hash of the imported transaction
fn import_private_transaction(&self, rlp: &[u8]) -> Result<H256, String>;
/// Function called on new signed private transaction received.
/// Returns the hash of the imported transaction
fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result<H256, String>;
/// Function called on new signed private transaction received.
/// Returns the hash of the imported transaction
fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result<H256, String>;
}
/// Nonoperative private transaction handler.
pub struct NoopPrivateTxHandler;
impl PrivateTxHandler for NoopPrivateTxHandler {
fn import_private_transaction(&self, _rlp: &[u8]) -> Result<H256, String> {
Ok(H256::default())
}
fn import_private_transaction(&self, _rlp: &[u8]) -> Result<H256, String> {
Ok(H256::default())
}
fn import_signed_private_transaction(&self, _rlp: &[u8]) -> Result<H256, String> {
Ok(H256::default())
}
fn import_signed_private_transaction(&self, _rlp: &[u8]) -> Result<H256, String> {
Ok(H256::default())
}
}
/// Simple private transaction handler. Used for tests.
#[derive(Default)]
pub struct SimplePrivateTxHandler {
/// imported private transactions
pub txs: Mutex<Vec<Vec<u8>>>,
/// imported signed private transactions
pub signed_txs: Mutex<Vec<Vec<u8>>>,
/// imported private transactions
pub txs: Mutex<Vec<Vec<u8>>>,
/// imported signed private transactions
pub signed_txs: Mutex<Vec<Vec<u8>>>,
}
impl PrivateTxHandler for SimplePrivateTxHandler {
fn import_private_transaction(&self, rlp: &[u8]) -> Result<H256, String> {
self.txs.lock().push(rlp.to_vec());
Ok(H256::default())
}
fn import_private_transaction(&self, rlp: &[u8]) -> Result<H256, String> {
self.txs.lock().push(rlp.to_vec());
Ok(H256::default())
}
fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result<H256, String> {
self.signed_txs.lock().push(rlp.to_vec());
Ok(H256::default())
}
fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result<H256, String> {
self.signed_txs.lock().push(rlp.to_vec());
Ok(H256::default())
}
}

View File

@@ -18,255 +18,268 @@ use ethcore::snapshot::{ManifestData, SnapshotService};
use ethereum_types::H256;
use hash::keccak;
use std::collections::HashSet;
use std::iter::FromIterator;
use std::{collections::HashSet, iter::FromIterator};
#[derive(PartialEq, Eq, Debug)]
pub enum ChunkType {
State(H256),
Block(H256),
State(H256),
Block(H256),
}
pub struct Snapshot {
pending_state_chunks: Vec<H256>,
pending_block_chunks: Vec<H256>,
downloading_chunks: HashSet<H256>,
completed_chunks: HashSet<H256>,
snapshot_hash: Option<H256>,
bad_hashes: HashSet<H256>,
initialized: bool,
pending_state_chunks: Vec<H256>,
pending_block_chunks: Vec<H256>,
downloading_chunks: HashSet<H256>,
completed_chunks: HashSet<H256>,
snapshot_hash: Option<H256>,
bad_hashes: HashSet<H256>,
initialized: bool,
}
impl Snapshot {
/// Create a new instance.
pub fn new() -> Snapshot {
Snapshot {
pending_state_chunks: Vec::new(),
pending_block_chunks: Vec::new(),
downloading_chunks: HashSet::new(),
completed_chunks: HashSet::new(),
snapshot_hash: None,
bad_hashes: HashSet::new(),
initialized: false,
}
}
/// Create a new instance.
pub fn new() -> Snapshot {
Snapshot {
pending_state_chunks: Vec::new(),
pending_block_chunks: Vec::new(),
downloading_chunks: HashSet::new(),
completed_chunks: HashSet::new(),
snapshot_hash: None,
bad_hashes: HashSet::new(),
initialized: false,
}
}
/// Sync the Snapshot completed chunks with the Snapshot Service
pub fn initialize(&mut self, snapshot_service: &SnapshotService) {
if self.initialized {
return;
}
/// Sync the Snapshot completed chunks with the Snapshot Service
pub fn initialize(&mut self, snapshot_service: &SnapshotService) {
if self.initialized {
return;
}
if let Some(completed_chunks) = snapshot_service.completed_chunks() {
self.completed_chunks = HashSet::from_iter(completed_chunks);
}
if let Some(completed_chunks) = snapshot_service.completed_chunks() {
self.completed_chunks = HashSet::from_iter(completed_chunks);
}
trace!(
target: "snapshot",
"Snapshot is now initialized with {} completed chunks.",
self.completed_chunks.len(),
);
trace!(
target: "snapshot",
"Snapshot is now initialized with {} completed chunks.",
self.completed_chunks.len(),
);
self.initialized = true;
}
self.initialized = true;
}
/// Clear everything.
pub fn clear(&mut self) {
self.pending_state_chunks.clear();
self.pending_block_chunks.clear();
self.downloading_chunks.clear();
self.completed_chunks.clear();
self.snapshot_hash = None;
self.initialized = false;
}
/// Clear everything.
pub fn clear(&mut self) {
self.pending_state_chunks.clear();
self.pending_block_chunks.clear();
self.downloading_chunks.clear();
self.completed_chunks.clear();
self.snapshot_hash = None;
self.initialized = false;
}
/// Check if currently downloading a snapshot.
pub fn have_manifest(&self) -> bool {
self.snapshot_hash.is_some()
}
/// Check if currently downloading a snapshot.
pub fn have_manifest(&self) -> bool {
self.snapshot_hash.is_some()
}
/// Reset collection for a manifest RLP
pub fn reset_to(&mut self, manifest: &ManifestData, hash: &H256) {
self.clear();
self.pending_state_chunks = manifest.state_hashes.clone();
self.pending_block_chunks = manifest.block_hashes.clone();
self.snapshot_hash = Some(hash.clone());
}
/// Reset collection for a manifest RLP
pub fn reset_to(&mut self, manifest: &ManifestData, hash: &H256) {
self.clear();
self.pending_state_chunks = manifest.state_hashes.clone();
self.pending_block_chunks = manifest.block_hashes.clone();
self.snapshot_hash = Some(hash.clone());
}
/// Validate chunk and mark it as downloaded
pub fn validate_chunk(&mut self, chunk: &[u8]) -> Result<ChunkType, ()> {
let hash = keccak(chunk);
if self.completed_chunks.contains(&hash) {
trace!(target: "sync", "Ignored proccessed chunk: {:x}", hash);
return Err(());
}
self.downloading_chunks.remove(&hash);
if self.pending_block_chunks.iter().any(|h| h == &hash) {
self.completed_chunks.insert(hash.clone());
return Ok(ChunkType::Block(hash));
}
if self.pending_state_chunks.iter().any(|h| h == &hash) {
self.completed_chunks.insert(hash.clone());
return Ok(ChunkType::State(hash));
}
trace!(target: "sync", "Ignored unknown chunk: {:x}", hash);
Err(())
}
/// Validate chunk and mark it as downloaded
pub fn validate_chunk(&mut self, chunk: &[u8]) -> Result<ChunkType, ()> {
let hash = keccak(chunk);
if self.completed_chunks.contains(&hash) {
trace!(target: "sync", "Ignored proccessed chunk: {:x}", hash);
return Err(());
}
self.downloading_chunks.remove(&hash);
if self.pending_block_chunks.iter().any(|h| h == &hash) {
self.completed_chunks.insert(hash.clone());
return Ok(ChunkType::Block(hash));
}
if self.pending_state_chunks.iter().any(|h| h == &hash) {
self.completed_chunks.insert(hash.clone());
return Ok(ChunkType::State(hash));
}
trace!(target: "sync", "Ignored unknown chunk: {:x}", hash);
Err(())
}
/// Find a chunk to download
pub fn needed_chunk(&mut self) -> Option<H256> {
// Find next needed chunk: first block, then state chunks
let chunk = {
let chunk_filter = |h| !self.downloading_chunks.contains(h) && !self.completed_chunks.contains(h);
/// Find a chunk to download
pub fn needed_chunk(&mut self) -> Option<H256> {
// Find next needed chunk: first block, then state chunks
let chunk = {
let chunk_filter =
|h| !self.downloading_chunks.contains(h) && !self.completed_chunks.contains(h);
let needed_block_chunk = self.pending_block_chunks.iter()
.filter(|&h| chunk_filter(h))
.map(|h| *h)
.next();
let needed_block_chunk = self
.pending_block_chunks
.iter()
.filter(|&h| chunk_filter(h))
.map(|h| *h)
.next();
// If no block chunks to download, get the state chunks
if needed_block_chunk.is_none() {
self.pending_state_chunks.iter()
.filter(|&h| chunk_filter(h))
.map(|h| *h)
.next()
} else {
needed_block_chunk
}
};
// If no block chunks to download, get the state chunks
if needed_block_chunk.is_none() {
self.pending_state_chunks
.iter()
.filter(|&h| chunk_filter(h))
.map(|h| *h)
.next()
} else {
needed_block_chunk
}
};
if let Some(hash) = chunk {
self.downloading_chunks.insert(hash.clone());
}
chunk
}
if let Some(hash) = chunk {
self.downloading_chunks.insert(hash.clone());
}
chunk
}
pub fn clear_chunk_download(&mut self, hash: &H256) {
self.downloading_chunks.remove(hash);
}
pub fn clear_chunk_download(&mut self, hash: &H256) {
self.downloading_chunks.remove(hash);
}
// note snapshot hash as bad.
pub fn note_bad(&mut self, hash: H256) {
self.bad_hashes.insert(hash);
}
// note snapshot hash as bad.
pub fn note_bad(&mut self, hash: H256) {
self.bad_hashes.insert(hash);
}
// whether snapshot hash is known to be bad.
pub fn is_known_bad(&self, hash: &H256) -> bool {
self.bad_hashes.contains(hash)
}
// whether snapshot hash is known to be bad.
pub fn is_known_bad(&self, hash: &H256) -> bool {
self.bad_hashes.contains(hash)
}
pub fn snapshot_hash(&self) -> Option<H256> {
self.snapshot_hash
}
pub fn snapshot_hash(&self) -> Option<H256> {
self.snapshot_hash
}
pub fn total_chunks(&self) -> usize {
self.pending_block_chunks.len() + self.pending_state_chunks.len()
}
pub fn total_chunks(&self) -> usize {
self.pending_block_chunks.len() + self.pending_state_chunks.len()
}
pub fn done_chunks(&self) -> usize {
self.completed_chunks.len()
}
pub fn done_chunks(&self) -> usize {
self.completed_chunks.len()
}
pub fn is_complete(&self) -> bool {
self.total_chunks() == self.completed_chunks.len()
}
pub fn is_complete(&self) -> bool {
self.total_chunks() == self.completed_chunks.len()
}
}
#[cfg(test)]
mod test {
use hash::keccak;
use bytes::Bytes;
use super::*;
use ethcore::snapshot::ManifestData;
use super::*;
use bytes::Bytes;
use ethcore::snapshot::ManifestData;
use hash::keccak;
fn is_empty(snapshot: &Snapshot) -> bool {
snapshot.pending_block_chunks.is_empty() &&
snapshot.pending_state_chunks.is_empty() &&
snapshot.completed_chunks.is_empty() &&
snapshot.downloading_chunks.is_empty() &&
snapshot.snapshot_hash.is_none()
}
fn is_empty(snapshot: &Snapshot) -> bool {
snapshot.pending_block_chunks.is_empty()
&& snapshot.pending_state_chunks.is_empty()
&& snapshot.completed_chunks.is_empty()
&& snapshot.downloading_chunks.is_empty()
&& snapshot.snapshot_hash.is_none()
}
fn test_manifest() -> (ManifestData, H256, Vec<Bytes>, Vec<Bytes>) {
let state_chunks: Vec<Bytes> = (0..20).map(|_| H256::random().to_vec()).collect();
let block_chunks: Vec<Bytes> = (0..20).map(|_| H256::random().to_vec()).collect();
let manifest = ManifestData {
version: 2,
state_hashes: state_chunks.iter().map(|data| keccak(data)).collect(),
block_hashes: block_chunks.iter().map(|data| keccak(data)).collect(),
state_root: H256::new(),
block_number: 42,
block_hash: H256::new(),
};
let mhash = keccak(manifest.clone().into_rlp());
(manifest, mhash, state_chunks, block_chunks)
}
fn test_manifest() -> (ManifestData, H256, Vec<Bytes>, Vec<Bytes>) {
let state_chunks: Vec<Bytes> = (0..20).map(|_| H256::random().to_vec()).collect();
let block_chunks: Vec<Bytes> = (0..20).map(|_| H256::random().to_vec()).collect();
let manifest = ManifestData {
version: 2,
state_hashes: state_chunks.iter().map(|data| keccak(data)).collect(),
block_hashes: block_chunks.iter().map(|data| keccak(data)).collect(),
state_root: H256::new(),
block_number: 42,
block_hash: H256::new(),
};
let mhash = keccak(manifest.clone().into_rlp());
(manifest, mhash, state_chunks, block_chunks)
}
#[test]
fn create_clear() {
let mut snapshot = Snapshot::new();
assert!(is_empty(&snapshot));
let (manifest, mhash, _, _,) = test_manifest();
snapshot.reset_to(&manifest, &mhash);
assert!(!is_empty(&snapshot));
snapshot.clear();
assert!(is_empty(&snapshot));
}
#[test]
fn create_clear() {
let mut snapshot = Snapshot::new();
assert!(is_empty(&snapshot));
let (manifest, mhash, _, _) = test_manifest();
snapshot.reset_to(&manifest, &mhash);
assert!(!is_empty(&snapshot));
snapshot.clear();
assert!(is_empty(&snapshot));
}
#[test]
fn validate_chunks() {
let mut snapshot = Snapshot::new();
let (manifest, mhash, state_chunks, block_chunks) = test_manifest();
snapshot.reset_to(&manifest, &mhash);
assert_eq!(snapshot.done_chunks(), 0);
assert!(snapshot.validate_chunk(&H256::random().to_vec()).is_err());
#[test]
fn validate_chunks() {
let mut snapshot = Snapshot::new();
let (manifest, mhash, state_chunks, block_chunks) = test_manifest();
snapshot.reset_to(&manifest, &mhash);
assert_eq!(snapshot.done_chunks(), 0);
assert!(snapshot.validate_chunk(&H256::random().to_vec()).is_err());
let requested: Vec<H256> = (0..40).map(|_| snapshot.needed_chunk().unwrap()).collect();
assert!(snapshot.needed_chunk().is_none());
let requested: Vec<H256> = (0..40).map(|_| snapshot.needed_chunk().unwrap()).collect();
assert!(snapshot.needed_chunk().is_none());
let requested_all_block_chunks = manifest.block_hashes.iter()
.all(|h| requested.iter().any(|rh| rh == h));
assert!(requested_all_block_chunks);
let requested_all_block_chunks = manifest
.block_hashes
.iter()
.all(|h| requested.iter().any(|rh| rh == h));
assert!(requested_all_block_chunks);
let requested_all_state_chunks = manifest.state_hashes.iter()
.all(|h| requested.iter().any(|rh| rh == h));
assert!(requested_all_state_chunks);
let requested_all_state_chunks = manifest
.state_hashes
.iter()
.all(|h| requested.iter().any(|rh| rh == h));
assert!(requested_all_state_chunks);
assert_eq!(snapshot.downloading_chunks.len(), 40);
assert_eq!(snapshot.downloading_chunks.len(), 40);
assert_eq!(snapshot.validate_chunk(&state_chunks[4]), Ok(ChunkType::State(manifest.state_hashes[4].clone())));
assert_eq!(snapshot.completed_chunks.len(), 1);
assert_eq!(snapshot.downloading_chunks.len(), 39);
assert_eq!(
snapshot.validate_chunk(&state_chunks[4]),
Ok(ChunkType::State(manifest.state_hashes[4].clone()))
);
assert_eq!(snapshot.completed_chunks.len(), 1);
assert_eq!(snapshot.downloading_chunks.len(), 39);
assert_eq!(snapshot.validate_chunk(&block_chunks[10]), Ok(ChunkType::Block(manifest.block_hashes[10].clone())));
assert_eq!(snapshot.completed_chunks.len(), 2);
assert_eq!(snapshot.downloading_chunks.len(), 38);
assert_eq!(
snapshot.validate_chunk(&block_chunks[10]),
Ok(ChunkType::Block(manifest.block_hashes[10].clone()))
);
assert_eq!(snapshot.completed_chunks.len(), 2);
assert_eq!(snapshot.downloading_chunks.len(), 38);
for (i, data) in state_chunks.iter().enumerate() {
if i != 4 {
assert!(snapshot.validate_chunk(data).is_ok());
}
}
for (i, data) in state_chunks.iter().enumerate() {
if i != 4 {
assert!(snapshot.validate_chunk(data).is_ok());
}
}
for (i, data) in block_chunks.iter().enumerate() {
if i != 10 {
assert!(snapshot.validate_chunk(data).is_ok());
}
}
for (i, data) in block_chunks.iter().enumerate() {
if i != 10 {
assert!(snapshot.validate_chunk(data).is_ok());
}
}
assert!(snapshot.is_complete());
assert_eq!(snapshot.done_chunks(), 40);
assert_eq!(snapshot.done_chunks(), snapshot.total_chunks());
assert_eq!(snapshot.snapshot_hash(), Some(keccak(manifest.into_rlp())));
}
assert!(snapshot.is_complete());
assert_eq!(snapshot.done_chunks(), 40);
assert_eq!(snapshot.done_chunks(), snapshot.total_chunks());
assert_eq!(snapshot.snapshot_hash(), Some(keccak(manifest.into_rlp())));
}
#[test]
fn tracks_known_bad() {
let mut snapshot = Snapshot::new();
let hash = H256::random();
#[test]
fn tracks_known_bad() {
let mut snapshot = Snapshot::new();
let hash = H256::random();
assert_eq!(snapshot.is_known_bad(&hash), false);
snapshot.note_bad(hash);
assert_eq!(snapshot.is_known_bad(&hash), true);
}
assert_eq!(snapshot.is_known_bad(&hash), false);
snapshot.note_bad(hash);
assert_eq!(snapshot.is_known_bad(&hash), true);
}
}

View File

@@ -14,127 +14,134 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::collections::HashMap;
use chain::sync_packet::{PacketInfo, SyncPacket};
use network::{NetworkContext, PeerId, PacketId, Error, SessionInfo, ProtocolId};
use network::client_version::ClientVersion;
use bytes::Bytes;
use ethcore::client::BlockChainClient;
use types::BlockNumber;
use ethcore::snapshot::SnapshotService;
use chain::sync_packet::{PacketInfo, SyncPacket};
use ethcore::{client::BlockChainClient, snapshot::SnapshotService};
use network::{
client_version::ClientVersion, Error, NetworkContext, PacketId, PeerId, ProtocolId, SessionInfo,
};
use parking_lot::RwLock;
use std::collections::HashMap;
use types::BlockNumber;
/// IO interface for the syncing handler.
/// Provides peer connection management and an interface to the blockchain client.
// TODO: ratings
pub trait SyncIo {
/// Disable a peer
fn disable_peer(&mut self, peer_id: PeerId);
/// Disconnect peer
fn disconnect_peer(&mut self, peer_id: PeerId);
/// Respond to current request with a packet. Can be called from an IO handler for incoming packet.
fn respond(&mut self, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error>;
/// Send a packet to a peer using specified protocol.
fn send(&mut self, peer_id: PeerId, packet_id: SyncPacket, data: Vec<u8>) -> Result<(), Error>;
/// Get the blockchain
fn chain(&self) -> &BlockChainClient;
/// Get the snapshot service.
fn snapshot_service(&self) -> &SnapshotService;
/// Returns peer version identifier
fn peer_version(&self, peer_id: PeerId) -> ClientVersion {
ClientVersion::from(peer_id.to_string())
}
/// Returns information on p2p session
fn peer_session_info(&self, peer_id: PeerId) -> Option<SessionInfo>;
/// Maximum mutually supported ETH protocol version
fn eth_protocol_version(&self, peer_id: PeerId) -> u8;
/// Maximum mutually supported version of a gien protocol.
fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8;
/// Returns if the chain block queue empty
fn is_chain_queue_empty(&self) -> bool {
self.chain().is_queue_empty()
}
/// Check if the session is expired
fn is_expired(&self) -> bool;
/// Return sync overlay
fn chain_overlay(&self) -> &RwLock<HashMap<BlockNumber, Bytes>>;
/// Returns the size the payload shouldn't exceed
fn payload_soft_limit(&self) -> usize;
/// Disable a peer
fn disable_peer(&mut self, peer_id: PeerId);
/// Disconnect peer
fn disconnect_peer(&mut self, peer_id: PeerId);
/// Respond to current request with a packet. Can be called from an IO handler for incoming packet.
fn respond(&mut self, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error>;
/// Send a packet to a peer using specified protocol.
fn send(&mut self, peer_id: PeerId, packet_id: SyncPacket, data: Vec<u8>) -> Result<(), Error>;
/// Get the blockchain
fn chain(&self) -> &BlockChainClient;
/// Get the snapshot service.
fn snapshot_service(&self) -> &SnapshotService;
/// Returns peer version identifier
fn peer_version(&self, peer_id: PeerId) -> ClientVersion {
ClientVersion::from(peer_id.to_string())
}
/// Returns information on p2p session
fn peer_session_info(&self, peer_id: PeerId) -> Option<SessionInfo>;
/// Maximum mutually supported ETH protocol version
fn eth_protocol_version(&self, peer_id: PeerId) -> u8;
/// Maximum mutually supported version of a gien protocol.
fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8;
/// Returns if the chain block queue empty
fn is_chain_queue_empty(&self) -> bool {
self.chain().is_queue_empty()
}
/// Check if the session is expired
fn is_expired(&self) -> bool;
/// Return sync overlay
fn chain_overlay(&self) -> &RwLock<HashMap<BlockNumber, Bytes>>;
/// Returns the size the payload shouldn't exceed
fn payload_soft_limit(&self) -> usize;
}
/// Wraps `NetworkContext` and the blockchain client
pub struct NetSyncIo<'s> {
network: &'s NetworkContext,
chain: &'s BlockChainClient,
snapshot_service: &'s SnapshotService,
chain_overlay: &'s RwLock<HashMap<BlockNumber, Bytes>>,
network: &'s NetworkContext,
chain: &'s BlockChainClient,
snapshot_service: &'s SnapshotService,
chain_overlay: &'s RwLock<HashMap<BlockNumber, Bytes>>,
}
impl<'s> NetSyncIo<'s> {
/// Creates a new instance from the `NetworkContext` and the blockchain client reference.
pub fn new(network: &'s NetworkContext,
chain: &'s BlockChainClient,
snapshot_service: &'s SnapshotService,
chain_overlay: &'s RwLock<HashMap<BlockNumber, Bytes>>) -> NetSyncIo<'s> {
NetSyncIo {
network: network,
chain: chain,
snapshot_service: snapshot_service,
chain_overlay: chain_overlay,
}
}
/// Creates a new instance from the `NetworkContext` and the blockchain client reference.
pub fn new(
network: &'s NetworkContext,
chain: &'s BlockChainClient,
snapshot_service: &'s SnapshotService,
chain_overlay: &'s RwLock<HashMap<BlockNumber, Bytes>>,
) -> NetSyncIo<'s> {
NetSyncIo {
network: network,
chain: chain,
snapshot_service: snapshot_service,
chain_overlay: chain_overlay,
}
}
}
impl<'s> SyncIo for NetSyncIo<'s> {
fn disable_peer(&mut self, peer_id: PeerId) {
self.network.disable_peer(peer_id);
}
fn disable_peer(&mut self, peer_id: PeerId) {
self.network.disable_peer(peer_id);
}
fn disconnect_peer(&mut self, peer_id: PeerId) {
self.network.disconnect_peer(peer_id);
}
fn disconnect_peer(&mut self, peer_id: PeerId) {
self.network.disconnect_peer(peer_id);
}
fn respond(&mut self, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error>{
self.network.respond(packet_id, data)
}
fn respond(&mut self, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error> {
self.network.respond(packet_id, data)
}
fn send(&mut self, peer_id: PeerId, packet_id: SyncPacket, data: Vec<u8>) -> Result<(), Error>{
self.network.send_protocol(packet_id.protocol(), peer_id, packet_id.id(), data)
}
fn send(&mut self, peer_id: PeerId, packet_id: SyncPacket, data: Vec<u8>) -> Result<(), Error> {
self.network
.send_protocol(packet_id.protocol(), peer_id, packet_id.id(), data)
}
fn chain(&self) -> &BlockChainClient {
self.chain
}
fn chain(&self) -> &BlockChainClient {
self.chain
}
fn chain_overlay(&self) -> &RwLock<HashMap<BlockNumber, Bytes>> {
self.chain_overlay
}
fn chain_overlay(&self) -> &RwLock<HashMap<BlockNumber, Bytes>> {
self.chain_overlay
}
fn snapshot_service(&self) -> &SnapshotService {
self.snapshot_service
}
fn snapshot_service(&self) -> &SnapshotService {
self.snapshot_service
}
fn peer_session_info(&self, peer_id: PeerId) -> Option<SessionInfo> {
self.network.session_info(peer_id)
}
fn peer_session_info(&self, peer_id: PeerId) -> Option<SessionInfo> {
self.network.session_info(peer_id)
}
fn is_expired(&self) -> bool {
self.network.is_expired()
}
fn is_expired(&self) -> bool {
self.network.is_expired()
}
fn eth_protocol_version(&self, peer_id: PeerId) -> u8 {
self.network.protocol_version(self.network.subprotocol_name(), peer_id).unwrap_or(0)
}
fn eth_protocol_version(&self, peer_id: PeerId) -> u8 {
self.network
.protocol_version(self.network.subprotocol_name(), peer_id)
.unwrap_or(0)
}
fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 {
self.network.protocol_version(*protocol, peer_id).unwrap_or(0)
}
fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 {
self.network
.protocol_version(*protocol, peer_id)
.unwrap_or(0)
}
fn peer_version(&self, peer_id: PeerId) -> ClientVersion {
self.network.peer_client_version(peer_id)
}
fn peer_version(&self, peer_id: PeerId) -> ClientVersion {
self.network.peer_client_version(peer_id)
}
fn payload_soft_limit(&self) -> usize {
self.network.payload_soft_limit()
}
fn payload_soft_limit(&self) -> usize {
self.network.payload_soft_limit()
}
}

View File

@@ -14,251 +14,288 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use ethcore::client::{TestBlockChainClient, BlockChainClient, BlockId, EachBlockWith, ChainInfo, BlockInfo};
use chain::{SyncState};
use super::helpers::*;
use {SyncConfig, WarpSync};
use chain::SyncState;
use ethcore::client::{
BlockChainClient, BlockId, BlockInfo, ChainInfo, EachBlockWith, TestBlockChainClient,
};
use std::sync::Arc;
use SyncConfig;
use WarpSync;
#[test]
fn two_peers() {
::env_logger::try_init().ok();
let mut net = TestNet::new(3);
net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle);
net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle);
net.sync();
assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some());
assert_eq!(*net.peer(0).chain.blocks.read(), *net.peer(1).chain.blocks.read());
::env_logger::try_init().ok();
let mut net = TestNet::new(3);
net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle);
net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle);
net.sync();
assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some());
assert_eq!(
*net.peer(0).chain.blocks.read(),
*net.peer(1).chain.blocks.read()
);
}
#[test]
fn long_chain() {
::env_logger::try_init().ok();
let mut net = TestNet::new(2);
net.peer(1).chain.add_blocks(50000, EachBlockWith::Nothing);
net.sync();
assert!(net.peer(0).chain.block(BlockId::Number(50000)).is_some());
assert_eq!(*net.peer(0).chain.blocks.read(), *net.peer(1).chain.blocks.read());
::env_logger::try_init().ok();
let mut net = TestNet::new(2);
net.peer(1).chain.add_blocks(50000, EachBlockWith::Nothing);
net.sync();
assert!(net.peer(0).chain.block(BlockId::Number(50000)).is_some());
assert_eq!(
*net.peer(0).chain.blocks.read(),
*net.peer(1).chain.blocks.read()
);
}
#[test]
fn status_after_sync() {
::env_logger::try_init().ok();
let mut net = TestNet::new(3);
net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle);
net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle);
net.sync();
let status = net.peer(0).sync.read().status();
assert_eq!(status.state, SyncState::Idle);
::env_logger::try_init().ok();
let mut net = TestNet::new(3);
net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle);
net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle);
net.sync();
let status = net.peer(0).sync.read().status();
assert_eq!(status.state, SyncState::Idle);
}
#[test]
fn takes_few_steps() {
let mut net = TestNet::new(3);
net.peer(1).chain.add_blocks(100, EachBlockWith::Uncle);
net.peer(2).chain.add_blocks(100, EachBlockWith::Uncle);
let total_steps = net.sync();
assert!(total_steps < 20);
let mut net = TestNet::new(3);
net.peer(1).chain.add_blocks(100, EachBlockWith::Uncle);
net.peer(2).chain.add_blocks(100, EachBlockWith::Uncle);
let total_steps = net.sync();
assert!(total_steps < 20);
}
#[test]
fn empty_blocks() {
::env_logger::try_init().ok();
let mut net = TestNet::new(3);
for n in 0..200 {
let with = if n % 2 == 0 { EachBlockWith::Nothing } else { EachBlockWith::Uncle };
net.peer(1).chain.add_blocks(5, with.clone());
net.peer(2).chain.add_blocks(5, with);
}
net.sync();
assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some());
assert_eq!(*net.peer(0).chain.blocks.read(), *net.peer(1).chain.blocks.read());
::env_logger::try_init().ok();
let mut net = TestNet::new(3);
for n in 0..200 {
let with = if n % 2 == 0 {
EachBlockWith::Nothing
} else {
EachBlockWith::Uncle
};
net.peer(1).chain.add_blocks(5, with.clone());
net.peer(2).chain.add_blocks(5, with);
}
net.sync();
assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some());
assert_eq!(
*net.peer(0).chain.blocks.read(),
*net.peer(1).chain.blocks.read()
);
}
#[test]
fn forked() {
::env_logger::try_init().ok();
let mut net = TestNet::new(3);
net.peer(0).chain.add_blocks(30, EachBlockWith::Uncle);
net.peer(1).chain.add_blocks(30, EachBlockWith::Uncle);
net.peer(2).chain.add_blocks(30, EachBlockWith::Uncle);
net.peer(0).chain.add_blocks(10, EachBlockWith::Nothing); //fork
net.peer(1).chain.add_blocks(20, EachBlockWith::Uncle);
net.peer(2).chain.add_blocks(20, EachBlockWith::Uncle);
net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); //fork between 1 and 2
net.peer(2).chain.add_blocks(1, EachBlockWith::Nothing);
// peer 1 has the best chain of 601 blocks
let peer1_chain = net.peer(1).chain.numbers.read().clone();
net.sync();
assert_eq!(*net.peer(0).chain.difficulty.read(), *net.peer(1).chain.difficulty.read());
assert_eq!(&*net.peer(0).chain.numbers.read(), &peer1_chain);
assert_eq!(&*net.peer(1).chain.numbers.read(), &peer1_chain);
assert_eq!(&*net.peer(2).chain.numbers.read(), &peer1_chain);
::env_logger::try_init().ok();
let mut net = TestNet::new(3);
net.peer(0).chain.add_blocks(30, EachBlockWith::Uncle);
net.peer(1).chain.add_blocks(30, EachBlockWith::Uncle);
net.peer(2).chain.add_blocks(30, EachBlockWith::Uncle);
net.peer(0).chain.add_blocks(10, EachBlockWith::Nothing); //fork
net.peer(1).chain.add_blocks(20, EachBlockWith::Uncle);
net.peer(2).chain.add_blocks(20, EachBlockWith::Uncle);
net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); //fork between 1 and 2
net.peer(2).chain.add_blocks(1, EachBlockWith::Nothing);
// peer 1 has the best chain of 601 blocks
let peer1_chain = net.peer(1).chain.numbers.read().clone();
net.sync();
assert_eq!(
*net.peer(0).chain.difficulty.read(),
*net.peer(1).chain.difficulty.read()
);
assert_eq!(&*net.peer(0).chain.numbers.read(), &peer1_chain);
assert_eq!(&*net.peer(1).chain.numbers.read(), &peer1_chain);
assert_eq!(&*net.peer(2).chain.numbers.read(), &peer1_chain);
}
#[test]
fn forked_with_misbehaving_peer() {
::env_logger::try_init().ok();
let mut net = TestNet::new(3);
::env_logger::try_init().ok();
let mut net = TestNet::new(3);
let mut alt_spec = ::ethcore::spec::Spec::new_test();
alt_spec.extra_data = b"fork".to_vec();
// peer 0 is on a totally different chain with higher total difficulty
net.peer_mut(0).chain = Arc::new(TestBlockChainClient::new_with_spec(alt_spec));
net.peer(0).chain.add_blocks(50, EachBlockWith::Nothing);
net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing);
net.peer(2).chain.add_blocks(10, EachBlockWith::Nothing);
let mut alt_spec = ::ethcore::spec::Spec::new_test();
alt_spec.extra_data = b"fork".to_vec();
// peer 0 is on a totally different chain with higher total difficulty
net.peer_mut(0).chain = Arc::new(TestBlockChainClient::new_with_spec(alt_spec));
net.peer(0).chain.add_blocks(50, EachBlockWith::Nothing);
net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing);
net.peer(2).chain.add_blocks(10, EachBlockWith::Nothing);
net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing);
net.peer(2).chain.add_blocks(20, EachBlockWith::Uncle);
// peer 1 should sync to peer 2, others should not change
let peer0_chain = net.peer(0).chain.numbers.read().clone();
let peer2_chain = net.peer(2).chain.numbers.read().clone();
net.sync();
assert_eq!(&*net.peer(0).chain.numbers.read(), &peer0_chain);
assert_eq!(&*net.peer(1).chain.numbers.read(), &peer2_chain);
assert_eq!(&*net.peer(2).chain.numbers.read(), &peer2_chain);
net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing);
net.peer(2).chain.add_blocks(20, EachBlockWith::Uncle);
// peer 1 should sync to peer 2, others should not change
let peer0_chain = net.peer(0).chain.numbers.read().clone();
let peer2_chain = net.peer(2).chain.numbers.read().clone();
net.sync();
assert_eq!(&*net.peer(0).chain.numbers.read(), &peer0_chain);
assert_eq!(&*net.peer(1).chain.numbers.read(), &peer2_chain);
assert_eq!(&*net.peer(2).chain.numbers.read(), &peer2_chain);
}
#[test]
fn net_hard_fork() {
::env_logger::try_init().ok();
let ref_client = TestBlockChainClient::new();
ref_client.add_blocks(50, EachBlockWith::Uncle);
{
let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockId::Number(50)).unwrap())));
net.peer(0).chain.add_blocks(100, EachBlockWith::Uncle);
net.sync();
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 100);
}
{
let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockId::Number(50)).unwrap())));
net.peer(0).chain.add_blocks(100, EachBlockWith::Nothing);
net.sync();
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 0);
}
::env_logger::try_init().ok();
let ref_client = TestBlockChainClient::new();
ref_client.add_blocks(50, EachBlockWith::Uncle);
{
let mut net = TestNet::new_with_fork(
2,
Some((50, ref_client.block_hash(BlockId::Number(50)).unwrap())),
);
net.peer(0).chain.add_blocks(100, EachBlockWith::Uncle);
net.sync();
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 100);
}
{
let mut net = TestNet::new_with_fork(
2,
Some((50, ref_client.block_hash(BlockId::Number(50)).unwrap())),
);
net.peer(0).chain.add_blocks(100, EachBlockWith::Nothing);
net.sync();
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 0);
}
}
#[test]
fn restart() {
::env_logger::try_init().ok();
let mut net = TestNet::new(3);
net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle);
net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle);
::env_logger::try_init().ok();
let mut net = TestNet::new(3);
net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle);
net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle);
net.sync();
net.sync();
// make sure that sync has actually happened
assert!(net.peer(0).chain.chain_info().best_block_number > 100);
net.restart_peer(0);
// make sure that sync has actually happened
assert!(net.peer(0).chain.chain_info().best_block_number > 100);
net.restart_peer(0);
let status = net.peer(0).sync.read().status();
assert_eq!(status.state, SyncState::Idle);
let status = net.peer(0).sync.read().status();
assert_eq!(status.state, SyncState::Idle);
}
#[test]
fn status_empty() {
let net = TestNet::new(2);
assert_eq!(net.peer(0).sync.read().status().state, SyncState::Idle);
let mut config = SyncConfig::default();
config.warp_sync = WarpSync::Enabled;
let net = TestNet::new_with_config(2, config);
assert_eq!(net.peer(0).sync.read().status().state, SyncState::WaitingPeers);
let net = TestNet::new(2);
assert_eq!(net.peer(0).sync.read().status().state, SyncState::Idle);
let mut config = SyncConfig::default();
config.warp_sync = WarpSync::Enabled;
let net = TestNet::new_with_config(2, config);
assert_eq!(
net.peer(0).sync.read().status().state,
SyncState::WaitingPeers
);
}
#[test]
fn status_packet() {
let mut net = TestNet::new(2);
net.peer(0).chain.add_blocks(100, EachBlockWith::Uncle);
net.peer(1).chain.add_blocks(1, EachBlockWith::Uncle);
let mut net = TestNet::new(2);
net.peer(0).chain.add_blocks(100, EachBlockWith::Uncle);
net.peer(1).chain.add_blocks(1, EachBlockWith::Uncle);
net.start();
net.start();
net.sync_step_peer(0);
net.sync_step_peer(0);
assert_eq!(1, net.peer(0).queue.read().len());
assert_eq!(0x00, net.peer(0).queue.read()[0].packet_id);
assert_eq!(1, net.peer(0).queue.read().len());
assert_eq!(0x00, net.peer(0).queue.read()[0].packet_id);
}
#[test]
fn propagate_hashes() {
let mut net = TestNet::new(6);
net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle);
net.sync();
let mut net = TestNet::new(6);
net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle);
net.sync();
net.peer(0).chain.add_blocks(10, EachBlockWith::Uncle);
net.sync();
net.trigger_chain_new_blocks(0); //first event just sets the marker
net.trigger_chain_new_blocks(0);
net.peer(0).chain.add_blocks(10, EachBlockWith::Uncle);
net.sync();
net.trigger_chain_new_blocks(0); //first event just sets the marker
net.trigger_chain_new_blocks(0);
// 5 peers with NewHahses, 4 with blocks
assert_eq!(9, net.peer(0).queue.read().len());
let mut hashes = 0;
let mut blocks = 0;
for i in 0..net.peer(0).queue.read().len() {
if net.peer(0).queue.read()[i].packet_id == 0x1 {
hashes += 1;
}
if net.peer(0).queue.read()[i].packet_id == 0x7 {
blocks += 1;
}
}
assert_eq!(blocks, 4);
assert_eq!(hashes, 5);
// 5 peers with NewHahses, 4 with blocks
assert_eq!(9, net.peer(0).queue.read().len());
let mut hashes = 0;
let mut blocks = 0;
for i in 0..net.peer(0).queue.read().len() {
if net.peer(0).queue.read()[i].packet_id == 0x1 {
hashes += 1;
}
if net.peer(0).queue.read()[i].packet_id == 0x7 {
blocks += 1;
}
}
assert_eq!(blocks, 4);
assert_eq!(hashes, 5);
}
#[test]
fn propagate_blocks() {
let mut net = TestNet::new(20);
net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle);
net.sync();
let mut net = TestNet::new(20);
net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle);
net.sync();
net.peer(0).chain.add_blocks(10, EachBlockWith::Uncle);
net.trigger_chain_new_blocks(0); //first event just sets the marker
net.trigger_chain_new_blocks(0);
net.peer(0).chain.add_blocks(10, EachBlockWith::Uncle);
net.trigger_chain_new_blocks(0); //first event just sets the marker
net.trigger_chain_new_blocks(0);
assert!(!net.peer(0).queue.read().is_empty());
// NEW_BLOCK_PACKET
let blocks = net.peer(0).queue.read().iter().filter(|p| p.packet_id == 0x7).count();
assert!(blocks > 0);
assert!(!net.peer(0).queue.read().is_empty());
// NEW_BLOCK_PACKET
let blocks = net
.peer(0)
.queue
.read()
.iter()
.filter(|p| p.packet_id == 0x7)
.count();
assert!(blocks > 0);
}
#[test]
fn restart_on_malformed_block() {
::env_logger::try_init().ok();
let mut net = TestNet::new(2);
net.peer(1).chain.add_blocks(5, EachBlockWith::Nothing);
net.peer(1).chain.add_block(EachBlockWith::Nothing, |mut header| {
header.set_extra_data(b"This extra data is way too long to be considered valid".to_vec());
header
});
net.sync_steps(20);
::env_logger::try_init().ok();
let mut net = TestNet::new(2);
net.peer(1).chain.add_blocks(5, EachBlockWith::Nothing);
net.peer(1)
.chain
.add_block(EachBlockWith::Nothing, |mut header| {
header
.set_extra_data(b"This extra data is way too long to be considered valid".to_vec());
header
});
net.sync_steps(20);
// This gets accepted just fine since the TestBlockChainClient performs no validation.
// Probably remove this test?
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 6);
// This gets accepted just fine since the TestBlockChainClient performs no validation.
// Probably remove this test?
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 6);
}
#[test]
fn reject_on_broken_chain() {
let mut net = TestNet::new(2);
net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing);
net.peer(1).chain.corrupt_block_parent(6);
net.sync_steps(20);
let mut net = TestNet::new(2);
net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing);
net.peer(1).chain.corrupt_block_parent(6);
net.sync_steps(20);
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 0);
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 0);
}
#[test]
fn disconnect_on_unrelated_chain() {
::env_logger::try_init().ok();
let mut net = TestNet::new(2);
net.peer(0).chain.set_history(Some(20));
net.peer(1).chain.set_history(Some(20));
net.restart_peer(0);
net.restart_peer(1);
net.peer(0).chain.add_blocks(500, EachBlockWith::Uncle);
net.peer(1).chain.add_blocks(300, EachBlockWith::Nothing);
net.sync();
assert_eq!(net.disconnect_events, vec![(0, 0)]);
::env_logger::try_init().ok();
let mut net = TestNet::new(2);
net.peer(0).chain.set_history(Some(20));
net.peer(1).chain.set_history(Some(20));
net.restart_peer(0);
net.restart_peer(1);
net.peer(0).chain.add_blocks(500, EachBlockWith::Uncle);
net.peer(1).chain.add_blocks(300, EachBlockWith::Nothing);
net.sync();
assert_eq!(net.disconnect_events, vec![(0, 0)]);
}

View File

@@ -14,111 +14,161 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use hash::keccak;
use ethereum_types::{U256, Address};
use io::{IoHandler, IoChannel};
use ethcore::client::{ChainInfo, ClientIoMessage};
use ethcore::engines;
use ethcore::spec::Spec;
use ethcore::miner::{self, MinerService};
use ethkey::{KeyPair, Secret};
use types::transaction::{Action, PendingTransaction, Transaction};
use super::helpers::*;
use ethcore::{
client::{ChainInfo, ClientIoMessage},
engines,
miner::{self, MinerService},
spec::Spec,
};
use ethereum_types::{Address, U256};
use ethkey::{KeyPair, Secret};
use hash::keccak;
use io::{IoChannel, IoHandler};
use std::sync::Arc;
use types::transaction::{Action, PendingTransaction, Transaction};
use SyncConfig;
fn new_tx(secret: &Secret, nonce: U256, chain_id: u64) -> PendingTransaction {
let signed = Transaction {
nonce: nonce.into(),
gas_price: 0.into(),
gas: 21000.into(),
action: Action::Call(Address::default()),
value: 0.into(),
data: Vec::new(),
}.sign(secret, Some(chain_id));
PendingTransaction::new(signed, None)
let signed = Transaction {
nonce: nonce.into(),
gas_price: 0.into(),
gas: 21000.into(),
action: Action::Call(Address::default()),
value: 0.into(),
data: Vec::new(),
}
.sign(secret, Some(chain_id));
PendingTransaction::new(signed, None)
}
#[test]
fn authority_round() {
let s0 = KeyPair::from_secret_slice(&keccak("1")).unwrap();
let s1 = KeyPair::from_secret_slice(&keccak("0")).unwrap();
let s0 = KeyPair::from_secret_slice(&keccak("1")).unwrap();
let s1 = KeyPair::from_secret_slice(&keccak("0")).unwrap();
let chain_id = Spec::new_test_round().chain_id();
let mut net = TestNet::with_spec(2, SyncConfig::default(), Spec::new_test_round);
let io_handler0: Arc<IoHandler<ClientIoMessage>> = Arc::new(TestIoHandler::new(net.peer(0).chain.clone()));
let io_handler1: Arc<IoHandler<ClientIoMessage>> = Arc::new(TestIoHandler::new(net.peer(1).chain.clone()));
// Push transaction to both clients. Only one of them gets lucky to produce a block.
net.peer(0).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s0.clone())));
net.peer(1).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s1.clone())));
net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain) as _);
net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain) as _);
net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1)));
net.peer(1).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0)));
// exchange statuses
net.sync();
// Trigger block proposal
net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), chain_id)).unwrap();
net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), chain_id)).unwrap();
// Sync a block
net.sync();
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1);
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1);
let chain_id = Spec::new_test_round().chain_id();
let mut net = TestNet::with_spec(2, SyncConfig::default(), Spec::new_test_round);
let io_handler0: Arc<IoHandler<ClientIoMessage>> =
Arc::new(TestIoHandler::new(net.peer(0).chain.clone()));
let io_handler1: Arc<IoHandler<ClientIoMessage>> =
Arc::new(TestIoHandler::new(net.peer(1).chain.clone()));
// Push transaction to both clients. Only one of them gets lucky to produce a block.
net.peer(0)
.miner
.set_author(miner::Author::Sealer(engines::signer::from_keypair(
s0.clone(),
)));
net.peer(1)
.miner
.set_author(miner::Author::Sealer(engines::signer::from_keypair(
s1.clone(),
)));
net.peer(0)
.chain
.engine()
.register_client(Arc::downgrade(&net.peer(0).chain) as _);
net.peer(1)
.chain
.engine()
.register_client(Arc::downgrade(&net.peer(1).chain) as _);
net.peer(0)
.chain
.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1)));
net.peer(1)
.chain
.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0)));
// exchange statuses
net.sync();
// Trigger block proposal
net.peer(0)
.miner
.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), chain_id))
.unwrap();
net.peer(1)
.miner
.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), chain_id))
.unwrap();
// Sync a block
net.sync();
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1);
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1);
net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), chain_id)).unwrap();
net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), chain_id)).unwrap();
// Move to next proposer step.
net.peer(0).chain.engine().step();
net.peer(1).chain.engine().step();
net.sync();
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 2);
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2);
net.peer(0)
.miner
.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), chain_id))
.unwrap();
net.peer(1)
.miner
.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), chain_id))
.unwrap();
// Move to next proposer step.
net.peer(0).chain.engine().step();
net.peer(1).chain.engine().step();
net.sync();
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 2);
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2);
// Fork the network with equal height.
net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), chain_id)).unwrap();
net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), chain_id)).unwrap();
// Let both nodes build one block.
net.peer(0).chain.engine().step();
let early_hash = net.peer(0).chain.chain_info().best_block_hash;
net.peer(1).chain.engine().step();
net.peer(0).chain.engine().step();
net.peer(1).chain.engine().step();
let ci0 = net.peer(0).chain.chain_info();
let ci1 = net.peer(1).chain.chain_info();
assert_eq!(ci0.best_block_number, 3);
assert_eq!(ci1.best_block_number, 3);
assert!(ci0.best_block_hash != ci1.best_block_hash);
// Reorg to the chain with earlier view.
net.sync();
let ci0 = net.peer(0).chain.chain_info();
let ci1 = net.peer(1).chain.chain_info();
assert_eq!(ci0.best_block_number, 3);
assert_eq!(ci1.best_block_number, 3);
assert_eq!(ci0.best_block_hash, ci1.best_block_hash);
assert_eq!(ci1.best_block_hash, early_hash);
// Fork the network with equal height.
net.peer(0)
.miner
.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), chain_id))
.unwrap();
net.peer(1)
.miner
.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), chain_id))
.unwrap();
// Let both nodes build one block.
net.peer(0).chain.engine().step();
let early_hash = net.peer(0).chain.chain_info().best_block_hash;
net.peer(1).chain.engine().step();
net.peer(0).chain.engine().step();
net.peer(1).chain.engine().step();
let ci0 = net.peer(0).chain.chain_info();
let ci1 = net.peer(1).chain.chain_info();
assert_eq!(ci0.best_block_number, 3);
assert_eq!(ci1.best_block_number, 3);
assert!(ci0.best_block_hash != ci1.best_block_hash);
// Reorg to the chain with earlier view.
net.sync();
let ci0 = net.peer(0).chain.chain_info();
let ci1 = net.peer(1).chain.chain_info();
assert_eq!(ci0.best_block_number, 3);
assert_eq!(ci1.best_block_number, 3);
assert_eq!(ci0.best_block_hash, ci1.best_block_hash);
assert_eq!(ci1.best_block_hash, early_hash);
// Selfish miner
net.peer(0).miner.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 3.into(), chain_id)).unwrap();
net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 3.into(), chain_id)).unwrap();
// Node 0 is an earlier primary.
net.peer(0).chain.engine().step();
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4);
net.peer(0).chain.engine().step();
net.peer(0).chain.engine().step();
net.peer(0).chain.engine().step();
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4);
// Node 1 makes 2 blocks, but is a later primary on the first one.
net.peer(1).chain.engine().step();
net.peer(1).chain.engine().step();
net.peer(1).miner.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 4.into(), chain_id)).unwrap();
net.peer(1).chain.engine().step();
net.peer(1).chain.engine().step();
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 5);
// Reorg to the longest chain one not ealier view one.
net.sync();
let ci0 = net.peer(0).chain.chain_info();
let ci1 = net.peer(1).chain.chain_info();
assert_eq!(ci0.best_block_number, 5);
assert_eq!(ci1.best_block_number, 5);
assert_eq!(ci0.best_block_hash, ci1.best_block_hash);
// Selfish miner
net.peer(0)
.miner
.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 3.into(), chain_id))
.unwrap();
net.peer(1)
.miner
.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 3.into(), chain_id))
.unwrap();
// Node 0 is an earlier primary.
net.peer(0).chain.engine().step();
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4);
net.peer(0).chain.engine().step();
net.peer(0).chain.engine().step();
net.peer(0).chain.engine().step();
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4);
// Node 1 makes 2 blocks, but is a later primary on the first one.
net.peer(1).chain.engine().step();
net.peer(1).chain.engine().step();
net.peer(1)
.miner
.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 4.into(), chain_id))
.unwrap();
net.peer(1).chain.engine().step();
net.peer(1).chain.engine().step();
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 5);
// Reorg to the longest chain one not ealier view one.
net.sync();
let ci0 = net.peer(0).chain.chain_info();
let ci1 = net.peer(1).chain.chain_info();
assert_eq!(ci0.best_block_number, 5);
assert_eq!(ci1.best_block_number, 5);
assert_eq!(ci0.best_block_hash, ci1.best_block_hash);
}

File diff suppressed because it is too large Load Diff

View File

@@ -14,11 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
pub mod helpers;
pub mod snapshot;
mod chain;
mod consensus;
pub mod helpers;
mod private;
pub mod snapshot;
#[cfg(feature = "ipc")]
mod rpc;

View File

@@ -14,141 +14,187 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use hash::keccak;
use io::{IoHandler, IoChannel};
use types::transaction::{Transaction, Action};
use types::ids::BlockId;
use ethcore::CreateContractAddress;
use ethcore::client::{ClientIoMessage, BlockChainClient};
use ethcore::executive::{contract_address};
use ethcore::engines;
use ethcore::miner::{self, MinerService};
use ethcore::spec::Spec;
use ethcore::test_helpers::{push_block_with_transactions};
use ethcore_private_tx::{Provider, ProviderConfig, NoopEncryptor, Importer, SignedPrivateTransaction, StoringKeyProvider};
use ethcore::{
client::{BlockChainClient, ClientIoMessage},
engines,
executive::contract_address,
miner::{self, MinerService},
spec::Spec,
test_helpers::push_block_with_transactions,
CreateContractAddress,
};
use ethcore_private_tx::{
Importer, NoopEncryptor, Provider, ProviderConfig, SignedPrivateTransaction, StoringKeyProvider,
};
use ethkey::KeyPair;
use tests::helpers::{TestNet, TestIoHandler};
use rustc_hex::FromHex;
use hash::keccak;
use io::{IoChannel, IoHandler};
use rlp::Rlp;
use rustc_hex::FromHex;
use std::sync::Arc;
use tests::helpers::{TestIoHandler, TestNet};
use types::{
ids::BlockId,
transaction::{Action, Transaction},
};
use SyncConfig;
fn seal_spec() -> Spec {
let spec_data = include_str!("../res/private_spec.json");
Spec::load(&::std::env::temp_dir(), spec_data.as_bytes()).unwrap()
let spec_data = include_str!("../res/private_spec.json");
Spec::load(&::std::env::temp_dir(), spec_data.as_bytes()).unwrap()
}
#[test]
fn send_private_transaction() {
// Setup two clients
let s0 = KeyPair::from_secret_slice(&keccak("1")).unwrap();
let s1 = KeyPair::from_secret_slice(&keccak("0")).unwrap();
// Setup two clients
let s0 = KeyPair::from_secret_slice(&keccak("1")).unwrap();
let s1 = KeyPair::from_secret_slice(&keccak("0")).unwrap();
let signer = Arc::new(ethcore_private_tx::KeyPairSigner(vec![s0.clone(), s1.clone()]));
let signer = Arc::new(ethcore_private_tx::KeyPairSigner(vec![
s0.clone(),
s1.clone(),
]));
let mut net = TestNet::with_spec(2, SyncConfig::default(), seal_spec);
let client0 = net.peer(0).chain.clone();
let client1 = net.peer(1).chain.clone();
let io_handler0: Arc<IoHandler<ClientIoMessage>> = Arc::new(TestIoHandler::new(net.peer(0).chain.clone()));
let io_handler1: Arc<IoHandler<ClientIoMessage>> = Arc::new(TestIoHandler::new(net.peer(1).chain.clone()));
let mut net = TestNet::with_spec(2, SyncConfig::default(), seal_spec);
let client0 = net.peer(0).chain.clone();
let client1 = net.peer(1).chain.clone();
let io_handler0: Arc<IoHandler<ClientIoMessage>> =
Arc::new(TestIoHandler::new(net.peer(0).chain.clone()));
let io_handler1: Arc<IoHandler<ClientIoMessage>> =
Arc::new(TestIoHandler::new(net.peer(1).chain.clone()));
net.peer(0).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s0.clone())));
net.peer(1).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s1.clone())));
net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain) as _);
net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain) as _);
net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0)));
net.peer(1).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1)));
net.peer(0)
.miner
.set_author(miner::Author::Sealer(engines::signer::from_keypair(
s0.clone(),
)));
net.peer(1)
.miner
.set_author(miner::Author::Sealer(engines::signer::from_keypair(
s1.clone(),
)));
net.peer(0)
.chain
.engine()
.register_client(Arc::downgrade(&net.peer(0).chain) as _);
net.peer(1)
.chain
.engine()
.register_client(Arc::downgrade(&net.peer(1).chain) as _);
net.peer(0)
.chain
.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0)));
net.peer(1)
.chain
.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1)));
let (address, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &s0.address(), &0.into(), &[]);
let chain_id = client0.signing_chain_id();
let (address, _) = contract_address(
CreateContractAddress::FromSenderAndNonce,
&s0.address(),
&0.into(),
&[],
);
let chain_id = client0.signing_chain_id();
// Exhange statuses
net.sync();
// Exhange statuses
net.sync();
// Setup private providers
let validator_config = ProviderConfig{
validator_accounts: vec![s1.address()],
signer_account: None,
};
// Setup private providers
let validator_config = ProviderConfig {
validator_accounts: vec![s1.address()],
signer_account: None,
};
let signer_config = ProviderConfig{
validator_accounts: Vec::new(),
signer_account: Some(s0.address()),
};
let signer_config = ProviderConfig {
validator_accounts: Vec::new(),
signer_account: Some(s0.address()),
};
let private_keys = Arc::new(StoringKeyProvider::default());
let private_keys = Arc::new(StoringKeyProvider::default());
let pm0 = Arc::new(Provider::new(
client0.clone(),
net.peer(0).miner.clone(),
signer.clone(),
Box::new(NoopEncryptor::default()),
signer_config,
IoChannel::to_handler(Arc::downgrade(&io_handler0)),
private_keys.clone(),
));
pm0.add_notify(net.peers[0].clone());
let pm0 = Arc::new(Provider::new(
client0.clone(),
net.peer(0).miner.clone(),
signer.clone(),
Box::new(NoopEncryptor::default()),
signer_config,
IoChannel::to_handler(Arc::downgrade(&io_handler0)),
private_keys.clone(),
));
pm0.add_notify(net.peers[0].clone());
let pm1 = Arc::new(Provider::new(
client1.clone(),
net.peer(1).miner.clone(),
signer.clone(),
Box::new(NoopEncryptor::default()),
validator_config,
IoChannel::to_handler(Arc::downgrade(&io_handler1)),
private_keys.clone(),
));
pm1.add_notify(net.peers[1].clone());
let pm1 = Arc::new(Provider::new(
client1.clone(),
net.peer(1).miner.clone(),
signer.clone(),
Box::new(NoopEncryptor::default()),
validator_config,
IoChannel::to_handler(Arc::downgrade(&io_handler1)),
private_keys.clone(),
));
pm1.add_notify(net.peers[1].clone());
// Create and deploy contract
let private_contract_test = "6060604052341561000f57600080fd5b60d88061001d6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630c55699c146046578063bc64b76d14607457600080fd5b3415605057600080fd5b60566098565b60405180826000191660001916815260200191505060405180910390f35b3415607e57600080fd5b6096600480803560001916906020019091905050609e565b005b60005481565b8060008160001916905550505600a165627a7a723058206acbdf4b15ca4c2d43e1b1879b830451a34f1e9d02ff1f2f394d8d857e79d2080029".from_hex().unwrap();
let mut private_create_tx = Transaction::default();
private_create_tx.action = Action::Create;
private_create_tx.data = private_contract_test;
private_create_tx.gas = 200000.into();
let private_create_tx_signed = private_create_tx.sign(&s0.secret(), None);
let validators = vec![s1.address()];
let (public_tx, _) = pm0.public_creation_transaction(BlockId::Latest, &private_create_tx_signed, &validators, 0.into()).unwrap();
let public_tx = public_tx.sign(&s0.secret(), chain_id);
// Create and deploy contract
let private_contract_test = "6060604052341561000f57600080fd5b60d88061001d6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630c55699c146046578063bc64b76d14607457600080fd5b3415605057600080fd5b60566098565b60405180826000191660001916815260200191505060405180910390f35b3415607e57600080fd5b6096600480803560001916906020019091905050609e565b005b60005481565b8060008160001916905550505600a165627a7a723058206acbdf4b15ca4c2d43e1b1879b830451a34f1e9d02ff1f2f394d8d857e79d2080029".from_hex().unwrap();
let mut private_create_tx = Transaction::default();
private_create_tx.action = Action::Create;
private_create_tx.data = private_contract_test;
private_create_tx.gas = 200000.into();
let private_create_tx_signed = private_create_tx.sign(&s0.secret(), None);
let validators = vec![s1.address()];
let (public_tx, _) = pm0
.public_creation_transaction(
BlockId::Latest,
&private_create_tx_signed,
&validators,
0.into(),
)
.unwrap();
let public_tx = public_tx.sign(&s0.secret(), chain_id);
let public_tx_copy = public_tx.clone();
push_block_with_transactions(&client0, &[public_tx]);
push_block_with_transactions(&client1, &[public_tx_copy]);
let public_tx_copy = public_tx.clone();
push_block_with_transactions(&client0, &[public_tx]);
push_block_with_transactions(&client1, &[public_tx_copy]);
net.sync();
net.sync();
//Create private transaction for modifying state
let mut private_tx = Transaction::default();
private_tx.action = Action::Call(address.clone());
private_tx.data = "bc64b76d2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(); //setX(42)
private_tx.gas = 120000.into();
private_tx.nonce = 1.into();
let private_tx = private_tx.sign(&s0.secret(), None);
assert!(pm0.create_private_transaction(private_tx).is_ok());
//Create private transaction for modifying state
let mut private_tx = Transaction::default();
private_tx.action = Action::Call(address.clone());
private_tx.data = "bc64b76d2a00000000000000000000000000000000000000000000000000000000000000"
.from_hex()
.unwrap(); //setX(42)
private_tx.gas = 120000.into();
private_tx.nonce = 1.into();
let private_tx = private_tx.sign(&s0.secret(), None);
assert!(pm0.create_private_transaction(private_tx).is_ok());
//send private transaction message to validator
net.sync();
//send private transaction message to validator
net.sync();
let validator_handler = net.peer(1).private_tx_handler.clone();
let received_private_transactions = validator_handler.txs.lock().clone();
assert_eq!(received_private_transactions.len(), 1);
let validator_handler = net.peer(1).private_tx_handler.clone();
let received_private_transactions = validator_handler.txs.lock().clone();
assert_eq!(received_private_transactions.len(), 1);
//process received private transaction message
let private_transaction = received_private_transactions[0].clone();
assert!(pm1.import_private_transaction(&private_transaction).is_ok());
//process received private transaction message
let private_transaction = received_private_transactions[0].clone();
assert!(pm1.import_private_transaction(&private_transaction).is_ok());
//send signed response
net.sync();
//send signed response
net.sync();
let sender_handler = net.peer(0).private_tx_handler.clone();
let received_signed_private_transactions = sender_handler.signed_txs.lock().clone();
assert_eq!(received_signed_private_transactions.len(), 1);
let sender_handler = net.peer(0).private_tx_handler.clone();
let received_signed_private_transactions = sender_handler.signed_txs.lock().clone();
assert_eq!(received_signed_private_transactions.len(), 1);
//process signed response
let signed_private_transaction = received_signed_private_transactions[0].clone();
assert!(pm0.import_signed_private_transaction(&signed_private_transaction).is_ok());
let signature: SignedPrivateTransaction = Rlp::new(&signed_private_transaction).as_val().unwrap();
assert!(pm0.process_signature(&signature).is_ok());
let local_transactions = net.peer(0).miner.local_transactions();
assert_eq!(local_transactions.len(), 1);
//process signed response
let signed_private_transaction = received_signed_private_transactions[0].clone();
assert!(pm0
.import_signed_private_transaction(&signed_private_transaction)
.is_ok());
let signature: SignedPrivateTransaction =
Rlp::new(&signed_private_transaction).as_val().unwrap();
assert!(pm0.process_signature(&signature).is_ok());
let local_transactions = net.peer(0).miner.local_transactions();
assert_eq!(local_transactions.len(), 1);
}

View File

@@ -15,15 +15,15 @@
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use super::super::NetworkConfiguration;
use ipc::binary::{deserialize, serialize};
use network::NetworkConfiguration as BasicNetworkConfiguration;
use std::convert::From;
use ipc::binary::{serialize, deserialize};
#[test]
fn network_settings_serialize() {
let net_cfg = NetworkConfiguration::from(BasicNetworkConfiguration::new_local());
let serialized = serialize(&net_cfg).unwrap();
let deserialized = deserialize::<NetworkConfiguration>(&serialized).unwrap();
let net_cfg = NetworkConfiguration::from(BasicNetworkConfiguration::new_local());
let serialized = serialize(&net_cfg).unwrap();
let deserialized = deserialize::<NetworkConfiguration>(&serialized).unwrap();
assert_eq!(net_cfg.udp_port, deserialized.udp_port);
assert_eq!(net_cfg.udp_port, deserialized.udp_port);
}

View File

@@ -14,145 +14,203 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::collections::HashMap;
use std::sync::Arc;
use hash::keccak;
use ethereum_types::H256;
use parking_lot::Mutex;
use bytes::Bytes;
use ethcore::snapshot::{SnapshotService, ManifestData, RestorationStatus};
use ethcore::client::EachBlockWith;
use types::BlockNumber;
use super::helpers::*;
use {SyncConfig, WarpSync};
use bytes::Bytes;
use ethcore::{
client::EachBlockWith,
snapshot::{ManifestData, RestorationStatus, SnapshotService},
};
use ethereum_types::H256;
use hash::keccak;
use parking_lot::Mutex;
use std::{collections::HashMap, sync::Arc};
use types::BlockNumber;
use SyncConfig;
use WarpSync;
pub struct TestSnapshotService {
manifest: Option<ManifestData>,
chunks: HashMap<H256, Bytes>,
manifest: Option<ManifestData>,
chunks: HashMap<H256, Bytes>,
restoration_manifest: Mutex<Option<ManifestData>>,
state_restoration_chunks: Mutex<HashMap<H256, Bytes>>,
block_restoration_chunks: Mutex<HashMap<H256, Bytes>>,
restoration_manifest: Mutex<Option<ManifestData>>,
state_restoration_chunks: Mutex<HashMap<H256, Bytes>>,
block_restoration_chunks: Mutex<HashMap<H256, Bytes>>,
}
impl TestSnapshotService {
pub fn new() -> TestSnapshotService {
TestSnapshotService {
manifest: None,
chunks: HashMap::new(),
restoration_manifest: Mutex::new(None),
state_restoration_chunks: Mutex::new(HashMap::new()),
block_restoration_chunks: Mutex::new(HashMap::new()),
}
}
pub fn new() -> TestSnapshotService {
TestSnapshotService {
manifest: None,
chunks: HashMap::new(),
restoration_manifest: Mutex::new(None),
state_restoration_chunks: Mutex::new(HashMap::new()),
block_restoration_chunks: Mutex::new(HashMap::new()),
}
}
pub fn new_with_snapshot(num_chunks: usize, block_hash: H256, block_number: BlockNumber) -> TestSnapshotService {
let num_state_chunks = num_chunks / 2;
let num_block_chunks = num_chunks - num_state_chunks;
let state_chunks: Vec<Bytes> = (0..num_state_chunks).map(|_| H256::random().to_vec()).collect();
let block_chunks: Vec<Bytes> = (0..num_block_chunks).map(|_| H256::random().to_vec()).collect();
let manifest = ManifestData {
version: 2,
state_hashes: state_chunks.iter().map(|data| keccak(data)).collect(),
block_hashes: block_chunks.iter().map(|data| keccak(data)).collect(),
state_root: H256::new(),
block_number: block_number,
block_hash: block_hash,
};
let mut chunks: HashMap<H256, Bytes> = state_chunks.into_iter().map(|data| (keccak(&data), data)).collect();
chunks.extend(block_chunks.into_iter().map(|data| (keccak(&data), data)));
TestSnapshotService {
manifest: Some(manifest),
chunks: chunks,
restoration_manifest: Mutex::new(None),
state_restoration_chunks: Mutex::new(HashMap::new()),
block_restoration_chunks: Mutex::new(HashMap::new()),
}
}
pub fn new_with_snapshot(
num_chunks: usize,
block_hash: H256,
block_number: BlockNumber,
) -> TestSnapshotService {
let num_state_chunks = num_chunks / 2;
let num_block_chunks = num_chunks - num_state_chunks;
let state_chunks: Vec<Bytes> = (0..num_state_chunks)
.map(|_| H256::random().to_vec())
.collect();
let block_chunks: Vec<Bytes> = (0..num_block_chunks)
.map(|_| H256::random().to_vec())
.collect();
let manifest = ManifestData {
version: 2,
state_hashes: state_chunks.iter().map(|data| keccak(data)).collect(),
block_hashes: block_chunks.iter().map(|data| keccak(data)).collect(),
state_root: H256::new(),
block_number: block_number,
block_hash: block_hash,
};
let mut chunks: HashMap<H256, Bytes> = state_chunks
.into_iter()
.map(|data| (keccak(&data), data))
.collect();
chunks.extend(block_chunks.into_iter().map(|data| (keccak(&data), data)));
TestSnapshotService {
manifest: Some(manifest),
chunks: chunks,
restoration_manifest: Mutex::new(None),
state_restoration_chunks: Mutex::new(HashMap::new()),
block_restoration_chunks: Mutex::new(HashMap::new()),
}
}
}
impl SnapshotService for TestSnapshotService {
fn manifest(&self) -> Option<ManifestData> {
self.manifest.as_ref().cloned()
}
fn manifest(&self) -> Option<ManifestData> {
self.manifest.as_ref().cloned()
}
fn supported_versions(&self) -> Option<(u64, u64)> {
Some((1, 2))
}
fn supported_versions(&self) -> Option<(u64, u64)> {
Some((1, 2))
}
fn completed_chunks(&self) -> Option<Vec<H256>> {
Some(vec![])
}
fn completed_chunks(&self) -> Option<Vec<H256>> {
Some(vec![])
}
fn chunk(&self, hash: H256) -> Option<Bytes> {
self.chunks.get(&hash).cloned()
}
fn chunk(&self, hash: H256) -> Option<Bytes> {
self.chunks.get(&hash).cloned()
}
fn status(&self) -> RestorationStatus {
match *self.restoration_manifest.lock() {
Some(ref manifest) if self.state_restoration_chunks.lock().len() == manifest.state_hashes.len() &&
self.block_restoration_chunks.lock().len() == manifest.block_hashes.len() => RestorationStatus::Inactive,
Some(ref manifest) => RestorationStatus::Ongoing {
state_chunks: manifest.state_hashes.len() as u32,
block_chunks: manifest.block_hashes.len() as u32,
state_chunks_done: self.state_restoration_chunks.lock().len() as u32,
block_chunks_done: self.block_restoration_chunks.lock().len() as u32,
},
None => RestorationStatus::Inactive,
}
}
fn status(&self) -> RestorationStatus {
match *self.restoration_manifest.lock() {
Some(ref manifest)
if self.state_restoration_chunks.lock().len() == manifest.state_hashes.len()
&& self.block_restoration_chunks.lock().len()
== manifest.block_hashes.len() =>
{
RestorationStatus::Inactive
}
Some(ref manifest) => RestorationStatus::Ongoing {
state_chunks: manifest.state_hashes.len() as u32,
block_chunks: manifest.block_hashes.len() as u32,
state_chunks_done: self.state_restoration_chunks.lock().len() as u32,
block_chunks_done: self.block_restoration_chunks.lock().len() as u32,
},
None => RestorationStatus::Inactive,
}
}
fn begin_restore(&self, manifest: ManifestData) {
let mut restoration_manifest = self.restoration_manifest.lock();
fn begin_restore(&self, manifest: ManifestData) {
let mut restoration_manifest = self.restoration_manifest.lock();
if let Some(ref c_manifest) = *restoration_manifest {
if c_manifest.state_root == manifest.state_root {
return;
}
}
if let Some(ref c_manifest) = *restoration_manifest {
if c_manifest.state_root == manifest.state_root {
return;
}
}
*restoration_manifest = Some(manifest);
self.state_restoration_chunks.lock().clear();
self.block_restoration_chunks.lock().clear();
}
*restoration_manifest = Some(manifest);
self.state_restoration_chunks.lock().clear();
self.block_restoration_chunks.lock().clear();
}
fn abort_restore(&self) {
*self.restoration_manifest.lock() = None;
self.state_restoration_chunks.lock().clear();
self.block_restoration_chunks.lock().clear();
}
fn abort_restore(&self) {
*self.restoration_manifest.lock() = None;
self.state_restoration_chunks.lock().clear();
self.block_restoration_chunks.lock().clear();
}
fn abort_snapshot(&self) {}
fn abort_snapshot(&self) {}
fn restore_state_chunk(&self, hash: H256, chunk: Bytes) {
if self.restoration_manifest.lock().as_ref().map_or(false, |m| m.state_hashes.iter().any(|h| h == &hash)) {
self.state_restoration_chunks.lock().insert(hash, chunk);
}
}
fn restore_state_chunk(&self, hash: H256, chunk: Bytes) {
if self
.restoration_manifest
.lock()
.as_ref()
.map_or(false, |m| m.state_hashes.iter().any(|h| h == &hash))
{
self.state_restoration_chunks.lock().insert(hash, chunk);
}
}
fn restore_block_chunk(&self, hash: H256, chunk: Bytes) {
if self.restoration_manifest.lock().as_ref().map_or(false, |m| m.block_hashes.iter().any(|h| h == &hash)) {
self.block_restoration_chunks.lock().insert(hash, chunk);
}
}
fn restore_block_chunk(&self, hash: H256, chunk: Bytes) {
if self
.restoration_manifest
.lock()
.as_ref()
.map_or(false, |m| m.block_hashes.iter().any(|h| h == &hash))
{
self.block_restoration_chunks.lock().insert(hash, chunk);
}
}
fn shutdown(&self) {
self.abort_restore();
}
fn shutdown(&self) {
self.abort_restore();
}
}
#[test]
fn snapshot_sync() {
::env_logger::try_init().ok();
let mut config = SyncConfig::default();
config.warp_sync = WarpSync::Enabled;
let mut net = TestNet::new_with_config(5, config);
let snapshot_service = Arc::new(TestSnapshotService::new_with_snapshot(16, H256::new(), 500000));
for i in 0..4 {
net.peer_mut(i).snapshot_service = snapshot_service.clone();
net.peer(i).chain.add_blocks(1, EachBlockWith::Nothing);
}
net.sync_steps(50);
assert_eq!(net.peer(4).snapshot_service.state_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().state_hashes.len());
assert_eq!(net.peer(4).snapshot_service.block_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().block_hashes.len());
::env_logger::try_init().ok();
let mut config = SyncConfig::default();
config.warp_sync = WarpSync::Enabled;
let mut net = TestNet::new_with_config(5, config);
let snapshot_service = Arc::new(TestSnapshotService::new_with_snapshot(
16,
H256::new(),
500000,
));
for i in 0..4 {
net.peer_mut(i).snapshot_service = snapshot_service.clone();
net.peer(i).chain.add_blocks(1, EachBlockWith::Nothing);
}
net.sync_steps(50);
assert_eq!(
net.peer(4)
.snapshot_service
.state_restoration_chunks
.lock()
.len(),
net.peer(0)
.snapshot_service
.manifest
.as_ref()
.unwrap()
.state_hashes
.len()
);
assert_eq!(
net.peer(4)
.snapshot_service
.block_restoration_chunks
.lock()
.len(),
net.peer(0)
.snapshot_service
.manifest
.as_ref()
.unwrap()
.block_hashes
.len()
);
}

View File

@@ -15,121 +15,137 @@
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use api::TransactionStats;
use std::hash::BuildHasher;
use std::collections::{HashSet, HashMap};
use ethereum_types::{H256, H512};
use fastmap::H256FastMap;
use std::{
collections::{HashMap, HashSet},
hash::BuildHasher,
};
use types::BlockNumber;
type NodeId = H512;
#[derive(Debug, PartialEq, Clone)]
pub struct Stats {
first_seen: BlockNumber,
propagated_to: HashMap<NodeId, usize>,
first_seen: BlockNumber,
propagated_to: HashMap<NodeId, usize>,
}
impl Stats {
pub fn new(number: BlockNumber) -> Self {
Stats {
first_seen: number,
propagated_to: Default::default(),
}
}
pub fn new(number: BlockNumber) -> Self {
Stats {
first_seen: number,
propagated_to: Default::default(),
}
}
}
impl<'a> From<&'a Stats> for TransactionStats {
fn from(other: &'a Stats) -> Self {
TransactionStats {
first_seen: other.first_seen,
propagated_to: other.propagated_to
.iter()
.map(|(hash, size)| (*hash, *size))
.collect(),
}
}
fn from(other: &'a Stats) -> Self {
TransactionStats {
first_seen: other.first_seen,
propagated_to: other
.propagated_to
.iter()
.map(|(hash, size)| (*hash, *size))
.collect(),
}
}
}
#[derive(Debug, Default)]
pub struct TransactionsStats {
pending_transactions: H256FastMap<Stats>,
pending_transactions: H256FastMap<Stats>,
}
impl TransactionsStats {
/// Increases number of propagations to given `enodeid`.
pub fn propagated(&mut self, hash: &H256, enode_id: Option<NodeId>, current_block_num: BlockNumber) {
let enode_id = enode_id.unwrap_or_default();
let stats = self.pending_transactions.entry(*hash).or_insert_with(|| Stats::new(current_block_num));
let count = stats.propagated_to.entry(enode_id).or_insert(0);
*count = count.saturating_add(1);
}
/// Increases number of propagations to given `enodeid`.
pub fn propagated(
&mut self,
hash: &H256,
enode_id: Option<NodeId>,
current_block_num: BlockNumber,
) {
let enode_id = enode_id.unwrap_or_default();
let stats = self
.pending_transactions
.entry(*hash)
.or_insert_with(|| Stats::new(current_block_num));
let count = stats.propagated_to.entry(enode_id).or_insert(0);
*count = count.saturating_add(1);
}
/// Returns propagation stats for given hash or `None` if hash is not known.
#[cfg(test)]
pub fn get(&self, hash: &H256) -> Option<&Stats> {
self.pending_transactions.get(hash)
}
/// Returns propagation stats for given hash or `None` if hash is not known.
#[cfg(test)]
pub fn get(&self, hash: &H256) -> Option<&Stats> {
self.pending_transactions.get(hash)
}
pub fn stats(&self) -> &H256FastMap<Stats> {
&self.pending_transactions
}
pub fn stats(&self) -> &H256FastMap<Stats> {
&self.pending_transactions
}
/// Retains only transactions present in given `HashSet`.
pub fn retain<S: BuildHasher>(&mut self, hashes: &HashSet<H256, S>) {
let to_remove = self.pending_transactions.keys()
.filter(|hash| !hashes.contains(hash))
.cloned()
.collect::<Vec<_>>();
/// Retains only transactions present in given `HashSet`.
pub fn retain<S: BuildHasher>(&mut self, hashes: &HashSet<H256, S>) {
let to_remove = self
.pending_transactions
.keys()
.filter(|hash| !hashes.contains(hash))
.cloned()
.collect::<Vec<_>>();
for hash in to_remove {
self.pending_transactions.remove(&hash);
}
}
for hash in to_remove {
self.pending_transactions.remove(&hash);
}
}
}
#[cfg(test)]
mod tests {
use std::collections::{HashMap, HashSet};
use super::{Stats, TransactionsStats};
use super::{Stats, TransactionsStats};
use std::collections::{HashMap, HashSet};
#[test]
fn should_keep_track_of_propagations() {
// given
let mut stats = TransactionsStats::default();
let hash = 5.into();
let enodeid1 = 2.into();
let enodeid2 = 5.into();
#[test]
fn should_keep_track_of_propagations() {
// given
let mut stats = TransactionsStats::default();
let hash = 5.into();
let enodeid1 = 2.into();
let enodeid2 = 5.into();
// when
stats.propagated(&hash, Some(enodeid1), 5);
stats.propagated(&hash, Some(enodeid1), 10);
stats.propagated(&hash, Some(enodeid2), 15);
// when
stats.propagated(&hash, Some(enodeid1), 5);
stats.propagated(&hash, Some(enodeid1), 10);
stats.propagated(&hash, Some(enodeid2), 15);
// then
let stats = stats.get(&hash);
assert_eq!(stats, Some(&Stats {
first_seen: 5,
propagated_to: hash_map![
enodeid1 => 2,
enodeid2 => 1
],
}));
}
// then
let stats = stats.get(&hash);
assert_eq!(
stats,
Some(&Stats {
first_seen: 5,
propagated_to: hash_map![
enodeid1 => 2,
enodeid2 => 1
],
})
);
}
#[test]
fn should_remove_hash_from_tracking() {
// given
let mut stats = TransactionsStats::default();
let hash = 5.into();
let enodeid1 = 5.into();
stats.propagated(&hash, Some(enodeid1), 10);
#[test]
fn should_remove_hash_from_tracking() {
// given
let mut stats = TransactionsStats::default();
let hash = 5.into();
let enodeid1 = 5.into();
stats.propagated(&hash, Some(enodeid1), 10);
// when
stats.retain(&HashSet::new());
// when
stats.retain(&HashSet::new());
// then
let stats = stats.get(&hash);
assert_eq!(stats, None);
}
// then
let stats = stats.get(&hash);
assert_eq!(stats, None);
}
}