From 93b73fb89ac896bd328a319396dd2cc1000ffd70 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Fri, 9 Apr 2021 12:40:43 -0700 Subject: [PATCH] Recursive Merkle proofs --- src/gadgets/hash.rs | 1 + src/gadgets/mod.rs | 1 - src/hash.rs | 21 +++++++++++++++++++-- src/lib.rs | 1 + src/{gadgets => }/merkle_proofs.rs | 30 ++++++++++++++++++++++-------- src/proof.rs | 20 ++++++++++++++++++-- 6 files changed, 61 insertions(+), 13 deletions(-) rename src/{gadgets => }/merkle_proofs.rs (73%) diff --git a/src/gadgets/hash.rs b/src/gadgets/hash.rs index cbf8f5e2..20b26244 100644 --- a/src/gadgets/hash.rs +++ b/src/gadgets/hash.rs @@ -7,6 +7,7 @@ use crate::hash::GMIMC_ROUNDS; use crate::target::Target; use crate::wire::Wire; +// TODO: Move to be next to native `permute`? impl CircuitBuilder { pub fn permute(&mut self, inputs: [Target; 12]) -> [Target; 12] { let zero = self.zero(); diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index c4f8dad4..ed84207e 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -1,4 +1,3 @@ pub mod arithmetic; pub mod hash; -pub(crate) mod merkle_proofs; pub(crate) mod split_join; diff --git a/src/hash.rs b/src/hash.rs index 9ac7b120..da7f1765 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -6,8 +6,10 @@ use rayon::prelude::*; use crate::field::field::Field; use crate::gmimc::gmimc_permute_array; -use crate::proof::Hash; +use crate::proof::{Hash, HashTarget}; use crate::util::reverse_index_bits_in_place; +use crate::circuit_builder::CircuitBuilder; +use crate::target::Target; pub(crate) const SPONGE_RATE: usize = 8; pub(crate) const SPONGE_CAPACITY: usize = 4; @@ -25,7 +27,7 @@ const ELEMS_PER_CHUNK: usize = 1 << 8; /// Hash the vector if necessary to reduce its length to ~256 bits. If it already fits, this is a /// no-op. -pub fn hash_or_noop(mut inputs: Vec) -> Hash { +pub fn hash_or_noop(inputs: Vec) -> Hash { if inputs.len() <= 4 { Hash::from_partial(inputs) } else { @@ -33,6 +35,21 @@ pub fn hash_or_noop(mut inputs: Vec) -> Hash { } } +impl CircuitBuilder { + pub fn hash_or_noop(&mut self, inputs: Vec) -> HashTarget { + let zero = self.zero(); + if inputs.len() <= 4 { + HashTarget::from_partial(inputs, zero) + } else { + self.hash_n_to_hash(inputs, false) + } + } + + pub fn hash_n_to_hash(&mut self, inputs: Vec, pad: bool) -> HashTarget { + todo!() + } +} + /// A one-way compression function which takes two ~256 bit inputs and returns a ~256 bit output. pub fn compress(x: Hash, y: Hash) -> Hash { let mut inputs = Vec::with_capacity(8); diff --git a/src/lib.rs b/src/lib.rs index 5f840e78..89762ead 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ pub mod gates; pub mod generator; pub mod gmimc; pub mod hash; +pub mod merkle_proofs; pub mod plonk_challenger; pub mod plonk_common; pub mod polynomial; diff --git a/src/gadgets/merkle_proofs.rs b/src/merkle_proofs.rs similarity index 73% rename from src/gadgets/merkle_proofs.rs rename to src/merkle_proofs.rs index 9154647b..7e1b5ecf 100644 --- a/src/gadgets/merkle_proofs.rs +++ b/src/merkle_proofs.rs @@ -53,7 +53,8 @@ impl CircuitBuilder { let height = proof.siblings.len(); let purported_index_bits = self.split_le_virtual(leaf_index, height); - let mut state: Vec = todo!(); // hash leaf data + let mut state: HashTarget = self.hash_or_noop(leaf_data); + let mut acc_leaf_index = zero; for (bit, sibling) in purported_index_bits.into_iter().zip(proof.siblings) { let gate = self.add_gate_no_constants( @@ -63,26 +64,39 @@ impl CircuitBuilder { let swap_wire = Target::Wire(Wire { gate, input: swap_wire }); self.generate_copy(bit, swap_wire); + let old_acc_wire = GMiMCGate::::WIRE_INDEX_ACCUMULATOR_OLD; + let old_acc_wire = Target::Wire(Wire { gate, input: old_acc_wire }); + self.route(acc_leaf_index, old_acc_wire); + + let new_acc_wire = GMiMCGate::::WIRE_INDEX_ACCUMULATOR_NEW; + let new_acc_wire = Target::Wire(Wire { gate, input: new_acc_wire }); + acc_leaf_index = new_acc_wire; + let input_wires = (0..12) .map(|i| Target::Wire( Wire { gate, input: GMiMCGate::::wire_input(i) })) .collect::>(); for i in 0..4 { - self.route(state[i], input_wires[i]); + self.route(state.elements[i], input_wires[i]); self.route(sibling.elements[i], input_wires[4 + i]); self.route(zero, input_wires[8 + i]); } - state = (0..4) + state = HashTarget::from_vec((0..4) .map(|i| Target::Wire( Wire { gate, input: GMiMCGate::::wire_output(i) })) - .collect::>() - .try_into() - .unwrap(); + .collect()) } - // TODO: Verify that weighted sum of bits matches index. - // TODO: Verify that state matches merkle root. + self.assert_equal(acc_leaf_index, leaf_index); + + self.assert_hashes_equal(state, merkle_root) + } + + pub(crate) fn assert_hashes_equal(&mut self, x: HashTarget, y: HashTarget) { + for i in 0..4 { + self.assert_equal(x.elements[i], y.elements[i]); + } } } diff --git a/src/proof.rs b/src/proof.rs index 41d29f09..88513ce8 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,6 +1,7 @@ use crate::field::field::Field; use crate::target::Target; -use crate::gadgets::merkle_proofs::{MerkleProofTarget, MerkleProof}; +use crate::merkle_proofs::{MerkleProofTarget, MerkleProof}; +use std::convert::TryInto; /// Represents a ~256 bit hash output. #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -20,7 +21,22 @@ impl Hash { /// Represents a ~256 bit hash output. pub struct HashTarget { - pub(crate) elements: Vec, + pub(crate) elements: [Target; 4], +} + +impl HashTarget { + pub(crate) fn from_vec(elements: Vec) -> Self { + debug_assert!(elements.len() == 4); + HashTarget { elements: elements.try_into().unwrap() } + } + + pub(crate) fn from_partial(mut elements: Vec, zero: Target) -> Self { + debug_assert!(elements.len() <= 4); + while elements.len() < 4 { + elements.push(zero); + } + Self { elements: [elements[0], elements[1], elements[2], elements[3]] } + } } pub struct Proof {