Skip to content

Commit

Permalink
Merge pull request #33 from okx/hash
Browse files Browse the repository at this point in the history
add hash_to_scalar
  • Loading branch information
doutv authored Jun 13, 2024
2 parents 9c3f6f0 + 2e18d4b commit 140d97a
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 1 deletion.
14 changes: 13 additions & 1 deletion ecgfp5/src/curve/scalar_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@ use core::{
iter::{Product, Sum},
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
};
use plonky2::hash::hash_types::HashOut;
use plonky2_field::extension::quintic::QuinticExtension;
use rand::RngCore;

use itertools::Itertools;
use num::{bigint::BigUint, One};
use serde::{Deserialize, Serialize};

use plonky2_field::types::{Field, PrimeField, PrimeField64, Sample};
use plonky2_field::{
extension::FieldExtension,
goldilocks_field::GoldilocksField,
types::{Field, PrimeField, PrimeField64, Sample},
};

use super::GFp5;

Expand Down Expand Up @@ -462,6 +467,13 @@ impl Scalar {
Self::from_noncanonical_biguint(biguint_from_array(limbs.map(|l| l.to_canonical_u64())))
}

pub fn from_hashout(x: HashOut<GoldilocksField>) -> Self {
let mut arr: [GoldilocksField; 5] = [GoldilocksField::ZERO; 5];
arr[1..].copy_from_slice(&x.elements);
let gfp5 = GFp5::from_basefield_array(arr);
Self::from_gfp5(gfp5)
}

/// Decode the provided byte slice into a scalar. The bytes are
/// interpreted into an integer in little-endian unsigned convention.
/// All slice bytes are read, and the value is REDUCED modulo n. This
Expand Down
1 change: 1 addition & 0 deletions ecgfp5/src/gadgets/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod base_field;
pub mod curve;
pub mod poseidon;
pub mod scalar_field;
pub mod schnorr;
95 changes: 95 additions & 0 deletions ecgfp5/src/gadgets/poseidon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use crate::curve::scalar_field::Scalar;
use plonky2::{
hash::poseidon::PoseidonHash,
iop::target::Target,
plonk::{circuit_builder::CircuitBuilder, config::Hasher},
};
use plonky2_ecdsa::gadgets::nonnative::NonNativeTarget;
use plonky2_field::{goldilocks_field::GoldilocksField, types::Field};

use super::base_field::{CircuitBuilderGFp5, QuinticExtensionTarget};

pub fn hash_to_scalar(domain: &[u8], msg: &[u8]) -> Scalar {
let f_domain = u8_to_goldilocks(domain);
let f_msg = u8_to_goldilocks(msg);
let hashout = PoseidonHash::hash_no_pad(&[f_domain.as_slice(), f_msg.as_slice()].concat());
Scalar::from_hashout(hashout)
}

pub fn hash_to_scalar_target(
builder: &mut CircuitBuilder<GoldilocksField, 2>,
domain: &[u8],
msg: Vec<Target>,
) -> NonNativeTarget<Scalar> {
let mut preimage = vec![];
let f_domain: Vec<Target> =
u8_to_goldilocks(domain).iter().map(|x| builder.constant(*x)).collect();
preimage.extend(f_domain);
preimage.extend(msg);
let hashout = builder.hash_n_to_hash_no_pad::<PoseidonHash>(preimage);
let mut limbs = [builder.zero(); 5];
limbs[1..].copy_from_slice(&hashout.elements);
let result = QuinticExtensionTarget::new(limbs);
builder.encode_quintic_ext_as_scalar(result)
}

/// Convert [u8; 8] to one GoldilocksField
///
/// non-canoncial [u8; 8] will panic
pub fn u8_to_goldilocks(data: &[u8]) -> Vec<GoldilocksField> {
const CHUNK_SIZE: usize = 8;
data.chunks(CHUNK_SIZE)
.map(|chunk| {
let mut padded = [0u8; CHUNK_SIZE];
let len = chunk.len().min(CHUNK_SIZE);
padded[..len].copy_from_slice(&chunk[..len]);
GoldilocksField::from_canonical_u64(u64::from_le_bytes(padded))
})
.collect::<Vec<GoldilocksField>>()
}

#[cfg(test)]
mod tests {
use plonky2::{
iop::witness::PartialWitness,
plonk::{
circuit_data::CircuitConfig,
config::{GenericConfig, PoseidonGoldilocksConfig},
},
};
use plonky2_ecdsa::gadgets::nonnative::PartialWitnessNonNative;
use plonky2_field::types::Field64;

use super::*;

#[test]
#[should_panic]
fn test_u8_to_goldilocks_noncanonical() {
let order_plus_one = (GoldilocksField::ORDER + 1).to_le_bytes();
// noncanonical u64 will panic
u8_to_goldilocks(&order_plus_one);
}

#[test]
fn test_hash_to_scalar() {
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;

let domain = b"domain-plonky2-ecgfp5-poseidon";
let msg = b"msg-to-hash-to-scalar";
let scalar = hash_to_scalar(domain, msg);

let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let msg_target: Vec<Target> =
u8_to_goldilocks(msg).iter().map(|x| builder.constant(*x)).collect();
let scalar_target = hash_to_scalar_target(&mut builder, domain, msg_target);

let circuit = builder.build::<C>();
let mut pw = PartialWitness::new();
pw.set_nonnative_target(scalar_target, scalar);
let proof = circuit.prove(pw).unwrap();
circuit.verify(proof).unwrap();
}
}

0 comments on commit 140d97a

Please sign in to comment.