Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Hash to Curve chip for BLS12-381 (G2) #179

Open
wants to merge 61 commits into
base: community-edition
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
12a68f3
add bls12 module
nulltea Sep 30, 2023
c23d264
make compile
nulltea Sep 30, 2023
14077b4
add tests; they fail :(
nulltea Sep 30, 2023
29f3e82
tests WIP
nulltea Oct 1, 2023
701f8f4
fix miller look args names
nulltea Oct 1, 2023
8355312
impl (single) miller_loop
nulltea Oct 1, 2023
4cd431a
cargo fix+fmt
nulltea Oct 1, 2023
54d0d3e
add bls signature chip
nulltea Oct 1, 2023
02fb814
add tests
nulltea Oct 2, 2023
4c107de
add comments
nulltea Oct 2, 2023
0ee4b0a
Merge branch 'feat/bls12-381-pairing' into feat/bls12-381-signatures
nulltea Oct 2, 2023
9c35ca7
add comments
nulltea Oct 2, 2023
405609b
add hash2curve chip & trait
nulltea Oct 6, 2023
a7ed71b
refactor
nulltea Oct 6, 2023
43a464a
add test
nulltea Oct 6, 2023
bd6d76e
expand message chip trait
nulltea Oct 7, 2023
7d067d0
move traits and general impls to ecc module
nulltea Oct 7, 2023
7e2866d
generalize output for hash trait
nulltea Oct 7, 2023
eeb652c
refactor
nulltea Oct 7, 2023
e432e88
complete test
nulltea Oct 7, 2023
9a7c941
abstract thread manager
nulltea Oct 7, 2023
2732d17
add comments
nulltea Oct 9, 2023
21c4e43
cargo fix + fmt
nulltea Oct 9, 2023
f689ab8
use separte halo2curves with `halo2-pse` feature
nulltea Oct 12, 2023
1a01328
use separte halo2curves with `halo2-pse` feature
nulltea Oct 12, 2023
c3718f5
use separte halo2curves with `halo2-pse` feature
nulltea Oct 12, 2023
765f52c
fix ambiguous use of halo2curves
nulltea Oct 12, 2023
def5b60
refactor naming
nulltea Nov 8, 2023
6d931a4
halo2_proofs -> halo2-axiom
nulltea Nov 24, 2023
1361ce5
halo2curves from crates.io
nulltea Nov 24, 2023
9eb229c
update hash interface
nulltea Nov 30, 2023
d7bbe5b
Merge remote-tracking branch 'upstream/community-edition' into feat/b…
nulltea Jan 5, 2024
d1d1b92
optimize final exp
nulltea Jan 5, 2024
9452a6c
Merge branch 'feat/bls12-381-pairing' into feat/bls12-381-signatures
nulltea Jan 5, 2024
90b7594
update test config
nulltea Dec 10, 2023
723962a
Merge branch 'feat/bls12-381-signatures' into feat/bls12-381-hash2curve
nulltea Jan 5, 2024
b371954
update test config
nulltea Jan 5, 2024
ae455d8
fix bigint for bls12-381
nulltea Feb 19, 2024
9ce6725
fix bigint for bls12-381
nulltea Feb 19, 2024
86f27b7
Merge branch 'feat/bls12-381-signatures' into feat/bls12-381-hash2curve
nulltea Feb 19, 2024
8151307
optimize `hash_to_field` & `mul_by_bls_x`
nulltea Dec 9, 2023
702dd96
clean comments
nulltea Feb 20, 2024
1b01da4
update test config
nulltea Feb 20, 2024
01654cf
Merge remote-tracking branch 'origin/community-edition' into feat/bls…
nulltea Feb 20, 2024
8b79482
fix build
nulltea Feb 20, 2024
5775309
Merge branch 'feat/bls12-381-pairing' into feat/bls12-381-signatures
nulltea Feb 20, 2024
ac6bdb3
Merge branch 'feat/bls12-381-signatures' into feat/bls12-381-hash2curve
nulltea Feb 20, 2024
dd8b0d2
fix for halo2-pse
nulltea Feb 20, 2024
39994bf
Merge branch 'feat/bls12-381-pairing' into feat/bls12-381-signatures
nulltea Feb 20, 2024
249ee16
Merge branch 'feat/bls12-381-signatures' into feat/bls12-381-hash2curve
nulltea Feb 20, 2024
23197fc
add `assign_sha256_rows`
nulltea Feb 20, 2024
0350dd7
fix ec_add test config
nulltea Apr 15, 2024
8f59988
Fix conversion from Bytes to BigInt (#4)
nulltea May 15, 2024
dbefb15
Fix unsafe division by zero (#5)
nulltea May 15, 2024
f40a58a
Fix under-constrained sqrt_ratio (#7)
nulltea May 15, 2024
4467ae2
Fix potentially under-constraint hash2curve msg (#8)
nulltea May 15, 2024
beaee9f
Fix missing add unequal check in map_to_curve (#9)
nulltea May 15, 2024
d992418
Add expand message length asserts (#10)
nulltea May 15, 2024
69ad067
Warn user about the sign of ratio root (#12)
nulltea May 15, 2024
0fd89e1
Revise comments & docs (#13)
nulltea May 15, 2024
b700180
Remove unnecessary clones (#15)
nulltea May 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ halo2-ecc = { path = "../halo2-lib/halo2-ecc" }
[patch.crates-io]
halo2-base = { path = "../halo2-lib/halo2-base" }
halo2-ecc = { path = "../halo2-lib/halo2-ecc" }
halo2curves-axiom = { git = "https://github.com/timoftime/halo2curves", branch = "support_bls12-381" }
28 changes: 23 additions & 5 deletions halo2-base/src/gates/flex_gate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,22 @@ pub trait GateInstructions<F: ScalarField> {
ctx.last().unwrap()
}

/// Constrains and returns `a ^ b`.
fn bitwise_xor<const BITS: usize>(
&self,
ctx: &mut Context<F>,
a: AssignedValue<F>,
b: AssignedValue<F>,
) -> AssignedValue<F> {
let a_bits = self.num_to_bits(ctx, a, BITS);
let b_bits = self.num_to_bits(ctx, b, BITS);

let xor_bits =
a_bits.into_iter().zip(b_bits).map(|(a, b)| self.xor(ctx, a, b)).collect_vec();

self.bits_to_num(ctx, &xor_bits)
}

/// Constrains and returns `!a` assumeing `a` is boolean.
///
/// Defines a vertical gate of form | 1 - a | a | 1 | 1 |, where 1 - a = out.
Expand Down Expand Up @@ -863,11 +879,13 @@ pub trait GateInstructions<F: ScalarField> {
range_bits: usize,
) -> Vec<AssignedValue<F>>;

/// Constrains and returns field representation of little-endian bit vector `bits`.
///
/// Assumes values of `bits` are boolean.
/// * `bits`: slice of [QuantumCell]'s that contains bit representation in little-endian form
fn bits_to_num(&self, ctx: &mut Context<F>, bits: &[AssignedValue<F>]) -> AssignedValue<F>;
/// Constrains and returns the number represented by the little-endian bit vector `bits`.
fn bits_to_num(&self, ctx: &mut Context<F>, bits: &[AssignedValue<F>]) -> AssignedValue<F> {
assert!(bits.len() <= F::NUM_BITS as usize);
bits.iter().rev().fold(ctx.load_zero(), |acc, bit| {
self.mul_add(ctx, acc, QuantumCell::Constant(F::from(2u64)), *bit)
})
}

/// Constrains and computes `a`<sup>`exp`</sup> where both `a, exp` are witnesses. The exponent is computed in the native field `F`.
///
Expand Down
36 changes: 36 additions & 0 deletions halo2-base/src/gates/flex_gate/threads/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,39 @@ pub mod single_phase;
pub use multi_phase::{GateStatistics, MultiPhaseCoreManager};
pub use parallelize::parallelize_core;
pub use single_phase::SinglePhaseCoreManager;

use crate::{utils::BigPrimeField, Context};

/// Abstracts basic context management for custom circuit builders.
pub trait CommonCircuitBuilder<F: BigPrimeField> {
/// Returns a mutable reference to the [Context] of a gate thread. Spawns a new thread for the given phase, if none exists.
fn main(&mut self) -> &mut Context<F>;

/// Returns the number of threads
fn thread_count(&self) -> usize;

/// Creates new context but does not append to `self.threads`
fn new_context(&self, context_id: usize) -> Context<F>;

/// Spawns a new thread for a new given `phase`. Returns a mutable reference to the [Context] of the new thread.
/// * `phase`: The phase (index) of the gate thread.
fn new_thread(&mut self) -> &mut Context<F>;
}

impl<F: BigPrimeField> CommonCircuitBuilder<F> for SinglePhaseCoreManager<F> {
fn main(&mut self) -> &mut Context<F> {
self.main()
}

fn thread_count(&self) -> usize {
self.thread_count()
}

fn new_context(&self, context_id: usize) -> Context<F> {
self.new_context(context_id)
}

fn new_thread(&mut self) -> &mut Context<F> {
self.new_thread()
}
}
77 changes: 66 additions & 11 deletions halo2-base/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,71 @@ pub trait BigPrimeField: ScalarField {
fn from_u64_digits(val: &[u64]) -> Self;
}
#[cfg(feature = "halo2-axiom")]
impl<F> BigPrimeField for F
where
F: ScalarField + From<[u64; 4]>, // Assume [u64; 4] is little-endian. We only implement ScalarField when this is true.
{
#[inline(always)]
fn from_u64_digits(val: &[u64]) -> Self {
debug_assert!(val.len() <= 4);
let mut raw = [0u64; 4];
raw[..val.len()].copy_from_slice(val);
Self::from(raw)
mod bn256 {
use crate::halo2_proofs::halo2curves::bn256::{Fq, Fr};

impl super::BigPrimeField for Fr {
#[inline(always)]
fn from_u64_digits(val: &[u64]) -> Self {
let mut raw = [0u64; 4];
raw[..val.len()].copy_from_slice(val);
Self::from(raw)
}
}

impl super::BigPrimeField for Fq {
#[inline(always)]
fn from_u64_digits(val: &[u64]) -> Self {
let mut raw = [0u64; 4];
raw[..val.len()].copy_from_slice(val);
Self::from(raw)
}
}
}

#[cfg(feature = "halo2-axiom")]
mod secp256k1 {
use crate::halo2_proofs::halo2curves::secp256k1::{Fp, Fq};

impl super::BigPrimeField for Fp {
#[inline(always)]
fn from_u64_digits(val: &[u64]) -> Self {
let mut raw = [0u64; 4];
raw[..val.len()].copy_from_slice(val);
Self::from(raw)
}
}

impl super::BigPrimeField for Fq {
#[inline(always)]
fn from_u64_digits(val: &[u64]) -> Self {
let mut raw = [0u64; 4];
raw[..val.len()].copy_from_slice(val);
Self::from(raw)
}
}
}

#[cfg(feature = "halo2-axiom")]
mod bls12_381 {
use crate::halo2_proofs::halo2curves::bls12_381::{Fq, Fr};

impl super::BigPrimeField for Fr {
#[inline(always)]
fn from_u64_digits(val: &[u64]) -> Self {
let mut raw = [0u64; 4];
raw[..val.len()].copy_from_slice(val);
Self::from(raw)
}
}

impl super::BigPrimeField for Fq {
#[inline(always)]
fn from_u64_digits(val: &[u64]) -> Self {
let mut raw = [0u64; 6];
raw[..val.len()].copy_from_slice(val);
Self::from(raw)
}
}
}

Expand Down Expand Up @@ -95,7 +150,7 @@ pub trait ScalarField: PrimeField + FromUniformBytes<64> + From<bool> + Hash + O

/// [ScalarField] that is ~256 bits long
#[cfg(feature = "halo2-pse")]
pub trait BigPrimeField = PrimeField<Repr = [u8; 32]> + ScalarField;
pub trait BigPrimeField = PrimeField + ScalarField;

/// Converts an [Iterator] of u64 digits into `number_of_limbs` limbs of `bit_len` bits returned as a [Vec].
///
Expand Down
11 changes: 9 additions & 2 deletions halo2-ecc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ itertools = "0.11"
num-bigint = { version = "0.4", features = ["rand"] }
num-integer = "0.1"
num-traits = "0.2"
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
rand_core = { version = "0.6", default-features = false, features = [
"getrandom",
] }
rand = "0.8"
rand_chacha = "0.3.1"
serde = { version = "1.0", features = ["derive"] }
Expand All @@ -23,6 +25,9 @@ rayon = "1.8"
test-case = "3.1.0"

halo2-base = { version = "=0.4.1", path = "../halo2-base", default-features = false }
# Use additional axiom-crypto halo2curves for BLS12-381 chips when [feature = "halo2-pse"] is on,
# because the PSE halo2curves does not support BLS12-381 chips and Halo2 depnds on lower major version so patching it is not possible
halo2curves = { package = "halo2curves-axiom", version = "0.5", optional=true }

# plotting circuit layout
plotters = { version = "0.3.0", optional = true }
Expand All @@ -35,13 +40,15 @@ criterion-macro = "0.4"
halo2-base = { version = "=0.4.1", path = "../halo2-base", default-features = false, features = ["test-utils"] }
test-log = "0.2.12"
env_logger = "0.10.0"
sha2 = "0.10"


[features]
default = ["jemallocator", "halo2-axiom", "display"]
dev-graph = ["halo2-base/dev-graph", "plotters"]
display = ["halo2-base/display"]
asm = ["halo2-base/asm"]
halo2-pse = ["halo2-base/halo2-pse"]
halo2-pse = ["halo2-base/halo2-pse", "halo2curves"]
halo2-axiom = ["halo2-base/halo2-axiom"]
jemallocator = ["halo2-base/jemallocator"]
mimalloc = ["halo2-base/mimalloc"]
Expand Down
8 changes: 8 additions & 0 deletions halo2-ecc/configs/bls12_381/bench_bls_signature.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{"strategy":"Simple","degree":15,"num_advice":105,"num_lookup_advice":14,"num_fixed":1,"lookup_bits":14,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":16,"num_advice":50,"num_lookup_advice":6,"num_fixed":1,"lookup_bits":15,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":17,"num_advice":25,"num_lookup_advice":3,"num_fixed":1,"lookup_bits":16,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":18,"num_advice":13,"num_lookup_advice":2,"num_fixed":1,"lookup_bits":17,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":19,"num_advice":6,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":18,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":20,"num_advice":3,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":19,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":21,"num_advice":2,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":20,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":22,"num_advice":1,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":21,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
5 changes: 5 additions & 0 deletions halo2-ecc/configs/bls12_381/bench_ec_add.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{"strategy":"Simple","degree":15,"num_advice":10,"num_lookup_advice":2,"num_fixed":1,"lookup_bits":14,"limb_bits":120,"num_limbs":4,"batch_size":100}
{"strategy":"Simple","degree":16,"num_advice":5,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":15,"limb_bits":120,"num_limbs":4,"batch_size":100}
{"strategy":"Simple","degree":17,"num_advice":4,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":16,"limb_bits":120,"num_limbs":4,"batch_size":100}
{"strategy":"Simple","degree":18,"num_advice":2,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":17,"limb_bits":120,"num_limbs":4,"batch_size":100}
{"strategy":"Simple","degree":19,"num_advice":1,"num_lookup_advice":0,"num_fixed":1,"lookup_bits":18,"limb_bits":120,"num_limbs":4,"batch_size":100}
8 changes: 8 additions & 0 deletions halo2-ecc/configs/bls12_381/bench_hash_to_curve.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{"strategy":"Simple","degree":15,"num_advice":105,"num_lookup_advice":14,"num_fixed":1,"lookup_bits":14,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":16,"num_advice":50,"num_lookup_advice":6,"num_fixed":1,"lookup_bits":15,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":17,"num_advice":25,"num_lookup_advice":3,"num_fixed":1,"lookup_bits":16,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":18,"num_advice":13,"num_lookup_advice":2,"num_fixed":1,"lookup_bits":17,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":19,"num_advice":6,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":18,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":20,"num_advice":3,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":19,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":21,"num_advice":2,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":20,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
{"strategy":"Simple","degree":22,"num_advice":1,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":21,"limb_bits":120,"num_limbs":4,"num_aggregation":2}
9 changes: 9 additions & 0 deletions halo2-ecc/configs/bls12_381/bench_pairing.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{"strategy":"Simple","degree":14,"num_advice":211,"num_lookup_advice":27,"num_fixed":1,"lookup_bits":13,"limb_bits":120,"num_limbs":4}
{"strategy":"Simple","degree":15,"num_advice":105,"num_lookup_advice":14,"num_fixed":1,"lookup_bits":14,"limb_bits":120,"num_limbs":4}
{"strategy":"Simple","degree":16,"num_advice":50,"num_lookup_advice":6,"num_fixed":1,"lookup_bits":15,"limb_bits":120,"num_limbs":4}
{"strategy":"Simple","degree":17,"num_advice":25,"num_lookup_advice":3,"num_fixed":1,"lookup_bits":16,"limb_bits":120,"num_limbs":4}
{"strategy":"Simple","degree":18,"num_advice":13,"num_lookup_advice":2,"num_fixed":1,"lookup_bits":17,"limb_bits":120,"num_limbs":4}
{"strategy":"Simple","degree":19,"num_advice":6,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":18,"limb_bits":120,"num_limbs":4}
{"strategy":"Simple","degree":20,"num_advice":3,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":19,"limb_bits":120,"num_limbs":4}
{"strategy":"Simple","degree":21,"num_advice":2,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":20,"limb_bits":120,"num_limbs":4}
{"strategy":"Simple","degree":22,"num_advice":1,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":21,"limb_bits":120,"num_limbs":4}
1 change: 1 addition & 0 deletions halo2-ecc/configs/bls12_381/bls_signature_circuit.config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"strategy":"Simple","degree":19,"num_advice":6,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":18,"limb_bits":104,"num_limbs":5,"num_aggregation":30}
1 change: 1 addition & 0 deletions halo2-ecc/configs/bls12_381/ec_add_circuit.config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"strategy":"Simple","degree":19,"num_advice":6,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":18,"limb_bits":104,"num_limbs":5,"batch_size":100}
1 change: 1 addition & 0 deletions halo2-ecc/configs/bls12_381/hash_to_curve_circuit.config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"strategy":"Simple","degree":19,"num_advice":6,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":18,"limb_bits":104,"num_limbs":5,"num_aggregation":30}
1 change: 1 addition & 0 deletions halo2-ecc/configs/bls12_381/pairing_circuit.config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"strategy":"Simple","degree":19,"num_advice":6,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":18,"limb_bits":104,"num_limbs":5}
23 changes: 17 additions & 6 deletions halo2-ecc/src/bigint/carry_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ pub fn crt<F: BigPrimeField>(
// Let n' <= quot_max_bits - n(k-1) - 1
// If quot[i] <= 2^n for i < k - 1 and quot[k-1] <= 2^{n'} then
// quot < 2^{n(k-1)+1} + 2^{n' + n(k-1)} = (2+2^{n'}) 2^{n(k-1)} < 2^{n'+1} * 2^{n(k-1)} <= 2^{quot_max_bits - n(k-1)} * 2^{n(k-1)}
let quot_last_limb_bits = quot_max_bits - n * (k - 1);

let bits_wo_last_limb: usize = n * (k - 1);
// `has_redunant_limb` will be true when native element can be represented in k-1 limbs, but some cases require an extra limb to carry.
// This is only the case for BLS12-381, which requires k=5 and n > 102 because of the check above.
let has_redunant_limb = quot_max_bits < bits_wo_last_limb;
let out_max_bits = modulus.bits() as usize;
// we assume `modulus` requires *exactly* `k` limbs to represent (if `< k` limbs ok, you should just be using that)
let out_last_limb_bits = out_max_bits - n * (k - 1);

// these are witness vectors:
// we need to find `out_vec` as a proper BigInt with k limbs
Expand Down Expand Up @@ -138,13 +138,24 @@ pub fn crt<F: BigPrimeField>(

// range check limbs of `out` are in [0, 2^n) except last limb should be in [0, 2^out_last_limb_bits)
for (out_index, out_cell) in out_assigned.iter().enumerate() {
let limb_bits = if out_index == k - 1 { out_last_limb_bits } else { n };
if has_redunant_limb && out_index == k - 1 {
let zero = ctx.load_zero();
ctx.constrain_equal(out_cell, &zero);
continue;
}
// we assume `modulus` requires *exactly* `k` limbs to represent (if `< k` limbs ok, you should just be using that)
let limb_bits = if out_index == k - 1 { out_max_bits - bits_wo_last_limb } else { n };
range.range_check(ctx, *out_cell, limb_bits);
}

// range check that quot_cell in quot_assigned is in [-2^n, 2^n) except for last cell check it's in [-2^quot_last_limb_bits, 2^quot_last_limb_bits)
for (q_index, quot_cell) in quot_assigned.iter().enumerate() {
let limb_bits = if q_index == k - 1 { quot_last_limb_bits } else { n };
if has_redunant_limb && q_index == k - 1 {
let zero = ctx.load_zero();
ctx.constrain_equal(quot_cell, &zero);
continue;
}
let limb_bits = if q_index == k - 1 { quot_max_bits - bits_wo_last_limb } else { n };
let limb_base =
if q_index == k - 1 { range.gate().pow_of_two()[limb_bits] } else { limb_bases[1] };

Expand Down
12 changes: 10 additions & 2 deletions halo2-ecc/src/bigint/check_carry_mod_to_zero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ pub fn crt<F: BigPrimeField>(
// see carry_mod.rs for explanation
let quot_max_bits = trunc_len - 1 + (F::NUM_BITS as usize) - 1 - (modulus.bits() as usize);
assert!(quot_max_bits < trunc_len);
let quot_last_limb_bits = quot_max_bits - n * (k - 1);
let bits_wo_last_limb: usize = n * (k - 1);
// `has_redunant_limb` will be true when native element can be represented in k-1 limbs, but some cases require an extra limb to carry.
// This is only the case for BLS12-381, which requires k=5 and n > 102 because of the check above.
let has_redunant_limb = quot_max_bits < bits_wo_last_limb;

// these are witness vectors:
// we need to find `quot_vec` as a proper BigInt with k limbs
Expand Down Expand Up @@ -90,7 +93,12 @@ pub fn crt<F: BigPrimeField>(

// range check that quot_cell in quot_assigned is in [-2^n, 2^n) except for last cell check it's in [-2^quot_last_limb_bits, 2^quot_last_limb_bits)
for (q_index, quot_cell) in quot_assigned.iter().enumerate() {
let limb_bits = if q_index == k - 1 { quot_last_limb_bits } else { n };
if has_redunant_limb && q_index == k - 1 {
let zero = ctx.load_zero();
ctx.constrain_equal(quot_cell, &zero);
continue;
}
let limb_bits = if q_index == k - 1 { quot_max_bits - n * (k - 1) } else { n };
let limb_base =
if q_index == k - 1 { range.gate().pow_of_two()[limb_bits] } else { limb_bases[1] };

Expand Down
1 change: 1 addition & 0 deletions halo2-ecc/src/bigint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod select;
pub mod select_by_indicator;
pub mod sub;
pub mod sub_no_carry;
pub mod utils;

#[derive(Clone, Debug, PartialEq, Default)]
pub enum BigIntStrategy {
Expand Down
37 changes: 37 additions & 0 deletions halo2-ecc/src/bigint/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use halo2_base::{utils::BigPrimeField, Context, gates::GateInstructions, AssignedValue, QuantumCell};
use itertools::Itertools;
use num_bigint::BigUint;

use super::{ProperCrtUint, ProperUint};


/// Converts assigned bytes in little-endian into biginterger
/// Warning: method does not perform any checks on input `bytes`.
pub fn decode_into_bn<F: BigPrimeField>(
ctx: &mut Context<F>,
gate: &impl GateInstructions<F>,
bytes: Vec<AssignedValue<F>>,
limb_bases: &[F],
limb_bits: usize,
) -> ProperCrtUint<F> {
let limb_bytes = limb_bits / 8;
let bits = limb_bases.len() * limb_bits;

let value =
BigUint::from_bytes_le(&bytes.iter().map(|v| v.value().get_lower_32() as u8).collect_vec());

// inputs is a bool or uint8.
let assigned_uint = if bits == 1 || bits == 8 || limb_bits == 8 {
ProperUint(bytes)
} else {
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::<Vec<_>>();
ProperUint(limbs)
};

assigned_uint.into_crt(ctx, gate, value, limb_bases, limb_bits)
}
Loading