Skip to content

Commit

Permalink
invalid witness length
Browse files Browse the repository at this point in the history
  • Loading branch information
distractedm1nd committed Sep 9, 2024
1 parent 728e28b commit 503f862
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 108 deletions.
116 changes: 14 additions & 102 deletions src/nova/insert.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
use crate::{
nova::utils::{next_rom_index_and_pc, Digest},
tree::{Hasher, InsertProof, SPARSE_MERKLE_PLACEHOLDER_HASH},
nova::utils::{
allocate_bits_to_binary_number, next_rom_index_and_pc, verify_membership_proof, Digest,
},
tree::InsertProof,
};
use anyhow::Result;
use arecibo::supernova::StepCircuit;
use bellpepper::gadgets::sha256::sha256;
use bellpepper_core::{
boolean::{AllocatedBit, Boolean},
num::AllocatedNum,
ConstraintSystem, SynthesisError,
};
use bellpepper_core::{num::AllocatedNum, ConstraintSystem, SynthesisError};
use ff::{PrimeField, PrimeFieldBits};
use jmt::proof::{SparseMerkleLeafNode, SparseMerkleNode, SparseMerkleProof};
use sha2::Sha256;

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

let old_root_bits = allocate_bits_to_binary_number(
cs,
Some(self.proof.non_membership_proof.root.to_bytes().to_vec()),
)?;

let pre_insertion_scalar = Digest::new(self.proof.non_membership_proof.root)
.to_scalar()
.map_err(|_| SynthesisError::Unsatisfiable);
Expand All @@ -81,6 +73,13 @@ impl<Scalar: PrimeField + PrimeFieldBits> StepCircuit<Scalar> for InsertCircuit<
.map_err(|_| SynthesisError::Unsatisfiable)
})?;

let new_root_bits =
allocate_bits_to_binary_number(cs, self.proof.membership_proof.root_hash().0.to_vec())?;

self.proof
.verify()
.map_err(|_| SynthesisError::Unsatisfiable)?;

// Verify the non-membership proof
// verify_non_membership_proof(
// cs.namespace(|| "non_membership_proof"),
Expand All @@ -95,8 +94,7 @@ impl<Scalar: PrimeField + PrimeFieldBits> StepCircuit<Scalar> for InsertCircuit<
.leaf()
.ok_or(SynthesisError::AssignmentMissing)?;

// Verify the membership proof (update)
verify_membership_proof(cs, &self.proof.membership_proof, &old_root_bits, *leaf)?;
verify_membership_proof(cs, &self.proof.membership_proof, &new_root_bits, *leaf)?;

let mut z_next = vec![new_root];
z_next.push(rom_index_next);
Expand All @@ -109,89 +107,3 @@ impl<Scalar: PrimeField + PrimeFieldBits> StepCircuit<Scalar> for InsertCircuit<
0
}
}

fn allocate_bits_to_binary_number<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
cs: &mut CS,
value: Option<Vec<u8>>,
) -> Result<Vec<Boolean>, SynthesisError> {
let bits = value
.map(|bytes| {
bytes
.iter()
.flat_map(|byte| (0..8).map(move |i| (byte >> i) & 1 == 1))
.collect::<Vec<_>>()
})
.unwrap_or_else(|| vec![false; 256]);

let mut result = Vec::new();
for (i, &bit) in bits.iter().enumerate() {
let allocated_bit = AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(bit))?;
result.push(Boolean::from(allocated_bit));
}
Ok(result)
}

// fn verify_non_membership_proof<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
// mut cs: CS,
// proof: &NonMembershipProof,
// root: &[Boolean],
// key: &[Boolean],
// ) -> Result<(), SynthesisError> {
// // 1. Hash the key
// let key_hash = sha256(cs.namespace(|| "hash key"), key)?;

// // 2. Traverse the Merkle path

// // 3. Check that the computed root does not match the given root

// Ok(())
// }

