Make Inventory and NetworkMessage enums exhaustive

Both by added an `Unknown` variant.
This commit is contained in:
Steven Roose 2020-12-21 11:59:25 +00:00
parent 944371d6a2
commit 767b14f696
No known key found for this signature in database
GPG Key ID: 2F2A88D7F8D68E87
5 changed files with 46 additions and 29 deletions

View File

@ -80,12 +80,6 @@ pub enum Error {
ParseFailed(&'static str), ParseFailed(&'static str),
/// Unsupported Segwit flag /// Unsupported Segwit flag
UnsupportedSegwitFlag(u8), UnsupportedSegwitFlag(u8),
/// Unrecognized network command with its length
UnrecognizedNetworkCommand(String, usize),
/// Invalid Inventory type
UnknownInventoryType(u32),
/// The network command is longer than the maximum allowed (12 chars)
NetworkCommandTooLong(String),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -104,10 +98,6 @@ impl fmt::Display for Error {
Error::ParseFailed(ref e) => write!(f, "parse failed: {}", e), Error::ParseFailed(ref e) => write!(f, "parse failed: {}", e),
Error::UnsupportedSegwitFlag(ref swflag) => write!(f, Error::UnsupportedSegwitFlag(ref swflag) => write!(f,
"unsupported segwit version: {}", swflag), "unsupported segwit version: {}", swflag),
Error::UnrecognizedNetworkCommand(ref nwcmd, _) => write!(f,
"unrecognized network command: {}", nwcmd),
Error::UnknownInventoryType(ref tp) => write!(f, "Unknown Inventory type: {}", tp),
Error::NetworkCommandTooLong(ref cmd) => write!(f, "Network Command too long: {}", cmd),
} }
} }
} }
@ -123,10 +113,7 @@ impl error::Error for Error {
| Error::NonMinimalVarInt | Error::NonMinimalVarInt
| Error::UnknownNetworkMagic(..) | Error::UnknownNetworkMagic(..)
| Error::ParseFailed(..) | Error::ParseFailed(..)
| Error::UnsupportedSegwitFlag(..) | Error::UnsupportedSegwitFlag(..) => None,
| Error::UnrecognizedNetworkCommand(..)
| Error::UnknownInventoryType(..)
| Error::NetworkCommandTooLong(..) => None,
} }
} }
} }

View File

