use core::fmt;
use eth_valkyoth_codec::{DecodeError, DecodeErrorCategory};
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ReceiptField {
Payload,
StatusOrStateRoot,
CumulativeGasUsed,
LogsBloom,
Logs,
LogAddress,
LogTopics,
LogData,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ReceiptDecodeError {
EmptyInput,
UnsupportedReceiptType {
type_byte: u8,
},
ScalarPrefix {
prefix: u8,
},
ReservedPrefix,
WrongFieldCount {
expected: usize,
found: usize,
},
InvalidLogFieldCount {
found: usize,
},
FieldDecode {
field: ReceiptField,
source: DecodeError,
},
InvalidStatusOrStateRoot {
found: usize,
},
InvalidLogsBloomLength {
found: usize,
},
InvalidLogAddressLength {
found: usize,
},
InvalidLogTopicLength {
found: usize,
},
}
impl ReceiptDecodeError {
#[must_use]
pub const fn code(self) -> &'static str {
match self {
Self::EmptyInput => "ETH_RECEIPT_EMPTY_INPUT",
Self::UnsupportedReceiptType { .. } => "ETH_RECEIPT_UNSUPPORTED_TYPE",
Self::ScalarPrefix { .. } => "ETH_RECEIPT_SCALAR_PREFIX",
Self::ReservedPrefix => "ETH_RECEIPT_RESERVED_PREFIX",
Self::WrongFieldCount { .. } => "ETH_RECEIPT_WRONG_FIELD_COUNT",
Self::InvalidLogFieldCount { .. } => "ETH_RECEIPT_INVALID_LOG_FIELD_COUNT",
Self::FieldDecode { source, .. } => source.code(),
Self::InvalidStatusOrStateRoot { .. } => "ETH_RECEIPT_INVALID_STATUS_OR_ROOT",
Self::InvalidLogsBloomLength { .. } => "ETH_RECEIPT_INVALID_BLOOM_LENGTH",
Self::InvalidLogAddressLength { .. } => "ETH_RECEIPT_INVALID_LOG_ADDRESS_LENGTH",
Self::InvalidLogTopicLength { .. } => "ETH_RECEIPT_INVALID_LOG_TOPIC_LENGTH",
}
}
#[must_use]
pub const fn message(self) -> &'static str {
match self {
Self::EmptyInput => "receipt input is empty",
Self::UnsupportedReceiptType { .. } => {
"receipt type is not supported by this receipt decoder"
}
Self::ScalarPrefix { .. } => "receipt envelope starts with an RLP scalar prefix",
Self::ReservedPrefix => "receipt envelope starts with the reserved 0xff prefix",
Self::WrongFieldCount { .. } => "receipt must contain exactly four RLP fields",
Self::InvalidLogFieldCount { .. } => "receipt log must contain exactly three fields",
Self::FieldDecode { .. } => "receipt field failed bounded decoding",
Self::InvalidStatusOrStateRoot { .. } => {
"receipt status/root field must be status 0 or 1, or a 32-byte root"
}
Self::InvalidLogsBloomLength { .. } => "receipt logs bloom must be exactly 256 bytes",
Self::InvalidLogAddressLength { .. } => "receipt log address must be exactly 20 bytes",
Self::InvalidLogTopicLength { .. } => "receipt log topic must be exactly 32 bytes",
}
}
#[must_use]
pub const fn category(self) -> ReceiptDecodeErrorCategory {
match self {
Self::UnsupportedReceiptType { .. } => ReceiptDecodeErrorCategory::Unsupported,
Self::EmptyInput
| Self::ScalarPrefix { .. }
| Self::ReservedPrefix
| Self::WrongFieldCount { .. }
| Self::InvalidLogFieldCount { .. }
| Self::InvalidStatusOrStateRoot { .. }
| Self::InvalidLogsBloomLength { .. }
| Self::InvalidLogAddressLength { .. }
| Self::InvalidLogTopicLength { .. } => ReceiptDecodeErrorCategory::MalformedInput,
Self::FieldDecode { source, .. } => match source.category() {
DecodeErrorCategory::MalformedInput => ReceiptDecodeErrorCategory::MalformedInput,
DecodeErrorCategory::ResourceExhaustion => {
ReceiptDecodeErrorCategory::ResourceExhaustion
}
_ => ReceiptDecodeErrorCategory::MalformedInput,
},
}
}
}
impl fmt::Display for ReceiptDecodeError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.message())
}
}
#[cfg(feature = "std")]
impl std::error::Error for ReceiptDecodeError {}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ReceiptDecodeErrorCategory {
MalformedInput,
Unsupported,
ResourceExhaustion,
}