mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-03 00:31:50 +00:00
Add PeerDAS KZG lib integration (construction & KZG verification) (#6212)
* Add peerdas KZG library and use it for data column construction and cell kzg verification (#5701, #5941, #6118, #6179) Co-authored-by: kevaundray <kevtheappdev@gmail.com> * Update `rust_eth_kzg` crate to published version. * Update kzg metrics buckets. * Merge branch 'unstable' into peerdas-kzg * Update KZG version to fix windows mem allocation. * Refactor common logic from build sidecar and reconstruction. Remove unnecessary `needless_lifetimes`. Co-authored-by: realbigsean <sean@sigmaprime.io> * Copy existing trusted setup into `PeerDASTrustedSetup` for consistency and maintain `--trusted-setup` functionality. * Merge branch 'unstable' into peerdas-kzg * Merge branch 'peerdas-kzg' of github.com:jimmygchen/lighthouse into peerdas-kzg * Merge branch 'unstable' into peerdas-kzg * Merge branch 'unstable' into peerdas-kzg * Load PeerDAS KZG only if PeerDAS is enabled.
This commit is contained in:
@@ -17,3 +17,13 @@ ethereum_serde_utils = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
ethereum_hashing = { workspace = true }
|
||||
c-kzg = { workspace = true }
|
||||
rust_eth_kzg = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
eth2_network_config = { workspace = true }
|
||||
|
||||
[[bench]]
|
||||
name = "benchmark"
|
||||
harness = false
|
||||
|
||||
31
crypto/kzg/benches/benchmark.rs
Normal file
31
crypto/kzg/benches/benchmark.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use c_kzg::KzgSettings;
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use eth2_network_config::TRUSTED_SETUP_BYTES;
|
||||
use kzg::TrustedSetup;
|
||||
use rust_eth_kzg::{DASContext, TrustedSetup as PeerDASTrustedSetup};
|
||||
|
||||
pub fn bench_init_context(c: &mut Criterion) {
|
||||
let trusted_setup: TrustedSetup = serde_json::from_reader(TRUSTED_SETUP_BYTES)
|
||||
.map_err(|e| format!("Unable to read trusted setup file: {}", e))
|
||||
.expect("should have trusted setup");
|
||||
|
||||
c.bench_function(&format!("Initialize context rust_eth_kzg"), |b| {
|
||||
b.iter(|| {
|
||||
const NUM_THREADS: usize = 1;
|
||||
let trusted_setup = PeerDASTrustedSetup::from(&trusted_setup);
|
||||
DASContext::with_threads(&trusted_setup, NUM_THREADS)
|
||||
})
|
||||
});
|
||||
c.bench_function(&format!("Initialize context c-kzg (4844)"), |b| {
|
||||
b.iter(|| {
|
||||
let trusted_setup: TrustedSetup = serde_json::from_reader(TRUSTED_SETUP_BYTES)
|
||||
.map_err(|e| format!("Unable to read trusted setup file: {}", e))
|
||||
.expect("should have trusted setup");
|
||||
KzgSettings::load_trusted_setup(&trusted_setup.g1_points(), &trusted_setup.g2_points())
|
||||
.unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_init_context);
|
||||
criterion_main!(benches);
|
||||
@@ -2,6 +2,7 @@ mod kzg_commitment;
|
||||
mod kzg_proof;
|
||||
mod trusted_setup;
|
||||
|
||||
use rust_eth_kzg::{CellIndex, DASContext};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub use crate::{
|
||||
@@ -9,18 +10,35 @@ pub use crate::{
|
||||
kzg_proof::KzgProof,
|
||||
trusted_setup::TrustedSetup,
|
||||
};
|
||||
|
||||
pub use c_kzg::{
|
||||
Blob, Bytes32, Bytes48, KzgSettings, BYTES_PER_BLOB, BYTES_PER_COMMITMENT,
|
||||
BYTES_PER_FIELD_ELEMENT, BYTES_PER_PROOF, FIELD_ELEMENTS_PER_BLOB,
|
||||
};
|
||||
|
||||
pub use rust_eth_kzg::{
|
||||
constants::{BYTES_PER_CELL, CELLS_PER_EXT_BLOB},
|
||||
Cell, CellIndex as CellID, CellRef, TrustedSetup as PeerDASTrustedSetup,
|
||||
};
|
||||
|
||||
pub type CellsAndKzgProofs = ([Cell; CELLS_PER_EXT_BLOB], [KzgProof; CELLS_PER_EXT_BLOB]);
|
||||
|
||||
pub type KzgBlobRef<'a> = &'a [u8; BYTES_PER_BLOB];
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// An error from the underlying kzg library.
|
||||
Kzg(c_kzg::Error),
|
||||
/// A prover/verifier error from the rust-eth-kzg library.
|
||||
PeerDASKZG(rust_eth_kzg::Error),
|
||||
/// The kzg verification failed
|
||||
KzgVerificationFailed,
|
||||
/// Misc indexing error
|
||||
InconsistentArrayLength(String),
|
||||
/// Error reconstructing data columns.
|
||||
ReconstructFailed(String),
|
||||
/// Kzg was not initialized with PeerDAS enabled.
|
||||
DASContextUninitialized,
|
||||
}
|
||||
|
||||
impl From<c_kzg::Error> for Error {
|
||||
@@ -29,32 +47,11 @@ impl From<c_kzg::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub const CELLS_PER_EXT_BLOB: usize = 128;
|
||||
|
||||
// TODO(das): use proper crypto once ckzg merges das branch
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Cell {
|
||||
bytes: [u8; 2048usize],
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
pub fn from_bytes(b: &[u8]) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
bytes: b
|
||||
.try_into()
|
||||
.map_err(|_| Error::Kzg(c_kzg::Error::MismatchLength("".to_owned())))?,
|
||||
})
|
||||
}
|
||||
pub fn into_inner(self) -> [u8; 2048usize] {
|
||||
self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper over a kzg library that holds the trusted setup parameters.
|
||||
#[derive(Debug)]
|
||||
pub struct Kzg {
|
||||
trusted_setup: KzgSettings,
|
||||
context: Option<DASContext>,
|
||||
}
|
||||
|
||||
impl Kzg {
|
||||
@@ -65,9 +62,36 @@ impl Kzg {
|
||||
&trusted_setup.g1_points(),
|
||||
&trusted_setup.g2_points(),
|
||||
)?,
|
||||
context: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_from_trusted_setup_das_enabled(trusted_setup: TrustedSetup) -> Result<Self, Error> {
|
||||
// Initialize the trusted setup using default parameters
|
||||
//
|
||||
// Note: One can also use `from_json` to initialize it from the consensus-specs
|
||||
// json string.
|
||||
let peerdas_trusted_setup = PeerDASTrustedSetup::from(&trusted_setup);
|
||||
// Set the number of threads to be used
|
||||
//
|
||||
// we set it to 1 to match the c-kzg performance
|
||||
const NUM_THREADS: usize = 1;
|
||||
|
||||
let context = DASContext::with_threads(&peerdas_trusted_setup, NUM_THREADS);
|
||||
|
||||
Ok(Self {
|
||||
trusted_setup: KzgSettings::load_trusted_setup(
|
||||
&trusted_setup.g1_points(),
|
||||
&trusted_setup.g2_points(),
|
||||
)?,
|
||||
context: Some(context),
|
||||
})
|
||||
}
|
||||
|
||||
fn context(&self) -> Result<&DASContext, Error> {
|
||||
self.context.as_ref().ok_or(Error::DASContextUninitialized)
|
||||
}
|
||||
|
||||
/// Compute the kzg proof given a blob and its kzg commitment.
|
||||
pub fn compute_blob_kzg_proof(
|
||||
&self,
|
||||
@@ -167,21 +191,18 @@ impl Kzg {
|
||||
}
|
||||
|
||||
/// Computes the cells and associated proofs for a given `blob` at index `index`.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn compute_cells_and_proofs(
|
||||
&self,
|
||||
_blob: &Blob,
|
||||
) -> Result<
|
||||
(
|
||||
Box<[Cell; CELLS_PER_EXT_BLOB]>,
|
||||
Box<[KzgProof; CELLS_PER_EXT_BLOB]>,
|
||||
),
|
||||
Error,
|
||||
> {
|
||||
// TODO(das): use proper crypto once ckzg merges das branch
|
||||
let cells = Box::new(core::array::from_fn(|_| Cell { bytes: [0u8; 2048] }));
|
||||
let proofs = Box::new([KzgProof([0u8; BYTES_PER_PROOF]); CELLS_PER_EXT_BLOB]);
|
||||
Ok((cells, proofs))
|
||||
blob: KzgBlobRef<'_>,
|
||||
) -> Result<CellsAndKzgProofs, Error> {
|
||||
let (cells, proofs) = self
|
||||
.context()?
|
||||
.compute_cells_and_kzg_proofs(blob)
|
||||
.map_err(Error::PeerDASKZG)?;
|
||||
|
||||
// Convert the proof type to a c-kzg proof type
|
||||
let c_kzg_proof = proofs.map(KzgProof);
|
||||
Ok((cells, c_kzg_proof))
|
||||
}
|
||||
|
||||
/// Verifies a batch of cell-proof-commitment triplets.
|
||||
@@ -191,35 +212,43 @@ impl Kzg {
|
||||
/// to the data column index.
|
||||
pub fn verify_cell_proof_batch(
|
||||
&self,
|
||||
_cells: &[Cell],
|
||||
_kzg_proofs: &[Bytes48],
|
||||
_coordinates: &[(u64, u64)],
|
||||
_kzg_commitments: &[Bytes48],
|
||||
cells: &[CellRef<'_>],
|
||||
kzg_proofs: &[Bytes48],
|
||||
columns: Vec<CellIndex>,
|
||||
kzg_commitments: &[Bytes48],
|
||||
) -> Result<(), Error> {
|
||||
// TODO(das): use proper crypto once ckzg merges das branch
|
||||
Ok(())
|
||||
let proofs: Vec<_> = kzg_proofs.iter().map(|proof| proof.as_ref()).collect();
|
||||
let commitments: Vec<_> = kzg_commitments
|
||||
.iter()
|
||||
.map(|commitment| commitment.as_ref())
|
||||
.collect();
|
||||
let verification_result = self.context()?.verify_cell_kzg_proof_batch(
|
||||
commitments.to_vec(),
|
||||
columns,
|
||||
cells.to_vec(),
|
||||
proofs.to_vec(),
|
||||
);
|
||||
|
||||
// Modify the result so it matches roughly what the previous method was doing.
|
||||
match verification_result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) if e.invalid_proof() => Err(Error::KzgVerificationFailed),
|
||||
Err(e) => Err(Error::PeerDASKZG(e)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cells_to_blob(&self, _cells: &[Cell; CELLS_PER_EXT_BLOB]) -> Result<Blob, Error> {
|
||||
// TODO(das): use proper crypto once ckzg merges das branch
|
||||
Ok(Blob::new([0u8; 131072usize]))
|
||||
}
|
||||
|
||||
pub fn recover_all_cells(
|
||||
pub fn recover_cells_and_compute_kzg_proofs(
|
||||
&self,
|
||||
_cell_ids: &[u64],
|
||||
_cells: &[Cell],
|
||||
) -> Result<Box<[Cell; CELLS_PER_EXT_BLOB]>, Error> {
|
||||
// TODO(das): use proper crypto once ckzg merges das branch
|
||||
let cells = Box::new(core::array::from_fn(|_| Cell { bytes: [0u8; 2048] }));
|
||||
Ok(cells)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<TrustedSetup> for Kzg {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(trusted_setup: TrustedSetup) -> Result<Self, Self::Error> {
|
||||
Kzg::new_from_trusted_setup(trusted_setup)
|
||||
cell_ids: &[u64],
|
||||
cells: &[CellRef<'_>],
|
||||
) -> Result<CellsAndKzgProofs, Error> {
|
||||
let (cells, proofs) = self
|
||||
.context()?
|
||||
.recover_cells_and_proofs(cell_ids.to_vec(), cells.to_vec())
|
||||
.map_err(Error::PeerDASKZG)?;
|
||||
|
||||
// Convert the proof type to a c-kzg proof type
|
||||
let c_kzg_proof = proofs.map(KzgProof);
|
||||
Ok((cells, c_kzg_proof))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::PeerDASTrustedSetup;
|
||||
use c_kzg::{BYTES_PER_G1_POINT, BYTES_PER_G2_POINT};
|
||||
use serde::{
|
||||
de::{self, Deserializer, Visitor},
|
||||
@@ -43,6 +44,28 @@ impl TrustedSetup {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&TrustedSetup> for PeerDASTrustedSetup {
|
||||
fn from(trusted_setup: &TrustedSetup) -> Self {
|
||||
Self {
|
||||
g1_monomial: trusted_setup
|
||||
.g1_monomial_points
|
||||
.iter()
|
||||
.map(|g1_point| format!("0x{}", hex::encode(g1_point.0)))
|
||||
.collect::<Vec<_>>(),
|
||||
g1_lagrange: trusted_setup
|
||||
.g1_points
|
||||
.iter()
|
||||
.map(|g1_point| format!("0x{}", hex::encode(g1_point.0)))
|
||||
.collect::<Vec<_>>(),
|
||||
g2_monomial: trusted_setup
|
||||
.g2_points
|
||||
.iter()
|
||||
.map(|g2_point| format!("0x{}", hex::encode(g2_point.0)))
|
||||
.collect::<Vec<_>>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for G1Point {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
|
||||
Reference in New Issue
Block a user