Skip to content

Commit

Permalink
Update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
moCello committed Feb 2, 2024
1 parent 44b5a77 commit 0042cb3
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 75 deletions.
64 changes: 31 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,49 @@

Reference implementation for the Poseidon Hashing algorithm.

#### Reference

Reference:
[Starkad and Poseidon: New Hash Functions for Zero Knowledge Proof Systems](https://eprint.iacr.org/2019/458.pdf)

This repository has been created so there's a unique library that holds the tools & functions
required to perform Poseidon Hashes.
This repository has been created so there's a unique library that holds the tools & functions required to perform Poseidon Hashes on field elements of the bls12-381 elliptic curve.

These hashes heavily rely on the Hades design for its inner permutation.
The hash use the Hades design for its inner permutation and the [SAFE](https://eprint.iacr.org/2023/522.pdf) framework for contstructing the sponge.

**The library provides the two hashing techniques of Poseidon:**
The library provides the two hashing techniques of Poseidon:
- The 'normal' hashing functionalities operating on `BlsScalar`.
- The 'gadget' hashing functionalities that build a circuit which outputs the hash.

## Sponge Hash
## Example

The `Sponge` technique in Poseidon allows to hash an unlimited amount of data
into a single `Scalar`.
The sponge hash technique requires a padding to be applied before the data can
be hashed.
```rust
use rand::rngs::StdRng;
use rand::SeedableRng;

This is done to avoid hash collisions as stated in the paper of the Poseidon Hash
algorithm. See: <https://eprint.iacr.org/2019/458.pdf>.
The inputs of the `sponge_hash` are always `Scalar` or need to be capable of being represented
as it.
use dusk_poseidon::{Domain, Hash};
use dusk_bls12_381::BlsScalar;
use ff::Field;

The module provides two sponge hash implementations:
// generate random input
let mut rng = StdRng::seed_from_u64(0xbeef);
let mut input = [BlsScalar::zero(); 42];
for scalar in input.iter_mut() {
*scalar = BlsScalar::random(&mut rng);
}

- Sponge hash using `Scalar` as backend. Which hashes the inputted `Scalar`s and returns a single
`Scalar`.
// digest the input all at once
let hash = Hash::digest(Domain::Other, &input);

- Sponge hash gadget using `dusk_plonk::Witness` as a backend. This technique is used/required
when you want to proof pre-images of unconstrained data inside Zero-Knowledge PLONK circuits.
// update the input gradually
let mut hasher = Hash::new(Domain::Other);
hasher.update(&input[..3]);
hasher.update(&input[3..]);
assert_eq!(hash, hasher.finalize());

## Documentation
// create a hash used for merkle tree hashing with arity = 4
let merkle_hash = Hash::digest(Domain::Merkle4, &input[..4]);

This crate contains info about all the functions that the library provides as well as the
documentation regarding the data structures that it exports. To check it, please feel free to go to
the [documentation page](https://dusk-network.github.io/Poseidon252/poseidon252/index.html)
// which is different when another domain is used
assert_ne!(merkle_hash, Hash::digest(Domain::Other, &input[..4]));
```

## Benchmarks

Expand All @@ -53,15 +60,6 @@ cargo bench --features=zk,cipher
```
in the repository.

To run a specific benchmark, run
```shell
cargo bench --bench <name>
```
where you replace `<name>` with the benchmark name. For example to run the benchmarks for the poseidon cipher encription from the file 'benches/cipher_encrypt.rs', you would need to run
```shell
cargo bench --benches cipher_encrypt
```

## Licensing

This code is licensed under Mozilla Public License Version 2.0 (MPL-2.0). Please see [LICENSE](https://github.com/dusk-network/plonk/blob/master/LICENSE) for further info.
Expand Down
100 changes: 58 additions & 42 deletions src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,54 @@ impl From<&Domain> for DomainSeparator {
}
}

fn io_pattern<T>(
domain: Domain,
input: &Vec<&[T]>,
output_len: usize,
) -> Result<(usize, Vec<IOCall>), safe::Error> {
let mut io_pattern = Vec::new();
// check total input length against domain
let input_len = input.iter().fold(0, |acc, input| acc + input.len());
match domain {
Domain::Merkle2 => {
if input_len != 2 || output_len != 1 {
return Err(safe::Error::IOPatternViolation);
}
}
Domain::Merkle4 => {
if input_len != 4 || output_len != 1 {
return Err(safe::Error::IOPatternViolation);
}
}
_ => {}
}
for input in input.iter() {
io_pattern.push(IOCall::Absorb(input.len() as u32));
}
io_pattern.push(IOCall::Squeeze(output_len as u32));

Ok((input_len, io_pattern))
}

fn domain_separator(
domain: Domain,
input_len: usize,
output_len: usize,
) -> DomainSeparator {
match domain {
// when the domain separator is not set, we calculate it from
// the input and output length:
Domain::Other => {
// input_len * 2^8 + output_len - 1
let mut encoding = (input_len as u64) << 8;
encoding += output_len as u64 - 1;
DomainSeparator::from(encoding)
}
// in all other cases we use the encoding defined by the From trait
_ => DomainSeparator::from(&domain),
}
}

/// Hash struct.
pub struct Hash<'a> {
domain: Domain,
Expand Down Expand Up @@ -75,30 +123,14 @@ impl<'a> Hash<'a> {
/// Finalize the hash.
pub fn finalize(&self) -> Result<Vec<BlsScalar>, safe::Error> {
// generate the io-pattern
let mut io_pattern = Vec::new();
for input in self.input.iter() {
io_pattern.push(IOCall::Absorb(input.len() as u32));
}
io_pattern.push(IOCall::Squeeze(self.output_len as u32));
let (input_len, io_pattern) =
io_pattern(self.domain, &self.input, self.output_len)?;

// get the domain-separator
let domain_sep = match self.domain {
// when the domain separator is not set, we calculate it from
// the input and output length:
Domain::Other => {
// input_len * 2^8 + output_len - 1
let mut encoding = 0u64;
for input in self.input.iter() {
encoding += input.len() as u64;
}
encoding <<= 8;
encoding += self.output_len as u64 - 1;
DomainSeparator::from(encoding)
}
// in all other cases we take the domain encoding
_ => DomainSeparator::from(&self.domain),
};
let domain_sep =
domain_separator(self.domain, input_len, self.output_len);

// Generate the hash using the sponge framework.
// initialize the sponge
let mut sponge = Sponge::start(
HadesPermutation::new([BlsScalar::zero(); WIDTH]),
Expand Down Expand Up @@ -197,30 +229,14 @@ pub(crate) mod zk {
composer: &mut Composer,
) -> Result<Vec<Witness>, safe::Error> {
// generate the io-pattern
let mut io_pattern = Vec::new();
for input in self.input.iter() {
io_pattern.push(IOCall::Absorb(input.len() as u32));
}
io_pattern.push(IOCall::Squeeze(self.output_len as u32));
let (input_len, io_pattern) =
io_pattern(self.domain, &self.input, self.output_len)?;

// get the domain-separator
let domain_sep = match self.domain {
// when the domain separator is not set, we calculate it from
// the input and output length:
Domain::Other => {
// input_len * 2^8 + output_len - 1
let mut encoding = 0u64;
for input in self.input.iter() {
encoding += input.len() as u64;
}
encoding <<= 8;
encoding += self.output_len as u64 - 1;
DomainSeparator::from(encoding)
}
// in all other cases we take the domain encoding
_ => DomainSeparator::from(&self.domain),
};
let domain_sep =
domain_separator(self.domain, input_len, self.output_len);

// Generate the hash using the sponge framework.
// initialize the sponge
let mut sponge = Sponge::start(
HadesPermutationGadget::new(composer, [Composer::ZERO; WIDTH]),
Expand Down

0 comments on commit 0042cb3

Please sign in to comment.