summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSanto Cariotti <santo@dcariotti.me>2025-06-23 07:54:32 +0000
committerSanto Cariotti <santo@dcariotti.me>2025-06-23 07:54:32 +0000
commit9ec408203a1a13d2b8e450f7601b994070f0c91d (patch)
treeb51dc940fa549c6cfd5b02f1e11c1af5f9488463
parentbe8ec280a056be688913c1a35c589ef406f72f50 (diff)
Add `Blake3Hasher`
-rw-r--r--Cargo.lock47
-rw-r--r--Cargo.toml1
-rw-r--r--benches/bigfile.rs29
-rw-r--r--src/hasher.rs72
4 files changed, 125 insertions, 24 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d728f74..331fab3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index e360f3c..4802c64 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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);
}