diff --git a/src/network/message.rs b/src/network/message.rs index 7f15ae98..2e25206d 100644 --- a/src/network/message.rs +++ b/src/network/message.rs @@ -131,7 +131,9 @@ pub enum NetworkMessage { /// BIP157 cfcheckpt CFCheckpt(message_filter::CFCheckpt), /// `alert` - Alert(Vec) + Alert(Vec), + /// `reject` + Reject(message_network::Reject) } impl RawNetworkMessage { @@ -161,6 +163,7 @@ impl RawNetworkMessage { NetworkMessage::GetCFCheckpt(_) => "getcfckpt", NetworkMessage::CFCheckpt(_) => "cfcheckpt", NetworkMessage::Alert(_) => "alert", + NetworkMessage::Reject(_) => "reject", }.to_owned() } } @@ -211,6 +214,7 @@ impl Encodable for RawNetworkMessage { NetworkMessage::GetCFCheckpt(ref dat) => serialize(dat), NetworkMessage::CFCheckpt(ref dat) => serialize(dat), NetworkMessage::Alert(ref dat) => serialize(dat), + NetworkMessage::Reject(ref dat) => serialize(dat), NetworkMessage::Verack | NetworkMessage::SendHeaders | NetworkMessage::MemPool @@ -275,6 +279,7 @@ impl Decodable for RawNetworkMessage { "cfheaders" => NetworkMessage::CFHeaders(Decodable::consensus_decode(&mut mem_d)?), "getcfckpt" => NetworkMessage::GetCFCheckpt(Decodable::consensus_decode(&mut mem_d)?), "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)), }; diff --git a/src/network/message_network.rs b/src/network/message_network.rs index 8280a238..4d83bbd8 100644 --- a/src/network/message_network.rs +++ b/src/network/message_network.rs @@ -20,6 +20,12 @@ use network::address::Address; use network::constants; +use consensus::{Encodable, Decodable, ReadExt}; +use consensus::encode; +use std::io; +use byteorder::WriteBytesExt; +use network::message_network::RejectReason::{MALFORMED, INVALID, OBSOLETE, DUPLICATE, NONSTANDARD, DUST, CHECKPOINT, FEE}; +use hashes::sha256d; /// Some simple messages @@ -78,6 +84,65 @@ impl_consensus_encoding!(VersionMessage, version, services, timestamp, receiver, sender, nonce, user_agent, start_height, relay); +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +/// message rejection reason as a code +pub enum RejectReason { + /// malformed message + MALFORMED = 0x01, + /// invalid message + INVALID = 0x10, + /// obsolete message + OBSOLETE = 0x11, + /// duplicate message + DUPLICATE = 0x12, + /// nonstandard transaction + NONSTANDARD = 0x40, + /// an output is below dust limit + DUST = 0x41, + /// insufficient fee + FEE = 0x42, + /// checkpoint + CHECKPOINT = 0x43 +} + +impl Encodable for RejectReason { + fn consensus_encode(&self, mut e: W) -> Result { + e.write_u8(*self as u8)?; + Ok(1) + } +} + +impl Decodable for RejectReason { + fn consensus_decode(mut d: D) -> Result { + Ok(match d.read_u8()? { + 0x01 => MALFORMED, + 0x10 => INVALID, + 0x11 => OBSOLETE, + 0x12 => DUPLICATE, + 0x40 => NONSTANDARD, + 0x41 => DUST, + 0x42 => FEE, + 0x43 => CHECKPOINT, + _ => return Err(encode::Error::ParseFailed("unknown reject code")) + }) + } +} + +/// Reject message might be sent by peers rejecting one of our messages +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct Reject { + /// message type rejected + pub message: String, + /// reason of rejection as code + pub ccode: RejectReason, + /// reason of rejectection + pub reason: String, + /// reference to rejected item + pub hash: sha256d::Hash +} + +impl_consensus_encoding!(Reject, message, ccode, reason, hash); + #[cfg(test)] mod tests { use super::VersionMessage;