Skip to content

Conversation

@srinathsetty
Copy link
Collaborator

No description provided.

This commit adds several utility functions and methods for circuit gadgets
that are useful for building advanced applications like MicroNova:

ecc.rs - AllocatedPoint:
- alloc_constant: Allocate a point with constant x,y coords
- absorb_in_ro: Absorb point into random oracle circuit (handles infinity)
- enforce_equal: Enforce equality constraints between points

ecc.rs - AllocatedPointNonInfinity:
- alloc: Allocate a new point from coordinates
- conditionally_select2: Conditional select using AllocatedNum
- enforce_equal: Enforce equality constraints

utils.rs:
- alloc_constant: Allocate constant value in circuit
- base_as_bigint: Convert base field element to BigInt
- le_num_to_num: Convert little-endian AllocatedNum bits to number
- fingerprint: Compute running sum for fingerprinting values

nonnative/bignat.rs - BigNat:
- as_limb_values: Get copy of limb values
- fold_bn: Fold two BigNats: self + r * other mod modulus
- inputize: Make limbs into public inputs
- decompose_allocated: Decompose to allocated bit vector
- sub_mod: Subtraction modulo

nonnative/util.rs:
- get_base_modulus: Get base field modulus as BigInt
- absorb_bignat_in_ro: Absorb BigNat into RO circuit
- absorb_bignat_in_ro_native: Native RO absorption for scalar
- fingerprint_bignat: Fingerprint a BigNat's limbs
@srinathsetty srinathsetty requested a review from Copilot January 9, 2026 22:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds gadget extensions to the nova-snark library, introducing utility functions for constraint systems, BigNat operations, and elliptic curve point manipulations. The changes support version 0.45.0.

  • Adds utility functions for allocating constants, fingerprinting values, and converting field elements
  • Extends BigNat with folding, decomposition, subtraction modulo, and public input operations
  • Adds methods for elliptic curve points including constant allocation, equality enforcement, and random oracle absorption

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/gadgets/utils.rs Adds alloc_constant, base_as_bigint, le_num_to_num, and fingerprint utility functions for circuit constraints
src/gadgets/nonnative/util.rs Adds BigNat absorption functions for random oracles and fingerprinting, plus base modulus getter
src/gadgets/nonnative/bignat.rs Extends BigNat with fold_bn, inputize, decompose_allocated, sub_mod, and as_limb_values methods
src/gadgets/ecc.rs Adds alloc_constant, absorb_in_ro, and enforce_equal methods for AllocatedPoint and AllocatedPointNonInfinity
Cargo.toml Version bump from 0.44.0 to 0.45.0

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

