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