From b04142c7459853e4e550224ad52d00f43709babb Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 18 Oct 2024 10:11:54 +1100 Subject: [PATCH] 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. --- bitcoin/src/consensus/encode.rs | 9 +++------ bitcoin/src/consensus/error.rs | 15 +++++++++++++-- bitcoin/src/consensus/serde.rs | 2 ++ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs index c64868ffe..e5aa9fdf9 100644 --- a/bitcoin/src/consensus/encode.rs +++ b/bitcoin/src/consensus/encode.rs @@ -1027,13 +1027,11 @@ mod tests { ]) .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, - // 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 = deserialize::(&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::(); test_len_is_max_vec::(); @@ -1055,11 +1053,10 @@ mod tests { Vec: Decodable, T: fmt::Debug, { - let rand_io_err = Error::Io(io::Error::new(io::ErrorKind::Other, "")); let mut buf = Vec::new(); buf.emit_compact_size(super::MAX_VEC_SIZE / mem::size_of::()).unwrap(); let err = deserialize::>(&buf).unwrap_err(); - assert_eq!(discriminant(&err), discriminant(&rand_io_err)); + assert!(matches!(err, Error::MissingData)); } #[test] diff --git a/bitcoin/src/consensus/error.rs b/bitcoin/src/consensus/error.rs index e74c54ec8..d4f16fe1e 100644 --- a/bitcoin/src/consensus/error.rs +++ b/bitcoin/src/consensus/error.rs @@ -56,6 +56,8 @@ impl std::error::Error for DecodeEr pub enum Error { /// And I/O 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. OversizedVectorAllocation { /// The capacity requested. @@ -86,6 +88,7 @@ impl fmt::Display for Error { match *self { 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 } => write!(f, "allocation of oversized vector: requested {}, maximum {}", r, m), InvalidChecksum { expected: ref e, actual: ref a } => @@ -105,7 +108,8 @@ impl std::error::Error for Error { match self { Io(e) => Some(e), - OversizedVectorAllocation { .. } + MissingData + | OversizedVectorAllocation { .. } | InvalidChecksum { .. } | NonMinimalVarInt | ParseFailed(_) @@ -115,7 +119,14 @@ impl std::error::Error for Error { } impl From 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. diff --git a/bitcoin/src/consensus/serde.rs b/bitcoin/src/consensus/serde.rs index 790dd5781..f96363f18 100644 --- a/bitcoin/src/consensus/serde.rs +++ b/bitcoin/src/consensus/serde.rs @@ -361,6 +361,8 @@ impl serde::de::Expected for DisplayExpected { fn consensus_error_into_serde(error: ConsensusError) -> E { match 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!( "the requested allocation of {} items exceeds maximum of {}", requested, max