Return DeserError from encode::deserialize
The `encode::deserialize` function never returns an I/O error. Add a new error type that expresses this.
This commit is contained in:
parent
33566ac58c
commit
713196be0d
|
@ -1486,7 +1486,7 @@ mod tests {
|
||||||
let tx_bytes = hex!("0000fd000001021921212121212121212121f8b372b0239cc1dff600000000004f4f4f4f4f4f4f4f000000000000000000000000000000333732343133380d000000000000000000000000000000ff000000000009000dff000000000000000800000000000000000d");
|
let tx_bytes = hex!("0000fd000001021921212121212121212121f8b372b0239cc1dff600000000004f4f4f4f4f4f4f4f000000000000000000000000000000333732343133380d000000000000000000000000000000ff000000000009000dff000000000000000800000000000000000d");
|
||||||
let tx: Result<Transaction, _> = deserialize(&tx_bytes);
|
let tx: Result<Transaction, _> = deserialize(&tx_bytes);
|
||||||
assert!(tx.is_err());
|
assert!(tx.is_err());
|
||||||
assert!(matches!(tx.unwrap_err(), crate::consensus::Error::Parse(_)));
|
assert!(matches!(tx.unwrap_err(), crate::consensus::DeserializeError::Parse(_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -36,7 +36,7 @@ use crate::taproot::TapLeafHash;
|
||||||
use crate::transaction::{Transaction, TxIn, TxOut};
|
use crate::transaction::{Transaction, TxIn, TxOut};
|
||||||
|
|
||||||
#[rustfmt::skip] // Keep public re-exports separate.
|
#[rustfmt::skip] // Keep public re-exports separate.
|
||||||
pub use super::{Error, FromHexError, ParseError};
|
pub use super::{Error, FromHexError, ParseError, DeserializeError};
|
||||||
|
|
||||||
/// Encodes an object into a vector.
|
/// Encodes an object into a vector.
|
||||||
pub fn serialize<T: Encodable + ?Sized>(data: &T) -> Vec<u8> {
|
pub fn serialize<T: Encodable + ?Sized>(data: &T) -> Vec<u8> {
|
||||||
|
@ -53,14 +53,14 @@ pub fn serialize_hex<T: Encodable + ?Sized>(data: &T) -> String {
|
||||||
|
|
||||||
/// Deserializes an object from a vector, will error if said deserialization
|
/// Deserializes an object from a vector, will error if said deserialization
|
||||||
/// doesn't consume the entire vector.
|
/// doesn't consume the entire vector.
|
||||||
pub fn deserialize<T: Decodable>(data: &[u8]) -> Result<T, Error> {
|
pub fn deserialize<T: Decodable>(data: &[u8]) -> Result<T, DeserializeError> {
|
||||||
let (rv, consumed) = deserialize_partial(data)?;
|
let (rv, consumed) = deserialize_partial(data)?;
|
||||||
|
|
||||||
// Fail if data are not consumed entirely.
|
// Fail if data are not consumed entirely.
|
||||||
if consumed == data.len() {
|
if consumed == data.len() {
|
||||||
Ok(rv)
|
Ok(rv)
|
||||||
} else {
|
} else {
|
||||||
Err(super::parse_failed_error("data not consumed entirely when explicitly deserializing"))
|
Err(DeserializeError::Unconsumed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,10 +74,15 @@ pub fn deserialize_hex<T: Decodable>(hex: &str) -> Result<T, FromHexError> {
|
||||||
|
|
||||||
/// Deserializes an object from a vector, but will not report an error if said deserialization
|
/// Deserializes an object from a vector, but will not report an error if said deserialization
|
||||||
/// doesn't consume the entire vector.
|
/// doesn't consume the entire vector.
|
||||||
pub fn deserialize_partial<T: Decodable>(data: &[u8]) -> Result<(T, usize), Error> {
|
pub fn deserialize_partial<T: Decodable>(data: &[u8]) -> Result<(T, usize), ParseError> {
|
||||||
let mut decoder = Cursor::new(data);
|
let mut decoder = Cursor::new(data);
|
||||||
|
|
||||||
let rv = Decodable::consensus_decode_from_finite_reader(&mut decoder)?;
|
let rv = match Decodable::consensus_decode_from_finite_reader(&mut decoder) {
|
||||||
|
Ok(rv) => rv,
|
||||||
|
Err(Error::Parse(e)) => return Err(e),
|
||||||
|
Err(Error::Io(_)) =>
|
||||||
|
unreachable!("consensus_decode code never returns an I/O error for in-memory reads"),
|
||||||
|
};
|
||||||
let consumed = decoder.position() as usize;
|
let consumed = decoder.position() as usize;
|
||||||
|
|
||||||
Ok((rv, consumed))
|
Ok((rv, consumed))
|
||||||
|
@ -1033,10 +1038,7 @@ mod tests {
|
||||||
// by making sure it fails with `MissingData` 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();
|
||||||
match err {
|
assert_eq!(err, DeserializeError::Parse(ParseError::MissingData));
|
||||||
Error::Io(e) => panic!("unexpected I/O error {}", e),
|
|
||||||
Error::Parse(e) => assert_eq!(e, ParseError::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>();
|
||||||
|
@ -1061,10 +1063,7 @@ mod tests {
|
||||||
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();
|
||||||
match err {
|
assert_eq!(err, DeserializeError::Parse(ParseError::MissingData));
|
||||||
Error::Io(e) => panic!("unexpected I/O error {}", e),
|
|
||||||
Error::Parse(e) => assert_eq!(e, ParseError::MissingData),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -11,6 +11,45 @@ use internals::write_err;
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
use super::IterReader;
|
use super::IterReader;
|
||||||
|
|
||||||
|
/// Error deserializing from a slice.
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum DeserializeError {
|
||||||
|
/// Error parsing encoded object.
|
||||||
|
Parse(ParseError),
|
||||||
|
/// Data unconsumed error.
|
||||||
|
Unconsumed,
|
||||||
|
}
|
||||||
|
|
||||||
|
internals::impl_from_infallible!(DeserializeError);
|
||||||
|
|
||||||
|
impl fmt::Display for DeserializeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use DeserializeError::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Parse(ref e) => write_err!(f, "error parsing encoded object"; e),
|
||||||
|
Unconsumed => write!(f, "data not consumed entirely when deserializing"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl std::error::Error for DeserializeError {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
use DeserializeError::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Parse(ref e) => Some(e),
|
||||||
|
Unconsumed => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ParseError> for DeserializeError {
|
||||||
|
fn from(e: ParseError) -> Self { Self::Parse(e) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Error when consensus decoding from an `[IterReader]`.
|
/// Error when consensus decoding from an `[IterReader]`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DecodeError<E> {
|
pub enum DecodeError<E> {
|
||||||
|
|
|
@ -20,7 +20,7 @@ use crate::consensus;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use self::{
|
pub use self::{
|
||||||
encode::{deserialize, deserialize_partial, serialize, Decodable, Encodable, ReadExt, WriteExt},
|
encode::{deserialize, deserialize_partial, serialize, Decodable, Encodable, ReadExt, WriteExt},
|
||||||
error::{Error, FromHexError, DecodeError, ParseError},
|
error::{Error, FromHexError, DecodeError, ParseError, DeserializeError},
|
||||||
};
|
};
|
||||||
pub(crate) use self::error::parse_failed_error;
|
pub(crate) use self::error::parse_failed_error;
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,10 @@ pub enum Error {
|
||||||
CombineInconsistentKeySources(Box<Xpub>),
|
CombineInconsistentKeySources(Box<Xpub>),
|
||||||
/// Serialization error in bitcoin consensus-encoded structures
|
/// Serialization error in bitcoin consensus-encoded structures
|
||||||
ConsensusEncoding(encode::Error),
|
ConsensusEncoding(encode::Error),
|
||||||
|
/// Deserialization error in bitcoin consensus-encoded structures.
|
||||||
|
ConsensusDeserialize(encode::DeserializeError),
|
||||||
|
/// Error parsing bitcoin consensus-encoded object.
|
||||||
|
ConsensusParse(encode::ParseError),
|
||||||
/// Negative fee
|
/// Negative fee
|
||||||
NegativeFee,
|
NegativeFee,
|
||||||
/// Integer overflow in fee calculation
|
/// Integer overflow in fee calculation
|
||||||
|
@ -141,6 +145,9 @@ impl fmt::Display for Error {
|
||||||
write!(f, "combine conflict: {}", s)
|
write!(f, "combine conflict: {}", s)
|
||||||
}
|
}
|
||||||
ConsensusEncoding(ref e) => write_err!(f, "bitcoin consensus encoding error"; e),
|
ConsensusEncoding(ref e) => write_err!(f, "bitcoin consensus encoding error"; e),
|
||||||
|
ConsensusDeserialize(ref e) =>
|
||||||
|
write_err!(f, "bitcoin consensus deserializaton error"; e),
|
||||||
|
ConsensusParse(ref e) => write_err!(f, "error parsing bitcoin consensus encoded object"; e),
|
||||||
NegativeFee => f.write_str("PSBT has a negative fee which is not allowed"),
|
NegativeFee => f.write_str("PSBT has a negative fee which is not allowed"),
|
||||||
FeeOverflow => f.write_str("integer overflow in fee calculation"),
|
FeeOverflow => f.write_str("integer overflow in fee calculation"),
|
||||||
InvalidPublicKey(ref e) => write_err!(f, "invalid public key"; e),
|
InvalidPublicKey(ref e) => write_err!(f, "invalid public key"; e),
|
||||||
|
@ -169,6 +176,8 @@ impl std::error::Error for Error {
|
||||||
match *self {
|
match *self {
|
||||||
InvalidHash(ref e) => Some(e),
|
InvalidHash(ref e) => Some(e),
|
||||||
ConsensusEncoding(ref e) => Some(e),
|
ConsensusEncoding(ref e) => Some(e),
|
||||||
|
ConsensusDeserialize(ref e) => Some(e),
|
||||||
|
ConsensusParse(ref e) => Some(e),
|
||||||
Io(ref e) => Some(e),
|
Io(ref e) => Some(e),
|
||||||
InvalidMagic
|
InvalidMagic
|
||||||
| MissingUtxo
|
| MissingUtxo
|
||||||
|
@ -211,6 +220,14 @@ impl From<encode::Error> for Error {
|
||||||
fn from(e: encode::Error) -> Self { Error::ConsensusEncoding(e) }
|
fn from(e: encode::Error) -> Self { Error::ConsensusEncoding(e) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<encode::DeserializeError> for Error {
|
||||||
|
fn from(e: encode::DeserializeError) -> Self { Error::ConsensusDeserialize(e) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<encode::ParseError> for Error {
|
||||||
|
fn from(e: encode::ParseError) -> Self { Error::ConsensusParse(e) }
|
||||||
|
}
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
impl From<io::Error> for Error {
|
||||||
fn from(e: io::Error) -> Self { Error::Io(e) }
|
fn from(e: io::Error) -> Self { Error::Io(e) }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue