Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
Implement RootCircuit for SuperCircuit aggregation (#1000)
Browse files Browse the repository at this point in the history
* feat: implement `AggregationConfig` and `RootCircuit` for `SuperCircuit` aggregation

* fix: return `Result` instead of `Option`

* fix: `instances` to `instance`

* fix: add error handling when there is identity point in protocol or proof, and comments why we need to assign some random values in testing circuit `StandardPlonk`
  • Loading branch information
han0110 authored Feb 9, 2023
1 parent b62b7dc commit 76ba1f0
Show file tree
Hide file tree
Showing 6 changed files with 1,003 additions and 3 deletions.
48 changes: 47 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions zkevm-circuits/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ libsecp256k1 = "0.7"
num-bigint = { version = "0.4" }
subtle = "2.4"
rand_chacha = "0.3"
snark-verifier = { git = "https://github.com/privacy-scaling-explorations/snark-verifier", tag = "v2023_02_02", default-features = false, features = ["loader_halo2", "system_halo2"] }

[dev-dependencies]
bus-mapping = { path = "../bus-mapping", features = ["test"] }
Expand Down
1 change: 1 addition & 0 deletions zkevm-circuits/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod evm_circuit;
pub mod exp_circuit;
pub mod keccak_circuit;
pub mod pi_circuit;
pub mod root_circuit;
pub mod state_circuit;
pub mod super_circuit;
pub mod table;
Expand Down
223 changes: 223 additions & 0 deletions zkevm-circuits/src/root_circuit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
//! The Root circuit implementation.
use halo2_proofs::{
arithmetic::Field,
circuit::{Layouter, SimpleFloorPlanner, Value},
halo2curves::serde::SerdeObject,
plonk::{Circuit, ConstraintSystem, Error},
poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG},
};
use itertools::Itertools;
use maingate::MainGateInstructions;
use snark_verifier::{util::arithmetic::MultiMillerLoop, verifier::plonk::PlonkProtocol};
use std::iter;

mod aggregation;

pub use aggregation::{
aggregate, AggregationConfig, EccChip, Halo2Loader, KzgAs, KzgDk, KzgSvk,
PlonkSuccinctVerifier, PlonkVerifier, PoseidonTranscript, Snark, SnarkWitness, BITS, LIMBS,
};
pub use snark_verifier::system::halo2::{compile, Config};

#[cfg(any(feature = "test", test))]
pub use aggregation::TestAggregationCircuit;

/// RootCircuit for aggregating SuperCircuit into a much smaller proof.
pub struct RootCircuit<'a, M: MultiMillerLoop> {
svk: KzgSvk<M>,
snark: SnarkWitness<'a, M::G1Affine>,
instance: Vec<M::Scalar>,
}

impl<'a, M: MultiMillerLoop> RootCircuit<'a, M>
where
M::G1Affine: SerdeObject,
M::G2Affine: SerdeObject,
{
/// Create a `RootCircuit` with accumulator computed given a `SuperCircuit`
/// proof and its instance. Returns `None` if given proof is invalid.
pub fn new(
params: &ParamsKZG<M>,
super_circuit_protocol: &'a PlonkProtocol<M::G1Affine>,
super_circuit_instances: Value<&'a Vec<Vec<M::Scalar>>>,
super_circuit_proof: Value<&'a [u8]>,
) -> Result<Self, snark_verifier::Error> {
let num_instances = super_circuit_protocol.num_instance.iter().sum::<usize>() + 4 * LIMBS;
let instance = {
let mut instance = Ok(vec![M::Scalar::zero(); num_instances]);
super_circuit_instances
.as_ref()
.zip(super_circuit_proof.as_ref())
.map(|(super_circuit_instances, super_circuit_proof)| {
let snark = Snark::new(
super_circuit_protocol,
super_circuit_instances,
super_circuit_proof,
);
instance = aggregate::<M>(params, [snark]).map(|accumulator_limbs| {
iter::empty()
// Propagate `SuperCircuit`'s instance
.chain(super_circuit_instances.iter().flatten().cloned())
// Output aggregated accumulator limbs
.chain(accumulator_limbs)
.collect_vec()
});
});
instance?
};
debug_assert_eq!(instance.len(), num_instances);

Ok(Self {
svk: KzgSvk::<M>::new(params.get_g()[0]),
snark: SnarkWitness::new(
super_circuit_protocol,
super_circuit_instances,
super_circuit_proof,
),
instance,
})
}

/// Returns accumulator indices in instance columns, which will be in
/// the last `4 * LIMBS` rows of instance column in `MainGate`.
pub fn accumulator_indices(&self) -> Vec<(usize, usize)> {
let offset = self.snark.protocol().num_instance.iter().sum::<usize>();
(offset..).map(|idx| (0, idx)).take(4 * LIMBS).collect()
}

/// Returns number of instance
pub fn num_instance(&self) -> Vec<usize> {
vec![self.snark.protocol().num_instance.iter().sum::<usize>() + 4 * LIMBS]
}

/// Returns instance
pub fn instance(&self) -> Vec<Vec<M::Scalar>> {
vec![self.instance.clone()]
}
}

