diff --git a/README.md b/README.md index 0dd0370e..b38eb324 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ cd halo2-lib - [halo2-rsa](https://github.com/zkemail/halo2-rsa/tree/feat/new_bigint) - [halo2-fri-gadget](https://github.com/maxgillett/halo2-fri-gadget) -- FRI verifier in halo2. - [eth-voice-recovery](https://github.com/SoraSuegami/voice_recovery_circuit) -- Verify the voice recovery process. +- [PLUME ERC 7524 Signatures](https://github.com/plume-sig/zk-nullifier-sig/pull/83) - Verify deterministic PLUME signatures of Ethereum keys, for private voting nullifiers. - [zkEVM signature verification circuit](https://github.com/scroll-tech/zkevm-circuits/tree/develop/zkevm-circuits/src/sig_circuit.rs) - [zkEVM tx-circuit](https://github.com/scroll-tech/zkevm-circuits/tree/develop/zkevm-circuits/src/tx_circuit) - [webauthn-halo2](https://github.com/zkwebauthn/webauthn-halo2) -- Proving and verifying WebAuthn with halo2. diff --git a/halo2-base/README.md b/halo2-base/README.md index 94cbbc58..3ba763ea 100644 --- a/halo2-base/README.md +++ b/halo2-base/README.md @@ -121,7 +121,7 @@ QuantumCell contains the following enum variants. Assigns an entirely new witness value into the advice column, such as a private input. When `assign_cell()` is called the value is wrapped in as an `Assigned::Trivial()` which marks it for exclusion from batch inversion. - **WitnessFraction**: - Assigns an entirely new witness value to the advice column. `WitnessFraction` exists for optimization purposes and accepts Assigned values wrapped in `Assigned::Rational()` marked for batch inverion (see [Assigned](#assigned)). + Assigns an entirely new witness value to the advice column. `WitnessFraction` exists for optimization purposes and accepts Assigned values wrapped in `Assigned::Rational()` marked for batch inversion (see [Assigned](#assigned)). - **Constant**: A value that is a "known" constant. A "known" refers to known at circuit creation time to both the Prover and Verifier. When you assign a constant value there exists another secret Fixed column in the circuit constraint table whose values are fixed at circuit creation time. When you assign a Constant value, you are adding this value to the Fixed column, adding the value as a witness to the Advice column, and then imposing an equality constraint between the two corresponding cells in the Fixed and Advice columns. diff --git a/halo2-base/src/gates/circuit/builder.rs b/halo2-base/src/gates/circuit/builder.rs index dabd50f1..ae7e188d 100644 --- a/halo2-base/src/gates/circuit/builder.rs +++ b/halo2-base/src/gates/circuit/builder.rs @@ -67,7 +67,7 @@ impl Default for BaseCircuitBuilder { impl BaseCircuitBuilder { /// Creates a new [BaseCircuitBuilder] with all default managers. /// * `witness_gen_only`: - /// * If true, the builder only does witness asignments and does not store constraint information -- this should only be used for the real prover. + /// * If true, the builder only does witness assignments and does not store constraint information -- this should only be used for the real prover. /// * If false, the builder also imposes constraints (selectors, fixed columns, copy constraints). Primarily used for keygen and mock prover (but can also be used for real prover). /// /// By default, **no** circuit configuration parameters have been set. @@ -254,7 +254,7 @@ impl BaseCircuitBuilder { /// Auto-calculates configuration parameters for the circuit and sets them. /// - /// * `k`: The number of in the circuit (i.e. numeber of rows = 2k) + /// * `k`: The number of in the circuit (i.e. number of rows = 2k) /// * `minimum_rows`: The minimum number of rows in the circuit that cannot be used for witness assignments and contain random `blinding factors` to ensure zk property, defaults to 0. /// * `lookup_bits`: The fixed lookup table will consist of [0, 2lookup_bits) pub fn calculate_params(&mut self, minimum_rows: Option) -> BaseCircuitParams { diff --git a/halo2-base/src/gates/flex_gate/mod.rs b/halo2-base/src/gates/flex_gate/mod.rs index 03f952b6..82408e63 100644 --- a/halo2-base/src/gates/flex_gate/mod.rs +++ b/halo2-base/src/gates/flex_gate/mod.rs @@ -137,7 +137,7 @@ impl FlexGateConfig { Self { basic_gates, constants, - /// Warning: this needs to be updated if you create more advice columns after this `FlexGateConfig` is created + // Warning: this needs to be updated if you create more advice columns after this `FlexGateConfig` is created max_rows: (1 << params.k) - meta.minimum_rows(), } } @@ -545,7 +545,7 @@ pub trait GateInstructions { ctx.last().unwrap() } - /// Constrains and returns `a & b`, assumeing `a` and `b` are boolean. + /// Constrains and returns `a & b`, assuming `a` and `b` are boolean. /// /// Defines a vertical gate of form | 0 | a | b | out |, where out = a * b. /// * `ctx`: [Context] to add the constraints to. @@ -590,7 +590,7 @@ pub trait GateInstructions { ctx.last().unwrap() } - /// Constrains and returns `!a` assumeing `a` is boolean. + /// Constrains and returns `!a` assuming `a` is boolean. /// /// Defines a vertical gate of form | 1 - a | a | 1 | 1 |, where 1 - a = out. /// * `ctx`: [Context] to add the constraints to. @@ -1204,7 +1204,7 @@ impl GateInstructions for GateChip { ctx.last().unwrap() } - /// Constains and returns `a || (b && c)`, assuming `a`, `b` and `c` are boolean. + /// Constrains and returns `a || (b && c)`, assuming `a`, `b` and `c` are boolean. /// /// Defines a vertical gate of form `| 1 - b c | b | c | 1 | a - 1 | 1 - b c | out | a - 1 | 1 | 1 | a |`, where out = a + b * c - a * b * c. /// * `ctx`: [Context] to add the constraints to diff --git a/halo2-base/src/gates/flex_gate/threads/multi_phase.rs b/halo2-base/src/gates/flex_gate/threads/multi_phase.rs index ae893fb1..44e3401f 100644 --- a/halo2-base/src/gates/flex_gate/threads/multi_phase.rs +++ b/halo2-base/src/gates/flex_gate/threads/multi_phase.rs @@ -29,7 +29,7 @@ impl MultiPhaseCoreManager { /// Creates a new [MultiPhaseCoreManager] with a default [SinglePhaseCoreManager] in phase 0. /// Creates an empty [SharedCopyConstraintManager] and sets `witness_gen_only` flag. /// * `witness_gen_only`: If true, the [MultiPhaseCoreManager] is used for witness generation only. - /// * If true, the gate thread builder only does witness asignments and does not store constraint information -- this should only be used for the real prover. + /// * If true, the gate thread builder only does witness assignments and does not store constraint information -- this should only be used for the real prover. /// * If false, the gate thread builder is used for keygen and mock prover (it can also be used for real prover) and the builder stores circuit information (e.g. copy constraints, fixed columns, enabled selectors). /// * These values are fixed for the circuit at key generation time, and they do not need to be re-computed by the prover in the actual proving phase. pub fn new(witness_gen_only: bool) -> Self { @@ -126,7 +126,7 @@ impl MultiPhaseCoreManager { /// Auto-calculates configuration parameters for the circuit /// - /// * `k`: The number of in the circuit (i.e. numeber of rows = 2k) + /// * `k`: The number of in the circuit (i.e. number of rows = 2k) /// * `minimum_rows`: The minimum number of rows in the circuit that cannot be used for witness assignments and contain random `blinding factors` to ensure zk property, defaults to 0. pub fn calculate_params(&self, k: usize, minimum_rows: Option) -> FlexGateConfigParams { let max_rows = (1 << k) - minimum_rows.unwrap_or(0); diff --git a/halo2-base/src/gates/flex_gate/threads/single_phase.rs b/halo2-base/src/gates/flex_gate/threads/single_phase.rs index a554d727..472ae999 100644 --- a/halo2-base/src/gates/flex_gate/threads/single_phase.rs +++ b/halo2-base/src/gates/flex_gate/threads/single_phase.rs @@ -42,7 +42,7 @@ pub struct SinglePhaseCoreManager { impl SinglePhaseCoreManager { /// Creates a new [SinglePhaseCoreManager] and spawns a main thread. /// * `witness_gen_only`: If true, the [SinglePhaseCoreManager] is used for witness generation only. - /// * If true, the gate thread builder only does witness asignments and does not store constraint information -- this should only be used for the real prover. + /// * If true, the gate thread builder only does witness assignments and does not store constraint information -- this should only be used for the real prover. /// * If false, the gate thread builder is used for keygen and mock prover (it can also be used for real prover) and the builder stores circuit information (e.g. copy constraints, fixed columns, enabled selectors). /// * These values are fixed for the circuit at key generation time, and they do not need to be re-computed by the prover in the actual proving phase. pub fn new(witness_gen_only: bool, copy_manager: SharedCopyConstraintManager) -> Self { diff --git a/halo2-base/src/gates/tests/bitwise_rotate.rs b/halo2-base/src/gates/tests/bitwise_rotate.rs index d854ffbe..e2a262d3 100644 --- a/halo2-base/src/gates/tests/bitwise_rotate.rs +++ b/halo2-base/src/gates/tests/bitwise_rotate.rs @@ -71,7 +71,7 @@ fn test_bitwise_rotate_gen( #[test] fn test_bitwise_rotate() { - // "<<" is leftroate. ">>" is rightrotate. + // "<<" is leftrotate. ">>" is rightrotate. // 1 << 8 == 256 test_bitwise_rotate_gen::<8, 10>(8, true, 1, 256, true); // 1 << 8 != 255 diff --git a/halo2-base/src/gates/tests/neg_prop.rs b/halo2-base/src/gates/tests/neg_prop.rs index 27994ac0..cc098bb0 100644 --- a/halo2-base/src/gates/tests/neg_prop.rs +++ b/halo2-base/src/gates/tests/neg_prop.rs @@ -101,7 +101,7 @@ fn arb_indicator(max_size: usize) -> impl Strategy bool { // check that: - // the length of the witnes array is correct + // the length of the witness array is correct // the sum of the witnesses is 1, indicting that there is only one index that is 1 if ind_witnesses.len() != len || ind_witnesses.iter().fold(Fr::zero(), |acc, val| acc + *val) != Fr::one() diff --git a/halo2-base/src/lib.rs b/halo2-base/src/lib.rs index 669bc1ad..ac2a3226 100644 --- a/halo2-base/src/lib.rs +++ b/halo2-base/src/lib.rs @@ -48,7 +48,7 @@ use virtual_region::copy_constraints::SharedCopyConstraintManager; pub mod gates; /// Module for the Poseidon hash function. pub mod poseidon; -/// Module for SafeType which enforce value range and realted functions. +/// Module for SafeType which enforce value range and related functions. pub mod safe_types; /// Utility functions for converting between different types of field elements. pub mod utils; diff --git a/halo2-base/src/poseidon/hasher/mds.rs b/halo2-base/src/poseidon/hasher/mds.rs index 91b7d262..5088a864 100644 --- a/halo2-base/src/poseidon/hasher/mds.rs +++ b/halo2-base/src/poseidon/hasher/mds.rs @@ -34,7 +34,7 @@ pub struct SparseMDSMatrix { pub(crate) col_hat: [F; RATE], } -/// `MDSMatrix` is applied to `State` to achive linear layer of Poseidon +/// `MDSMatrix` is applied to `State` to achieve linear layer of Poseidon #[derive(Clone, Debug)] pub struct MDSMatrix(pub(crate) Mds); diff --git a/halo2-base/src/poseidon/hasher/mod.rs b/halo2-base/src/poseidon/hasher/mod.rs index 68cf64c6..035862d5 100644 --- a/halo2-base/src/poseidon/hasher/mod.rs +++ b/halo2-base/src/poseidon/hasher/mod.rs @@ -243,7 +243,7 @@ impl PoseidonHasher { } // check if the results from hasher and native sponge are same for hash_var_len_array. -fn hasher_compatiblity_verification< +fn hasher_compatibility_verification< const T: usize, const RATE: usize, const R_F: usize, @@ -50,7 +50,7 @@ fn hasher_compatiblity_verification< } // check if the results from hasher and native sponge are same for hash_compact_input. -fn hasher_compact_inputs_compatiblity_verification< +fn hasher_compact_inputs_compatibility_verification< const T: usize, const RATE: usize, const R_F: usize, @@ -115,7 +115,7 @@ fn hasher_compact_inputs_compatiblity_verification< } // check if the results from hasher and native sponge are same for hash_compact_input. -fn hasher_compact_chunk_inputs_compatiblity_verification< +fn hasher_compact_chunk_inputs_compatibility_verification< const T: usize, const RATE: usize, const R_F: usize, @@ -189,7 +189,7 @@ fn random_payload_without_len(max_len: usize, max_value: usize) } #[test] -fn test_poseidon_hasher_compatiblity() { +fn test_poseidon_hasher_compatibility() { { const T: usize = 3; const RATE: usize = 2; @@ -216,7 +216,7 @@ fn test_poseidon_hasher_compatiblity() { random_payload(RATE * 2 + 1, RATE * 2 + 1, usize::MAX), random_payload(RATE * 5 + 1, RATE * 5 + 1, usize::MAX), ]; - hasher_compatiblity_verification::(payloads); + hasher_compatibility_verification::(payloads); } } @@ -262,7 +262,9 @@ fn test_poseidon_hasher_compact_inputs() { random_payload(RATE * 5, RATE * 5, usize::MAX), ]; base_test().k(12).run(|ctx, range| { - hasher_compact_inputs_compatiblity_verification::(payloads, ctx, range); + hasher_compact_inputs_compatibility_verification::( + payloads, ctx, range, + ); }); } } @@ -289,7 +291,7 @@ fn test_poseidon_hasher_compact_inputs_with_prover() { .collect::>(); base_test().k(12).bench_builder(init_payloads, logic_payloads, |pool, range, input| { let ctx = pool.main(); - hasher_compact_inputs_compatiblity_verification::(input, ctx, range); + hasher_compact_inputs_compatibility_verification::(input, ctx, range); }); } } @@ -306,7 +308,7 @@ fn test_poseidon_hasher_compact_chunk_inputs() { (random_payload(RATE * 3, RATE * 3, usize::MAX), true), ]; base_test().k(12).run(|ctx, range| { - hasher_compact_chunk_inputs_compatiblity_verification::( + hasher_compact_chunk_inputs_compatibility_verification::( payloads, ctx, range, ); }); @@ -320,7 +322,7 @@ fn test_poseidon_hasher_compact_chunk_inputs() { (random_payload(0, 0, usize::MAX), false), ]; base_test().k(12).run(|ctx, range| { - hasher_compact_chunk_inputs_compatiblity_verification::( + hasher_compact_chunk_inputs_compatibility_verification::( payloads, ctx, range, ); }); @@ -349,7 +351,7 @@ fn test_poseidon_hasher_compact_chunk_inputs_with_prover() { .collect::>(); base_test().k(12).bench_builder(init_payloads, logic_payloads, |pool, range, input| { let ctx = pool.main(); - hasher_compact_chunk_inputs_compatiblity_verification::( + hasher_compact_chunk_inputs_compatibility_verification::( input, ctx, range, ); }); diff --git a/halo2-base/src/poseidon/hasher/tests/mod.rs b/halo2-base/src/poseidon/hasher/tests/mod.rs index a734f7d0..76087be2 100644 --- a/halo2-base/src/poseidon/hasher/tests/mod.rs +++ b/halo2-base/src/poseidon/hasher/tests/mod.rs @@ -11,18 +11,18 @@ mod state; fn test_mds() { let spec = OptimizedPoseidonSpec::::new::<8, 57, 0>(); - let mds = vec![ - vec![ + let mds = [ + [ "7511745149465107256748700652201246547602992235352608707588321460060273774987", "10370080108974718697676803824769673834027675643658433702224577712625900127200", "19705173408229649878903981084052839426532978878058043055305024233888854471533", ], - vec![ + [ "18732019378264290557468133440468564866454307626475683536618613112504878618481", "20870176810702568768751421378473869562658540583882454726129544628203806653987", "7266061498423634438633389053804536045105766754026813321943009179476902321146", ], - vec![ + [ "9131299761947733513298312097611845208338517739621853568979632113419485819303", "10595341252162738537912664445405114076324478519622938027420701542910180337937", "11597556804922396090267472882856054602429588299176362916247939723151043581408", diff --git a/halo2-base/src/safe_types/mod.rs b/halo2-base/src/safe_types/mod.rs index 205c314e..98485637 100644 --- a/halo2-base/src/safe_types/mod.rs +++ b/halo2-base/src/safe_types/mod.rs @@ -34,7 +34,7 @@ const BITS_PER_BYTE: usize = 8; /// where each [`AssignedValue`] represents 1 byte. However, the range of [`AssignedValue`] is much /// larger than 1 byte(0~255). If a circuit takes 32 [`AssignedValue`] as inputs and some of them /// are actually greater than 255, there could be some undefined behaviors. -/// [`SafeType`] gurantees the value range of its owned [`AssignedValue`]. So circuits don't need to +/// [`SafeType`] guarantees the value range of its owned [`AssignedValue`]. So circuits don't need to /// do any extra value checking if they take SafeType as inputs. /// - `TOTAL_BITS` is the number of total bits of this type. /// - `BYTES_PER_ELE` is the number of bytes of each element. @@ -131,7 +131,7 @@ impl<'a, F: ScalarField> SafeTypeChip<'a, F> { /// Convert a vector of AssignedValue (treated as little-endian) to a SafeType. /// The number of bytes of inputs must equal to the number of bytes of outputs. - /// This function also add contraints that a AssignedValue in inputs must be in the range of a byte. + /// This function also add constraints that a AssignedValue in inputs must be in the range of a byte. pub fn raw_bytes_to( &self, ctx: &mut Context, @@ -276,7 +276,7 @@ impl<'a, F: ScalarField> SafeTypeChip<'a, F> { /// /// * inputs: Vector representing the byte array, right padded to `max_len`. See [VarLenBytesVec] for details about padding. /// * len: [`AssignedValue`] witness representing the variable length of the byte array. Constrained to be `<= max_len`. - /// * max_len: [usize] representing the maximum length of the byte array and the number of elements it must contain. We enforce this to be provided explictly to make sure length of `inputs` is determinstic. + /// * max_len: [usize] representing the maximum length of the byte array and the number of elements it must contain. We enforce this to be provided explicitly to make sure length of `inputs` is deterministic. /// /// ## Assumptions /// * `max_len < u64::MAX` to prevent overflow (but you should never make an array this large) @@ -311,7 +311,7 @@ impl<'a, F: ScalarField> SafeTypeChip<'a, F> { /// Converts a slice of AssignedValue(treated as little-endian) to FixLenBytesVec. /// /// * inputs: Slice representing the byte array. - /// * len: length of the byte array. We enforce this to be provided explictly to make sure length of `inputs` is determinstic. + /// * len: length of the byte array. We enforce this to be provided explicitly to make sure length of `inputs` is deterministic. pub fn raw_to_fix_len_bytes_vec( &self, ctx: &mut Context, diff --git a/halo2-base/src/safe_types/tests/bytes.rs b/halo2-base/src/safe_types/tests/bytes.rs index 9c24444f..2e58ddc4 100644 --- a/halo2-base/src/safe_types/tests/bytes.rs +++ b/halo2-base/src/safe_types/tests/bytes.rs @@ -16,7 +16,7 @@ use rand::rngs::OsRng; use std::vec; use test_case::test_case; -// =========== Utilies =============== +// =========== Utilities =============== fn mock_circuit_test, SafeTypeChip<'_, Fr>)>(mut f: FM) { base_test().k(10).lookup_bits(8).run(|ctx, range| { let safe = SafeTypeChip::new(range); diff --git a/halo2-base/src/utils/mod.rs b/halo2-base/src/utils/mod.rs index 2aaa5166..87404349 100644 --- a/halo2-base/src/utils/mod.rs +++ b/halo2-base/src/utils/mod.rs @@ -62,7 +62,7 @@ pub trait ScalarField: PrimeField + FromUniformBytes<64> + From + Hash + O /// Creates a field element from a little endian byte representation. /// /// The default implementation assumes that `PrimeField::from_repr` is implemented for little-endian. - /// It should be overriden if this is not the case. + /// It should be overridden if this is not the case. fn from_bytes_le(bytes: &[u8]) -> Self { let mut repr = Self::Repr::default(); repr.as_mut()[..bytes.len()].copy_from_slice(bytes); diff --git a/halo2-ecc/Cargo.toml b/halo2-ecc/Cargo.toml index fba53531..98ff56e5 100644 --- a/halo2-ecc/Cargo.toml +++ b/halo2-ecc/Cargo.toml @@ -13,7 +13,7 @@ rust-version = "1.73.0" itertools = "0.11" num-bigint = { version = "0.4", features = ["rand"] } num-integer = "0.1" -num-traits = "0.2" +num-traits = "0.2.17" rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } rand = "0.8" rand_chacha = "0.3.1" @@ -26,6 +26,7 @@ halo2-base = { version = "=0.4.1", path = "../halo2-base", default-features = fa # plotting circuit layout plotters = { version = "0.3.0", optional = true } +sha2 = "0.10.8" [dev-dependencies] ark-std = { version = "0.3.0", features = ["print-trace"] } diff --git a/halo2-ecc/src/bigint/mod.rs b/halo2-ecc/src/bigint/mod.rs index 37c32ecf..c7965adf 100644 --- a/halo2-ecc/src/bigint/mod.rs +++ b/halo2-ecc/src/bigint/mod.rs @@ -125,7 +125,7 @@ impl FixedOverflowInteger { /// Input: a BigInteger `value`, Output: the `FixedOverflowInteger` that represents the same value /// Can handle signs - /// Note the representation of the integer will be in proper (no overflow) format, if signs are interpretted correctly + /// Note the representation of the integer will be in proper (no overflow) format, if signs are interpreted correctly pub fn from_native(value: &BigUint, num_limbs: usize, limb_bits: usize) -> Self { let limbs = decompose_biguint(value, num_limbs, limb_bits); Self { limbs } diff --git a/halo2-ecc/src/ecc/fixed_base.rs b/halo2-ecc/src/ecc/fixed_base.rs index 304cd6b8..936b0efb 100644 --- a/halo2-ecc/src/ecc/fixed_base.rs +++ b/halo2-ecc/src/ecc/fixed_base.rs @@ -208,7 +208,7 @@ where }, ); let ctx = builder.main(); - // sum `scalar_mults` but take into account possiblity of identity points + // sum `scalar_mults` but take into account possibility of identity points let any_point2 = chip.load_random_point::(ctx); let mut acc = any_point2.clone(); for point in scalar_mults { diff --git a/halo2-ecc/src/ecc/mod.rs b/halo2-ecc/src/ecc/mod.rs index b410b1e0..703a4305 100644 --- a/halo2-ecc/src/ecc/mod.rs +++ b/halo2-ecc/src/ecc/mod.rs @@ -415,7 +415,7 @@ where EcPoint::new(Rx, Ry) } -// takes the dot product of points with sel, where each is intepreted as +// takes the dot product of points with sel, where each is interpreted as // a _vector_ pub fn ec_select_by_indicator( chip: &FC, @@ -635,7 +635,7 @@ where // need to supply an extra generic `C` implementing `CurveAffine` trait in order to generate random witness points on the curve in question // Using Simultaneous 2^w-Ary Method, see https://www.bmoeller.de/pdf/multiexp-sac2001.pdf -// Random Accumlation point trick learned from halo2wrong: https://hackmd.io/ncuKqRXzR-Cw-Au2fGzsMg?view +// Random Accumulation point trick learned from halo2wrong: https://hackmd.io/ncuKqRXzR-Cw-Au2fGzsMg?view // Input: // - `scalars` is vector of same length as `P` // - each `scalar` in `scalars` satisfies same assumptions as in `scalar_multiply` above @@ -1090,7 +1090,7 @@ where if radix == 0 { radix = 1; }*/ - // guessing that is is always better to use parallelism for >25 points + // guessing that is always better to use parallelism for >25 points pippenger::multi_exp_par::( self.field_chip, builder, diff --git a/halo2-ecc/src/ecc/schnorr_signature.rs b/halo2-ecc/src/ecc/schnorr_signature.rs index a124560f..85b42c02 100644 --- a/halo2-ecc/src/ecc/schnorr_signature.rs +++ b/halo2-ecc/src/ecc/schnorr_signature.rs @@ -63,7 +63,7 @@ where let x_neq = base_chip.gate().not(ctx, x_eq); // R = s⋅G - e⋅P - // R is not infinity point implicitly constrainted by is_strict = true + // R is not infinity point implicitly constrained by is_strict = true let R = chip.sub_unequal(ctx, s_G, e_P, true); // check R.y is even diff --git a/halo2-ecc/src/fields/mod.rs b/halo2-ecc/src/fields/mod.rs index 469397df..de79dea2 100644 --- a/halo2-ecc/src/fields/mod.rs +++ b/halo2-ecc/src/fields/mod.rs @@ -61,7 +61,7 @@ pub trait FieldChip: Clone + Send + Sync { /// Assigns `fe` as private witness. Note that the witness may **not** be constrained to be a unique representation of the field element `fe`. fn load_private(&self, ctx: &mut Context, fe: Self::FieldType) -> Self::FieldPoint; - /// Assigns `fe` as private witness and contrains the witness to be in reduced form. + /// Assigns `fe` as private witness and constrains the witness to be in reduced form. fn load_private_reduced( &self, ctx: &mut Context, diff --git a/halo2-ecc/src/fields/native_fp.rs b/halo2-ecc/src/fields/native_fp.rs index 7391d1db..bb3f70b4 100644 --- a/halo2-ecc/src/fields/native_fp.rs +++ b/halo2-ecc/src/fields/native_fp.rs @@ -145,7 +145,7 @@ impl<'range, F: BigPrimeField> FieldChip for NativeFieldChip<'range, F> { a: impl Into>, max_bits: usize, // the maximum bits that a.value could take ) { - // skip range chek if max_bits >= F::NUM_BITS + // skip range check if max_bits >= F::NUM_BITS if max_bits < F::NUM_BITS as usize { let a: AssignedValue = a.into(); self.range().range_check(ctx, a, max_bits); diff --git a/halo2-ecc/src/secp256k1/hash_to_curve/constants.rs b/halo2-ecc/src/secp256k1/hash_to_curve/constants.rs new file mode 100644 index 00000000..53e623e5 --- /dev/null +++ b/halo2-ecc/src/secp256k1/hash_to_curve/constants.rs @@ -0,0 +1,286 @@ +use crate::{bigint::ProperCrtUint, secp256k1::FpChip}; +use halo2_base::{utils::BigPrimeField, AssignedValue, Context}; +use num_bigint::BigUint; +use num_traits::Num; + +pub(crate) fn get_z_pad(ctx: &mut Context) -> Vec> { + let zero = ctx.load_zero(); + vec![zero; 64] +} + +pub(crate) fn get_lib_str(ctx: &mut Context) -> Vec> { + let zero = ctx.load_zero(); + let ninety_six = ctx.load_constant(F::from(96)); + vec![zero, ninety_six] +} + +pub(crate) fn get_dst_prime(ctx: &mut Context) -> Vec> { + let dst_prime = [ + 81, 85, 85, 88, 45, 86, 48, 49, 45, 67, 83, 48, 50, 45, 119, 105, 116, 104, 45, 115, 101, + 99, 112, 50, 53, 54, 107, 49, 95, 88, 77, 68, 58, 83, 72, 65, 45, 50, 53, 54, 95, 83, 83, + 87, 85, 95, 82, 79, 95, 49, + ]; + dst_prime.into_iter().map(F::from).map(|v| ctx.load_constant(v)).collect::>() +} + +// Z = 115792089237316195423570985008687907853269984665640564039457584007908834671652 +pub(crate) fn get_Z( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "115792089237316195423570985008687907853269984665640564039457584007908834671652", + 10, + ) + .unwrap(), + ) +} + +// A = 28734576633528757162648956269730739219262246272443394170905244663053633733939 +pub(crate) fn get_A( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "28734576633528757162648956269730739219262246272443394170905244663053633733939", + 10, + ) + .unwrap(), + ) +} + +pub(crate) fn get_B( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint(ctx, BigUint::from(1771u64)) +} + +// c1 = 5324262023205125242632636178842408935272934169651804884418803605709653231043 +pub(crate) fn get_C1( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "5324262023205125242632636178842408935272934169651804884418803605709653231043", + 10, + ) + .unwrap(), + ) +} + +// c2 = 31579660701086235115519359547823974869073632181538335647124795638520591274090 +pub(crate) fn get_C2( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "31579660701086235115519359547823974869073632181538335647124795638520591274090", + 10, + ) + .unwrap(), + ) +} + +// k_(1,0) = 0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7 +pub(crate) fn get_k_1_0( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7", + 16, + ) + .unwrap(), + ) +} + +// k_(1,1) = 0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581 +pub(crate) fn get_k_1_1( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581", + 16, + ) + .unwrap(), + ) +} + +// k_(1,2) = 0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262 +pub(crate) fn get_k_1_2( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262", + 16, + ) + .unwrap(), + ) +} + +// k_(1,3) = 0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c +pub(crate) fn get_k_1_3( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c", + 16, + ) + .unwrap(), + ) +} + +// k_(2,0) = 0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b +pub(crate) fn get_k_2_0( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "d35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b", + 16, + ) + .unwrap(), + ) +} + +// k_(2,1) = 0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14 +pub(crate) fn get_k_2_1( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "edadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14", + 16, + ) + .unwrap(), + ) +} + +// k_(3,0) = 0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c +pub(crate) fn get_k_3_0( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c", + 16, + ) + .unwrap(), + ) +} + +// k_(3,1) = 0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3 +pub(crate) fn get_k_3_1( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "c75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3", + 16, + ) + .unwrap(), + ) +} + +// k_(3,2) = 0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931 +pub(crate) fn get_k_3_2( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931", + 16, + ) + .unwrap(), + ) +} + +// k_(3,3) = 0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84 +pub(crate) fn get_k_3_3( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84", + 16, + ) + .unwrap(), + ) +} + +// k_(4,0) = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b +pub(crate) fn get_k_4_0( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b", + 16, + ) + .unwrap(), + ) +} + +// k_(4,1) = 0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573 +pub(crate) fn get_k_4_1( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573", + 16, + ) + .unwrap(), + ) +} + +// k_(4,2) = 0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f +pub(crate) fn get_k_4_2( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, +) -> ProperCrtUint { + fp_chip.load_constant_uint( + ctx, + BigUint::from_str_radix( + "6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f", + 16, + ) + .unwrap(), + ) +} diff --git a/halo2-ecc/src/secp256k1/hash_to_curve/expand_message_xmd.rs b/halo2-ecc/src/secp256k1/hash_to_curve/expand_message_xmd.rs new file mode 100644 index 00000000..bec46975 --- /dev/null +++ b/halo2-ecc/src/secp256k1/hash_to_curve/expand_message_xmd.rs @@ -0,0 +1,143 @@ +use super::{ + constants::{get_dst_prime, get_lib_str, get_z_pad}, + util::{bits_le_to_bytes_assigned, bytes_to_bits_le_assigned}, +}; +use crate::secp256k1::sha256::Sha256Chip; +use halo2_base::{ + gates::{GateInstructions, RangeChip, RangeInstructions}, + utils::BigPrimeField, + AssignedValue, Context, QuantumCell, +}; + +fn calc_msg_prime_output_length(msg_length: usize) -> usize { + msg_length + 64 + 2 + 50 + 1 // msg + z_pad + lib_str + dst_prime + 0 +} + +fn msg_prime( + ctx: &mut Context, + msg_bytes: &[AssignedValue], +) -> Vec> { + let zero = ctx.load_zero(); + + let z_pad = get_z_pad(ctx); + let lib_str = get_lib_str(ctx); + let dst_prime = get_dst_prime(ctx); + + let msg_prime_len = calc_msg_prime_output_length(msg_bytes.len()); + let mut msg_prime = Vec::>::with_capacity(msg_prime_len); + + // msg_prme = z_pad ... + msg_prime.extend(z_pad); + + // msg_prme = z_pad || msg ... + msg_prime.extend(msg_bytes); + + // msg_prme = z_pad || msg || lib_str ... + msg_prime.extend(lib_str); + + // msg_prme = z_pad || msg || lib_str || 0 ... + msg_prime.push(zero); + + // msg_prme = z_pad || msg || lib_str || 0 || dst_prime + msg_prime.extend(dst_prime); + + assert_eq!(msg_prime.len(), msg_prime_len); + msg_prime +} + +fn hash_msg_prime_to_b0( + ctx: &mut Context, + sha256_chip: &Sha256Chip<'_, F>, + msg_prime_bytes: &[AssignedValue], +) -> Vec> { + let msg_prime_bytes = msg_prime_bytes.iter().map(|byte| QuantumCell::Existing(*byte)); + // TODO: Have a max len for the sha hash. + let hash = sha256_chip.digest(ctx, msg_prime_bytes).unwrap(); + hash +} + +fn hash_bi( + ctx: &mut Context, + range: &RangeChip, + sha256_chip: &Sha256Chip, + b_idx_byte: &AssignedValue, + b0_bytes: &[AssignedValue], + bi_minus_one_bytes: &[AssignedValue], +) -> Vec> { + assert_eq!(b0_bytes.len(), 32); + assert_eq!(b0_bytes.len(), bi_minus_one_bytes.len()); + + let b0_bits = bytes_to_bits_le_assigned(ctx, range, b0_bytes); + let bi_minus_one_bits = bytes_to_bits_le_assigned(ctx, range, bi_minus_one_bytes); + + let xor_bits = str_xor(ctx, range, &b0_bits, &bi_minus_one_bits); + let xor_bytes = bits_le_to_bytes_assigned(ctx, range, &xor_bits); + + let bi_bytes = hash_b(ctx, sha256_chip, b_idx_byte, &xor_bytes); + + bi_bytes +} + +fn hash_b( + ctx: &mut Context, + sha256_chip: &Sha256Chip<'_, F>, + b_idx_byte: &AssignedValue, + b_bytes: &[AssignedValue], +) -> Vec> { + assert_eq!(b_bytes.len(), 32); + assert!(b_idx_byte.value() < &F::from(8u64)); + + let dst_prime = get_dst_prime(ctx); + + let mut preimage = Vec::>::new(); + preimage.extend(b_bytes); + preimage.push(*b_idx_byte); + preimage.extend(dst_prime); + + let preimage = preimage.iter().map(|byte| QuantumCell::Existing(*byte)); + // TODO: Have a max len for the sha hash. + let hash = sha256_chip.digest(ctx, preimage).unwrap(); + + hash +} + +fn str_xor( + ctx: &mut Context, + range: &RangeChip, + a_bits: &[AssignedValue], + b_bits: &[AssignedValue], +) -> Vec> { + assert_eq!(a_bits.len(), b_bits.len()); + + let gate = range.gate(); + + let mut xor = Vec::>::new(); + for (a_bit, b_bit) in a_bits.iter().zip(b_bits.iter()) { + let res = gate.xor(ctx, *a_bit, *b_bit); + xor.push(res); + } + + xor +} + +pub(crate) fn expand_message_xmd( + ctx: &mut Context, + range: &RangeChip, + sha256_chip: &Sha256Chip, + msg_bytes: &[AssignedValue], +) -> Vec> { + let one = ctx.load_constant(F::from(1)); + let two = ctx.load_constant(F::from(2)); + let three = ctx.load_constant(F::from(3)); + + let msg_prime_bytes = msg_prime(ctx, msg_bytes); + let b0 = hash_msg_prime_to_b0(ctx, sha256_chip, &msg_prime_bytes); + let b1 = hash_b(ctx, sha256_chip, &one, &b0); + let b2 = hash_bi(ctx, range, sha256_chip, &two, &b0, &b1); + let b3 = hash_bi(ctx, range, sha256_chip, &three, &b0, &b2); + + let mut expanded_msg = [b1, b2, b3].concat(); + expanded_msg.reverse(); + + expanded_msg +} diff --git a/halo2-ecc/src/secp256k1/hash_to_curve/hash_to_field.rs b/halo2-ecc/src/secp256k1/hash_to_curve/hash_to_field.rs new file mode 100644 index 00000000..2ae6c4d0 --- /dev/null +++ b/halo2-ecc/src/secp256k1/hash_to_curve/hash_to_field.rs @@ -0,0 +1,70 @@ +use super::expand_message_xmd::expand_message_xmd; +use crate::{ + bigint::ProperCrtUint, + fields::FieldChip, + secp256k1::{hash_to_curve::util::limbs_le_to_bn, sha256::Sha256Chip, FpChip}, +}; +use halo2_base::{ + gates::{GateInstructions, RangeInstructions}, + utils::BigPrimeField, + AssignedValue, Context, QuantumCell, +}; +use itertools::Itertools; +use num_bigint::BigUint; + +fn bytes_le_to_limbs( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, + bytes: &[AssignedValue], +) -> Vec> { + let gate = fp_chip.range().gate(); + + let limb_bytes = fp_chip.limb_bits() / 8; + let byte_base = + (0..limb_bytes).map(|i| QuantumCell::Constant(gate.pow_of_two()[i * 8])).collect_vec(); + let limbs = bytes + .chunks(limb_bytes) + .map(|chunk| gate.inner_product(ctx, chunk.to_vec(), byte_base[..chunk.len()].to_vec())) + .collect::>(); + + limbs +} + +fn bytes_to_registers( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, + bytes: &[AssignedValue], +) -> ProperCrtUint { + let limbs = bytes_le_to_limbs(ctx, fp_chip, bytes); + + let lo_limbs = limbs[..3].to_vec(); + + let mut hi_limbs = limbs[3..].to_vec(); + hi_limbs.push(ctx.load_zero()); + + let lo = limbs_le_to_bn(ctx, fp_chip, lo_limbs.as_slice(), fp_chip.limb_bits()); + let hi = limbs_le_to_bn(ctx, fp_chip, hi_limbs.as_slice(), fp_chip.limb_bits()); + + let two_power_264 = fp_chip.load_constant_uint(ctx, BigUint::from(2u8).pow(264)); + + let num = fp_chip.mul_no_carry(ctx, hi, two_power_264); + let num = fp_chip.add_no_carry(ctx, num, lo); + let num = fp_chip.carry_mod(ctx, num); + + num +} + +pub(crate) fn hash_to_field( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, + sha256_chip: &Sha256Chip, + msg_bytes: &[AssignedValue], +) -> (ProperCrtUint, ProperCrtUint) { + let expanded_msg_bytes = expand_message_xmd(ctx, fp_chip.range, sha256_chip, msg_bytes); + assert_eq!(expanded_msg_bytes.len(), 96); + + let u1 = bytes_to_registers(ctx, fp_chip, &expanded_msg_bytes[0..48]); + let u0 = bytes_to_registers(ctx, fp_chip, &expanded_msg_bytes[48..96]); + + (u0, u1) +} diff --git a/halo2-ecc/src/secp256k1/hash_to_curve/iso_map.rs b/halo2-ecc/src/secp256k1/hash_to_curve/iso_map.rs new file mode 100644 index 00000000..7a161d77 --- /dev/null +++ b/halo2-ecc/src/secp256k1/hash_to_curve/iso_map.rs @@ -0,0 +1,164 @@ +use super::constants::{ + get_k_1_0, get_k_1_1, get_k_1_2, get_k_1_3, get_k_2_0, get_k_2_1, get_k_3_0, get_k_3_1, + get_k_3_2, get_k_3_3, get_k_4_0, get_k_4_1, get_k_4_2, +}; +use crate::{bigint::ProperCrtUint, fields::FieldChip, secp256k1::FpChip}; +use halo2_base::{utils::BigPrimeField, Context}; + +fn x_num( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, + x: &ProperCrtUint, + x_2: &ProperCrtUint, + x_3: &ProperCrtUint, +) -> ProperCrtUint { + let k_1_3 = get_k_1_3(ctx, fp_chip); + let k_1_2 = get_k_1_2(ctx, fp_chip); + let k_1_1 = get_k_1_1(ctx, fp_chip); + let k_1_0 = get_k_1_0(ctx, fp_chip); + + // Step 1: a = k_(1,3) * x'^3 + let a = fp_chip.mul(ctx, k_1_3, x_3); + + // Step 2: b = k_(1,2) * x'^2 + + let b = fp_chip.mul(ctx, k_1_2, x_2); + + // Step 3: c = k_(1,1) * x' + + let c = fp_chip.mul(ctx, k_1_1, x); + + // Step 4: a + b + let a_plus_b = fp_chip.add_no_carry(ctx, a, b); + let a_plus_b = fp_chip.carry_mod(ctx, a_plus_b); + + // Step 5: a + b + c + let a_plus_b_plus_c = fp_chip.add_no_carry(ctx, a_plus_b, c); + let a_plus_b_plus_c = fp_chip.carry_mod(ctx, a_plus_b_plus_c); + + // Step 6: a + b + c + k_1_0 + let a_plus_b_plus_c_plus_k_1_0 = fp_chip.add_no_carry(ctx, a_plus_b_plus_c, k_1_0); + let a_plus_b_plus_c_plus_k_1_0 = fp_chip.carry_mod(ctx, a_plus_b_plus_c_plus_k_1_0); + + a_plus_b_plus_c_plus_k_1_0 +} + +fn x_den( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, + x: &ProperCrtUint, + x_2: &ProperCrtUint, +) -> ProperCrtUint { + let k_2_0 = get_k_2_0(ctx, fp_chip); + let k_2_1 = get_k_2_1(ctx, fp_chip); + + // Step 1: a = x_2 + k_2_0 + let a = fp_chip.add_no_carry(ctx, x_2, k_2_0); + let a = fp_chip.carry_mod(ctx, a); + + // Step 2: b = x * k_2_1 + let b = fp_chip.mul(ctx, x, k_2_1); + + // Step 3: c = a + b + let c = fp_chip.add_no_carry(ctx, a, b); + let c = fp_chip.carry_mod(ctx, c); + + c +} + +fn y_num( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, + x: &ProperCrtUint, + x_2: &ProperCrtUint, + x_3: &ProperCrtUint, +) -> ProperCrtUint { + let k_3_3 = get_k_3_3(ctx, fp_chip); + let k_3_2 = get_k_3_2(ctx, fp_chip); + let k_3_1 = get_k_3_1(ctx, fp_chip); + let k_3_0 = get_k_3_0(ctx, fp_chip); + + // Step 1: a = k_3_3 * x_3 + let a = fp_chip.mul(ctx, k_3_3, x_3); + + // Step 2: b = k_3_2 * x_2 + let b = fp_chip.mul(ctx, k_3_2, x_2); + + // Step 3: c = k_3_1 * x + let c = fp_chip.mul(ctx, k_3_1, x); + + // Step 4: a + b + let a_plus_b = fp_chip.add_no_carry(ctx, a, b); + let a_plus_b = fp_chip.carry_mod(ctx, a_plus_b); + + // Step 5: a + b + c + let a_plus_b_plus_c = fp_chip.add_no_carry(ctx, a_plus_b, c); + let a_plus_b_plus_c = fp_chip.carry_mod(ctx, a_plus_b_plus_c); + + // Step 6: a + b + c + k_3_0 + let a_plus_b_plus_c_plus_k_3_0 = fp_chip.add_no_carry(ctx, a_plus_b_plus_c, k_3_0); + let a_plus_b_plus_c_plus_k_3_0 = fp_chip.carry_mod(ctx, a_plus_b_plus_c_plus_k_3_0); + + a_plus_b_plus_c_plus_k_3_0 +} + +fn y_den( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, + x: &ProperCrtUint, + x_2: &ProperCrtUint, + x_3: &ProperCrtUint, +) -> ProperCrtUint { + let k_4_0 = get_k_4_0(ctx, fp_chip); + let k_4_1 = get_k_4_1(ctx, fp_chip); + let k_4_2 = get_k_4_2(ctx, fp_chip); + + // Step 1: a = x_3 + k_4_0 + let a = fp_chip.add_no_carry(ctx, x_3, k_4_0); + let a = fp_chip.carry_mod(ctx, a); + + // Step 2: b = k_4_2 * x_2 + let b = fp_chip.mul(ctx, k_4_2, x_2); + + // Step 3: c = k_4_1 * x + let c = fp_chip.mul(ctx, k_4_1, x); + + // Step 4: a + b + let a_plus_b = fp_chip.add_no_carry(ctx, a, b); + let a_plus_b = fp_chip.carry_mod(ctx, a_plus_b); + + // Step 5: a + b + c + let a_plus_b_plus_c = fp_chip.add_no_carry(ctx, a_plus_b, c); + let a_plus_b_plus_c = fp_chip.carry_mod(ctx, a_plus_b_plus_c); + + a_plus_b_plus_c +} + +pub(crate) fn iso_map( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, + x: &ProperCrtUint, + y: &ProperCrtUint, +) -> (ProperCrtUint, ProperCrtUint) { + // Step 1: calculate x^2 + let x_2 = fp_chip.mul(ctx, x, x); + + // Step 2: calculate x^3 + let x_3 = fp_chip.mul(ctx, x, &x_2); + + // Step 3: calculate x_num + let x_num = x_num(ctx, fp_chip, x, &x_2, &x_3); + + // Step 4: calculate x_den + let x_den = x_den(ctx, fp_chip, x, &x_2); + + // Step 5: calculate y_num + let y_num = y_num(ctx, fp_chip, x, &x_2, &x_3); + + // Step 6: calculate y_den + let y_den = y_den(ctx, fp_chip, x, &x_2, &x_3); + + let x_mapped = fp_chip.divide(ctx, &x_num, &x_den); + let y_mapped = fp_chip.divide(ctx, &y_num, &y_den); + let y_mapped = fp_chip.mul(ctx, &y_mapped, y); + + (x_mapped, y_mapped) +} diff --git a/halo2-ecc/src/secp256k1/hash_to_curve/map_to_curve.rs b/halo2-ecc/src/secp256k1/hash_to_curve/map_to_curve.rs new file mode 100644 index 00000000..0cf9c1f1 --- /dev/null +++ b/halo2-ecc/src/secp256k1/hash_to_curve/map_to_curve.rs @@ -0,0 +1,176 @@ +use halo2_base::{ + gates::{GateInstructions, RangeInstructions}, + halo2_proofs::{arithmetic::Field, halo2curves::secp256k1::Fp}, + utils::{BigPrimeField, ScalarField}, + Context, +}; +use num_bigint::BigUint; + +use crate::{ + bigint::ProperCrtUint, + fields::{FieldChip, Selectable}, + secp256k1::{ + hash_to_curve::{ + constants::{get_A, get_B, get_C1, get_C2, get_Z}, + iso_map::iso_map, + }, + FpChip, + }, +}; + +fn xy2_selector( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, + gx1: &ProperCrtUint, + gx2: &ProperCrtUint, + x1: &ProperCrtUint, + x2: &ProperCrtUint, +) -> (ProperCrtUint, ProperCrtUint) { + let gx1_sqrt = sqrt(ctx, fp_chip, gx1); + let sq_gx1_sqrt = fp_chip.mul(ctx, &gx1_sqrt, &gx1_sqrt); + + let s1 = fp_chip.is_equal(ctx, &sq_gx1_sqrt, gx1); + + let x = fp_chip.select(ctx, x1.into(), x2.into(), s1); + let y2 = fp_chip.select(ctx, gx1.into(), gx2.into(), s1); + + (x, y2) +} + +fn mod_inverse( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, + num: &ProperCrtUint, +) -> ProperCrtUint { + let one = ctx.load_constant(F::ONE); + let one_int = fp_chip.load_constant(ctx, Fp::ONE); + + let p = fp_chip.p.to_biguint().unwrap(); + let p_minus_two = p.clone() - 2u64; + + let num_native = num.value(); + let inverse_native = num_native.modpow(&p_minus_two, &p); + assert_eq!((num_native * inverse_native.clone()) % p, BigUint::from(1u64)); + + let mod_inverse = fp_chip.load_private(ctx, Fp::from_bytes_le(&inverse_native.to_bytes_le())); + let is_one = fp_chip.mul(ctx, num, &mod_inverse); + let is_equal = fp_chip.is_equal(ctx, is_one, one_int); + assert_eq!(is_equal.value(), &F::ONE); + ctx.constrain_equal(&is_equal, &one); + + mod_inverse +} + +pub(crate) fn sqrt( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, + num: &ProperCrtUint, +) -> ProperCrtUint { + let p = fp_chip.p.to_biguint().unwrap(); + assert_eq!(&p % 4u64, BigUint::from(3u64), "p must be congruent to 3 mod 4"); + + let p_plus_1 = p.clone() + 1u64; + let p_plus_1_by_4 = p_plus_1 / 4u64; + + let sqrt_native = num.value().modpow(&p_plus_1_by_4, &p); + let sqrt = fp_chip.load_private(ctx, Fp::from_bytes_le(&sqrt_native.to_bytes_le())); + + sqrt +} + +pub(crate) fn map_to_curve( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, + u: &ProperCrtUint, +) -> (ProperCrtUint, ProperCrtUint) { + let range = fp_chip.range(); + let gate = range.gate(); + + let one = ctx.load_constant(F::ONE); + + let zero_int = + fp_chip.load_constant(ctx, Fp::from_bytes_le(BigUint::from(0u64).to_bytes_le().as_slice())); + let one_int = + fp_chip.load_constant(ctx, Fp::from_bytes_le(BigUint::from(1u64).to_bytes_le().as_slice())); + + // Step 1: tv1 = Z * u^2 + let u_sq = fp_chip.mul(ctx, u, u); + let z = get_Z(ctx, fp_chip); + let tv1 = fp_chip.mul(ctx, z, u_sq); + + // Step 2: tv2 = tv1^2 + let tv2 = fp_chip.mul(ctx, &tv1, &tv1); + + // Step 3: x1 = tv1 + tv2 + let x1 = fp_chip.add_no_carry(ctx, &tv1, &tv2); + let x1 = fp_chip.carry_mod(ctx, x1); + + // Step 4: x1 = inv0(x1) + let x1 = mod_inverse(ctx, fp_chip, &x1); + + // Step 5: e1 = x1 == 0& + let e1 = fp_chip.is_equal(ctx, &x1, zero_int); + + // Step 6: x1 = x1 + 1 + let x1 = fp_chip.add_no_carry(ctx, &x1, one_int); + let x1 = fp_chip.carry_mod(ctx, x1); + + // Step 7: x1 = e1 ? c2 : x1 + let c2 = get_C2(ctx, fp_chip); + let x1 = fp_chip.select(ctx, c2, x1, e1); + + // Step 8: x1 = x1 * c1 # x1 = (-B / A) * (1 + (1 / (Z^2 * u^4 + Z * u^2))) + let c1 = get_C1(ctx, fp_chip); + let x1 = fp_chip.mul(ctx, &x1, c1); + + // Step 9: gx1 = x1^2 + let gx1 = fp_chip.mul(ctx, &x1, &x1); + + // Step 10: gx1 = gx1 + A + let a = get_A(ctx, fp_chip); + let gx1 = fp_chip.add_no_carry(ctx, gx1, a); + let gx1 = fp_chip.carry_mod(ctx, gx1); + + // Step 11: gx1 = gx1 * x1 + let gx1 = fp_chip.mul(ctx, gx1, &x1); + + // Step 12: gx1 = gx1 + B # gx1 = g(x1) = x1^3 + A * x1 + B + let b = get_B(ctx, fp_chip); + let gx1 = fp_chip.add_no_carry(ctx, gx1, b); + let gx1 = fp_chip.carry_mod(ctx, gx1); + + // Step 13: x2 = tv1 * x1 # x2 = Z * u^2 * x1 + let x2 = fp_chip.mul(ctx, &tv1, &x1); + + // Step 14: tv2 = tv1 * tv2 + let tv2 = fp_chip.mul(ctx, tv1, tv2); + + // Step 15: gx2 = gx1 * tv2 # gx2 = (Z * u^2)^3 * gx1 + let gx2 = fp_chip.mul(ctx, &gx1, tv2); + + // Steps 16-18: + // e2 = is_square(gx1) + // x = CMOV(x2, x1, e2) # If is_square(gx1), x = x1, else x = x2 + // y2 = CMOV(gx2, gx1, e2) # If is_square(gx1), y2 = gx1, else y2 = gx2 + let (x, y2) = xy2_selector(ctx, fp_chip, &gx1, &gx2, &x1, &x2); + + // Step 19: y = sqrt(y2) + let y = sqrt(ctx, fp_chip, &y2); + let y_sq = fp_chip.mul(ctx, &y, &y); + let e2 = fp_chip.is_equal(ctx, y_sq, y2); + assert_eq!(e2.value(), &F::ONE); + ctx.constrain_equal(&e2, &one); + + // Step 20: e3 = sgn0(u) == sgn0(y) # Fix sign of y + let sgn_u = fp_chip.is_even(ctx, u); + let sgn_y = fp_chip.is_even(ctx, &y); + let e3 = gate.is_equal(ctx, sgn_u, sgn_y); + + // Step 21: y = e3 ? y : -y + let neg_y = fp_chip.negate(ctx, y.clone()); + let y = fp_chip.select(ctx, y, neg_y, e3); + + let (x_mapped, y_mapped) = iso_map(ctx, fp_chip, &x, &y); + + (x_mapped, y_mapped) +} diff --git a/halo2-ecc/src/secp256k1/hash_to_curve/mod.rs b/halo2-ecc/src/secp256k1/hash_to_curve/mod.rs new file mode 100644 index 00000000..f6a185b6 --- /dev/null +++ b/halo2-ecc/src/secp256k1/hash_to_curve/mod.rs @@ -0,0 +1,148 @@ +use halo2_base::{utils::BigPrimeField, AssignedValue, Context}; + +use crate::{bigint::ProperCrtUint, ecc::EcPoint}; + +use self::{hash_to_field::hash_to_field, map_to_curve::map_to_curve}; + +use super::{sha256::Sha256Chip, Secp256k1Chip}; + +pub mod constants; +pub mod expand_message_xmd; +pub mod hash_to_field; +pub mod iso_map; +pub mod map_to_curve; +pub mod util; + +pub fn hash_to_curve( + ctx: &mut Context, + secp256k1_chip: &Secp256k1Chip<'_, F>, + sha256_chip: &Sha256Chip, + msg_bytes: &[AssignedValue], +) -> EcPoint> { + let fp_chip = secp256k1_chip.field_chip(); + + // Step 1: u = hash_to_field(msg) + let (u0, u1) = hash_to_field(ctx, fp_chip, sha256_chip, msg_bytes); + + // Step 2: Q0 = map_to_curve(u[0]) + let (q0_x, q0_y) = map_to_curve(ctx, fp_chip, &u0); + + // Step 3: Q1 = map_to_curve(u[1]) + let (q1_x, q1_y) = map_to_curve(ctx, fp_chip, &u1); + + // Step 4: return A + B + let q0 = EcPoint::>::new(q0_x, q0_y); + let q1 = EcPoint::>::new(q1_x, q1_y); + + let point_add = secp256k1_chip.add_unequal(ctx, q0, q1, false); + + point_add +} + +#[cfg(test)] +mod test { + use halo2_base::{halo2_proofs::halo2curves::bn256::Fr, utils::testing::base_test}; + + use crate::{ + ecc::EccChip, + secp256k1::{sha256::Sha256Chip, FpChip}, + }; + + use super::hash_to_curve; + + #[derive(Debug, Default, Clone)] + struct TestData { + message: String, + point: (String, String), + } + + #[test] + fn test_hash_to_curve() { + let test_data = vec![ + TestData { + message: String::from(""), + point: ( + String::from( + "c1cae290e291aee617ebaef1be6d73861479c48b841eaba9b7b5852ddfeb1346" + ), + String::from( + "64fa678e07ae116126f08b022a94af6de15985c996c3a91b64c406a960e51067" + ), + ), + }, + TestData { + message: String::from("abc"), + point: ( + String::from( + "3377e01eab42db296b512293120c6cee72b6ecf9f9205760bd9ff11fb3cb2c4b" + ), + String::from( + "7f95890f33efebd1044d382a01b1bee0900fb6116f94688d487c6c7b9c8371f6" + ), + ), + }, + TestData { + message: String::from("abcdef0123456789"), + point: ( + String::from( + "bac54083f293f1fe08e4a70137260aa90783a5cb84d3f35848b324d0674b0e3a" + ), + String::from( + "4436476085d4c3c4508b60fcf4389c40176adce756b398bdee27bca19758d828" + ), + ), + }, + TestData { + message: String::from( + "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" + ), + point: ( + String::from( + "e2167bc785333a37aa562f021f1e881defb853839babf52a7f72b102e41890e9" + ), + String::from( + "f2401dd95cc35867ffed4f367cd564763719fbc6a53e969fb8496a1e6685d873" + ), + ), + }, + TestData { + message: String::from( + "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ), + point: ( + String::from( + "e3c8d35aaaf0b9b647e88a0a0a7ee5d5bed5ad38238152e4e6fd8c1f8cb7c998" + ), + String::from( + "8446eeb6181bf12f56a9d24e262221cc2f0c4725c7e3803024b5888ee5823aa6" + ), + ), + } + ]; + + base_test().k(15).lookup_bits(14).expect_satisfied(true).bench_builder( + TestData::default(), + test_data[2].clone(), + |pool, range, _: TestData| { + let ctx = pool.main(); + + let fp_chip = FpChip::::new(range, 88, 3); + let ecc_chip = EccChip::>::new(&fp_chip); + + let sha256_chip = Sha256Chip::new(range); + + let msg_bytes = test_data[2] + .message + .as_bytes() + .iter() + .map(|&x| ctx.load_witness(Fr::from(x as u64))) + .collect::>(); + + let point = hash_to_curve(ctx, &ecc_chip, &sha256_chip, msg_bytes.as_slice()); + + assert_eq!(point.x.value().to_str_radix(16), test_data[2].point.0); + assert_eq!(point.y.value().to_str_radix(16), test_data[2].point.1); + }, + ); + } +} diff --git a/halo2-ecc/src/secp256k1/hash_to_curve/util.rs b/halo2-ecc/src/secp256k1/hash_to_curve/util.rs new file mode 100644 index 00000000..f6408bb3 --- /dev/null +++ b/halo2-ecc/src/secp256k1/hash_to_curve/util.rs @@ -0,0 +1,71 @@ +use halo2_base::{ + gates::{GateInstructions, RangeChip, RangeInstructions}, + utils::{fe_to_biguint, BigPrimeField}, + AssignedValue, Context, +}; +use itertools::Itertools; +use num_bigint::{BigUint, ToBigInt}; + +use crate::{ + bigint::{CRTInteger, OverflowInteger, ProperCrtUint}, + fields::FieldChip, + secp256k1::FpChip, +}; + +pub(crate) fn byte_to_bits_le_assigned( + ctx: &mut Context, + range: &RangeChip, + byte: &AssignedValue, +) -> Vec> { + range.range_check(ctx, *byte, 8); + range.gate().num_to_bits(ctx, *byte, 8) +} + +pub(crate) fn bytes_to_bits_le_assigned( + ctx: &mut Context, + range: &RangeChip, + bytes: &[AssignedValue], +) -> Vec> { + bytes.iter().flat_map(|byte| byte_to_bits_le_assigned(ctx, range, byte)).collect_vec() +} + +pub(crate) fn bits_le_to_byte_assigned( + ctx: &mut Context, + range: &RangeChip, + bits: &[AssignedValue], +) -> AssignedValue { + assert_eq!(bits.len(), 8); + let _ = bits.iter().map(|bit| range.gate().assert_bit(ctx, *bit)); + range.gate().bits_to_num(ctx, bits) +} + +pub(crate) fn bits_le_to_bytes_assigned( + ctx: &mut Context, + range: &RangeChip, + bits: &[AssignedValue], +) -> Vec> { + bits.chunks(8).map(|chunk| bits_le_to_byte_assigned(ctx, range, chunk)).collect_vec() +} + +pub(crate) fn limbs_le_to_bn( + ctx: &mut Context, + fp_chip: &FpChip<'_, F>, + limbs: &[AssignedValue], + max_limb_bits: usize, +) -> ProperCrtUint { + let mut value = BigUint::from(0u64); + for i in 0..limbs.len() { + value += (BigUint::from(1u64) << (max_limb_bits * i)) * fe_to_biguint(limbs[i].value()); + } + + let assigned_uint = OverflowInteger::new(limbs.to_vec(), max_limb_bits); + let assigned_native = OverflowInteger::evaluate_native( + ctx, + fp_chip.range().gate(), + limbs.to_vec(), + &fp_chip.limb_bases, + ); + let assigned_uint = CRTInteger::new(assigned_uint, assigned_native, value.to_bigint().unwrap()); + + fp_chip.carry_mod(ctx, assigned_uint) +} diff --git a/halo2-ecc/src/secp256k1/mod.rs b/halo2-ecc/src/secp256k1/mod.rs index ca4528e4..38970d20 100644 --- a/halo2-ecc/src/secp256k1/mod.rs +++ b/halo2-ecc/src/secp256k1/mod.rs @@ -8,5 +8,8 @@ pub type FqChip<'range, F> = fp::FpChip<'range, F, Fq>; pub type Secp256k1Chip<'chip, F> = ecc::EccChip<'chip, F, FpChip<'chip, F>>; pub const SECP_B: u64 = 7; +pub mod hash_to_curve; +pub mod sha256; #[cfg(test)] mod tests; +pub mod util; diff --git a/halo2-ecc/src/secp256k1/sha256/compression.rs b/halo2-ecc/src/secp256k1/sha256/compression.rs new file mode 100644 index 00000000..ecfc97d0 --- /dev/null +++ b/halo2-ecc/src/secp256k1/sha256/compression.rs @@ -0,0 +1,609 @@ +use halo2_base::{ + gates::{GateInstructions, RangeInstructions}, + halo2_proofs::plonk::Error, + utils::BigPrimeField, + AssignedValue, Context, QuantumCell, +}; +use itertools::Itertools; + +use crate::secp256k1::util::{bits_le_to_fe, fe_to_bits_le}; + +use super::spread::SpreadChip; + +pub const NUM_ROUND: usize = 64; +pub const NUM_STATE_WORD: usize = 8; +const ROUND_CONSTANTS: [u32; NUM_ROUND] = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +]; + +pub const INIT_STATE: [u32; NUM_STATE_WORD] = [ + 0x6a09_e667, + 0xbb67_ae85, + 0x3c6e_f372, + 0xa54f_f53a, + 0x510e_527f, + 0x9b05_688c, + 0x1f83_d9ab, + 0x5be0_cd19, +]; + +pub type SpreadU32<'a, F> = (AssignedValue, AssignedValue); + +pub fn sha256_compression<'a, 'b: 'a, F: BigPrimeField>( + ctx: &mut Context, + spread_chip: &SpreadChip<'a, F>, + assigned_input_bytes: &[AssignedValue], + pre_state_words: &[AssignedValue], +) -> Result>, Error> { + debug_assert_eq!(assigned_input_bytes.len(), 64); + debug_assert_eq!(pre_state_words.len(), 8); + let range = spread_chip.range(); + let gate = range.gate(); + // message schedule. + let mut i = 0; + let mut message_u32s = assigned_input_bytes + .chunks(4) + .map(|bytes| { + let mut sum = ctx.load_zero(); + for idx in 0..4 { + sum = gate.mul_add( + ctx, + QuantumCell::Existing(bytes[3 - idx]), + QuantumCell::Constant(F::from(1u64 << (8 * idx))), + QuantumCell::Existing(sum), + ); + } + i += 1; + // println!("idx {} sum {:?}", i, sum.value()); + sum + }) + .collect_vec(); + + // let mut message_bits = message_u32s + // .iter() + // .map(|val: &AssignedValue| gate.num_to_bits(ctx, val, 32)) + // .collect_vec(); + let mut message_spreads = message_u32s + .iter() + .map(|dense| state_to_spread_u32(ctx, spread_chip, dense)) + .collect::>, Error>>()?; + for idx in 16..64 { + // let w_2_spread = state_to_spread_u32(ctx, range, ctx_spread, &message_u32s[idx - 2])?; + // let w_15_spread = state_to_spread_u32(ctx, range, ctx_spread, &message_u32s[idx - 15])?; + let term1 = sigma_lower1(ctx, spread_chip, &message_spreads[idx - 2])?; + let term3 = sigma_lower0(ctx, spread_chip, &message_spreads[idx - 15])?; + // let term1_u32 = bits2u32(ctx, gate, &term1_bits); + // let term3_u32 = bits2u32(ctx, gate, &term3_bits); + let new_w = { + let mut sum = gate.add(ctx, term1, message_u32s[idx - 7]); + sum = gate.add(ctx, sum, term3); + sum = gate.add(ctx, sum, message_u32s[idx - 16]); + mod_u32(ctx, range, &sum) + }; + // println!( + // "idx {} term1 {:?}, term3 {:?}, new_w {:?}", + // idx, + // term1.value(), + // term3.value(), + // new_w.value() + // ); + message_u32s.push(new_w); + let new_w_spread = state_to_spread_u32(ctx, spread_chip, &new_w)?; + message_spreads.push(new_w_spread); + // if idx <= 61 { + // let new_w_bits = gate.num_to_bits(ctx, &new_w, 32); + // message_bits.push(new_w_bits); + // } + } + + // compression + let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h) = ( + pre_state_words[0], + pre_state_words[1], + pre_state_words[2], + pre_state_words[3], + pre_state_words[4], + pre_state_words[5], + pre_state_words[6], + pre_state_words[7], + ); + let mut a_spread = state_to_spread_u32(ctx, spread_chip, &a)?; + let mut b_spread = state_to_spread_u32(ctx, spread_chip, &b)?; + let mut c_spread = state_to_spread_u32(ctx, spread_chip, &c)?; + // let mut d_spread = state_to_spread_u32(ctx, range, ctx_spread, &d)?; + let mut e_spread = state_to_spread_u32(ctx, spread_chip, &e)?; + let mut f_spread = state_to_spread_u32(ctx, spread_chip, &f)?; + let mut g_spread = state_to_spread_u32(ctx, spread_chip, &g)?; + // let mut h_spread = state_to_spread_u32(ctx, range, ctx_spread, &h)?; + // let mut a_bits = gate.num_to_bits(ctx, &a, 32); + // let mut b_bits = gate.num_to_bits(ctx, &b, 32); + // let mut c_bits = gate.num_to_bits(ctx, &c, 32); + // let mut e_bits = gate.num_to_bits(ctx, &e, 32); + // let mut f_bits = gate.num_to_bits(ctx, &f, 32); + // let mut g_bits = gate.num_to_bits(ctx, &g, 32); + #[allow(unused_assignments)] + let mut t1 = ctx.load_zero(); + #[allow(unused_assignments)] + let mut t2 = ctx.load_zero(); + for idx in 0..64 { + t1 = { + // let e_spread = state_to_spread_u32(ctx, range, ctx_spread, &e)?; + // 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(ctx, spread_chip, &e_spread)?; + let ch_term = ch(ctx, spread_chip, &e_spread, &f_spread, &g_spread)?; + // println!( + // "idx {} sigma {:?} ch {:?}", + // idx, + // sigma_term.value(), + // ch_term.value() + // ); + let add1 = gate.add(ctx, h, sigma_term); + let add2 = gate.add(ctx, QuantumCell::Existing(add1), QuantumCell::Existing(ch_term)); + let add3 = gate.add( + ctx, + QuantumCell::Existing(add2), + QuantumCell::Constant(F::from(ROUND_CONSTANTS[idx] as u64)), + ); + let add4 = gate.add( + ctx, + QuantumCell::Existing(add3), + QuantumCell::Existing(message_u32s[idx]), + ); + mod_u32(ctx, range, &add4) + }; + t2 = { + // let a_spread = state_to_spread_u32(ctx, range, ctx_spread, &a)?; + // 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(ctx, spread_chip, &a_spread)?; + let maj_term = maj(ctx, spread_chip, &a_spread, &b_spread, &c_spread)?; + let add = + gate.add(ctx, QuantumCell::Existing(sigma_term), QuantumCell::Existing(maj_term)); + mod_u32(ctx, range, &add) + }; + // println!("idx {}, t1 {:?}, t2 {:?}", idx, t1.value(), t2.value()); + h = g; + // h_spread = g_spread; + g = f; + g_spread = f_spread; + f = e; + f_spread = e_spread; + e = { + let add = gate.add(ctx, QuantumCell::Existing(d), QuantumCell::Existing(t1)); + mod_u32(ctx, range, &add) + }; + e_spread = state_to_spread_u32(ctx, spread_chip, &e)?; + d = c; + // d_spread = c_spread; + c = b; + c_spread = b_spread; + b = a; + b_spread = a_spread; + a = { + let add = gate.add(ctx, QuantumCell::Existing(t1), QuantumCell::Existing(t2)); + mod_u32(ctx, range, &add) + }; + a_spread = state_to_spread_u32(ctx, spread_chip, &a)?; + } + let new_states = [a, b, c, d, e, f, g, h]; + let next_state_words = new_states + .iter() + .copied() + .zip(pre_state_words.iter().copied()) + .map(|(x, y)| { + let add = gate.add(ctx, QuantumCell::Existing(x), QuantumCell::Existing(y)); + // println!( + // "pre {:?} new {:?} add {:?}", + // y.value(), + // x.value(), + // add.value() + // ); + mod_u32(ctx, range, &add) + }) + .collect_vec(); + Ok(next_state_words) +} + +fn state_to_spread_u32<'a, F: BigPrimeField>( + ctx: &mut Context, + spread_chip: &SpreadChip<'a, F>, + x: &AssignedValue, +) -> Result, Error> { + let gate = spread_chip.range().gate(); + let lo = F::from((x.value().get_lower_32() & ((1 << 16) - 1)) as u64); + let hi = F::from((x.value().get_lower_32() >> 16) as u64); + let assigned_lo = ctx.load_witness(lo); + let assigned_hi = ctx.load_witness(hi); + let composed = gate.mul_add( + ctx, + QuantumCell::Existing(assigned_hi), + QuantumCell::Constant(F::from(1u64 << 16)), + QuantumCell::Existing(assigned_lo), + ); + ctx.constrain_equal(x, &composed); + let lo_spread = spread_chip.spread(ctx, &assigned_lo)?; + let hi_spread = spread_chip.spread(ctx, &assigned_hi)?; + Ok((lo_spread, hi_spread)) +} + +fn mod_u32<'a, 'b: 'a, F: BigPrimeField>( + ctx: &mut Context, + range: &impl RangeInstructions, + x: &AssignedValue, +) -> AssignedValue { + let gate = range.gate(); + let lo = F::from(x.value().get_lower_32() as u64); + let hi = F::from((x.value().get_lower_64() >> 32) & ((1u64 << 32) - 1)); + let assigned_lo = ctx.load_witness(lo); + let assigned_hi = ctx.load_witness(hi); + range.range_check(ctx, assigned_lo, 32); + let composed = + gate.mul_add(ctx, assigned_hi, QuantumCell::Constant(F::from(1u64 << 32)), assigned_lo); + ctx.constrain_equal(x, &composed); + assigned_lo +} + +fn ch<'a, 'b: 'a, F: BigPrimeField>( + ctx: &mut Context, + spread_chip: &SpreadChip<'a, F>, + x: &SpreadU32<'a, F>, + y: &SpreadU32<'a, F>, + z: &SpreadU32<'a, F>, +) -> Result, Error> { + let (x_lo, x_hi) = *x; + let (y_lo, y_hi) = *y; + let (z_lo, z_hi) = *z; + let range = spread_chip.range(); + let gate = range.gate(); + let p_lo = gate.add(ctx, QuantumCell::Existing(x_lo), QuantumCell::Existing(y_lo)); + let p_hi = gate.add(ctx, QuantumCell::Existing(x_hi), QuantumCell::Existing(y_hi)); + const MASK_EVEN_32: u64 = 0x55555555; + let x_neg_lo = gate.neg(ctx, QuantumCell::Existing(x_lo)); + let x_neg_hi = gate.neg(ctx, QuantumCell::Existing(x_hi)); + let q_lo = three_add( + ctx, + gate, + QuantumCell::Constant(F::from(MASK_EVEN_32)), + QuantumCell::Existing(x_neg_lo), + QuantumCell::Existing(z_lo), + ); + let q_hi = three_add( + ctx, + gate, + QuantumCell::Constant(F::from(MASK_EVEN_32)), + QuantumCell::Existing(x_neg_hi), + QuantumCell::Existing(z_hi), + ); + let (p_lo_even, p_lo_odd) = spread_chip.decompose_even_and_odd_unchecked(ctx, &p_lo)?; + let (p_hi_even, p_hi_odd) = spread_chip.decompose_even_and_odd_unchecked(ctx, &p_hi)?; + let (q_lo_even, q_lo_odd) = spread_chip.decompose_even_and_odd_unchecked(ctx, &q_lo)?; + let (q_hi_even, q_hi_odd) = spread_chip.decompose_even_and_odd_unchecked(ctx, &q_hi)?; + { + let even_spread = spread_chip.spread(ctx, &p_lo_even)?; + let odd_spread = spread_chip.spread(ctx, &p_lo_odd)?; + let sum = gate.mul_add( + ctx, + QuantumCell::Constant(F::from(2)), + QuantumCell::Existing(odd_spread), + QuantumCell::Existing(even_spread), + ); + ctx.constrain_equal(&sum, &p_lo); + } + { + let even_spread = spread_chip.spread(ctx, &p_hi_even)?; + let odd_spread = spread_chip.spread(ctx, &p_hi_odd)?; + let sum = gate.mul_add( + ctx, + QuantumCell::Constant(F::from(2)), + QuantumCell::Existing(odd_spread), + QuantumCell::Existing(even_spread), + ); + ctx.constrain_equal(&sum, &p_hi); + } + { + let even_spread = spread_chip.spread(ctx, &q_lo_even)?; + let odd_spread = spread_chip.spread(ctx, &q_lo_odd)?; + let sum = gate.mul_add( + ctx, + QuantumCell::Constant(F::from(2)), + QuantumCell::Existing(odd_spread), + QuantumCell::Existing(even_spread), + ); + ctx.constrain_equal(&sum, &q_lo); + } + { + let even_spread = spread_chip.spread(ctx, &q_hi_even)?; + let odd_spread = spread_chip.spread(ctx, &q_hi_odd)?; + let sum = gate.mul_add( + ctx, + QuantumCell::Constant(F::from(2)), + QuantumCell::Existing(odd_spread), + QuantumCell::Existing(even_spread), + ); + ctx.constrain_equal(&sum, &q_hi); + } + let out_lo = gate.add(ctx, QuantumCell::Existing(p_lo_odd), QuantumCell::Existing(q_lo_odd)); + let out_hi = gate.add(ctx, QuantumCell::Existing(p_hi_odd), QuantumCell::Existing(q_hi_odd)); + let out = gate.mul_add( + ctx, + QuantumCell::Existing(out_hi), + QuantumCell::Constant(F::from(1u64 << 16)), + QuantumCell::Existing(out_lo), + ); + Ok(out) +} + +fn maj<'a, 'b: 'a, F: BigPrimeField>( + ctx: &mut Context, + spread_chip: &SpreadChip<'a, F>, + x: &SpreadU32<'a, F>, + y: &SpreadU32<'a, F>, + z: &SpreadU32<'a, F>, +) -> Result, Error> { + let (x_lo, x_hi) = *x; + let (y_lo, y_hi) = *y; + let (z_lo, z_hi) = *z; + let range = spread_chip.range(); + let gate = range.gate(); + let m_lo = three_add( + ctx, + range.gate(), + QuantumCell::Existing(x_lo), + QuantumCell::Existing(y_lo), + QuantumCell::Existing(z_lo), + ); + let m_hi = three_add( + ctx, + range.gate(), + QuantumCell::Existing(x_hi), + QuantumCell::Existing(y_hi), + QuantumCell::Existing(z_hi), + ); + let (m_lo_even, m_lo_odd) = spread_chip.decompose_even_and_odd_unchecked(ctx, &m_lo)?; + let (m_hi_even, m_hi_odd) = spread_chip.decompose_even_and_odd_unchecked(ctx, &m_hi)?; + { + let even_spread = spread_chip.spread(ctx, &m_lo_even)?; + let odd_spread = spread_chip.spread(ctx, &m_lo_odd)?; + let sum = gate.mul_add( + ctx, + QuantumCell::Constant(F::from(2)), + QuantumCell::Existing(odd_spread), + QuantumCell::Existing(even_spread), + ); + ctx.constrain_equal(&sum, &m_lo); + } + { + let even_spread = spread_chip.spread(ctx, &m_hi_even)?; + let odd_spread = spread_chip.spread(ctx, &m_hi_odd)?; + let sum = gate.mul_add( + ctx, + QuantumCell::Constant(F::from(2)), + QuantumCell::Existing(odd_spread), + QuantumCell::Existing(even_spread), + ); + ctx.constrain_equal(&sum, &m_hi); + } + let m = gate.mul_add( + ctx, + QuantumCell::Existing(m_hi_odd), + QuantumCell::Constant(F::from(1u64 << 16)), + QuantumCell::Existing(m_lo_odd), + ); + Ok(m) +} + +fn three_add<'a, 'b: 'a, F: BigPrimeField>( + ctx: &mut Context, + gate: &impl GateInstructions, + x: QuantumCell, + y: QuantumCell, + z: QuantumCell, +) -> AssignedValue { + let add1 = gate.add(ctx, x, y); + gate.add(ctx, QuantumCell::Existing(add1), z) +} + +fn sigma_upper0<'a, 'b: 'a, F: BigPrimeField>( + ctx: &mut Context, + spread_chip: &SpreadChip<'a, F>, + x_spread: &SpreadU32, +) -> Result, Error> { + const STARTS: [usize; 4] = [0, 2, 13, 22]; + const ENDS: [usize; 4] = [2, 13, 22, 32]; + const PADDINGS: [usize; 4] = [6, 5, 7, 6]; + let coeffs = [ + F::from((1u64 << 60) + (1u64 << 38) + (1u64 << 20)), + F::from((1u64 << 0) + (1u64 << 42) + (1u64 << 24)), + F::from((1u64 << 22) + (1u64 << 0) + (1u64 << 46)), + F::from((1u64 << 40) + (1u64 << 18) + (1u64 << 0)), + ]; + sigma_generic(ctx, spread_chip, x_spread, &STARTS, &ENDS, &PADDINGS, &coeffs) +} + +fn sigma_upper1<'a, 'b: 'a, F: BigPrimeField>( + ctx: &mut Context, + spread_chip: &SpreadChip<'a, F>, + x_spread: &SpreadU32, +) -> Result, Error> { + const STARTS: [usize; 4] = [0, 6, 11, 25]; + const ENDS: [usize; 4] = [6, 11, 25, 32]; + const PADDINGS: [usize; 4] = [2, 3, 2, 1]; + let coeffs = [ + F::from((1u64 << 52) + (1u64 << 42) + (1u64 << 14)), + F::from((1u64 << 0) + (1u64 << 54) + (1u64 << 26)), + F::from((1u64 << 10) + (1u64 << 0) + (1u64 << 36)), + F::from((1u64 << 38) + (1u64 << 28) + (1u64 << 0)), + ]; + sigma_generic(ctx, spread_chip, x_spread, &STARTS, &ENDS, &PADDINGS, &coeffs) +} + +fn sigma_lower0<'a, 'b: 'a, F: BigPrimeField>( + ctx: &mut Context, + spread_chip: &SpreadChip<'a, F>, + x_spread: &SpreadU32, +) -> Result, Error> { + const STARTS: [usize; 4] = [0, 3, 7, 18]; + const ENDS: [usize; 4] = [3, 7, 18, 32]; + const PADDINGS: [usize; 4] = [5, 4, 5, 2]; + let coeffs = [ + F::from((1u64 << 50) + (1u64 << 28)), + F::from((1u64 << 0) + (1u64 << 56) + (1u64 << 34)), + F::from((1u64 << 8) + (1u64 << 0) + (1u64 << 42)), + F::from((1u64 << 30) + (1u64 << 22) + (1u64 << 0)), + ]; + sigma_generic(ctx, spread_chip, x_spread, &STARTS, &ENDS, &PADDINGS, &coeffs) +} + +fn sigma_lower1<'a, 'b: 'a, F: BigPrimeField>( + ctx: &mut Context, + spread_chip: &SpreadChip<'a, F>, + x_spread: &SpreadU32, +) -> Result, Error> { + const STARTS: [usize; 4] = [0, 10, 17, 19]; + const ENDS: [usize; 4] = [10, 17, 19, 32]; + const PADDINGS: [usize; 4] = [6, 1, 6, 3]; + let coeffs = [ + F::from((1u64 << 30) + (1u64 << 26)), + F::from((1u64 << 0) + (1u64 << 50) + (1u64 << 46)), + F::from((1u64 << 14) + (1u64 << 0) + (1u64 << 60)), + F::from((1u64 << 18) + (1u64 << 4) + (1u64 << 0)), + ]; + sigma_generic(ctx, spread_chip, x_spread, &STARTS, &ENDS, &PADDINGS, &coeffs) +} + +#[allow(clippy::too_many_arguments)] +fn sigma_generic<'a, 'b: 'a, F: BigPrimeField>( + ctx: &mut Context, + spread_chip: &SpreadChip<'a, F>, + x_spread: &SpreadU32, + starts: &[usize; 4], + ends: &[usize; 4], + paddings: &[usize; 4], + coeffs: &[F; 4], +) -> Result, Error> { + let range = spread_chip.range(); + let gate = range.gate(); + // let x_spread = spread_config.spread(ctx, range, x)?; + let bits_val = { + let (lo, hi) = (x_spread.0.value(), x_spread.1.value()); + let mut bits = fe_to_bits_le(lo, 32); + bits.append(&mut fe_to_bits_le(hi, 32)); + bits + }; + let mut assign_bits = |bits: &Vec, start: usize, end: usize, _padding: usize| { + let fe_val: F = { + let mut bits = bits[2 * start..2 * end].to_vec(); + bits.extend_from_slice(&vec![false; 64 - bits.len()]); + bits_le_to_fe(&bits) + }; + + // let assigned_spread = spread_config.spread(ctx, range, &assigned_dense)?; + // let result: Result, Error> = Ok(assigned_spread); + ctx.load_witness(fe_val) + }; + let assigned_a = assign_bits(&bits_val, starts[0], ends[0], paddings[0]); + let assigned_b = assign_bits(&bits_val, starts[1], ends[1], paddings[1]); + let assigned_c = assign_bits(&bits_val, starts[2], ends[2], paddings[2]); + let assigned_d = assign_bits(&bits_val, starts[3], ends[3], paddings[3]); + { + let mut sum = assigned_a; + sum = gate.mul_add( + ctx, + assigned_b, + QuantumCell::Constant(F::from(1 << (2 * starts[1]))), + sum, + ); + sum = gate.mul_add( + ctx, + assigned_c, + QuantumCell::Constant(F::from(1 << (2 * starts[2]))), + sum, + ); + sum = gate.mul_add( + ctx, + assigned_d, + QuantumCell::Constant(F::from(1 << (2 * starts[3]))), + sum, + ); + let x_composed = + gate.mul_add(ctx, x_spread.1, QuantumCell::Constant(F::from(1 << 32)), x_spread.0); + ctx.constrain_equal(&x_composed, &sum); + } + + let r_spread = { + // let a_coeff = F::from(1u64 << 60 + 1u64 << 38 + 1u64 << 20); + // let b_coeff = F::from(1u64 << 0 + 1u64 << 42 + 1u64 << 24); + // let c_coeff = F::from(1u64 << 22 + 1u64 << 0 + 1u64 << 46); + // let d_coeff = F::from(1u64 << 40 + 1u64 << 18 + 1u64 << 0); + let mut sum = ctx.load_zero(); + // let assigned_a_spread = spread_config.spread(ctx, range, &assigned_a)?; + // 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(ctx, QuantumCell::Constant(coeffs[0]), assigned_a, sum); + sum = gate.mul_add(ctx, QuantumCell::Constant(coeffs[1]), assigned_b, sum); + sum = gate.mul_add(ctx, QuantumCell::Constant(coeffs[2]), assigned_c, sum); + sum = gate.mul_add(ctx, QuantumCell::Constant(coeffs[3]), assigned_d, sum); + sum + }; + let (r_lo, r_hi) = { + let lo = F::from(r_spread.value().get_lower_32() as u64); + let hi = F::from(((r_spread.value().get_lower_64() >> 32) & ((1u64 << 32) - 1)) as u64); + let assigned_lo = ctx.load_witness(lo); + let assigned_hi = ctx.load_witness(hi); + range.range_check(ctx, assigned_lo, 32); + range.range_check(ctx, assigned_hi, 32); + let composed = gate.mul_add( + ctx, + QuantumCell::Existing(assigned_hi), + QuantumCell::Constant(F::from(1u64 << 32)), + QuantumCell::Existing(assigned_lo), + ); + ctx.constrain_equal(&r_spread, &composed); + (assigned_lo, assigned_hi) + }; + + let (r_lo_even, r_lo_odd) = spread_chip.decompose_even_and_odd_unchecked(ctx, &r_lo)?; + let (r_hi_even, r_hi_odd) = spread_chip.decompose_even_and_odd_unchecked(ctx, &r_hi)?; + + { + let even_spread = spread_chip.spread(ctx, &r_lo_even)?; + let odd_spread = spread_chip.spread(ctx, &r_lo_odd)?; + let sum = gate.mul_add( + ctx, + QuantumCell::Constant(F::from(2)), + QuantumCell::Existing(odd_spread), + QuantumCell::Existing(even_spread), + ); + ctx.constrain_equal(&sum, &r_lo); + } + + { + let even_spread = spread_chip.spread(ctx, &r_hi_even)?; + let odd_spread = spread_chip.spread(ctx, &r_hi_odd)?; + let sum = gate.mul_add( + ctx, + QuantumCell::Constant(F::from(2)), + QuantumCell::Existing(odd_spread), + QuantumCell::Existing(even_spread), + ); + ctx.constrain_equal(&sum, &r_hi); + } + + let r = gate.mul_add( + ctx, + QuantumCell::Existing(r_hi_even), + QuantumCell::Constant(F::from(1 << 16)), + QuantumCell::Existing(r_lo_even), + ); + + Ok(r) +} diff --git a/halo2-ecc/src/secp256k1/sha256/mod.rs b/halo2-ecc/src/secp256k1/sha256/mod.rs new file mode 100644 index 00000000..70a4407b --- /dev/null +++ b/halo2-ecc/src/secp256k1/sha256/mod.rs @@ -0,0 +1,221 @@ +// ! This file is a modified version of the original file from https://github.com/ChainSafe/Spectre (sha256-flex) (GPL-3.0 license) + +use halo2_base::{ + gates::{GateInstructions, RangeChip, RangeInstructions}, + halo2_proofs::plonk::Error, + utils::BigPrimeField, + AssignedValue, Context, QuantumCell, +}; +use itertools::Itertools; + +use self::compression::{sha256_compression, INIT_STATE}; + +use self::spread::SpreadChip; + +mod compression; +mod spread; + +#[derive(Debug, Clone)] +pub struct Sha256Chip<'a, F: BigPrimeField> { + spread: SpreadChip<'a, F>, +} + +impl<'a, F: BigPrimeField> Sha256Chip<'a, F> { + const BLOCK_SIZE: usize = 64; + const DIGEST_SIZE: usize = 32; + + pub fn new(range: &'a RangeChip) -> Self { + // Spread chip requires 16 % lookup_bits == 0 so we set it to either 8 or 16 based on circuit degree. + let lookup_bits = if range.lookup_bits() > 8 { 16 } else { 8 }; + + Self { spread: SpreadChip::new(range, lookup_bits) } + } + + fn digest_varlen( + &self, + ctx: &mut Context, + input: impl IntoIterator>, + max_len: usize, + ) -> Result>, Error> { + let max_processed_bytes = { + let mut max_bytes = max_len + 9; + let remainder = max_bytes % 64; + if remainder != 0 { + max_bytes += 64 - remainder; + } + max_bytes + }; + + let mut assigned_input_bytes = input + .into_iter() + .map(|cell| match cell { + QuantumCell::Existing(v) => v, + QuantumCell::Witness(v) => ctx.load_witness(v), + QuantumCell::Constant(v) => ctx.load_constant(v), + _ => unreachable!(), + }) + .collect_vec(); + + let input_byte_size = assigned_input_bytes.len(); + let input_byte_size_with_9 = input_byte_size + 9; + let range = self.spread.range(); + let gate = &range.gate; + + assert!(input_byte_size <= max_len); + + let one_round_size = Self::BLOCK_SIZE; + + let num_round = if input_byte_size_with_9 % one_round_size == 0 { + input_byte_size_with_9 / one_round_size + } else { + input_byte_size_with_9 / one_round_size + 1 + }; + let padded_size = one_round_size * num_round; + let zero_padding_byte_size = padded_size - input_byte_size_with_9; + + let mut assign_byte = |byte: u8| ctx.load_witness(F::from(byte as u64)); + + assigned_input_bytes.push(assign_byte(0x80)); + + for _ in 0..zero_padding_byte_size { + assigned_input_bytes.push(assign_byte(0u8)); + } + + let mut input_len_bytes = [0; 8]; + let le_size_bytes = (8 * input_byte_size).to_le_bytes(); + input_len_bytes[0..le_size_bytes.len()].copy_from_slice(&le_size_bytes); + for byte in input_len_bytes.iter().rev() { + assigned_input_bytes.push(assign_byte(*byte)); + } + + assert_eq!(assigned_input_bytes.len(), num_round * one_round_size); + + let assigned_num_round = ctx.load_witness(F::from(num_round as u64)); + + // compute an initial state from the precomputed_input. + let last_state = INIT_STATE; + + let mut assigned_last_state_vec = vec![last_state + .iter() + .map(|state| ctx.load_constant(F::from(*state as u64))) + .collect_vec()]; + + let mut num_processed_input = 0; + while num_processed_input < max_processed_bytes { + let assigned_input_word_at_round = + &assigned_input_bytes[num_processed_input..num_processed_input + one_round_size]; + let new_assigned_hs_out = sha256_compression( + ctx, + &self.spread, + assigned_input_word_at_round, + assigned_last_state_vec.last().unwrap(), + )?; + + assigned_last_state_vec.push(new_assigned_hs_out); + num_processed_input += one_round_size; + } + + let zero = ctx.load_zero(); + let mut output_h_out = vec![zero; 8]; + for (n_round, assigned_state) in assigned_last_state_vec.into_iter().enumerate() { + let selector = gate.is_equal( + ctx, + QuantumCell::Constant(F::from(n_round as u64)), + assigned_num_round, + ); + for i in 0..8 { + output_h_out[i] = gate.select(ctx, assigned_state[i], output_h_out[i], selector); + } + } + let output_digest_bytes = output_h_out + .into_iter() + .flat_map(|assigned_word| { + let be_bytes = assigned_word.value().get_lower_32().to_be_bytes().to_vec(); + let assigned_bytes = (0..4) + .map(|idx| { + let assigned = ctx.load_witness(F::from(be_bytes[idx] as u64)); + range.range_check(ctx, assigned, 8); + assigned + }) + .collect_vec(); + let mut sum = ctx.load_zero(); + for (idx, assigned_byte) in assigned_bytes.iter().copied().enumerate() { + sum = gate.mul_add( + ctx, + assigned_byte, + QuantumCell::Constant(F::from(1u64 << (24 - 8 * idx))), + sum, + ); + } + ctx.constrain_equal(&assigned_word, &sum); + assigned_bytes + }) + .collect_vec(); + + Ok(output_digest_bytes) + } + + pub fn digest( + &self, + ctx: &mut Context, + input: impl IntoIterator>, + ) -> Result>, Error> { + let input = input.into_iter().collect_vec(); + let input_len = input.len(); + self.digest_varlen(ctx, input, input_len) + } + + pub fn digest_le( + &self, + ctx: &mut Context, + input: impl IntoIterator>, + ) -> Result>, Error> { + let mut digest = self.digest(ctx, input).unwrap(); + digest.reverse(); + Ok(digest) + } +} + +#[cfg(test)] +mod test { + use halo2_base::{ + gates::RangeInstructions, halo2_proofs::halo2curves::grumpkin::Fq as Fr, + utils::testing::base_test, QuantumCell, + }; + use itertools::Itertools; + use sha2::{Digest, Sha256}; + + use super::Sha256Chip; + + #[test] + fn test_sha256() { + let preimage = b"hello world"; + + let mut hasher = Sha256::new(); + hasher.update(preimage); + let result = hasher.finalize(); + + base_test().k(14).lookup_bits(13).expect_satisfied(true).run(|ctx, range| { + let preimage_assigned = preimage + .iter() + .map(|byte| QuantumCell::Existing(ctx.load_witness(Fr::from(*byte as u64)))) + .collect_vec(); + + let result_assinged = result + .iter() + .map(|byte| { + let assigned = ctx.load_witness(Fr::from(*byte as u64)); + range.range_check(ctx, assigned, 8); + assigned + }) + .collect_vec(); + + let sha256_chip = Sha256Chip::new(range); + let digest = sha256_chip.digest(ctx, preimage_assigned).unwrap(); + + for (assigned, expected) in digest.iter().zip(result_assinged.iter()) { + ctx.constrain_equal(assigned, expected); + } + }) + } +} diff --git a/halo2-ecc/src/secp256k1/sha256/spread.rs b/halo2-ecc/src/secp256k1/sha256/spread.rs new file mode 100644 index 00000000..50eb824e --- /dev/null +++ b/halo2-ecc/src/secp256k1/sha256/spread.rs @@ -0,0 +1,101 @@ +use halo2_base::{ + gates::{GateInstructions, RangeChip, RangeInstructions}, + halo2_proofs::plonk::Error, + utils::{decompose, BigPrimeField}, + AssignedValue, Context, QuantumCell, +}; +use itertools::Itertools; + +use crate::secp256k1::util::{bits_le_to_fe, bits_le_to_fe_assigned, fe_to_bits_le}; + +#[derive(Debug, Clone)] +pub struct SpreadChip<'a, F: BigPrimeField> { + lookup_bits: usize, + range: &'a RangeChip, +} + +impl<'a, F: BigPrimeField> SpreadChip<'a, F> { + pub fn new(range: &'a RangeChip, lookup_bits: usize) -> Self { + debug_assert_eq!(16 % lookup_bits, 0); + + Self { range, lookup_bits } + } + pub fn spread( + &self, + ctx: &mut Context, + dense: &AssignedValue, + ) -> Result, Error> { + let gate = self.range.gate(); + let limb_bits = self.lookup_bits; + let num_limbs = 16 / limb_bits; + let limbs = decompose(dense.value(), num_limbs, limb_bits); + let assigned_limbs = ctx.assign_witnesses(limbs); + { + let mut limbs_sum = ctx.load_zero(); + for (idx, limb) in assigned_limbs.iter().copied().enumerate() { + limbs_sum = gate.mul_add( + ctx, + QuantumCell::Existing(limb), + QuantumCell::Constant(F::from(1 << (limb_bits * idx))), + QuantumCell::Existing(limbs_sum), + ); + } + ctx.constrain_equal(&limbs_sum, dense); + } + let mut assigned_spread = ctx.load_zero(); + for (idx, limb) in assigned_limbs.iter().enumerate() { + let spread_limb = self.spread_limb(ctx, limb)?; + assigned_spread = gate.mul_add( + ctx, + QuantumCell::Existing(spread_limb), + QuantumCell::Constant(F::from(1 << (2 * limb_bits * idx))), + QuantumCell::Existing(assigned_spread), + ); + } + Ok(assigned_spread) + } + + pub fn decompose_even_and_odd_unchecked( + &self, + ctx: &mut Context, + spread: &AssignedValue, + ) -> Result<(AssignedValue, AssignedValue), Error> { + let bits = fe_to_bits_le(spread.value(), 32); + let even_bits = (0..bits.len() / 2).map(|idx| bits[2 * idx]).collect_vec(); + let odd_bits = (0..bits.len() / 2).map(|idx| bits[2 * idx + 1]).collect_vec(); + let (even_val, odd_val) = (bits_le_to_fe(&even_bits), bits_le_to_fe(&odd_bits)); + let even_assigned = ctx.load_witness(even_val); + let odd_assigned = ctx.load_witness(odd_val); + self.range.range_check(ctx, even_assigned, 16); + self.range.range_check(ctx, odd_assigned, 16); + Ok((even_assigned, odd_assigned)) + } + + fn spread_limb( + &self, + ctx: &mut Context, + limb: &AssignedValue, + ) -> Result, Error> { + let range = self.range; + + let limb_bits = fe_to_bits_le(limb.value(), 32); + let assigned_limb_bits = + limb_bits.iter().map(|bit| ctx.load_constant(F::from(*bit))).collect_vec(); + + let limb_sum = bits_le_to_fe_assigned(ctx, range, &assigned_limb_bits)?; + ctx.constrain_equal(&limb_sum, limb); + + let mut assigned_spread_bits = vec![ctx.load_zero(); limb_bits.len() * 2]; + for i in 0..assigned_limb_bits.len() { + assigned_spread_bits[2 * i] = assigned_limb_bits[i]; + } + + let assigned_spread = bits_le_to_fe_assigned(ctx, range, &assigned_spread_bits)?; + + Ok(assigned_spread) + } + + pub fn range(&self) -> &RangeChip { + self.range + } +} diff --git a/halo2-ecc/src/secp256k1/tests/ecdsa_tests.rs b/halo2-ecc/src/secp256k1/tests/ecdsa_tests.rs index d3d47da7..c4002722 100644 --- a/halo2-ecc/src/secp256k1/tests/ecdsa_tests.rs +++ b/halo2-ecc/src/secp256k1/tests/ecdsa_tests.rs @@ -28,14 +28,18 @@ fn custom_parameters_ecdsa(sk: u64, msg_hash: u64, k: u64) -> ECDSAInput { } #[test] -#[should_panic(expected = "assertion failed: `(left == right)`")] +#[should_panic( + expected = "assertion `left == right` failed\n left: 0x0000000000000000000000000000000000000000000000000000000000000000\n right: 0x0000000000000000000000000000000000000000000000000000000000000001" +)] fn test_ecdsa_msg_hash_zero() { let input = custom_parameters_ecdsa(random::(), 0, random::()); run_test(input); } #[test] -#[should_panic(expected = "assertion failed: `(left == right)`")] +#[should_panic( + expected = "assertion `left == right` failed\n left: 0x0000000000000000000000000000000000000000000000000000000000000000\n right: 0x0000000000000000000000000000000000000000000000000000000000000001" +)] fn test_ecdsa_private_key_zero() { let input = custom_parameters_ecdsa(0, random::(), random::()); run_test(input); diff --git a/halo2-ecc/src/secp256k1/util.rs b/halo2-ecc/src/secp256k1/util.rs new file mode 100644 index 00000000..b3ead1d5 --- /dev/null +++ b/halo2-ecc/src/secp256k1/util.rs @@ -0,0 +1,51 @@ +use halo2_base::{ + gates::{GateInstructions, RangeChip, RangeInstructions}, + halo2_proofs::plonk::Error, + utils::{biguint_to_fe, fe_to_biguint, BigPrimeField}, + AssignedValue, Context, QuantumCell, +}; +use itertools::Itertools; +use num_bigint::BigUint; + +pub fn fe_to_bits_le(val: &F, size: usize) -> Vec { + let val_bytes = fe_to_biguint(val).to_bytes_le(); + let mut bits = + val_bytes.iter().flat_map(|byte| (0..8).map(move |i| ((byte >> i) & 1) == 1)).collect_vec(); + bits.extend_from_slice(&vec![false; size - bits.len()]); + bits +} + +pub fn bits_le_to_fe(bits: &[bool]) -> F { + let bytes = bits + .chunks(8) + .map(|bits| { + let mut byte = 0u8; + for idx in 0..8 { + if bits[idx] { + byte += 1 << idx; + } + } + byte + }) + .collect_vec(); + biguint_to_fe(&BigUint::from_bytes_le(&bytes)) +} + +pub fn bits_le_to_fe_assigned( + ctx: &mut Context, + range: &RangeChip, + bits: &[AssignedValue], +) -> Result, Error> { + let gate = range.gate(); + let mut sum = ctx.load_zero(); + for (idx, bit) in bits.iter().enumerate() { + gate.assert_bit(ctx, *bit); + sum = gate.mul_add( + ctx, + QuantumCell::Existing(*bit), + QuantumCell::Constant(F::from(1 << idx)), + QuantumCell::Existing(sum), + ); + } + Ok(sum) +} diff --git a/hashes/zkevm/Cargo.toml b/hashes/zkevm/Cargo.toml index c4d5425e..22945267 100644 --- a/hashes/zkevm/Cargo.toml +++ b/hashes/zkevm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zkevm-hashes" -version = "0.2.1" +version = "0.2.2" authors = ["Privacy Scaling Explorations Team", "Taiko Labs", "Intrinsic Technologies"] license = "MIT OR Apache-2.0" edition = "2021" @@ -23,7 +23,7 @@ rayon = "1.8" sha3 = "0.10.8" # always included but without features to use Native poseidon and get CircuitExt trait # snark-verifier-sdk = { version = "=0.1.7", default-features = false } -snark-verifier-sdk = { version = "=0.1.7", git = "https://github.com/axiom-crypto/snark-verifier.git", tag = "v0.1.7-git", default-features = false } +snark-verifier-sdk = { git = "https://github.com/axiom-crypto/snark-verifier.git", branch = "community-edition", default-features = false } getset = "0.1.2" type-map = "0.5.0" diff --git a/hashes/zkevm/src/keccak/README.md b/hashes/zkevm/src/keccak/README.md index 527d671f..89c838a9 100644 --- a/hashes/zkevm/src/keccak/README.md +++ b/hashes/zkevm/src/keccak/README.md @@ -4,13 +4,13 @@ Keccak circuit in vanilla halo2. This implementation starts from [PSE version](https://github.com/privacy-scaling-explorations/zkevm-circuits/tree/main/zkevm-circuits/src/keccak_circuit), then adopts some changes from [this PR](https://github.com/scroll-tech/zkevm-circuits/pull/216) and later updates in PSE version. -The major differences is that this version directly represent raw inputs and Keccak results as witnesses, while the original version only has RLCs(random linear combination) of raw inputs and Keccak results. Because this version doesn't need RLCs, it doesn't have the 2nd phase or use challenge APIs. +The major difference is that this version directly represent raw inputs and Keccak results as witnesses, while the original version only has RLCs(random linear combination) of raw inputs and Keccak results. Because this version doesn't need RLCs, it doesn't have the 2nd phase or use challenge APIs. ### Logical Input/Output Logically the circuit takes an array of bytes as inputs and Keccak results of these bytes as outputs. -`keccak::vanilla::witness::multi_keccak` generates the witnesses of the ciruit for a given input. +`keccak::vanilla::witness::multi_keccak` generates the witnesses of the circuit for a given input. ### Background Knowledge @@ -23,8 +23,8 @@ All these items remain consistent across all versions. - Every input is padded to be a multiple of RATE (136 bytes). If the length of the logical input already matches a multiple of RATE, an additional RATE bytes are added as padding. - Each `keccak_f` absorbs `RATE` bytes, which are splitted into `NUM_WORDS_TO_ABSORB`(17) words. Each word has `NUM_BYTES_PER_WORD`(8) bytes. - Each of the first `NUM_WORDS_TO_ABSORB`(17) rounds of each `keccak_f` absorbs a word. -- `is_final`(anothe name is `is_enabled`) is meaningful only at the first row of the "squeeze" round. It must be true if this is the last `keccak_f` of an logical input. -- The first round of the circuit is a dummy round, which doesn't crespond to any input. +- `is_final`(anothe name is `is_enabled`) is meaningful only at the first row of the "squeeze" round. It must be true if this is the last `keccak_f` of a logical input. +- The first round of the circuit is a dummy round, which doesn't correspond to any input. ### Raw inputs @@ -73,7 +73,7 @@ Let's say `rows_per_round = 10` and `inputs = [[], [0x89, 0x88, .., 0x01]]`. The - Removed column `output_rlc` and related gates. - Removed challenges. - Refactored the folder structure to follow [Scroll's repo](https://github.com/scroll-tech/zkevm-circuits/tree/95f82762cfec46140d6866c34a420ee1fc1e27c7/zkevm-circuits/src/keccak_circuit). `mod.rs` and `witness.rs` could be found [here](https://github.com/scroll-tech/zkevm-circuits/blob/develop/zkevm-circuits/src/keccak_circuit.rs). `KeccakTable` could be found [here](https://github.com/scroll-tech/zkevm-circuits/blob/95f82762cfec46140d6866c34a420ee1fc1e27c7/zkevm-circuits/src/table.rs#L1308). -- Imported utilites from [PSE zkevm-circuits repo](https://github.com/privacy-scaling-explorations/zkevm-circuits/blob/588b8b8c55bf639fc5cbf7eae575da922ea7f1fd/zkevm-circuits/src/util/word.rs). +- Imported utilities from [PSE zkevm-circuits repo](https://github.com/privacy-scaling-explorations/zkevm-circuits/blob/588b8b8c55bf639fc5cbf7eae575da922ea7f1fd/zkevm-circuits/src/util/word.rs). ## Component diff --git a/hashes/zkevm/src/keccak/component/circuit/shard.rs b/hashes/zkevm/src/keccak/component/circuit/shard.rs index 604da477..e35d473b 100644 --- a/hashes/zkevm/src/keccak/component/circuit/shard.rs +++ b/hashes/zkevm/src/keccak/component/circuit/shard.rs @@ -71,7 +71,7 @@ pub struct KeccakComponentShardCircuitParams { // Number of unusable rows withhold by Halo2. #[getset(get_copy = "pub")] num_unusable_row: usize, - /// Max keccak_f this circuits can aceept. The circuit can at most process `capacity` of inputs + /// Max keccak_f this circuits can accept. The circuit can at most process `capacity` of inputs /// with < NUM_BYTES_TO_ABSORB bytes or an input with `capacity * NUM_BYTES_TO_ABSORB - 1` bytes. #[getset(get_copy = "pub")] capacity: usize, @@ -96,7 +96,7 @@ impl KeccakComponentShardCircuitParams { let max_rows = (1 << k) - num_unusable_row; // Derived from [crate::keccak::vanilla::keccak_packed_multi::get_keccak_capacity]. let rows_per_round = max_rows / (capacity * (NUM_ROUNDS + 1) + 1 + NUM_WORDS_TO_ABSORB); - assert!(rows_per_round > 0, "No enough rows for the speficied capacity"); + assert!(rows_per_round > 0, "No enough rows for the specified capacity"); let keccak_circuit_params = KeccakConfigParams { k: k as u32, rows_per_round }; let base_circuit_params = BaseCircuitParams { k, @@ -410,7 +410,7 @@ pub(crate) fn create_hasher() -> PoseidonHasher( ctx: &mut Context, gate: &impl GateInstructions, @@ -466,7 +466,7 @@ pub fn pack_inputs_from_keccak_fs( /// Encode raw inputs from Keccak circuit witnesses into lookup keys. /// -/// Each element in the return value corrresponds to a Keccak chunk. If is_final = true, this element is the lookup key of the corresponding logical input. +/// Each element in the return value corresponds to a Keccak chunk. If is_final = true, this element is the lookup key of the corresponding logical input. pub fn encode_inputs_from_keccak_fs( ctx: &mut Context, gate: &impl GateInstructions, diff --git a/hashes/zkevm/src/keccak/component/circuit/tests/shard.rs b/hashes/zkevm/src/keccak/component/circuit/tests/shard.rs index 17726327..c41f4d70 100644 --- a/hashes/zkevm/src/keccak/component/circuit/tests/shard.rs +++ b/hashes/zkevm/src/keccak/component/circuit/tests/shard.rs @@ -175,7 +175,7 @@ fn test_prove_shard_circuit_commit() { circuit.set_base_circuit_break_points(break_points); let circuit_outputs = multi_inputs_to_circuit_outputs::(&inputs, circuit_params.capacity()); - let instances = vec![vec![calculate_circuit_outputs_commit(&circuit_outputs)]]; + let instances = [[calculate_circuit_outputs_commit(&circuit_outputs)]]; let proof = gen_proof_with_instances( ¶ms, diff --git a/hashes/zkevm/src/keccak/component/output.rs b/hashes/zkevm/src/keccak/component/output.rs index 22688b5f..5787bdc4 100644 --- a/hashes/zkevm/src/keccak/component/output.rs +++ b/hashes/zkevm/src/keccak/component/output.rs @@ -19,7 +19,7 @@ pub struct KeccakCircuitOutput { pub hash_hi: E, } -/// Return circuit outputs of the specified Keccak corprocessor circuit for a specified input. +/// Return circuit outputs of the specified Keccak coprocessor circuit for a specified input. pub fn multi_inputs_to_circuit_outputs( inputs: &[Vec], capacity: usize, diff --git a/hashes/zkevm/src/keccak/vanilla/keccak_packed_multi.rs b/hashes/zkevm/src/keccak/vanilla/keccak_packed_multi.rs index 6a78efc9..d149e982 100644 --- a/hashes/zkevm/src/keccak/vanilla/keccak_packed_multi.rs +++ b/hashes/zkevm/src/keccak/vanilla/keccak_packed_multi.rs @@ -489,7 +489,7 @@ pub(crate) mod transform { } } -// Transfroms values to cells +// Transforms values to cells pub(crate) mod transform_to { use crate::{ halo2_proofs::plonk::{ConstraintSystem, TableColumn}, diff --git a/hashes/zkevm/src/keccak/vanilla/mod.rs b/hashes/zkevm/src/keccak/vanilla/mod.rs index c049ed89..8faa6d7b 100644 --- a/hashes/zkevm/src/keccak/vanilla/mod.rs +++ b/hashes/zkevm/src/keccak/vanilla/mod.rs @@ -266,7 +266,7 @@ impl KeccakCircuitConfig { // that allows reusing the same parts in an optimal way for the chi step. // We can save quite a few columns by not recombining the parts after rho/pi and // re-splitting the words again before chi. Instead we do chi directly - // on the output parts of rho/pi. For rho/pi specically we do + // on the output parts of rho/pi. For rho/pi specially we do // `s[j][2 * i + 3 * j) % 5] = normalize(rot(s[i][j], RHOM[i][j]))`. cell_manager.start_region(); let mut lookup_counter = 0; @@ -278,7 +278,7 @@ impl KeccakCircuitConfig { // multiple rows with lookups in a way that doesn't require any // extra additional cells or selectors we have to put all `s[i]`'s on the same // row. This isn't that strong of a requirement actually because we the - // words are split into multipe parts, and so only the parts at the same + // words are split into multiple parts, and so only the parts at the same // position of those words need to be on the same row. let target_word_sizes = target_part_sizes(part_size); let num_word_parts = target_word_sizes.len(); @@ -637,7 +637,7 @@ impl KeccakCircuitConfig { // Logically here we want !q_input[cur] && !start_new_hash(cur) ==> bytes_left[cur + num_rows_per_round] == bytes_left[cur] // In practice, in order to save a degree we use !(q_input[cur] ^ start_new_hash(cur)) ==> bytes_left[cur + num_rows_per_round] == bytes_left[cur] // When q_input[cur] is true, the above constraint q_input[cur] ==> bytes_left[cur + num_rows_per_round] + word_len == bytes_left[cur] has - // already been enabled. Even is_final in start_new_hash(cur) is true, it's just over-constrainted. + // already been enabled. Even is_final in start_new_hash(cur) is true, it's just over-constrained. // Note: At the first row of any round except the last round, is_final could be either true or false. cb.condition(not::expr(q(q_input, meta) + start_new_hash(meta, Rotation::cur())), |cb| { let bytes_left_next_expr = diff --git a/hashes/zkevm/src/sha256/README.md b/hashes/zkevm/src/sha256/README.md index 058cb371..23ed9fe3 100644 --- a/hashes/zkevm/src/sha256/README.md +++ b/hashes/zkevm/src/sha256/README.md @@ -4,14 +4,14 @@ SHA-256 circuit in vanilla halo2. This implementation is largely based on [Brechtpd](https://github.com/Brechtpd)'s [PR](https://github.com/privacy-scaling-explorations/zkevm-circuits/pull/756) to the PSE `zkevm-circuits`. His implementation of SHA-256 is in turn based on his implementation of Keccak using the "Bits" approach: one can read more about it [here](https://hackmd.io/NaTuIvmaQCybaOYgd-DG1Q?view#Bit-implementation). -The major differences is that this version directly represent raw inputs and SHA-256 digests as witnesses, while the original version only has RLCs (random linear combination) of raw inputs and outputs. Because this version doesn't need RLCs, it doesn't have the 2nd phase or use challenge APIs. +The major difference is that this version directly represent raw inputs and SHA-256 digests as witnesses, while the original version only has RLCs (random linear combination) of raw inputs and outputs. Because this version doesn't need RLCs, it doesn't have the 2nd phase or use challenge APIs. ### Logical Input/Output Logically the circuit takes a variable length array of variable length bytes as inputs and SHA-256 digests of these bytes as outputs. While these logical inputs are variable, what is fixed in a given circuit is max number of _total number of SHA-256 input blocks_ that can be processed (see below). We refer to this as the capacity of the circuit. -`sha256::vanilla::witness::generate_witnesses_multi_sha256` generates the witnesses of the ciruit for a given input. +`sha256::vanilla::witness::generate_witnesses_multi_sha256` generates the witnesses of the circuit for a given input. ### Background Knowledge diff --git a/hashes/zkevm/src/util/word.rs b/hashes/zkevm/src/util/word.rs index 9d91f5ee..e466eb7e 100644 --- a/hashes/zkevm/src/util/word.rs +++ b/hashes/zkevm/src/util/word.rs @@ -1,5 +1,5 @@ //! Define generic Word type with utility functions -// Naming Convesion +// Naming conversion // - Limbs: An EVM word is 256 bits **big-endian**. Limbs N means split 256 into N limb. For example, N = 4, each // limb is 256/4 = 64 bits diff --git a/rust-toolchain b/rust-toolchain index ee2d639b..36e57ce7 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2023-08-12 \ No newline at end of file +nightly-2024-02-08