@ -109,9 +109,9 @@ pub struct RawNetworkMessage {
pub payload: NetworkMessage pub payload: NetworkMessage
} }
#[derive(Clone, PartialEq, Eq, Debug)]
/// A Network message payload. Proper documentation is available on at /// A Network message payload. Proper documentation is available on at
/// [Bitcoin Wiki: Protocol Specification](https://en.bitcoin.it/wiki/Protocol_specification) /// [Bitcoin Wiki: Protocol Specification](https://en.bitcoin.it/wiki/Protocol_specification)
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum NetworkMessage { pub enum NetworkMessage {
/// `version` /// `version`
Version(message_network::VersionMessage), Version(message_network::VersionMessage),
@ -173,10 +173,22 @@ pub enum NetworkMessage {
AddrV2(Vec<AddrV2Message>), AddrV2(Vec<AddrV2Message>),
/// `sendaddrv2` /// `sendaddrv2`
SendAddrV2, SendAddrV2,
/// Any other message.
Unknown {
/// The command of this message.
command: CommandString,
/// The payload of this message.
payload: Vec<u8>,
}
} }
impl NetworkMessage { impl NetworkMessage {
/// Return the message command. This is useful for debug outputs. /// Return the message command as a static string reference.
///
/// This returns `"unknown"` for [NetworkMessage::Unknown],
/// regardless of the actual command in the unknown message.
/// Use the [command] method to get the command for unknown messages.
pub fn cmd(&self) -> &'static str { pub fn cmd(&self) -> &'static str {
match *self { match *self {
NetworkMessage::Version(_) => "version", NetworkMessage::Version(_) => "version",
@ -207,17 +219,25 @@ impl NetworkMessage {
NetworkMessage::WtxidRelay => "wtxidrelay", NetworkMessage::WtxidRelay => "wtxidrelay",
NetworkMessage::AddrV2(_) => "addrv2", NetworkMessage::AddrV2(_) => "addrv2",
NetworkMessage::SendAddrV2 => "sendaddrv2", NetworkMessage::SendAddrV2 => "sendaddrv2",
NetworkMessage::Unknown { .. } => "unknown",
} }
} }
/// Return the CommandString for the message command. /// Return the CommandString for the message command.
pub fn command(&self) -> CommandString { pub fn command(&self) -> CommandString {
CommandString::try_from(self.cmd()).expect("cmd returns valid commands") match *self {
NetworkMessage::Unknown { command: ref c, .. } => c.clone(),
_ => CommandString::try_from(self.cmd()).expect("cmd returns valid commands")
}
} }
} }
impl RawNetworkMessage { impl RawNetworkMessage {
/// Return the message command. This is useful for debug outputs. /// Return the message command as a static string reference.
///
/// This returns `"unknown"` for [NetworkMessage::Unknown],
/// regardless of the actual command in the unknown message.
/// Use the [command] method to get the command for unknown messages.
pub fn cmd(&self) -> &'static str { pub fn cmd(&self) -> &'static str {
self.payload.cmd() self.payload.cmd()
} }
@ -281,8 +301,9 @@ impl Encodable for RawNetworkMessage {
| NetworkMessage::SendHeaders | NetworkMessage::SendHeaders
| NetworkMessage::MemPool | NetworkMessage::MemPool
| NetworkMessage::GetAddr | NetworkMessage::GetAddr
| NetworkMessage::WtxidRelay => vec![], | NetworkMessage::WtxidRelay
| NetworkMessage::SendAddrV2 => vec![], | NetworkMessage::SendAddrV2 => vec![],
NetworkMessage::Unknown { payload: ref data, .. } => serialize(data),
}).consensus_encode(&mut s)?; }).consensus_encode(&mut s)?;
Ok(len) Ok(len)
} }
@ -314,12 +335,11 @@ impl Decodable for HeaderDeserializationWrapper {
impl Decodable for RawNetworkMessage { impl Decodable for RawNetworkMessage {
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> {
let magic = Decodable::consensus_decode(&mut d)?; let magic = Decodable::consensus_decode(&mut d)?;
let cmd = CommandString::consensus_decode(&mut d)?.0; let cmd = CommandString::consensus_decode(&mut d)?;
let raw_payload = CheckedData::consensus_decode(&mut d)?.0; let raw_payload = CheckedData::consensus_decode(&mut d)?.0;
let raw_payload_len = raw_payload.len();
let mut mem_d = Cursor::new(raw_payload); let mut mem_d = Cursor::new(raw_payload);
let payload = match &cmd[..] { let payload = match &cmd.0[..] {
"version" => NetworkMessage::Version(Decodable::consensus_decode(&mut mem_d)?), "version" => NetworkMessage::Version(Decodable::consensus_decode(&mut mem_d)?),
"verack" => NetworkMessage::Verack, "verack" => NetworkMessage::Verack,
"addr" => NetworkMessage::Addr(Decodable::consensus_decode(&mut mem_d)?), "addr" => NetworkMessage::Addr(Decodable::consensus_decode(&mut mem_d)?),
@ -350,7 +370,10 @@ impl Decodable for RawNetworkMessage {
"wtxidrelay" => NetworkMessage::WtxidRelay, "wtxidrelay" => NetworkMessage::WtxidRelay,
"addrv2" => NetworkMessage::AddrV2(Decodable::consensus_decode(&mut mem_d)?), "addrv2" => NetworkMessage::AddrV2(Decodable::consensus_decode(&mut mem_d)?),
"sendaddrv2" => NetworkMessage::SendAddrV2, "sendaddrv2" => NetworkMessage::SendAddrV2,
_ => return Err(encode::Error::UnrecognizedNetworkCommand(cmd.into_owned(), 4 + 12 + 4 + 4 + raw_payload_len)), // magic + msg str + payload len + checksum + payload _ => NetworkMessage::Unknown {
command: cmd,
payload: mem_d.into_inner(),
}
}; };
Ok(RawNetworkMessage { Ok(RawNetworkMessage {
magic: magic, magic: magic,

View File

@ -41,6 +41,13 @@ pub enum Inventory {
WitnessTransaction(Txid), WitnessTransaction(Txid),
/// Witness Block /// Witness Block
WitnessBlock(BlockHash), WitnessBlock(BlockHash),
/// Unknown inventory type
Unknown {
/// The inventory item type.
inv_type: u32,
/// The hash of the inventory item
hash: [u8; 32],
}
} }
impl Encodable for Inventory { impl Encodable for Inventory {
@ -62,6 +69,7 @@ impl Encodable for Inventory {
Inventory::WTx(w) => encode_inv!(5, w), Inventory::WTx(w) => encode_inv!(5, w),
Inventory::WitnessTransaction(ref t) => encode_inv!(0x40000001, t), Inventory::WitnessTransaction(ref t) => encode_inv!(0x40000001, t),
Inventory::WitnessBlock(ref b) => encode_inv!(0x40000002, b), Inventory::WitnessBlock(ref b) => encode_inv!(0x40000002, b),
Inventory::Unknown { inv_type: t, hash: ref d } => encode_inv!(t, d),
}) })
} }
} }
@ -77,7 +85,10 @@ impl Decodable for Inventory {
5 => Inventory::WTx(Decodable::consensus_decode(&mut d)?), 5 => Inventory::WTx(Decodable::consensus_decode(&mut d)?),
0x40000001 => Inventory::WitnessTransaction(Decodable::consensus_decode(&mut d)?), 0x40000001 => Inventory::WitnessTransaction(Decodable::consensus_decode(&mut d)?),
0x40000002 => Inventory::WitnessBlock(Decodable::consensus_decode(&mut d)?), 0x40000002 => Inventory::WitnessBlock(Decodable::consensus_decode(&mut d)?),
tp => return Err(encode::Error::UnknownInventoryType(tp)), tp => Inventory::Unknown {
inv_type: tp,
hash: Decodable::consensus_decode(&mut d)?,
}
}) })
} }
} }

View File

@ -84,8 +84,8 @@ impl_consensus_encoding!(VersionMessage, version, services, timestamp,
receiver, sender, nonce, receiver, sender, nonce,
user_agent, start_height, relay); user_agent, start_height, relay);
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
/// message rejection reason as a code /// message rejection reason as a code
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum RejectReason { pub enum RejectReason {
/// malformed message /// malformed message
Malformed = 0x01, Malformed = 0x01,

View File

@ -68,10 +68,6 @@ impl<R: Read> StreamReader<R> {
return Err(encode::Error::Io(io::Error::from(io::ErrorKind::UnexpectedEof))); return Err(encode::Error::Io(io::Error::from(io::ErrorKind::UnexpectedEof)));
} }
}, },
Err(encode::Error::UnrecognizedNetworkCommand(message, len)) => {
self.unparsed.drain(..len);
return Err(encode::Error::UnrecognizedNetworkCommand(message, len))
},
Err(err) => return Err(err), Err(err) => return Err(err),
// We have successfully read from the buffer // We have successfully read from the buffer
Ok((message, index)) => { Ok((message, index)) => {