impl<'a, M: MultiMillerLoop> Circuit<M::Scalar> for RootCircuit<'a, M> {
type Config = AggregationConfig;
type FloorPlanner = SimpleFloorPlanner;

fn without_witnesses(&self) -> Self {
Self {
svk: self.svk,
snark: self.snark.without_witnesses(),
instance: vec![M::Scalar::zero(); self.instance.len()],
}
}

fn configure(meta: &mut ConstraintSystem<M::Scalar>) -> Self::Config {
AggregationConfig::configure::<M::G1Affine>(meta)
}

fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<M::Scalar>,
) -> Result<(), Error> {
config.load_table(&mut layouter)?;
let (instance, accumulator_limbs) =
config.aggregate::<M>(&mut layouter, &self.svk, [self.snark])?;

// Constrain equality to instance values
let main_gate = config.main_gate();
for (row, limb) in instance
.into_iter()
.flatten()
.flatten()
.chain(accumulator_limbs)
.enumerate()
{
main_gate.expose_public(layouter.namespace(|| ""), limb, row)?;
}

Ok(())
}
}

#[cfg(test)]
mod test {
use crate::{
root_circuit::{compile, Config, PoseidonTranscript, RootCircuit},
super_circuit::{super_circuit_tests::block_1tx, SuperCircuit},
};
use bus_mapping::circuit_input_builder::CircuitsParams;
use halo2_proofs::{
circuit::Value,
dev::MockProver,
halo2curves::bn256::Bn256,
plonk::{create_proof, keygen_pk, keygen_vk},
poly::kzg::{
commitment::{KZGCommitmentScheme, ParamsKZG},
multiopen::ProverGWC,
},
};
use itertools::Itertools;
use rand::rngs::OsRng;

#[ignore = "Due to high memory requirement"]
#[test]
fn test_root_circuit() {
let (params, protocol, proof, instance) = {
// Preprocess
const MAX_TXS: usize = 1;
const MAX_CALLDATA: usize = 32;
const TEST_MOCK_RANDOMNESS: u64 = 0x100;
let circuits_params = CircuitsParams {
max_txs: MAX_TXS,
max_calldata: MAX_CALLDATA,
max_rws: 256,
max_copy_rows: 256,
max_bytecode: 512,
keccak_padding: None,
};
let (k, circuit, instance, _) =
SuperCircuit::<_, MAX_TXS, MAX_CALLDATA, TEST_MOCK_RANDOMNESS>::build(
block_1tx(),
circuits_params,
)
.unwrap();
let params = ParamsKZG::<Bn256>::setup(k, OsRng);
let pk = keygen_pk(&params, keygen_vk(&params, &circuit).unwrap(), &circuit).unwrap();
let protocol = compile(
&params,
pk.get_vk(),
Config::kzg()
.with_num_instance(instance.iter().map(|instance| instance.len()).collect()),
);

// Create proof
let proof = {
let mut transcript = PoseidonTranscript::new(Vec::new());
create_proof::<KZGCommitmentScheme<_>, ProverGWC<_>, _, _, _, _>(
&params,
&pk,
&[circuit],
&[&instance.iter().map(Vec::as_slice).collect_vec()],
OsRng,
&mut transcript,
)
.unwrap();
transcript.finalize()
};

(params, protocol, proof, instance)
};

let root_circuit = RootCircuit::new(
&params,
&protocol,
Value::known(&instance),
Value::known(&proof),
)
.unwrap();
assert_eq!(
MockProver::run(26, &root_circuit, root_circuit.instance())
.unwrap()
.verify_par(),
Ok(())
);
}
}
Loading

0 comments on commit 76ba1f0

Please sign in to comment.