mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
Refactor p2p reader (#3433)
* Refactor p2p reader * Rename Output enum * Consume enum takes owned values instead of references * Deserialization in codec, remove Consume enum * Calculate block header size * Read headers in batches * Remove headers type from deserializer
This commit is contained in:
parent
4944679fb2
commit
defc714f6e
12 changed files with 701 additions and 341 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1027,6 +1027,7 @@ version = "4.2.0-alpha.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2-rfc",
|
"blake2-rfc",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
"croaring-mw",
|
"croaring-mw",
|
||||||
"enum_primitive",
|
"enum_primitive",
|
||||||
|
@ -1073,6 +1074,7 @@ name = "grin_p2p"
|
||||||
version = "4.2.0-alpha.1"
|
version = "4.2.0-alpha.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.2.1",
|
||||||
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
"enum_primitive",
|
"enum_primitive",
|
||||||
"grin_chain",
|
"grin_chain",
|
||||||
|
|
|
@ -27,6 +27,7 @@ siphasher = "0.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
chrono = { version = "0.4.11", features = ["serde"] }
|
chrono = { version = "0.4.11", features = ["serde"] }
|
||||||
zeroize = { version = "1.1", features =["zeroize_derive"] }
|
zeroize = { version = "1.1", features =["zeroize_derive"] }
|
||||||
|
bytes = "0.5"
|
||||||
|
|
||||||
keychain = { package = "grin_keychain", path = "../keychain", version = "4.2.0-alpha.1" }
|
keychain = { package = "grin_keychain", path = "../keychain", version = "4.2.0-alpha.1" }
|
||||||
util = { package = "grin_util", path = "../util", version = "4.2.0-alpha.1" }
|
util = { package = "grin_util", path = "../util", version = "4.2.0-alpha.1" }
|
||||||
|
|
|
@ -26,7 +26,7 @@ use crate::core::block::HeaderVersion;
|
||||||
use crate::{
|
use crate::{
|
||||||
pow::{
|
pow::{
|
||||||
self, new_cuckaroo_ctx, new_cuckarood_ctx, new_cuckaroom_ctx, new_cuckarooz_ctx,
|
self, new_cuckaroo_ctx, new_cuckarood_ctx, new_cuckaroom_ctx, new_cuckarooz_ctx,
|
||||||
new_cuckatoo_ctx, PoWContext,
|
new_cuckatoo_ctx, BitVec, PoWContext,
|
||||||
},
|
},
|
||||||
ser::ProtocolVersion,
|
ser::ProtocolVersion,
|
||||||
};
|
};
|
||||||
|
@ -411,3 +411,51 @@ where
|
||||||
last_n.reverse();
|
last_n.reverse();
|
||||||
last_n
|
last_n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculates the size of a header (in bytes) given a number of edge bits in the PoW
|
||||||
|
#[inline]
|
||||||
|
pub fn header_size_bytes(edge_bits: u8) -> usize {
|
||||||
|
let size = 2 + 2 * 8 + 5 * 32 + 32 + 2 * 8;
|
||||||
|
let proof_size = 8 + 4 + 8 + 1 + BitVec::bytes_len(edge_bits as usize * proofsize());
|
||||||
|
size + proof_size
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::core::Block;
|
||||||
|
use crate::genesis::*;
|
||||||
|
use crate::pow::mine_genesis_block;
|
||||||
|
use crate::ser::{BinWriter, Writeable};
|
||||||
|
|
||||||
|
fn test_header_len(genesis: Block) {
|
||||||
|
let mut raw = Vec::<u8>::with_capacity(1_024);
|
||||||
|
let mut writer = BinWriter::new(&mut raw, ProtocolVersion::local());
|
||||||
|
genesis.header.write(&mut writer).unwrap();
|
||||||
|
assert_eq!(raw.len(), header_size_bytes(genesis.header.pow.edge_bits()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn automated_testing_header_len() {
|
||||||
|
set_local_chain_type(ChainTypes::AutomatedTesting);
|
||||||
|
test_header_len(mine_genesis_block().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn user_testing_header_len() {
|
||||||
|
set_local_chain_type(ChainTypes::UserTesting);
|
||||||
|
test_header_len(mine_genesis_block().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn floonet_header_len() {
|
||||||
|
set_local_chain_type(ChainTypes::Floonet);
|
||||||
|
test_header_len(genesis_floo());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mainnet_header_len() {
|
||||||
|
set_local_chain_type(ChainTypes::Mainnet);
|
||||||
|
test_header_len(genesis_main());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -471,15 +471,17 @@ impl Writeable for Proof {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A bit vector
|
||||||
// TODO this could likely be optimized by writing whole bytes (or even words)
|
// TODO this could likely be optimized by writing whole bytes (or even words)
|
||||||
// in the `BitVec` at once, dealing with the truncation, instead of bits by bits
|
// in the `BitVec` at once, dealing with the truncation, instead of bits by bits
|
||||||
struct BitVec {
|
pub struct BitVec {
|
||||||
bits: Vec<u8>,
|
bits: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitVec {
|
impl BitVec {
|
||||||
/// Number of bytes required to store the provided number of bits
|
/// Number of bytes required to store the provided number of bits
|
||||||
fn bytes_len(bits_len: usize) -> usize {
|
#[inline]
|
||||||
|
pub fn bytes_len(bits_len: usize) -> usize {
|
||||||
(bits_len + 7) / 8
|
(bits_len + 7) / 8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
110
core/src/ser.rs
110
core/src/ser.rs
|
@ -22,6 +22,7 @@
|
||||||
use crate::core::hash::{DefaultHashable, Hash, Hashed};
|
use crate::core::hash::{DefaultHashable, Hash, Hashed};
|
||||||
use crate::global::PROTOCOL_VERSION;
|
use crate::global::PROTOCOL_VERSION;
|
||||||
use byteorder::{BigEndian, ByteOrder, ReadBytesExt};
|
use byteorder::{BigEndian, ByteOrder, ReadBytesExt};
|
||||||
|
use bytes::Buf;
|
||||||
use keychain::{BlindingFactor, Identifier, IDENTIFIER_SIZE};
|
use keychain::{BlindingFactor, Identifier, IDENTIFIER_SIZE};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
|
@ -79,6 +80,12 @@ impl From<io::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<io::ErrorKind> for Error {
|
||||||
|
fn from(e: io::ErrorKind) -> Error {
|
||||||
|
Error::IOErr(format!("{}", io::Error::from(e)), e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -575,6 +582,109 @@ impl<'a> Reader for StreamingReader<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Protocol version-aware wrapper around a `Buf` impl
|
||||||
|
pub struct BufReader<'a, B: Buf> {
|
||||||
|
inner: &'a mut B,
|
||||||
|
version: ProtocolVersion,
|
||||||
|
bytes_read: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, B: Buf> BufReader<'a, B> {
|
||||||
|
/// Construct a new BufReader
|
||||||
|
pub fn new(buf: &'a mut B, version: ProtocolVersion) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: buf,
|
||||||
|
version,
|
||||||
|
bytes_read: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether the buffer has enough bytes remaining to perform a read
|
||||||
|
fn has_remaining(&mut self, len: usize) -> Result<(), Error> {
|
||||||
|
if self.inner.remaining() >= len {
|
||||||
|
self.bytes_read += len;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(io::ErrorKind::UnexpectedEof.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The total bytes read
|
||||||
|
pub fn bytes_read(&self) -> u64 {
|
||||||
|
self.bytes_read as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience function to read from the buffer and deserialize
|
||||||
|
pub fn body<T: Readable>(&mut self) -> Result<T, Error> {
|
||||||
|
T::read(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, B: Buf> Reader for BufReader<'a, B> {
|
||||||
|
fn read_u8(&mut self) -> Result<u8, Error> {
|
||||||
|
self.has_remaining(1)?;
|
||||||
|
Ok(self.inner.get_u8())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u16(&mut self) -> Result<u16, Error> {
|
||||||
|
self.has_remaining(2)?;
|
||||||
|
Ok(self.inner.get_u16())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u32(&mut self) -> Result<u32, Error> {
|
||||||
|
self.has_remaining(4)?;
|
||||||
|
Ok(self.inner.get_u32())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u64(&mut self) -> Result<u64, Error> {
|
||||||
|
self.has_remaining(8)?;
|
||||||
|
Ok(self.inner.get_u64())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_i32(&mut self) -> Result<i32, Error> {
|
||||||
|
self.has_remaining(4)?;
|
||||||
|
Ok(self.inner.get_i32())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_i64(&mut self) -> Result<i64, Error> {
|
||||||
|
self.has_remaining(8)?;
|
||||||
|
Ok(self.inner.get_i64())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_bytes_len_prefix(&mut self) -> Result<Vec<u8>, Error> {
|
||||||
|
let len = self.read_u64()?;
|
||||||
|
self.read_fixed_bytes(len as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_fixed_bytes(&mut self, len: usize) -> Result<Vec<u8>, Error> {
|
||||||
|
// not reading more than 100k bytes in a single read
|
||||||
|
if len > 100_000 {
|
||||||
|
return Err(Error::TooLargeReadErr);
|
||||||
|
}
|
||||||
|
self.has_remaining(len)?;
|
||||||
|
|
||||||
|
let mut buf = vec![0; len];
|
||||||
|
self.inner.copy_to_slice(&mut buf[..]);
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_u8(&mut self, val: u8) -> Result<u8, Error> {
|
||||||
|
let b = self.read_u8()?;
|
||||||
|
if b == val {
|
||||||
|
Ok(b)
|
||||||
|
} else {
|
||||||
|
Err(Error::UnexpectedData {
|
||||||
|
expected: vec![val],
|
||||||
|
received: vec![b],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn protocol_version(&self) -> ProtocolVersion {
|
||||||
|
self.version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Readable for Commitment {
|
impl Readable for Commitment {
|
||||||
fn read<R: Reader>(reader: &mut R) -> Result<Commitment, Error> {
|
fn read<R: Reader>(reader: &mut R) -> Result<Commitment, Error> {
|
||||||
let a = reader.read_fixed_bytes(PEDERSEN_COMMITMENT_SIZE)?;
|
let a = reader.read_fixed_bytes(PEDERSEN_COMMITMENT_SIZE)?;
|
||||||
|
|
|
@ -20,6 +20,7 @@ serde_derive = "1"
|
||||||
tempfile = "3.1"
|
tempfile = "3.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
chrono = { version = "0.4.11", features = ["serde"] }
|
chrono = { version = "0.4.11", features = ["serde"] }
|
||||||
|
bytes = "0.5"
|
||||||
|
|
||||||
grin_core = { path = "../core", version = "4.2.0-alpha.1" }
|
grin_core = { path = "../core", version = "4.2.0-alpha.1" }
|
||||||
grin_store = { path = "../store", version = "4.2.0-alpha.1" }
|
grin_store = { path = "../store", version = "4.2.0-alpha.1" }
|
||||||
|
|
245
p2p/src/codec.rs
Normal file
245
p2p/src/codec.rs
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
use crate::core::core::block::{BlockHeader, UntrustedBlockHeader};
|
||||||
|
use crate::core::global::header_size_bytes;
|
||||||
|
use crate::core::ser::{BufReader, ProtocolVersion, Readable};
|
||||||
|
use crate::msg::{Message, MsgHeader, MsgHeaderWrapper, Type};
|
||||||
|
use crate::types::{AttachmentMeta, AttachmentUpdate, Error};
|
||||||
|
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||||
|
use core::ser::Reader;
|
||||||
|
use std::cmp::min;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::mem;
|
||||||
|
use std::net::TcpStream;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
use MsgHeaderWrapper::*;
|
||||||
|
use State::*;
|
||||||
|
|
||||||
|
const HEADER_IO_TIMEOUT: Duration = Duration::from_millis(2000);
|
||||||
|
pub const BODY_IO_TIMEOUT: Duration = Duration::from_millis(60000);
|
||||||
|
const HEADER_BATCH_SIZE: usize = 32;
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
None,
|
||||||
|
Header(MsgHeaderWrapper),
|
||||||
|
BlockHeaders {
|
||||||
|
bytes_left: usize,
|
||||||
|
items_left: usize,
|
||||||
|
headers: Vec<BlockHeader>,
|
||||||
|
},
|
||||||
|
Attachment(usize, Arc<AttachmentMeta>, Instant),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn is_none(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
State::None => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Codec {
|
||||||
|
pub version: ProtocolVersion,
|
||||||
|
stream: TcpStream,
|
||||||
|
buffer: BytesMut,
|
||||||
|
state: State,
|
||||||
|
bytes_read: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Codec {
|
||||||
|
pub fn new(version: ProtocolVersion, stream: TcpStream) -> Self {
|
||||||
|
Self {
|
||||||
|
version,
|
||||||
|
stream,
|
||||||
|
buffer: BytesMut::with_capacity(8 * 1024),
|
||||||
|
state: None,
|
||||||
|
bytes_read: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destroy the codec and return the reader
|
||||||
|
pub fn stream(self) -> TcpStream {
|
||||||
|
self.stream
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inform codec next `len` bytes are an attachment
|
||||||
|
/// Panics if already reading a body
|
||||||
|
pub fn expect_attachment(&mut self, meta: Arc<AttachmentMeta>) {
|
||||||
|
assert!(self.state.is_none());
|
||||||
|
self.state = Attachment(meta.size, meta, Instant::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Length of the next item we are expecting, could be msg header, body, block header or attachment chunk
|
||||||
|
fn next_len(&self) -> usize {
|
||||||
|
match &self.state {
|
||||||
|
None => MsgHeader::LEN,
|
||||||
|
Header(Known(h)) if h.msg_type == Type::Headers => {
|
||||||
|
// If we are receiving a list of headers, read off the item count first
|
||||||
|
min(h.msg_len as usize, 2)
|
||||||
|
}
|
||||||
|
Header(Known(header)) => header.msg_len as usize,
|
||||||
|
Header(Unknown(len, _)) => *len as usize,
|
||||||
|
BlockHeaders { bytes_left, .. } => {
|
||||||
|
// The header length varies with the number of edge bits. Therefore we overestimate
|
||||||
|
// its size and only actually read the bytes we need
|
||||||
|
min(*bytes_left, header_size_bytes(63))
|
||||||
|
}
|
||||||
|
Attachment(left, _, _) => min(*left, 48_000),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set stream timeout depending on the next expected item
|
||||||
|
fn set_stream_timeout(&self) -> Result<(), Error> {
|
||||||
|
let timeout = match &self.state {
|
||||||
|
None => HEADER_IO_TIMEOUT,
|
||||||
|
_ => BODY_IO_TIMEOUT,
|
||||||
|
};
|
||||||
|
self.stream.set_read_timeout(Some(timeout))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_inner(&mut self) -> Result<Message, Error> {
|
||||||
|
self.bytes_read = 0;
|
||||||
|
loop {
|
||||||
|
let next_len = self.next_len();
|
||||||
|
let pre_len = self.buffer.len();
|
||||||
|
// Buffer could already be partially filled, calculate additional bytes we need
|
||||||
|
let to_read = next_len.saturating_sub(pre_len);
|
||||||
|
if to_read > 0 {
|
||||||
|
self.buffer.reserve(to_read);
|
||||||
|
for _ in 0..to_read {
|
||||||
|
self.buffer.put_u8(0);
|
||||||
|
}
|
||||||
|
self.set_stream_timeout()?;
|
||||||
|
if let Err(e) = self.stream.read_exact(&mut self.buffer[pre_len..]) {
|
||||||
|
// Undo reserved bytes on a failed read
|
||||||
|
self.buffer.truncate(pre_len);
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
self.bytes_read += to_read;
|
||||||
|
}
|
||||||
|
match &mut self.state {
|
||||||
|
None => {
|
||||||
|
// Parse header and keep reading
|
||||||
|
let mut raw = self.buffer.split_to(next_len).freeze();
|
||||||
|
let mut reader = BufReader::new(&mut raw, self.version);
|
||||||
|
let header = MsgHeaderWrapper::read(&mut reader)?;
|
||||||
|
self.state = Header(header);
|
||||||
|
}
|
||||||
|
Header(Known(header)) => {
|
||||||
|
let mut raw = self.buffer.split_to(next_len).freeze();
|
||||||
|
if header.msg_type == Type::Headers {
|
||||||
|
// Special consideration for a list of headers, as we want to verify and process
|
||||||
|
// them as they come in instead of only after the full list has been received
|
||||||
|
let mut reader = BufReader::new(&mut raw, self.version);
|
||||||
|
let items_left = reader.read_u16()? as usize;
|
||||||
|
self.state = BlockHeaders {
|
||||||
|
bytes_left: header.msg_len as usize - 2,
|
||||||
|
items_left,
|
||||||
|
headers: Vec::with_capacity(min(HEADER_BATCH_SIZE, items_left)),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Return full message
|
||||||
|
let msg = decode_message(header, &mut raw, self.version);
|
||||||
|
self.state = None;
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Header(Unknown(_, msg_type)) => {
|
||||||
|
// Discard body and return
|
||||||
|
let msg_type = *msg_type;
|
||||||
|
self.buffer.advance(next_len);
|
||||||
|
self.state = None;
|
||||||
|
return Ok(Message::Unknown(msg_type));
|
||||||
|
}
|
||||||
|
BlockHeaders {
|
||||||
|
bytes_left,
|
||||||
|
items_left,
|
||||||
|
headers,
|
||||||
|
} => {
|
||||||
|
if *bytes_left == 0 {
|
||||||
|
// Incorrect item count
|
||||||
|
self.state = None;
|
||||||
|
return Err(Error::BadMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reader = BufReader::new(&mut self.buffer, self.version);
|
||||||
|
let header: UntrustedBlockHeader = reader.body()?;
|
||||||
|
let bytes_read = reader.bytes_read() as usize;
|
||||||
|
headers.push(header.into());
|
||||||
|
*bytes_left = bytes_left.saturating_sub(bytes_read);
|
||||||
|
*items_left -= 1;
|
||||||
|
|
||||||
|
if headers.len() == HEADER_BATCH_SIZE || *items_left == 0 {
|
||||||
|
let mut h = Vec::with_capacity(min(HEADER_BATCH_SIZE, *items_left));
|
||||||
|
mem::swap(headers, &mut h);
|
||||||
|
if *items_left == 0 {
|
||||||
|
let bytes_left = *bytes_left;
|
||||||
|
self.state = None;
|
||||||
|
if bytes_left > 0 {
|
||||||
|
return Err(Error::BadMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(Message::Headers(h));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Attachment(left, meta, now) => {
|
||||||
|
let raw = self.buffer.split_to(next_len).freeze();
|
||||||
|
*left -= next_len;
|
||||||
|
if now.elapsed().as_secs() > 10 {
|
||||||
|
*now = Instant::now();
|
||||||
|
debug!("attachment: {}/{}", meta.size - *left, meta.size);
|
||||||
|
}
|
||||||
|
let update = AttachmentUpdate {
|
||||||
|
read: next_len,
|
||||||
|
left: *left,
|
||||||
|
meta: Arc::clone(meta),
|
||||||
|
};
|
||||||
|
if *left == 0 {
|
||||||
|
self.state = None;
|
||||||
|
debug!("attachment: DONE");
|
||||||
|
}
|
||||||
|
return Ok(Message::Attachment(update, Some(raw)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Blocking read of the next message
|
||||||
|
pub fn read(&mut self) -> (Result<Message, Error>, u64) {
|
||||||
|
let msg = self.read_inner();
|
||||||
|
(msg, self.bytes_read as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: replace with a macro?
|
||||||
|
fn decode_message(
|
||||||
|
header: &MsgHeader,
|
||||||
|
body: &mut Bytes,
|
||||||
|
version: ProtocolVersion,
|
||||||
|
) -> Result<Message, Error> {
|
||||||
|
let mut msg = BufReader::new(body, version);
|
||||||
|
let c = match header.msg_type {
|
||||||
|
Type::Ping => Message::Ping(msg.body()?),
|
||||||
|
Type::Pong => Message::Pong(msg.body()?),
|
||||||
|
Type::BanReason => Message::BanReason(msg.body()?),
|
||||||
|
Type::TransactionKernel => Message::TransactionKernel(msg.body()?),
|
||||||
|
Type::GetTransaction => Message::GetTransaction(msg.body()?),
|
||||||
|
Type::Transaction => Message::Transaction(msg.body()?),
|
||||||
|
Type::StemTransaction => Message::StemTransaction(msg.body()?),
|
||||||
|
Type::GetBlock => Message::GetBlock(msg.body()?),
|
||||||
|
Type::Block => Message::Block(msg.body()?),
|
||||||
|
Type::GetCompactBlock => Message::GetCompactBlock(msg.body()?),
|
||||||
|
Type::CompactBlock => Message::CompactBlock(msg.body()?),
|
||||||
|
Type::GetHeaders => Message::GetHeaders(msg.body()?),
|
||||||
|
Type::Header => Message::Header(msg.body()?),
|
||||||
|
Type::GetPeerAddrs => Message::GetPeerAddrs(msg.body()?),
|
||||||
|
Type::PeerAddrs => Message::PeerAddrs(msg.body()?),
|
||||||
|
Type::TxHashSetRequest => Message::TxHashSetRequest(msg.body()?),
|
||||||
|
Type::TxHashSetArchive => Message::TxHashSetArchive(msg.body()?),
|
||||||
|
Type::Error | Type::Hand | Type::Shake | Type::Headers => {
|
||||||
|
return Err(Error::UnexpectedMessage)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(c)
|
||||||
|
}
|
189
p2p/src/conn.rs
189
p2p/src/conn.rs
|
@ -20,40 +20,28 @@
|
||||||
//! forces us to go through some additional gymnastic to loop over the async
|
//! forces us to go through some additional gymnastic to loop over the async
|
||||||
//! stream and make sure we get the right number of bytes out.
|
//! stream and make sure we get the right number of bytes out.
|
||||||
|
|
||||||
use crate::core::ser;
|
use crate::codec::{Codec, BODY_IO_TIMEOUT};
|
||||||
use crate::core::ser::ProtocolVersion;
|
use crate::core::ser::ProtocolVersion;
|
||||||
use crate::msg::{
|
use crate::msg::{write_message, Consumed, Message, Msg};
|
||||||
read_body, read_discard, read_header, read_item, write_message, Msg, MsgHeader,
|
|
||||||
MsgHeaderWrapper,
|
|
||||||
};
|
|
||||||
use crate::types::Error;
|
use crate::types::Error;
|
||||||
use crate::util::{RateCounter, RwLock};
|
use crate::util::{RateCounter, RwLock};
|
||||||
use std::io::{self, Read, Write};
|
use std::fs::File;
|
||||||
|
use std::io::{self, Write};
|
||||||
use std::net::{Shutdown, TcpStream};
|
use std::net::{Shutdown, TcpStream};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::mpsc::RecvTimeoutError;
|
use std::sync::mpsc::RecvTimeoutError;
|
||||||
use std::sync::{mpsc, Arc};
|
use std::sync::{mpsc, Arc};
|
||||||
|
use std::thread::{self, JoinHandle};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{
|
|
||||||
cmp,
|
|
||||||
thread::{self, JoinHandle},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const SEND_CHANNEL_CAP: usize = 100;
|
pub const SEND_CHANNEL_CAP: usize = 100;
|
||||||
|
|
||||||
const HEADER_IO_TIMEOUT: Duration = Duration::from_millis(2000);
|
|
||||||
const CHANNEL_TIMEOUT: Duration = Duration::from_millis(1000);
|
const CHANNEL_TIMEOUT: Duration = Duration::from_millis(1000);
|
||||||
const BODY_IO_TIMEOUT: Duration = Duration::from_millis(60000);
|
|
||||||
|
|
||||||
/// A trait to be implemented in order to receive messages from the
|
/// A trait to be implemented in order to receive messages from the
|
||||||
/// connection. Allows providing an optional response.
|
/// connection. Allows providing an optional response.
|
||||||
pub trait MessageHandler: Send + 'static {
|
pub trait MessageHandler: Send + 'static {
|
||||||
fn consume<'a, R: Read>(
|
fn consume(&self, message: Message) -> Result<Consumed, Error>;
|
||||||
&self,
|
|
||||||
msg: Message<'a, R>,
|
|
||||||
stopped: Arc<AtomicBool>,
|
|
||||||
tracker: Arc<Tracker>,
|
|
||||||
) -> Result<Option<Msg>, Error>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Macro to simplify the boilerplate around I/O and Grin error handling
|
// Macro to simplify the boilerplate around I/O and Grin error handling
|
||||||
|
@ -79,54 +67,6 @@ macro_rules! try_break {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! try_header {
|
|
||||||
($res:expr, $conn: expr) => {{
|
|
||||||
let _ = $conn.set_read_timeout(Some(HEADER_IO_TIMEOUT));
|
|
||||||
try_break!($res)
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A message as received by the connection. Provides access to the message
|
|
||||||
/// header lazily consumes the message body, handling its deserialization.
|
|
||||||
pub struct Message<'a, R: Read> {
|
|
||||||
pub header: MsgHeader,
|
|
||||||
stream: &'a mut R,
|
|
||||||
version: ProtocolVersion,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, R: Read> Message<'a, R> {
|
|
||||||
fn from_header(header: MsgHeader, stream: &'a mut R, version: ProtocolVersion) -> Self {
|
|
||||||
Message {
|
|
||||||
header,
|
|
||||||
stream,
|
|
||||||
version,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read the message body from the underlying connection
|
|
||||||
pub fn body<T: ser::Readable>(&mut self) -> Result<T, Error> {
|
|
||||||
read_body(&self.header, self.stream, self.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a single "thing" from the underlying connection.
|
|
||||||
/// Return the thing and the total bytes read.
|
|
||||||
pub fn streaming_read<T: ser::Readable>(&mut self) -> Result<(T, u64), Error> {
|
|
||||||
read_item(self.stream, self.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_attachment(&mut self, len: usize, writer: &mut dyn Write) -> Result<usize, Error> {
|
|
||||||
let mut written = 0;
|
|
||||||
while written < len {
|
|
||||||
let read_len = cmp::min(8000, len - written);
|
|
||||||
let mut buf = vec![0u8; read_len];
|
|
||||||
self.stream.read_exact(&mut buf[..])?;
|
|
||||||
writer.write_all(&buf)?;
|
|
||||||
written += read_len;
|
|
||||||
}
|
|
||||||
Ok(written)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct StopHandle {
|
pub struct StopHandle {
|
||||||
/// Channel to close the connection
|
/// Channel to close the connection
|
||||||
stopped: Arc<AtomicBool>,
|
stopped: Arc<AtomicBool>,
|
||||||
|
@ -281,7 +221,7 @@ where
|
||||||
H: MessageHandler,
|
H: MessageHandler,
|
||||||
{
|
{
|
||||||
// Split out tcp stream out into separate reader/writer halves.
|
// Split out tcp stream out into separate reader/writer halves.
|
||||||
let mut reader = conn.try_clone().expect("clone conn for reader failed");
|
let reader = conn.try_clone().expect("clone conn for reader failed");
|
||||||
let mut writer = conn.try_clone().expect("clone conn for writer failed");
|
let mut writer = conn.try_clone().expect("clone conn for writer failed");
|
||||||
let reader_stopped = stopped.clone();
|
let reader_stopped = stopped.clone();
|
||||||
|
|
||||||
|
@ -291,58 +231,83 @@ where
|
||||||
let reader_thread = thread::Builder::new()
|
let reader_thread = thread::Builder::new()
|
||||||
.name("peer_read".to_string())
|
.name("peer_read".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
|
let peer_addr = reader
|
||||||
|
.peer_addr()
|
||||||
|
.map(|a| a.to_string())
|
||||||
|
.unwrap_or_else(|_| "?".to_owned());
|
||||||
|
let mut codec = Codec::new(version, reader);
|
||||||
|
let mut attachment: Option<File> = None;
|
||||||
loop {
|
loop {
|
||||||
// check the read end
|
|
||||||
match try_header!(read_header(&mut reader, version), &reader) {
|
|
||||||
Some(MsgHeaderWrapper::Known(header)) => {
|
|
||||||
let _ = reader.set_read_timeout(Some(BODY_IO_TIMEOUT));
|
|
||||||
let msg = Message::from_header(header, &mut reader, version);
|
|
||||||
|
|
||||||
trace!(
|
|
||||||
"Received message header, type {:?}, len {}.",
|
|
||||||
msg.header.msg_type,
|
|
||||||
msg.header.msg_len
|
|
||||||
);
|
|
||||||
|
|
||||||
// Increase received bytes counter
|
|
||||||
reader_tracker.inc_received(MsgHeader::LEN as u64 + msg.header.msg_len);
|
|
||||||
|
|
||||||
let resp_msg = try_break!(handler.consume(
|
|
||||||
msg,
|
|
||||||
reader_stopped.clone(),
|
|
||||||
reader_tracker.clone()
|
|
||||||
));
|
|
||||||
if let Some(Some(resp_msg)) = resp_msg {
|
|
||||||
try_break!(conn_handle.send(resp_msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(MsgHeaderWrapper::Unknown(msg_len, type_byte)) => {
|
|
||||||
debug!(
|
|
||||||
"Received unknown message header, type {:?}, len {}.",
|
|
||||||
type_byte, msg_len
|
|
||||||
);
|
|
||||||
// Increase received bytes counter
|
|
||||||
reader_tracker.inc_received(MsgHeader::LEN as u64 + msg_len);
|
|
||||||
|
|
||||||
try_break!(read_discard(msg_len, &mut reader));
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check the close channel
|
// check the close channel
|
||||||
if reader_stopped.load(Ordering::Relaxed) {
|
if reader_stopped.load(Ordering::Relaxed) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check the read end
|
||||||
|
let (next, bytes_read) = codec.read();
|
||||||
|
|
||||||
|
// increase the appropriate counter
|
||||||
|
match &next {
|
||||||
|
Ok(Message::Attachment(_, _)) => reader_tracker.inc_quiet_received(bytes_read),
|
||||||
|
_ => reader_tracker.inc_received(bytes_read),
|
||||||
|
}
|
||||||
|
|
||||||
|
let message = match try_break!(next) {
|
||||||
|
Some(Message::Unknown(type_byte)) => {
|
||||||
|
debug!(
|
||||||
|
"Received unknown message, type {:?}, len {}.",
|
||||||
|
type_byte, bytes_read
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Some(Message::Attachment(update, bytes)) => {
|
||||||
|
let a = match &mut attachment {
|
||||||
|
Some(a) => a,
|
||||||
|
None => {
|
||||||
|
error!("Received unexpected attachment chunk");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let bytes = bytes.unwrap();
|
||||||
|
if let Err(e) = a.write_all(&bytes) {
|
||||||
|
error!("Unable to write attachment file: {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if update.left == 0 {
|
||||||
|
if let Err(e) = a.sync_all() {
|
||||||
|
error!("Unable to sync attachment file: {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
attachment.take();
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::Attachment(update, None)
|
||||||
|
}
|
||||||
|
Some(message) => {
|
||||||
|
trace!("Received message, type {}, len {}.", message, bytes_read);
|
||||||
|
message
|
||||||
|
}
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let consumed = try_break!(handler.consume(message)).unwrap_or(Consumed::None);
|
||||||
|
match consumed {
|
||||||
|
Consumed::Response(resp_msg) => {
|
||||||
|
try_break!(conn_handle.send(resp_msg));
|
||||||
|
}
|
||||||
|
Consumed::Attachment(meta, file) => {
|
||||||
|
// Start attachment
|
||||||
|
codec.expect_attachment(meta);
|
||||||
|
attachment = Some(file);
|
||||||
|
}
|
||||||
|
Consumed::Disconnect => break,
|
||||||
|
Consumed::None => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(
|
debug!("Shutting down reader connection with {}", peer_addr);
|
||||||
"Shutting down reader connection with {}",
|
let _ = codec.stream().shutdown(Shutdown::Both);
|
||||||
reader
|
|
||||||
.peer_addr()
|
|
||||||
.map(|a| a.to_string())
|
|
||||||
.unwrap_or_else(|_| "?".to_owned())
|
|
||||||
);
|
|
||||||
let _ = reader.shutdown(Shutdown::Both);
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let writer_thread = thread::Builder::new()
|
let writer_thread = thread::Builder::new()
|
||||||
|
|
|
@ -36,6 +36,7 @@ extern crate serde_derive;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
|
mod codec;
|
||||||
mod conn;
|
mod conn;
|
||||||
pub mod handshake;
|
pub mod handshake;
|
||||||
pub mod msg;
|
pub mod msg;
|
||||||
|
|
|
@ -16,16 +16,21 @@
|
||||||
|
|
||||||
use crate::conn::Tracker;
|
use crate::conn::Tracker;
|
||||||
use crate::core::core::hash::Hash;
|
use crate::core::core::hash::Hash;
|
||||||
use crate::core::core::BlockHeader;
|
use crate::core::core::{
|
||||||
|
BlockHeader, Transaction, UntrustedBlock, UntrustedBlockHeader, UntrustedCompactBlock,
|
||||||
|
};
|
||||||
use crate::core::pow::Difficulty;
|
use crate::core::pow::Difficulty;
|
||||||
use crate::core::ser::{
|
use crate::core::ser::{
|
||||||
self, ProtocolVersion, Readable, Reader, StreamingReader, Writeable, Writer,
|
self, ProtocolVersion, Readable, Reader, StreamingReader, Writeable, Writer,
|
||||||
};
|
};
|
||||||
use crate::core::{consensus, global};
|
use crate::core::{consensus, global};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
Capabilities, Error, PeerAddr, ReasonForBan, MAX_BLOCK_HEADERS, MAX_LOCATORS, MAX_PEER_ADDRS,
|
AttachmentMeta, AttachmentUpdate, Capabilities, Error, PeerAddr, ReasonForBan,
|
||||||
|
MAX_BLOCK_HEADERS, MAX_LOCATORS, MAX_PEER_ADDRS,
|
||||||
};
|
};
|
||||||
|
use bytes::Bytes;
|
||||||
use num::FromPrimitive;
|
use num::FromPrimitive;
|
||||||
|
use std::fmt;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -704,3 +709,77 @@ impl Readable for TxHashSetArchive {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum Message {
|
||||||
|
Unknown(u8),
|
||||||
|
Ping(Ping),
|
||||||
|
Pong(Pong),
|
||||||
|
BanReason(BanReason),
|
||||||
|
TransactionKernel(Hash),
|
||||||
|
GetTransaction(Hash),
|
||||||
|
Transaction(Transaction),
|
||||||
|
StemTransaction(Transaction),
|
||||||
|
GetBlock(Hash),
|
||||||
|
Block(UntrustedBlock),
|
||||||
|
GetCompactBlock(Hash),
|
||||||
|
CompactBlock(UntrustedCompactBlock),
|
||||||
|
GetHeaders(Locator),
|
||||||
|
Header(UntrustedBlockHeader),
|
||||||
|
Headers(Vec<BlockHeader>),
|
||||||
|
GetPeerAddrs(GetPeerAddrs),
|
||||||
|
PeerAddrs(PeerAddrs),
|
||||||
|
TxHashSetRequest(TxHashSetRequest),
|
||||||
|
TxHashSetArchive(TxHashSetArchive),
|
||||||
|
Attachment(AttachmentUpdate, Option<Bytes>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Message {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Message::Unknown(_) => write!(f, "unknown"),
|
||||||
|
Message::Ping(_) => write!(f, "ping"),
|
||||||
|
Message::Pong(_) => write!(f, "pong"),
|
||||||
|
Message::BanReason(_) => write!(f, "ban reason"),
|
||||||
|
Message::TransactionKernel(_) => write!(f, "tx kernel"),
|
||||||
|
Message::GetTransaction(_) => write!(f, "get tx"),
|
||||||
|
Message::Transaction(_) => write!(f, "tx"),
|
||||||
|
Message::StemTransaction(_) => write!(f, "stem tx"),
|
||||||
|
Message::GetBlock(_) => write!(f, "get block"),
|
||||||
|
Message::Block(_) => write!(f, "block"),
|
||||||
|
Message::GetCompactBlock(_) => write!(f, "get compact block"),
|
||||||
|
Message::CompactBlock(_) => write!(f, "compact block"),
|
||||||
|
Message::GetHeaders(_) => write!(f, "get headers"),
|
||||||
|
Message::Header(_) => write!(f, "header"),
|
||||||
|
Message::Headers(_) => write!(f, "headers"),
|
||||||
|
Message::GetPeerAddrs(_) => write!(f, "get peer addrs"),
|
||||||
|
Message::PeerAddrs(_) => write!(f, "peer addrs"),
|
||||||
|
Message::TxHashSetRequest(_) => write!(f, "tx hash set request"),
|
||||||
|
Message::TxHashSetArchive(_) => write!(f, "tx hash set"),
|
||||||
|
Message::Attachment(_, _) => write!(f, "attachment"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Message {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Consume({})", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Consumed {
|
||||||
|
Response(Msg),
|
||||||
|
Attachment(Arc<AttachmentMeta>, File),
|
||||||
|
None,
|
||||||
|
Disconnect,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Consumed {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Consumed::Response(msg) => write!(f, "Consumed::Response({:?})", msg.header.msg_type),
|
||||||
|
Consumed::Attachment(meta, _) => write!(f, "Consumed::Attachment({:?})", meta.size),
|
||||||
|
Consumed::None => write!(f, "Consumed::None"),
|
||||||
|
Consumed::Disconnect => write!(f, "Consumed::Disconnect"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -13,21 +13,16 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::chain;
|
use crate::chain;
|
||||||
use crate::conn::{Message, MessageHandler, Tracker};
|
use crate::conn::MessageHandler;
|
||||||
use crate::core::core::{self, hash::Hash, hash::Hashed, CompactBlock};
|
use crate::core::core::{hash::Hashed, CompactBlock};
|
||||||
use crate::msg::{
|
|
||||||
BanReason, GetPeerAddrs, Headers, Locator, Msg, PeerAddrs, Ping, Pong, TxHashSetArchive,
|
use crate::msg::{Consumed, Headers, Message, Msg, PeerAddrs, Pong, TxHashSetArchive, Type};
|
||||||
TxHashSetRequest, Type,
|
use crate::types::{AttachmentMeta, Error, NetAdapter, PeerInfo};
|
||||||
};
|
|
||||||
use crate::types::{Error, NetAdapter, PeerInfo};
|
|
||||||
use chrono::prelude::Utc;
|
use chrono::prelude::Utc;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use std::cmp;
|
use std::fs::{self, File};
|
||||||
use std::fs::{self, File, OpenOptions};
|
|
||||||
use std::io::{BufWriter, Read};
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
pub struct Protocol {
|
pub struct Protocol {
|
||||||
adapter: Arc<dyn NetAdapter>,
|
adapter: Arc<dyn NetAdapter>,
|
||||||
|
@ -50,12 +45,7 @@ impl Protocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageHandler for Protocol {
|
impl MessageHandler for Protocol {
|
||||||
fn consume<R: Read>(
|
fn consume(&self, message: Message) -> Result<Consumed, Error> {
|
||||||
&self,
|
|
||||||
mut msg: Message<R>,
|
|
||||||
stopped: Arc<AtomicBool>,
|
|
||||||
tracker: Arc<Tracker>,
|
|
||||||
) -> Result<Option<Msg>, Error> {
|
|
||||||
let adapter = &self.adapter;
|
let adapter = &self.adapter;
|
||||||
|
|
||||||
// If we received a msg from a banned peer then log and drop it.
|
// If we received a msg from a banned peer then log and drop it.
|
||||||
|
@ -63,210 +53,169 @@ impl MessageHandler for Protocol {
|
||||||
// banned peers up correctly?
|
// banned peers up correctly?
|
||||||
if adapter.is_banned(self.peer_info.addr) {
|
if adapter.is_banned(self.peer_info.addr) {
|
||||||
debug!(
|
debug!(
|
||||||
"handler: consume: peer {:?} banned, received: {:?}, dropping.",
|
"handler: consume: peer {:?} banned, received: {}, dropping.",
|
||||||
self.peer_info.addr, msg.header.msg_type,
|
self.peer_info.addr, message,
|
||||||
);
|
);
|
||||||
return Ok(None);
|
return Ok(Consumed::Disconnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
match msg.header.msg_type {
|
let consumed = match message {
|
||||||
Type::Ping => {
|
Message::Attachment(update, _) => {
|
||||||
let ping: Ping = msg.body()?;
|
self.adapter.txhashset_download_update(
|
||||||
adapter.peer_difficulty(self.peer_info.addr, ping.total_difficulty, ping.height);
|
update.meta.start_time,
|
||||||
|
(update.meta.size - update.left) as u64,
|
||||||
|
update.meta.size as u64,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(Some(Msg::new(
|
if update.left == 0 {
|
||||||
|
let meta = update.meta;
|
||||||
|
trace!(
|
||||||
|
"handle_payload: txhashset archive save to file {:?} success",
|
||||||
|
meta.path,
|
||||||
|
);
|
||||||
|
|
||||||
|
let zip = File::open(meta.path.clone())?;
|
||||||
|
let res =
|
||||||
|
self.adapter
|
||||||
|
.txhashset_write(meta.hash.clone(), zip, &self.peer_info)?;
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"handle_payload: txhashset archive for {} at {}, DONE. Data Ok: {}",
|
||||||
|
meta.hash, meta.height, !res
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(e) = fs::remove_file(meta.path.clone()) {
|
||||||
|
warn!("fail to remove tmp file: {:?}. err: {}", meta.path, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Consumed::None
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::Ping(ping) => {
|
||||||
|
adapter.peer_difficulty(self.peer_info.addr, ping.total_difficulty, ping.height);
|
||||||
|
Consumed::Response(Msg::new(
|
||||||
Type::Pong,
|
Type::Pong,
|
||||||
Pong {
|
Pong {
|
||||||
total_difficulty: adapter.total_difficulty()?,
|
total_difficulty: adapter.total_difficulty()?,
|
||||||
height: adapter.total_height()?,
|
height: adapter.total_height()?,
|
||||||
},
|
},
|
||||||
self.peer_info.version,
|
self.peer_info.version,
|
||||||
)?))
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::Pong => {
|
Message::Pong(pong) => {
|
||||||
let pong: Pong = msg.body()?;
|
|
||||||
adapter.peer_difficulty(self.peer_info.addr, pong.total_difficulty, pong.height);
|
adapter.peer_difficulty(self.peer_info.addr, pong.total_difficulty, pong.height);
|
||||||
Ok(None)
|
Consumed::None
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::BanReason => {
|
Message::BanReason(ban_reason) => {
|
||||||
let ban_reason: BanReason = msg.body()?;
|
|
||||||
error!("handle_payload: BanReason {:?}", ban_reason);
|
error!("handle_payload: BanReason {:?}", ban_reason);
|
||||||
Ok(None)
|
Consumed::Disconnect
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::TransactionKernel => {
|
Message::TransactionKernel(h) => {
|
||||||
let h: Hash = msg.body()?;
|
debug!("handle_payload: received tx kernel: {}", h);
|
||||||
debug!(
|
|
||||||
"handle_payload: received tx kernel: {}, msg_len: {}",
|
|
||||||
h, msg.header.msg_len
|
|
||||||
);
|
|
||||||
adapter.tx_kernel_received(h, &self.peer_info)?;
|
adapter.tx_kernel_received(h, &self.peer_info)?;
|
||||||
Ok(None)
|
Consumed::None
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::GetTransaction => {
|
Message::GetTransaction(h) => {
|
||||||
let h: Hash = msg.body()?;
|
debug!("handle_payload: GetTransaction: {}", h);
|
||||||
debug!(
|
|
||||||
"handle_payload: GetTransaction: {}, msg_len: {}",
|
|
||||||
h, msg.header.msg_len,
|
|
||||||
);
|
|
||||||
let tx = adapter.get_transaction(h);
|
let tx = adapter.get_transaction(h);
|
||||||
if let Some(tx) = tx {
|
if let Some(tx) = tx {
|
||||||
Ok(Some(Msg::new(
|
Consumed::Response(Msg::new(Type::Transaction, tx, self.peer_info.version)?)
|
||||||
Type::Transaction,
|
|
||||||
tx,
|
|
||||||
self.peer_info.version,
|
|
||||||
)?))
|
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Consumed::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::Transaction => {
|
Message::Transaction(tx) => {
|
||||||
debug!(
|
debug!("handle_payload: received tx");
|
||||||
"handle_payload: received tx: msg_len: {}",
|
|
||||||
msg.header.msg_len
|
|
||||||
);
|
|
||||||
let tx: core::Transaction = msg.body()?;
|
|
||||||
adapter.transaction_received(tx, false)?;
|
adapter.transaction_received(tx, false)?;
|
||||||
Ok(None)
|
Consumed::None
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::StemTransaction => {
|
Message::StemTransaction(tx) => {
|
||||||
debug!(
|
debug!("handle_payload: received stem tx");
|
||||||
"handle_payload: received stem tx: msg_len: {}",
|
|
||||||
msg.header.msg_len
|
|
||||||
);
|
|
||||||
let tx: core::Transaction = msg.body()?;
|
|
||||||
adapter.transaction_received(tx, true)?;
|
adapter.transaction_received(tx, true)?;
|
||||||
Ok(None)
|
Consumed::None
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::GetBlock => {
|
Message::GetBlock(h) => {
|
||||||
let h: Hash = msg.body()?;
|
trace!("handle_payload: GetBlock: {}", h);
|
||||||
trace!(
|
|
||||||
"handle_payload: GetBlock: {}, msg_len: {}",
|
|
||||||
h,
|
|
||||||
msg.header.msg_len,
|
|
||||||
);
|
|
||||||
|
|
||||||
let bo = adapter.get_block(h, &self.peer_info);
|
let bo = adapter.get_block(h, &self.peer_info);
|
||||||
if let Some(b) = bo {
|
if let Some(b) = bo {
|
||||||
return Ok(Some(Msg::new(Type::Block, b, self.peer_info.version)?));
|
Consumed::Response(Msg::new(Type::Block, b, self.peer_info.version)?)
|
||||||
|
} else {
|
||||||
|
Consumed::None
|
||||||
}
|
}
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::Block => {
|
Message::Block(b) => {
|
||||||
debug!(
|
debug!("handle_payload: received block");
|
||||||
"handle_payload: received block: msg_len: {}",
|
|
||||||
msg.header.msg_len
|
|
||||||
);
|
|
||||||
let b: core::UntrustedBlock = msg.body()?;
|
|
||||||
|
|
||||||
// We default to NONE opts here as we do not know know yet why this block was
|
// We default to NONE opts here as we do not know know yet why this block was
|
||||||
// received.
|
// received.
|
||||||
// If we requested this block from a peer due to our node syncing then
|
// If we requested this block from a peer due to our node syncing then
|
||||||
// the peer adapter will override opts to reflect this.
|
// the peer adapter will override opts to reflect this.
|
||||||
adapter.block_received(b.into(), &self.peer_info, chain::Options::NONE)?;
|
adapter.block_received(b.into(), &self.peer_info, chain::Options::NONE)?;
|
||||||
Ok(None)
|
Consumed::None
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::GetCompactBlock => {
|
Message::GetCompactBlock(h) => {
|
||||||
let h: Hash = msg.body()?;
|
|
||||||
if let Some(b) = adapter.get_block(h, &self.peer_info) {
|
if let Some(b) = adapter.get_block(h, &self.peer_info) {
|
||||||
let cb: CompactBlock = b.into();
|
let cb: CompactBlock = b.into();
|
||||||
Ok(Some(Msg::new(
|
Consumed::Response(Msg::new(Type::CompactBlock, cb, self.peer_info.version)?)
|
||||||
Type::CompactBlock,
|
|
||||||
cb,
|
|
||||||
self.peer_info.version,
|
|
||||||
)?))
|
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Consumed::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::CompactBlock => {
|
Message::CompactBlock(b) => {
|
||||||
debug!(
|
debug!("handle_payload: received compact block");
|
||||||
"handle_payload: received compact block: msg_len: {}",
|
|
||||||
msg.header.msg_len
|
|
||||||
);
|
|
||||||
let b: core::UntrustedCompactBlock = msg.body()?;
|
|
||||||
|
|
||||||
adapter.compact_block_received(b.into(), &self.peer_info)?;
|
adapter.compact_block_received(b.into(), &self.peer_info)?;
|
||||||
Ok(None)
|
Consumed::None
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::GetHeaders => {
|
Message::GetHeaders(loc) => {
|
||||||
// load headers from the locator
|
// load headers from the locator
|
||||||
let loc: Locator = msg.body()?;
|
|
||||||
let headers = adapter.locate_headers(&loc.hashes)?;
|
let headers = adapter.locate_headers(&loc.hashes)?;
|
||||||
|
|
||||||
// serialize and send all the headers over
|
// serialize and send all the headers over
|
||||||
Ok(Some(Msg::new(
|
Consumed::Response(Msg::new(
|
||||||
Type::Headers,
|
Type::Headers,
|
||||||
Headers { headers },
|
Headers { headers },
|
||||||
self.peer_info.version,
|
self.peer_info.version,
|
||||||
)?))
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// "header first" block propagation - if we have not yet seen this block
|
// "header first" block propagation - if we have not yet seen this block
|
||||||
// we can go request it from some of our peers
|
// we can go request it from some of our peers
|
||||||
Type::Header => {
|
Message::Header(header) => {
|
||||||
let header: core::UntrustedBlockHeader = msg.body()?;
|
|
||||||
adapter.header_received(header.into(), &self.peer_info)?;
|
adapter.header_received(header.into(), &self.peer_info)?;
|
||||||
Ok(None)
|
Consumed::None
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::Headers => {
|
Message::Headers(headers) => {
|
||||||
let mut total_bytes_read = 0;
|
adapter.headers_received(&headers, &self.peer_info)?;
|
||||||
|
Consumed::None
|
||||||
// Read the count (u16) so we now how many headers to read.
|
|
||||||
let (count, bytes_read): (u16, _) = msg.streaming_read()?;
|
|
||||||
total_bytes_read += bytes_read;
|
|
||||||
|
|
||||||
// Read chunks of headers off the stream and pass them off to the adapter.
|
|
||||||
let chunk_size = 32u16;
|
|
||||||
let mut headers = Vec::with_capacity(chunk_size as usize);
|
|
||||||
for i in 1..=count {
|
|
||||||
let (header, bytes_read) =
|
|
||||||
msg.streaming_read::<core::UntrustedBlockHeader>()?;
|
|
||||||
headers.push(header.into());
|
|
||||||
total_bytes_read += bytes_read;
|
|
||||||
if i % chunk_size == 0 || i == count {
|
|
||||||
adapter.headers_received(&headers, &self.peer_info)?;
|
|
||||||
headers.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now check we read the correct total number of bytes off the stream.
|
|
||||||
if total_bytes_read != msg.header.msg_len {
|
|
||||||
return Err(Error::MsgLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::GetPeerAddrs => {
|
Message::GetPeerAddrs(get_peers) => {
|
||||||
let get_peers: GetPeerAddrs = msg.body()?;
|
|
||||||
let peers = adapter.find_peer_addrs(get_peers.capabilities);
|
let peers = adapter.find_peer_addrs(get_peers.capabilities);
|
||||||
Ok(Some(Msg::new(
|
Consumed::Response(Msg::new(
|
||||||
Type::PeerAddrs,
|
Type::PeerAddrs,
|
||||||
PeerAddrs { peers },
|
PeerAddrs { peers },
|
||||||
self.peer_info.version,
|
self.peer_info.version,
|
||||||
)?))
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::PeerAddrs => {
|
Message::PeerAddrs(peer_addrs) => {
|
||||||
let peer_addrs: PeerAddrs = msg.body()?;
|
|
||||||
adapter.peer_addrs_received(peer_addrs.peers);
|
adapter.peer_addrs_received(peer_addrs.peers);
|
||||||
Ok(None)
|
Consumed::None
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::TxHashSetRequest => {
|
Message::TxHashSetRequest(sm_req) => {
|
||||||
let sm_req: TxHashSetRequest = msg.body()?;
|
|
||||||
debug!(
|
debug!(
|
||||||
"handle_payload: txhashset req for {} at {}",
|
"handle_payload: txhashset req for {} at {}",
|
||||||
sm_req.hash, sm_req.height
|
sm_req.hash, sm_req.height
|
||||||
|
@ -288,14 +237,13 @@ impl MessageHandler for Protocol {
|
||||||
self.peer_info.version,
|
self.peer_info.version,
|
||||||
)?;
|
)?;
|
||||||
resp.add_attachment(txhashset.reader);
|
resp.add_attachment(txhashset.reader);
|
||||||
Ok(Some(resp))
|
Consumed::Response(resp)
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Consumed::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::TxHashSetArchive => {
|
Message::TxHashSetArchive(sm_arch) => {
|
||||||
let sm_arch: TxHashSetArchive = msg.body()?;
|
|
||||||
debug!(
|
debug!(
|
||||||
"handle_payload: txhashset archive for {} at {}. size={}",
|
"handle_payload: txhashset archive for {} at {}. size={}",
|
||||||
sm_arch.hash, sm_arch.height, sm_arch.bytes,
|
sm_arch.hash, sm_arch.height, sm_arch.bytes,
|
||||||
|
@ -313,93 +261,34 @@ impl MessageHandler for Protocol {
|
||||||
// Update the sync state requested status
|
// Update the sync state requested status
|
||||||
self.state_sync_requested.store(false, Ordering::Relaxed);
|
self.state_sync_requested.store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
let download_start_time = Utc::now();
|
let start_time = Utc::now();
|
||||||
self.adapter
|
self.adapter
|
||||||
.txhashset_download_update(download_start_time, 0, sm_arch.bytes);
|
.txhashset_download_update(start_time, 0, sm_arch.bytes);
|
||||||
|
|
||||||
let nonce: u32 = thread_rng().gen_range(0, 1_000_000);
|
let nonce: u32 = thread_rng().gen_range(0, 1_000_000);
|
||||||
let tmp = self.adapter.get_tmpfile_pathname(format!(
|
let path = self.adapter.get_tmpfile_pathname(format!(
|
||||||
"txhashset-{}-{}.zip",
|
"txhashset-{}-{}.zip",
|
||||||
download_start_time.timestamp(),
|
start_time.timestamp(),
|
||||||
nonce
|
nonce
|
||||||
));
|
));
|
||||||
let mut now = Instant::now();
|
|
||||||
let mut save_txhashset_to_file = |file| -> Result<(), Error> {
|
|
||||||
let mut tmp_zip =
|
|
||||||
BufWriter::new(OpenOptions::new().write(true).create_new(true).open(file)?);
|
|
||||||
let total_size = sm_arch.bytes as usize;
|
|
||||||
let mut downloaded_size: usize = 0;
|
|
||||||
let mut request_size = cmp::min(48_000, total_size);
|
|
||||||
while request_size > 0 {
|
|
||||||
let size = msg.copy_attachment(request_size, &mut tmp_zip)?;
|
|
||||||
downloaded_size += size;
|
|
||||||
request_size = cmp::min(48_000, total_size - downloaded_size);
|
|
||||||
self.adapter.txhashset_download_update(
|
|
||||||
download_start_time,
|
|
||||||
downloaded_size as u64,
|
|
||||||
total_size as u64,
|
|
||||||
);
|
|
||||||
if now.elapsed().as_secs() > 10 {
|
|
||||||
now = Instant::now();
|
|
||||||
debug!(
|
|
||||||
"handle_payload: txhashset archive: {}/{}",
|
|
||||||
downloaded_size, total_size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Increase received bytes quietly (without affecting the counters).
|
|
||||||
// Otherwise we risk banning a peer as "abusive".
|
|
||||||
tracker.inc_quiet_received(size as u64);
|
|
||||||
|
|
||||||
// check the close channel
|
let file = fs::OpenOptions::new()
|
||||||
if stopped.load(Ordering::Relaxed) {
|
.write(true)
|
||||||
debug!("stopping txhashset download early");
|
.create_new(true)
|
||||||
return Err(Error::ConnectionClose);
|
.open(path.clone())?;
|
||||||
}
|
|
||||||
}
|
let meta = AttachmentMeta {
|
||||||
debug!(
|
size: sm_arch.bytes as usize,
|
||||||
"handle_payload: txhashset archive: {}/{} ... DONE",
|
hash: sm_arch.hash,
|
||||||
downloaded_size, total_size
|
height: sm_arch.height,
|
||||||
);
|
start_time,
|
||||||
tmp_zip
|
path,
|
||||||
.into_inner()
|
|
||||||
.map_err(|_| Error::Internal)?
|
|
||||||
.sync_all()?;
|
|
||||||
Ok(())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = save_txhashset_to_file(tmp.clone()) {
|
Consumed::Attachment(Arc::new(meta), file)
|
||||||
error!(
|
|
||||||
"handle_payload: txhashset archive save to file fail. err={:?}",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
trace!(
|
|
||||||
"handle_payload: txhashset archive save to file {:?} success",
|
|
||||||
tmp,
|
|
||||||
);
|
|
||||||
|
|
||||||
let tmp_zip = File::open(tmp.clone())?;
|
|
||||||
let res = self
|
|
||||||
.adapter
|
|
||||||
.txhashset_write(sm_arch.hash, tmp_zip, &self.peer_info)?;
|
|
||||||
|
|
||||||
debug!(
|
|
||||||
"handle_payload: txhashset archive for {} at {}, DONE. Data Ok: {}",
|
|
||||||
sm_arch.hash, sm_arch.height, !res
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(e) = fs::remove_file(tmp.clone()) {
|
|
||||||
warn!("fail to remove tmp file: {:?}. err: {}", tmp, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
Type::Error | Type::Hand | Type::Shake => {
|
Message::Unknown(_) => Consumed::None,
|
||||||
debug!("Received an unexpected msg: {:?}", msg.header.msg_type);
|
};
|
||||||
Ok(None)
|
Ok(consumed)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ pub enum Error {
|
||||||
Connection(io::Error),
|
Connection(io::Error),
|
||||||
/// Header type does not match the expected message type
|
/// Header type does not match the expected message type
|
||||||
BadMessage,
|
BadMessage,
|
||||||
|
UnexpectedMessage,
|
||||||
MsgLen,
|
MsgLen,
|
||||||
Banned,
|
Banned,
|
||||||
ConnectionClose,
|
ConnectionClose,
|
||||||
|
@ -659,3 +660,19 @@ pub trait NetAdapter: ChainAdapter {
|
||||||
/// Is this peer currently banned?
|
/// Is this peer currently banned?
|
||||||
fn is_banned(&self, addr: PeerAddr) -> bool;
|
fn is_banned(&self, addr: PeerAddr) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct AttachmentMeta {
|
||||||
|
pub size: usize,
|
||||||
|
pub hash: Hash,
|
||||||
|
pub height: u64,
|
||||||
|
pub start_time: DateTime<Utc>,
|
||||||
|
pub path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct AttachmentUpdate {
|
||||||
|
pub read: usize,
|
||||||
|
pub left: usize,
|
||||||
|
pub meta: Arc<AttachmentMeta>,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue