diff options
| -rw-r--r-- | Cargo.lock | 20 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | benches/bigfile.rs | 53 | ||||
| -rw-r--r-- | src/hasher.rs | 43 |
4 files changed, 111 insertions, 6 deletions
@@ -279,6 +279,15 @@ dependencies = [ ] [[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] name = "libc" version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -304,6 +313,7 @@ dependencies = [ "hex", "rand", "sha2", + "sha3", ] [[package]] @@ -526,6 +536,16 @@ dependencies = [ ] [[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] name = "syn" version = "2.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -7,6 +7,7 @@ edition = "2024" [dependencies] hex = { version = "0.4.3" } sha2 = { version = "0.10.9" } +sha3 = "0.10.8" [dev-dependencies] criterion = { version = "0.6.0" } diff --git a/benches/bigfile.rs b/benches/bigfile.rs index bac637a..4a092e3 100644 --- a/benches/bigfile.rs +++ b/benches/bigfile.rs @@ -1,6 +1,6 @@ use criterion::{Criterion, criterion_group, criterion_main}; use mt_rs::{ - hasher::SHA256Hasher, + hasher::{Hasher, Keccak512Hasher, SHA256Hasher}, merkletree::MerkleTree, proof::{DefaultProofer, Proofer}, }; @@ -46,9 +46,7 @@ fn cleanup_files(filenames: &Vec<String>) -> std::io::Result<()> { Ok(()) } -fn test_merkle_tree(files: &Vec<Vec<u8>>) { - let hasher = SHA256Hasher::new(); - +fn test_merkle_tree<H: Hasher + Clone + 'static>(hasher: H, files: &Vec<Vec<u8>>) { let tree = MerkleTree::new(hasher.clone(), files); let proofer = DefaultProofer::new(&hasher, tree.leaves().clone()); let root = tree.root(); @@ -75,7 +73,46 @@ fn bench_large_merkle_tree_sha256(c: &mut Criterion) { let files = setup_files(&filenames, size).expect("failed to allocate new files"); b.iter(|| { - test_merkle_tree(&files); + let hasher = SHA256Hasher::new(); + test_merkle_tree(hasher, &files); + }); + cleanup_files(&filenames).expect("failed to deallocate data"); + }, + ); + + group.bench_function( + format!("MerkleTree creation and validation with 10 nodes and Keccak512 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(); + test_merkle_tree(hasher, &files); + }); + cleanup_files(&filenames).expect("failed to deallocate data"); + }, + ); + } + group.finish(); +} + +/// Example of a MarkleTree with 10 nodes which use Keccak512 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) { + 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."), + |b| { + let files = setup_files(&filenames, size).expect("failed to allocate new files"); + + b.iter(|| { + let hasher = Keccak512Hasher::new(); + test_merkle_tree(hasher, &files); }); cleanup_files(&filenames).expect("failed to deallocate data"); }, @@ -84,5 +121,9 @@ fn bench_large_merkle_tree_sha256(c: &mut Criterion) { group.finish(); } -criterion_group!(benches, bench_large_merkle_tree_sha256); +criterion_group!( + benches, + bench_large_merkle_tree_sha256, + bench_large_merkle_tree_keccak512 +); criterion_main!(benches); diff --git a/src/hasher.rs b/src/hasher.rs index 088b16a..7692e0f 100644 --- a/src/hasher.rs +++ b/src/hasher.rs @@ -1,6 +1,7 @@ //! Provides hashing abstractions and implementations including SHA256 and a default dummy hasher. use sha2::{Digest, Sha256}; +use sha3::Keccak512; /// A trait representing a generic hash function. /// @@ -47,6 +48,30 @@ impl Hasher for SHA256Hasher { } } +#[derive(Clone)] +/// A hasher implementation using the Keccak512 cryptographic hash function. +pub struct Keccak512Hasher; + +impl Default for Keccak512Hasher { + fn default() -> Self { + Self::new() + } +} + +impl Keccak512Hasher { + pub fn new() -> Self { + Self {} + } +} + +impl Hasher for Keccak512Hasher { + fn hash(&self, input: &[u8]) -> String { + let mut hasher = Keccak512::new(); + hasher.update(input); + hex::encode(hasher.finalize()) + } +} + #[cfg(test)] mod tests { use super::*; @@ -68,4 +93,22 @@ mod tests { let actual_hash = hasher.hash(input); assert_eq!(actual_hash, expected_hash); } + + #[test] + fn test_keccak512_hasher_with_known_input() { + let hasher = Keccak512Hasher; + let input = "hello".as_bytes(); + let expected_hash = "52fa80662e64c128f8389c9ea6c73d4c02368004bf4463491900d11aaadca39d47de1b01361f207c512cfa79f0f92c3395c67ff7928e3f5ce3e3c852b392f976"; + let actual_hash = hasher.hash(input); + assert_eq!(actual_hash, expected_hash); + } + + #[test] + fn test_keccak512_hasher_empty_string() { + let hasher = Keccak512Hasher; + let input = &[]; + let expected_hash = "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e"; + let actual_hash = hasher.hash(input); + assert_eq!(actual_hash, expected_hash); + } } |
