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

Implement KZG EF Tests #4274

Merged
merged 1 commit into from
May 8, 2023
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
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 23 additions & 1 deletion beacon_node/beacon_chain/src/kzg_utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use kzg::{Error as KzgError, Kzg, BYTES_PER_BLOB};
use types::{Blob, EthSpec, KzgCommitment, KzgProof};
use types::{Blob, EthSpec, Hash256, KzgCommitment, KzgProof};

/// Converts a blob ssz List object to an array to be used with the kzg
/// crypto library.
Expand Down Expand Up @@ -56,3 +56,25 @@ pub fn blob_to_kzg_commitment<T: EthSpec>(
) -> Result<KzgCommitment, KzgError> {
kzg.blob_to_kzg_commitment(ssz_blob_to_crypto_blob::<T>(blob))
}

/// Compute the kzg proof for a given blob and an evaluation point z.
pub fn compute_kzg_proof<T: EthSpec>(
kzg: &Kzg,
blob: Blob<T>,
z: Hash256,
) -> Result<(KzgProof, Hash256), KzgError> {
let z = z.0.into();
kzg.compute_kzg_proof(ssz_blob_to_crypto_blob::<T>(blob), z)
.map(|(proof, z)| (proof, Hash256::from_slice(&z.to_vec())))
}

/// Verify a `kzg_proof` for a `kzg_commitment` that evaluating a polynomial at `z` results in `y`
pub fn verify_kzg_proof<T: EthSpec>(
kzg: &Kzg,
kzg_commitment: KzgCommitment,
kzg_proof: KzgProof,
z: Hash256,
y: Hash256,
) -> Result<bool, KzgError> {
kzg.verify_kzg_proof(kzg_commitment, z.0.into(), y.0.into(), kzg_proof)
}
27 changes: 26 additions & 1 deletion crypto/kzg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ mod kzg_proof;
mod trusted_setup;

pub use crate::{kzg_commitment::KzgCommitment, kzg_proof::KzgProof, trusted_setup::TrustedSetup};
use c_kzg::Bytes48;
pub use c_kzg::{
Blob, Error as CKzgError, KzgSettings, BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT,
FIELD_ELEMENTS_PER_BLOB,
};
use c_kzg::{Bytes32, Bytes48};
use std::path::PathBuf;

#[derive(Debug)]
Expand Down Expand Up @@ -116,4 +116,29 @@ impl Kzg {
.map_err(Error::InvalidBlob)
.map(|com| KzgCommitment(com.to_bytes().into_inner()))
}

/// Computes the kzg proof for a given `blob` and an evaluation point `z`
pub fn compute_kzg_proof(&self, blob: Blob, z: Bytes32) -> Result<(KzgProof, Bytes32), Error> {
c_kzg::KzgProof::compute_kzg_proof(blob, z, &self.trusted_setup)
.map_err(Error::KzgProofComputationFailed)
.map(|(proof, y)| (KzgProof(proof.to_bytes().into_inner()), y))
}

/// Verifies a `kzg_proof` for a `kzg_commitment` that evaluating a polynomial at `z` results in `y`
pub fn verify_kzg_proof(
&self,
kzg_commitment: KzgCommitment,
z: Bytes32,
y: Bytes32,
kzg_proof: KzgProof,
) -> Result<bool, Error> {
c_kzg::KzgProof::verify_kzg_proof(
kzg_commitment.into(),
z,
y,
kzg_proof.into(),
&self.trusted_setup,
)
.map_err(Error::InvalidKzgProof)
}
}
4 changes: 4 additions & 0 deletions testing/ef_tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ compare_fields_derive = { path = "../../common/compare_fields_derive" }
derivative = "2.1.1"
ethereum-types = "0.14.1"
hex = "0.4.2"
kzg = { path = "../../crypto/kzg" }
rayon = "1.4.1"
serde = "1.0.116"
serde_derive = "1.0.116"
serde_json = "1.0.58"
serde_repr = "0.1.6"
serde_yaml = "0.8.13"
eth2_network_config = { path = "../../common/eth2_network_config" }
eth2_serde_utils = { path = "../../consensus/serde_utils" }
eth2_ssz = "0.4.1"
eth2_ssz_derive = "0.3.1"
tree_hash = "0.4.1"
Expand Down
12 changes: 12 additions & 0 deletions testing/ef_tests/src/cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ mod fork;
mod fork_choice;
mod genesis_initialization;
mod genesis_validity;
mod kzg_blob_to_kzg_commitment;
mod kzg_compute_blob_kzg_proof;
mod kzg_compute_kzg_proof;
mod kzg_verify_blob_kzg_proof;
mod kzg_verify_blob_kzg_proof_batch;
mod kzg_verify_kzg_proof;
mod merkle_proof_validity;
mod operations;
mod rewards;
Expand All @@ -42,6 +48,12 @@ pub use epoch_processing::*;
pub use fork::ForkTest;
pub use genesis_initialization::*;
pub use genesis_validity::*;
pub use kzg_blob_to_kzg_commitment::*;
pub use kzg_compute_blob_kzg_proof::*;
pub use kzg_compute_kzg_proof::*;
pub use kzg_verify_blob_kzg_proof::*;
pub use kzg_verify_blob_kzg_proof_batch::*;
pub use kzg_verify_kzg_proof::*;
pub use merkle_proof_validity::*;
pub use operations::*;
pub use rewards::RewardsTest;
Expand Down
46 changes: 46 additions & 0 deletions testing/ef_tests/src/cases/kzg_blob_to_kzg_commitment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use super::*;
use crate::case_result::compare_result;
use beacon_chain::kzg_utils::blob_to_kzg_commitment;
use kzg::KzgCommitment;
use serde_derive::Deserialize;
use std::marker::PhantomData;

