Skip to content

Commit

Permalink
refactor: hash logic clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
rkdud007 committed Dec 27, 2023
1 parent 6096bdc commit a4a69e2
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 70 deletions.
20 changes: 17 additions & 3 deletions src/hasher/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,25 @@ use anyhow::Result;
use std::{fmt::Debug, str::FromStr};
use strum_macros::EnumIter;

// Default Hasher Options
pub const DEFAULT_BLOCK_SIZE_BITS: usize = 256;

/// A trait for hash functions
pub trait Hasher: Send + Sync + Debug {
/// Hashes a data which is a vector of strings
fn hash(&self, data: Vec<String>) -> Result<String>;

/// Checks if the element size is valid, i.e. if it is less than the block size
fn is_element_size_valid(&self, element: &str) -> bool;

/// Hashes a single element
fn hash_single(&self, data: &str) -> String;

/// Returns the genesis hash
fn get_genesis(&self) -> String;

/// Returns the name of the [`HashingFunction`]
fn get_name(&self) -> HashingFunction;
}

/// Hashing functions types supported by the hasher
#[derive(EnumIter, Debug, PartialEq, Eq, Clone, Copy)]
pub enum HashingFunction {
Keccak256,
Expand Down Expand Up @@ -42,3 +50,9 @@ impl ToString for HashingFunction {
}
}
}

/// Returns the byte size of a hex string
pub(crate) fn byte_size(hex: &str) -> usize {
let hex = hex.strip_prefix("0x").unwrap_or(hex);
hex.len() / 2
}
31 changes: 22 additions & 9 deletions src/hasher/hashers/keccak.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use anyhow::Result;
use anyhow::{bail, Result};
use num_bigint::BigInt;
use num_traits::{identities::Zero, Num};
use sha3::{Digest, Keccak256};

use crate::hasher::HashingFunction;
use crate::hasher::{byte_size, HashingFunction};

use super::super::Hasher;

#[derive(Debug)]
/// Hasher for Keccak256
#[derive(Debug, Clone)]
pub struct KeccakHasher {
/// The block size in bits for Keccak256 is 256
block_size_bits: usize,
}

Expand All @@ -17,7 +19,24 @@ impl Hasher for KeccakHasher {
HashingFunction::Keccak256
}

