From 6a44766cdc70e0fcf8e843ac2e52b1d330b0dc0c Mon Sep 17 00:00:00 2001 From: ec2 Date: Tue, 5 Sep 2023 19:10:03 +0000 Subject: [PATCH 01/17] add instance column to step config --- lightclient-circuits/src/sync_step_circuit.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 198fac23..ebc7b2c8 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -47,7 +47,7 @@ use halo2_ecc::{ use halo2_proofs::{ circuit::{Layouter, Region, SimpleFloorPlanner, Value}, dev::MockProver, - plonk::{Circuit, ConstraintSystem, Error, ProvingKey}, + plonk::{Circuit, ConstraintSystem, Error, ProvingKey, Instance, Column}, poly::{commitment::Params, kzg::commitment::ParamsKZG}, }; use halo2curves::{ @@ -68,6 +68,7 @@ pub struct SyncStepCircuitConfig { range: RangeConfig, sha256_config: RefCell>, challenges: Challenges>, + pi: Column, } #[allow(type_alias_bounds)] @@ -99,9 +100,11 @@ impl Circuit for SyncStepCircuit { fn configure(meta: &mut ConstraintSystem) -> Self::Config { let range = RangeCircuitBuilder::configure(meta); let sha256_config = SpreadConfig::::configure(meta, 8, 1); - + let pi = meta.instance_column(); + meta.enable_equality(pi); SyncStepCircuitConfig { range, + pi, sha256_config: RefCell::new(sha256_config), challenges: Challenges::mock(Value::known(Sha256CircuitConfig::fixed_challenge())), } From c757ab9a7a183053a44f1a1eaeb4e4a6079a350e Mon Sep 17 00:00:00 2001 From: ec2 Date: Tue, 5 Sep 2023 19:53:25 +0000 Subject: [PATCH 02/17] assign instance commitment --- lightclient-circuits/src/sync_step_circuit.rs | 60 ++++++++++++++++--- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index ebc7b2c8..1b6fb8d3 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -47,7 +47,7 @@ use halo2_ecc::{ use halo2_proofs::{ circuit::{Layouter, Region, SimpleFloorPlanner, Value}, dev::MockProver, - plonk::{Circuit, ConstraintSystem, Error, ProvingKey, Instance, Column}, + plonk::{Circuit, Column, ConstraintSystem, Error, Instance, ProvingKey}, poly::{commitment::Params, kzg::commitment::ParamsKZG}, }; use halo2curves::{ @@ -84,7 +84,7 @@ pub struct SyncStepCircuit { execution_merkle_branch: Vec>, beacon_state_root: Vec, finality_merkle_branch: Vec>, - pariticipation_bits: Vec, + participation_bits: Vec, dry_run: bool, _spec: PhantomData, } @@ -251,11 +251,54 @@ impl Circuit for SyncStepCircuit { self.execution_merkle_branch .iter() .map(|w| w.clone().into_witness()), - execution_state_root, + execution_state_root.clone(), &finilized_block_body_root, S::EXECUTION_STATE_ROOT_INDEX, )?; + // Public Input Commitment + let attested_slot = self.attested_block.slot.into_witness(); + let finalized_slot = self.finalized_block.slot.into_witness(); + let h = sha256_chip.digest::<32>( + HashInput::TwoToOne(attested_slot, finalized_slot), + ctx, + &mut region, + )?; + + let finalized_header_root = self + .finalized_block + .clone() + .hash_tree_root() + .map_err(|e| Error::Synthesis)? + .as_ref() + .iter() + .map(|&b| ctx.load_witness(F::from(b as u64))) + .collect_vec(); + let h = sha256_chip.digest::<128>( + HashInput::TwoToOne(h.output_bytes.into(), finalized_header_root.into()), + ctx, + &mut region, + )?; + + let participation = participation_sum; + let h = sha256_chip.digest::<128>( + HashInput::TwoToOne(h.output_bytes.into(), iter::once(participation).into()), + ctx, + &mut region, + )?; + + let h = sha256_chip.digest::<128>( + HashInput::TwoToOne(h.output_bytes.into(), execution_state_root.into()), + ctx, + &mut region, + )?; + + let h = sha256_chip.digest::<128>( + HashInput::TwoToOne(h.output_bytes.into(), iter::once(poseidon_commit).into()), + ctx, + &mut region, + )?; + let extra_assignments = sha256_chip.take_extra_assignments(); if self.dry_run { @@ -312,10 +355,9 @@ impl SyncStepCircuit { assert_eq!(self.pubkeys.len(), S::SYNC_COMMITTEE_SIZE); - for (&pk, is_attested) in itertools::multizip(( - self.pubkeys.iter(), - self.pariticipation_bits.iter().copied(), - )) { + for (&pk, is_attested) in + itertools::multizip((self.pubkeys.iter(), self.participation_bits.iter().copied())) + { let participation_bit = ctx.load_witness(F::from(is_attested as u64)); gate.assert_bit(ctx, participation_bit); @@ -398,7 +440,7 @@ impl AppCircuitExt for SyncStepCircuit { .unwrap() }) .collect_vec(), - pariticipation_bits: state + participation_bits: state .sync_committee .iter() .cloned() @@ -514,7 +556,7 @@ impl Default for SyncStepCircuit { pubkeys: iter::repeat(dummy_pk_point) .take(S::SYNC_COMMITTEE_SIZE) .collect_vec(), - pariticipation_bits: vec![true; S::SYNC_COMMITTEE_SIZE], + participation_bits: vec![true; S::SYNC_COMMITTEE_SIZE], dry_run: false, finality_merkle_branch, beacon_state_root, From c135671ffea4f2d3ab0546ba85ba68d341da97de Mon Sep 17 00:00:00 2001 From: ec2 Date: Tue, 5 Sep 2023 20:01:46 +0000 Subject: [PATCH 03/17] todo: constrain to instance --- lightclient-circuits/src/sync_step_circuit.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 1b6fb8d3..79bc493b 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -141,12 +141,12 @@ impl Circuit for SyncStepCircuit { builder_clone.borrow_mut() }; - layouter.assign_region( + let public_input_commitment = layouter.assign_region( || "AggregationCircuitBuilder generated circuit", |mut region| { if first_pass { first_pass = false; - return Ok(()); + return Ok(None); } let ctx = builder.main(0); @@ -293,7 +293,7 @@ impl Circuit for SyncStepCircuit { &mut region, )?; - let h = sha256_chip.digest::<128>( + let public_input_commitment = sha256_chip.digest::<128>( HashInput::TwoToOne(h.output_bytes.into(), iter::once(poseidon_commit).into()), ctx, &mut region, @@ -302,7 +302,7 @@ impl Circuit for SyncStepCircuit { let extra_assignments = sha256_chip.take_extra_assignments(); if self.dry_run { - return Ok(()); + return Ok(None); } let _ = builder.assign_all( @@ -314,9 +314,13 @@ impl Circuit for SyncStepCircuit { ); // TODO: constaint source_root, target_root with instances: `layouter.constrain_instance` - Ok(()) + Ok(Some(public_input_commitment)) }, - ) + )?; + // public_input_commitment.and_then(|v| { + // Some(layouter.constrain_instance(v[0], config.pi, 0)) + // }); + Ok(()) } } From fc70062ae3f178be4b2a2725145ec7dbabfa85c8 Mon Sep 17 00:00:00 2001 From: ec2 Date: Tue, 5 Sep 2023 20:28:22 +0000 Subject: [PATCH 04/17] use range with instance instead of vanilla instance col --- lightclient-circuits/src/sync_step_circuit.rs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 79bc493b..9aaf2f9f 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -30,7 +30,10 @@ use ff::PrimeField; use group::UncompressedEncoding; use halo2_base::{ gates::{ - builder::{FlexGateConfigParams, GateThreadBuilder, RangeCircuitBuilder}, + builder::{ + FlexGateConfigParams, GateThreadBuilder, RangeCircuitBuilder, + RangeWithInstanceCircuitBuilder, RangeWithInstanceConfig, + }, flex_gate::GateStrategy, range::RangeConfig, }, @@ -65,10 +68,9 @@ use ssz_rs::{GeneralizedIndex, Merkleized, Node}; #[derive(Clone, Debug)] pub struct SyncStepCircuitConfig { - range: RangeConfig, + range: RangeWithInstanceConfig, sha256_config: RefCell>, challenges: Challenges>, - pi: Column, } #[allow(type_alias_bounds)] @@ -98,13 +100,11 @@ impl Circuit for SyncStepCircuit { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let range = RangeCircuitBuilder::configure(meta); + let range = RangeWithInstanceCircuitBuilder::configure(meta); let sha256_config = SpreadConfig::::configure(meta, 8, 1); - let pi = meta.instance_column(); - meta.enable_equality(pi); + meta.enable_equality(range.instance); SyncStepCircuitConfig { range, - pi, sha256_config: RefCell::new(sha256_config), challenges: Challenges::mock(Value::known(Sha256CircuitConfig::fixed_challenge())), } @@ -116,6 +116,7 @@ impl Circuit for SyncStepCircuit { mut layouter: impl Layouter, ) -> Result<(), Error> { config + .range .range .load_lookup_table(&mut layouter) .expect("load range lookup table"); @@ -123,7 +124,7 @@ impl Circuit for SyncStepCircuit { assert!(!self.signature.is_empty(), "no attestations supplied"); let mut first_pass = halo2_base::SKIP_FIRST_PASS; - let range = RangeChip::default(config.range.lookup_bits()); + let range = RangeChip::default(config.range.range.lookup_bits()); let fp_chip = FpChip::::new(&range, G2::LIMB_BITS, G2::NUM_LIMBS); let fp2_chip = Fp2Chip::::new(&fp_chip); let g1_chip = EccChip::new(fp2_chip.fp_chip()); @@ -306,9 +307,9 @@ impl Circuit for SyncStepCircuit { } let _ = builder.assign_all( - &config.range.gate, - &config.range.lookup_advice, - &config.range.q_lookup, + &config.range.range.gate, + &config.range.range.lookup_advice, + &config.range.range.q_lookup, &mut region, extra_assignments, ); From 4526079a7b4b14d850e9e1562f18022ccaf0ba97 Mon Sep 17 00:00:00 2001 From: ec2 Date: Tue, 5 Sep 2023 20:33:29 +0000 Subject: [PATCH 05/17] Revert "use range with instance instead of vanilla instance col" This reverts commit fc70062ae3f178be4b2a2725145ec7dbabfa85c8. --- lightclient-circuits/src/sync_step_circuit.rs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 9aaf2f9f..79bc493b 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -30,10 +30,7 @@ use ff::PrimeField; use group::UncompressedEncoding; use halo2_base::{ gates::{ - builder::{ - FlexGateConfigParams, GateThreadBuilder, RangeCircuitBuilder, - RangeWithInstanceCircuitBuilder, RangeWithInstanceConfig, - }, + builder::{FlexGateConfigParams, GateThreadBuilder, RangeCircuitBuilder}, flex_gate::GateStrategy, range::RangeConfig, }, @@ -68,9 +65,10 @@ use ssz_rs::{GeneralizedIndex, Merkleized, Node}; #[derive(Clone, Debug)] pub struct SyncStepCircuitConfig { - range: RangeWithInstanceConfig, + range: RangeConfig, sha256_config: RefCell>, challenges: Challenges>, + pi: Column, } #[allow(type_alias_bounds)] @@ -100,11 +98,13 @@ impl Circuit for SyncStepCircuit { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let range = RangeWithInstanceCircuitBuilder::configure(meta); + let range = RangeCircuitBuilder::configure(meta); let sha256_config = SpreadConfig::::configure(meta, 8, 1); - meta.enable_equality(range.instance); + let pi = meta.instance_column(); + meta.enable_equality(pi); SyncStepCircuitConfig { range, + pi, sha256_config: RefCell::new(sha256_config), challenges: Challenges::mock(Value::known(Sha256CircuitConfig::fixed_challenge())), } @@ -116,7 +116,6 @@ impl Circuit for SyncStepCircuit { mut layouter: impl Layouter, ) -> Result<(), Error> { config - .range .range .load_lookup_table(&mut layouter) .expect("load range lookup table"); @@ -124,7 +123,7 @@ impl Circuit for SyncStepCircuit { assert!(!self.signature.is_empty(), "no attestations supplied"); let mut first_pass = halo2_base::SKIP_FIRST_PASS; - let range = RangeChip::default(config.range.range.lookup_bits()); + let range = RangeChip::default(config.range.lookup_bits()); let fp_chip = FpChip::::new(&range, G2::LIMB_BITS, G2::NUM_LIMBS); let fp2_chip = Fp2Chip::::new(&fp_chip); let g1_chip = EccChip::new(fp2_chip.fp_chip()); @@ -307,9 +306,9 @@ impl Circuit for SyncStepCircuit { } let _ = builder.assign_all( - &config.range.range.gate, - &config.range.range.lookup_advice, - &config.range.range.q_lookup, + &config.range.gate, + &config.range.lookup_advice, + &config.range.q_lookup, &mut region, extra_assignments, ); From cd51ad07453e564c4f58203ab649ff6c2857b500 Mon Sep 17 00:00:00 2001 From: ec2 Date: Tue, 5 Sep 2023 20:44:01 +0000 Subject: [PATCH 06/17] constrain instance to advice --- lightclient-circuits/src/sync_step_circuit.rs | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 79bc493b..2fcc110e 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -141,7 +141,7 @@ impl Circuit for SyncStepCircuit { builder_clone.borrow_mut() }; - let public_input_commitment = layouter.assign_region( + let assignments = layouter.assign_region( || "AggregationCircuitBuilder generated circuit", |mut region| { if first_pass { @@ -305,7 +305,7 @@ impl Circuit for SyncStepCircuit { return Ok(None); } - let _ = builder.assign_all( + let assignments = builder.assign_all( &config.range.gate, &config.range.lookup_advice, &config.range.q_lookup, @@ -313,13 +313,21 @@ impl Circuit for SyncStepCircuit { extra_assignments, ); - // TODO: constaint source_root, target_root with instances: `layouter.constrain_instance` - Ok(Some(public_input_commitment)) + Ok(Some((public_input_commitment, assignments))) }, )?; - // public_input_commitment.and_then(|v| { - // Some(layouter.constrain_instance(v[0], config.pi, 0)) - // }); + if let Some(assignments) = assignments { + let assigned_instances = assignments.0.output_bytes; + let assigned_advices = assignments.1.assigned_advices; + + for (i, instance) in assigned_instances.into_iter().enumerate() { + let cell = instance.cell.unwrap(); + let (cell, _) = assigned_advices + .get(&(cell.context_id, cell.offset)) + .expect("instance not assigned"); + layouter.constrain_instance(*cell, config.pi, i); + } + } Ok(()) } } From d2a1631a9ed99aa5c5ab4f0ca122688254f40ad0 Mon Sep 17 00:00:00 2001 From: ec2 Date: Tue, 5 Sep 2023 21:57:00 +0000 Subject: [PATCH 07/17] todo: figure out how to generate test data --- lightclient-circuits/Cargo.toml | 4 +- .../config/sync_step_k20.json | 2 +- lightclient-circuits/src/ssz_merkle.rs | 2 +- lightclient-circuits/src/sync_step_circuit.rs | 61 ++++++++++++++++--- 4 files changed, 59 insertions(+), 10 deletions(-) diff --git a/lightclient-circuits/Cargo.toml b/lightclient-circuits/Cargo.toml index fcc1660f..90d76dca 100644 --- a/lightclient-circuits/Cargo.toml +++ b/lightclient-circuits/Cargo.toml @@ -31,7 +31,6 @@ snark-verifier-sdk = { git = "https://github.com/axiom-crypto/snark-verifier.git "loader_evm", "halo2-pse", ] } - # crypto num-bigint = { version = "0.4", features = ["rand"] } pasta_curves = "0.4.1" @@ -60,5 +59,8 @@ hex = "0.4" rayon = "1.7.0" ark-std = { version = "0.4.0", features = ["print-trace"] } +[dev-dependencies] +poseidon_native = { git = "https://github.com/axiom-crypto/halo2.git", branch = "axiom/dev", package = "poseidon" } + [features] default = [] diff --git a/lightclient-circuits/config/sync_step_k20.json b/lightclient-circuits/config/sync_step_k20.json index 33037f69..3d2984db 100644 --- a/lightclient-circuits/config/sync_step_k20.json +++ b/lightclient-circuits/config/sync_step_k20.json @@ -1 +1 @@ -{ "strategy": "Vertical", "k": 20, "num_advice_per_phase": [17, 0, 0], "num_lookup_advice_per_phase": [2, 0, 0], "num_fixed": 1 } +{ "strategy": "Vertical", "k": 20, "num_advice_per_phase": [18, 0, 0], "num_lookup_advice_per_phase": [2, 0, 0], "num_fixed": 1 } diff --git a/lightclient-circuits/src/ssz_merkle.rs b/lightclient-circuits/src/ssz_merkle.rs index 36c6fa02..e44d6827 100644 --- a/lightclient-circuits/src/ssz_merkle.rs +++ b/lightclient-circuits/src/ssz_merkle.rs @@ -289,7 +289,7 @@ pub fn ssz_merkleize_chunks( .take(3) .map(|(left, right)| { hasher - .digest::<64>(HashInput::TwoToOne(left, right), ctx, region) + .digest::<128>(HashInput::TwoToOne(left, right), ctx, region) .map(|res| res.output_bytes.into()) }) .collect::, _>>()?; diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 2fcc110e..7963ef82 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -259,7 +259,7 @@ impl Circuit for SyncStepCircuit { // Public Input Commitment let attested_slot = self.attested_block.slot.into_witness(); let finalized_slot = self.finalized_block.slot.into_witness(); - let h = sha256_chip.digest::<32>( + let h = sha256_chip.digest::<128>( HashInput::TwoToOne(attested_slot, finalized_slot), ctx, &mut region, @@ -281,19 +281,17 @@ impl Circuit for SyncStepCircuit { )?; let participation = participation_sum; - let h = sha256_chip.digest::<128>( + let h = sha256_chip.digest::<64>( HashInput::TwoToOne(h.output_bytes.into(), iter::once(participation).into()), ctx, &mut region, )?; - let h = sha256_chip.digest::<128>( HashInput::TwoToOne(h.output_bytes.into(), execution_state_root.into()), ctx, &mut region, )?; - - let public_input_commitment = sha256_chip.digest::<128>( + let public_input_commitment = sha256_chip.digest::<64>( HashInput::TwoToOne(h.output_bytes.into(), iter::once(poseidon_commit).into()), ctx, &mut region, @@ -602,7 +600,7 @@ mod tests { flex_gate::GateStrategy, range::RangeStrategy, }, - utils::fs::gen_srs, + utils::{fs::gen_srs, ScalarField}, }; use halo2_proofs::{ circuit::SimpleFloorPlanner, @@ -622,6 +620,8 @@ mod tests { CircuitExt, SHPLONK, }; + use poseidon_native::Poseidon as PoseidonNative; + fn get_circuit_with_data( k: usize, config_path: &str, @@ -630,6 +630,53 @@ mod tests { let state: SyncState = serde_json::from_slice(&fs::read("../test_data/sync_state.json").unwrap()).unwrap(); + let attested_slot = state.attested_block.slot; + let finalized_slot = state.finalized_block.slot; + let h = sha2::Sha256::digest(&[]).to_vec(); + // let h = sha256_chip.digest::<32>( + // HashInput::TwoToOne(attested_slot, finalized_slot), + // ctx, + // &mut region, + // )?; + + let finalized_header_root: [u8; 32] = state + .finalized_block + .clone() + .hash_tree_root() + .unwrap() + .as_bytes() + .try_into() + .unwrap(); + // let h = sha256_chip.digest::<128>( + // HashInput::TwoToOne(h.output_bytes.into(), finalized_header_root.into()), + // ctx, + // &mut region, + // )?; + + let participation = state + .sync_committee + .iter() + .cloned() + .map(|v| v.is_attested as u64) + .sum::(); + // let h = sha256_chip.digest::<128>( + // HashInput::TwoToOne(h.output_bytes.into(), iter::once(participation).into()), + // ctx, + // &mut region, + // )?; + let execution_state_root = state.execution_state_root.clone(); + + // let h = sha256_chip.digest::<128>( + // HashInput::TwoToOne(h.output_bytes.into(), execution_state_root.into()), + // ctx, + // &mut region, + // )?; + + // let public_input_commitment = sha256_chip.digest::<128>( + // HashInput::TwoToOne(h.output_bytes.into(), iter::once(poseidon_commit).into()), + // ctx, + // &mut region, + // )?; let config = if let Ok(f) = fs::read(config_path) { serde_json::from_slice(&f).expect("read config file") } else { @@ -653,7 +700,7 @@ mod tests { let (circuit, _) = get_circuit_with_data(k, "./config/sync_step_k20.json"); let timer = start_timer!(|| "sync circuit mock prover"); - let prover = MockProver::::run(k as u32, &circuit, vec![]).unwrap(); + let prover = MockProver::::run(k as u32, &circuit, vec![vec![Fr::zero(); 32]]).unwrap(); prover.assert_satisfied(); end_timer!(timer); } From ed2f44490cb68391a944c85f9989046864771cea Mon Sep 17 00:00:00 2001 From: ec2 Date: Thu, 7 Sep 2023 23:38:39 +0000 Subject: [PATCH 08/17] works! just need to add the poseidon commitment to the PI commitment and clean up --- lightclient-circuits/src/sync_step_circuit.rs | 107 ++++++++++-------- 1 file changed, 60 insertions(+), 47 deletions(-) diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 7963ef82..18758ad6 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -257,6 +257,7 @@ impl Circuit for SyncStepCircuit { )?; // Public Input Commitment + // TODO: This gets ssz padded to 32 bytes. Can probably just do a conversion to LE bytes to save a bunch of bytes let attested_slot = self.attested_block.slot.into_witness(); let finalized_slot = self.finalized_block.slot.into_witness(); let h = sha256_chip.digest::<128>( @@ -280,22 +281,29 @@ impl Circuit for SyncStepCircuit { &mut region, )?; - let participation = participation_sum; - let h = sha256_chip.digest::<64>( - HashInput::TwoToOne(h.output_bytes.into(), iter::once(participation).into()), - ctx, - &mut region, - )?; + let participation_s = self + .participation_bits + .iter() + .map(|b| *b as u64) + .sum::() + .into_witness(); + let h = sha256_chip.digest::<128>( - HashInput::TwoToOne(h.output_bytes.into(), execution_state_root.into()), + HashInput::TwoToOne(h.output_bytes.into(), participation_s), ctx, &mut region, )?; - let public_input_commitment = sha256_chip.digest::<64>( - HashInput::TwoToOne(h.output_bytes.into(), iter::once(poseidon_commit).into()), + let public_input_commitment = sha256_chip.digest::<128>( + HashInput::TwoToOne(h.output_bytes.into(), execution_state_root.into()), ctx, &mut region, )?; + // TODO: Remember to commit to the poseidon commitment. Removing this for now for ease of testing. + // let public_input_commitment = sha256_chip.digest::<64>( + // HashInput::TwoToOne(h.output_bytes.into(), iter::once(poseidon_commit).into()), + // ctx, + // &mut region, + // )?; let extra_assignments = sha256_chip.take_extra_assignments(); @@ -625,19 +633,21 @@ mod tests { fn get_circuit_with_data( k: usize, config_path: &str, - ) -> (SyncStepCircuit, FlexGateConfigParams) { + ) -> (SyncStepCircuit, FlexGateConfigParams, Vec) { let builder = GateThreadBuilder::new(false); let state: SyncState = serde_json::from_slice(&fs::read("../test_data/sync_state.json").unwrap()).unwrap(); - let attested_slot = state.attested_block.slot; - let finalized_slot = state.finalized_block.slot; - let h = sha2::Sha256::digest(&[]).to_vec(); - // let h = sha256_chip.digest::<32>( - // HashInput::TwoToOne(attested_slot, finalized_slot), - // ctx, - // &mut region, - // )?; + let mut input: [u8; 64] = [0; 64]; + + let mut attested_slot = state.attested_block.slot.to_le_bytes().to_vec(); + let mut finalized_slot = state.finalized_block.slot.to_le_bytes().to_vec(); + attested_slot.resize(32, 0); + finalized_slot.resize(32, 0); + + input[..32].copy_from_slice(&attested_slot); + input[32..].copy_from_slice(&finalized_slot); + let h = sha2::Sha256::digest(&input).to_vec(); let finalized_header_root: [u8; 32] = state .finalized_block @@ -647,36 +657,32 @@ mod tests { .as_bytes() .try_into() .unwrap(); - // let h = sha256_chip.digest::<128>( - // HashInput::TwoToOne(h.output_bytes.into(), finalized_header_root.into()), - // ctx, - // &mut region, - // )?; - let participation = state + input[..32].copy_from_slice(&h); + input[32..].copy_from_slice(&finalized_header_root); + let h = sha2::Sha256::digest(&input).to_vec(); + + let mut participation = state .sync_committee .iter() .cloned() .map(|v| v.is_attested as u64) - .sum::(); - // let h = sha256_chip.digest::<128>( - // HashInput::TwoToOne(h.output_bytes.into(), iter::once(participation).into()), - // ctx, - // &mut region, - // )?; + .sum::() + .to_le_bytes() + .to_vec(); + participation.resize(32, 0); + + input[..32].copy_from_slice(&h); + input[32..].copy_from_slice(&participation); + let h = sha2::Sha256::digest(&input).to_vec(); + let execution_state_root = state.execution_state_root.clone(); + input[..32].copy_from_slice(&h); + input[32..].copy_from_slice(&execution_state_root); + + let public_input_commitment = sha2::Sha256::digest(&input).to_vec(); + // TODO: Also hash the poseidon commitment - // let h = sha256_chip.digest::<128>( - // HashInput::TwoToOne(h.output_bytes.into(), execution_state_root.into()), - // ctx, - // &mut region, - // )?; - - // let public_input_commitment = sha256_chip.digest::<128>( - // HashInput::TwoToOne(h.output_bytes.into(), iter::once(poseidon_commit).into()), - // ctx, - // &mut region, - // )?; let config = if let Ok(f) = fs::read(config_path) { serde_json::from_slice(&f).expect("read config file") } else { @@ -691,16 +697,23 @@ mod tests { println!("config used: {:?}", config); let builder = RefCell::from(builder); - (SyncStepCircuit::new_from_state(builder, &state), config) + ( + SyncStepCircuit::new_from_state(builder, &state), + config, + public_input_commitment, + ) } #[test] fn test_sync_circuit() { let k = 20; - let (circuit, _) = get_circuit_with_data(k, "./config/sync_step_k20.json"); - + let (circuit, _, instance) = get_circuit_with_data(k, "./config/sync_step_k20.json"); + let pi = instance + .into_iter() + .map(|b| Fr::from(b as u64)) + .collect_vec(); let timer = start_timer!(|| "sync circuit mock prover"); - let prover = MockProver::::run(k as u32, &circuit, vec![vec![Fr::zero(); 32]]).unwrap(); + let prover = MockProver::::run(k as u32, &circuit, vec![pi]).unwrap(); prover.assert_satisfied(); end_timer!(timer); } @@ -708,7 +721,7 @@ mod tests { #[test] fn test_sync_proofgen() { let k = 20; - let (circuit, _) = get_circuit_with_data(k, "./config/sync_step_k20_mock.json"); + let (circuit, _, _) = get_circuit_with_data(k, "./config/sync_step_k20_mock.json"); let params = gen_srs(k as u32); @@ -723,7 +736,7 @@ mod tests { #[test] fn test_sync_evm_verify() { let k = 20; - let (circuit, config) = get_circuit_with_data(k, "./config/sync_step_k21.json"); + let (circuit, config, _) = get_circuit_with_data(k, "./config/sync_step_k21.json"); let (params, pk) = SyncStepCircuit::::setup(&config, None); From 360acacedc428ff0718af154070ded558b1ada51 Mon Sep 17 00:00:00 2001 From: ec2 Date: Thu, 7 Sep 2023 23:52:15 +0000 Subject: [PATCH 09/17] a little cleanup --- lightclient-circuits/src/sync_step_circuit.rs | 127 ++++++++---------- 1 file changed, 56 insertions(+), 71 deletions(-) diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 18758ad6..5cc54506 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -238,7 +238,7 @@ impl Circuit for SyncStepCircuit { self.finality_merkle_branch .iter() .map(|w| w.clone().into_witness()), - finalized_header.into(), + finalized_header.clone().into(), &beacon_state_root, S::FINALIZED_HEADER_INDEX, )?; @@ -266,17 +266,8 @@ impl Circuit for SyncStepCircuit { &mut region, )?; - let finalized_header_root = self - .finalized_block - .clone() - .hash_tree_root() - .map_err(|e| Error::Synthesis)? - .as_ref() - .iter() - .map(|&b| ctx.load_witness(F::from(b as u64))) - .collect_vec(); let h = sha256_chip.digest::<128>( - HashInput::TwoToOne(h.output_bytes.into(), finalized_header_root.into()), + HashInput::TwoToOne(h.output_bytes.into(), finalized_header.into()), ctx, &mut region, )?; @@ -428,7 +419,54 @@ impl SyncStepCircuit { impl CircuitExt for SyncStepCircuit { fn instances(&self) -> Vec> { - vec![] + let mut input: [u8; 64] = [0; 64]; + + let mut attested_slot = self.attested_block.slot.to_le_bytes().to_vec(); + let mut finalized_slot = self.finalized_block.slot.to_le_bytes().to_vec(); + attested_slot.resize(32, 0); + finalized_slot.resize(32, 0); + + input[..32].copy_from_slice(&attested_slot); + input[32..].copy_from_slice(&finalized_slot); + let h = sha2::Sha256::digest(&input).to_vec(); + + let finalized_header_root: [u8; 32] = self + .finalized_block + .clone() + .hash_tree_root() + .unwrap() + .as_bytes() + .try_into() + .unwrap(); + + input[..32].copy_from_slice(&h); + input[32..].copy_from_slice(&finalized_header_root); + let h = sha2::Sha256::digest(&input).to_vec(); + + let mut participation = self + .participation_bits + .iter() + .map(|v| *v as u64) + .sum::() + .to_le_bytes() + .to_vec(); + participation.resize(32, 0); + + input[..32].copy_from_slice(&h); + input[32..].copy_from_slice(&participation); + let h = sha2::Sha256::digest(&input).to_vec(); + + let execution_state_root = &self.execution_state_root; + input[..32].copy_from_slice(&h); + input[32..].copy_from_slice(execution_state_root); + + let public_input_commitment = sha2::Sha256::digest(&input).to_vec(); + // TODO: Also hash the poseidon commitment + + vec![public_input_commitment + .into_iter() + .map(|b| F::from(b as u64)) + .collect_vec()] } fn num_instance(&self) -> Vec { @@ -633,56 +671,11 @@ mod tests { fn get_circuit_with_data( k: usize, config_path: &str, - ) -> (SyncStepCircuit, FlexGateConfigParams, Vec) { + ) -> (SyncStepCircuit, FlexGateConfigParams) { let builder = GateThreadBuilder::new(false); let state: SyncState = serde_json::from_slice(&fs::read("../test_data/sync_state.json").unwrap()).unwrap(); - let mut input: [u8; 64] = [0; 64]; - - let mut attested_slot = state.attested_block.slot.to_le_bytes().to_vec(); - let mut finalized_slot = state.finalized_block.slot.to_le_bytes().to_vec(); - attested_slot.resize(32, 0); - finalized_slot.resize(32, 0); - - input[..32].copy_from_slice(&attested_slot); - input[32..].copy_from_slice(&finalized_slot); - let h = sha2::Sha256::digest(&input).to_vec(); - - let finalized_header_root: [u8; 32] = state - .finalized_block - .clone() - .hash_tree_root() - .unwrap() - .as_bytes() - .try_into() - .unwrap(); - - input[..32].copy_from_slice(&h); - input[32..].copy_from_slice(&finalized_header_root); - let h = sha2::Sha256::digest(&input).to_vec(); - - let mut participation = state - .sync_committee - .iter() - .cloned() - .map(|v| v.is_attested as u64) - .sum::() - .to_le_bytes() - .to_vec(); - participation.resize(32, 0); - - input[..32].copy_from_slice(&h); - input[32..].copy_from_slice(&participation); - let h = sha2::Sha256::digest(&input).to_vec(); - - let execution_state_root = state.execution_state_root.clone(); - input[..32].copy_from_slice(&h); - input[32..].copy_from_slice(&execution_state_root); - - let public_input_commitment = sha2::Sha256::digest(&input).to_vec(); - // TODO: Also hash the poseidon commitment - let config = if let Ok(f) = fs::read(config_path) { serde_json::from_slice(&f).expect("read config file") } else { @@ -697,23 +690,15 @@ mod tests { println!("config used: {:?}", config); let builder = RefCell::from(builder); - ( - SyncStepCircuit::new_from_state(builder, &state), - config, - public_input_commitment, - ) + (SyncStepCircuit::new_from_state(builder, &state), config) } #[test] fn test_sync_circuit() { let k = 20; - let (circuit, _, instance) = get_circuit_with_data(k, "./config/sync_step_k20.json"); - let pi = instance - .into_iter() - .map(|b| Fr::from(b as u64)) - .collect_vec(); + let (circuit, _) = get_circuit_with_data(k, "./config/sync_step_k20.json"); let timer = start_timer!(|| "sync circuit mock prover"); - let prover = MockProver::::run(k as u32, &circuit, vec![pi]).unwrap(); + let prover = MockProver::::run(k as u32, &circuit, circuit.instances()).unwrap(); prover.assert_satisfied(); end_timer!(timer); } @@ -721,7 +706,7 @@ mod tests { #[test] fn test_sync_proofgen() { let k = 20; - let (circuit, _, _) = get_circuit_with_data(k, "./config/sync_step_k20_mock.json"); + let (circuit, _) = get_circuit_with_data(k, "./config/sync_step_k20_mock.json"); let params = gen_srs(k as u32); @@ -736,7 +721,7 @@ mod tests { #[test] fn test_sync_evm_verify() { let k = 20; - let (circuit, config, _) = get_circuit_with_data(k, "./config/sync_step_k21.json"); + let (circuit, config) = get_circuit_with_data(k, "./config/sync_step_k21.json"); let (params, pk) = SyncStepCircuit::::setup(&config, None); From 3074a1b5bd83e1c6e0e143c2a0ff5547ee2da6c1 Mon Sep 17 00:00:00 2001 From: ec2 Date: Mon, 11 Sep 2023 14:57:27 +0000 Subject: [PATCH 10/17] public input commitment as 1 field elem --- lightclient-circuits/src/sync_step_circuit.rs | 59 ++++++++++++++----- lightclient-circuits/src/util.rs | 2 +- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 5cc54506..cda0893f 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -289,6 +289,7 @@ impl Circuit for SyncStepCircuit { ctx, &mut region, )?; + // TODO: Remember to commit to the poseidon commitment. Removing this for now for ease of testing. // let public_input_commitment = sha256_chip.digest::<64>( // HashInput::TwoToOne(h.output_bytes.into(), iter::once(poseidon_commit).into()), @@ -296,6 +297,18 @@ impl Circuit for SyncStepCircuit { // &mut region, // )?; + // Truncate the public input commitment to 253 bits and convert to one field element + let gate = range.gate(); + let mut public_input_commitment_bytes = public_input_commitment.output_bytes; + let cleared_byte = clear_3_bits(&range, &public_input_commitment_bytes[31], ctx); + public_input_commitment_bytes[31] = cleared_byte; + + let byte_base = (0..32) + .map(|i| QuantumCell::Constant(gate.pow_of_two()[i * 8])) + .collect_vec(); + + let pi_field = gate.inner_product(ctx, public_input_commitment_bytes, byte_base); + let extra_assignments = sha256_chip.take_extra_assignments(); if self.dry_run { @@ -310,25 +323,41 @@ impl Circuit for SyncStepCircuit { extra_assignments, ); - Ok(Some((public_input_commitment, assignments))) + Ok(Some((pi_field, assignments))) }, )?; + if let Some(assignments) = assignments { - let assigned_instances = assignments.0.output_bytes; + let pi_instance = assignments.0; let assigned_advices = assignments.1.assigned_advices; - for (i, instance) in assigned_instances.into_iter().enumerate() { - let cell = instance.cell.unwrap(); - let (cell, _) = assigned_advices - .get(&(cell.context_id, cell.offset)) - .expect("instance not assigned"); - layouter.constrain_instance(*cell, config.pi, i); - } + let cell = pi_instance.cell.unwrap(); + let (cell, _) = assigned_advices + .get(&(cell.context_id, cell.offset)) + .expect("instance not assigned"); + layouter.constrain_instance(*cell, config.pi, 0); } Ok(()) } } +/// Clears the 3 first least significat bits. +/// This function emulates bitwise and on 00011111 (0x1F): `b & 0b00011111` = c +fn clear_3_bits( + range: &RangeChip, + b: &AssignedValue, + ctx: &mut Context, +) -> AssignedValue { + let gate = range.gate(); + // Shift `a` three bits to the left (equivalent to a << 3 mod 256) + let b_shifted = gate.mul(ctx, *b, QuantumCell::Constant(F::from(8))); + // since b_shifted can at max be 255*8=2^4 we use 16 bits for modulo division. + let b_shifted = range.div_mod(ctx, b_shifted, BigUint::from(256u64), 16).1; + + // Shift `s` three bits to the right (equivalent to s >> 3) to zeroing the first three bits (MSB) of `a`. + range.div_mod(ctx, b_shifted, BigUint::from(8u64), 8).0 +} + impl SyncStepCircuit { pub fn dry_run(mut self) -> Self { self.dry_run = true; @@ -459,15 +488,15 @@ impl CircuitExt for SyncStepCircuit { let execution_state_root = &self.execution_state_root; input[..32].copy_from_slice(&h); input[32..].copy_from_slice(execution_state_root); - - let public_input_commitment = sha2::Sha256::digest(&input).to_vec(); // TODO: Also hash the poseidon commitment - vec![public_input_commitment - .into_iter() - .map(|b| F::from(b as u64)) - .collect_vec()] + let mut public_input_commitment = sha2::Sha256::digest(&input).to_vec(); + // Truncate to 253 bits + public_input_commitment[31] &= 0b00011111; + let pi_field = F::from_bytes_le_unsecure(&public_input_commitment); + vec![vec![pi_field]] } + //chung fn num_instance(&self) -> Vec { self.instances().iter().map(|v| v.len()).collect() diff --git a/lightclient-circuits/src/util.rs b/lightclient-circuits/src/util.rs index bb3df090..4bf16707 100644 --- a/lightclient-circuits/src/util.rs +++ b/lightclient-circuits/src/util.rs @@ -154,7 +154,7 @@ pub mod to_bytes { } /// Converts assigned bytes into biginterger -/// Warning: method does not perfrom any checks on input `bytes`. +/// Warning: method does not perform any checks on input `bytes`. pub fn decode_into_field( bytes: impl IntoIterator>, limb_bases: &[F], From 69d889243e90e31a79fc9e4957525c6ad665cad7 Mon Sep 17 00:00:00 2001 From: ec2 Date: Tue, 12 Sep 2023 11:12:53 +0000 Subject: [PATCH 11/17] wip: poseidon commitment to public input --- lightclient-circuits/Cargo.toml | 2 +- lightclient-circuits/src/poseidon.rs | 4 + lightclient-circuits/src/sync_step_circuit.rs | 74 ++++++++++++++++--- 3 files changed, 68 insertions(+), 12 deletions(-) diff --git a/lightclient-circuits/Cargo.toml b/lightclient-circuits/Cargo.toml index 90d76dca..0659f668 100644 --- a/lightclient-circuits/Cargo.toml +++ b/lightclient-circuits/Cargo.toml @@ -59,8 +59,8 @@ hex = "0.4" rayon = "1.7.0" ark-std = { version = "0.4.0", features = ["print-trace"] } -[dev-dependencies] poseidon_native = { git = "https://github.com/axiom-crypto/halo2.git", branch = "axiom/dev", package = "poseidon" } +# [dev-dependencies] [features] default = [] diff --git a/lightclient-circuits/src/poseidon.rs b/lightclient-circuits/src/poseidon.rs index 6b783a12..6f495d67 100644 --- a/lightclient-circuits/src/poseidon.rs +++ b/lightclient-circuits/src/poseidon.rs @@ -21,6 +21,10 @@ pub fn g1_array_poseidon( .flat_map(|p| p.x.limbs()) .copied() .collect_vec(); + println!( + "synthewsis limbs: {:?}", + &limbs[..3].iter().map(|x| x.value()).collect_vec() + ); let mut poseidon = PoseidonChip::::new(ctx, R_F, R_P) .expect("failed to construct Poseidon circuit"); diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index cda0893f..356aa8f0 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -35,7 +35,7 @@ use halo2_base::{ range::RangeConfig, }, safe_types::{GateInstructions, RangeChip, RangeInstructions}, - utils::{fs::gen_srs, CurveAffineExt}, + utils::{decompose, fe_to_bigint, fe_to_biguint, fs::gen_srs, CurveAffineExt, ScalarField}, AssignedValue, Context, QuantumCell, }; use halo2_ecc::{ @@ -257,7 +257,6 @@ impl Circuit for SyncStepCircuit { )?; // Public Input Commitment - // TODO: This gets ssz padded to 32 bytes. Can probably just do a conversion to LE bytes to save a bunch of bytes let attested_slot = self.attested_block.slot.into_witness(); let finalized_slot = self.finalized_block.slot.into_witness(); let h = sha256_chip.digest::<128>( @@ -284,18 +283,29 @@ impl Circuit for SyncStepCircuit { ctx, &mut region, )?; - let public_input_commitment = sha256_chip.digest::<128>( + let h = sha256_chip.digest::<128>( HashInput::TwoToOne(h.output_bytes.into(), execution_state_root.into()), ctx, &mut region, )?; - // TODO: Remember to commit to the poseidon commitment. Removing this for now for ease of testing. - // let public_input_commitment = sha256_chip.digest::<64>( - // HashInput::TwoToOne(h.output_bytes.into(), iter::once(poseidon_commit).into()), - // ctx, - // &mut region, - // )?; + println!( + "synthesis poseidon_commitment {:?}", + poseidon_commit.value() + ); + let poseidon_commit_bytes = poseidon_commit + .value() + .to_bytes_le() + .into_iter() + .map(|v| ctx.load_witness(F::from(v as u64))) + .collect_vec(); + // println!("Synthesis: {:#04x?}", poseidon_commit_bytes.iter().map(|b| b.value()).collect_vec()); + + let public_input_commitment = sha256_chip.digest::<128>( + HashInput::TwoToOne(h.output_bytes.into(), poseidon_commit_bytes.into()), + ctx, + &mut region, + )?; // Truncate the public input commitment to 253 bits and convert to one field element let gate = range.gate(); @@ -488,7 +498,16 @@ impl CircuitExt for SyncStepCircuit { let execution_state_root = &self.execution_state_root; input[..32].copy_from_slice(&h); input[32..].copy_from_slice(execution_state_root); - // TODO: Also hash the poseidon commitment + let h = sha2::Sha256::digest(&input).to_vec(); + + let poseidon_commitment = g1_array_poseidon_native::(&self.pubkeys); + println!("wgen poseidon_commitment: {:x?}", poseidon_commitment); + let poseidon_commitment_bytes = g1_array_poseidon_native::(&self.pubkeys) + .unwrap() + .to_bytes_le(); + // println!("wgen: {:#04x?}", poseidon_commitment_bytes); + input[..32].copy_from_slice(&h); + input[32..].copy_from_slice(&poseidon_commitment_bytes); let mut public_input_commitment = sha2::Sha256::digest(&input).to_vec(); // Truncate to 253 bits @@ -496,7 +515,6 @@ impl CircuitExt for SyncStepCircuit { let pi_field = F::from_bytes_le_unsecure(&public_input_commitment); vec![vec![pi_field]] } - //chung fn num_instance(&self) -> Vec { self.instances().iter().map(|v| v.len()).collect() @@ -651,6 +669,40 @@ impl Default for SyncStepCircuit { } } } +use poseidon_native::Poseidon as PoseidonNative; +const POSEIDON_SIZE: usize = 16; +const R_F: usize = 8; +const R_P: usize = 68; +pub fn g1_array_poseidon_native(points: &[G1Affine]) -> Result { + let limbs_fq = points + .iter() + // Converts the point (usually in Fq) to limbs. + .map(|p| decompose(&p.x, 4, 112)) + .flatten() + // Converts the Fq point to a circuit field. It is safe because the limbs should be smaller + // even if the bits in the Field of the point are larger than the bits of the circuit field. + // .map(|fq_limbs| F::from_bytes_le_unsecure(&fq_limbs.to_bytes_le())) + .collect_vec(); + println!("wgen fqlimbs: {:?}", &limbs_fq[..3]); + + let limbs = limbs_fq + .iter() + .map(|fq_limbs| F::from_bytes_le_unsecure(&fq_limbs.to_bytes_le())) + .collect_vec(); + println!("wgen fr limbs: {:?}", &limbs[..3]); + + let mut poseidon = PoseidonNative::::new(R_F, R_P); + let mut current_poseidon_hash = None; + + for (i, chunk) in limbs.chunks(POSEIDON_SIZE - 3).enumerate() { + poseidon.update(chunk); + if i != 0 { + poseidon.update(&[current_poseidon_hash.unwrap()]); + } + current_poseidon_hash.insert(poseidon.squeeze()); + } + Ok(current_poseidon_hash.unwrap()) +} #[cfg(test)] mod tests { From c541816c8c1f255a71cf6b4ef54908dcdbc75148 Mon Sep 17 00:00:00 2001 From: ec2 Date: Tue, 12 Sep 2023 12:43:57 +0000 Subject: [PATCH 12/17] TODO: Cleanup --- Cargo.toml | 6 +++--- lightclient-circuits/src/poseidon.rs | 5 +---- lightclient-circuits/src/sync_step_circuit.rs | 21 +++++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6d0cc812..8d725a0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,12 +26,12 @@ halo2curves = { git = "https://github.com/timoftime/halo2curves", branch = "dev/ # halo2curves = { path = "../halo2curves" } [patch."https://github.com/axiom-crypto/halo2-lib"] -halo2-base = { git = "https://github.com/timoftime/halo2-lib", rev = "7cebe716bb0569a7cb741c44ef8e87b2eadbbafb", default-features = false, features = [ +halo2-base = { git = "https://github.com/timoftime/halo2-lib", rev = "03c7baedeae208b21359e542b8683644b3eb1ac2", default-features = false, features = [ "halo2-pse", "display", ] } -halo2-ecc = { git = "https://github.com/timoftime/halo2-lib", rev = "7cebe716bb0569a7cb741c44ef8e87b2eadbbafb", default-features = false } -poseidon = { git = "https://github.com/timoftime/halo2-lib", rev = "7cebe716bb0569a7cb741c44ef8e87b2eadbbafb", default-features = false } +halo2-ecc = { git = "https://github.com/timoftime/halo2-lib", rev = "03c7baedeae208b21359e542b8683644b3eb1ac2", default-features = false } +poseidon = { git = "https://github.com/timoftime/halo2-lib", rev = "03c7baedeae208b21359e542b8683644b3eb1ac2", default-features = false } # halo2-base = { path = "../halo2-lib/halo2-base", default-features = false, features = [ # "halo2-pse", diff --git a/lightclient-circuits/src/poseidon.rs b/lightclient-circuits/src/poseidon.rs index 6f495d67..85ff46a5 100644 --- a/lightclient-circuits/src/poseidon.rs +++ b/lightclient-circuits/src/poseidon.rs @@ -21,10 +21,6 @@ pub fn g1_array_poseidon( .flat_map(|p| p.x.limbs()) .copied() .collect_vec(); - println!( - "synthewsis limbs: {:?}", - &limbs[..3].iter().map(|x| x.value()).collect_vec() - ); let mut poseidon = PoseidonChip::::new(ctx, R_F, R_P) .expect("failed to construct Poseidon circuit"); @@ -35,6 +31,7 @@ pub fn g1_array_poseidon( if i != 0 { poseidon.update(&[current_poseidon_hash.unwrap()]); } + current_poseidon_hash.insert(poseidon.squeeze(ctx, gate)?); } diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 356aa8f0..33bd03be 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -227,7 +227,7 @@ impl Circuit for SyncStepCircuit { &mut h2c_cache, )?; - let res = bls_chip.verify_pairing(signature, msghash, agg_pubkey, g1_neg, ctx); + let res = bls_chip.verify_pairing(ctx, signature, msghash, agg_pubkey, g1_neg); fp12_chip.assert_equal(ctx, res, fp12_one); // verify finilized block header against current beacon state merkle proof @@ -500,11 +500,9 @@ impl CircuitExt for SyncStepCircuit { input[32..].copy_from_slice(execution_state_root); let h = sha2::Sha256::digest(&input).to_vec(); - let poseidon_commitment = g1_array_poseidon_native::(&self.pubkeys); + let poseidon_commitment = g1_array_poseidon_native::(&self.pubkeys).unwrap(); println!("wgen poseidon_commitment: {:x?}", poseidon_commitment); - let poseidon_commitment_bytes = g1_array_poseidon_native::(&self.pubkeys) - .unwrap() - .to_bytes_le(); + let poseidon_commitment_bytes = poseidon_commitment.to_bytes_le(); // println!("wgen: {:#04x?}", poseidon_commitment_bytes); input[..32].copy_from_slice(&h); input[32..].copy_from_slice(&poseidon_commitment_bytes); @@ -677,19 +675,24 @@ pub fn g1_array_poseidon_native(points: &[G1Affine]) -> Result::new(R_F, R_P); let mut current_poseidon_hash = None; From d30176b22eac56001fcd5fa4f8ffc9aee9023bdd Mon Sep 17 00:00:00 2001 From: ec2 Date: Tue, 12 Sep 2023 17:36:37 +0000 Subject: [PATCH 13/17] constrain poseidon bytes and participation sum bytes --- lightclient-circuits/src/sync_step_circuit.rs | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 33bd03be..cb35c019 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -257,6 +257,8 @@ impl Circuit for SyncStepCircuit { )?; // Public Input Commitment + let gate = range.gate(); + let attested_slot = self.attested_block.slot.into_witness(); let finalized_slot = self.finalized_block.slot.into_witness(); let h = sha256_chip.digest::<128>( @@ -264,42 +266,52 @@ impl Circuit for SyncStepCircuit { ctx, &mut region, )?; - + // TODO: Investigate if we should hash it all concatinated in one go let h = sha256_chip.digest::<128>( HashInput::TwoToOne(h.output_bytes.into(), finalized_header.into()), ctx, &mut region, )?; - let participation_s = self - .participation_bits - .iter() - .map(|b| *b as u64) - .sum::() - .into_witness(); + let byte_base = (0..32) + .map(|i| QuantumCell::Constant(gate.pow_of_two()[i * 8])) + .collect_vec(); + + let participation_sum_bytes = participation_sum + .value() + .to_bytes_le() + .into_iter() + .map(|v| ctx.load_witness(F::from(v as u64))) + .collect_vec(); let h = sha256_chip.digest::<128>( - HashInput::TwoToOne(h.output_bytes.into(), participation_s), + HashInput::TwoToOne( + h.output_bytes.into(), + participation_sum_bytes.clone().into(), + ), ctx, &mut region, )?; + // Constrain the participation sum bytes to be equal to the participation_sum + let sum_field = gate.inner_product(ctx, participation_sum_bytes, byte_base.clone()); + ctx.constrain_equal(&sum_field, &participation_sum); + let h = sha256_chip.digest::<128>( HashInput::TwoToOne(h.output_bytes.into(), execution_state_root.into()), ctx, &mut region, )?; - println!( - "synthesis poseidon_commitment {:?}", - poseidon_commit.value() - ); let poseidon_commit_bytes = poseidon_commit .value() .to_bytes_le() .into_iter() .map(|v| ctx.load_witness(F::from(v as u64))) .collect_vec(); - // println!("Synthesis: {:#04x?}", poseidon_commit_bytes.iter().map(|b| b.value()).collect_vec()); + // Constrain poseidon bytes to be equal to the poseidon_commit_value + let poseidon_commit_field = + gate.inner_product(ctx, poseidon_commit_bytes.clone(), byte_base.clone()); + ctx.constrain_equal(&poseidon_commit_field, &poseidon_commit); let public_input_commitment = sha256_chip.digest::<128>( HashInput::TwoToOne(h.output_bytes.into(), poseidon_commit_bytes.into()), @@ -308,15 +320,10 @@ impl Circuit for SyncStepCircuit { )?; // Truncate the public input commitment to 253 bits and convert to one field element - let gate = range.gate(); let mut public_input_commitment_bytes = public_input_commitment.output_bytes; let cleared_byte = clear_3_bits(&range, &public_input_commitment_bytes[31], ctx); public_input_commitment_bytes[31] = cleared_byte; - let byte_base = (0..32) - .map(|i| QuantumCell::Constant(gate.pow_of_two()[i * 8])) - .collect_vec(); - let pi_field = gate.inner_product(ctx, public_input_commitment_bytes, byte_base); let extra_assignments = sha256_chip.take_extra_assignments(); @@ -501,9 +508,7 @@ impl CircuitExt for SyncStepCircuit { let h = sha2::Sha256::digest(&input).to_vec(); let poseidon_commitment = g1_array_poseidon_native::(&self.pubkeys).unwrap(); - println!("wgen poseidon_commitment: {:x?}", poseidon_commitment); let poseidon_commitment_bytes = poseidon_commitment.to_bytes_le(); - // println!("wgen: {:#04x?}", poseidon_commitment_bytes); input[..32].copy_from_slice(&h); input[32..].copy_from_slice(&poseidon_commitment_bytes); From 59db8be1a4ee7f80d3b2415885f87a114ae98031 Mon Sep 17 00:00:00 2001 From: ec2 Date: Tue, 12 Sep 2023 17:48:20 +0000 Subject: [PATCH 14/17] Clean up --- lightclient-circuits/src/poseidon.rs | 36 +++++++++++++++- lightclient-circuits/src/sync_step_circuit.rs | 41 +------------------ 2 files changed, 35 insertions(+), 42 deletions(-) diff --git a/lightclient-circuits/src/poseidon.rs b/lightclient-circuits/src/poseidon.rs index 85ff46a5..748d389a 100644 --- a/lightclient-circuits/src/poseidon.rs +++ b/lightclient-circuits/src/poseidon.rs @@ -1,11 +1,13 @@ +use crate::gadget::crypto::G1Point; use eth_types::{AppCurveExt, Field}; +use halo2_base::safe_types::ScalarField; use halo2_base::{safe_types::GateInstructions, AssignedValue, Context}; use halo2_proofs::plonk::Error; +use halo2curves::bls12_381::G1Affine; use halo2curves::bls12_381::G1; use itertools::Itertools; use poseidon::PoseidonChip; - -use crate::gadget::crypto::G1Point; +use poseidon_native::Poseidon as PoseidonNative; const POSEIDON_SIZE: usize = 16; const R_F: usize = 8; @@ -38,6 +40,36 @@ pub fn g1_array_poseidon( Ok(current_poseidon_hash.unwrap()) } +pub fn g1_array_poseidon_native(points: &[G1Affine]) -> Result { + let limbs = points + .iter() + // Converts the point (usually in Fq) to limbs. + .flat_map(|point| { + point + .x + .to_bytes_le() + .chunks(14) + .map(F::from_bytes_le) + .collect_vec() + }) + // Converts the Fq point to a circuit field. It is safe because the limbs should be smaller + // even if the bits in the Field of the point are larger than the bits of the circuit field. + .map(|fq_limbs| F::from_bytes_le_unsecure(&fq_limbs.to_bytes_le())) + .collect_vec(); + + let mut poseidon = PoseidonNative::::new(R_F, R_P); + let mut current_poseidon_hash = None; + + for (i, chunk) in limbs.chunks(POSEIDON_SIZE - 3).enumerate() { + poseidon.update(chunk); + if i != 0 { + poseidon.update(&[current_poseidon_hash.unwrap()]); + } + current_poseidon_hash.insert(poseidon.squeeze()); + } + Ok(current_poseidon_hash.unwrap()) +} + pub fn poseidon_sponge( ctx: &mut Context, gate: &impl GateInstructions, diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index cb35c019..9d0d334a 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -15,7 +15,7 @@ use crate::{ Fp2Point, FpPoint, G1Chip, G1Point, G2Chip, G2Point, HashChip, HashToCurveCache, HashToCurveChip, Sha256Chip, SpreadConfig, }, - poseidon::{g1_array_poseidon, poseidon_sponge}, + poseidon::{g1_array_poseidon, g1_array_poseidon_native, poseidon_sponge}, sha256_circuit::{util::NUM_ROUNDS, Sha256CircuitConfig}, ssz_merkle::{ssz_merkleize_chunks, verify_merkle_proof}, table::Sha256Table, @@ -672,45 +672,6 @@ impl Default for SyncStepCircuit { } } } -use poseidon_native::Poseidon as PoseidonNative; -const POSEIDON_SIZE: usize = 16; -const R_F: usize = 8; -const R_P: usize = 68; -pub fn g1_array_poseidon_native(points: &[G1Affine]) -> Result { - let limbs_fq = points - .iter() - // Converts the point (usually in Fq) to limbs. - .map(|point| { - let x_fq_bytes = point.x.to_bytes_le(); - let mut chunks = x_fq_bytes.chunks(14); - let mut limbs = vec![]; - while let Some(chunk) = chunks.next() { - limbs.push(F::from_bytes_le(chunk)); - } - limbs - }) - .flatten() - // Converts the Fq point to a circuit field. It is safe because the limbs should be smaller - // even if the bits in the Field of the point are larger than the bits of the circuit field. - // .map(|fq_limbs| F::from_bytes_le_unsecure(&fq_limbs.to_bytes_le())) - .collect_vec(); - let limbs = limbs_fq - .iter() - .map(|fq_limbs| F::from_bytes_le_unsecure(&fq_limbs.to_bytes_le())) - .collect_vec(); - - let mut poseidon = PoseidonNative::::new(R_F, R_P); - let mut current_poseidon_hash = None; - - for (i, chunk) in limbs.chunks(POSEIDON_SIZE - 3).enumerate() { - poseidon.update(chunk); - if i != 0 { - poseidon.update(&[current_poseidon_hash.unwrap()]); - } - current_poseidon_hash.insert(poseidon.squeeze()); - } - Ok(current_poseidon_hash.unwrap()) -} #[cfg(test)] mod tests { From 86a1b1a41bdc54ea351d09094821ca5fb22af9e8 Mon Sep 17 00:00:00 2001 From: ec2 Date: Tue, 12 Sep 2023 17:51:04 +0000 Subject: [PATCH 15/17] get correct config --- lightclient-circuits/src/sync_step_circuit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 9d0d334a..0e2d7034 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -756,7 +756,7 @@ mod tests { #[test] fn test_sync_proofgen() { let k = 20; - let (circuit, _) = get_circuit_with_data(k, "./config/sync_step_k20_mock.json"); + let (circuit, _) = get_circuit_with_data(k, "./config/sync_step_k20.json"); let params = gen_srs(k as u32); From 52641b64cbce2565fa716a26903661abcd100968 Mon Sep 17 00:00:00 2001 From: ec2 Date: Wed, 13 Sep 2023 10:46:48 +0000 Subject: [PATCH 16/17] clippy --- lightclient-circuits/src/sync_step_circuit.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 6a8bfc0d..f86fb4f4 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -267,7 +267,7 @@ impl SyncStepCircuit { let h = sha256_chip.digest::<64>( thread_pool, - HashInput::TwoToOne(h.output_bytes.into(), execution_state_root.into()), + HashInput::TwoToOne(h.output_bytes.into(), execution_state_root), false, )?; @@ -296,7 +296,7 @@ impl SyncStepCircuit { // Truncate the public input commitment to 253 bits and convert to one field element let mut public_input_commitment_bytes = public_input_commitment.output_bytes; let cleared_byte = clear_3_bits( - &range, + range, &public_input_commitment_bytes[31], thread_pool.main(), ); @@ -317,7 +317,7 @@ impl SyncStepCircuit { input[..32].copy_from_slice(&attested_slot); input[32..].copy_from_slice(&finalized_slot); - let h = sha2::Sha256::digest(&input).to_vec(); + let h = sha2::Sha256::digest(input).to_vec(); let finalized_header_root: [u8; 32] = args .finalized_block @@ -330,7 +330,7 @@ impl SyncStepCircuit { input[..32].copy_from_slice(&h); input[32..].copy_from_slice(&finalized_header_root); - let h = sha2::Sha256::digest(&input).to_vec(); + let h = sha2::Sha256::digest(input).to_vec(); let mut participation = args .pariticipation_bits @@ -343,12 +343,12 @@ impl SyncStepCircuit { input[..32].copy_from_slice(&h); input[32..].copy_from_slice(&participation); - let h = sha2::Sha256::digest(&input).to_vec(); + let h = sha2::Sha256::digest(input).to_vec(); let execution_state_root = &args.execution_state_root; input[..32].copy_from_slice(&h); input[32..].copy_from_slice(execution_state_root); - let h = sha2::Sha256::digest(&input).to_vec(); + let h = sha2::Sha256::digest(input).to_vec(); let pubkey_affines = args .pubkeys_uncompressed @@ -364,7 +364,7 @@ impl SyncStepCircuit { input[..32].copy_from_slice(&h); input[32..].copy_from_slice(&poseidon_commitment_bytes); - let mut public_input_commitment = sha2::Sha256::digest(&input).to_vec(); + let mut public_input_commitment = sha2::Sha256::digest(input).to_vec(); // Truncate to 253 bits public_input_commitment[31] &= 0b00011111; let pi_field = bn256::Fr::from_bytes_le(&public_input_commitment); From eb952736837c6ea9e08666b3e27d26a7de31d66b Mon Sep 17 00:00:00 2001 From: ec2 Date: Wed, 13 Sep 2023 11:07:46 +0000 Subject: [PATCH 17/17] rename to sha flex --- lightclient-circuits/src/gadget/crypto/builder.rs | 2 +- lightclient-circuits/src/gadget/crypto/hash2curve.rs | 2 +- lightclient-circuits/src/gadget/crypto/mod.rs | 4 ++-- .../src/gadget/crypto/{sha256.rs => sha256_flex.rs} | 2 +- .../src/gadget/crypto/{sha256 => sha256_flex}/builder.rs | 0 .../src/gadget/crypto/{sha256 => sha256_flex}/compression.rs | 0 .../src/gadget/crypto/{sha256 => sha256_flex}/spread.rs | 0 .../src/gadget/crypto/{sha256 => sha256_flex}/util.rs | 0 lightclient-circuits/src/sync_step_circuit.rs | 2 +- 9 files changed, 6 insertions(+), 6 deletions(-) rename lightclient-circuits/src/gadget/crypto/{sha256.rs => sha256_flex.rs} (99%) rename lightclient-circuits/src/gadget/crypto/{sha256 => sha256_flex}/builder.rs (100%) rename lightclient-circuits/src/gadget/crypto/{sha256 => sha256_flex}/compression.rs (100%) rename lightclient-circuits/src/gadget/crypto/{sha256 => sha256_flex}/spread.rs (100%) rename lightclient-circuits/src/gadget/crypto/{sha256 => sha256_flex}/util.rs (100%) diff --git a/lightclient-circuits/src/gadget/crypto/builder.rs b/lightclient-circuits/src/gadget/crypto/builder.rs index 39466f0a..a05ac3e5 100644 --- a/lightclient-circuits/src/gadget/crypto/builder.rs +++ b/lightclient-circuits/src/gadget/crypto/builder.rs @@ -23,7 +23,7 @@ use crate::{ util::{ThreadBuilderBase, ThreadBuilderConfigBase}, }; -use super::sha256::{assign_threads_sha, SpreadConfig, FIRST_PHASE}; +use super::sha256_flex::{assign_threads_sha, SpreadConfig, FIRST_PHASE}; #[derive(Debug, Clone)] pub struct SHAConfig> { diff --git a/lightclient-circuits/src/gadget/crypto/hash2curve.rs b/lightclient-circuits/src/gadget/crypto/hash2curve.rs index 275bd0a6..b39ef11f 100644 --- a/lightclient-circuits/src/gadget/crypto/hash2curve.rs +++ b/lightclient-circuits/src/gadget/crypto/hash2curve.rs @@ -625,7 +625,7 @@ mod test { use std::vec; use std::{cell::RefCell, marker::PhantomData}; - use crate::gadget::crypto::sha256::{SpreadChip, SpreadConfig}; + use crate::gadget::crypto::sha256_flex::{SpreadChip, SpreadConfig}; use crate::gadget::crypto::ShaCircuitBuilder; use crate::gadget::crypto::{Sha256Chip, ShaThreadBuilder}; use crate::util::{print_fq2_dev, Challenges, IntoWitness}; diff --git a/lightclient-circuits/src/gadget/crypto/mod.rs b/lightclient-circuits/src/gadget/crypto/mod.rs index 60b118d8..2bba9c67 100644 --- a/lightclient-circuits/src/gadget/crypto/mod.rs +++ b/lightclient-circuits/src/gadget/crypto/mod.rs @@ -1,7 +1,7 @@ mod builder; mod ecc; mod hash2curve; -mod sha256; +mod sha256_flex; mod sha256_wide; mod util; @@ -17,7 +17,7 @@ use halo2_ecc::{ use halo2_proofs::plonk::Error; pub use hash2curve::{HashToCurveCache, HashToCurveChip}; use lazy_static::lazy_static; -pub use sha256::{Sha256Chip, ShaContexts, ShaThreadBuilder}; +pub use sha256_flex::{Sha256Chip, ShaContexts, ShaThreadBuilder}; pub use sha256_wide::{Sha256ChipWide, ShaBitThreadBuilder}; pub use ecc::calculate_ysquared; diff --git a/lightclient-circuits/src/gadget/crypto/sha256.rs b/lightclient-circuits/src/gadget/crypto/sha256_flex.rs similarity index 99% rename from lightclient-circuits/src/gadget/crypto/sha256.rs rename to lightclient-circuits/src/gadget/crypto/sha256_flex.rs index 8b6293d0..45105ef5 100644 --- a/lightclient-circuits/src/gadget/crypto/sha256.rs +++ b/lightclient-circuits/src/gadget/crypto/sha256_flex.rs @@ -16,7 +16,7 @@ use sha2::digest::generic_array::GenericArray; use std::collections::HashMap; use std::{cell::RefCell, char::MAX}; -use crate::gadget::crypto::sha256::compression::{sha256_compression, INIT_STATE}; +use crate::gadget::crypto::sha256_flex::compression::{sha256_compression, INIT_STATE}; use crate::util::{AssignedValueCell, ThreadBuilderBase}; use crate::witness::HashInput; use halo2_base::safe_types::RangeChip; diff --git a/lightclient-circuits/src/gadget/crypto/sha256/builder.rs b/lightclient-circuits/src/gadget/crypto/sha256_flex/builder.rs similarity index 100% rename from lightclient-circuits/src/gadget/crypto/sha256/builder.rs rename to lightclient-circuits/src/gadget/crypto/sha256_flex/builder.rs diff --git a/lightclient-circuits/src/gadget/crypto/sha256/compression.rs b/lightclient-circuits/src/gadget/crypto/sha256_flex/compression.rs similarity index 100% rename from lightclient-circuits/src/gadget/crypto/sha256/compression.rs rename to lightclient-circuits/src/gadget/crypto/sha256_flex/compression.rs diff --git a/lightclient-circuits/src/gadget/crypto/sha256/spread.rs b/lightclient-circuits/src/gadget/crypto/sha256_flex/spread.rs similarity index 100% rename from lightclient-circuits/src/gadget/crypto/sha256/spread.rs rename to lightclient-circuits/src/gadget/crypto/sha256_flex/spread.rs diff --git a/lightclient-circuits/src/gadget/crypto/sha256/util.rs b/lightclient-circuits/src/gadget/crypto/sha256_flex/util.rs similarity index 100% rename from lightclient-circuits/src/gadget/crypto/sha256/util.rs rename to lightclient-circuits/src/gadget/crypto/sha256_flex/util.rs diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index f86fb4f4..39cb6530 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -585,7 +585,7 @@ mod tests { #[test] fn test_sync_evm_verify() { - const K: usize = 21; + const K: usize = 22; let (params, pk, break_points) = SyncStepCircuit::::setup(K, None);