Skip to content

Commit 503f862

Browse files
invalid witness length
1 parent 728e28b commit 503f862

File tree

3 files changed

+152
-108
lines changed

3 files changed

+152
-108
lines changed

src/nova/insert.rs

Lines changed: 14 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
use crate::{
2-
nova::utils::{next_rom_index_and_pc, Digest},
3-
tree::{Hasher, InsertProof, SPARSE_MERKLE_PLACEHOLDER_HASH},
2+
nova::utils::{
3+
allocate_bits_to_binary_number, next_rom_index_and_pc, verify_membership_proof, Digest,
4+
},
5+
tree::InsertProof,
46
};
57
use anyhow::Result;
68
use arecibo::supernova::StepCircuit;
7-
use bellpepper::gadgets::sha256::sha256;
8-
use bellpepper_core::{
9-
boolean::{AllocatedBit, Boolean},
10-
num::AllocatedNum,
11-
ConstraintSystem, SynthesisError,
12-
};
9+
use bellpepper_core::{num::AllocatedNum, ConstraintSystem, SynthesisError};
1310
use ff::{PrimeField, PrimeFieldBits};
14-
use jmt::proof::{SparseMerkleLeafNode, SparseMerkleNode, SparseMerkleProof};
11+
use sha2::Sha256;
1512

1613
#[derive(Clone)]
1714
pub struct InsertCircuit<F> {
@@ -54,11 +51,6 @@ impl<Scalar: PrimeField + PrimeFieldBits> StepCircuit<Scalar> for InsertCircuit<
5451
pc,
5552
)?;
5653

57-
let old_root_bits = allocate_bits_to_binary_number(
58-
cs,
59-
Some(self.proof.non_membership_proof.root.to_bytes().to_vec()),
60-
)?;
61-
6254
let pre_insertion_scalar = Digest::new(self.proof.non_membership_proof.root)
6355
.to_scalar()
6456
.map_err(|_| SynthesisError::Unsatisfiable);
@@ -81,6 +73,13 @@ impl<Scalar: PrimeField + PrimeFieldBits> StepCircuit<Scalar> for InsertCircuit<
8173
.map_err(|_| SynthesisError::Unsatisfiable)
8274
})?;
8375

76+
let new_root_bits =
77+
allocate_bits_to_binary_number(cs, self.proof.membership_proof.root_hash().0.to_vec())?;
78+
79+
self.proof
80+
.verify()
81+
.map_err(|_| SynthesisError::Unsatisfiable)?;
82+
8483
// Verify the non-membership proof
8584
// verify_non_membership_proof(
8685
// cs.namespace(|| "non_membership_proof"),
@@ -95,8 +94,7 @@ impl<Scalar: PrimeField + PrimeFieldBits> StepCircuit<Scalar> for InsertCircuit<
9594
.leaf()
9695
.ok_or(SynthesisError::AssignmentMissing)?;
9796

98-
// Verify the membership proof (update)
99-
verify_membership_proof(cs, &self.proof.membership_proof, &old_root_bits, *leaf)?;
97+
verify_membership_proof(cs, &self.proof.membership_proof, &new_root_bits, *leaf)?;
10098