#[derive(Debug, Clone, Deserialize)]
pub struct KZGBlobToKZGCommitmentInput {
pub blob: String,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct KZGBlobToKZGCommitment<E: EthSpec> {
pub input: KZGBlobToKZGCommitmentInput,
pub output: Option<String>,
#[serde(skip)]
_phantom: PhantomData<E>,
}

impl<E: EthSpec> LoadCase for KZGBlobToKZGCommitment<E> {
fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result<Self, Error> {
decode::yaml_decode_file(path.join("data.yaml").as_path())
}
}

impl<E: EthSpec> Case for KZGBlobToKZGCommitment<E> {
fn is_enabled_for_fork(fork_name: ForkName) -> bool {
fork_name == ForkName::Deneb
}

fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> {
let kzg = get_kzg()?;

let commitment = parse_blob::<E>(&self.input.blob).and_then(|blob| {
blob_to_kzg_commitment::<E>(&kzg, blob).map_err(|e| {
Error::InternalError(format!("Failed to compute kzg commitment: {:?}", e))
})
});

let expected = self.output.as_ref().and_then(|s| parse_commitment(s).ok());

compare_result::<KzgCommitment, _>(&commitment, &expected)
}
}
51 changes: 51 additions & 0 deletions testing/ef_tests/src/cases/kzg_compute_blob_kzg_proof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use super::*;
use crate::case_result::compare_result;
use beacon_chain::kzg_utils::compute_blob_kzg_proof;
use kzg::KzgProof;
use serde_derive::Deserialize;
use std::marker::PhantomData;

#[derive(Debug, Clone, Deserialize)]
pub struct KZGComputeBlobKZGProofInput {
pub blob: String,
pub commitment: String,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct KZGComputeBlobKZGProof<E: EthSpec> {
pub input: KZGComputeBlobKZGProofInput,
pub output: Option<String>,
#[serde(skip)]
_phantom: PhantomData<E>,
}

impl<E: EthSpec> LoadCase for KZGComputeBlobKZGProof<E> {
fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result<Self, Error> {
decode::yaml_decode_file(path.join("data.yaml").as_path())
}
}

impl<E: EthSpec> Case for KZGComputeBlobKZGProof<E> {
fn is_enabled_for_fork(fork_name: ForkName) -> bool {
fork_name == ForkName::Deneb
}

fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> {
let parse_input = |input: &KZGComputeBlobKZGProofInput| -> Result<_, Error> {
let blob = parse_blob::<E>(&input.blob)?;
let commitment = parse_commitment(&input.commitment)?;
Ok((blob, commitment))
};

let kzg = get_kzg()?;
let proof = parse_input(&self.input).and_then(|(blob, commitment)| {
compute_blob_kzg_proof::<E>(&kzg, &blob, commitment)
.map_err(|e| Error::InternalError(format!("Failed to compute kzg proof: {:?}", e)))
});

let expected = self.output.as_ref().and_then(|s| parse_proof(s).ok());

compare_result::<KzgProof, _>(&proof, &expected)
}
}
61 changes: 61 additions & 0 deletions testing/ef_tests/src/cases/kzg_compute_kzg_proof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use super::*;
use crate::case_result::compare_result;
use beacon_chain::kzg_utils::compute_kzg_proof;
use kzg::KzgProof;
use serde_derive::Deserialize;
use std::marker::PhantomData;
use std::str::FromStr;
use types::Hash256;

pub fn parse_point(point: &str) -> Result<Hash256, Error> {
Hash256::from_str(&point[2..])
.map_err(|e| Error::FailedToParseTest(format!("Failed to parse point: {:?}", e)))
}

#[derive(Debug, Clone, Deserialize)]
pub struct KZGComputeKZGProofInput {
pub blob: String,
pub z: String,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct KZGComputeKZGProof<E: EthSpec> {
pub input: KZGComputeKZGProofInput,
pub output: Option<(String, Hash256)>,
#[serde(skip)]
_phantom: PhantomData<E>,
}

impl<E: EthSpec> LoadCase for KZGComputeKZGProof<E> {
fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result<Self, Error> {
decode::yaml_decode_file(path.join("data.yaml").as_path())
}
}

impl<E: EthSpec> Case for KZGComputeKZGProof<E> {
fn is_enabled_for_fork(fork_name: ForkName) -> bool {
fork_name == ForkName::Deneb
}

fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> {
let parse_input = |input: &KZGComputeKZGProofInput| -> Result<_, Error> {
let blob = parse_blob::<E>(&input.blob)?;
let z = parse_point(&input.z)?;
Ok((blob, z))
};

let kzg = get_kzg()?;
let proof = parse_input(&self.input).and_then(|(blob, z)| {
compute_kzg_proof::<E>(&kzg, blob, z)
.map_err(|e| Error::InternalError(format!("Failed to compute kzg proof: {:?}", e)))
});

let expected = self
.output
.as_ref()
.and_then(|(s, z)| parse_proof(s).ok().map(|proof| (proof, *z)));

compare_result::<(KzgProof, Hash256), _>(&proof, &expected)
}
}
92 changes: 92 additions & 0 deletions testing/ef_tests/src/cases/kzg_verify_blob_kzg_proof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use super::*;
use crate::case_result::compare_result;
use beacon_chain::kzg_utils::validate_blob;
use eth2_network_config::TRUSTED_SETUP;
use kzg::{Kzg, KzgCommitment, KzgProof, TrustedSetup};
use serde_derive::Deserialize;
use std::convert::TryInto;
use std::marker::PhantomData;
use types::Blob;

pub fn get_kzg() -> Result<Kzg, Error> {
let trusted_setup: TrustedSetup = serde_json::from_reader(TRUSTED_SETUP)
.map_err(|e| Error::InternalError(format!("Failed to initialize kzg: {:?}", e)))?;
Kzg::new_from_trusted_setup(trusted_setup)
.map_err(|e| Error::InternalError(format!("Failed to initialize kzg: {:?}", e)))
}

pub fn parse_proof(proof: &str) -> Result<KzgProof, Error> {
hex::decode(&proof[2..])
.map_err(|e| Error::FailedToParseTest(format!("Failed to parse proof: {:?}", e)))
.and_then(|bytes| {
bytes
.try_into()
.map_err(|e| Error::FailedToParseTest(format!("Failed to parse proof: {:?}", e)))
})
.map(KzgProof)
}

pub fn parse_commitment(commitment: &str) -> Result<KzgCommitment, Error> {
hex::decode(&commitment[2..])
.map_err(|e| Error::FailedToParseTest(format!("Failed to parse commitment: {:?}", e)))
.and_then(|bytes| {
bytes.try_into().map_err(|e| {
Error::FailedToParseTest(format!("Failed to parse commitment: {:?}", e))
})
})
.map(KzgCommitment)
}

pub fn parse_blob<E: EthSpec>(blob: &str) -> Result<Blob<E>, Error> {
hex::decode(&blob[2..])
.map_err(|e| Error::FailedToParseTest(format!("Failed to parse blob: {:?}", e)))
.and_then(|bytes| {
Blob::<E>::new(bytes)
.map_err(|e| Error::FailedToParseTest(format!("Failed to parse blob: {:?}", e)))
})
}

#[derive(Debug, Clone, Deserialize)]
pub struct KZGVerifyBlobKZGProofInput {
pub blob: String,
pub commitment: String,
pub proof: String,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct KZGVerifyBlobKZGProof<E: EthSpec> {
pub input: KZGVerifyBlobKZGProofInput,
pub output: Option<bool>,
#[serde(skip)]
_phantom: PhantomData<E>,
}

impl<E: EthSpec> LoadCase for KZGVerifyBlobKZGProof<E> {
fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result<Self, Error> {
decode::yaml_decode_file(path.join("data.yaml").as_path())
}
}

impl<E: EthSpec> Case for KZGVerifyBlobKZGProof<E> {
fn is_enabled_for_fork(fork_name: ForkName) -> bool {
fork_name == ForkName::Deneb
}

fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> {
let parse_input = |input: &KZGVerifyBlobKZGProofInput| -> Result<(Blob<E>, KzgCommitment, KzgProof), Error> {
let blob = parse_blob::<E>(&input.blob)?;
let commitment = parse_commitment(&input.commitment)?;
let proof = parse_proof(&input.proof)?;
Ok((blob, commitment, proof))
};

let kzg = get_kzg()?;
let result = parse_input(&self.input).and_then(|(blob, commitment, proof)| {
validate_blob::<E>(&kzg, blob, commitment, proof)
.map_err(|e| Error::InternalError(format!("Failed to validate blob: {:?}", e)))
});

compare_result::<bool, _>(&result, &self.output)
}
}
Loading