Skip to content

Commit f7fdec1

Browse files
committed
feat: hashes
1 parent d2042f8 commit f7fdec1

File tree

4 files changed

+137
-1
lines changed

4 files changed

+137
-1
lines changed

Nargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ type = "lib"
44
authors = ["Oleh Misarosh <olehmisar@gmail.com>"]
55

66
[dependencies]
7+
sha256 = { tag = "v0.1.0", git = "https://github.com/noir-lang/sha256" }

README.md

+48-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Nodash is a utility library for [Noir](https://github.com/noir-lang/noir) langua
77
Put this into your Nargo.toml.
88

99
```toml
10-
nodash = { git = "https://github.com/olehmisar/nodash/", tag = "v0.39.4" }
10+
nodash = { git = "https://github.com/olehmisar/nodash/", tag = "v0.39.5" }
1111
```
1212

1313
## Docs
@@ -46,6 +46,53 @@ use nodash::div_ceil;
4646
assert(div_ceil(10 as u64, 3) == 4);
4747
```
4848

49+
### Hashes
50+
51+
Hash functions can either accept a `[T; N]` or a `BoundedVec<T, N>` (if technically possible).
52+
53+
#### `poseidon2`
54+
55+
```rs
56+
use nodash::poseidon2;
57+
58+
// hashes the whole array
59+
let hash = poseidon2([10, 20]);
60+
// hashes elements up to the length (in this case, 2)
61+
let hash = poseidon2(BoundedVec::from_parts([10, 20, 0], 2));
62+
```
63+
64+
#### `pedersen`
65+
66+
```rs
67+
use nodash::pedersen;
68+
69+
let hash = pedersen([10, 20]);
70+
```
71+
72+
#### `sha256`
73+
74+
sha256 is expensive to compute in Noir, so use [poseidon2](#poseidon2) where possible.
75+
76+
```rs
77+
use nodash::sha256;
78+
79+
let hash = sha256([10, 20]);
80+
// or
81+
let hash = sha256(BoundedVec::from_parts([10, 20, 0], 2));
82+
```
83+
84+
#### `keccak256`
85+
86+
keccak256 is expensive to compute in Noir, so use [poseidon2](#poseidon2) where possible.
87+
88+
```rs
89+
use nodash::keccak256;
90+
91+
let hash = keccak256([10, 20]);
92+
// or
93+
let hash = keccak256(BoundedVec::from_parts([10, 20, 0], 2));
94+
```
95+
4996
### `solidity::encode_with_selector`
5097

5198
Equivalent to `abi.encodeWithSelector` in Solidity.

src/hash.nr

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
pub fn poseidon2<let N: u32>(input: impl ArrayOrBoundedVec<Field, N>) -> Field {
2+
let input = input.as_bounded_vec();
3+
std::hash::poseidon2::Poseidon2::hash(input.storage(), input.len())
4+
}
5+
6+
// TODO: is it possible for pedersen to accept BoundedVec?
7+
pub fn pedersen<let N: u32>(input: [Field; N]) -> Field {
8+
std::hash::pedersen_hash(input)
9+
}
10+
11+
pub fn sha256<let N: u32>(input: impl ArrayOrBoundedVec<u8, N>) -> [u8; 32] {
12+
let input = input.as_bounded_vec();
13+
dep::sha256::sha256_var(input.storage(), input.len() as u64)
14+
}
15+
16+
pub fn keccak256<let N: u32>(input: impl ArrayOrBoundedVec<u8, N>) -> [u8; 32] {
17+
let input = input.as_bounded_vec();
18+
std::hash::keccak256(input.storage(), input.len())
19+
}
20+
21+
/// Needed because of https://github.com/noir-lang/noir/issues/7054
22+
trait ArrayOrBoundedVec<T, let N: u32> {
23+
fn as_bounded_vec(self) -> BoundedVec<T, N>;
24+
}
25+
26+
impl<T, let N: u32> ArrayOrBoundedVec<T, N> for [T; N] {
27+
fn as_bounded_vec(self) -> BoundedVec<T, N> {
28+
BoundedVec::from(self)
29+
}
30+
}
31+
32+
impl<T, let N: u32> ArrayOrBoundedVec<T, N> for BoundedVec<T, N> {
33+
fn as_bounded_vec(self) -> BoundedVec<T, N> {
34+
self
35+
}
36+
}
37+
38+
mod tests {
39+
use crate::hash::{keccak256, pedersen, poseidon2, sha256};
40+
41+
global FIELD_INPUT_ARR: [Field; 2] = [1, 2];
42+
global FIELD_INPUT_VEC: BoundedVec<Field, 2> = BoundedVec::from(FIELD_INPUT_ARR);
43+
global FIELD_INPUT_VEC_LONGER: BoundedVec<Field, 3> = BoundedVec::from(FIELD_INPUT_ARR);
44+
45+
global U8_INPUT_ARR: [u8; 2] = [1, 2];
46+
global U8_INPUT_VEC: BoundedVec<u8, 2> = BoundedVec::from(U8_INPUT_ARR);
47+
global U8_INPUT_VEC_LONGER: BoundedVec<u8, 3> = BoundedVec::from(U8_INPUT_ARR);
48+
49+
#[test]
50+
fn test_equivalence() {
51+
assert(
52+
(poseidon2(FIELD_INPUT_ARR) == poseidon2(FIELD_INPUT_VEC)),
53+
// TODO: is this a bug? https://discord.com/channels/1113924620781883405/1333383938198212659
54+
// & (poseidon2(FIELD_INPUT_ARR) == poseidon2(FIELD_INPUT_VEC_LONGER)),
55+
);
56+
assert(
57+
(sha256(U8_INPUT_VEC) == sha256(U8_INPUT_ARR))
58+
& (sha256(U8_INPUT_VEC_LONGER) == sha256(U8_INPUT_ARR)),
59+
);
60+
assert(
61+
(keccak256(U8_INPUT_VEC) == keccak256(U8_INPUT_ARR))
62+
& (keccak256(U8_INPUT_VEC_LONGER) == keccak256(U8_INPUT_ARR)),
63+
);
64+
}
65+
66+
#[test]
67+
fn test_against_std() {
68+
assert(
69+
poseidon2(FIELD_INPUT_ARR)
70+
== std::hash::poseidon2::Poseidon2::hash(FIELD_INPUT_ARR, FIELD_INPUT_ARR.len()),
71+
);
72+
assert(
73+
poseidon2(FIELD_INPUT_VEC_LONGER)
74+
== std::hash::poseidon2::Poseidon2::hash(
75+
FIELD_INPUT_VEC_LONGER.storage(),
76+
FIELD_INPUT_VEC_LONGER.len(),
77+
),
78+
);
79+
assert(pedersen(FIELD_INPUT_ARR) == std::hash::pedersen_hash(FIELD_INPUT_ARR));
80+
assert(
81+
sha256(U8_INPUT_ARR)
82+
== dep::sha256::sha256_var(U8_INPUT_ARR, U8_INPUT_ARR.len() as u64),
83+
);
84+
assert(keccak256(U8_INPUT_ARR) == std::hash::keccak256(U8_INPUT_ARR, U8_INPUT_ARR.len()));
85+
}
86+
}

src/lib.nr

+2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
mod hash;
12
mod math;
23
mod solidity;
34
mod string;
45
mod tables;
56
mod array;
67

78
pub use array::pack_bytes;
9+
pub use hash::{keccak256, pedersen, poseidon2, sha256};
810
pub use math::{clamp, div_ceil, sqrt::sqrt};
911
pub use string::{field_to_hex, ord, str_to_u64, to_hex_string_bytes};
1012

0 commit comments

Comments
 (0)