10199
let mut z_next = vec![new_root];
102100
z_next.push(rom_index_next);
@@ -109,89 +107,3 @@ impl<Scalar: PrimeField + PrimeFieldBits> StepCircuit<Scalar> for InsertCircuit<
109107
0
110108
}
111109
}
112-
113-
fn allocate_bits_to_binary_number<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
114-
cs: &mut CS,
115-
value: Option<Vec<u8>>,
116-
) -> Result<Vec<Boolean>, SynthesisError> {
117-
let bits = value
118-
.map(|bytes| {
119-
bytes
120-
.iter()
121-
.flat_map(|byte| (0..8).map(move |i| (byte >> i) & 1 == 1))
122-
.collect::<Vec<_>>()
123-
})
124-
.unwrap_or_else(|| vec![false; 256]);
125-
126-
let mut result = Vec::new();
127-
for (i, &bit) in bits.iter().enumerate() {
128-
let allocated_bit = AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(bit))?;
129-
result.push(Boolean::from(allocated_bit));
130-
}
131-
Ok(result)
132-
}
133-
134-
// fn verify_non_membership_proof<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
135-
// mut cs: CS,
136-
// proof: &NonMembershipProof,
137-
// root: &[Boolean],
138-
// key: &[Boolean],
139-
// ) -> Result<(), SynthesisError> {
140-
// // 1. Hash the key
141-
// let key_hash = sha256(cs.namespace(|| "hash key"), key)?;
142-
143-
// // 2. Traverse the Merkle path
144-
145-
// // 3. Check that the computed root does not match the given root
146-
147-
// Ok(())
148-
// }
149-
150-
fn hash_node<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
151-
cs: &mut CS,
152-
node: &SparseMerkleNode,
153-
) -> Result<Vec<Boolean>, SynthesisError> {
154-
match node {
155-
SparseMerkleNode::Leaf(node) => {
156-
let node_bits = allocate_bits_to_binary_number(cs, Some(node.to_bytes()))?;
157-
sha256(cs.namespace(|| "hash key"), &node_bits)
158-
}
159-
SparseMerkleNode::Internal(node) => {
160-
let node_bits = allocate_bits_to_binary_number(cs, Some(node.to_bytes()))?;
161-
sha256(cs.namespace(|| "hash key"), &node_bits)
162-
}
163-
SparseMerkleNode::Null => allocate_bits_to_binary_number(
164-
cs,
165-
Some(SPARSE_MERKLE_PLACEHOLDER_HASH.to_bytes().to_vec()),
166-
),
167-
}
168-
}
169-
170-
fn verify_membership_proof<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
171-
cs: &mut CS,
172-
proof: &SparseMerkleProof<Hasher>,
173-
root: &Vec<Boolean>,
174-
leaf: SparseMerkleLeafNode,
175-
) -> Result<(), SynthesisError> {
176-
// let leaf = self.proof.membership_proof.leaf().ok_or(SynthesisError::Unsatisfiable)?;
177-
let mut current = hash_node(cs, &SparseMerkleNode::Leaf(leaf))?;
178-
179-
for (i, sibling) in proof.siblings().iter().enumerate() {
180-
let sibling_hash = hash_node(cs, sibling)?;
181-
182-
current = sha256(
183-
cs.namespace(|| format!("hash node {}", i)),
184-
&[current, sibling_hash].concat(),
185-
)?;
186-
}
187-
188-
for (i, (computed_bit, given_bit)) in current.iter().zip(root.iter()).enumerate() {
189-
Boolean::enforce_equal(
190-
cs.namespace(|| format!("root bit {} should be equal", i)),
191-
computed_bit,
192-
given_bit,
193-
)?;
194-
}
195-
196-
Ok(())
197-
}

src/nova/update.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
use crate::{
2-
nova::utils::{next_rom_index_and_pc, Digest as NovaDigest},
2+
nova::utils::{
3+
allocate_bits_to_binary_number, next_rom_index_and_pc, verify_membership_proof,
4+
Digest as NovaDigest,
5+
},
36
tree::UpdateProof,
47
};
58
use anyhow::Result;
69
use arecibo::supernova::StepCircuit;
710
use bellpepper_core::{num::AllocatedNum, ConstraintSystem, SynthesisError};
8-
use ff::PrimeField;
11+
use ff::{PrimeField, PrimeFieldBits};
912

