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_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/// Protocol validation failure.
49#[non_exhaustive]
50#[derive(Clone, Copy, Debug, Eq, PartialEq)]
51pub enum ProtocolError {
52    /// A feature is disabled or unsupported for the selected operation.
53    Feature(FeatureError),
54    /// Fork context is missing, unsupported, or inactive.
55    Fork(ForkError),
56    /// A validation state transition was attempted out of order.
57    InvalidStateTransition,
58}
59
60impl ProtocolError {
61    /// Stable machine-readable error code.
62    #[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    /// Stable human-readable error message.
72    #[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    /// Stable high-level category for policy decisions.
84    #[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/// Stable high-level protocol error categories.
104#[non_exhaustive]
105#[derive(Clone, Copy, Debug, Eq, PartialEq)]
106pub enum ProtocolErrorCategory {
107    /// Feature support or configuration failure.
108    Feature,
109    /// Fork selection or activation failure.
110    Fork,
111    /// Validation state failure.
112    State,
113}
114
115/// Feature availability failure.
116#[non_exhaustive]
117#[derive(Clone, Copy, Debug, Eq, PartialEq)]
118pub enum FeatureError {
119    /// The feature is intentionally not enabled for this build or context.
120    Disabled,
121    /// The feature is not implemented by this crate version.
122    Unsupported,
123}
124
125impl FeatureError {
126    /// Stable machine-readable error code.
127    #[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    /// Stable human-readable error message.
136    #[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}