Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions benches/recursive-snark-supernova.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ fn bench_one_augmented_circuit_recursive_snark(c: &mut Criterion) {
RecursiveSNARK::iter_base_step(
&running_claim1,
digest,
program_counter,
Some(program_counter),
0,
1,
&z0_primary,
Expand Down Expand Up @@ -216,7 +216,7 @@ fn bench_two_augmented_circuit_recursive_snark(c: &mut Criterion) {
RecursiveSNARK::iter_base_step(
&running_claim1,
digest,
program_counter,
Some(program_counter),
0,
2,
&z0_primary,
Expand Down Expand Up @@ -315,16 +315,16 @@ where
fn synthesize<CS: ConstraintSystem<F>>(
&self,
cs: &mut CS,
pc: &AllocatedNum<F>,
pc: Option<&AllocatedNum<F>>,
z: &[AllocatedNum<F>],
) -> Result<(AllocatedNum<F>, Vec<AllocatedNum<F>>), SynthesisError> {
) -> Result<(Option<AllocatedNum<F>>, Vec<AllocatedNum<F>>), SynthesisError> {
// Consider a an equation: `x^2 = y`, where `x` and `y` are respectively the input and output.
let mut x = z[0].clone();
let mut y = x.clone();
for i in 0..self.num_cons {
y = x.square(cs.namespace(|| format!("x_sq_{i}")))?;
x = y.clone();
}
Ok((pc.clone(), vec![y]))
Ok((pc.cloned(), vec![y]))
}
}
51 changes: 33 additions & 18 deletions src/supernova/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub struct SuperNovaAugmentedCircuitInputs<'a, G: Group> {
U: Option<&'a [Option<RelaxedR1CSInstance<G>>]>,
u: Option<&'a R1CSInstance<G>>,
T: Option<&'a Commitment<G>>,
program_counter: G::Base,
program_counter: Option<G::Base>,
last_augmented_circuit_index: G::Base,
}

Expand All @@ -91,7 +91,7 @@ impl<'a, G: Group> SuperNovaAugmentedCircuitInputs<'a, G> {
U: Option<&'a [Option<RelaxedR1CSInstance<G>>]>,
u: Option<&'a R1CSInstance<G>>,
T: Option<&'a Commitment<G>>,
program_counter: G::Base,
program_counter: Option<G::Base>,
last_augmented_circuit_index: G::Base,
) -> Self {
Self {
Expand Down Expand Up @@ -178,7 +178,15 @@ impl<'a, G: Group, SC: StepCircuit<G::Base>> SuperNovaAugmentedCircuit<'a, G, SC
let program_counter = if self.params.is_primary_circuit {
Some(AllocatedNum::alloc(
cs.namespace(|| "program_counter"),
|| Ok(self.inputs.get()?.program_counter),
|| {
Ok(
self
.inputs
.get()?
.program_counter
.expect("program_counter missing"),
Comment on lines +186 to +187
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a panic rather than SynthesisError::AssignementMissing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because of the extremely confusing convention of how Option is overloaded in the bellman line of circuit-generation (phrased thus because maybe bellpepper will eventually help clean this up). Specifically, if inputs are missing (meaning we are synthesizing a blank shape), then the .get()? on line 185 will indeed result in the SynthesisError::AssignmentMissing. However, if that doesn't happen and inputs have been supplied, then those inputs are either for a primary or secondary circuit. In the secondary case, the program counter will not be present. However, in this code branch, we are specifically handling a primary circuit — so we require the actual value for the witness.

)
},
)?)
} else {
None
Expand Down Expand Up @@ -330,14 +338,21 @@ impl<'a, G: Group, SC: StepCircuit<G::Base>> SuperNovaAugmentedCircuit<'a, G, SC
let mut ro = G::ROCircuit::new(
self.ro_consts.clone(),
2 // params_next, i_new
+ program_counter.as_ref().map_or(0, |_| 1) // optional program counter
+ program_counter.as_ref().map_or(0, |_|
usize::from(self.params.is_primary_circuit)
) // optional program counter
+ 2 * arity // zo, z1
+ num_augmented_circuits * (7 + 2 * self.params.n_limbs), // #num_augmented_circuits * (7 + [X0, X1]*#num_limb)
);
ro.absorb(&params);
ro.absorb(&i);
if let Some(program_counter) = program_counter.as_ref() {
ro.absorb(program_counter)

if self.params.is_primary_circuit {
if let Some(program_counter) = program_counter.as_ref() {
ro.absorb(program_counter)
} else {
Err(SynthesisError::AssignmentMissing)?
}
}

for e in &z_0 {
Expand Down Expand Up @@ -407,7 +422,7 @@ impl<'a, G: Group, SC: StepCircuit<G::Base>> SuperNovaAugmentedCircuit<'a, G, SC
pub fn synthesize<CS: ConstraintSystem<<G as Group>::Base>>(
self,
cs: &mut CS,
) -> Result<(AllocatedNum<G::Base>, Vec<AllocatedNum<G::Base>>), SynthesisError> {
) -> Result<(Option<AllocatedNum<G::Base>>, Vec<AllocatedNum<G::Base>>), SynthesisError> {
// NOTE `last_augmented_circuit_index` is aux without any constraint.
// Reason is prover can only produce valid running instance by folding u into proper U_i[last_augmented_circuit_index]
// However, there is crucial pre-asumption: `last_augmented_circuit_index` must within range [0, num_augmented_circuits)
Expand Down Expand Up @@ -534,16 +549,12 @@ impl<'a, G: Group, SC: StepCircuit<G::Base>> SuperNovaAugmentedCircuit<'a, G, SC
&Boolean::from(is_base_case),
)?;

let (program_counter_new, z_next) = if let Some(program_counter) = &program_counter {
self
.step_circuit
.synthesize(&mut cs.namespace(|| "F"), program_counter, &z_input)?
} else {
let zero_program_counter = alloc_zero(cs.namespace(|| "zero pc"))?;
self
.step_circuit
.synthesize(&mut cs.namespace(|| "F"), &zero_program_counter, &z_input)?
};
let (program_counter_new, z_next) = self.step_circuit.synthesize(
&mut cs.namespace(|| "F"),
program_counter.as_ref(),
&z_input,
)?;

if z_next.len() != arity {
return Err(SynthesisError::IncompatibleLengthVector(
"z_next".to_string(),
Expand Down Expand Up @@ -576,7 +587,11 @@ impl<'a, G: Group, SC: StepCircuit<G::Base>> SuperNovaAugmentedCircuit<'a, G, SC
ro.absorb(&i_next);
// optionally absorb program counter if exist
if program_counter.is_some() {
ro.absorb(&program_counter_new)
ro.absorb(
program_counter_new
.as_ref()
.expect("new program counter missing"),
)
}
for e in &z_0 {
ro.absorb(e);
Expand Down
23 changes: 18 additions & 5 deletions src/supernova/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ where
pub fn iter_base_step<C1: StepCircuit<G1::Scalar>, C2: StepCircuit<G2::Scalar>>(
claim: &RunningClaim<G1, G2, C1, C2>,
pp_digest: G1::Scalar,
initial_program_counter: G1::Scalar,
initial_program_counter: Option<G1::Scalar>,
first_augmented_circuit_index: usize,
num_augmented_circuits: usize,
z0_primary: &[G1::Scalar],
Expand Down Expand Up @@ -329,7 +329,7 @@ where
None,
Some(&u_primary),
None,
G2::Scalar::ZERO, // secondary circuit never constrain/bump program counter
None,
G2::Scalar::from(claim.augmented_circuit_index as u64),
);
let circuit_secondary: SuperNovaAugmentedCircuit<'_, G1, C2> = SuperNovaAugmentedCircuit::new(
Expand Down Expand Up @@ -379,6 +379,7 @@ where
})
.collect::<Result<Vec<<G1 as Group>::Scalar>, SuperNovaError>>()?;
let zi_primary_pc_next = zi_primary_pc_next
.expect("zi_primary_pc_next missing")
.get_value()
.ok_or(SuperNovaError::NovaError(NovaError::SynthesisError))?;
let zi_secondary = zi_secondary
Expand Down Expand Up @@ -473,7 +474,7 @@ where
Some(&self.r_U_secondary),
Some(&self.l_u_secondary),
Some(&T),
self.program_counter,
Some(self.program_counter),
G1::Scalar::ZERO,
);

Expand Down Expand Up @@ -540,7 +541,7 @@ where
Some(&self.r_U_primary),
Some(&l_u_primary),
Some(&binding),
G2::Scalar::ZERO, // secondary circuit never constrain/bump program counter
None,
G2::Scalar::from(claim.get_augmented_circuit_index() as u64),
);

Expand Down Expand Up @@ -573,6 +574,7 @@ where
})
.collect::<Result<Vec<<G1 as Group>::Scalar>, SuperNovaError>>()?;
let zi_primary_pc_next = zi_primary_pc_next
.expect("zi_primary_pc_next missing")
.get_value()
.ok_or(SuperNovaError::NovaError(NovaError::SynthesisError))?;
let zi_secondary = zi_secondary
Expand Down Expand Up @@ -659,7 +661,7 @@ where

// secondary circuit
let num_field_secondary_ro = 2 // params_next, i_new
+ 2 * pp.F_arity_primary // zo, z1
+ 2 * pp.F_arity_secondary // zo, z1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👍

+ self.num_augmented_circuits * (7 + 2 * pp.augmented_circuit_params_primary.get_n_limbs()); // #num_augmented_circuits * (7 + [X0, X1]*#num_limb)

let (hash_primary, hash_secondary) = {
Expand Down Expand Up @@ -810,3 +812,14 @@ where

(ck_primary, ck_secondary)
}

/// SuperNova helper trait, for implementors that provide sets of sub-circuits to be proved via NIVC.
pub trait CircuitSet<G: Group> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you looking for an heterogeneous list (or more likely ExactSizeIterator) of dyn StepCircuit<F>, where F: Primefield?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, but this trait will end up doing some heavy lifting, and it's not really clear yet what it needs to look like. The above is just a first foot in the door. I will iterate in future revisions and bear your ideas in mind.

/// Initial program counter, defaults to zero.
fn initial_program_counter(&self) -> G::Scalar {
G::Scalar::ZERO
}

/// How many augmented circuits are provided?
fn num_augmented_circuits(&self) -> usize;
}
Loading