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, SET_CODE_AUTHORIZATION_FIELD_COUNT, SET_CODE_TRANSACTION_FIELD_COUNT,
33    SET_CODE_TRANSACTION_TYPE, SetCodeAuthorization, SetCodeAuthorizationChainId,
34    SetCodeAuthorizationField, SetCodeAuthorizationItems, SetCodeAuthorizationList,
35    SetCodeTransactionDecodeError, SetCodeTransactionDecodeErrorCategory, SetCodeTransactionField,
36    SignatureYParity, TransactionEncodeError, TransactionEncodeErrorCategory, TransactionEnvelope,
37    TransactionEnvelopeError, TransactionEnvelopeErrorCategory, TypedTransactionEnvelope,
38    UnvalidatedAccessListTransaction, UnvalidatedBlobTransaction, UnvalidatedDynamicFeeTransaction,
39    UnvalidatedLegacyTransaction, UnvalidatedSetCodeTransaction, UnvalidatedTransaction,
40    decode_access_list_transaction, decode_blob_transaction, decode_dynamic_fee_transaction,
41    decode_legacy_transaction, decode_set_code_transaction, decode_transaction_envelope,
42    encode_access_list_signing_preimage, encode_access_list_transaction,
43    encode_blob_signing_preimage, encode_blob_transaction, encode_dynamic_fee_signing_preimage,
44    encode_dynamic_fee_transaction, encode_legacy_eip155_signing_preimage,
45    encode_legacy_transaction, encode_set_code_transaction, encode_transaction,
46    encoded_access_list_signing_preimage_len, encoded_access_list_transaction_len,
47    encoded_blob_signing_preimage_len, encoded_blob_transaction_len,
48    encoded_dynamic_fee_signing_preimage_len, encoded_dynamic_fee_transaction_len,
49    encoded_legacy_eip155_signing_preimage_len, encoded_legacy_transaction_len,
50    encoded_set_code_transaction_len, encoded_transaction_len,
51};
52
53/// Protocol validation failure.
54#[non_exhaustive]
55#[derive(Clone, Copy, Debug, Eq, PartialEq)]
56pub enum ProtocolError {
57    /// A feature is disabled or unsupported for the selected operation.
58    Feature(FeatureError),
59    /// Fork context is missing, unsupported, or inactive.
60    Fork(ForkError),
61    /// A validation state transition was attempted out of order.
62    InvalidStateTransition,
63}
64
65impl ProtocolError {
66    /// Stable machine-readable error code.
67    #[must_use]
68    pub const fn code(self) -> &'static str {
69        match self {
70            Self::Feature(error) => error.code(),
71            Self::Fork(error) => error.code(),
72            Self::InvalidStateTransition => "ETH_PROTOCOL_INVALID_STATE_TRANSITION",
73        }
74    }
75
76    /// Stable human-readable error message.
77    #[must_use]
78    pub const fn message(self) -> &'static str {
79        match self {
80            Self::Feature(error) => error.message(),
81            Self::Fork(error) => error.message(),
82            Self::InvalidStateTransition => {
83                "validation state transition is not allowed from the current state"
84            }
85        }
86    }
87
88    /// Stable high-level category for policy decisions.
89    #[must_use]
90    pub const fn category(self) -> ProtocolErrorCategory {
91        match self {
92            Self::Feature(_) => ProtocolErrorCategory::Feature,
93            Self::Fork(_) => ProtocolErrorCategory::Fork,
94            Self::InvalidStateTransition => ProtocolErrorCategory::State,
95        }
96    }
97}
98
99impl fmt::Display for ProtocolError {
100    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
101        formatter.write_str(self.message())
102    }
103}
104
105#[cfg(feature = "std")]
106impl std::error::Error for ProtocolError {}
107
108/// Stable high-level protocol error categories.
109#[non_exhaustive]
110#[derive(Clone, Copy, Debug, Eq, PartialEq)]
111pub enum ProtocolErrorCategory {
112    /// Feature support or configuration failure.
113    Feature,
114    /// Fork selection or activation failure.
115    Fork,
116    /// Validation state failure.
117    State,
118}
119
120/// Feature availability failure.
121#[non_exhaustive]
122#[derive(Clone, Copy, Debug, Eq, PartialEq)]
123pub enum FeatureError {
124    /// The feature is intentionally not enabled for this build or context.
125    Disabled,
126    /// The feature is not implemented by this crate version.
127    Unsupported,
128}
129
130impl FeatureError {
131    /// Stable machine-readable error code.
132    #[must_use]
133    pub const fn code(self) -> &'static str {
134        match self {
135            Self::Disabled => "ETH_FEATURE_DISABLED",
136            Self::Unsupported => "ETH_FEATURE_UNSUPPORTED",
137        }
138    }
139
140    /// Stable human-readable error message.
141    #[must_use]
142    pub const fn message(self) -> &'static str {
143        match self {
144            Self::Disabled => "feature is disabled for this operation",
145            Self::Unsupported => "feature is not supported by this crate version",
146        }
147    }
148}
149
150impl fmt::Display for FeatureError {
151    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
152        formatter.write_str(self.message())
153    }
154}
155
156#[cfg(feature = "std")]
157impl std::error::Error for FeatureError {}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162    extern crate std;
163    use std::string::ToString;
164
165    #[test]
166    fn protocol_errors_have_stable_codes_and_categories() {
167        let error = ProtocolError::Fork(ForkError::Inactive);
168
169        assert_eq!(error.code(), "ETH_FORK_INACTIVE");
170        assert_eq!(
171            error.message(),
172            "fork is not active for the validation context"
173        );
174        assert_eq!(error.category(), ProtocolErrorCategory::Fork);
175        assert_eq!(
176            error.to_string(),
177            "fork is not active for the validation context"
178        );
179    }
180
181    #[test]
182    fn feature_errors_format_without_payloads() {
183        let error = ProtocolError::Feature(FeatureError::Unsupported);
184
185        assert_eq!(error.code(), "ETH_FEATURE_UNSUPPORTED");
186        assert_eq!(
187            FeatureError::Unsupported.to_string(),
188            "feature is not supported by this crate version"
189        );
190    }
191}