diff --git a/Cargo.toml b/Cargo.toml index f54a4800..55141a38 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 = "d3455345c40fdee4462e400b55b94559c6d494e9", 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 = "d3455345c40fdee4462e400b55b94559c6d494e9", default-features = false } -poseidon = { git = "https://github.com/timoftime/halo2-lib", rev = "d3455345c40fdee4462e400b55b94559c6d494e9", 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/Cargo.toml b/lightclient-circuits/Cargo.toml index fcc1660f..0659f668 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"] } +poseidon_native = { git = "https://github.com/axiom-crypto/halo2.git", branch = "axiom/dev", package = "poseidon" } +# [dev-dependencies] + [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/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/ecc.rs b/lightclient-circuits/src/gadget/crypto/ecc.rs index 73fef72b..1423508f 100644 --- a/lightclient-circuits/src/gadget/crypto/ecc.rs +++ b/lightclient-circuits/src/gadget/crypto/ecc.rs @@ -1,6 +1,6 @@ -use eth_types::{Field, AppCurveExt}; +use eth_types::{AppCurveExt, Field}; use halo2_base::Context; -use halo2_ecc::{bls12_381::FpChip, bigint::ProperCrtUint, fields::FieldChip}; +use halo2_ecc::{bigint::ProperCrtUint, bls12_381::FpChip, fields::FieldChip}; // Calculates y^2 = x^3 + 4 (the curve equation) pub fn calculate_ysquared( diff --git a/lightclient-circuits/src/gadget/crypto/hash2curve.rs b/lightclient-circuits/src/gadget/crypto/hash2curve.rs index 6e85e92b..b39ef11f 100644 --- a/lightclient-circuits/src/gadget/crypto/hash2curve.rs +++ b/lightclient-circuits/src/gadget/crypto/hash2curve.rs @@ -33,7 +33,7 @@ use super::{ util::{fp2_sgn0, i2osp, strxor}, Fp2Point, G1Point, G2Point, HashInstructions, }; -use super::{ShaContexts, ShaThreadBuilder, AssignedHashResult}; +use super::{AssignedHashResult, ShaContexts, ShaThreadBuilder}; const G2_EXT_DEGREE: usize = 2; @@ -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 9a5d75f7..2bba9c67 100644 --- a/lightclient-circuits/src/gadget/crypto/mod.rs +++ b/lightclient-circuits/src/gadget/crypto/mod.rs @@ -1,9 +1,9 @@ mod builder; +mod ecc; mod hash2curve; -mod sha256; +mod sha256_flex; mod sha256_wide; mod util; -mod ecc; pub use builder::{SHAConfig, ShaCircuitBuilder}; use eth_types::{AppCurveExt, Field, HashCurveExt}; @@ -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 98% rename from lightclient-circuits/src/gadget/crypto/sha256.rs rename to lightclient-circuits/src/gadget/crypto/sha256_flex.rs index 2c0958ef..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; @@ -35,7 +35,7 @@ pub use self::builder::ShaContexts; pub(super) use self::builder::{assign_threads_sha, FIRST_PHASE}; pub use self::spread::SpreadChip; -use super::{HashInstructions, AssignedHashResult}; +use super::{AssignedHashResult, HashInstructions}; const SHA256_CONTEXT_ID: usize = usize::MAX; diff --git a/lightclient-circuits/src/gadget/crypto/sha256/builder.rs b/lightclient-circuits/src/gadget/crypto/sha256_flex/builder.rs similarity index 99% rename from lightclient-circuits/src/gadget/crypto/sha256/builder.rs rename to lightclient-circuits/src/gadget/crypto/sha256_flex/builder.rs index 171aa54a..879ab9dc 100644 --- a/lightclient-circuits/src/gadget/crypto/sha256/builder.rs +++ b/lightclient-circuits/src/gadget/crypto/sha256_flex/builder.rs @@ -15,7 +15,7 @@ use halo2_base::{ }; use halo2_proofs::{ circuit::{self, Region, Value}, - plonk::{Advice, Column, Selector, Error}, + plonk::{Advice, Column, Error, Selector}, }; use itertools::Itertools; @@ -137,7 +137,6 @@ impl ThreadBuilderBase for ShaThreadBuilder { region: &mut Region, break_points: &mut MultiPhaseThreadBreakPoints, ) -> Result<(), Error> { - let break_points_gate = mem::take(&mut break_points[FIRST_PHASE]); // warning: we currently take all contexts from phase 0, which means you can't read the values // from these contexts later in phase 1. If we want to read, should clone here @@ -161,7 +160,6 @@ impl ThreadBuilderBase for ShaThreadBuilder { } } - impl ShaThreadBuilder { pub fn sha_contexts_pair(&mut self) -> (&mut Context, ShaContexts) { if self.threads_dense.is_empty() { diff --git a/lightclient-circuits/src/gadget/crypto/sha256/compression.rs b/lightclient-circuits/src/gadget/crypto/sha256_flex/compression.rs similarity index 92% rename from lightclient-circuits/src/gadget/crypto/sha256/compression.rs rename to lightclient-circuits/src/gadget/crypto/sha256_flex/compression.rs index bb4c2d35..60b3cf23 100644 --- a/lightclient-circuits/src/gadget/crypto/sha256/compression.rs +++ b/lightclient-circuits/src/gadget/crypto/sha256_flex/compression.rs @@ -2,10 +2,10 @@ use std::cell::RefMut; use crate::util::ThreadBuilderBase; -use super::ShaThreadBuilder; use super::builder::ShaContexts; use super::spread::{self, SpreadChip, SpreadConfig}; use super::util::{bits_le_to_fe, fe_to_bits_le}; +use super::ShaThreadBuilder; use eth_types::Field; use halo2_base::halo2_proofs::halo2curves::FieldExt; use halo2_base::halo2_proofs::{ @@ -155,13 +155,7 @@ pub fn sha256_compression<'a, 'b: 'a, F: Field>( // let f_spread = state_to_spread_u32(ctx, range, ctx_spread, &f)?; // let g_spread = state_to_spread_u32(ctx, range, ctx_spread, &g)?; let sigma_term = sigma_upper1(thread_pool, spread_chip, &e_spread)?; - let ch_term = ch( - thread_pool, - spread_chip, - &e_spread, - &f_spread, - &g_spread, - )?; + let ch_term = ch(thread_pool, spread_chip, &e_spread, &f_spread, &g_spread)?; // println!( // "idx {} sigma {:?} ch {:?}", // idx, @@ -191,13 +185,7 @@ pub fn sha256_compression<'a, 'b: 'a, F: Field>( // let b_spread = state_to_spread_u32(ctx, range, ctx_spread, &b)?; // let c_spread = state_to_spread_u32(ctx, range, ctx_spread, &c)?; let sigma_term = sigma_upper0(thread_pool, spread_chip, &a_spread)?; - let maj_term = maj( - thread_pool, - spread_chip, - &a_spread, - &b_spread, - &c_spread, - )?; + let maj_term = maj(thread_pool, spread_chip, &a_spread, &b_spread, &c_spread)?; let add = gate.add( thread_pool.main(), QuantumCell::Existing(sigma_term), @@ -243,7 +231,11 @@ pub fn sha256_compression<'a, 'b: 'a, F: Field>( .copied() .zip(pre_state_words.iter().copied()) .map(|(x, y)| { - let add = gate.add(thread_pool.main(), QuantumCell::Existing(x), QuantumCell::Existing(y)); + let add = gate.add( + thread_pool.main(), + QuantumCell::Existing(x), + QuantumCell::Existing(y), + ); // println!( // "pre {:?} new {:?} add {:?}", // y.value(), @@ -338,10 +330,14 @@ fn ch<'a, 'b: 'a, F: Field>( QuantumCell::Existing(x_neg_hi), QuantumCell::Existing(z_hi), ); - let (p_lo_even, p_lo_odd) = spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &p_lo)?; - let (p_hi_even, p_hi_odd) = spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &p_hi)?; - let (q_lo_even, q_lo_odd) = spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &q_lo)?; - let (q_hi_even, q_hi_odd) = spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &q_hi)?; + let (p_lo_even, p_lo_odd) = + spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &p_lo)?; + let (p_hi_even, p_hi_odd) = + spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &p_hi)?; + let (q_lo_even, q_lo_odd) = + spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &q_lo)?; + let (q_hi_even, q_hi_odd) = + spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &q_hi)?; { let even_spread = spread_chip.spread(thread_pool, &p_lo_even)?; let odd_spread = spread_chip.spread(thread_pool, &p_lo_odd)?; @@ -431,8 +427,10 @@ fn maj<'a, 'b: 'a, F: Field>( QuantumCell::Existing(y_hi), QuantumCell::Existing(z_hi), ); - let (m_lo_even, m_lo_odd) = spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &m_lo)?; - let (m_hi_even, m_hi_odd) = spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &m_hi)?; + let (m_lo_even, m_lo_odd) = + spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &m_lo)?; + let (m_hi_even, m_hi_odd) = + spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &m_hi)?; { let even_spread = spread_chip.spread(thread_pool, &m_lo_even)?; let odd_spread = spread_chip.spread(thread_pool, &m_lo_odd)?; @@ -648,10 +646,30 @@ fn sigma_generic<'a, 'b: 'a, F: Field>( // let assigned_b_spread = spread_config.spread(ctx, range, &assigned_b)?; // let assigned_c_spread = spread_config.spread(ctx, range, &assigned_c)?; // let assigned_d_spread = spread_config.spread(ctx, range, &assigned_d)?; - sum = gate.mul_add(thread_pool.main(), QuantumCell::Constant(coeffs[0]), assigned_a, sum); - sum = gate.mul_add(thread_pool.main(), QuantumCell::Constant(coeffs[1]), assigned_b, sum); - sum = gate.mul_add(thread_pool.main(), QuantumCell::Constant(coeffs[2]), assigned_c, sum); - sum = gate.mul_add(thread_pool.main(), QuantumCell::Constant(coeffs[3]), assigned_d, sum); + sum = gate.mul_add( + thread_pool.main(), + QuantumCell::Constant(coeffs[0]), + assigned_a, + sum, + ); + sum = gate.mul_add( + thread_pool.main(), + QuantumCell::Constant(coeffs[1]), + assigned_b, + sum, + ); + sum = gate.mul_add( + thread_pool.main(), + QuantumCell::Constant(coeffs[2]), + assigned_c, + sum, + ); + sum = gate.mul_add( + thread_pool.main(), + QuantumCell::Constant(coeffs[3]), + assigned_d, + sum, + ); sum }; let (r_lo, r_hi) = { @@ -671,8 +689,10 @@ fn sigma_generic<'a, 'b: 'a, F: Field>( (assigned_lo, assigned_hi) }; - let (r_lo_even, r_lo_odd) = spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &r_lo)?; - let (r_hi_even, r_hi_odd) = spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &r_hi)?; + let (r_lo_even, r_lo_odd) = + spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &r_lo)?; + let (r_hi_even, r_hi_odd) = + spread_chip.decompose_even_and_odd_unchecked(thread_pool.main(), &r_hi)?; { let even_spread = spread_chip.spread(thread_pool, &r_lo_even)?; diff --git a/lightclient-circuits/src/gadget/crypto/sha256/spread.rs b/lightclient-circuits/src/gadget/crypto/sha256_flex/spread.rs similarity index 97% rename from lightclient-circuits/src/gadget/crypto/sha256/spread.rs rename to lightclient-circuits/src/gadget/crypto/sha256_flex/spread.rs index 1b119401..6bb47097 100644 --- a/lightclient-circuits/src/gadget/crypto/sha256/spread.rs +++ b/lightclient-circuits/src/gadget/crypto/sha256_flex/spread.rs @@ -2,8 +2,8 @@ use std::marker::PhantomData; use crate::util::{ThreadBuilderBase, ThreadBuilderConfigBase}; -use super::ShaThreadBuilder; use super::builder::ShaContexts; +use super::ShaThreadBuilder; use super::{compression::SpreadU32, util::*}; use eth_types::Field; use halo2_base::gates::builder::FlexGateConfigParams; @@ -92,10 +92,9 @@ impl SpreadConfig { } } - impl ThreadBuilderConfigBase for SpreadConfig { fn configure(meta: &mut ConstraintSystem, params: FlexGateConfigParams) -> Self { - Self::configure(meta, 8, 2) // TODO configure num_advice_columns + Self::configure(meta, 8, 2) // TODO configure num_advice_columns } fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { @@ -223,7 +222,9 @@ impl<'a, F: Field> SpreadChip<'a, F> { let assigned_spread = ctx_base.load_witness(spread_value); let assigned_spread_vanila = ctx_spread.load_witness(*assigned_spread.value()); - thread_pool.main().constrain_equal(&assigned_spread_vanila, &assigned_spread); + thread_pool + .main() + .constrain_equal(&assigned_spread_vanila, &assigned_spread); Ok(assigned_spread) } 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/gadget/crypto/sha256_wide/builder.rs b/lightclient-circuits/src/gadget/crypto/sha256_wide/builder.rs index 14cd7a4d..1832e83e 100644 --- a/lightclient-circuits/src/gadget/crypto/sha256_wide/builder.rs +++ b/lightclient-circuits/src/gadget/crypto/sha256_wide/builder.rs @@ -4,8 +4,8 @@ use eth_types::Field; use halo2_base::{ gates::{ builder::{ - CircuitBuilderStage, FlexGateConfigParams, GateThreadBuilder, KeygenAssignments, - ThreadBreakPoints, assign_threads_in, + assign_threads_in, CircuitBuilderStage, FlexGateConfigParams, GateThreadBuilder, + KeygenAssignments, ThreadBreakPoints, }, flex_gate::FlexGateConfig, }, @@ -182,14 +182,9 @@ impl ThreadBuilderBase for ShaBitThreadBuilder { break_points_gate, ); - self.sha_contexts().assign_in_region( - region, - config, - use_unknown, - None, - ) + self.sha_contexts() + .assign_in_region(region, config, use_unknown, None) } - } impl ShaBitThreadBuilder { diff --git a/lightclient-circuits/src/gadget/crypto/sha256_wide/util.rs b/lightclient-circuits/src/gadget/crypto/sha256_wide/util.rs index ab43a19e..51150ab0 100644 --- a/lightclient-circuits/src/gadget/crypto/sha256_wide/util.rs +++ b/lightclient-circuits/src/gadget/crypto/sha256_wide/util.rs @@ -1,5 +1,8 @@ use eth_types::Field; -use halo2_base::{utils::{biguint_to_fe, fe_to_biguint}, AssignedValue}; +use halo2_base::{ + utils::{biguint_to_fe, fe_to_biguint}, + AssignedValue, +}; use halo2_proofs::circuit::AssignedCell; use itertools::Itertools; use num_bigint::BigUint; @@ -55,8 +58,6 @@ pub struct Sha256AssignedRows { pub output_rlc: Vec>, } - - impl Sha256AssignedRows { pub fn new() -> Self { Self { diff --git a/lightclient-circuits/src/lib.rs b/lightclient-circuits/src/lib.rs index d41dabd2..d87f3353 100644 --- a/lightclient-circuits/src/lib.rs +++ b/lightclient-circuits/src/lib.rs @@ -16,8 +16,8 @@ pub mod witness; pub mod committee_update_circuit; pub mod sync_step_circuit; +mod builder; mod poseidon; mod ssz_merkle; -mod builder; pub use halo2_base::gates::builder::FlexGateConfigParams; diff --git a/lightclient-circuits/src/poseidon.rs b/lightclient-circuits/src/poseidon.rs index 9751728f..11fafb02 100644 --- a/lightclient-circuits/src/poseidon.rs +++ b/lightclient-circuits/src/poseidon.rs @@ -1,12 +1,14 @@ +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_ecc::bigint::{ProperUint, ProperCrtUint}; +use halo2_ecc::bigint::{ProperCrtUint, ProperUint}; 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; @@ -17,7 +19,11 @@ pub fn fq_array_poseidon<'a, F: Field>( gate: &impl GateInstructions, fields: impl IntoIterator>, ) -> Result, Error> { - let limbs = fields.into_iter().flat_map(|f| f.limbs()).copied().collect_vec(); + let limbs = fields + .into_iter() + .flat_map(|f| f.limbs()) + .copied() + .collect_vec(); let mut poseidon = PoseidonChip::::new(ctx, R_F, R_P) .expect("failed to construct Poseidon circuit"); @@ -29,12 +35,43 @@ pub fn fq_array_poseidon<'a, F: Field>( if i != 0 { poseidon.update(&[current_poseidon_hash.unwrap()]); } + current_poseidon_hash.insert(poseidon.squeeze(ctx, gate)?); } 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 54c666e4..39cb6530 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -16,7 +16,7 @@ use crate::{ calculate_ysquared, Fp2Point, FpPoint, G1Chip, G1Point, G2Chip, G2Point, HashInstructions, HashToCurveCache, HashToCurveChip, Sha256Chip, ShaCircuitBuilder, ShaThreadBuilder, }, - poseidon::{fq_array_poseidon, poseidon_sponge}, + poseidon::{fq_array_poseidon, g1_array_poseidon_native, poseidon_sponge}, ssz_merkle::{ssz_merkleize_chunks, verify_merkle_proof}, util::{ decode_into_field, gen_pkey, AppCircuitExt, AssignedValueCell, Challenges, IntoWitness, @@ -38,8 +38,9 @@ use halo2_base::{ range::{RangeConfig, RangeStrategy}, }, safe_types::{GateInstructions, RangeChip, RangeInstructions}, - utils::{fs::gen_srs, CurveAffineExt}, - AssignedValue, Context, QuantumCell, + utils::{decompose, fe_to_bigint, fe_to_biguint, fs::gen_srs, CurveAffineExt, ScalarField}, + AssignedValue, Context, + QuantumCell::{self, Witness}, }; use halo2_ecc::{ bigint::ProperCrtUint, @@ -50,7 +51,7 @@ use halo2_ecc::{ use halo2_proofs::{ circuit::{Layouter, Region, SimpleFloorPlanner, Value}, dev::MockProver, - plonk::{Circuit, ConstraintSystem, Error, ProvingKey}, + plonk::{Circuit, Column, ConstraintSystem, Error, Instance, ProvingKey}, poly::{commitment::Params, kzg::commitment::ParamsKZG}, }; use halo2curves::{ @@ -134,7 +135,7 @@ impl SyncStepCircuit { }; let mut h2c_cache = HashToCurveCache::::default(); - // // Verify attestted header + // Verify attested header let attested_header = ssz_merkleize_chunks( thread_pool, &sha256_chip, @@ -147,7 +148,7 @@ impl SyncStepCircuit { ], )?; - let finilized_block_body_root = args + let finalized_block_body_root = args .finalized_block .body_root .as_ref() @@ -163,7 +164,7 @@ impl SyncStepCircuit { args.finalized_block.proposer_index.into_witness(), args.finalized_block.parent_root.as_ref().into_witness(), args.finalized_block.state_root.as_ref().into_witness(), - finilized_block_body_root.clone().into(), + finalized_block_body_root.clone().into(), ], )?; @@ -201,7 +202,7 @@ impl SyncStepCircuit { args.finality_merkle_branch .iter() .map(|w| w.clone().into_witness()), - finalized_header.into(), + finalized_header.clone().into(), &beacon_state_root, S::FINALIZED_HEADER_INDEX, )?; @@ -213,17 +214,181 @@ impl SyncStepCircuit { args.execution_merkle_branch .iter() .map(|w| w.clone().into_witness()), - execution_state_root, - &finilized_block_body_root, + execution_state_root.clone(), + &finalized_block_body_root, S::EXECUTION_STATE_ROOT_INDEX, )?; - let instances = vec![]; + // Public Input Commitment + let gate = range.gate(); - Ok(instances) + let attested_slot = args.attested_block.slot.into_witness(); + let finalized_slot = args.finalized_block.slot.into_witness(); + let h = sha256_chip.digest::<64>( + thread_pool, + HashInput::TwoToOne(attested_slot, finalized_slot), + false, + )?; + // TODO: Investigate if we should hash it all concatinated in one go + let h = sha256_chip.digest::<64>( + thread_pool, + HashInput::TwoToOne(h.output_bytes.into(), finalized_header.into()), + false, + )?; + + 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| thread_pool.main().load_witness(F::from(v as u64))) + .collect_vec(); + + let h = sha256_chip.digest::<64>( + thread_pool, + HashInput::TwoToOne( + h.output_bytes.into(), + participation_sum_bytes.clone().into(), + ), + false, + )?; + // Constrain the participation sum bytes to be equal to the participation_sum + let sum_field = gate.inner_product( + thread_pool.main(), + participation_sum_bytes, + byte_base.clone(), + ); + thread_pool + .main() + .constrain_equal(&sum_field, &participation_sum); + + let h = sha256_chip.digest::<64>( + thread_pool, + HashInput::TwoToOne(h.output_bytes.into(), execution_state_root), + false, + )?; + + let poseidon_commit_bytes = poseidon_commit + .value() + .to_bytes_le() + .into_iter() + .map(|v| thread_pool.main().load_witness(F::from(v as u64))) + .collect_vec(); + // Constrain poseidon bytes to be equal to the poseidon_commit_value + let poseidon_commit_field = gate.inner_product( + thread_pool.main(), + poseidon_commit_bytes.clone(), + byte_base.clone(), + ); + thread_pool + .main() + .constrain_equal(&poseidon_commit_field, &poseidon_commit); + + let public_input_commitment = sha256_chip.digest::<64>( + thread_pool, + HashInput::TwoToOne(h.output_bytes.into(), poseidon_commit_bytes.into()), + false, + )?; + + // 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, + &public_input_commitment_bytes[31], + thread_pool.main(), + ); + public_input_commitment_bytes[31] = cleared_byte; + + let pi_field = + gate.inner_product(thread_pool.main(), public_input_commitment_bytes, byte_base); + Ok(vec![pi_field]) + } + + fn instances(args: SyncStepArgs) -> Vec> { + let mut input: [u8; 64] = [0; 64]; + + let mut attested_slot = args.attested_block.slot.to_le_bytes().to_vec(); + let mut finalized_slot = args.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] = args + .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 = args + .pariticipation_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 = &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 pubkey_affines = args + .pubkeys_uncompressed + .iter() + .cloned() + .map(|bytes| { + G1Affine::from_uncompressed_unchecked(&bytes.as_slice().try_into().unwrap()) + .unwrap() + }) + .collect_vec(); + let poseidon_commitment = g1_array_poseidon_native::(&pubkey_affines).unwrap(); + let poseidon_commitment_bytes = poseidon_commitment.to_bytes_le(); + 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 + public_input_commitment[31] &= 0b00011111; + let pi_field = bn256::Fr::from_bytes_le(&public_input_commitment); + vec![vec![pi_field]] } } +/// 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 { fn assign_signature( ctx: &mut Context, @@ -347,7 +512,7 @@ mod tests { flex_gate::GateStrategy, range::RangeStrategy, }, - utils::fs::gen_srs, + utils::{fs::gen_srs, ScalarField}, }; use halo2_proofs::{ circuit::SimpleFloorPlanner, @@ -370,7 +535,7 @@ mod tests { fn load_circuit_with_data( thread_pool: &mut ShaThreadBuilder, k: usize, - ) -> Vec> { + ) -> (Vec>, SyncStepArgs) { let args: SyncStepArgs = serde_json::from_slice(&fs::read("../test_data/sync_step.json").unwrap()).unwrap(); let circuit = SyncStepCircuit::::default(); @@ -384,7 +549,7 @@ mod tests { let instance = vec![]; - instance + (instance, args) } #[test] @@ -392,7 +557,7 @@ mod tests { const K: usize = 20; let mut builder = ShaThreadBuilder::mock(); - let assigned_instances = load_circuit_with_data(&mut builder, K); + let (assigned_instances, args) = load_circuit_with_data(&mut builder, K); let circuit = Eth2CircuitBuilder::mock(assigned_instances, builder); @@ -409,10 +574,10 @@ mod tests { let (params, pk, break_points) = SyncStepCircuit::::setup(K, None); let mut builder = ShaThreadBuilder::prover(); - let assigned_instances = load_circuit_with_data(&mut builder, K); + let (assigned_instances, args) = load_circuit_with_data(&mut builder, K); let circuit = Eth2CircuitBuilder::prover(assigned_instances, builder, break_points); - let instances = circuit.instances(); + let instances = SyncStepCircuit::::instances(args); let proof = full_prover(¶ms, &pk, circuit, instances.clone()); assert!(full_verifier(¶ms, pk.get_vk(), proof, instances)) @@ -420,20 +585,20 @@ 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); let mut builder = ShaThreadBuilder::prover(); - let assigned_instances = load_circuit_with_data(&mut builder, K); + let (assigned_instances, args) = load_circuit_with_data(&mut builder, K); let circuit = Eth2CircuitBuilder::prover(assigned_instances, builder, break_points); + let instances = SyncStepCircuit::::instances(args); - let num_instance = circuit.num_instance(); + let num_instance = vec![instances[0].len()]; let deployment_code = gen_evm_verifier_shplonk::< Eth2CircuitBuilder>, >(¶ms, pk.get_vk(), num_instance, None); - let instances = circuit.instances(); let proof = gen_evm_proof_shplonk(¶ms, &pk, circuit, instances.clone()); evm_verify(deployment_code, instances, proof); diff --git a/lightclient-circuits/src/util.rs b/lightclient-circuits/src/util.rs index cf90da9a..3cd47f76 100644 --- a/lightclient-circuits/src/util.rs +++ b/lightclient-circuits/src/util.rs @@ -75,6 +75,10 @@ pub trait AppCircuitExt: Default { ProvingKey, MultiPhaseThreadBreakPoints, ); + + fn instances(&self) -> Vec> { + vec![] + } } /// Randomness used in circuits. @@ -153,7 +157,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], diff --git a/lightclient-circuits/src/util/common.rs b/lightclient-circuits/src/util/common.rs index d8f3a11f..09bb9615 100644 --- a/lightclient-circuits/src/util/common.rs +++ b/lightclient-circuits/src/util/common.rs @@ -9,7 +9,9 @@ use halo2_base::{ }; use halo2_proofs::{ circuit::{AssignedCell, Layouter, Region, Value}, - plonk::{Advice, Assigned, Column, Error, Expression, Selector, VirtualCells, ConstraintSystem}, + plonk::{ + Advice, Assigned, Column, ConstraintSystem, Error, Expression, Selector, VirtualCells, + }, poly::Rotation, }; diff --git a/lightclient-circuits/src/witness/rotation.rs b/lightclient-circuits/src/witness/rotation.rs index e39f8d6e..280f848a 100644 --- a/lightclient-circuits/src/witness/rotation.rs +++ b/lightclient-circuits/src/witness/rotation.rs @@ -30,7 +30,7 @@ pub struct CommitteeRotationArgs { impl Default for CommitteeRotationArgs { fn default() -> Self { let dummy_x_bytes = iter::once(192).pad_using(48, |_| 0).rev().collect(); - + Self { pubkeys_compressed: iter::repeat(dummy_x_bytes) .take(S::SYNC_COMMITTEE_SIZE) diff --git a/lightclient-circuits/src/witness/sync.rs b/lightclient-circuits/src/witness/sync.rs index f3f30874..3a605387 100644 --- a/lightclient-circuits/src/witness/sync.rs +++ b/lightclient-circuits/src/witness/sync.rs @@ -9,7 +9,7 @@ use ethereum_consensus::capella; use ethereum_consensus::phase0::BeaconBlockHeader; use itertools::Itertools; use serde::Deserialize; -use sha2::{Sha256, Digest}; +use sha2::{Digest, Sha256}; use ssz_rs::Merkleized; use ssz_rs::Node;