fn hash_node<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
cs: &mut CS,
node: &SparseMerkleNode,
) -> Result<Vec<Boolean>, SynthesisError> {
match node {
SparseMerkleNode::Leaf(node) => {
let node_bits = allocate_bits_to_binary_number(cs, Some(node.to_bytes()))?;
sha256(cs.namespace(|| "hash key"), &node_bits)
}
SparseMerkleNode::Internal(node) => {
let node_bits = allocate_bits_to_binary_number(cs, Some(node.to_bytes()))?;
sha256(cs.namespace(|| "hash key"), &node_bits)
}
SparseMerkleNode::Null => allocate_bits_to_binary_number(
cs,
Some(SPARSE_MERKLE_PLACEHOLDER_HASH.to_bytes().to_vec()),
),
}
}

fn verify_membership_proof<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
cs: &mut CS,
proof: &SparseMerkleProof<Hasher>,
root: &Vec<Boolean>,
leaf: SparseMerkleLeafNode,
) -> Result<(), SynthesisError> {
// let leaf = self.proof.membership_proof.leaf().ok_or(SynthesisError::Unsatisfiable)?;
let mut current = hash_node(cs, &SparseMerkleNode::Leaf(leaf))?;

for (i, sibling) in proof.siblings().iter().enumerate() {
let sibling_hash = hash_node(cs, sibling)?;

current = sha256(
cs.namespace(|| format!("hash node {}", i)),
&[current, sibling_hash].concat(),
)?;
}

for (i, (computed_bit, given_bit)) in current.iter().zip(root.iter()).enumerate() {
Boolean::enforce_equal(
cs.namespace(|| format!("root bit {} should be equal", i)),
computed_bit,
given_bit,
)?;
}

Ok(())
}
26 changes: 21 additions & 5 deletions src/nova/update.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use crate::{
nova::utils::{next_rom_index_and_pc, Digest as NovaDigest},
nova::utils::{
allocate_bits_to_binary_number, next_rom_index_and_pc, verify_membership_proof,
Digest as NovaDigest,
},
tree::UpdateProof,
};
use anyhow::Result;
use arecibo::supernova::StepCircuit;
use bellpepper_core::{num::AllocatedNum, ConstraintSystem, SynthesisError};
use ff::PrimeField;
use ff::{PrimeField, PrimeFieldBits};

