This crate provides an implementation of the Monolith hash function that can be employed in the Plonky2 proving system. Monolith hash function is a new zk-friendly hash function which is much faster than state-of-the-art zk-friendly hash functions, exhibiting performance similar to the Keccak hash function. In particular, according to our initial benchmarks, Monolith is from 2 to 3 times faster than Poseidon, the current hash function employed in the Plonky2 proving system.
This crate can be employed to:
- Generate Plonky2 proofs employing Monolith hash function
- Write Plonky2 circuits computing Monolith hashes, which is also useful to recursively verify Plonky2 proofs generated with Monolith. To this extent, this crate provides a Plonky2 gate for the Monolith permutation.
The crate also provides benchmarks that compare the Monolith implementation and the Monolith gate with the corresponding Poseidon components currently employed in Plonky2.
Generate a proof employing Monolith hash function:
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::iop::witness::PartialWitness;
use plonky2::field::goldilocks_field::GoldilocksField;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::field::types::Sample;
use plonky2::iop::witness::WitnessWrite;
use plonky2_monolith::monolith_hash::monolith_goldilocks::MonolithGoldilocksConfig;
use std::error::Error;
const D: usize = 2;
type F = GoldilocksField;
fn main() -> Result<(), Box<dyn Error>> {
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let init_t = builder.add_virtual_public_input();
let mut res_t = builder.add_virtual_target();
builder.connect(init_t, res_t);
for _ in 0..100 {
res_t = builder.mul(res_t, init_t);
}
builder.register_public_input(res_t);
let data = builder.build::<MonolithGoldilocksConfig>();
let mut pw = PartialWitness::<F>::new();
let input = F::rand();
pw.set_target(init_t, input);
let proof = data.prove(pw)?;
Ok(data.verify(proof)?)
}
Build a circuit employing Monolith gate:
use std::cmp;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::iop::witness::PartialWitness;
use plonky2::field::goldilocks_field::GoldilocksField;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::field::types::Sample;
use plonky2::iop::witness::WitnessWrite;
use plonky2::gates::gate::Gate;
use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS;
use plonky2_monolith::monolith_hash::monolith_goldilocks::MonolithGoldilocksConfig;
use plonky2_monolith::gates::monolith::MonolithGate;
use plonky2_monolith::monolith_hash::MonolithHash;
use std::error::Error;
const D: usize = 2;
type F = GoldilocksField;
fn generate_config_for_monolith() -> CircuitConfig {
let needed_wires = cmp::max(MonolithGate::<F,D>::new().num_wires(), CircuitConfig::standard_recursion_config().num_wires);
CircuitConfig {
num_wires: needed_wires,
num_routed_wires: needed_wires,
..CircuitConfig::standard_recursion_config()
}
}
fn main() -> Result<(), Box<dyn Error>> {
let config = generate_config_for_monolith();
let mut builder = CircuitBuilder::<F, D>::new(config);
let inp_targets_array = builder.add_virtual_target_arr::<{NUM_HASH_OUT_ELTS}>();
let mut res_targets_array = inp_targets_array.clone();
for _ in 0..100 {
res_targets_array = builder.hash_or_noop::<MonolithHash>(res_targets_array.to_vec()).elements;
}
builder.register_public_inputs(&res_targets_array);
let data = builder.build::<MonolithGoldilocksConfig>();
let mut pw = PartialWitness::<F>::new();
inp_targets_array.into_iter().for_each(|t| {
let input = F::rand();
pw.set_target(t, input);
});
let proof = data.prove(pw)?;
Ok(data.verify(proof)?)
}