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.)
//!
use std::{mem, u32};
use std::error;
use std::fmt;
use std::io;
use std::{fmt, error, io, mem, u32};
use std::borrow::Cow;
use std::io::{Cursor, Read, Write};
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
macro_rules! impl_array {
@ -894,6 +911,10 @@ mod tests {
#[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(::std::borrow::Cow::Borrowed("Andrew"))
);
}
#[test]

View File

@ -19,7 +19,8 @@
//! 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 blockdata::block;
@ -34,7 +35,31 @@ use consensus::encode::MAX_VEC_SIZE;
/// Serializer for command string
#[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 {
#[inline]
@ -42,11 +67,10 @@ impl Encodable for CommandString {
&self,
s: S,
) -> Result<usize, encode::Error> {
let &CommandString(ref inner_str) = self;
let mut rawbytes = [0u8; 12];
let strbytes = inner_str.as_bytes();
let strbytes = self.0.as_bytes();
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() {
rawbytes[x] = strbytes[x];
@ -116,7 +140,6 @@ pub enum NetworkMessage {
Ping(u64),
/// `pong`
Pong(u64),
// TODO: reject,
// TODO: bloom filtering
/// BIP157 getcfilters
GetCFilters(message_filter::GetCFilters),
@ -136,10 +159,10 @@ pub enum NetworkMessage {
Reject(message_network::Reject)
}
impl RawNetworkMessage {
impl NetworkMessage {
/// Return the message command. This is useful for debug outputs.
pub fn command(&self) -> String {
match self.payload {
pub fn cmd(&self) -> &'static str {
match *self {
NetworkMessage::Version(_) => "version",
NetworkMessage::Verack => "verack",
NetworkMessage::Addr(_) => "addr",
@ -164,7 +187,24 @@ impl RawNetworkMessage {
NetworkMessage::CFCheckpt(_) => "cfcheckpt",
NetworkMessage::Alert(_) => "alert",
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> {
let mut len = 0;
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 {
NetworkMessage::Version(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)?),
"reject" => NetworkMessage::Reject(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 {
magic: magic,
@ -292,20 +332,26 @@ impl Decodable for RawNetworkMessage {
#[cfg(test)]
mod test {
use std::io;
use super::{RawNetworkMessage, NetworkMessage, CommandString};
use consensus::encode::{deserialize, deserialize_partial, serialize};
use consensus::encode::{Encodable, deserialize, deserialize_partial, serialize};
#[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]);
// Test oversized one.
let mut encoder = io::Cursor::new(vec![]);
assert!(CommandString("AndrewAndrewA".into()).consensus_encode(&mut encoder).is_err());
}
#[test]
fn deserialize_commandstring_test() {
let cs: Result<CommandString, _> = deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]);
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]);
assert!(short_cs.is_err());

View File

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

View File

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