/// Make the limbs of this BigNat into public inputs.
pub fn inputize<CS: ConstraintSystem<Scalar>>(&self, mut cs: CS) -> Result<(), SynthesisError> {
for (i, l) in self.limbs.iter().enumerate() {
let mut c = cs.namespace(|| format!("limb {i}"));
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constraint on line 703 enforces 0 * 0 = v - l, which means v - l must be zero. However, the constraint is written in an unusual way. A more standard and clearer form would be to use cs.enforce(|| "eq", |lc| lc + v - l, |lc| lc + CS::one(), |lc| lc); to enforce (v - l) * 1 = 0.

Copilot uses AI. Check for mistakes.
Comment on lines +280 to +292
// comb = limb[0] + limb[1] * 2^limb_width
let comb: AllocatedNum<E::Base> = AllocatedNum::alloc(
cs.namespace(|| format!("combine two limbs into one scalar {i}")),
|| Ok(*w[0].get_value().get()? + *w[1].get_value().get()? * pow_2_limb_width),
)?;

// check that comb = limb[0] + limb[1] * 2^limb_width
cs.enforce(
|| format!("check combine two limbs into one scalar {i}"),
|lc| lc + w[0].get_variable() + (pow_2_limb_width, w[1].get_variable()),
|lc| lc + CS::one(),
|lc| lc + comb.get_variable(),
);
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function uses chunks(2) on limbs without checking if the number of limbs is even. If the BigNat has an odd number of limbs, the last chunk will only have one element, causing a panic when accessing w[1] on line 283. Consider adding a check or handling odd-sized chunks differently.

Suggested change
// comb = limb[0] + limb[1] * 2^limb_width
let comb: AllocatedNum<E::Base> = AllocatedNum::alloc(
cs.namespace(|| format!("combine two limbs into one scalar {i}")),
|| Ok(*w[0].get_value().get()? + *w[1].get_value().get()? * pow_2_limb_width),
)?;
// check that comb = limb[0] + limb[1] * 2^limb_width
cs.enforce(
|| format!("check combine two limbs into one scalar {i}"),
|lc| lc + w[0].get_variable() + (pow_2_limb_width, w[1].get_variable()),
|lc| lc + CS::one(),
|lc| lc + comb.get_variable(),
);
// Handle both full (2-limb) chunks and a possible final single-limb chunk.
let comb: AllocatedNum<E::Base> = if w.len() == 2 {
// comb = limb[0] + limb[1] * 2^limb_width
AllocatedNum::alloc(
cs.namespace(|| format!("combine two limbs into one scalar {i}")),
|| Ok(*w[0].get_value().get()? + *w[1].get_value().get()? * pow_2_limb_width),
)?
} else {
// Only one limb in this chunk; treat comb as just limb[0].
AllocatedNum::alloc(
cs.namespace(|| format!("pass-through single limb {i}")),
|| Ok(*w[0].get_value().get()?),
)?
};
if w.len() == 2 {
// check that comb = limb[0] + limb[1] * 2^limb_width
cs.enforce(
|| format!("check combine two limbs into one scalar {i}"),
|lc| lc + w[0].get_variable() + (pow_2_limb_width, w[1].get_variable()),
|lc| lc + CS::one(),
|lc| lc + comb.get_variable(),
);
} else {
// check that comb = limb[0] for a single-limb chunk
cs.enforce(
|| format!("check pass-through single limb {i}"),
|lc| lc + w[0].get_variable(),
|lc| lc + CS::one(),
|lc| lc + comb.get_variable(),
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +385 to +399
// comb = limb[0] + limb[1] * 2^limb_width
let comb = AllocatedNum::alloc(
cs.namespace(|| format!("combine two limbs into one scalar {i}")),
|| Ok(*w[0].get_value().get()? + *w[1].get_value().get()? * pow_2_limb_width),
)?;

// check that comb = limb[0] + limb[1] * 2^limb_width
cs.enforce(
|| format!("check combine two limbs into one scalar {i}"),
|lc| lc + w[0].get_variable() + (pow_2_limb_width, w[1].get_variable()),
|lc| lc + CS::one(),
|lc| lc + comb.get_variable(),
);

Ok(comb)
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function uses chunks(2) on limbs without checking if the number of limbs is even. If the BigNat has an odd number of limbs, the last chunk will only have one element, causing a panic when accessing w[1] on line 388. Consider adding a check or handling odd-sized chunks differently.

Suggested change
// comb = limb[0] + limb[1] * 2^limb_width
let comb = AllocatedNum::alloc(
cs.namespace(|| format!("combine two limbs into one scalar {i}")),
|| Ok(*w[0].get_value().get()? + *w[1].get_value().get()? * pow_2_limb_width),
)?;
// check that comb = limb[0] + limb[1] * 2^limb_width
cs.enforce(
|| format!("check combine two limbs into one scalar {i}"),
|lc| lc + w[0].get_variable() + (pow_2_limb_width, w[1].get_variable()),
|lc| lc + CS::one(),
|lc| lc + comb.get_variable(),
);
Ok(comb)
match w {
// comb = limb[0] + limb[1] * 2^limb_width
[ref limb0, ref limb1] => {
let comb = AllocatedNum::alloc(
cs.namespace(|| format!("combine two limbs into one scalar {i}")),
|| Ok(*limb0.get_value().get()? + *limb1.get_value().get()? * pow_2_limb_width),
)?;
// check that comb = limb[0] + limb[1] * 2^limb_width
cs.enforce(
|| format!("check combine two limbs into one scalar {i}"),
|lc| lc + limb0.get_variable() + (pow_2_limb_width, limb1.get_variable()),
|lc| lc + CS::one(),
|lc| lc + comb.get_variable(),
);
Ok(comb)
}
// Handle an odd number of limbs: the last chunk has a single limb.
[ref limb0] => {
let comb = AllocatedNum::alloc(
cs.namespace(|| format!("combine remaining single limb into one scalar {i}")),
|| Ok(*limb0.get_value().get()?),
)?;
// Enforce comb = limb[0]
cs.enforce(
|| format!("check combine remaining single limb into one scalar {i}"),
|lc| lc + limb0.get_variable(),
|lc| lc + CS::one(),
|lc| lc + comb.get_variable(),
);
Ok(comb)
}
_ => unreachable!(),
}

Copilot uses AI. Check for mistakes.
})?;

cs.enforce(
|| format!("eq scalar limb_{i}"),
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The panic message "We should have never reached there" contains a grammatical error. It should be "We should have never reached here" or "This should never be reached".

Copilot uses AI. Check for mistakes.
Comment on lines +276 to +296
let limbs_packed: Vec<AllocatedNum<E::Base>> = limbs
.chunks(2)
.enumerate()
.map(|(i, w)| {
// comb = limb[0] + limb[1] * 2^limb_width
let comb: AllocatedNum<E::Base> = AllocatedNum::alloc(
cs.namespace(|| format!("combine two limbs into one scalar {i}")),
|| Ok(*w[0].get_value().get()? + *w[1].get_value().get()? * pow_2_limb_width),
)?;

// check that comb = limb[0] + limb[1] * 2^limb_width
cs.enforce(
|| format!("check combine two limbs into one scalar {i}"),
|lc| lc + w[0].get_variable() + (pow_2_limb_width, w[1].get_variable()),
|lc| lc + CS::one(),
|lc| lc + comb.get_variable(),
);

Ok(comb)
})
.collect::<Result<Vec<AllocatedNum<E::Base>>, SynthesisError>>()?;
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is significant code duplication between absorb_bignat_in_ro and absorb_bignat_in_ro_scalar. Both functions perform the same limb-packing logic with chunks(2). Consider extracting the common packing logic into a helper function to reduce duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants