Skip to main content

eth_valkyoth_protocol/
lib.rs

1#![no_std]
2#![forbid(unsafe_code)]
3//! Fork-aware Ethereum protocol validation state.
4
5#[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/// Protocol validation failure.
44#[non_exhaustive]
45#[derive(Clone, Copy, Debug, Eq, PartialEq)]
46pub enum ProtocolError {
47    /// A feature is disabled or unsupported for the selected operation.
48    Feature(FeatureError),
49    /// Fork context is missing, unsupported, or inactive.
50    Fork(ForkError),
51    /// A validation state transition was attempted out of order.
52    InvalidStateTransition,
53}
54
55impl ProtocolError {
56    /// Stable machine-readable error code.
57    #[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    /// Stable human-readable error message.
67    #[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    /// Stable high-level category for policy decisions.
79    #[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/// Stable high-level protocol error categories.
99#[non_exhaustive]
100#[derive(Clone, Copy, Debug, Eq, PartialEq)]
101pub enum ProtocolErrorCategory {
102    /// Feature support or configuration failure.
103    Feature,
104    /// Fork selection or activation failure.
105    Fork,
106    /// Validation state failure.
107    State,
108}
109
110/// Feature availability failure.
111#[non_exhaustive]
112#[derive(Clone, Copy, Debug, Eq, PartialEq)]
113pub enum FeatureError {
114    /// The feature is intentionally not enabled for this build or context.
115    Disabled,
116    /// The feature is not implemented by this crate version.
117    Unsupported,
118}
119
120impl FeatureError {
121    /// Stable machine-readable error code.
122    #[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    /// Stable human-readable error message.
131    #[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}