Add encode::Error::MissingData variant

The `io::Error` is troublesome because it contains a bunch of stuff that
never happens when reading from a buffer. However the EOF variant can
occur if the buffer is too short. As an initial step towards reducing
usage of the `io::Error` add a `MissingData` variant to the
`encode::Error` and when converting from an IO error map to
`MissingData` if EOF is encountered.
This commit is contained in:
Tobin C. Harding 2024-10-18 10:11:54 +11:00
parent 5a42ef2850
commit b04142c745
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
3 changed files with 18 additions and 8 deletions

View File

@ -1027,13 +1027,11 @@ mod tests {
]) ])
.is_err()); .is_err());
let rand_io_err = Error::Io(io::Error::new(io::ErrorKind::Other, ""));
// Check serialization that `if len > MAX_VEC_SIZE {return err}` isn't inclusive, // Check serialization that `if len > MAX_VEC_SIZE {return err}` isn't inclusive,
// by making sure it fails with IO Error and not an `OversizedVectorAllocation` Error. // by making sure it fails with `MissingData` and not an `OversizedVectorAllocation` Error.
let err = let err =
deserialize::<CheckedData>(&serialize(&(super::MAX_VEC_SIZE as u32))).unwrap_err(); deserialize::<CheckedData>(&serialize(&(super::MAX_VEC_SIZE as u32))).unwrap_err();
assert_eq!(discriminant(&err), discriminant(&rand_io_err)); assert!(matches!(err, Error::MissingData));
test_len_is_max_vec::<u8>(); test_len_is_max_vec::<u8>();
test_len_is_max_vec::<BlockHash>(); test_len_is_max_vec::<BlockHash>();
@ -1055,11 +1053,10 @@ mod tests {
Vec<T>: Decodable, Vec<T>: Decodable,
T: fmt::Debug, T: fmt::Debug,
{ {
let rand_io_err = Error::Io(io::Error::new(io::ErrorKind::Other, ""));
let mut buf = Vec::new(); let mut buf = Vec::new();
buf.emit_compact_size(super::MAX_VEC_SIZE / mem::size_of::<T>()).unwrap(); buf.emit_compact_size(super::MAX_VEC_SIZE / mem::size_of::<T>()).unwrap();
let err = deserialize::<Vec<T>>(&buf).unwrap_err(); let err = deserialize::<Vec<T>>(&buf).unwrap_err();
assert_eq!(discriminant(&err), discriminant(&rand_io_err)); assert!(matches!(err, Error::MissingData));
} }
#[test] #[test]

View File

@ -56,6 +56,8 @@ impl<E: fmt::Debug + std::error::Error + 'static> std::error::Error for DecodeEr
pub enum Error { pub enum Error {
/// And I/O error. /// And I/O error.
Io(io::Error), Io(io::Error),
/// Missing data (early end of file or slice too short).
MissingData, // TODO: Can we add more context?
/// Tried to allocate an oversized vector. /// Tried to allocate an oversized vector.
OversizedVectorAllocation { OversizedVectorAllocation {
/// The capacity requested. /// The capacity requested.
@ -86,6 +88,7 @@ impl fmt::Display for Error {
match *self { match *self {
Io(ref e) => write_err!(f, "IO error"; e), Io(ref e) => write_err!(f, "IO error"; e),
MissingData => write!(f, "missing data (early end of file or slice too short)"),
OversizedVectorAllocation { requested: ref r, max: ref m } => OversizedVectorAllocation { requested: ref r, max: ref m } =>
write!(f, "allocation of oversized vector: requested {}, maximum {}", r, m), write!(f, "allocation of oversized vector: requested {}, maximum {}", r, m),
InvalidChecksum { expected: ref e, actual: ref a } => InvalidChecksum { expected: ref e, actual: ref a } =>
@ -105,7 +108,8 @@ impl std::error::Error for Error {
match self { match self {
Io(e) => Some(e), Io(e) => Some(e),
OversizedVectorAllocation { .. } MissingData
| OversizedVectorAllocation { .. }
| InvalidChecksum { .. } | InvalidChecksum { .. }
| NonMinimalVarInt | NonMinimalVarInt
| ParseFailed(_) | ParseFailed(_)
@ -115,7 +119,14 @@ impl std::error::Error for Error {
} }
impl From<io::Error> for Error { impl From<io::Error> for Error {
fn from(error: io::Error) -> Self { Error::Io(error) } fn from(e: io::Error) -> Self {
use io::ErrorKind;
match e.kind() {
ErrorKind::UnexpectedEof => Error::MissingData,
_ => Error::Io(e),
}
}
} }
/// Hex deserialization error. /// Hex deserialization error.

View File

@ -361,6 +361,8 @@ impl<D: fmt::Display> serde::de::Expected for DisplayExpected<D> {
fn consensus_error_into_serde<E: serde::de::Error>(error: ConsensusError) -> E { fn consensus_error_into_serde<E: serde::de::Error>(error: ConsensusError) -> E {
match error { match error {
ConsensusError::Io(error) => panic!("unexpected IO error {:?}", error), ConsensusError::Io(error) => panic!("unexpected IO error {:?}", error),
ConsensusError::MissingData =>
E::custom("missing data (early end of file or slice too short)"),
ConsensusError::OversizedVectorAllocation { requested, max } => E::custom(format_args!( ConsensusError::OversizedVectorAllocation { requested, max } => E::custom(format_args!(
"the requested allocation of {} items exceeds maximum of {}", "the requested allocation of {} items exceeds maximum of {}",
requested, max requested, max