Skip to content

Commit

Permalink
chore: pedersen hash in Noir (#5217)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

Related to  #4931 

## Summary\*
Noir implementation for Pedersen Hash.


## Additional Context
I added a derive_generators() built-in function, which only works for
bn254, and then use pedersen commitments and scalar mul.


## Documentation\*

Check one:
- [X] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [X] I have tested the changes locally.
- [X] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
guipublic authored Jun 19, 2024
1 parent 0ea608f commit d2ea8a9
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 13 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fn default_generators() -> &'static [Affine<GrumpkinParameters>; NUM_DEFAULT_GEN
/// index-addressable generators.
///
/// [hash_to_curve]: super::hash_to_curve::hash_to_curve
pub(crate) fn derive_generators(
pub fn derive_generators(
domain_separator_bytes: &[u8],
num_generators: u32,
starting_index: u32,
Expand Down
13 changes: 8 additions & 5 deletions acvm-repo/bn254_blackbox_solver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod schnorr;

use ark_ec::AffineRepr;
pub use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul};
pub use generator::generators::derive_generators;
pub use poseidon2::poseidon2_permutation;

// Temporary hack, this ensure that we always use a bn254 field here
Expand Down Expand Up @@ -47,11 +48,13 @@ impl BlackBoxFunctionSolver<FieldElement> for Bn254BlackBoxSolver {
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> {
let inputs: Vec<grumpkin::Fq> = inputs.iter().map(|input| input.into_repr()).collect();
let result = pedersen::commitment::commit_native_with_index(&inputs, domain_separator);
let res_x =
FieldElement::from_repr(*result.x().expect("should not commit to point at infinity"));
let res_y =
FieldElement::from_repr(*result.y().expect("should not commit to point at infinity"));
Ok((res_x, res_y))
let result = if let Some((x, y)) = result.xy() {
(FieldElement::from_repr(*x), FieldElement::from_repr(*y))
} else {
(FieldElement::from(0_u128), FieldElement::from(0_u128))
};

Ok(result)
}

fn pedersen_hash(
Expand Down
1 change: 1 addition & 0 deletions compiler/noirc_evaluator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ license.workspace = true
noirc_frontend.workspace = true
noirc_errors.workspace = true
acvm.workspace = true
bn254_blackbox_solver.workspace = true
fxhash.workspace = true
iter-extended.workspace = true
thiserror.workspace = true
Expand Down
6 changes: 5 additions & 1 deletion compiler/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ pub(crate) enum Intrinsic {
AsField,
AsWitness,
IsUnconstrained,
DerivePedersenGenerators,
}

impl std::fmt::Display for Intrinsic {
Expand All @@ -92,6 +93,7 @@ impl std::fmt::Display for Intrinsic {
Intrinsic::AsField => write!(f, "as_field"),
Intrinsic::AsWitness => write!(f, "as_witness"),
Intrinsic::IsUnconstrained => write!(f, "is_unconstrained"),
Intrinsic::DerivePedersenGenerators => write!(f, "derive_pedersen_generators"),
}
}
}
Expand Down Expand Up @@ -120,7 +122,8 @@ impl Intrinsic {
| Intrinsic::StrAsBytes
| Intrinsic::FromField
| Intrinsic::AsField
| Intrinsic::IsUnconstrained => false,
| Intrinsic::IsUnconstrained
| Intrinsic::DerivePedersenGenerators => false,

// Some black box functions have side-effects
Intrinsic::BlackBox(func) => matches!(
Expand Down Expand Up @@ -155,6 +158,7 @@ impl Intrinsic {
"as_field" => Some(Intrinsic::AsField),
"as_witness" => Some(Intrinsic::AsWitness),
"is_unconstrained" => Some(Intrinsic::IsUnconstrained),
"derive_pedersen_generators" => Some(Intrinsic::DerivePedersenGenerators),
other => BlackBoxFunc::lookup(other).map(Intrinsic::BlackBox),
}
}
Expand Down
52 changes: 52 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use fxhash::FxHashMap as HashMap;
use std::{collections::VecDeque, rc::Rc};

use acvm::{acir::AcirField, acir::BlackBoxFunc, BlackBoxResolutionError, FieldElement};
use bn254_blackbox_solver::derive_generators;
use iter_extended::vecmap;
use num_bigint::BigUint;

Expand Down Expand Up @@ -295,6 +296,13 @@ pub(super) fn simplify_call(
}
Intrinsic::AsWitness => SimplifyResult::None,
Intrinsic::IsUnconstrained => SimplifyResult::None,
Intrinsic::DerivePedersenGenerators => {
if let Some(Type::Array(_, len)) = ctrl_typevars.unwrap().first() {
simplify_derive_generators(dfg, arguments, *len as u32)
} else {
unreachable!("Derive Pedersen Generators must return an array");
}
}
}
}

Expand Down Expand Up @@ -626,3 +634,47 @@ fn simplify_signature(
_ => SimplifyResult::None,
}
}

fn simplify_derive_generators(
dfg: &mut DataFlowGraph,
arguments: &[ValueId],
num_generators: u32,
) -> SimplifyResult {
if arguments.len() == 2 {
let domain_separator_string = dfg.get_array_constant(arguments[0]);
let starting_index = dfg.get_numeric_constant(arguments[1]);
if let (Some(domain_separator_string), Some(starting_index)) =
(domain_separator_string, starting_index)
{
let domain_separator_bytes = domain_separator_string
.0
.iter()
.map(|&x| dfg.get_numeric_constant(x).unwrap().to_u128() as u8)
.collect::<Vec<u8>>();
let generators = derive_generators(
&domain_separator_bytes,
num_generators,
starting_index.try_to_u32().expect("argument is declared as u32"),
);
let is_infinite = dfg.make_constant(FieldElement::zero(), Type::bool());
let mut results = Vec::new();
for gen in generators {
let x_big: BigUint = gen.x.into();
let x = FieldElement::from_be_bytes_reduce(&x_big.to_bytes_be());
let y_big: BigUint = gen.y.into();
let y = FieldElement::from_be_bytes_reduce(&y_big.to_bytes_be());
results.push(dfg.make_constant(x, Type::field()));
results.push(dfg.make_constant(y, Type::field()));
results.push(is_infinite);
}
let len = results.len();
let result =
dfg.make_array(results.into(), Type::Array(vec![Type::field()].into(), len));
SimplifyResult::SimplifiedTo(result)
} else {
SimplifyResult::None
}
} else {
unreachable!("Unexpected number of arguments to derive_generators");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ impl Context {
| Intrinsic::AsField
| Intrinsic::AsSlice
| Intrinsic::AsWitness
| Intrinsic::IsUnconstrained => false,
| Intrinsic::IsUnconstrained
| Intrinsic::DerivePedersenGenerators => false,
},

// We must assume that functions contain a side effect as we cannot inspect more deeply.
Expand Down
3 changes: 2 additions & 1 deletion compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ fn slice_capacity_change(
| Intrinsic::FromField
| Intrinsic::AsField
| Intrinsic::AsWitness
| Intrinsic::IsUnconstrained => SizeChange::None,
| Intrinsic::IsUnconstrained
| Intrinsic::DerivePedersenGenerators => SizeChange::None,
}
}
30 changes: 26 additions & 4 deletions noir_stdlib/src/hash.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod poseidon2;
use crate::default::Default;
use crate::uint128::U128;
use crate::sha256::{digest, sha256_var};
use crate::embedded_curve_ops::EmbeddedCurvePoint;
use crate::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar};

#[foreign(sha256)]
// docs:start:sha256
Expand All @@ -28,7 +28,12 @@ pub fn blake3<N>(input: [u8; N]) -> [u8; 32]
// docs:start:pedersen_commitment
pub fn pedersen_commitment<N>(input: [Field; N]) -> EmbeddedCurvePoint {
// docs:end:pedersen_commitment
pedersen_commitment_with_separator(input, 0)
let value = pedersen_commitment_with_separator(input, 0);
if (value.x == 0) & (value.y == 0) {
EmbeddedCurvePoint { x: 0, y: 0, is_infinite: true }
} else {
EmbeddedCurvePoint { x: value.x, y: value.y, is_infinite: false }
}
}

#[foreign(pedersen_commitment)]
Expand All @@ -46,8 +51,25 @@ pub fn pedersen_hash<N>(input: [Field; N]) -> Field
pedersen_hash_with_separator(input, 0)
}

