diff --git a/.gitignore b/.gitignore index 088ba6ba..7b36af12 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +book/book \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index e46a7605..729b78c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,10 +21,13 @@ merlin = { version = "3.0", default-features = false } rand = "0.8.4" sha2 = "0.10.1" plonk-core = { git = "https://github.com/heliaxdev/ark-plonk/", features = ["trace"], branch="taiga/randomized-circuits" } +plonk-hashing = { git = "https://github.com/heliaxdev/ark-plonk/", branch="taiga/randomized-circuits" } blake2 = "0.9" rs_merkle = { git = "https://github.com/heliaxdev/rs-merkle/" } # "0.2.2" +lazy_static = "1" -poseidon377 = { git = "https://github.com/penumbra-zone/poseidon377/" } +[dev-dependencies] +ark-std = "0.3" [patch.crates-io] ark-bls12-377 = { git="https://github.com/simonmasson/curves/", branch="new-curve-models" } diff --git a/src/lib.rs b/src/lib.rs index 1ac218c0..915d0a74 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,14 @@ +use crate::poseidon::{ + POSEIDON_HASH_PARAM_BLS12_377_SCALAR_ARITY2, POSEIDON_HASH_PARAM_BLS12_377_SCALAR_ARITY4, + WIDTH_3, WIDTH_5, +}; use ark_bls12_377::Fr as Fr377; use ark_ec::twisted_edwards_extended::GroupAffine as TEGroupAffine; use ark_ec::{AffineCurve, TEModelParameters}; use ark_ff::*; use ark_serialize::CanonicalSerialize; use circuit::circuit_parameters::CircuitParameters; +use plonk_hashing::poseidon::poseidon::{NativeSpec, Poseidon}; use rs_merkle::{algorithms::Blake2s, Hasher, MerkleTree}; use sha2::{Digest, Sha512}; @@ -11,6 +16,7 @@ pub mod action; pub mod circuit; pub mod el_gamal; pub mod note; +pub mod poseidon; pub mod token; pub mod transaction; pub mod user; @@ -42,16 +48,49 @@ impl HashToField for Fr377 { .map(|elt| Fr377::from_le_bytes_mod_order(elt)) .collect(); - assert!(elts.len() <= 5); + // TODO: decide the length, support 4 for now. + assert!(elts.len() <= 4); match elts.len() { - 1 => poseidon377::hash_1(&Fr377::zero(), elts[0]), - 2 => poseidon377::hash_2(&Fr377::zero(), (elts[0], elts[1])), - 3 => poseidon377::hash_3(&Fr377::zero(), (elts[0], elts[1], elts[2])), - 4 => poseidon377::hash_4(&Fr377::zero(), (elts[0], elts[1], elts[2], elts[3])), - _ => poseidon377::hash_5( - &Fr377::zero(), - (elts[0], elts[1], elts[2], elts[3], elts[4]), - ), + 1 => { + let mut poseidon = Poseidon::<(), NativeSpec, WIDTH_3>::new( + &mut (), + &POSEIDON_HASH_PARAM_BLS12_377_SCALAR_ARITY2, + ); + poseidon.input(elts[0]).unwrap(); + poseidon.input(Fr377::zero()).unwrap(); + poseidon.output_hash(&mut ()) + } + 2 => { + let mut poseidon = Poseidon::<(), NativeSpec, WIDTH_3>::new( + &mut (), + &POSEIDON_HASH_PARAM_BLS12_377_SCALAR_ARITY2, + ); + poseidon.input(elts[0]).unwrap(); + poseidon.input(elts[1]).unwrap(); + poseidon.output_hash(&mut ()) + } + 3 => { + let mut poseidon = Poseidon::<(), NativeSpec, WIDTH_5>::new( + &mut (), + &POSEIDON_HASH_PARAM_BLS12_377_SCALAR_ARITY4, + ); + poseidon.input(elts[0]).unwrap(); + poseidon.input(elts[1]).unwrap(); + poseidon.input(elts[2]).unwrap(); + poseidon.input(Fr377::zero()).unwrap(); + poseidon.output_hash(&mut ()) + } + _ => { + let mut poseidon = Poseidon::<(), NativeSpec, WIDTH_5>::new( + &mut (), + &POSEIDON_HASH_PARAM_BLS12_377_SCALAR_ARITY4, + ); + poseidon.input(elts[0]).unwrap(); + poseidon.input(elts[1]).unwrap(); + poseidon.input(elts[2]).unwrap(); + poseidon.input(elts[3]).unwrap(); + poseidon.output_hash(&mut ()) + } } } } diff --git a/src/poseidon.rs b/src/poseidon.rs new file mode 100644 index 00000000..131b91cb --- /dev/null +++ b/src/poseidon.rs @@ -0,0 +1,68 @@ +use lazy_static::lazy_static; +use plonk_hashing::poseidon::constants::PoseidonConstants; + +// ARITY: input number of hash +// WIDTH_3 = ARITY + 1 +pub const WIDTH_3: usize = 3; +pub const WIDTH_5: usize = 5; +lazy_static! { + pub static ref POSEIDON_HASH_PARAM_BLS12_377_SCALAR_ARITY2: PoseidonConstants = + PoseidonConstants::generate::(); + pub static ref POSEIDON_HASH_PARAM_BLS12_377_SCALAR_ARITY4: PoseidonConstants = + PoseidonConstants::generate::(); + + // Hashes of bls12_377::BaseField are generated automatically, not tested yet. + // Especially we need to check the round number generation from the paper. + pub static ref POSEIDON_HASH_PARAM_BLS12_377_BASE_ARITY2: PoseidonConstants = + PoseidonConstants::generate::(); + pub static ref POSEIDON_HASH_PARAM_BLS12_377_BASE_ARITY4: PoseidonConstants = + PoseidonConstants::generate::(); +} + +#[test] +fn test_poseidon_circuit_example() { + use ark_ec::PairingEngine; + use ark_std::{test_rng, UniformRand}; + use plonk_hashing::poseidon::poseidon::{NativeSpec, PlonkSpec, Poseidon}; + type E = ark_bls12_377::Bls12_377; + type P = ark_ed_on_bls12_377::EdwardsParameters; + type Fr = ::Fr; + use plonk_core::constraint_system::StandardComposer; + + let mut rng = test_rng(); + let mut poseidon_native = Poseidon::<(), NativeSpec, WIDTH_3>::new( + &mut (), + &POSEIDON_HASH_PARAM_BLS12_377_SCALAR_ARITY2, + ); + let inputs = (0..(WIDTH_3 - 1)) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + + inputs.iter().for_each(|x| { + let _ = poseidon_native.input(*x).unwrap(); + }); + let native_hash: Fr = poseidon_native.output_hash(&mut ()); + + let mut c = StandardComposer::::new(); + let inputs_var = inputs.iter().map(|x| c.add_input(*x)).collect::>(); + let mut poseidon_circuit = Poseidon::<_, PlonkSpec, WIDTH_3>::new( + &mut c, + &POSEIDON_HASH_PARAM_BLS12_377_SCALAR_ARITY2, + ); + inputs_var.iter().for_each(|x| { + let _ = poseidon_circuit.input(*x).unwrap(); + }); + let plonk_hash = poseidon_circuit.output_hash(&mut c); + + c.check_circuit_satisfied(); + + let expected = c.add_input(native_hash); + c.assert_equal(expected, plonk_hash); + + c.check_circuit_satisfied(); + println!( + "circuit size for WIDTH_3 {} poseidon: {}", + WIDTH_3, + c.circuit_bound() + ) +}