Skip to content

Commit

Permalink
feat(primitives): add Keccak256 hasher struct
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes committed Dec 28, 2023
1 parent e473bb2 commit 4cc9237
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 38 deletions.
11 changes: 6 additions & 5 deletions crates/primitives/src/bits/address.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{aliases::U160, utils::keccak256, wrap_fixed_bytes, FixedBytes, Hasher};
use crate::{aliases::U160, utils::keccak256, wrap_fixed_bytes, FixedBytes};
use alloc::{
borrow::Borrow,
string::{String, ToString},
Expand Down Expand Up @@ -229,16 +229,17 @@ impl Address {
buf[1] = b'x';
hex::encode_to_slice(self, &mut buf[2..]).unwrap();

let mut hasher = crate::Keccak::v256();
let mut hasher = crate::Keccak256::new();
match chain_id {
Some(chain_id) => {
hasher.update(itoa::Buffer::new().format(chain_id).as_bytes());
hasher.update(buf);
// Clippy suggests an unnecessary copy.
#[allow(clippy::needless_borrows_for_generic_args)]
hasher.update(&*buf);
}
None => hasher.update(&buf[2..]),
}
let mut hash = [0u8; 32];
hasher.finalize(&mut hash);
let hash = hasher.finalize();

let mut hash_hex = [0u8; 64];
hex::encode_to_slice(hash, &mut hash_hex).unwrap();
Expand Down
9 changes: 3 additions & 6 deletions crates/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#[macro_use]
extern crate alloc;

use tiny_keccak as _;

#[cfg(feature = "postgres")]
pub mod postgres;

Expand Down Expand Up @@ -51,21 +53,16 @@ mod signed;
pub use signed::{BigIntConversionError, ParseSignedError, Sign, Signed};

pub mod utils;
pub use utils::{eip191_hash_message, keccak256};
pub use utils::{eip191_hash_message, keccak256, Keccak256};

#[doc(no_inline)]
pub use {
::bytes,
::hex,
hex_literal::{self, hex},
ruint::{self, Uint},
tiny_keccak::{self, Hasher, Keccak},
};

#[cfg(feature = "asm-keccak")]
#[doc(no_inline)]
pub use keccak_asm::{self, digest, Keccak256};

/// Re-export of [`ruint::uint!`] for convenience. Note that users of this macro
/// must also add [`ruint`] to their `Cargo.toml` as a dependency.
#[doc(inline)]
Expand Down
133 changes: 120 additions & 13 deletions crates/primitives/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@
use crate::B256;
use alloc::vec::Vec;
use core::mem::MaybeUninit;
use cfg_if::cfg_if;
use core::{fmt, mem::MaybeUninit};

mod units;
pub use units::{
format_ether, format_units, parse_ether, parse_units, ParseUnits, Unit, UnitsError,
};

cfg_if! {
if #[cfg(all(feature = "asm-keccak", not(miri)))] {
use keccak_asm::Digest as _;
} else {
use tiny_keccak::Hasher as _;
}
}

#[doc(hidden)]
#[deprecated(since = "0.5.0", note = "use `Unit::ETHER.wei()` instead")]
pub const WEI_IN_ETHER: crate::U256 = Unit::ETHER.wei_const();
Expand Down Expand Up @@ -61,7 +70,7 @@ pub fn keccak256<T: AsRef<[u8]>>(bytes: T) -> B256 {
fn keccak256(bytes: &[u8]) -> B256 {
let mut output = MaybeUninit::<B256>::uninit();

cfg_if::cfg_if! {
cfg_if! {
if #[cfg(all(feature = "native-keccak", not(feature = "tiny-keccak"), not(miri)))] {
#[link(wasm_import_module = "vm_hooks")]
extern "C" {
Expand All @@ -84,20 +93,11 @@ pub fn keccak256<T: AsRef<[u8]>>(bytes: T) -> B256 {

// SAFETY: The output is 32-bytes, and the input comes from a slice.
unsafe { native_keccak256(bytes.as_ptr(), bytes.len(), output.as_mut_ptr().cast::<u8>()) };
} else if #[cfg(all(feature = "asm-keccak", not(miri)))] {
use keccak_asm::{digest::Digest, Keccak256};

let mut hasher = Keccak256::new();
hasher.update(bytes);
// SAFETY: Never reads from `output`.
hasher.finalize_into(unsafe { (&mut (*output.as_mut_ptr()).0).into() });
} else {
use tiny_keccak::{Hasher, Keccak};

let mut hasher = Keccak::v256();
let mut hasher = Keccak256::new();
hasher.update(bytes);
// SAFETY: Never reads from `output`.
hasher.finalize(unsafe { &mut (*output.as_mut_ptr()).0 });
unsafe { hasher.finalize_into_raw(output.as_mut_ptr().cast()) };
}
}

Expand All @@ -108,6 +108,102 @@ pub fn keccak256<T: AsRef<[u8]>>(bytes: T) -> B256 {
keccak256(bytes.as_ref())
}

/// Simple [`Keccak-256`] hasher.
///
/// Note that the "native-keccak" feature is not supported for this struct, and will default to the
/// [`tiny_keccak`] implementation.
///
/// [`Keccak-256`]: https://en.wikipedia.org/wiki/SHA-3
#[derive(Clone)]
pub struct Keccak256 {
#[cfg(all(feature = "asm-keccak", not(miri)))]
hasher: keccak_asm::Keccak256,
#[cfg(not(all(feature = "asm-keccak", not(miri))))]
hasher: tiny_keccak::Keccak,
}

impl Default for Keccak256 {
#[inline]
fn default() -> Self {
Self::new()
}
}

impl fmt::Debug for Keccak256 {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Keccak256").finish_non_exhaustive()
}
}

impl Keccak256 {
/// Creates a new [`Keccak256`] hasher.
#[inline]
pub fn new() -> Self {
cfg_if! {
if #[cfg(all(feature = "asm-keccak", not(miri)))] {
let hasher = keccak_asm::Keccak256::new();
} else {
let hasher = tiny_keccak::Keccak::v256();
}
}
Self { hasher }
}

/// Absorbs additional input. Can be called multiple times.
#[inline]
pub fn update(&mut self, bytes: impl AsRef<[u8]>) {
self.hasher.update(bytes.as_ref());
}

/// Pad and squeeze the state.
#[inline]
pub fn finalize(self) -> B256 {
let mut output = MaybeUninit::<B256>::uninit();
// SAFETY: The output is 32-bytes.
unsafe { self.finalize_into_raw(output.as_mut_ptr().cast()) };
// SAFETY: Initialized above.
unsafe { output.assume_init() }
}

/// Pad and squeeze the state into `output`.
///
/// # Panics
///
/// Panics if `output` is not 32 bytes long.
#[inline]
#[track_caller]
pub fn finalize_into(self, output: &mut [u8]) {
self.finalize_into_array(output.try_into().unwrap())
}

/// Pad and squeeze the state into `output`.
///
/// # Panics
///
/// Panics if `output` is not 32 bytes long.
#[inline]
pub fn finalize_into_array(self, output: &mut [u8; 32]) {
cfg_if! {
if #[cfg(all(feature = "asm-keccak", not(miri)))] {
self.hasher.finalize_into(output.into());
} else {
self.hasher.finalize(output);
}
}
}

/// Pad and squeeze the state into `output`.
///
/// # Safety
///
/// `output` must point to a buffer that is at least 32-bytes long.
#[inline]
pub unsafe fn finalize_into_raw(self, output: *mut u8) {
self.finalize_into_array(&mut *output.cast::<[u8; 32]>())
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -126,4 +222,15 @@ mod tests {
assert_eq!(hash, b256!("a1de988600a42c4b4ab089b619297c17d53cffae5d5120d82d8a92d0bb3b78f2"));
assert_eq!(eip191_hash_message(msg), hash);
}

#[test]
fn keccak256_hasher() {
let mut hasher = Keccak256::new();
hasher.update(b"hello");
hasher.update(b" world");
let mut hash = [0u8; 32];
hasher.finalize_into(&mut hash);
assert_eq!(hash, b256!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"));
assert_eq!(hash, keccak256("hello world"));
}
}
11 changes: 4 additions & 7 deletions crates/sol-types/src/eip712.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,10 @@ impl Eip712Domain {
/// Hashes this domain according to [EIP-712 `hashStruct`](https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct).
#[inline]
pub fn hash_struct(&self) -> B256 {
use alloy_primitives::Hasher;
let mut hasher = alloy_primitives::Keccak::v256();
hasher.update(self.type_hash().as_ref());
hasher.update(&self.encode_data());
let mut out = B256::ZERO;
hasher.finalize(out.as_mut());
out
let mut hasher = alloy_primitives::Keccak256::new();
hasher.update(self.type_hash());
hasher.update(self.encode_data());
hasher.finalize()
}
}

Expand Down
11 changes: 4 additions & 7 deletions crates/sol-types/src/types/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,10 @@ pub trait SolStruct: SolType<RustType = Self> {
/// Hashes this struct according to [EIP-712 `hashStruct`](https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct).
#[inline]
fn eip712_hash_struct(&self) -> B256 {
use alloy_primitives::Hasher;
let mut hasher = alloy_primitives::Keccak::v256();
hasher.update(self.eip712_type_hash().as_ref());
hasher.update(&self.eip712_encode_data());
let mut out = B256::ZERO;
hasher.finalize(out.as_mut());
out
let mut hasher = alloy_primitives::Keccak256::new();
hasher.update(self.eip712_type_hash());
hasher.update(self.eip712_encode_data());
hasher.finalize()
}

/// Does something.
Expand Down

0 comments on commit 4cc9237

Please sign in to comment.