#[foreign(pedersen_hash)]
pub fn pedersen_hash_with_separator<N>(input: [Field; N], separator: u32) -> Field {}
#[field(bn254)]
fn derive_generators<N, M>(domain_separator_bytes: [u8; M], starting_index: u32) -> [EmbeddedCurvePoint; N] {
crate::assert_constant(domain_separator_bytes);
crate::assert_constant(starting_index);
__derive_generators(domain_separator_bytes, starting_index)
}

#[builtin(derive_pedersen_generators)]
#[field(bn254)]
fn __derive_generators<N, M>(domain_separator_bytes: [u8; M], starting_index: u32) -> [EmbeddedCurvePoint; N] {}

pub fn pedersen_hash_with_separator<N>(input: [Field; N], separator: u32) -> Field {
let v1 = pedersen_commitment(input);
let length_generator :[EmbeddedCurvePoint;1] = derive_generators("pedersen_hash_length".as_bytes(), separator);
crate::embedded_curve_ops::multi_scalar_mul(
[length_generator[0], v1],
[EmbeddedCurveScalar { lo: N as Field, hi: 0 }, EmbeddedCurveScalar { lo: 1, hi: 0 }]
)[0]
}

pub fn hash_to_field(inputs: [Field]) -> Field {
let mut sum = 0;
Expand Down

0 comments on commit d2ea8a9

Please sign in to comment.