-
Notifications
You must be signed in to change notification settings - Fork 125
Conversation
|
||
fn configure(meta: &mut ConstraintSystem<N>) -> Self::Config { | ||
let integer_config = { | ||
const BIT_LEN_LIMB: usize = 68; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move constant variable declarations BIT_LEN_LIMB
and NUMBER_OF_LIMBS
to the top of a file?
ctx: &mut RegionCtx<N>, | ||
x: W | ||
) -> Result<AssignedInteger<W, N, NUMBER_OF_LIMBS, BIT_LEN_LIMB>, Error> { | ||
let rc_rns: std::rc::Rc<Rns<W, N, NUMBER_OF_LIMBS, BIT_LEN_LIMB>> = std::rc::Rc::new(Rns::<W, N, NUMBER_OF_LIMBS, BIT_LEN_LIMB>::construct()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we change the code format to
let rc_rns = std::rc::Rc::new(Rns::<W, N, NUMBER_OF_LIMBS, BIT_LEN_LIMB>::construct());
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, was just using explicit typing bc in this part of the code I had to be really careful to the types
let v: Value<Integer<W, N, NUMBER_OF_LIMBS, BIT_LEN_LIMB>> = Value::known(i); | ||
let u: UnassignedInteger<W, N, NUMBER_OF_LIMBS, BIT_LEN_LIMB> = UnassignedInteger::from(v); | ||
// TODO: is Range::Operand a correct range here? | ||
let a: AssignedInteger<W, N, NUMBER_OF_LIMBS, BIT_LEN_LIMB> = integer_chip.assign_integer(ctx, u, Range::Operand)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is a
for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assigned integer
use halo2_proofs::circuit::{Layouter,SimpleFloorPlanner,Value}; | ||
use halo2_proofs::halo2curves::ff::PrimeField as FieldExt; | ||
use halo2wrong_integer::{IntegerConfig, rns::Rns, IntegerChip, IntegerInstructions, rns::Integer, UnassignedInteger, Range, AssignedInteger}; | ||
use halo2wrong_maingate::{RangeChip, MainGate, RegionCtx}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the motivation to use halo2wrong_maingate
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's required for some operations, I think to allow me to setup the IntegerConfig with its underlying Range gate Config and Maingate Config. also I think that's where the RegionCtx operations come from.
} | ||
x2is | ||
}; | ||
let nbits: [W; 256] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, I do not no Halo2Wrong API and do not sure that I have correctly understood the related parts. However, if they work as I suppose and bit factorization of n (log2 of the order of w) is predefined, than I think that it should work correctly. This part of the code (the lines 54-71) can be written in a shorter way (defining nbits[j*8+i] directly without the boolean array), but it is not a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use cargo fmt
to format the code. Yeah I know it messes up the code how you want it to look, but it does make it easier to see the code in a nice standard way.
It seems like some tests did not pass as expected:
running 6 tests
test tests::wrong_witness_data ... FAILED
test tests::wrong_x - should panic ... ok
test tests::try_default has been running for over 60 seconds
test tests::try_random_datapoints has been running for over 60 seconds
test tests::wrong_n has been running for over 60 seconds
test tests::wrong_w has been running for over 60 seconds
test tests::try_default ... ok
test tests::wrong_n - should panic ... FAILED
test tests::wrong_w - should panic ... FAILED
test tests::try_random_datapoints ... ok
failures:
---- tests::wrong_witness_data stdout ----
thread 'tests::wrong_witness_data' panicked at 'not implemented', src/main.rs:519:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
---- tests::wrong_n stdout ----
note: test did not panic as expected
---- tests::wrong_w stdout ----
note: test did not panic as expected
failures:
tests::wrong_n
tests::wrong_w
tests::wrong_witness_data
test result: FAILED. 3 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out; finished in 144.90s
wrong_witness_data
makes sense how it is now (but please change it so all tests pass even if still to do), but the others seems like unexpected.
|
||
#[derive(Debug)] | ||
pub struct BlobKZGCircuit<W: PrimeField, N: PrimeField>{ // W is the wrong field, N is the native field | ||
datapoints: [W; 4096], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be good to use const variables everywhere like NUM_POINTS
instead of the constants like 4096
/256
/... themselves.
use halo2wrong_maingate::{RangeChip, MainGate, RegionCtx}; | ||
|
||
/// THIS CIRCUIT PERFORMS A NON-NATIVE FUNCTION EVAL USING BARYCENTRIC-FORMULA | ||
/// P(X) = (const) * sum (d_i * (w^i/x-w^i)) where const = (x^N - 1)/N with w^N = 1 and X^N != 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be nice to have some more comments explaining about the exact approach, and which values are calculated in the circuit and how, and which values are precomputed. Some comments which variable is what because the same variable names are used in the code would also be nice.
impl<W: PrimeField> WitnessValues<W> { | ||
fn new(x:&W, n:&W, w:&W, datapoints: &[W; 4096]) -> WitnessValues<W> { | ||
let x2is: [W; 256] = { | ||
let mut x2is = [W::default(); 256]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of W::default()
, let's just use W::ZERO
everywhere, that makes things more clear.
} | ||
} | ||
impl<W: PrimeField> WitnessValues<W> { | ||
fn new(x:&W, n:&W, w:&W, datapoints: &[W; 4096]) -> WitnessValues<W> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can use some simple comments for each step here so it's more clear what it being done and why. For example one big one could be // Split up n into bits so we can calculate the exponent using the square and multiply.
and the x2is
code would be // Calculate the exponent for each bit position
. You get the idea.
fn new(x:&W, n:&W, w:&W, datapoints: &[W; 4096]) -> WitnessValues<W> { | ||
let x2is: [W; 256] = { | ||
let mut x2is = [W::default(); 256]; | ||
let mut state = *x; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of the name state
, acc
or something like that is a more standard name I think.
let assigned_x = assign_wrong_integer(&integer_chip, &mut ctx, self.x)?; | ||
let assigned_w = assign_wrong_integer(&integer_chip, &mut ctx, self.w)?; | ||
let assigned_zero = assign_wrong_integer(&integer_chip, &mut ctx, W::ZERO)?; | ||
let assigned_one = assign_wrong_integer(&integer_chip, &mut ctx, W::ONE)?; | ||
let assigned_n = assign_wrong_integer(&integer_chip, &mut ctx, self.n)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these values (and some below here as well) are being assigned using assign_wrong_integer
which internally will use assign_integer
. But some of these are constants and need to have a fixed value, so shouldn't those use assign_constant
?
}).collect::<Result<Vec<_>, Error>>()?; | ||
assert!(assigned_datapoints.len() == 4096); | ||
|
||
// ctx = RegionCtx::new(ctx.into_region(), 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this is some old comment?
// TODO: check _div_result? | ||
integer_chip.add(&mut ctx, &assigned_evals[i], &summand)? | ||
}; | ||
integer_chip.assert_equal(&mut ctx, &assigned_evals[i+1], &eval_next)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to do all these assigned_evals
? Can't we just use eval_next
in the next loop iteration instead of assigned_evals[i]
?
// TODO: integer_chip operations (.mul, .sub) actually assigned NEW values to the circuit!!! how to fix? Because .assert_equal only seems to work between AssignedInteger<> values... | ||
// ctx = RegionCtx::new(ctx.into_region(), 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this still a TODO?
use halo2wrong_maingate::RangeInstructions; | ||
integer_chip.range_chip().load_table(&mut layouter)?; | ||
|
||
layouter.assign_region(|| "BlobKZGCircuit Region", | region| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some more comments here on what does what would also be nice.
An important remark about the correct way of choosing the pseudorandom evaluation point x for the polynomial encoding the data. The PR is aimed at implementing protocol on the screenshot. Note that the first 2 steps insist on choosing the value of x as the hash of both the polynomial outside the circuit and its evaluations inside the circuit. This means that (in the case of one-phase synthesis) the circuit must contain the constraints representing the hashing of the data polynomial evaluations. This inner hash value should be passed as a public input to the circuit to make the result of the in-circuit hashing accessible to the verifier. When a smart contract verifies the proof for the circuit, it will be given this inner hash and use it along with the hash of the polynomial outside the circuit to compute the evaluation point x, which is also a public input. Why do we need this inner hashing? To force the prover to choose the value of x strictly after the data polynomial evaluations in the circuit are fixed. If prover is allowed to change these evaluations after x is chosen, he can interpolate the polynomial, which is not equal to the one outside the circuit, but coincide with it at x. He can do it with polynomial time and space complexity and is free to choose the values at any 4095 of 4096 evaluation points! How could we simplify this approach? I think we can avoid writing the hash constraints, if we have a tricky proving system that allows two-phase synthesis, has the challenge API (returning the hash of the witnesses committed at the first phase) and allows us to generate some public input data between the phases of the synthesis. In this case the challenge value can be used instead of the aforesaid inner hash value - it can be used (along with the public input part representing the hash of the polynomial outside the circuit) to compute x between the synthesis phases. The value of x will be added to the public input before the second phase of synthesis. Unfortunately, it seems that Halo2 does not support this dynamic public input generation. |
@AlekseiVambol Could we reuse the KZG commitment for the column storing the data that could already be available for the verifier (?) instead of explicitly committing to the data in-circuit? |
Yes. |
Description
This PR adds the circuit required to integrate Taiko with EIP 4844. Basically it takes data stored in the circuit and uses it for function evaluation. This is then compared with the blob opening checked in the smart contract with EIP 4844. Make sure the data fed into this circuit is checked to be the same as all the other claims of the proof.
Issue Link
[link issue here]
Type of change
Contents
Contain the new circuit as well as dependencies which support the latest traits along with the latest halo2curves (hence the breaking change...but the circuit has not been integrated into the rest of the zkEVM so everything compiles for now)
Rationale
Required for the L2 to make use of EIP 4844 Blobs, greatly improving costs for publishing new proofs
How Has This Been Tested?
Unit tests (more to come)