/// Hashes a data which is a vector of strings
///
/// NOTE: data have no limit in length of elements
fn hash(&self, data: Vec<String>) -> Result<String> {
for element in &data {
if !self.is_element_size_valid(element) {
bail!(
"{}",
format!(
"Element {} is size {} bits. It is not of valid size {} bits",
element,
self.block_size_bits,
element.len() * 8
)
);
}
}

let mut keccak = Keccak256::new();

if data.is_empty() {
Expand Down Expand Up @@ -73,9 +92,3 @@ impl Default for KeccakHasher {
Self::new()
}
}

fn byte_size(hex: &str) -> usize {
let hex = hex.strip_prefix("0x").unwrap_or(hex);

hex.len() / 2
}
43 changes: 23 additions & 20 deletions src/hasher/hashers/stark_pedersen.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,43 @@
use anyhow::Result;
use std::collections::HashMap;
use anyhow::{bail, Result};

use primitive_types::U256;
use starknet::core::{crypto::pedersen_hash, types::FieldElement};

use crate::hasher::HashingFunction;
use crate::hasher::{byte_size, HashingFunction};

use super::super::Hasher;

/// Hasher for Stark Pedersen
#[derive(Debug, Clone)]
pub struct StarkPedersenHasher {
options: HashMap<String, usize>,
/// The block size in bits for Stark Pedersen is 252
block_size_bits: usize,
}

impl Hasher for StarkPedersenHasher {
fn get_name(&self) -> HashingFunction {
HashingFunction::Pedersen
}

/// Hashes a data which is a vector of strings
///
/// NOTE: data should be of size 2
fn hash(&self, data: Vec<String>) -> Result<String> {
if data.len() != 2 {
panic!("Stark Pedersen Hasher only accepts two elements");
bail!("Stark Pedersen Hasher only accepts two elements");
}

for element in &data {
if !self.is_element_size_valid(element) {
panic!("{}", format!("Element {} is not of valid size", element));
bail!(
"{}",
format!(
"Element {} is size {} bits. It is not of valid size {} bits",
element,
self.block_size_bits,
element.len() * 8
)
);
}
}

Expand Down Expand Up @@ -52,7 +65,7 @@ impl Hasher for StarkPedersenHasher {
}

fn is_element_size_valid(&self, element: &str) -> bool {
byte_size(element) <= *self.options.get("blockSizeBits").unwrap()
byte_size(element) <= self.block_size_bits
}

fn hash_single(&self, data: &str) -> String {
Expand All @@ -67,9 +80,9 @@ impl Hasher for StarkPedersenHasher {

impl StarkPedersenHasher {
pub fn new() -> Self {
let mut options = HashMap::new();
options.insert("blockSizeBits".to_string(), 252);
StarkPedersenHasher { options }
Self {
block_size_bits: 252,
}
}
}

Expand All @@ -78,13 +91,3 @@ impl Default for StarkPedersenHasher {
Self::new()
}
}

fn byte_size(hex: &str) -> usize {
let hex = if let Some(stripped) = hex.strip_prefix("0x") {
stripped
} else {
hex
};

hex.len() / 2
}
57 changes: 27 additions & 30 deletions src/hasher/hashers/stark_poseidon.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use crate::hasher::HashingFunction;
use crate::hasher::{byte_size, HashingFunction};

use super::super::Hasher;
use anyhow::{anyhow, Result};
use anyhow::{bail, Result};
use starknet::core::types::FieldElement;
use starknet_crypto::{poseidon_hash, poseidon_hash_many, poseidon_hash_single};

/// Hasher for Stark Poseidon
#[derive(Debug, Clone)]
pub struct StarkPoseidonHasher {
/// The block size in bits for Stark Poseidon is 252
block_size_bits: usize,
/// Boolean flag to indicate whether to pad the hash with zeros
should_pad: bool,
}

Expand All @@ -16,15 +19,22 @@ impl Hasher for StarkPoseidonHasher {
HashingFunction::Poseidon
}

/// Hashes a data which is a vector of strings
///
/// NOTE: data should be more than 1 element
fn hash(&self, data: Vec<String>) -> Result<String> {
let size_error_index = data.iter().position(|e| !self.is_element_size_valid(e));

if let Some(index) = size_error_index {
return Err(anyhow!(
"Stark Poseidon Hasher only accepts elements of size {} bits. Got {}",
self.block_size_bits,
data[index].len() * 8
));
for element in &data {
if !self.is_element_size_valid(element) {
bail!(
"{}",
format!(
"Element {} is size {} bits. It is not of valid size {} bits",
element,
self.block_size_bits,
element.len() * 8
)
);
}
}

let field_elements: Vec<FieldElement> =
Expand All @@ -33,9 +43,7 @@ impl Hasher for StarkPoseidonHasher {

match field_elements.len() {
0 => {
return Err(anyhow!(
"Stark Poseidon Hasher only accepts arrays of size 1 or greater".to_string()
))
bail!("Stark Poseidon Hasher only accepts arrays of size 1 or greater")
}
1 => hash_core = poseidon_hash_single(field_elements[0]),
2 => hash_core = poseidon_hash(field_elements[0], field_elements[1]),
Expand All @@ -62,15 +70,8 @@ impl Hasher for StarkPoseidonHasher {

fn get_genesis(&self) -> String {
let genesis_str = "brave new world";
let hex: String = genesis_str
.as_bytes()
.iter()
.fold(String::new(), |mut f, &b| {
f.push_str(&format!("{:02x}", b));
f
});
let hex_with_prefix = format!("0x{}", hex);
self.hash_single(&hex_with_prefix)
let hex_str = format!("0x{}", hex::encode(genesis_str));
self.hash_single(&hex_str)
}
}

Expand All @@ -83,12 +84,8 @@ impl StarkPoseidonHasher {
}
}

fn byte_size(hex: &str) -> usize {
let hex = if let Some(stripped) = hex.strip_prefix("0x") {
stripped
} else {
hex
};

hex.len() / 2
impl Default for StarkPoseidonHasher {
fn default() -> Self {
Self::new(None)
}
}
6 changes: 3 additions & 3 deletions tests/hasher/keccak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use accumulators::hasher::{keccak::KeccakHasher, Hasher};

#[test]
fn genesis() {
let hasher = KeccakHasher::new();
let hasher = KeccakHasher::default();

assert_eq!(
hasher.get_genesis(),
Expand All @@ -12,7 +12,7 @@ fn genesis() {

#[test]
fn should_compute_a_hash() {
let hasher = KeccakHasher::new();
let hasher = KeccakHasher::default();

let a = "0xa4b1d5793b631de611c922ea3ec938b359b3a49e687316d9a79c27be8ce84590".to_string();
let b = "0xa4b1d5793b631de611c922ea3ec938b359b3a49e687316d9a79c27be8ce84590".to_string();
Expand All @@ -33,7 +33,7 @@ fn should_compute_a_hash() {

#[test]
fn should_apply_padding() {
let hasher = KeccakHasher::new();
let hasher = KeccakHasher::default();
let a = "131".to_string();
let b = "10".to_string();
let hash = hasher.hash(vec![a, b]).unwrap();
Expand Down
4 changes: 2 additions & 2 deletions tests/hasher/stark_pedersen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use accumulators::hasher::{stark_pedersen::StarkPedersenHasher, Hasher};

#[test]
fn should_compute_a_hash() {
let hasher = StarkPedersenHasher::new();
let hasher = StarkPedersenHasher::default();
let a = "0x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d17761".to_string();
let b = "0x0194791558611599fe4ae0fcfa48f095659c90db18e54de86f2d2f547f7369bf".to_string();

Expand All @@ -20,7 +20,7 @@ fn should_compute_a_hash() {
#[test]
#[should_panic]
fn should_throw() {
let hasher = StarkPedersenHasher::new();
let hasher = StarkPedersenHasher::default();
let a =
"0x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4"
.to_string();
Expand Down
6 changes: 3 additions & 3 deletions tests/hasher/stark_poseidon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use accumulators::hasher::{stark_poseidon::StarkPoseidonHasher, Hasher};

#[test]
fn should_compute_a_hash() {
let hasher = StarkPoseidonHasher::new(Some(false));
let hasher = StarkPoseidonHasher::default();

let a = "0x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d17761".to_string();
let b = "0x0194791558611599fe4ae0fcfa48f095659c90db18e54de86f2d2f547f7369bf".to_string();
Expand All @@ -20,7 +20,7 @@ fn should_compute_a_hash() {

#[test]
fn check_genesis_hash() {
let hasher = StarkPoseidonHasher::new(Some(false));
let hasher = StarkPoseidonHasher::default();

assert_eq!(
hasher.get_genesis(),
Expand All @@ -42,7 +42,7 @@ fn check_genesis_hash() {

#[test]
fn should_throw() {
let hasher = StarkPoseidonHasher::new(Some(false));
let hasher = StarkPoseidonHasher::default();
let a = "0x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf40x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d177610x6109f1949f6a7555eccf4".to_string();

assert!(!hasher.is_element_size_valid(&a));
Expand Down

0 comments on commit a4a69e2

Please sign in to comment.