Skip to content
This repository was archived by the owner on Apr 18, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
429 changes: 238 additions & 191 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion aggregator/src/aggregation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/// EIP-4844 blob related config.
/// Config to evaluate blob polynomial at a random challenge.
mod barycentric;
/// Config to constrain blob data
mod blob_data;
/// Circuit implementation of aggregation circuit.
mod circuit;
Expand All @@ -7,6 +9,9 @@ mod config;
/// config for RLC circuit
mod rlc;

pub(crate) use barycentric::{
interpolate, AssignedBarycentricEvaluationConfig, BarycentricEvaluationConfig, BLS_MODULUS,
};
pub(crate) use blob_data::BlobDataConfig;
pub use circuit::AggregationCircuit;
pub use config::AggregationConfig;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,18 @@ use num_bigint::{BigInt, Sign};
use std::{iter::successors, sync::LazyLock};

use crate::{
blob::{BLOB_WIDTH, LOG_BLOG_WIDTH},
blob::{BLOB_WIDTH, LOG_BLOB_WIDTH},
constants::{BITS, LIMBS},
};

/*
mod scalar_field_element;
use scalar_field_element::ScalarFieldElement;
*/
pub static BLS_MODULUS: LazyLock<U256> = LazyLock::new(|| {
U256::from_str_radix(Scalar::MODULUS, 16).expect("BLS_MODULUS from bls crate")
});

