mirror of
https://github.com/logos-storage/proof-aggregation.git
synced 2026-01-04 06:43:07 +00:00
improve circuit description.
This commit is contained in:
parent
0c40b2e338
commit
623bc65dba
@ -32,7 +32,7 @@ pub struct NodeCircuit<
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// recursion node targets
|
/// recursion node targets
|
||||||
/// leaf_proofs: leaf proofs
|
/// leaf_proofs: leaf proofs - can be real or dummy
|
||||||
/// node_verifier_data: node verifier data, note: leaf verifier data is constant
|
/// node_verifier_data: node verifier data, note: leaf verifier data is constant
|
||||||
/// condition: for switching between leaf and node verifier data
|
/// condition: for switching between leaf and node verifier data
|
||||||
/// index: index of the node
|
/// index: index of the node
|
||||||
@ -75,7 +75,7 @@ impl<
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
leaf_verifier_data: VerifierCircuitData<F, C, D>,
|
leaf_verifier_data: VerifierCircuitData<F, C, D>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
assert!(N.is_power_of_two(), "M is NOT a power of two");
|
assert!(N.is_power_of_two(), "N is NOT a power of two");
|
||||||
Self{
|
Self{
|
||||||
leaf_verifier_data,
|
leaf_verifier_data,
|
||||||
phantom_data:PhantomData::default(),
|
phantom_data:PhantomData::default(),
|
||||||
@ -97,15 +97,30 @@ impl<
|
|||||||
type Targets = NodeTargets<D>;
|
type Targets = NodeTargets<D>;
|
||||||
type Input = NodeInput<F, D, C>;
|
type Input = NodeInput<F, D, C>;
|
||||||
|
|
||||||
|
/// Adds the in-circuit targets for the Node recursion circuit.
|
||||||
|
///
|
||||||
|
/// This function builds all necessary virtual targets for verifying `N` leaf proofs
|
||||||
|
/// and constructing the node-level public inputs. It performs the following steps:
|
||||||
|
/// 1. Creates virtual proof targets for each leaf proof and extracts their public inputs.
|
||||||
|
/// 2. Hashes and registers the concatenated public inputs of the inner proofs.
|
||||||
|
/// 3. Sets up virtual verifier data targets for node and leaf circuits and selects
|
||||||
|
/// between leaf and node verifier data based on the `condition` flag.
|
||||||
|
/// 4. Verifies each proof in-circuit, switching to dummy verifier data for flagged dummy proofs.
|
||||||
|
/// 5. Enforces zero flag buckets for any dummy proof to maintain soundness.
|
||||||
|
/// 6. Checks that each inner proof's index matches the expected index
|
||||||
|
/// i.e. it should be in the range of [S, S-1] where S = `index` * `N`
|
||||||
|
/// 7. Aggregates inner flag buckets across proofs into final flag bucket targets.
|
||||||
|
/// 8. Registers the final flag buckets as public inputs.
|
||||||
|
///
|
||||||
fn add_targets(&self, builder: &mut CircuitBuilder<F, D>, register_pi: bool) -> Result<Self::Targets> {
|
fn add_targets(&self, builder: &mut CircuitBuilder<F, D>, register_pi: bool) -> Result<Self::Targets> {
|
||||||
let inner_common = self.leaf_verifier_data.common.clone();
|
let inner_common = self.leaf_verifier_data.common.clone();
|
||||||
let zero_target = builder.zero();
|
let zero_target = builder.zero();
|
||||||
|
|
||||||
// assert public input is of size 8 + 1 (index) + B (flag buckets)
|
// assert public input is of size 8 (2 hash digests) + 1 (index) + B (flag buckets)
|
||||||
let n_bucket: usize = bucket_count(T);
|
let n_bucket: usize = bucket_count(T);
|
||||||
assert_eq!(inner_common.num_public_inputs, 9+n_bucket);
|
assert_eq!(inner_common.num_public_inputs, 9+n_bucket);
|
||||||
|
|
||||||
// the proof virtual targets - M proofs
|
// the proof virtual targets - N proofs
|
||||||
let mut vir_proofs = vec![];
|
let mut vir_proofs = vec![];
|
||||||
let mut pub_input = vec![];
|
let mut pub_input = vec![];
|
||||||
let mut inner_flag_buckets = vec![];
|
let mut inner_flag_buckets = vec![];
|
||||||
@ -114,29 +129,42 @@ impl<
|
|||||||
let vir_proof = builder.add_virtual_proof_with_pis(&inner_common);
|
let vir_proof = builder.add_virtual_proof_with_pis(&inner_common);
|
||||||
let inner_pub_input = vir_proof.public_inputs.clone();
|
let inner_pub_input = vir_proof.public_inputs.clone();
|
||||||
vir_proofs.push(vir_proof);
|
vir_proofs.push(vir_proof);
|
||||||
|
// public input [0...4] contains hash of inner proof public inputs
|
||||||
pub_input.extend_from_slice(&inner_pub_input[0..4]);
|
pub_input.extend_from_slice(&inner_pub_input[0..4]);
|
||||||
|
// public input [4...8] are skipped since they contain the inner verifier data
|
||||||
|
// public input [8] contains the index
|
||||||
inner_indexes.push(inner_pub_input[8]);
|
inner_indexes.push(inner_pub_input[8]);
|
||||||
|
// public input [9..(9+n_bucket)] contains the flag buckets
|
||||||
inner_flag_buckets.push(inner_pub_input[9..(9+n_bucket)].to_vec());
|
inner_flag_buckets.push(inner_pub_input[9..(9+n_bucket)].to_vec());
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash the public input & make it public
|
// hash the public input of all N inner proofs & make it public
|
||||||
let hash_inner_pub_input = builder.hash_n_to_hash_no_pad::<H>(pub_input);
|
let hash_inner_pub_input = builder.hash_n_to_hash_no_pad::<H>(pub_input);
|
||||||
if register_pi{
|
if register_pi{
|
||||||
builder.register_public_inputs(&hash_inner_pub_input.elements);
|
builder.register_public_inputs(&hash_inner_pub_input.elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual target for the verifier data
|
// virtual target for the verifier data
|
||||||
|
// this is the verifier data for the node which cannot be constant
|
||||||
|
// since it is unknown at compile time
|
||||||
let node_verifier_data = builder.add_virtual_verifier_data(inner_common.config.fri_config.cap_height);
|
let node_verifier_data = builder.add_virtual_verifier_data(inner_common.config.fri_config.cap_height);
|
||||||
|
|
||||||
// virtual target for the verifier data
|
// constant verifier data target
|
||||||
|
// this is the verifier data for the leaf which is known at this point and is constant
|
||||||
let const_leaf_verifier_data = builder.constant_verifier_data(&self.leaf_verifier_data.verifier_only);
|
let const_leaf_verifier_data = builder.constant_verifier_data(&self.leaf_verifier_data.verifier_only);
|
||||||
|
|
||||||
// virtual constant target for dummy verifier data
|
// constant target for dummy verifier data
|
||||||
|
// this can be the verifier data for either the node or the leaf
|
||||||
|
// doesn't matter if the given inner proof (leaf or node) is dummy anyway
|
||||||
|
// it just has to be in the same structure as the real verifier data.
|
||||||
let const_dummy_vd = builder.constant_verifier_data(
|
let const_dummy_vd = builder.constant_verifier_data(
|
||||||
&DummyProofGen::<F,D,C>::gen_dummy_verifier_data(&inner_common)
|
&DummyProofGen::<F,D,C>::gen_dummy_verifier_data(&inner_common)
|
||||||
);
|
);
|
||||||
|
|
||||||
// register only the node verifier data hash as public input.
|
// register only the node verifier data hash as public input.
|
||||||
|
// we need to register this as public input since this verifier data is supplied by the prover
|
||||||
|
// so it must be verified later by the verifier. Otherwise, the prover can use a dummy verifier data
|
||||||
|
// we don't need to register the leaf verifier data or the dummy verifier data since they are constants
|
||||||
let mut vd_pub_input = vec![];
|
let mut vd_pub_input = vec![];
|
||||||
vd_pub_input.extend_from_slice(&node_verifier_data.circuit_digest.elements);
|
vd_pub_input.extend_from_slice(&node_verifier_data.circuit_digest.elements);
|
||||||
for i in 0..builder.config.fri_config.num_cap_elements() {
|
for i in 0..builder.config.fri_config.num_cap_elements() {
|
||||||
@ -148,18 +176,23 @@ impl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// condition for switching between node and leaf
|
// condition for switching between node and leaf
|
||||||
|
// we need this to switch between node and leaf verifier data since
|
||||||
|
// the leaf and node circuit contain different logic -> different verifier data.
|
||||||
let condition = builder.add_virtual_bool_target_safe();
|
let condition = builder.add_virtual_bool_target_safe();
|
||||||
|
|
||||||
// flag buckets targets
|
// flag buckets targets
|
||||||
let mut flag_buckets: Vec<Target> = (0..n_bucket).map(|_i| zero_target.clone()).collect();
|
let mut flag_buckets: Vec<Target> = (0..n_bucket).map(|_i| zero_target.clone()).collect();
|
||||||
// index: 0 <= index < T where T = total number of proofs
|
// index: 0 <= index < T where T = total number of proofs
|
||||||
let index = builder.add_virtual_public_input();
|
let index = builder.add_virtual_public_input();
|
||||||
|
// N flags, one for each inner proof
|
||||||
let flags: Vec<BoolTarget> = (0..N).map(|_i| builder.add_virtual_bool_target_safe()).collect();
|
let flags: Vec<BoolTarget> = (0..N).map(|_i| builder.add_virtual_bool_target_safe()).collect();
|
||||||
|
|
||||||
|
// select the verifier data based on the condition - switch between node and leaf
|
||||||
// condition: true -> node, false -> leaf
|
// condition: true -> node, false -> leaf
|
||||||
let node_or_leaf_vd = builder.select_verifier_data(condition.clone(), &node_verifier_data, &const_leaf_verifier_data);
|
let node_or_leaf_vd = builder.select_verifier_data(condition.clone(), &node_verifier_data, &const_leaf_verifier_data);
|
||||||
// verify the proofs in-circuit - M proofs
|
// verify the proofs in-circuit - N proofs
|
||||||
for i in 0..N {
|
for i in 0..N {
|
||||||
|
// select the verifier data based on the flag - switch between real and dummy
|
||||||
// flag: true -> real, false -> dummy
|
// flag: true -> real, false -> dummy
|
||||||
let selected_vd = builder.select_verifier_data(flags[i].clone(), &node_or_leaf_vd, &const_dummy_vd);
|
let selected_vd = builder.select_verifier_data(flags[i].clone(), &node_or_leaf_vd, &const_dummy_vd);
|
||||||
builder.verify_proof::<C>(&vir_proofs[i], &selected_vd, &inner_common);
|
builder.verify_proof::<C>(&vir_proofs[i], &selected_vd, &inner_common);
|
||||||
@ -179,7 +212,9 @@ impl<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check inner proof indexes are correct
|
// check inner proof indices are correct
|
||||||
|
// we expect the inner proof indices to be in the range [`index` * N, `index` * N + N - 1]
|
||||||
|
// e.g. if index = 0, then we expect inner proof indices to be in the range [0, N - 1]
|
||||||
let m_const = builder.constant(F::from_canonical_u64(N as u64));
|
let m_const = builder.constant(F::from_canonical_u64(N as u64));
|
||||||
let mut expected_inner_index = builder.mul(index, m_const);
|
let mut expected_inner_index = builder.mul(index, m_const);
|
||||||
for i in 0..N {
|
for i in 0..N {
|
||||||
@ -217,7 +252,7 @@ impl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn assign_targets(&self, pw: &mut PartialWitness<F>, targets: &Self::Targets, input: &Self::Input) -> Result<()> {
|
fn assign_targets(&self, pw: &mut PartialWitness<F>, targets: &Self::Targets, input: &Self::Input) -> Result<()> {
|
||||||
// assert size of proofs vec
|
// assert size of vec
|
||||||
assert_eq!(input.inner_proofs.len(), N);
|
assert_eq!(input.inner_proofs.len(), N);
|
||||||
assert_eq!(input.flags.len(), N);
|
assert_eq!(input.flags.len(), N);
|
||||||
assert!(input.index <= T, "given index is not valid");
|
assert!(input.index <= T, "given index is not valid");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user