#[derive(Clone)]
pub struct UpdateCircuit<F> {
Expand All @@ -14,7 +17,7 @@ pub struct UpdateCircuit<F> {
_phantom: std::marker::PhantomData<F>,
}

impl<F: PrimeField> UpdateCircuit<F> {
impl<F: PrimeField + PrimeFieldBits> UpdateCircuit<F> {
pub fn new(update_proof: UpdateProof, rom_size: usize) -> Self {
Self {
update_proof,
Expand All @@ -26,7 +29,7 @@ impl<F: PrimeField> UpdateCircuit<F> {

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

// TODO: The provided merkle root is an inclusion proof of the node before the update.
// We actually need to create our own merkle proof by hashing the new node to verify the update
let old_root_bits =
allocate_bits_to_binary_number(cs, self.update_proof.old_root.0.to_vec())?;

cs.enforce(
|| "z0 == pre_insertion_root",
|lc| lc + old_root.get_variable(),
|lc| lc + CS::one(),
|lc| lc + pre_insertion_root.get_variable(),
);
// // TODO: bellpepper merkle proof gadget

let update_proof = &self.update_proof.proof.proofs()[0];

let leaf = &update_proof
.leaf()
.ok_or(SynthesisError::AssignmentMissing)?;

verify_membership_proof(cs, update_proof, &old_root_bits, *leaf)?;

self.update_proof
.verify()
.map_err(|_| SynthesisError::Unsatisfiable)?;
Expand Down
118 changes: 117 additions & 1 deletion src/nova/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ use bellpepper_core::{
};
use ff::PrimeField;
use itertools::Itertools as _;
use jmt::bytes32ext::Bytes32Ext;
use jmt::mock::MockTreeStore;
use jmt::proof::{
SparseMerkleInternalNode, SparseMerkleLeafNode, SparseMerkleNode, SparseMerkleProof,
INTERNAL_DOMAIN_SEPARATOR,
};
use jmt::RootHash;
use jmt::{mock::MockTreeStore, KeyHash};
use sha2::Sha256;
use std::marker::PhantomData;
use std::sync::Arc;

Expand Down Expand Up @@ -185,3 +191,113 @@ pub fn create_pp() -> PublicParams<PallasEngine> {
let circuit_sequence = EpochCircuitSequence::<E1>::new(operations);
PublicParams::setup(&circuit_sequence, &*default_ck_hint(), &*default_ck_hint())
}

pub fn allocate_bits_to_binary_number<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
cs: &mut CS,
value: Vec<u8>,
) -> Result<Vec<Boolean>, SynthesisError> {
let bits: Vec<bool> = value
.iter()
.flat_map(|byte| (0..8).rev().map(move |i| (byte >> i) & 1 == 1))
.collect();

let result: Result<Vec<Boolean>, SynthesisError> = bits
.into_iter()
.enumerate()
.map(|(i, bit)| {
let allocated_bit =
AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(bit))?;
Ok(Boolean::from(allocated_bit))
})
.collect();

result
}

pub fn hash_node<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
cs: &mut CS,
node: &SparseMerkleNode,
) -> Result<Vec<Boolean>, SynthesisError> {
match node {
SparseMerkleNode::Leaf(node) => {
let node_bits = allocate_bits_to_binary_number(cs, node.to_bytes())?;
sha256(cs.namespace(|| "hash key"), &node_bits)
}
SparseMerkleNode::Internal(node) => {
let node_bits = allocate_bits_to_binary_number(cs, node.to_bytes())?;
sha256(cs.namespace(|| "hash key"), &node_bits)
}
SparseMerkleNode::Null => {
allocate_bits_to_binary_number(cs, SPARSE_MERKLE_PLACEHOLDER_HASH.to_bytes().to_vec())
}
}
}

pub fn verify_membership_proof<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
cs: &mut CS,
proof: &SparseMerkleProof<Hasher>,
root: &Vec<Boolean>,
leaf: SparseMerkleLeafNode,
) -> Result<(), SynthesisError> {
let mut current = hash_node(cs, &SparseMerkleNode::Leaf(leaf))?;

let element_key = leaf.key_hash;

for (i, (sibling, key_bit)) in proof
.siblings()
.iter()
.zip(
element_key
.0
.iter_bits()
.rev()
.skip(256 - proof.siblings().len()),
)
.enumerate()
{
let sibling_hash = hash_node(cs, sibling)?;
let separator = allocate_bits_to_binary_number(cs, INTERNAL_DOMAIN_SEPARATOR.to_vec())?;

let mut result = Vec::new();
if key_bit {
result.extend_from_slice(&separator);
result.extend_from_slice(&sibling_hash);
result.extend_from_slice(&current);
} else {
result.extend_from_slice(&separator);
result.extend_from_slice(&current);
result.extend_from_slice(&sibling_hash);
}

current = sha256(
cs.namespace(|| format!("hash node {}", i)),
result.as_slice(),
)?;
}

for (i, (computed_bit, given_bit)) in current.iter().zip(root.iter()).enumerate() {
Boolean::enforce_equal(
cs.namespace(|| format!("root bit {} should be equal", i)),
computed_bit,
given_bit,
)?;
}

Ok(())
}

fn boolvec_to_bytes(value: Vec<Boolean>) -> Vec<u8> {
let bits: Vec<bool> = value
.iter()
.map(|b| b.get_value().unwrap_or(false))
.collect();

bits.chunks(8)
.map(|chunk| {
chunk
.iter()
.enumerate()
.fold(0u8, |acc, (i, &bit)| acc | ((bit as u8) << i))
})
.collect()
}

0 comments on commit 503f862

Please sign in to comment.