#![no_std]
#![forbid(unsafe_code)]
use subtle::ConstantTimeEq as _;
macro_rules! id_type {
($name:ident, $inner:ty, $doc:literal) => {
#[doc = $doc]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct $name($inner);
impl $name {
#[must_use]
pub const fn new(value: $inner) -> Self {
Self(value)
}
#[must_use]
pub const fn get(self) -> $inner {
self.0
}
}
};
}
id_type!(ChainId, u64, "Ethereum chain identifier.");
id_type!(BlockNumber, u64, "Ethereum execution-layer block number.");
id_type!(Gas, u64, "Gas quantity.");
id_type!(Nonce, u64, "Account transaction nonce.");
id_type!(UnixTimestamp, u64, "Block timestamp as Unix seconds.");
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Address([u8; 20]);
impl Address {
#[must_use]
pub const fn from_bytes(bytes: [u8; 20]) -> Self {
Self(bytes)
}
#[must_use]
pub const fn to_bytes(self) -> [u8; 20] {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct B256([u8; 32]);
impl B256 {
#[must_use]
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
Self(bytes)
}
#[must_use]
pub const fn to_bytes(self) -> [u8; 32] {
self.0
}
#[must_use]
pub fn ct_eq(&self, other: &Self) -> bool {
self.0.ct_eq(&other.0).into()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chain_id_round_trips() {
assert_eq!(ChainId::new(1).get(), 1);
}
#[test]
fn address_round_trips() {
let bytes = [7_u8; 20];
assert_eq!(Address::from_bytes(bytes).to_bytes(), bytes);
}
#[test]
fn b256_constant_time_equality_result_matches_equality() {
let left = B256::from_bytes([1_u8; 32]);
let same = B256::from_bytes([1_u8; 32]);
let different = B256::from_bytes([2_u8; 32]);
assert!(left.ct_eq(&same));
assert!(!left.ct_eq(&different));
}
}