Skip to content

Commit

Permalink
feat: hashes
Browse files Browse the repository at this point in the history
  • Loading branch information
olehmisar committed Jan 27, 2025
1 parent d2042f8 commit f7fdec1
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 1 deletion.
1 change: 1 addition & 0 deletions Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ type = "lib"
authors = ["Oleh Misarosh <olehmisar@gmail.com>"]

[dependencies]
sha256 = { tag = "v0.1.0", git = "https://github.com/noir-lang/sha256" }
49 changes: 48 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Nodash is a utility library for [Noir](https://github.com/noir-lang/noir) langua
Put this into your Nargo.toml.

```toml
nodash = { git = "https://github.com/olehmisar/nodash/", tag = "v0.39.4" }
nodash = { git = "https://github.com/olehmisar/nodash/", tag = "v0.39.5" }
```

## Docs
Expand Down Expand Up @@ -46,6 +46,53 @@ use nodash::div_ceil;
assert(div_ceil(10 as u64, 3) == 4);
```

### Hashes

Hash functions can either accept a `[T; N]` or a `BoundedVec<T, N>` (if technically possible).

#### `poseidon2`

```rs
use nodash::poseidon2;

// hashes the whole array
let hash = poseidon2([10, 20]);
// hashes elements up to the length (in this case, 2)
let hash = poseidon2(BoundedVec::from_parts([10, 20, 0], 2));
```

#### `pedersen`

```rs
use nodash::pedersen;

let hash = pedersen([10, 20]);
```

#### `sha256`

sha256 is expensive to compute in Noir, so use [poseidon2](#poseidon2) where possible.

```rs
use nodash::sha256;

let hash = sha256([10, 20]);
// or
let hash = sha256(BoundedVec::from_parts([10, 20, 0], 2));
```

#### `keccak256`

keccak256 is expensive to compute in Noir, so use [poseidon2](#poseidon2) where possible.

```rs
use nodash::keccak256;

let hash = keccak256([10, 20]);
// or
let hash = keccak256(BoundedVec::from_parts([10, 20, 0], 2));
```

### `solidity::encode_with_selector`

Equivalent to `abi.encodeWithSelector` in Solidity.
Expand Down
86 changes: 86 additions & 0 deletions src/hash.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
pub fn poseidon2<let N: u32>(input: impl ArrayOrBoundedVec<Field, N>) -> Field {
let input = input.as_bounded_vec();
std::hash::poseidon2::Poseidon2::hash(input.storage(), input.len())
}

// TODO: is it possible for pedersen to accept BoundedVec?
pub fn pedersen<let N: u32>(input: [Field; N]) -> Field {
std::hash::pedersen_hash(input)
}

pub fn sha256<let N: u32>(input: impl ArrayOrBoundedVec<u8, N>) -> [u8; 32] {
let input = input.as_bounded_vec();
dep::sha256::sha256_var(input.storage(), input.len() as u64)
}

pub fn keccak256<let N: u32>(input: impl ArrayOrBoundedVec<u8, N>) -> [u8; 32] {
let input = input.as_bounded_vec();
std::hash::keccak256(input.storage(), input.len())
}

/// Needed because of https://github.com/noir-lang/noir/issues/7054
trait ArrayOrBoundedVec<T, let N: u32> {
fn as_bounded_vec(self) -> BoundedVec<T, N>;
}

impl<T, let N: u32> ArrayOrBoundedVec<T, N> for [T; N] {
fn as_bounded_vec(self) -> BoundedVec<T, N> {
BoundedVec::from(self)
}
}

impl<T, let N: u32> ArrayOrBoundedVec<T, N> for BoundedVec<T, N> {
fn as_bounded_vec(self) -> BoundedVec<T, N> {
self
}
}

mod tests {
use crate::hash::{keccak256, pedersen, poseidon2, sha256};

global FIELD_INPUT_ARR: [Field; 2] = [1, 2];
global FIELD_INPUT_VEC: BoundedVec<Field, 2> = BoundedVec::from(FIELD_INPUT_ARR);
global FIELD_INPUT_VEC_LONGER: BoundedVec<Field, 3> = BoundedVec::from(FIELD_INPUT_ARR);

global U8_INPUT_ARR: [u8; 2] = [1, 2];
global U8_INPUT_VEC: BoundedVec<u8, 2> = BoundedVec::from(U8_INPUT_ARR);
global U8_INPUT_VEC_LONGER: BoundedVec<u8, 3> = BoundedVec::from(U8_INPUT_ARR);

#[test]
fn test_equivalence() {
assert(
(poseidon2(FIELD_INPUT_ARR) == poseidon2(FIELD_INPUT_VEC)),
// TODO: is this a bug? https://discord.com/channels/1113924620781883405/1333383938198212659
// & (poseidon2(FIELD_INPUT_ARR) == poseidon2(FIELD_INPUT_VEC_LONGER)),
);
assert(
(sha256(U8_INPUT_VEC) == sha256(U8_INPUT_ARR))
& (sha256(U8_INPUT_VEC_LONGER) == sha256(U8_INPUT_ARR)),
);
assert(
(keccak256(U8_INPUT_VEC) == keccak256(U8_INPUT_ARR))
& (keccak256(U8_INPUT_VEC_LONGER) == keccak256(U8_INPUT_ARR)),
);
}

#[test]
fn test_against_std() {
assert(
poseidon2(FIELD_INPUT_ARR)
== std::hash::poseidon2::Poseidon2::hash(FIELD_INPUT_ARR, FIELD_INPUT_ARR.len()),
);
assert(
poseidon2(FIELD_INPUT_VEC_LONGER)
== std::hash::poseidon2::Poseidon2::hash(
FIELD_INPUT_VEC_LONGER.storage(),
FIELD_INPUT_VEC_LONGER.len(),
),
);
assert(pedersen(FIELD_INPUT_ARR) == std::hash::pedersen_hash(FIELD_INPUT_ARR));
assert(
sha256(U8_INPUT_ARR)
== dep::sha256::sha256_var(U8_INPUT_ARR, U8_INPUT_ARR.len() as u64),
);
assert(keccak256(U8_INPUT_ARR) == std::hash::keccak256(U8_INPUT_ARR, U8_INPUT_ARR.len()));
}
}
2 changes: 2 additions & 0 deletions src/lib.nr
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
mod hash;
mod math;
mod solidity;
mod string;
mod tables;
mod array;

pub use array::pack_bytes;
pub use hash::{keccak256, pedersen, poseidon2, sha256};
pub use math::{clamp, div_ceil, sqrt::sqrt};
pub use string::{field_to_hex, ord, str_to_u64, to_hex_string_bytes};

Expand Down

0 comments on commit f7fdec1

Please sign in to comment.