pub static ROOTS_OF_UNITY: LazyLock<[Scalar; BLOB_WIDTH]> = LazyLock::new(|| {
// https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/polynomial-commitments.md#constants
let primitive_root_of_unity = Scalar::from(7);
let modulus = U256::from_str_radix(Scalar::MODULUS, 16).unwrap();
let modulus = *BLS_MODULUS;

let exponent = (modulus - U256::one()) / U256::from(4096);
let root_of_unity = primitive_root_of_unity.pow(&exponent.0);
Expand All @@ -40,7 +39,7 @@ pub static ROOTS_OF_UNITY: LazyLock<[Scalar; BLOB_WIDTH]> = LazyLock::new(|| {
.collect();
let bit_reversed_order: Vec<_> = (0..BLOB_WIDTH)
.map(|i| {
let j = u16::try_from(i).unwrap().reverse_bits() >> (16 - LOG_BLOG_WIDTH);
let j = u16::try_from(i).unwrap().reverse_bits() >> (16 - LOG_BLOB_WIDTH);
ascending_order[usize::from(j)]
})
.collect();
Expand All @@ -52,17 +51,14 @@ pub struct BarycentricEvaluationConfig {
pub scalar: FpConfig<Fr, Scalar>,
}

#[allow(dead_code)]
pub struct BarycentricEvaluationAssignments {
z: CRTInteger<Fr>,
y: CRTInteger<Fr>,
blob: [CRTInteger<Fr>; BLOB_WIDTH],
}

#[derive(Default)]
pub struct BarycentricEvaluationCells {
pub struct AssignedBarycentricEvaluationConfig {
/// CRTIntegers for the BLOB_WIDTH number of blob polynomial coefficients, followed by a
/// CRTInteger for the challenge digest.
pub(crate) barycentric_assignments: Vec<CRTInteger<Fr>>,
/// 32 Assigned cells representing the LE-bytes of challenge z.
pub(crate) z_le: Vec<AssignedValue<Fr>>,
/// 32 Assigned cells representing the LE-bytes of evaluation y.
pub(crate) y_le: Vec<AssignedValue<Fr>>,
}

Expand Down Expand Up @@ -100,19 +96,30 @@ impl BarycentricEvaluationConfig {
)
}

pub fn assign2(
pub fn assign(
&self,
ctx: &mut Context<Fr>,
blob: [U256; BLOB_WIDTH],
challenge_digest: U256,
evaluation: U256,
) -> BarycentricEvaluationCells {
// prechecks (challenge point z)
let bls_modulus = U256::from_dec_str(
"52435875175126190479447740508185965837690552500527637822603658699938581184513",
)
.expect("BLS_MODULUS from decimal string");
let (_, challenge) = challenge_digest.div_mod(bls_modulus);
) -> AssignedBarycentricEvaluationConfig {
// some constants for later use.
let one = self.scalar.load_constant(ctx, fe_to_biguint(&Fr::one()));
let blob_width = self
.scalar
.load_constant(ctx, fe_to_biguint(&Fr::from(BLOB_WIDTH as u64)));

let powers_of_256 =
std::iter::successors(Some(Fr::one()), |coeff| Some(Fr::from(256) * coeff))
.take(11)
.map(QuantumCell::Constant)
.collect::<Vec<_>>();

////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////// PRECHECKS z /////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////

let (_, challenge) = challenge_digest.div_mod(*BLS_MODULUS);
let challenge_scalar = Scalar::from_raw(challenge.0);

let challenge_digest_crt = self.load_u256(ctx, challenge_digest);
Expand All @@ -129,24 +136,6 @@ impl BarycentricEvaluationConfig {
.load_private(ctx, Value::known(fe_to_biguint(&challenge_scalar).into()));
self.scalar
.assert_equal(ctx, &challenge_digest_mod, &challenge_crt);

// prechecks (evaluation y)
let evaluation_le = self.scalar.range().gate.assign_witnesses(
ctx,
evaluation
.to_le_bytes()
.iter()
.map(|&x| Value::known(Fr::from(x as u64))),
);
let evaluation_scalar = Scalar::from_raw(evaluation.0);
let evaluation_crt = self
.scalar
.load_private(ctx, Value::known(fe_to_biguint(&evaluation_scalar).into()));
let powers_of_256 =
std::iter::successors(Some(Fr::one()), |coeff| Some(Fr::from(256) * coeff))
.take(11)
.map(QuantumCell::Constant)
.collect::<Vec<_>>();
let challenge_limb1 = self.scalar.range().gate.inner_product(
ctx,
challenge_le[0..11]
Expand Down Expand Up @@ -183,6 +172,21 @@ impl BarycentricEvaluationConfig {
QuantumCell::Existing(challenge_limb3),
QuantumCell::Existing(challenge_crt.truncation.limbs[2]),
);

////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////// PRECHECKS y /////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
let evaluation_le = self.scalar.range().gate.assign_witnesses(
ctx,
evaluation
.to_le_bytes()
.iter()
.map(|&x| Value::known(Fr::from(x as u64))),
);
let evaluation_scalar = Scalar::from_raw(evaluation.0);
let evaluation_crt = self
.scalar
.load_private(ctx, Value::known(fe_to_biguint(&evaluation_scalar).into()));
let evaluation_limb1 = self.scalar.range().gate.inner_product(
ctx,
evaluation_le[0..11]
Expand Down Expand Up @@ -220,12 +224,15 @@ impl BarycentricEvaluationConfig {
QuantumCell::Existing(evaluation_crt.truncation.limbs[2]),
);

// compute evaluation.
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////// BARYCENTRIC EVALUATION //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
let mut blob_crts = Vec::with_capacity(BLOB_WIDTH);
let mut evaluation_computed = self.scalar.load_constant(ctx, fe_to_biguint(&Fr::zero()));
blob.iter()
.zip_eq(ROOTS_OF_UNITY.map(|x| self.scalar.load_constant(ctx, fe_to_biguint(&x))))
.for_each(|(blob_i, root_i_crt)| {
// assign LE-bytes of blob scalar field element.
let blob_i_le = self.scalar.range().gate.assign_witnesses(
ctx,
blob_i
Expand All @@ -237,6 +244,8 @@ impl BarycentricEvaluationConfig {
let blob_i_crt = self
.scalar
.load_private(ctx, Value::known(fe_to_biguint(&blob_i_scalar).into()));

// compute the limbs for blob scalar field element.
let limb1 = self.scalar.range().gate.inner_product(
ctx,
blob_i_le[0..11].iter().map(|&x| QuantumCell::Existing(x)),
Expand Down Expand Up @@ -267,6 +276,9 @@ impl BarycentricEvaluationConfig {
QuantumCell::Existing(limb3),
QuantumCell::Existing(blob_i_crt.truncation.limbs[2]),
);

// the most-significant byte of blob scalar field element is 0 as we expect this
// representation to be in its canonical form.
self.scalar.range().gate.assert_equal(
ctx,
QuantumCell::Existing(blob_i_le[31]),
Expand All @@ -287,11 +299,7 @@ impl BarycentricEvaluationConfig {
blob_crts.push(blob_i_crt);
});

let one = self.scalar.load_constant(ctx, fe_to_biguint(&Fr::one()));
let blob_width = self
.scalar
.load_constant(ctx, fe_to_biguint(&Fr::from(BLOB_WIDTH as u64)));
let z_to_blob_width = (0..LOG_BLOG_WIDTH).fold(challenge_crt.clone(), |acc, _| {
let z_to_blob_width = (0..LOG_BLOB_WIDTH).fold(challenge_crt.clone(), |acc, _| {
self.scalar.mul(ctx, &acc, &acc)
});
let z_to_blob_width_minus_one = self.scalar.sub_no_carry(ctx, &z_to_blob_width, &one);
Expand All @@ -302,11 +310,14 @@ impl BarycentricEvaluationConfig {
evaluation_computed = self.scalar.mul(ctx, &evaluation_computed, &factor);
evaluation_computed = self.scalar.carry_mod(ctx, &evaluation_computed);

// The evaluation we computed is correct.
// computed evaluation matches the expected evaluation.
self.scalar
.assert_equal(ctx, &evaluation_computed, &evaluation_crt);

BarycentricEvaluationCells {
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////// EXPORT //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
AssignedBarycentricEvaluationConfig {
barycentric_assignments: blob_crts
.into_iter()
.chain(std::iter::once(challenge_digest_crt))
Expand All @@ -315,48 +326,6 @@ impl BarycentricEvaluationConfig {
y_le: evaluation_le,
}
}

/*
pub fn assign(
&self,
ctx: &mut Context<Fr>,
blob: [Scalar; BLOB_WIDTH],
z: Scalar,
) -> BarycentricEvaluationAssignments {
let one = ScalarFieldElement::constant(Scalar::one());
let blob_width = ScalarFieldElement::constant(u64::try_from(BLOB_WIDTH).unwrap().into());

let z = self
.scalar
.load_private(ctx, Value::known(fe_to_biguint(&z).into()));
let blob = blob.map(|e| {
self.scalar
.load_private(ctx, Value::known(fe_to_biguint(&e).into()))
});
let z_to_blob_width = successors(Some(ScalarFieldElement::private(z.clone())), |z| {
Some(z.clone() * z.clone())
})
.take(LOG_BLOG_WIDTH)
.last()
.unwrap();
let y = (z_to_blob_width - one)
* ROOTS_OF_UNITY
.map(ScalarFieldElement::constant)
.into_iter()
.zip_eq(blob.clone().map(ScalarFieldElement::private))
.map(|(root, f)| {
f * (root.clone() / (ScalarFieldElement::private(z.clone()) - root)).carry()
})
.sum()
/ blob_width;

BarycentricEvaluationAssignments {
z,
y: y.resolve(ctx, &self.scalar),
blob,
}
}
*/
}

pub fn interpolate(z: Scalar, coefficients: [Scalar; BLOB_WIDTH]) -> Scalar {
Expand All @@ -377,12 +346,12 @@ mod tests {

#[test]
fn log_blob_width() {
assert_eq!(2_usize.pow(LOG_BLOG_WIDTH.try_into().unwrap()), BLOB_WIDTH);
assert_eq!(2_usize.pow(LOG_BLOB_WIDTH.try_into().unwrap()), BLOB_WIDTH);
}

#[test]
fn scalar_field_modulus() {
let bls_modulus = U256::from_str_radix(Scalar::MODULUS, 16).unwrap();
let bls_modulus = *BLS_MODULUS;
// BLS_MODULUS as decimal string from https://eips.ethereum.org/EIPS/eip-4844.
let expected_bls_modulus = U256::from_str_radix(
"52435875175126190479447740508185965837690552500527637822603658699938581184513",
Expand Down
Loading