1013
#[derive(Clone)]
1114
pub struct UpdateCircuit<F> {
@@ -14,7 +17,7 @@ pub struct UpdateCircuit<F> {
1417
_phantom: std::marker::PhantomData<F>,
1518
}
1619

17-
impl<F: PrimeField> UpdateCircuit<F> {
20+
impl<F: PrimeField + PrimeFieldBits> UpdateCircuit<F> {
1821
pub fn new(update_proof: UpdateProof, rom_size: usize) -> Self {
1922
Self {
2023
update_proof,
@@ -26,7 +29,7 @@ impl<F: PrimeField> UpdateCircuit<F> {
2629

2730
impl<F> StepCircuit<F> for UpdateCircuit<F>
2831
where
29-
F: PrimeField,
32+
F: PrimeField + PrimeFieldBits,
3033
{
3134
fn arity(&self) -> usize {
3235
2 + self.rom_size // old_root + rom_index + rom[].len()
@@ -67,13 +70,26 @@ where
6770
.map_err(|_| SynthesisError::Unsatisfiable);
6871
let new_root = AllocatedNum::alloc(cs.namespace(|| "new_root"), || new_scalar)?;
6972

73+
// TODO: The provided merkle root is an inclusion proof of the node before the update.
74+
// We actually need to create our own merkle proof by hashing the new node to verify the update
75+
let old_root_bits =
76+
allocate_bits_to_binary_number(cs, self.update_proof.old_root.0.to_vec())?;
77+
7078
cs.enforce(
7179
|| "z0 == pre_insertion_root",
7280
|lc| lc + old_root.get_variable(),
7381
|lc| lc + CS::one(),
7482
|lc| lc + pre_insertion_root.get_variable(),
7583
);
76-
// // TODO: bellpepper merkle proof gadget
84+
85+
let update_proof = &self.update_proof.proof.proofs()[0];
86+
87+
let leaf = &update_proof
88+
.leaf()
89+
.ok_or(SynthesisError::AssignmentMissing)?;
90+
91+
verify_membership_proof(cs, update_proof, &old_root_bits, *leaf)?;
92+
7793
self.update_proof
7894
.verify()
7995
.map_err(|_| SynthesisError::Unsatisfiable)?;

src/nova/utils.rs

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,14 @@ use bellpepper_core::{
1212
};
1313
use ff::PrimeField;
1414
use itertools::Itertools as _;
15+
use jmt::bytes32ext::Bytes32Ext;
16+
use jmt::mock::MockTreeStore;
17+
use jmt::proof::{
18+
SparseMerkleInternalNode, SparseMerkleLeafNode, SparseMerkleNode, SparseMerkleProof,
19+
INTERNAL_DOMAIN_SEPARATOR,
20+
};
1521
use jmt::RootHash;
16-
use jmt::{mock::MockTreeStore, KeyHash};
22+
use sha2::Sha256;
1723
use std::marker::PhantomData;
1824
use std::sync::Arc;
1925

@@ -185,3 +191,113 @@ pub fn create_pp() -> PublicParams<PallasEngine> {
185191
let circuit_sequence = EpochCircuitSequence::<E1>::new(operations);
186192
PublicParams::setup(&circuit_sequence, &*default_ck_hint(), &*default_ck_hint())
187193
}
194+
195+
pub fn allocate_bits_to_binary_number<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
196+
cs: &mut CS,
197+
value: Vec<u8>,
198+
) -> Result<Vec<Boolean>, SynthesisError> {
199+
let bits: Vec<bool> = value
200+
.iter()
201+
.flat_map(|byte| (0..8).rev().map(move |i| (byte >> i) & 1 == 1))
202+
.collect();
203+
204+
let result: Result<Vec<Boolean>, SynthesisError> = bits
205+
.into_iter()
206+
.enumerate()
207+
.map(|(i, bit)| {
208+
let allocated_bit =
209+
AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(bit))?;
210+
Ok(Boolean::from(allocated_bit))
211+
})
212+
.collect();
213+
214+
result
215+
}
216+
217+
pub fn hash_node<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
218+
cs: &mut CS,
219+
node: &SparseMerkleNode,
220+
) -> Result<Vec<Boolean>, SynthesisError> {
221+
match node {
222+
SparseMerkleNode::Leaf(node) => {
223+
let node_bits = allocate_bits_to_binary_number(cs, node.to_bytes())?;
224+
sha256(cs.namespace(|| "hash key"), &node_bits)
225+
}
226+
SparseMerkleNode::Internal(node) => {
227+
let node_bits = allocate_bits_to_binary_number(cs, node.to_bytes())?;
228+
sha256(cs.namespace(|| "hash key"), &node_bits)
229+
}
230+
SparseMerkleNode::Null => {
231+
allocate_bits_to_binary_number(cs, SPARSE_MERKLE_PLACEHOLDER_HASH.to_bytes().to_vec())
232+
}
233+
}
234+
}
235+
236+
pub fn verify_membership_proof<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
237+
cs: &mut CS,
238+
proof: &SparseMerkleProof<Hasher>,
239+
root: &Vec<Boolean>,
240+
leaf: SparseMerkleLeafNode,
241+
) -> Result<(), SynthesisError> {
242+
let mut current = hash_node(cs, &SparseMerkleNode::Leaf(leaf))?;
243+
244+
let element_key = leaf.key_hash;
245+
246+
for (i, (sibling, key_bit)) in proof
247+
.siblings()
248+
.iter()
249+
.zip(
250+
element_key
251+
.0
252+
.iter_bits()
253+
.rev()
254+
.skip(256 - proof.siblings().len()),
255+
)
256+
.enumerate()
257+
{
258+
let sibling_hash = hash_node(cs, sibling)?;
259+
let separator = allocate_bits_to_binary_number(cs, INTERNAL_DOMAIN_SEPARATOR.to_vec())?;
260+
261+
let mut result = Vec::new();
262+
if key_bit {
263+
result.extend_from_slice(&separator);
264+
result.extend_from_slice(&sibling_hash);
265+
result.extend_from_slice(&current);
266+
} else {
267+
result.extend_from_slice(&separator);
268+
result.extend_from_slice(&current);
269+
result.extend_from_slice(&sibling_hash);
270+
}
271+
272+
current = sha256(
273+
cs.namespace(|| format!("hash node {}", i)),
274+
result.as_slice(),
275+
)?;
276+
}
277+
278+
for (i, (computed_bit, given_bit)) in current.iter().zip(root.iter()).enumerate() {
279+
Boolean::enforce_equal(
280+
cs.namespace(|| format!("root bit {} should be equal", i)),
281+
computed_bit,
282+
given_bit,
283+
)?;
284+
}
285+
286+
Ok(())
287+
}
288+
289+
fn boolvec_to_bytes(value: Vec<Boolean>) -> Vec<u8> {
290+
let bits: Vec<bool> = value
291+
.iter()
292+
.map(|b| b.get_value().unwrap_or(false))
293+
.collect();
294+
295+
bits.chunks(8)
296+
.map(|chunk| {
297+
chunk
298+
.iter()
299+
.enumerate()
300+
.fold(0u8, |acc, (i, &bit)| acc | ((bit as u8) << i))
301+
})
302+
.collect()
303+
}

0 commit comments

Comments
 (0)