Skip to main content

eth_valkyoth_primitives/
lib.rs

1#![no_std]
2#![forbid(unsafe_code)]
3//! Core `no_std` Ethereum primitive types used across the `eth` workspace.
4
5use subtle::ConstantTimeEq as _;
6
7macro_rules! id_type {
8    ($name:ident, $inner:ty, $doc:literal) => {
9        #[doc = $doc]
10        #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
11        pub struct $name($inner);
12
13        impl $name {
14            /// Creates a new value.
15            #[must_use]
16            pub const fn new(value: $inner) -> Self {
17                Self(value)
18            }
19
20            /// Returns the raw integer value.
21            #[must_use]
22            pub const fn get(self) -> $inner {
23                self.0
24            }
25        }
26    };
27}
28
29id_type!(ChainId, u64, "Ethereum chain identifier.");
30id_type!(BlockNumber, u64, "Ethereum execution-layer block number.");
31id_type!(Gas, u64, "Gas quantity.");
32id_type!(Nonce, u64, "Account transaction nonce.");
33id_type!(UnixTimestamp, u64, "Block timestamp as Unix seconds.");
34
35/// Fixed-width Ethereum address bytes.
36#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
37pub struct Address([u8; 20]);
38
39impl Address {
40    /// Creates an address from raw bytes.
41    #[must_use]
42    pub const fn from_bytes(bytes: [u8; 20]) -> Self {
43        Self(bytes)
44    }
45
46    /// Returns the raw address bytes.
47    #[must_use]
48    pub const fn to_bytes(self) -> [u8; 20] {
49        self.0
50    }
51}
52
53/// Fixed-width 256-bit hash bytes.
54///
55/// `PartialEq` is suitable for ordinary public hash comparisons. Use
56/// [`B256::ct_eq`] when comparison timing is part of a security boundary.
57#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
58pub struct B256([u8; 32]);
59
60impl B256 {
61    /// Creates a hash from raw bytes.
62    #[must_use]
63    pub const fn from_bytes(bytes: [u8; 32]) -> Self {
64        Self(bytes)
65    }
66
67    /// Returns the raw hash bytes.
68    #[must_use]
69    pub const fn to_bytes(self) -> [u8; 32] {
70        self.0
71    }
72
73    /// Compares two hashes in constant time.
74    ///
75    /// Use this instead of `==` whenever the comparison result could influence
76    /// control flow observable by an untrusted caller.
77    #[must_use]
78    pub fn ct_eq(&self, other: &Self) -> bool {
79        self.0.ct_eq(&other.0).into()
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn chain_id_round_trips() {
89        assert_eq!(ChainId::new(1).get(), 1);
90    }
91
92    #[test]
93    fn address_round_trips() {
94        let bytes = [7_u8; 20];
95        assert_eq!(Address::from_bytes(bytes).to_bytes(), bytes);
96    }
97
98    #[test]
99    fn b256_constant_time_equality_result_matches_equality() {
100        let left = B256::from_bytes([1_u8; 32]);
101        let same = B256::from_bytes([1_u8; 32]);
102        let different = B256::from_bytes([2_u8; 32]);
103        assert!(left.ct_eq(&same));
104        assert!(!left.ct_eq(&different));
105    }
106}