Merge pull request #357 from stevenroose/command-str

Various optimizations of the network code
This commit is contained in:
Steven Roose 2019-12-11 15:38:52 +00:00 committed by GitHub
commit 024557fe47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 110 additions and 41 deletions

View File

@ -29,11 +29,8 @@
//! big-endian decimals, etc.) //! big-endian decimals, etc.)
//! //!
use std::{mem, u32}; use std::{fmt, error, io, mem, u32};
use std::borrow::Cow;
use std::error;
use std::fmt;
use std::io;
use std::io::{Cursor, Read, Write}; use std::io::{Cursor, Read, Write};
use hashes::hex::ToHex; use hashes::hex::ToHex;
@ -483,6 +480,26 @@ impl Decodable for String {
} }
} }
// Cow<'static, str>
impl Encodable for Cow<'static, str> {
#[inline]
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, Error> {
let b = self.as_bytes();
let vi_len = VarInt(b.len() as u64).consensus_encode(&mut s)?;
s.emit_slice(&b)?;
Ok(vi_len + b.len())
}
}
impl Decodable for Cow<'static, str> {
#[inline]
fn consensus_decode<D: io::Read>(d: D) -> Result<Cow<'static, str>, Error> {
String::from_utf8(Decodable::consensus_decode(d)?)
.map_err(|_| self::Error::ParseFailed("String was not valid UTF8"))
.map(Cow::Owned)
}
}
// Arrays // Arrays
macro_rules! impl_array { macro_rules! impl_array {
@ -894,6 +911,10 @@ mod tests {
#[test] #[test]
fn deserialize_strbuf_test() { fn deserialize_strbuf_test() {
assert_eq!(deserialize(&[6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]).ok(), Some("Andrew".to_string())); assert_eq!(deserialize(&[6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]).ok(), Some("Andrew".to_string()));
assert_eq!(
deserialize(&[6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]).ok(),
Some(::std::borrow::Cow::Borrowed("Andrew"))
);
} }
#[test] #[test]

View File

@ -19,7 +19,8 @@
//! also defines (de)serialization routines for many primitives. //! also defines (de)serialization routines for many primitives.
//! //!
use std::{io, iter, mem}; use std::{io, iter, mem, fmt};
use std::borrow::Cow;
use std::io::Cursor; use std::io::Cursor;
use blockdata::block; use blockdata::block;
@ -34,7 +35,31 @@ use consensus::encode::MAX_VEC_SIZE;
/// Serializer for command string /// Serializer for command string
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct CommandString(pub String); pub struct CommandString(Cow<'static, str>);
impl fmt::Display for CommandString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.0.as_ref())
}
}
impl From<&'static str> for CommandString {
fn from(f: &'static str) -> Self {
CommandString(f.into())
}
}
impl From<String> for CommandString {
fn from(f: String) -> Self {
CommandString(f.into())
}
}
impl AsRef<str> for CommandString {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl Encodable for CommandString { impl Encodable for CommandString {
#[inline] #[inline]
@ -42,11 +67,10 @@ impl Encodable for CommandString {
&self, &self,
s: S, s: S,
) -> Result<usize, encode::Error> { ) -> Result<usize, encode::Error> {
let &CommandString(ref inner_str) = self;
let mut rawbytes = [0u8; 12]; let mut rawbytes = [0u8; 12];
let strbytes = inner_str.as_bytes(); let strbytes = self.0.as_bytes();
if strbytes.len() > 12 { if strbytes.len() > 12 {
panic!("Command string longer than 12 bytes"); return Err(encode::Error::UnrecognizedNetworkCommand(self.0.clone().into_owned()));
} }
for x in 0..strbytes.len() { for x in 0..strbytes.len() {
rawbytes[x] = strbytes[x]; rawbytes[x] = strbytes[x];
@ -116,7 +140,6 @@ pub enum NetworkMessage {
Ping(u64), Ping(u64),
/// `pong` /// `pong`
Pong(u64), Pong(u64),
// TODO: reject,
// TODO: bloom filtering // TODO: bloom filtering
/// BIP157 getcfilters /// BIP157 getcfilters
GetCFilters(message_filter::GetCFilters), GetCFilters(message_filter::GetCFilters),
@ -136,10 +159,10 @@ pub enum NetworkMessage {
Reject(message_network::Reject) Reject(message_network::Reject)
} }
impl RawNetworkMessage { impl NetworkMessage {
/// Return the message command. This is useful for debug outputs. /// Return the message command. This is useful for debug outputs.
pub fn command(&self) -> String { pub fn cmd(&self) -> &'static str {
match self.payload { match *self {
NetworkMessage::Version(_) => "version", NetworkMessage::Version(_) => "version",
NetworkMessage::Verack => "verack", NetworkMessage::Verack => "verack",
NetworkMessage::Addr(_) => "addr", NetworkMessage::Addr(_) => "addr",
@ -164,7 +187,24 @@ impl RawNetworkMessage {
NetworkMessage::CFCheckpt(_) => "cfcheckpt", NetworkMessage::CFCheckpt(_) => "cfcheckpt",
NetworkMessage::Alert(_) => "alert", NetworkMessage::Alert(_) => "alert",
NetworkMessage::Reject(_) => "reject", NetworkMessage::Reject(_) => "reject",
}.to_owned() }
}
/// Return the CommandString for the message command.
pub fn command(&self) -> CommandString {
self.cmd().into()
}
}
impl RawNetworkMessage {
/// Return the message command. This is useful for debug outputs.
pub fn cmd(&self) -> &'static str {
self.payload.cmd()
}
/// Return the CommandString for the message command.
pub fn command(&self) -> CommandString {
self.payload.command()
} }
} }
@ -193,7 +233,7 @@ impl Encodable for RawNetworkMessage {
) -> Result<usize, encode::Error> { ) -> Result<usize, encode::Error> {
let mut len = 0; let mut len = 0;
len += self.magic.consensus_encode(&mut s)?; len += self.magic.consensus_encode(&mut s)?;
len += CommandString(self.command()).consensus_encode(&mut s)?; len += self.command().consensus_encode(&mut s)?;
len += CheckedData(match self.payload { len += CheckedData(match self.payload {
NetworkMessage::Version(ref dat) => serialize(dat), NetworkMessage::Version(ref dat) => serialize(dat),
NetworkMessage::Addr(ref dat) => serialize(dat), NetworkMessage::Addr(ref dat) => serialize(dat),
@ -281,7 +321,7 @@ impl Decodable for RawNetworkMessage {
"cfcheckpt" => NetworkMessage::CFCheckpt(Decodable::consensus_decode(&mut mem_d)?), "cfcheckpt" => NetworkMessage::CFCheckpt(Decodable::consensus_decode(&mut mem_d)?),
"reject" => NetworkMessage::Reject(Decodable::consensus_decode(&mut mem_d)?), "reject" => NetworkMessage::Reject(Decodable::consensus_decode(&mut mem_d)?),
"alert" => NetworkMessage::Alert(Decodable::consensus_decode(&mut mem_d)?), "alert" => NetworkMessage::Alert(Decodable::consensus_decode(&mut mem_d)?),
_ => return Err(encode::Error::UnrecognizedNetworkCommand(cmd)), _ => return Err(encode::Error::UnrecognizedNetworkCommand(cmd.into_owned())),
}; };
Ok(RawNetworkMessage { Ok(RawNetworkMessage {
magic: magic, magic: magic,
@ -292,20 +332,26 @@ impl Decodable for RawNetworkMessage {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::io;
use super::{RawNetworkMessage, NetworkMessage, CommandString}; use super::{RawNetworkMessage, NetworkMessage, CommandString};
use consensus::encode::{deserialize, deserialize_partial, serialize}; use consensus::encode::{Encodable, deserialize, deserialize_partial, serialize};
#[test] #[test]
fn serialize_commandstring_test() { fn serialize_commandstring_test() {
let cs = CommandString("Andrew".to_owned()); let cs = CommandString("Andrew".into());
assert_eq!(serialize(&cs), vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]); assert_eq!(serialize(&cs), vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]);
// Test oversized one.
let mut encoder = io::Cursor::new(vec![]);
assert!(CommandString("AndrewAndrewA".into()).consensus_encode(&mut encoder).is_err());
} }
#[test] #[test]
fn deserialize_commandstring_test() { fn deserialize_commandstring_test() {
let cs: Result<CommandString, _> = deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]); let cs: Result<CommandString, _> = deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]);
assert!(cs.is_ok()); assert!(cs.is_ok());
assert_eq!(cs.unwrap(), CommandString("Andrew".to_owned())); assert_eq!(cs.as_ref().unwrap().to_string(), "Andrew".to_owned());
assert_eq!(cs.unwrap(), "Andrew".into());
let short_cs: Result<CommandString, _> = deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0]); let short_cs: Result<CommandString, _> = deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0]);
assert!(short_cs.is_err()); assert!(short_cs.is_err());

View File

@ -24,7 +24,7 @@ use hashes::sha256d;
use std::io; use std::io;
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug, Copy)]
/// The type of an inventory object /// The type of an inventory object
pub enum InvType { pub enum InvType {
/// Error --- these inventories can be ignored /// Error --- these inventories can be ignored

View File

@ -18,12 +18,14 @@
//! capabilities //! capabilities
//! //!
use std::io;
use std::borrow::Cow;
use network::address::Address; use network::address::Address;
use network::constants::{self, ServiceFlags}; use network::constants::{self, ServiceFlags};
use consensus::{Encodable, Decodable, ReadExt}; use consensus::{Encodable, Decodable, ReadExt};
use consensus::encode; use consensus::encode;
use std::io; use network::message::CommandString;
use network::message_network::RejectReason::{MALFORMED, INVALID, OBSOLETE, DUPLICATE, NONSTANDARD, DUST, CHECKPOINT, FEE};
use hashes::sha256d; use hashes::sha256d;
/// Some simple messages /// Some simple messages
@ -87,21 +89,21 @@ impl_consensus_encoding!(VersionMessage, version, services, timestamp,
/// message rejection reason as a code /// message rejection reason as a code
pub enum RejectReason { pub enum RejectReason {
/// malformed message /// malformed message
MALFORMED = 0x01, Malformed = 0x01,
/// invalid message /// invalid message
INVALID = 0x10, Invalid = 0x10,
/// obsolete message /// obsolete message
OBSOLETE = 0x11, Obsolete = 0x11,
/// duplicate message /// duplicate message
DUPLICATE = 0x12, Duplicate = 0x12,
/// nonstandard transaction /// nonstandard transaction
NONSTANDARD = 0x40, NonStandard = 0x40,
/// an output is below dust limit /// an output is below dust limit
DUST = 0x41, Dust = 0x41,
/// insufficient fee /// insufficient fee
FEE = 0x42, Fee = 0x42,
/// checkpoint /// checkpoint
CHECKPOINT = 0x43 Checkpoint = 0x43
} }
impl Encodable for RejectReason { impl Encodable for RejectReason {
@ -114,14 +116,14 @@ impl Encodable for RejectReason {
impl Decodable for RejectReason { impl Decodable for RejectReason {
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> { fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
Ok(match d.read_u8()? { Ok(match d.read_u8()? {
0x01 => MALFORMED, 0x01 => RejectReason::Malformed,
0x10 => INVALID, 0x10 => RejectReason::Invalid,
0x11 => OBSOLETE, 0x11 => RejectReason::Obsolete,
0x12 => DUPLICATE, 0x12 => RejectReason::Duplicate,
0x40 => NONSTANDARD, 0x40 => RejectReason::NonStandard,
0x41 => DUST, 0x41 => RejectReason::Dust,
0x42 => FEE, 0x42 => RejectReason::Fee,
0x43 => CHECKPOINT, 0x43 => RejectReason::Checkpoint,
_ => return Err(encode::Error::ParseFailed("unknown reject code")) _ => return Err(encode::Error::ParseFailed("unknown reject code"))
}) })
} }
@ -131,11 +133,11 @@ impl Decodable for RejectReason {
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct Reject { pub struct Reject {
/// message type rejected /// message type rejected
pub message: String, pub message: CommandString,
/// reason of rejection as code /// reason of rejection as code
pub ccode: RejectReason, pub ccode: RejectReason,
/// reason of rejectection /// reason of rejectection
pub reason: String, pub reason: Cow<'static, str>,
/// reference to rejected item /// reference to rejected item
pub hash: sha256d::Hash pub hash: sha256d::Hash
} }