Replace serialize::Error::Detail with variants

- Add serialize::Error::ParseFailed(&'static str) variant for
  serialization errors without context
- Add appropriate variants to replace network::Error::Detail for
  serialization error with context
- Remove error method from SimpleDecoders
This commit is contained in:
Carl Dong 2018-08-21 01:31:54 -07:00
parent d12a861f85
commit 0c172941af
8 changed files with 78 additions and 52 deletions

View File

@ -417,7 +417,7 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for Transaction {
} }
// We don't support anything else // We don't support anything else
x => { x => {
Err(d.error(format!("segwit flag {:02x} not understood", x))) Err(serialize::Error::UnsupportedSegwitFlag(x))
} }
} }
// non-segwit // non-segwit

View File

@ -117,7 +117,7 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for Network {
u32::consensus_decode(d) u32::consensus_decode(d)
.and_then(|m| { .and_then(|m| {
Network::from_magic(m) Network::from_magic(m)
.ok_or(d.error(format!("Unknown network (magic {:x})", m))) .ok_or(serialize::Error::UnknownNetworkMagic(m))
}) })
} }
} }

View File

@ -146,7 +146,7 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for String {
#[inline] #[inline]
fn consensus_decode(d: &mut D) -> Result<String, serialize::Error> { fn consensus_decode(d: &mut D) -> Result<String, serialize::Error> {
String::from_utf8(ConsensusDecodable::consensus_decode(d)?) String::from_utf8(ConsensusDecodable::consensus_decode(d)?)
.map_err(|_| d.error("String was not valid UTF8".to_owned())) .map_err(|_| serialize::Error::ParseFailed("String was not valid UTF8"))
} }
} }
@ -205,9 +205,9 @@ impl<D: SimpleDecoder, T: ConsensusDecodable<D>> ConsensusDecodable<D> for Vec<T
let VarInt(len): VarInt = ConsensusDecodable::consensus_decode(d)?; let VarInt(len): VarInt = ConsensusDecodable::consensus_decode(d)?;
let byte_size = (len as usize) let byte_size = (len as usize)
.checked_mul(mem::size_of::<T>()) .checked_mul(mem::size_of::<T>())
.ok_or(d.error("Invalid length".to_owned()))?; .ok_or(serialize::Error::ParseFailed("Invalid length"))?;
if byte_size > MAX_VEC_SIZE { if byte_size > MAX_VEC_SIZE {
return Err(d.error(format!("tried to allocate vec of size {} (max {})", byte_size, MAX_VEC_SIZE))); return Err(serialize::Error::OversizedVectorAllocation { requested: byte_size, max: MAX_VEC_SIZE })
} }
let mut ret = Vec::with_capacity(len as usize); let mut ret = Vec::with_capacity(len as usize);
for _ in 0..len { ret.push(ConsensusDecodable::consensus_decode(d)?); } for _ in 0..len { ret.push(ConsensusDecodable::consensus_decode(d)?); }
@ -226,7 +226,7 @@ impl<D: SimpleDecoder, T: ConsensusDecodable<D>> ConsensusDecodable<D> for Box<[
let VarInt(len): VarInt = ConsensusDecodable::consensus_decode(d)?; let VarInt(len): VarInt = ConsensusDecodable::consensus_decode(d)?;
let len = len as usize; let len = len as usize;
if len > MAX_VEC_SIZE { if len > MAX_VEC_SIZE {
return Err(d.error(format!("tried to allocate vec of size {} (max {})", len, MAX_VEC_SIZE))); return Err(serialize::Error::OversizedVectorAllocation { requested: len, max: MAX_VEC_SIZE })
} }
let mut ret = Vec::with_capacity(len); let mut ret = Vec::with_capacity(len);
for _ in 0..len { ret.push(ConsensusDecodable::consensus_decode(d)?); } for _ in 0..len { ret.push(ConsensusDecodable::consensus_decode(d)?); }
@ -291,7 +291,10 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for CheckedData {
for _ in 0..len { ret.push(ConsensusDecodable::consensus_decode(d)?); } for _ in 0..len { ret.push(ConsensusDecodable::consensus_decode(d)?); }
let expected_checksum = sha2_checksum(&ret); let expected_checksum = sha2_checksum(&ret);
if expected_checksum != checksum { if expected_checksum != checksum {
Err(d.error(format!("bad checksum {:?} (expected {:?})", checksum, expected_checksum))) Err(serialize::Error::InvalidChecksum {
expected: expected_checksum,
actual: checksum,
})
} else { } else {
Ok(CheckedData(ret)) Ok(CheckedData(ret))
} }

View File

@ -196,7 +196,7 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for RawNetworkMessage {
"pong" => NetworkMessage::Pong(ConsensusDecodable::consensus_decode(&mut mem_d)?), "pong" => NetworkMessage::Pong(ConsensusDecodable::consensus_decode(&mut mem_d)?),
"tx" => NetworkMessage::Tx(ConsensusDecodable::consensus_decode(&mut mem_d)?), "tx" => NetworkMessage::Tx(ConsensusDecodable::consensus_decode(&mut mem_d)?),
"alert" => NetworkMessage::Alert(ConsensusDecodable::consensus_decode(&mut mem_d)?), "alert" => NetworkMessage::Alert(ConsensusDecodable::consensus_decode(&mut mem_d)?),
cmd => return Err(d.error(format!("unrecognized network command `{}`", cmd))) _ => return Err(serialize::Error::UnrecognizedNetworkCommand(cmd)),
}; };
Ok(RawNetworkMessage { Ok(RawNetworkMessage {
magic: magic, magic: magic,

View File

@ -43,24 +43,57 @@ pub enum Error {
Bech32(bitcoin_bech32::Error), Bech32(bitcoin_bech32::Error),
/// Error from the `byteorder` crate /// Error from the `byteorder` crate
ByteOrder(io::Error), ByteOrder(io::Error),
/// Network magic was not what we expected /// Network magic was not expected
BadNetworkMagic(u32, u32), UnexpectedNetworkMagic {
/// Network message was unrecognized /// The expected network magic
BadNetworkMessage(String), expected: u32,
/// The unexpected network magic
actual: u32,
},
/// Tried to allocate an oversized vector
OversizedVectorAllocation{
/// The capacity requested
requested: usize,
/// The maximum capacity
max: usize,
},
/// Checksum was invalid
InvalidChecksum {
/// The expected checksum
expected: [u8; 4],
/// The invalid checksum
actual: [u8; 4],
},
/// Network magic was unknown
UnknownNetworkMagic(u32),
/// Parsing error /// Parsing error
ParseFailed, ParseFailed(&'static str),
/// Error propagated from subsystem
Detail(String, Box<Error>),
/// Unsupported witness version /// Unsupported witness version
UnsupportedWitnessVersion(u8), UnsupportedWitnessVersion(u8),
/// Unsupported Segwit flag
UnsupportedSegwitFlag(u8),
/// Unrecognized network command
UnrecognizedNetworkCommand(String),
/// Unexpected hex digit
UnexpectedHexDigit(char),
} }
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 {
Error::Io(ref e) => fmt::Display::fmt(e, f), Error::Io(ref e) => fmt::Display::fmt(e, f),
Error::Detail(ref s, ref e) => write!(f, "{}: {}", s, e), Error::Base58(ref e) => fmt::Display::fmt(e, f),
ref x => f.write_str(error::Error::description(x)), Error::Bech32(ref e) => fmt::Display::fmt(e, f),
Error::ByteOrder(ref e) => fmt::Display::fmt(e, f),
Error::UnexpectedNetworkMagic { expected: ref e, actual: ref a } => write!(f, "{}: expected {}, actual {}", error::Error::description(self), e, a),
Error::OversizedVectorAllocation { requested: ref r, max: ref m } => write!(f, "{}: requested {}, maximum {}", error::Error::description(self), r, m),
Error::InvalidChecksum { expected: ref e, actual: ref a } => write!(f, "{}: expected {}, actual {}", error::Error::description(self), hex_encode(e), hex_encode(a)),
Error::UnknownNetworkMagic(ref m) => write!(f, "{}: {}", error::Error::description(self), m),
Error::ParseFailed(ref e) => write!(f, "{}: {}", error::Error::description(self), e),
Error::UnsupportedWitnessVersion(ref wver) => write!(f, "{}: {}", error::Error::description(self), wver),
Error::UnsupportedSegwitFlag(ref swflag) => write!(f, "{}: {}", error::Error::description(self), swflag),
Error::UnrecognizedNetworkCommand(ref nwcmd) => write!(f, "{}: {}", error::Error::description(self), nwcmd),
Error::UnexpectedHexDigit(ref d) => write!(f, "{}: {}", error::Error::description(self), d),
} }
} }
} }
@ -72,7 +105,6 @@ impl error::Error for Error {
Error::Base58(ref e) => Some(e), Error::Base58(ref e) => Some(e),
Error::Bech32(ref e) => Some(e), Error::Bech32(ref e) => Some(e),
Error::ByteOrder(ref e) => Some(e), Error::ByteOrder(ref e) => Some(e),
Error::Detail(_, ref e) => Some(e),
_ => None _ => None
} }
} }
@ -80,14 +112,18 @@ impl error::Error for Error {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
Error::Io(ref e) => e.description(), Error::Io(ref e) => e.description(),
Error::ParseFailed => "parsing error",
Error::Detail(_, ref e) => e.description(),
Error::Base58(ref e) => e.description(), Error::Base58(ref e) => e.description(),
Error::Bech32(ref e) => e.description(), Error::Bech32(ref e) => e.description(),
Error::ByteOrder(ref e) => e.description(), Error::ByteOrder(ref e) => e.description(),
Error::BadNetworkMagic(_, _) => "incorrect network magic", Error::UnexpectedNetworkMagic { .. } => "unexpected network magic",
Error::BadNetworkMessage(_) => "incorrect/unexpected network message", Error::OversizedVectorAllocation { .. } => "allocation of oversized vector requested",
Error::UnsupportedWitnessVersion(_) => "unsupported witness version", Error::InvalidChecksum { .. } => "invalid checksum",
Error::UnknownNetworkMagic(..) => "unknown network magic",
Error::ParseFailed(..) => "parse failed",
Error::UnsupportedWitnessVersion(..) => "unsupported witness version",
Error::UnsupportedSegwitFlag(..) => "unsupported segwit version",
Error::UnrecognizedNetworkCommand(..) => "unrecognized network command",
Error::UnexpectedHexDigit(..) => "unexpected hex digit",
} }
} }
} }
@ -156,7 +192,7 @@ pub fn deserialize<'a, T>(data: &'a [u8]) -> Result<T, Error>
if decoder.into_inner().position() == data.len() as u64 { if decoder.into_inner().position() == data.len() as u64 {
Ok(rv) Ok(rv)
} else { } else {
Err(Error::ParseFailed) Err(Error::ParseFailed("data not consumed entirely when explicitly deserializing"))
} }
} }
@ -230,9 +266,6 @@ pub trait SimpleDecoder {
/// Read a boolean /// Read a boolean
fn read_bool(&mut self) -> Result<bool, Error>; fn read_bool(&mut self) -> Result<bool, Error>;
/// Signal a decoding error
fn error(&mut self, err: String) -> Error;
} }
macro_rules! encoder_fn { macro_rules! encoder_fn {
@ -298,11 +331,6 @@ impl<R: Read> SimpleDecoder for RawDecoder<R> {
Err(e) => Err(Error::Io(e)) Err(e) => Err(Error::Io(e))
} }
} }
#[inline]
fn error(&mut self, err: String) -> Error {
Error::Detail(err, Box::new(Error::ParseFailed))
}
} }
// Aren't really any tests here.. the main functions are serialize and // Aren't really any tests here.. the main functions are serialize and

