Skip to content

Commit

Permalink
refactor rework
Browse files Browse the repository at this point in the history
  • Loading branch information
rkdud007 committed Jan 2, 2024
1 parent c57034f commit 68ad6e3
Show file tree
Hide file tree
Showing 29 changed files with 695 additions and 459 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ sqlx = { version = "0.7", features = [
"runtime-tokio",
"sqlite",
] } # SQLite for rust
anyhow = "1.0" # Error handling
thiserror = "1.0" # Error handling
async-trait = "0.1.74" # Async traits
hex = "0.4.3" # Hex encoding
sha3 = "0.10.8" # Keccak hashing
Expand Down
4 changes: 3 additions & 1 deletion benches/incremental_benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ async fn prepare_incremental(count: usize) -> IncrementalMerkleTree<StarkPoseido

let store = Arc::new(store);

IncrementalMerkleTree::initialize(count, "0x0".to_string(), hasher, store, None).await
IncrementalMerkleTree::initialize(count, "0x0".to_string(), hasher, store, None)
.await
.unwrap()
}

fn bench(c: &mut Criterion) {
Expand Down
2 changes: 2 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[toolchain]
channel = "1.75.0"
49 changes: 41 additions & 8 deletions src/hasher/core.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,45 @@
use anyhow::Result;
use std::{fmt::Debug, str::FromStr};
use strum_macros::EnumIter;
use thiserror::Error;

// Default Hasher Options
pub const DEFAULT_BLOCK_SIZE_BITS: usize = 256;
/// Hasher error
#[derive(Error, Debug)]
pub enum HasherError {
#[error("Invalid hashing function")]
InvalidHashingFunction,
#[error("Element {} is size {} bits. It is not of valid size {} bits", .element, .element.len() * 8, .block_size_bits)]
InvalidElementSize {
element: String,
block_size_bits: usize,
},
#[error("Invalid elements length for hashing function")]
InvalidElementsLength,
#[error("Fail to convert to U256")]
U256ConversionError,
}

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

/// 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;
fn hash_single(&self, data: &str) -> String;
fn get_genesis(&self) -> String;

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

/// Returns the genesis hash
fn get_genesis(&self) -> Result<String, HasherError>;

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

/// Returns the block size in bits
fn get_block_size_bits(&self) -> usize;
}

/// Hashing functions types supported by the hasher
#[derive(EnumIter, Debug, PartialEq, Eq, Clone, Copy)]
pub enum HashingFunction {
Keccak256,
Expand All @@ -21,14 +48,14 @@ pub enum HashingFunction {
}

impl FromStr for HashingFunction {
type Err = anyhow::Error;
type Err = HasherError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"keccak" => Ok(HashingFunction::Keccak256),
"poseidon" => Ok(HashingFunction::Poseidon),
"pedersen" => Ok(HashingFunction::Pedersen),
_ => Err(anyhow::anyhow!("invalid hashing function")),
_ => Err(HasherError::InvalidHashingFunction),
}
}
}
Expand All @@ -42,3 +69,9 @@ impl ToString for HashingFunction {
}
}
}

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

use crate::hasher::HashingFunction;
use crate::hasher::{byte_size, HasherError, 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 +18,19 @@ impl Hasher for KeccakHasher {
HashingFunction::Keccak256
}

fn hash(&self, data: Vec<String>) -> Result<String> {
/// 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, HasherError> {
for element in &data {
if !self.is_element_size_valid(element) {
return Err(HasherError::InvalidElementSize {
element: element.clone(),
block_size_bits: self.block_size_bits,
});
}
}

let mut keccak = Keccak256::new();

if data.is_empty() {
Expand All @@ -33,7 +46,7 @@ impl Hasher for KeccakHasher {
let n = BigInt::from_str_radix(no_prefix, 16).unwrap_or(BigInt::zero());
let hex = format!("{:0>64x}", n);

for byte_pair in hex.as_str().as_bytes().chunks(2) {
for byte_pair in hex.as_bytes().chunks(2) {
let byte_str = std::str::from_utf8(byte_pair).unwrap();
let byte = u8::from_str_radix(byte_str, 16).unwrap();
result.push(byte);
Expand All @@ -50,14 +63,18 @@ impl Hasher for KeccakHasher {
byte_size(element) <= self.block_size_bits
}

fn hash_single(&self, data: &str) -> String {
self.hash(vec![data.to_string()]).unwrap()
fn hash_single(&self, data: &str) -> Result<String, HasherError> {
self.hash(vec![data.to_string()])
}

fn get_genesis(&self) -> String {
fn get_genesis(&self) -> Result<String, HasherError> {
let genesis_str = "brave new world";
self.hash_single(genesis_str)
}

fn get_block_size_bits(&self) -> usize {
self.block_size_bits
}
}

impl KeccakHasher {
Expand All @@ -73,9 +90,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
}
76 changes: 40 additions & 36 deletions src/hasher/hashers/stark_pedersen.rs
Original file line number Diff line number Diff line change
@@ -1,75 +1,89 @@
use anyhow::Result;
use std::collections::HashMap;

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

use crate::hasher::HashingFunction;
use crate::hasher::{byte_size, HasherError, 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
}

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

for element in &data {
if !self.is_element_size_valid(element) {
panic!("{}", format!("Element {} is not of valid size", element));
return Err(HasherError::InvalidElementSize {
element: element.clone(),
block_size_bits: self.block_size_bits,
});
}
}

let clean_data: Vec<String> = data
.iter()
.map(|s| {
if let Some(stripped) = s.strip_prefix("0x") {
U256::from_str_radix(stripped, 16).unwrap().to_string()
} else {
U256::from_str_radix(s, 16).unwrap().to_string()
}
})
.collect();
let mut clean_data = Vec::with_capacity(data.len());
for s in data.iter() {
let number_str = if let Some(stripped) = s.strip_prefix("0x") {
U256::from_str_radix(stripped, 16)
} else {
U256::from_str_radix(s, 16)
};

match number_str {
Ok(number) => clean_data.push(number.to_string()),
Err(_) => return Err(HasherError::U256ConversionError),
}
}

let result = pedersen_hash(
&FieldElement::from_dec_str(&clean_data[0]).unwrap_or_default(),
&FieldElement::from_dec_str(&clean_data[1]).unwrap_or_default(),
)
.to_string();

let computed_result = U256::from_dec_str(result.trim()).expect("Failed to convert to U256");
let computed_result =
U256::from_dec_str(result.trim()).map_err(|_| HasherError::U256ConversionError)?;
let padded_hex_str = format!("0x{:064x}", computed_result);

Ok(padded_hex_str)
}

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 {
self.hash(vec![data.to_string(), "".to_string()]).unwrap()
fn hash_single(&self, data: &str) -> Result<String, HasherError> {
self.hash(vec![data.to_string(), "".to_string()])
}

fn get_genesis(&self) -> String {
fn get_genesis(&self) -> Result<String, HasherError> {
let genesis_str = "brave new world";
self.hash_single(genesis_str)
}

fn get_block_size_bits(&self) -> usize {
self.block_size_bits
}
}

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 +92,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
}
Loading

0 comments on commit 68ad6e3

Please sign in to comment.