use core::fmt;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DecodeError {
InputTooLarge,
TrailingBytes,
DecoderOverread,
Malformed,
UnexpectedList,
UnexpectedScalar,
ListTooLong,
NestingTooDeep,
AllocationExceeded,
ProofTooLarge,
ItemCountExceeded,
UnreviewedDeploymentPolicy,
LengthOverflow,
OffsetOutOfBounds,
}
impl DecodeError {
#[must_use]
pub const fn code(self) -> &'static str {
match self {
Self::InputTooLarge => "ETH_CODEC_INPUT_TOO_LARGE",
Self::TrailingBytes => "ETH_CODEC_TRAILING_BYTES",
Self::DecoderOverread => "ETH_CODEC_DECODER_OVERREAD",
Self::Malformed => "ETH_CODEC_MALFORMED",
Self::UnexpectedList => "ETH_CODEC_UNEXPECTED_LIST",
Self::UnexpectedScalar => "ETH_CODEC_UNEXPECTED_SCALAR",
Self::ListTooLong => "ETH_CODEC_LIST_TOO_LONG",
Self::NestingTooDeep => "ETH_CODEC_NESTING_TOO_DEEP",
Self::AllocationExceeded => "ETH_CODEC_ALLOCATION_EXCEEDED",
Self::ProofTooLarge => "ETH_CODEC_PROOF_TOO_LARGE",
Self::ItemCountExceeded => "ETH_CODEC_ITEM_COUNT_EXCEEDED",
Self::UnreviewedDeploymentPolicy => "ETH_CODEC_UNREVIEWED_DEPLOYMENT_POLICY",
Self::LengthOverflow => "ETH_CODEC_LENGTH_OVERFLOW",
Self::OffsetOutOfBounds => "ETH_CODEC_OFFSET_OUT_OF_BOUNDS",
}
}
#[must_use]
pub const fn message(self) -> &'static str {
match self {
Self::InputTooLarge => "input exceeds the active decode byte limit",
Self::TrailingBytes => "decoded value did not consume the full input",
Self::DecoderOverread => "decoder consumed more bytes than were available",
Self::Malformed => "input is malformed for the selected codec",
Self::UnexpectedList => "RLP list encountered where scalar was required",
Self::UnexpectedScalar => "RLP scalar encountered where list was required",
Self::ListTooLong => "decoded list exceeds the active item limit",
Self::NestingTooDeep => "decoded structure exceeds the active nesting limit",
Self::AllocationExceeded => "decoder exceeded the active allocation limit",
Self::ProofTooLarge => "proof exceeds the active proof-node limit",
Self::ItemCountExceeded => "decoder exceeded the active cumulative item limit",
Self::UnreviewedDeploymentPolicy => {
"deployment decode policy must be reviewed and tightened"
}
Self::LengthOverflow => "length or offset arithmetic overflowed",
Self::OffsetOutOfBounds => "offset or range is outside the input",
}
}
#[must_use]
pub const fn category(self) -> DecodeErrorCategory {
match self {
Self::InputTooLarge
| Self::ListTooLong
| Self::NestingTooDeep
| Self::AllocationExceeded
| Self::ProofTooLarge
| Self::ItemCountExceeded
| Self::UnreviewedDeploymentPolicy => DecodeErrorCategory::ResourceExhaustion,
Self::TrailingBytes
| Self::DecoderOverread
| Self::Malformed
| Self::UnexpectedList
| Self::UnexpectedScalar
| Self::LengthOverflow
| Self::OffsetOutOfBounds => DecodeErrorCategory::MalformedInput,
}
}
#[must_use]
pub const fn resource(self) -> Option<ResourceError> {
match self {
Self::InputTooLarge => Some(ResourceError::InputBytes),
Self::ListTooLong => Some(ResourceError::ListItems),
Self::NestingTooDeep => Some(ResourceError::NestingDepth),
Self::AllocationExceeded => Some(ResourceError::AllocationBytes),
Self::ProofTooLarge => Some(ResourceError::ProofNodes),
Self::ItemCountExceeded => Some(ResourceError::TotalItems),
Self::UnreviewedDeploymentPolicy => Some(ResourceError::DeploymentPolicy),
Self::TrailingBytes
| Self::DecoderOverread
| Self::Malformed
| Self::UnexpectedList
| Self::UnexpectedScalar
| Self::LengthOverflow
| Self::OffsetOutOfBounds => None,
}
}
}
impl fmt::Display for DecodeError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.message())
}
}
#[cfg(feature = "std")]
impl std::error::Error for DecodeError {}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DecodeErrorCategory {
MalformedInput,
ResourceExhaustion,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ResourceError {
InputBytes,
ListItems,
NestingDepth,
AllocationBytes,
ProofNodes,
TotalItems,
DeploymentPolicy,
}
impl ResourceError {
#[must_use]
pub const fn code(self) -> &'static str {
match self {
Self::InputBytes => "ETH_RESOURCE_INPUT_BYTES",
Self::ListItems => "ETH_RESOURCE_LIST_ITEMS",
Self::NestingDepth => "ETH_RESOURCE_NESTING_DEPTH",
Self::AllocationBytes => "ETH_RESOURCE_ALLOCATION_BYTES",
Self::ProofNodes => "ETH_RESOURCE_PROOF_NODES",
Self::TotalItems => "ETH_RESOURCE_TOTAL_ITEMS",
Self::DeploymentPolicy => "ETH_RESOURCE_DEPLOYMENT_POLICY",
}
}
#[must_use]
pub const fn message(self) -> &'static str {
match self {
Self::InputBytes => "input byte budget exceeded",
Self::ListItems => "list item budget exceeded",
Self::NestingDepth => "nesting depth budget exceeded",
Self::AllocationBytes => "allocation byte budget exceeded",
Self::ProofNodes => "proof-node budget exceeded",
Self::TotalItems => "total item budget exceeded",
Self::DeploymentPolicy => "deployment policy review required",
}
}
}
impl fmt::Display for ResourceError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.message())
}
}
#[cfg(feature = "std")]
impl std::error::Error for ResourceError {}
#[cfg(test)]
mod tests {
use super::*;
extern crate std;
use std::string::ToString;
#[test]
fn decode_errors_have_stable_codes_and_messages() {
assert_eq!(DecodeError::Malformed.code(), "ETH_CODEC_MALFORMED");
assert_eq!(
DecodeError::Malformed.message(),
"input is malformed for the selected codec"
);
assert_eq!(
DecodeError::Malformed.category(),
DecodeErrorCategory::MalformedInput
);
assert_eq!(
DecodeError::Malformed.to_string(),
"input is malformed for the selected codec"
);
}
#[test]
fn resource_errors_are_classified_without_payloads() {
let error = DecodeError::AllocationExceeded;
assert_eq!(error.category(), DecodeErrorCategory::ResourceExhaustion);
assert_eq!(error.resource(), Some(ResourceError::AllocationBytes));
assert_eq!(
ResourceError::AllocationBytes.code(),
"ETH_RESOURCE_ALLOCATION_BYTES"
);
assert_eq!(
ResourceError::AllocationBytes.to_string(),
"allocation byte budget exceeded"
);
}
#[test]
fn new_resource_errors_are_classified() {
assert_eq!(
DecodeError::ProofTooLarge.resource(),
Some(ResourceError::ProofNodes)
);
assert_eq!(
DecodeError::ItemCountExceeded.resource(),
Some(ResourceError::TotalItems)
);
assert_eq!(
DecodeError::UnreviewedDeploymentPolicy.resource(),
Some(ResourceError::DeploymentPolicy)
);
}
#[test]
fn arithmetic_errors_are_malformed_input() {
assert_eq!(
DecodeError::LengthOverflow.category(),
DecodeErrorCategory::MalformedInput
);
assert_eq!(DecodeError::OffsetOutOfBounds.resource(), None);
}
#[test]
fn unexpected_list_is_malformed_input() {
assert_eq!(
DecodeError::UnexpectedList.code(),
"ETH_CODEC_UNEXPECTED_LIST"
);
assert_eq!(
DecodeError::UnexpectedList.category(),
DecodeErrorCategory::MalformedInput
);
assert_eq!(DecodeError::UnexpectedList.resource(), None);
}
#[test]
fn unexpected_scalar_is_malformed_input() {
assert_eq!(
DecodeError::UnexpectedScalar.code(),
"ETH_CODEC_UNEXPECTED_SCALAR"
);
assert_eq!(
DecodeError::UnexpectedScalar.category(),
DecodeErrorCategory::MalformedInput
);
assert_eq!(DecodeError::UnexpectedScalar.resource(), None);
}
}