View File

@ -182,7 +182,10 @@ impl Socket {
// Then for magic (this should come before parse error, but we can't // Then for magic (this should come before parse error, but we can't
// get to it if the deserialization failed). TODO restructure this // get to it if the deserialization failed). TODO restructure this
if decoded.magic != self.magic { if decoded.magic != self.magic {
Err(util::Error::Serialize(serialize::Error::BadNetworkMagic(self.magic, decoded.magic))) Err(serialize::Error::UnexpectedNetworkMagic {
expected: self.magic,
actual: decoded.magic,
}.into())
} else { } else {
Ok(decoded.payload) Ok(decoded.payload)
} }

View File

@ -29,24 +29,15 @@ pub fn hex_bytes(s: &str) -> Result<Vec<u8>, serialize::Error> {
if e.is_err() { e } if e.is_err() { e }
else { else {
match (f.to_digit(16), s.to_digit(16)) { match (f.to_digit(16), s.to_digit(16)) {
(None, _) => Err(serialize::Error::Detail( (None, _) => Err(serialize::Error::UnexpectedHexDigit(f)),
format!("expected hex, got {:}", f), (_, None) => Err(serialize::Error::UnexpectedHexDigit(s)),
Box::new(serialize::Error::ParseFailed)
)),
(_, None) => Err(serialize::Error::Detail(
format!("expected hex, got {:}", s),
Box::new(serialize::Error::ParseFailed)
)),
(Some(f), Some(s)) => { v.push((f * 0x10 + s) as u8); Ok(()) } (Some(f), Some(s)) => { v.push((f * 0x10 + s) as u8); Ok(()) }
} }
} }
)?; )?;
// Check that there was no remainder // Check that there was no remainder
match iter.remainder() { match iter.remainder() {
Some(_) => Err(serialize::Error::Detail( Some(_) => Err(serialize::Error::ParseFailed("hexstring of odd length")),
"hexstring of odd length".to_owned(),
Box::new(serialize::Error::ParseFailed)
)),
None => Ok(v) None => Ok(v)
} }
} }

View File

@ -59,19 +59,20 @@ pub trait BitArray {
fn one() -> Self; fn one() -> Self;
} }
/// A general error code /// A general error code, other errors should implement conversions to/from this
/// if appropriate.
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// The `target` field of a block header did not match the expected difficulty
SpvBadTarget,
/// The header hash is not below the target
SpvBadProofOfWork,
/// secp-related error /// secp-related error
Secp256k1(secp256k1::Error), Secp256k1(secp256k1::Error),
/// Serialization error /// Serialization error
Serialize(serialize::Error), Serialize(serialize::Error),
/// Network error /// Network error
Network(network::Error), Network(network::Error),
/// The header hash is not below the target
SpvBadProofOfWork,
/// The `target` field of a block header did not match the expected difficulty
SpvBadTarget,
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -96,10 +97,10 @@ impl error::Error for Error {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
Error::Secp256k1(ref e) => e.description(), Error::Secp256k1(ref e) => e.description(),
Error::SpvBadTarget => "target incorrect",
Error::SpvBadProofOfWork => "target correct but not attained",
Error::Serialize(ref e) => e.description(), Error::Serialize(ref e) => e.description(),
Error::Network(ref e) => e.description(), Error::Network(ref e) => e.description(),
Error::SpvBadProofOfWork => "target correct but not attained",
Error::SpvBadTarget => "target incorrect",
} }
} }
} }