Implement eth/66 (#467)

* Allow eth/66

* Add eth/66 request ids

* fmt

* Remove some leftovers

* fmt

* Change behaviour in case of missing peer info

- Assume eth/66 protocol, not earlier one
- Log just a trace, not an error
This commit is contained in:
Jochen Müller 2021-07-05 15:59:22 +02:00 committed by GitHub
parent fdaee51ca0
commit 43ee520904
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 319 additions and 114 deletions

View File

@ -33,7 +33,7 @@ use std::{
use chain::{ use chain::{
fork_filter::ForkFilterApi, ChainSyncApi, SyncState, SyncStatus as EthSyncStatus, fork_filter::ForkFilterApi, ChainSyncApi, SyncState, SyncStatus as EthSyncStatus,
ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, ETH_PROTOCOL_VERSION_65, ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, ETH_PROTOCOL_VERSION_65,
PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, ETH_PROTOCOL_VERSION_66, PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2,
}; };
use ethcore::{ use ethcore::{
client::{BlockChainClient, ChainMessageType, ChainNotify, NewBlocks}, client::{BlockChainClient, ChainMessageType, ChainNotify, NewBlocks},
@ -571,6 +571,7 @@ impl ChainNotify for EthSync {
ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_63,
ETH_PROTOCOL_VERSION_64, ETH_PROTOCOL_VERSION_64,
ETH_PROTOCOL_VERSION_65, ETH_PROTOCOL_VERSION_65,
ETH_PROTOCOL_VERSION_66,
], ],
) )
.unwrap_or_else(|e| warn!("Error registering ethereum protocol: {:?}", e)); .unwrap_or_else(|e| warn!("Error registering ethereum protocol: {:?}", e));

View File

@ -32,14 +32,17 @@ use std::{cmp, mem, time::Instant};
use sync_io::SyncIo; use sync_io::SyncIo;
use types::{block_status::BlockStatus, ids::BlockId, BlockNumber}; use types::{block_status::BlockStatus, ids::BlockId, BlockNumber};
use super::sync_packet::{ use super::{
request_id::strip_request_id,
sync_packet::{
PacketInfo, PacketInfo,
SyncPacket::{self, *}, SyncPacket::{self, *},
},
}; };
use super::{ use super::{
BlockSet, ChainSync, ForkConfirmation, PacketProcessError, PeerAsking, PeerInfo, SyncRequester, BlockSet, ChainSync, ForkConfirmation, PacketProcessError, PeerAsking, PeerInfo, SyncRequester,
SyncState, ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, ETH_PROTOCOL_VERSION_65, SyncState, ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, ETH_PROTOCOL_VERSION_66,
MAX_NEW_BLOCK_AGE, MAX_NEW_HASHES, PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, MAX_NEW_BLOCK_AGE, MAX_NEW_HASHES, PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2,
}; };
@ -55,9 +58,11 @@ impl SyncHandler {
packet_id: u8, packet_id: u8,
data: &[u8], data: &[u8],
) { ) {
let rlp = Rlp::new(data);
if let Some(packet_id) = SyncPacket::from_u8(packet_id) { if let Some(packet_id) = SyncPacket::from_u8(packet_id) {
let result = match packet_id { let rlp_result = strip_request_id(data, sync, &peer, &packet_id);
let result = match rlp_result {
Ok((rlp, _)) => match packet_id {
StatusPacket => SyncHandler::on_peer_status(sync, io, peer, &rlp), StatusPacket => SyncHandler::on_peer_status(sync, io, peer, &rlp),
BlockHeadersPacket => SyncHandler::on_peer_block_headers(sync, io, peer, &rlp), BlockHeadersPacket => SyncHandler::on_peer_block_headers(sync, io, peer, &rlp),
BlockBodiesPacket => SyncHandler::on_peer_block_bodies(sync, io, peer, &rlp), BlockBodiesPacket => SyncHandler::on_peer_block_bodies(sync, io, peer, &rlp),
@ -70,12 +75,16 @@ impl SyncHandler {
PooledTransactionsPacket => { PooledTransactionsPacket => {
SyncHandler::on_peer_pooled_transactions(sync, io, peer, &rlp) SyncHandler::on_peer_pooled_transactions(sync, io, peer, &rlp)
} }
SnapshotManifestPacket => SyncHandler::on_snapshot_manifest(sync, io, peer, &rlp), SnapshotManifestPacket => {
SyncHandler::on_snapshot_manifest(sync, io, peer, &rlp)
}
SnapshotDataPacket => SyncHandler::on_snapshot_data(sync, io, peer, &rlp), SnapshotDataPacket => SyncHandler::on_snapshot_data(sync, io, peer, &rlp),
_ => { _ => {
debug!(target: "sync", "{}: Unknown packet {}", peer, packet_id.id()); debug!(target: "sync", "{}: Unknown packet {}", peer, packet_id.id());
Ok(()) Ok(())
} }
},
Err(e) => Err(e.into()),
}; };
match result { match result {
@ -797,7 +806,7 @@ impl SyncHandler {
|| peer.protocol_version > PAR_PROTOCOL_VERSION_2.0)) || peer.protocol_version > PAR_PROTOCOL_VERSION_2.0))
|| (!warp_protocol || (!warp_protocol
&& (peer.protocol_version < ETH_PROTOCOL_VERSION_63.0 && (peer.protocol_version < ETH_PROTOCOL_VERSION_63.0
|| peer.protocol_version > ETH_PROTOCOL_VERSION_65.0)) || peer.protocol_version > ETH_PROTOCOL_VERSION_66.0))
{ {
trace!(target: "sync", "Peer {} unsupported eth protocol ({})", peer_id, peer.protocol_version); trace!(target: "sync", "Peer {} unsupported eth protocol ({})", peer_id, peer.protocol_version);
return Err(DownloaderImportError::Invalid); return Err(DownloaderImportError::Invalid);

View File

@ -90,6 +90,7 @@
pub mod fork_filter; pub mod fork_filter;
mod handler; mod handler;
mod propagator; mod propagator;
pub mod request_id;
mod requester; mod requester;
mod supplier; mod supplier;
pub mod sync_packet; pub mod sync_packet;
@ -153,6 +154,8 @@ impl From<DecoderError> for PacketProcessError {
} }
} }
/// Version 66 of the Ethereum protocol and number of packet IDs reserved by the protocol (packet count).
pub const ETH_PROTOCOL_VERSION_66: (u8, u8) = (66, 0x11);
/// Version 65 of the Ethereum protocol and number of packet IDs reserved by the protocol (packet count). /// Version 65 of the Ethereum protocol and number of packet IDs reserved by the protocol (packet count).
pub const ETH_PROTOCOL_VERSION_65: (u8, u8) = (65, 0x11); pub const ETH_PROTOCOL_VERSION_65: (u8, u8) = (65, 0x11);
/// 64 version of Ethereum protocol. /// 64 version of Ethereum protocol.

View File

@ -0,0 +1,148 @@
use bytes::Bytes;
use chain::{
sync_packet::{PacketInfo, SyncPacket},
ChainSync, PeerInfo,
};
use network::PeerId;
use rlp::{DecoderError, Rlp, RlpStream};
pub type RequestId = u64;
// Separate the eth/66 request id from a packet, if it exists.
pub fn strip_request_id<'a>(
data: &'a [u8],
sync: &ChainSync,
peer: &PeerId,
packet_id: &SyncPacket,
) -> Result<(Rlp<'a>, Option<RequestId>), DecoderError> {
let protocol_version = if let Some(peer_info) = sync.peers.get(peer) {
peer_info.protocol_version
} else {
trace!(
"Peer info missing for peer {}, assuming protocol version 66",
peer
);
66
};
let has_request_id = protocol_version >= 66 && packet_id.has_request_id_in_eth_66();
do_strip_request_id(data, has_request_id)
}
fn do_strip_request_id<'a>(
data: &'a [u8],
has_request_id: bool,
) -> Result<(Rlp<'a>, Option<RequestId>), DecoderError> {
let rlp = Rlp::new(data);
if has_request_id {
let request_id: RequestId = rlp.val_at(0)?;
let stripped_rlp = rlp.at(1)?;
Ok((stripped_rlp, Some(request_id)))
} else {
Ok((rlp, None))
}
}
// Add a given eth/66 request id to a packet being built.
pub fn prepend_request_id(rlp: RlpStream, request_id: Option<RequestId>) -> RlpStream {
match request_id {
Some(ref id) => {
let mut stream = RlpStream::new_list(2);
stream.append(id);
stream.append_raw(&rlp.out(), 1);
stream
}
None => rlp,
}
}
/// Prepend a new eth/66 request id to the packet if appropriate.
pub fn generate_request_id(
packet: Bytes,
peer: &PeerInfo,
packet_id: SyncPacket,
) -> (Bytes, Option<RequestId>) {
if peer.protocol_version >= 66 && packet_id.has_request_id_in_eth_66() {
do_generate_request_id(&packet)
} else {
(packet, None)
}
}
fn do_generate_request_id(packet: &Bytes) -> (Bytes, Option<RequestId>) {
let request_id: RequestId = rand::random();
let mut rlp = RlpStream::new_list(2);
rlp.append(&request_id);
rlp.append_raw(packet, 1);
(rlp.out(), Some(request_id))
}
#[cfg(test)]
mod tests {
use super::*;
use ethereum_types::H256;
#[test]
fn test_prepend_request_id() {
let mut request = RlpStream::new_list(2);
request.append(&H256::from_low_u64_be(1));
request.append(&H256::from_low_u64_be(2));
let with_id = prepend_request_id(request, Some(10));
let rlp = Rlp::new(with_id.as_raw());
let recovered_id: RequestId = rlp.val_at(0).unwrap();
let recovered_request: Vec<H256> = rlp.at(1).unwrap().as_list().unwrap();
assert_eq!(recovered_id, 10);
assert_eq!(
recovered_request,
[H256::from_low_u64_be(1), H256::from_low_u64_be(2)]
);
}
#[test]
fn test_strip_request_id() {
let request = vec![
H256::from_low_u64_be(1),
H256::from_low_u64_be(2),
H256::from_low_u64_be(3),
];
let mut request_with_id = RlpStream::new_list(2);
request_with_id.append(&20u64);
request_with_id.append_list(&request);
let data = request_with_id.out();
let (rlp, id) = do_strip_request_id(&data, true).unwrap();
assert_eq!(id, Some(20));
assert_eq!(rlp.as_list::<H256>().unwrap(), request);
}
#[test]
fn test_generate_request_id() {
let request = vec![
H256::from_low_u64_be(1),
H256::from_low_u64_be(2),
H256::from_low_u64_be(3),
];
let mut stream = RlpStream::new_list(3);
for hash in &request {
stream.append(hash);
}
let data = stream.out();
let (new_data, id) = do_generate_request_id(&data);
let recovered = Rlp::new(&new_data);
let recovered_id: RequestId = recovered.val_at(0).unwrap();
let recovered_request: Vec<H256> = recovered.at(1).unwrap().as_list().unwrap();
assert_eq!(recovered_id, id.unwrap());
assert_eq!(recovered_request, request);
}
}

View File

@ -23,7 +23,10 @@ use std::time::Instant;
use sync_io::SyncIo; use sync_io::SyncIo;
use types::BlockNumber; use types::BlockNumber;
use super::sync_packet::{SyncPacket::*, *}; use super::{
request_id::generate_request_id,
sync_packet::{SyncPacket::*, *},
};
use super::{BlockSet, ChainSync, PeerAsking}; use super::{BlockSet, ChainSync, PeerAsking};
@ -243,6 +246,8 @@ impl SyncRequester {
peer.asking = asking; peer.asking = asking;
peer.ask_time = Instant::now(); peer.ask_time = Instant::now();
let (packet, _) = generate_request_id(packet, peer, packet_id);
let result = io.send(peer_id, packet_id, packet); let result = io.send(peer_id, packet_id, packet);
if let Err(e) = result { if let Err(e) = result {

View File

@ -31,12 +31,16 @@ use types::{ids::BlockId, BlockNumber};
use sync_io::SyncIo; use sync_io::SyncIo;
use super::sync_packet::{PacketInfo, SyncPacket, SyncPacket::*}; use super::{
request_id::{prepend_request_id, strip_request_id, RequestId},
sync_packet::{PacketInfo, SyncPacket, SyncPacket::*},
};
use super::{ use super::{
ChainSync, PacketProcessError, RlpResponseResult, SyncHandler, MAX_BODIES_TO_SEND, ChainSync, PacketProcessError, RlpResponseResult, SyncHandler, MAX_BODIES_TO_SEND,
MAX_HEADERS_TO_SEND, MAX_RECEIPTS_HEADERS_TO_SEND, MAX_HEADERS_TO_SEND, MAX_RECEIPTS_HEADERS_TO_SEND,
}; };
use std::borrow::Borrow;
/// The Chain Sync Supplier: answers requests from peers with available data /// The Chain Sync Supplier: answers requests from peers with available data
pub struct SyncSupplier; pub struct SyncSupplier;
@ -52,14 +56,16 @@ impl SyncSupplier {
packet_id: u8, packet_id: u8,
data: &[u8], data: &[u8],
) { ) {
let rlp = Rlp::new(data);
if let Some(id) = SyncPacket::from_u8(packet_id) { if let Some(id) = SyncPacket::from_u8(packet_id) {
let result = match id { let rlp_result = strip_request_id(data, sync.read().borrow(), &peer, &id);
let result = match rlp_result {
Ok((rlp, request_id)) => match id {
GetPooledTransactionsPacket => SyncSupplier::return_rlp( GetPooledTransactionsPacket => SyncSupplier::return_rlp(
io, io,
&rlp, &rlp,
peer, peer,
request_id,
SyncSupplier::return_pooled_transactions, SyncSupplier::return_pooled_transactions,
|e| format!("Error sending pooled transactions: {:?}", e), |e| format!("Error sending pooled transactions: {:?}", e),
), ),
@ -68,6 +74,7 @@ impl SyncSupplier {
io, io,
&rlp, &rlp,
peer, peer,
request_id,
SyncSupplier::return_block_bodies, SyncSupplier::return_block_bodies,
|e| format!("Error sending block bodies: {:?}", e), |e| format!("Error sending block bodies: {:?}", e),
), ),
@ -76,19 +83,25 @@ impl SyncSupplier {
io, io,
&rlp, &rlp,
peer, peer,
request_id,
SyncSupplier::return_block_headers, SyncSupplier::return_block_headers,
|e| format!("Error sending block headers: {:?}", e), |e| format!("Error sending block headers: {:?}", e),
), ),
GetReceiptsPacket => { GetReceiptsPacket => SyncSupplier::return_rlp(
SyncSupplier::return_rlp(io, &rlp, peer, SyncSupplier::return_receipts, |e| { io,
format!("Error sending receipts: {:?}", e) &rlp,
}) peer,
} request_id,
SyncSupplier::return_receipts,
|e| format!("Error sending receipts: {:?}", e),
),
GetSnapshotManifestPacket => SyncSupplier::return_rlp( GetSnapshotManifestPacket => SyncSupplier::return_rlp(
io, io,
&rlp, &rlp,
peer, peer,
request_id,
SyncSupplier::return_snapshot_manifest, SyncSupplier::return_snapshot_manifest,
|e| format!("Error sending snapshot manifest: {:?}", e), |e| format!("Error sending snapshot manifest: {:?}", e),
), ),
@ -97,6 +110,7 @@ impl SyncSupplier {
io, io,
&rlp, &rlp,
peer, peer,
request_id,
SyncSupplier::return_snapshot_data, SyncSupplier::return_snapshot_data,
|e| format!("Error sending snapshot data: {:?}", e), |e| format!("Error sending snapshot data: {:?}", e),
), ),
@ -130,9 +144,10 @@ impl SyncSupplier {
sync.write().on_packet(io, peer, packet_id, data); sync.write().on_packet(io, peer, packet_id, data);
} }
} }
Ok(()) Ok(())
} }
},
Err(e) => Err(e.into()),
}; };
match result { match result {
@ -156,14 +171,16 @@ impl SyncSupplier {
packet_id: u8, packet_id: u8,
data: &[u8], data: &[u8],
) { ) {
let rlp = Rlp::new(data);
if let Some(id) = SyncPacket::from_u8(packet_id) { if let Some(id) = SyncPacket::from_u8(packet_id) {
let result = match id { let rlp_result = strip_request_id(data, sync.read().borrow(), &peer, &id);
let result = match rlp_result {
Ok((rlp, request_id)) => match id {
GetBlockHeadersPacket => SyncSupplier::send_rlp( GetBlockHeadersPacket => SyncSupplier::send_rlp(
io, io,
&rlp, &rlp,
peer, peer,
request_id,
SyncSupplier::return_block_headers, SyncSupplier::return_block_headers,
|e| format!("Error sending block headers: {:?}", e), |e| format!("Error sending block headers: {:?}", e),
), ),
@ -172,6 +189,8 @@ impl SyncSupplier {
debug!(target: "sync", "Unexpected packet {} was dispatched for delayed processing", packet_id); debug!(target: "sync", "Unexpected packet {} was dispatched for delayed processing", packet_id);
Ok(()) Ok(())
} }
},
Err(e) => Err(e.into()),
}; };
match result { match result {
@ -394,6 +413,7 @@ impl SyncSupplier {
io: &mut dyn SyncIo, io: &mut dyn SyncIo,
rlp: &Rlp, rlp: &Rlp,
peer: PeerId, peer: PeerId,
request_id: Option<RequestId>,
rlp_func: FRlp, rlp_func: FRlp,
error_func: FError, error_func: FError,
) -> Result<(), PacketProcessError> ) -> Result<(), PacketProcessError>
@ -403,6 +423,7 @@ impl SyncSupplier {
{ {
let response = rlp_func(io, rlp, peer); let response = rlp_func(io, rlp, peer);
if let Some((packet_id, rlp_stream)) = response? { if let Some((packet_id, rlp_stream)) = response? {
let rlp_stream = prepend_request_id(rlp_stream, request_id);
io.respond(packet_id.id(), rlp_stream.out()) io.respond(packet_id.id(), rlp_stream.out())
.unwrap_or_else(|e| debug!(target: "sync", "{:?}", error_func(e))); .unwrap_or_else(|e| debug!(target: "sync", "{:?}", error_func(e)));
} }
@ -413,6 +434,7 @@ impl SyncSupplier {
io: &mut dyn SyncIo, io: &mut dyn SyncIo,
rlp: &Rlp, rlp: &Rlp,
peer: PeerId, peer: PeerId,
request_id: Option<RequestId>,
rlp_func: FRlp, rlp_func: FRlp,
error_func: FError, error_func: FError,
) -> Result<(), PacketProcessError> ) -> Result<(), PacketProcessError>
@ -424,6 +446,7 @@ impl SyncSupplier {
match response { match response {
Err(e) => Err(e), Err(e) => Err(e),
Ok(Some((packet_id, rlp_stream))) => { Ok(Some((packet_id, rlp_stream))) => {
let rlp_stream = prepend_request_id(rlp_stream, request_id);
io.send(peer, packet_id, rlp_stream.out()) io.send(peer, packet_id, rlp_stream.out())
.unwrap_or_else(|e| debug!(target: "sync", "{:?}", error_func(e))); .unwrap_or_else(|e| debug!(target: "sync", "{:?}", error_func(e)));
Ok(()) Ok(())

View File

@ -68,6 +68,7 @@ use self::SyncPacket::*;
pub trait PacketInfo { pub trait PacketInfo {
fn id(&self) -> PacketId; fn id(&self) -> PacketId;
fn protocol(&self) -> ProtocolId; fn protocol(&self) -> ProtocolId;
fn has_request_id_in_eth_66(&self) -> bool;
} }
// The mechanism to match packet ids and protocol may be improved // The mechanism to match packet ids and protocol may be improved
@ -102,6 +103,21 @@ impl PacketInfo for SyncPacket {
fn id(&self) -> PacketId { fn id(&self) -> PacketId {
(*self) as PacketId (*self) as PacketId
} }
fn has_request_id_in_eth_66(&self) -> bool {
// Note: NodeDataPacket and GetNodeDataPacket also get a request id in eth-66.
match self {
GetBlockHeadersPacket
| BlockHeadersPacket
| GetBlockBodiesPacket
| BlockBodiesPacket
| GetPooledTransactionsPacket
| PooledTransactionsPacket
| GetReceiptsPacket
| ReceiptsPacket => true,
_ => false,
}
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -18,7 +18,7 @@ use api::PAR_PROTOCOL;
use bytes::Bytes; use bytes::Bytes;
use chain::{ use chain::{
sync_packet::{PacketInfo, SyncPacket}, sync_packet::{PacketInfo, SyncPacket},
ChainSync, ForkFilterApi, SyncSupplier, ETH_PROTOCOL_VERSION_65, PAR_PROTOCOL_VERSION_2, ChainSync, ForkFilterApi, SyncSupplier, ETH_PROTOCOL_VERSION_66, PAR_PROTOCOL_VERSION_2,
}; };
use ethcore::{ use ethcore::{
client::{ client::{
@ -172,7 +172,7 @@ where
if protocol == PAR_PROTOCOL { if protocol == PAR_PROTOCOL {
PAR_PROTOCOL_VERSION_2.0 PAR_PROTOCOL_VERSION_2.0
} else { } else {
ETH_PROTOCOL_VERSION_65.0 ETH_PROTOCOL_VERSION_66.0
} }
} }