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_signing_preimage,
38 encode_access_list_transaction, encode_blob_signing_preimage, encode_blob_transaction,
39 encode_dynamic_fee_signing_preimage, encode_dynamic_fee_transaction,
40 encode_legacy_eip155_signing_preimage, encode_legacy_transaction, encode_transaction,
41 encoded_access_list_signing_preimage_len, encoded_access_list_transaction_len,
42 encoded_blob_signing_preimage_len, encoded_blob_transaction_len,
43 encoded_dynamic_fee_signing_preimage_len, encoded_dynamic_fee_transaction_len,
44 encoded_legacy_eip155_signing_preimage_len, encoded_legacy_transaction_len,
45 encoded_transaction_len,
46};
47
48#[non_exhaustive]
50#[derive(Clone, Copy, Debug, Eq, PartialEq)]
51pub enum ProtocolError {
52 Feature(FeatureError),
54 Fork(ForkError),
56 InvalidStateTransition,
58}
59
60impl ProtocolError {
61 #[must_use]
63 pub const fn code(self) -> &'static str {
64 match self {
65 Self::Feature(error) => error.code(),
66 Self::Fork(error) => error.code(),
67 Self::InvalidStateTransition => "ETH_PROTOCOL_INVALID_STATE_TRANSITION",
68 }
69 }
70
71 #[must_use]
73 pub const fn message(self) -> &'static str {
74 match self {
75 Self::Feature(error) => error.message(),
76 Self::Fork(error) => error.message(),
77 Self::InvalidStateTransition => {
78 "validation state transition is not allowed from the current state"
79 }
80 }
81 }
82
83 #[must_use]
85 pub const fn category(self) -> ProtocolErrorCategory {
86 match self {
87 Self::Feature(_) => ProtocolErrorCategory::Feature,
88 Self::Fork(_) => ProtocolErrorCategory::Fork,
89 Self::InvalidStateTransition => ProtocolErrorCategory::State,
90 }
91 }
92}
93
94impl fmt::Display for ProtocolError {
95 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
96 formatter.write_str(self.message())
97 }
98}
99
100#[cfg(feature = "std")]
101impl std::error::Error for ProtocolError {}
102
103#[non_exhaustive]
105#[derive(Clone, Copy, Debug, Eq, PartialEq)]
106pub enum ProtocolErrorCategory {
107 Feature,
109 Fork,
111 State,
113}
114
115#[non_exhaustive]
117#[derive(Clone, Copy, Debug, Eq, PartialEq)]
118pub enum FeatureError {
119 Disabled,
121 Unsupported,
123}
124
125impl FeatureError {
126 #[must_use]
128 pub const fn code(self) -> &'static str {
129 match self {
130 Self::Disabled => "ETH_FEATURE_DISABLED",
131 Self::Unsupported => "ETH_FEATURE_UNSUPPORTED",
132 }
133 }
134
135 #[must_use]
137 pub const fn message(self) -> &'static str {
138 match self {
139 Self::Disabled => "feature is disabled for this operation",
140 Self::Unsupported => "feature is not supported by this crate version",
141 }
142 }
143}
144
145impl fmt::Display for FeatureError {
146 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
147 formatter.write_str(self.message())
148 }
149}
150
151#[cfg(feature = "std")]
152impl std::error::Error for FeatureError {}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 extern crate std;
158 use std::string::ToString;
159
160 #[test]
161 fn protocol_errors_have_stable_codes_and_categories() {
162 let error = ProtocolError::Fork(ForkError::Inactive);
163
164 assert_eq!(error.code(), "ETH_FORK_INACTIVE");
165 assert_eq!(
166 error.message(),
167 "fork is not active for the validation context"
168 );
169 assert_eq!(error.category(), ProtocolErrorCategory::Fork);
170 assert_eq!(
171 error.to_string(),
172 "fork is not active for the validation context"
173 );
174 }
175
176 #[test]
177 fn feature_errors_format_without_payloads() {
178 let error = ProtocolError::Feature(FeatureError::Unsupported);
179
180 assert_eq!(error.code(), "ETH_FEATURE_UNSUPPORTED");
181 assert_eq!(
182 FeatureError::Unsupported.to_string(),
183 "feature is not supported by this crate version"
184 );
185 }
186}