1#![no_std]
2#![forbid(unsafe_code)]
3#[cfg(feature = "std")]
6extern crate std;
7
8use core::fmt;
9
10mod fork;
11mod state;
12mod transaction;
13
14pub use fork::{ChainSpec, ForkActivation, ForkError, ForkSpec, Hardfork, ValidationContext};
15pub use state::{
16 Canonical, CanonicalValidationProof, Decoded, ForkValidated, ForkValidationProof,
17 SenderRecovered, SenderRecoveryProof, StateTransitionError, Transaction,
18};
19pub use transaction::{
20 ACCESS_LIST_TRANSACTION_FIELD_COUNT, ACCESS_LIST_TRANSACTION_TYPE, AccessList,
21 AccessListEntries, AccessListEntry, AccessListStorageKeyItems, AccessListStorageKeys,
22 AccessListTransactionDecodeError, AccessListTransactionDecodeErrorCategory,
23 AccessListTransactionField, AccessListTransactionTo, BLOB_TRANSACTION_FIELD_COUNT,
24 BLOB_TRANSACTION_TYPE, BlobTransactionDecodeError, BlobTransactionDecodeErrorCategory,
25 BlobTransactionField, BlobVersionedHashItems, BlobVersionedHashes,
26 DYNAMIC_FEE_TRANSACTION_FIELD_COUNT, DYNAMIC_FEE_TRANSACTION_TYPE,
27 DynamicFeeTransactionDecodeError, DynamicFeeTransactionDecodeErrorCategory,
28 DynamicFeeTransactionField, DynamicFeeTransactionTo, EIP_2718_MAX_TYPED_PREFIX,
29 EIP_2718_RESERVED_PREFIX, EIP_2718_SCALAR_PREFIX_START, EIP_2718_TYPED_ZERO_PREFIX,
30 InvalidSignatureYParity, LEGACY_TRANSACTION_FIELD_COUNT, LEGACY_TRANSACTION_PREFIX_START,
31 LegacyTransactionDecodeError, LegacyTransactionDecodeErrorCategory, LegacyTransactionField,
32 LegacyTransactionTo, SignatureYParity, TransactionEncodeError, TransactionEncodeErrorCategory,
33 TransactionEnvelope, TransactionEnvelopeError, TransactionEnvelopeErrorCategory,
34 TypedTransactionEnvelope, UnvalidatedAccessListTransaction, UnvalidatedBlobTransaction,
35 UnvalidatedDynamicFeeTransaction, UnvalidatedLegacyTransaction, UnvalidatedTransaction,
36 decode_access_list_transaction, decode_blob_transaction, decode_dynamic_fee_transaction,
37 decode_legacy_transaction, decode_transaction_envelope, encode_access_list_transaction,
38 encode_blob_transaction, encode_dynamic_fee_transaction, encode_legacy_transaction,
39 encode_transaction, encoded_access_list_transaction_len, encoded_blob_transaction_len,
40 encoded_dynamic_fee_transaction_len, encoded_legacy_transaction_len, encoded_transaction_len,
41};
42
43#[non_exhaustive]
45#[derive(Clone, Copy, Debug, Eq, PartialEq)]
46pub enum ProtocolError {
47 Feature(FeatureError),
49 Fork(ForkError),
51 InvalidStateTransition,
53}
54
55impl ProtocolError {
56 #[must_use]
58 pub const fn code(self) -> &'static str {
59 match self {
60 Self::Feature(error) => error.code(),
61 Self::Fork(error) => error.code(),
62 Self::InvalidStateTransition => "ETH_PROTOCOL_INVALID_STATE_TRANSITION",
63 }
64 }
65
66 #[must_use]
68 pub const fn message(self) -> &'static str {
69 match self {
70 Self::Feature(error) => error.message(),
71 Self::Fork(error) => error.message(),
72 Self::InvalidStateTransition => {
73 "validation state transition is not allowed from the current state"
74 }
75 }
76 }
77
78 #[must_use]
80 pub const fn category(self) -> ProtocolErrorCategory {
81 match self {
82 Self::Feature(_) => ProtocolErrorCategory::Feature,
83 Self::Fork(_) => ProtocolErrorCategory::Fork,
84 Self::InvalidStateTransition => ProtocolErrorCategory::State,
85 }
86 }
87}
88
89impl fmt::Display for ProtocolError {
90 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
91 formatter.write_str(self.message())
92 }
93}
94
95#[cfg(feature = "std")]
96impl std::error::Error for ProtocolError {}
97
98#[non_exhaustive]
100#[derive(Clone, Copy, Debug, Eq, PartialEq)]
101pub enum ProtocolErrorCategory {
102 Feature,
104 Fork,
106 State,
108}
109
110#[non_exhaustive]
112#[derive(Clone, Copy, Debug, Eq, PartialEq)]
113pub enum FeatureError {
114 Disabled,
116 Unsupported,
118}
119
120impl FeatureError {
121 #[must_use]
123 pub const fn code(self) -> &'static str {
124 match self {
125 Self::Disabled => "ETH_FEATURE_DISABLED",
126 Self::Unsupported => "ETH_FEATURE_UNSUPPORTED",
127 }
128 }
129
130 #[must_use]
132 pub const fn message(self) -> &'static str {
133 match self {
134 Self::Disabled => "feature is disabled for this operation",
135 Self::Unsupported => "feature is not supported by this crate version",
136 }
137 }
138}
139
140impl fmt::Display for FeatureError {
141 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
142 formatter.write_str(self.message())
143 }
144}
145
146#[cfg(feature = "std")]
147impl std::error::Error for FeatureError {}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 extern crate std;
153 use std::string::ToString;
154
155 #[test]
156 fn protocol_errors_have_stable_codes_and_categories() {
157 let error = ProtocolError::Fork(ForkError::Inactive);
158
159 assert_eq!(error.code(), "ETH_FORK_INACTIVE");
160 assert_eq!(
161 error.message(),
162 "fork is not active for the validation context"
163 );
164 assert_eq!(error.category(), ProtocolErrorCategory::Fork);
165 assert_eq!(
166 error.to_string(),
167 "fork is not active for the validation context"
168 );
169 }
170
171 #[test]
172 fn feature_errors_format_without_payloads() {
173 let error = ProtocolError::Feature(FeatureError::Unsupported);
174
175 assert_eq!(error.code(), "ETH_FEATURE_UNSUPPORTED");
176 assert_eq!(
177 FeatureError::Unsupported.to_string(),
178 "feature is not supported by this crate version"
179 );
180 }
181}