diff options
| author | Santo Cariotti <santo@dcariotti.me> | 2025-06-23 07:54:32 +0000 |
|---|---|---|
| committer | Santo Cariotti <santo@dcariotti.me> | 2025-06-23 07:54:32 +0000 |
| commit | 9ec408203a1a13d2b8e450f7601b994070f0c91d (patch) | |
| tree | b51dc940fa549c6cfd5b02f1e11c1af5f9488463 | |
| parent | be8ec280a056be688913c1a35c589ef406f72f50 (diff) | |
Add `Blake3Hasher`
| -rw-r--r-- | Cargo.lock | 47 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | benches/bigfile.rs | 29 | ||||
| -rw-r--r-- | src/hasher.rs | 72 |
4 files changed, 125 insertions, 24 deletions
@@ -24,12 +24,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] +name = "blake3" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -51,6 +76,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] +name = "cc" +version = "1.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +dependencies = [ + "shlex", +] + +[[package]] name = "cfg-if" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -109,6 +143,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] name = "cpufeatures" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -309,6 +349,7 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" name = "mt-rs" version = "0.1.0" dependencies = [ + "blake3", "criterion", "hex", "rand", @@ -546,6 +587,12 @@ dependencies = [ ] [[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] name = "syn" version = "2.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5,6 +5,7 @@ version = "0.1.0" edition = "2024" [dependencies] +blake3 = "1.8.2" hex = { version = "0.4.3" } sha2 = { version = "0.10.9" } sha3 = "0.10.8" diff --git a/benches/bigfile.rs b/benches/bigfile.rs index 4a092e3..b2b013d 100644 --- a/benches/bigfile.rs +++ b/benches/bigfile.rs @@ -1,6 +1,6 @@ use criterion::{Criterion, criterion_group, criterion_main}; use mt_rs::{ - hasher::{Hasher, Keccak512Hasher, SHA256Hasher}, + hasher::{Blake3Hasher, Hasher, Keccak256Hasher, SHA256Hasher}, merkletree::MerkleTree, proof::{DefaultProofer, Proofer}, }; @@ -79,14 +79,26 @@ fn bench_large_merkle_tree_sha256(c: &mut Criterion) { cleanup_files(&filenames).expect("failed to deallocate data"); }, ); + } + group.finish(); +} + +/// Example of a MarkleTree with 10 nodes which use Keccak256 algorithm to make hashes. +/// Each node has a size of 5, 10 or 15 MB. +/// Also, it verifies each node path with a proofer O(n). +fn bench_large_merkle_tree_keccak256(c: &mut Criterion) { + let filenames: Vec<String> = (1..=10).map(|i| format!("file-{i}.dat")).collect(); + let mut group = c.benchmark_group("MerkleTree"); + group.sample_size(10); + for size in [5, 10, 15] { group.bench_function( - format!("MerkleTree creation and validation with 10 nodes and Keccak512 algorithm. {size} MB per each file."), + format!("MerkleTree creation and validation with 10 nodes and Keccak256 algorithm. {size} MB per each file."), |b| { let files = setup_files(&filenames, size).expect("failed to allocate new files"); b.iter(|| { - let hasher = Keccak512Hasher::new(); + let hasher = Keccak256Hasher::new(); test_merkle_tree(hasher, &files); }); cleanup_files(&filenames).expect("failed to deallocate data"); @@ -96,22 +108,22 @@ fn bench_large_merkle_tree_sha256(c: &mut Criterion) { group.finish(); } -/// Example of a MarkleTree with 10 nodes which use Keccak512 algorithm to make hashes. +/// Example of a MarkleTree with 10 nodes which use Blake3 algorithm to make hashes. /// Each node has a size of 5, 10 or 15 MB. /// Also, it verifies each node path with a proofer O(n). -fn bench_large_merkle_tree_keccak512(c: &mut Criterion) { +fn bench_large_merkle_tree_blake3(c: &mut Criterion) { let filenames: Vec<String> = (1..=10).map(|i| format!("file-{i}.dat")).collect(); let mut group = c.benchmark_group("MerkleTree"); group.sample_size(10); for size in [5, 10, 15] { group.bench_function( - format!("MerkleTree creation and validation with 10 nodes and Keccak512 algorithm. {size} MB per each file."), + format!("MerkleTree creation and validation with 10 nodes and Keccak256 algorithm. {size} MB per each file."), |b| { let files = setup_files(&filenames, size).expect("failed to allocate new files"); b.iter(|| { - let hasher = Keccak512Hasher::new(); + let hasher = Blake3Hasher::new(); test_merkle_tree(hasher, &files); }); cleanup_files(&filenames).expect("failed to deallocate data"); @@ -124,6 +136,7 @@ fn bench_large_merkle_tree_keccak512(c: &mut Criterion) { criterion_group!( benches, bench_large_merkle_tree_sha256, - bench_large_merkle_tree_keccak512 + bench_large_merkle_tree_keccak256, + bench_large_merkle_tree_blake3 ); criterion_main!(benches); diff --git a/src/hasher.rs b/src/hasher.rs index 7692e0f..845127f 100644 --- a/src/hasher.rs +++ b/src/hasher.rs @@ -1,7 +1,6 @@ //! Provides hashing abstractions and implementations including SHA256 and a default dummy hasher. -use sha2::{Digest, Sha256}; -use sha3::Keccak512; +use sha2::Digest; /// A trait representing a generic hash function. /// @@ -14,7 +13,7 @@ pub trait Hasher { /// A dummy hasher used for testing or demonstration purposes. /// /// Always returns a static hash value. -#[derive(Clone)] +#[derive(Clone, Default)] pub struct DummyHasher; impl Hasher for DummyHasher { @@ -42,36 +41,59 @@ impl SHA256Hasher { impl Hasher for SHA256Hasher { fn hash(&self, input: &[u8]) -> String { - let mut hasher = Sha256::new(); + let mut hasher = sha2::Sha256::new(); hasher.update(input); hex::encode(hasher.finalize()) } } #[derive(Clone)] -/// A hasher implementation using the Keccak512 cryptographic hash function. -pub struct Keccak512Hasher; +/// A hasher implementation using the Keccak256 cryptographic hash function. +pub struct Keccak256Hasher; -impl Default for Keccak512Hasher { +impl Default for Keccak256Hasher { fn default() -> Self { Self::new() } } -impl Keccak512Hasher { +impl Keccak256Hasher { pub fn new() -> Self { Self {} } } -impl Hasher for Keccak512Hasher { +impl Hasher for Keccak256Hasher { fn hash(&self, input: &[u8]) -> String { - let mut hasher = Keccak512::new(); + let mut hasher = sha3::Keccak256::new(); hasher.update(input); hex::encode(hasher.finalize()) } } +#[derive(Clone)] +/// A hasher implementation using the Blake3 cryptographic hash function. +pub struct Blake3Hasher; + +impl Default for Blake3Hasher { + fn default() -> Self { + Self::new() + } +} + +impl Blake3Hasher { + pub fn new() -> Self { + Self {} + } +} + +impl Hasher for Blake3Hasher { + fn hash(&self, input: &[u8]) -> String { + let mut hasher = blake3::Hasher::new(); + hasher.update(input); + hasher.finalize().to_hex().to_string() + } +} #[cfg(test)] mod tests { use super::*; @@ -95,19 +117,37 @@ mod tests { } #[test] - fn test_keccak512_hasher_with_known_input() { - let hasher = Keccak512Hasher; + fn test_keccak256_hasher_with_known_input() { + let hasher = Keccak256Hasher; + let input = "hello".as_bytes(); + let expected_hash = "1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"; + let actual_hash = hasher.hash(input); + assert_eq!(actual_hash, expected_hash); + } + + #[test] + fn test_keccak256_hasher_empty_string() { + let hasher = Keccak256Hasher; + let input = &[]; + let expected_hash = "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; + let actual_hash = hasher.hash(input); + assert_eq!(actual_hash, expected_hash); + } + + #[test] + fn test_blake3_hasher_with_known_input() { + let hasher = Blake3Hasher; let input = "hello".as_bytes(); - let expected_hash = "52fa80662e64c128f8389c9ea6c73d4c02368004bf4463491900d11aaadca39d47de1b01361f207c512cfa79f0f92c3395c67ff7928e3f5ce3e3c852b392f976"; + let expected_hash = "ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f"; let actual_hash = hasher.hash(input); assert_eq!(actual_hash, expected_hash); } #[test] - fn test_keccak512_hasher_empty_string() { - let hasher = Keccak512Hasher; + fn test_blake3_hasher_empty_string() { + let hasher = Blake3Hasher; let input = &[]; - let expected_hash = "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e"; + let expected_hash = "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"; let actual_hash = hasher.hash(input); assert_eq!(actual_hash, expected_hash); } |
