mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-30 12:47:05 +00:00
Merge remote-tracking branch 'origin/deneb-free-blobs' into tree-states
This commit is contained in:
26
crypto/kzg/Cargo.toml
Normal file
26
crypto/kzg/Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "kzg"
|
||||
version = "0.1.0"
|
||||
authors = ["Pawan Dhananjay <pawandhananjay@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ethereum_ssz = "0.5.0"
|
||||
ethereum_ssz_derive = "0.5.3"
|
||||
tree_hash = "0.5.2"
|
||||
derivative = "2.1.1"
|
||||
serde = "1.0.116"
|
||||
serde_derive = "1.0.116"
|
||||
ethereum_serde_utils = "0.5.0"
|
||||
hex = "0.4.2"
|
||||
ethereum_hashing = "1.0.0-beta.2"
|
||||
c-kzg = { git = "https://github.com/ethereum/c-kzg-4844", rev = "f5f6f863d475847876a2bd5ee252058d37c3a15d" , features = ["mainnet-spec", "serde"]}
|
||||
c_kzg_min = { package = "c-kzg", git = "https://github.com/ethereum//c-kzg-4844", rev = "f5f6f863d475847876a2bd5ee252058d37c3a15d", features = ["minimal-spec", "serde"], optional = true }
|
||||
arbitrary = { version = "1.0", features = ["derive"] }
|
||||
|
||||
[features]
|
||||
# TODO(deneb): enabled by default for convenience, would need more cfg magic to disable
|
||||
default = ["c_kzg_min"]
|
||||
minimal-spec = ["c_kzg_min"]
|
||||
121
crypto/kzg/src/kzg_commitment.rs
Normal file
121
crypto/kzg/src/kzg_commitment.rs
Normal file
@@ -0,0 +1,121 @@
|
||||
use c_kzg::BYTES_PER_COMMITMENT;
|
||||
use derivative::Derivative;
|
||||
use ethereum_hashing::hash_fixed;
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use std::fmt;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
use tree_hash::{Hash256, PackedEncoding, TreeHash};
|
||||
|
||||
pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
|
||||
|
||||
#[derive(Derivative, Clone, Copy, Encode, Decode)]
|
||||
#[derivative(PartialEq, Eq, Hash)]
|
||||
#[ssz(struct_behaviour = "transparent")]
|
||||
pub struct KzgCommitment(pub [u8; c_kzg::BYTES_PER_COMMITMENT]);
|
||||
|
||||
impl KzgCommitment {
|
||||
pub fn calculate_versioned_hash(&self) -> Hash256 {
|
||||
let mut versioned_hash = hash_fixed(&self.0);
|
||||
versioned_hash[0] = VERSIONED_HASH_VERSION_KZG;
|
||||
Hash256::from_slice(versioned_hash.as_slice())
|
||||
}
|
||||
|
||||
pub fn empty_for_testing() -> Self {
|
||||
KzgCommitment([0; c_kzg::BYTES_PER_COMMITMENT])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KzgCommitment> for c_kzg::Bytes48 {
|
||||
fn from(value: KzgCommitment) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KzgCommitment> for c_kzg_min::Bytes48 {
|
||||
fn from(value: KzgCommitment) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for KzgCommitment {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", serde_utils::hex::encode(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl TreeHash for KzgCommitment {
|
||||
fn tree_hash_type() -> tree_hash::TreeHashType {
|
||||
<[u8; BYTES_PER_COMMITMENT] as TreeHash>::tree_hash_type()
|
||||
}
|
||||
|
||||
fn tree_hash_packed_encoding(&self) -> PackedEncoding {
|
||||
self.0.tree_hash_packed_encoding()
|
||||
}
|
||||
|
||||
fn tree_hash_packing_factor() -> usize {
|
||||
<[u8; BYTES_PER_COMMITMENT] as TreeHash>::tree_hash_packing_factor()
|
||||
}
|
||||
|
||||
fn tree_hash_root(&self) -> tree_hash::Hash256 {
|
||||
self.0.tree_hash_root()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for KzgCommitment {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for KzgCommitment {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let string = String::deserialize(deserializer)?;
|
||||
Self::from_str(&string).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for KzgCommitment {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Some(stripped) = s.strip_prefix("0x") {
|
||||
let bytes = hex::decode(stripped).map_err(|e| e.to_string())?;
|
||||
if bytes.len() == BYTES_PER_COMMITMENT {
|
||||
let mut kzg_commitment_bytes = [0; BYTES_PER_COMMITMENT];
|
||||
kzg_commitment_bytes[..].copy_from_slice(&bytes);
|
||||
Ok(Self(kzg_commitment_bytes))
|
||||
} else {
|
||||
Err(format!(
|
||||
"InvalidByteLength: got {}, expected {}",
|
||||
bytes.len(),
|
||||
BYTES_PER_COMMITMENT
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err("must start with 0x".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for KzgCommitment {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", serde_utils::hex::encode(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl arbitrary::Arbitrary<'_> for KzgCommitment {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
let mut bytes = [0u8; BYTES_PER_COMMITMENT];
|
||||
u.fill_buffer(&mut bytes)?;
|
||||
Ok(KzgCommitment(bytes))
|
||||
}
|
||||
}
|
||||
125
crypto/kzg/src/kzg_proof.rs
Normal file
125
crypto/kzg/src/kzg_proof.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use c_kzg::BYTES_PER_PROOF;
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::str::FromStr;
|
||||
use tree_hash::{PackedEncoding, TreeHash};
|
||||
|
||||
#[derive(PartialEq, Hash, Clone, Copy, Encode, Decode)]
|
||||
#[ssz(struct_behaviour = "transparent")]
|
||||
pub struct KzgProof(pub [u8; BYTES_PER_PROOF]);
|
||||
|
||||
impl From<KzgProof> for c_kzg::Bytes48 {
|
||||
fn from(value: KzgProof) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KzgProof> for c_kzg_min::Bytes48 {
|
||||
fn from(value: KzgProof) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl KzgProof {
|
||||
/// Creates a valid proof using `G1_POINT_AT_INFINITY`.
|
||||
pub fn empty() -> Self {
|
||||
let mut bytes = [0; BYTES_PER_PROOF];
|
||||
bytes[0] = 0xc0;
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for KzgProof {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", serde_utils::hex::encode(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; BYTES_PER_PROOF]> for KzgProof {
|
||||
fn from(bytes: [u8; BYTES_PER_PROOF]) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<[u8; BYTES_PER_PROOF]> for KzgProof {
|
||||
fn into(self) -> [u8; BYTES_PER_PROOF] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl TreeHash for KzgProof {
|
||||
fn tree_hash_type() -> tree_hash::TreeHashType {
|
||||
<[u8; BYTES_PER_PROOF]>::tree_hash_type()
|
||||
}
|
||||
|
||||
fn tree_hash_packed_encoding(&self) -> PackedEncoding {
|
||||
self.0.tree_hash_packed_encoding()
|
||||
}
|
||||
|
||||
fn tree_hash_packing_factor() -> usize {
|
||||
<[u8; BYTES_PER_PROOF]>::tree_hash_packing_factor()
|
||||
}
|
||||
|
||||
fn tree_hash_root(&self) -> tree_hash::Hash256 {
|
||||
self.0.tree_hash_root()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for KzgProof {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for KzgProof {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let string = String::deserialize(deserializer)?;
|
||||
Self::from_str(&string).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for KzgProof {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Some(stripped) = s.strip_prefix("0x") {
|
||||
let bytes = hex::decode(stripped).map_err(|e| e.to_string())?;
|
||||
if bytes.len() == BYTES_PER_PROOF {
|
||||
let mut kzg_proof_bytes = [0; BYTES_PER_PROOF];
|
||||
kzg_proof_bytes[..].copy_from_slice(&bytes);
|
||||
Ok(Self(kzg_proof_bytes))
|
||||
} else {
|
||||
Err(format!(
|
||||
"InvalidByteLength: got {}, expected {}",
|
||||
bytes.len(),
|
||||
BYTES_PER_PROOF
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err("must start with 0x".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for KzgProof {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", serde_utils::hex::encode(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl arbitrary::Arbitrary<'_> for KzgProof {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
let mut bytes = [0u8; BYTES_PER_PROOF];
|
||||
u.fill_buffer(&mut bytes)?;
|
||||
Ok(KzgProof(bytes))
|
||||
}
|
||||
}
|
||||
360
crypto/kzg/src/lib.rs
Normal file
360
crypto/kzg/src/lib.rs
Normal file
@@ -0,0 +1,360 @@
|
||||
mod kzg_commitment;
|
||||
mod kzg_proof;
|
||||
mod trusted_setup;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub use crate::{kzg_commitment::KzgCommitment, kzg_proof::KzgProof, trusted_setup::TrustedSetup};
|
||||
pub use c_kzg::{Bytes32, Bytes48, BYTES_PER_COMMITMENT, BYTES_PER_FIELD_ELEMENT, BYTES_PER_PROOF};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
InvalidTrustedSetup(CryptoError),
|
||||
InvalidKzgProof(CryptoError),
|
||||
InvalidBytes(CryptoError),
|
||||
KzgProofComputationFailed(CryptoError),
|
||||
InvalidBlob(CryptoError),
|
||||
InvalidBytesForBlob(CryptoError),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CryptoError {
|
||||
CKzg(c_kzg::Error),
|
||||
CKzgMin(c_kzg_min::Error),
|
||||
/// Trusted setup is for the incorrect kzg preset.
|
||||
InconsistentTrustedSetup,
|
||||
}
|
||||
|
||||
impl From<c_kzg::Error> for CryptoError {
|
||||
fn from(e: c_kzg::Error) -> Self {
|
||||
Self::CKzg(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<c_kzg_min::Error> for CryptoError {
|
||||
fn from(e: c_kzg_min::Error) -> Self {
|
||||
Self::CKzgMin(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BlobTrait: Sized + Clone {
|
||||
fn from_bytes(bytes: &[u8]) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
pub enum KzgPresetId {
|
||||
Mainnet,
|
||||
Minimal,
|
||||
}
|
||||
|
||||
impl FromStr for KzgPresetId {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"mainnet" => Ok(KzgPresetId::Mainnet),
|
||||
"minimal" => Ok(KzgPresetId::Minimal),
|
||||
_ => Err(format!("Unknown eth spec: {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait KzgPreset:
|
||||
'static + Default + Sync + Send + Clone + Debug + PartialEq + Eq + for<'a> arbitrary::Arbitrary<'a>
|
||||
{
|
||||
type KzgSettings: Debug + Sync + Send;
|
||||
type Blob: BlobTrait;
|
||||
type Bytes32: From<[u8; 32]> + Deref<Target = [u8; 32]>;
|
||||
type Bytes48: From<KzgCommitment> + From<KzgProof>;
|
||||
type Error: Into<CryptoError>;
|
||||
|
||||
const BYTES_PER_BLOB: usize;
|
||||
const FIELD_ELEMENTS_PER_BLOB: usize;
|
||||
|
||||
fn spec_name() -> KzgPresetId;
|
||||
|
||||
fn bytes32_in(bytes: Bytes32) -> Self::Bytes32 {
|
||||
let bytes: [u8; 32] = *bytes;
|
||||
Self::Bytes32::from(bytes)
|
||||
}
|
||||
|
||||
fn bytes32_out(bytes: Self::Bytes32) -> Bytes32 {
|
||||
let bytes: [u8; 32] = *bytes;
|
||||
Bytes32::from(bytes)
|
||||
}
|
||||
|
||||
fn load_trusted_setup(trusted_setup: TrustedSetup) -> Result<Self::KzgSettings, CryptoError>;
|
||||
|
||||
fn compute_blob_kzg_proof(
|
||||
blob: &Self::Blob,
|
||||
kzg_commitment: KzgCommitment,
|
||||
trusted_setup: &Self::KzgSettings,
|
||||
) -> Result<KzgProof, CryptoError>;
|
||||
|
||||
fn verify_blob_kzg_proof(
|
||||
blob: &Self::Blob,
|
||||
kzg_commitment: KzgCommitment,
|
||||
kzg_proof: KzgProof,
|
||||
trusted_setup: &Self::KzgSettings,
|
||||
) -> Result<bool, CryptoError>;
|
||||
|
||||
fn verify_blob_kzg_proof_batch(
|
||||
blobs: &[Self::Blob],
|
||||
commitments_bytes: &[Self::Bytes48],
|
||||
proofs_bytes: &[Self::Bytes48],
|
||||
trusted_setup: &Self::KzgSettings,
|
||||
) -> Result<bool, CryptoError>;
|
||||
|
||||
fn blob_to_kzg_commitment(
|
||||
blob: &Self::Blob,
|
||||
trusted_setup: &Self::KzgSettings,
|
||||
) -> Result<KzgCommitment, CryptoError>;
|
||||
|
||||
fn compute_kzg_proof(
|
||||
blob: &Self::Blob,
|
||||
z: &Self::Bytes32,
|
||||
trusted_setup: &Self::KzgSettings,
|
||||
) -> Result<(KzgProof, Self::Bytes32), CryptoError>;
|
||||
|
||||
fn verify_kzg_proof(
|
||||
kzg_commitment: KzgCommitment,
|
||||
z: &Self::Bytes32,
|
||||
y: &Self::Bytes32,
|
||||
kzg_proof: KzgProof,
|
||||
trusted_setup: &Self::KzgSettings,
|
||||
) -> Result<bool, CryptoError>;
|
||||
}
|
||||
|
||||
macro_rules! implement_kzg_preset {
|
||||
($preset_type:ident, $module_name:ident, $preset_id:ident) => {
|
||||
impl KzgPreset for $preset_type {
|
||||
type KzgSettings = $module_name::KzgSettings;
|
||||
type Blob = $module_name::Blob;
|
||||
type Bytes32 = $module_name::Bytes32;
|
||||
type Bytes48 = $module_name::Bytes48;
|
||||
type Error = $module_name::Error;
|
||||
|
||||
const BYTES_PER_BLOB: usize = $module_name::BYTES_PER_BLOB;
|
||||
const FIELD_ELEMENTS_PER_BLOB: usize = $module_name::FIELD_ELEMENTS_PER_BLOB;
|
||||
|
||||
fn spec_name() -> KzgPresetId {
|
||||
KzgPresetId::$preset_id
|
||||
}
|
||||
|
||||
fn load_trusted_setup(
|
||||
trusted_setup: TrustedSetup,
|
||||
) -> Result<Self::KzgSettings, CryptoError> {
|
||||
if trusted_setup.g1_len() != Self::FIELD_ELEMENTS_PER_BLOB {
|
||||
return Err(CryptoError::InconsistentTrustedSetup);
|
||||
}
|
||||
$module_name::KzgSettings::load_trusted_setup(
|
||||
&trusted_setup.g1_points(),
|
||||
&trusted_setup.g2_points(),
|
||||
)
|
||||
.map_err(CryptoError::from)
|
||||
}
|
||||
|
||||
fn compute_blob_kzg_proof(
|
||||
blob: &Self::Blob,
|
||||
kzg_commitment: KzgCommitment,
|
||||
trusted_setup: &Self::KzgSettings,
|
||||
) -> Result<KzgProof, CryptoError> {
|
||||
$module_name::KzgProof::compute_blob_kzg_proof(
|
||||
blob,
|
||||
&kzg_commitment.into(),
|
||||
trusted_setup,
|
||||
)
|
||||
.map(|proof| KzgProof(proof.to_bytes().into_inner()))
|
||||
.map_err(CryptoError::from)
|
||||
}
|
||||
|
||||
fn verify_blob_kzg_proof(
|
||||
blob: &Self::Blob,
|
||||
kzg_commitment: KzgCommitment,
|
||||
kzg_proof: KzgProof,
|
||||
trusted_setup: &Self::KzgSettings,
|
||||
) -> Result<bool, CryptoError> {
|
||||
$module_name::KzgProof::verify_blob_kzg_proof(
|
||||
blob,
|
||||
&kzg_commitment.into(),
|
||||
&kzg_proof.into(),
|
||||
trusted_setup,
|
||||
)
|
||||
.map_err(CryptoError::from)
|
||||
}
|
||||
|
||||
fn verify_blob_kzg_proof_batch(
|
||||
blobs: &[Self::Blob],
|
||||
commitments_bytes: &[Self::Bytes48],
|
||||
proofs_bytes: &[Self::Bytes48],
|
||||
trusted_setup: &Self::KzgSettings,
|
||||
) -> Result<bool, CryptoError> {
|
||||
$module_name::KzgProof::verify_blob_kzg_proof_batch(
|
||||
blobs,
|
||||
commitments_bytes,
|
||||
proofs_bytes,
|
||||
trusted_setup,
|
||||
)
|
||||
.map_err(CryptoError::from)
|
||||
}
|
||||
|
||||
fn blob_to_kzg_commitment(
|
||||
blob: &Self::Blob,
|
||||
trusted_setup: &Self::KzgSettings,
|
||||
) -> Result<KzgCommitment, CryptoError> {
|
||||
$module_name::KzgCommitment::blob_to_kzg_commitment(blob, trusted_setup)
|
||||
.map(|com| KzgCommitment(com.to_bytes().into_inner()))
|
||||
.map_err(CryptoError::from)
|
||||
}
|
||||
|
||||
fn compute_kzg_proof(
|
||||
blob: &Self::Blob,
|
||||
z: &Self::Bytes32,
|
||||
trusted_setup: &Self::KzgSettings,
|
||||
) -> Result<(KzgProof, Self::Bytes32), CryptoError> {
|
||||
$module_name::KzgProof::compute_kzg_proof(blob, z, trusted_setup)
|
||||
.map(|(proof, y)| (KzgProof(proof.to_bytes().into_inner()), y))
|
||||
.map_err(CryptoError::from)
|
||||
}
|
||||
|
||||
fn verify_kzg_proof(
|
||||
kzg_commitment: KzgCommitment,
|
||||
z: &Self::Bytes32,
|
||||
y: &Self::Bytes32,
|
||||
kzg_proof: KzgProof,
|
||||
trusted_setup: &Self::KzgSettings,
|
||||
) -> Result<bool, CryptoError> {
|
||||
$module_name::KzgProof::verify_kzg_proof(
|
||||
&kzg_commitment.into(),
|
||||
z,
|
||||
y,
|
||||
&kzg_proof.into(),
|
||||
trusted_setup,
|
||||
)
|
||||
.map_err(CryptoError::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl BlobTrait for $module_name::Blob {
|
||||
fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
|
||||
Self::from_bytes(bytes)
|
||||
.map_err(CryptoError::from)
|
||||
.map_err(Error::InvalidBlob)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, arbitrary::Arbitrary)]
|
||||
pub struct MainnetKzgPreset;
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, arbitrary::Arbitrary)]
|
||||
pub struct MinimalKzgPreset;
|
||||
|
||||
implement_kzg_preset!(MainnetKzgPreset, c_kzg, Mainnet);
|
||||
implement_kzg_preset!(MinimalKzgPreset, c_kzg_min, Minimal);
|
||||
|
||||
/// A wrapper over a kzg library that holds the trusted setup parameters.
|
||||
#[derive(Debug)]
|
||||
pub struct Kzg<P: KzgPreset> {
|
||||
trusted_setup: P::KzgSettings,
|
||||
}
|
||||
|
||||
impl<P: KzgPreset> Kzg<P> {
|
||||
/// Load the kzg trusted setup parameters from a vec of G1 and G2 points.
|
||||
///
|
||||
/// The number of G1 points should be equal to FIELD_ELEMENTS_PER_BLOB
|
||||
/// Note: this number changes based on the preset values.
|
||||
/// The number of G2 points should be equal to 65.
|
||||
pub fn new_from_trusted_setup(trusted_setup: TrustedSetup) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
trusted_setup: P::load_trusted_setup(trusted_setup)
|
||||
.map_err(Error::InvalidTrustedSetup)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute the kzg proof given a blob and its kzg commitment.
|
||||
pub fn compute_blob_kzg_proof(
|
||||
&self,
|
||||
blob: &P::Blob,
|
||||
kzg_commitment: KzgCommitment,
|
||||
) -> Result<KzgProof, Error> {
|
||||
P::compute_blob_kzg_proof(blob, kzg_commitment, &self.trusted_setup)
|
||||
.map_err(Error::KzgProofComputationFailed)
|
||||
}
|
||||
|
||||
/// Verify a kzg proof given the blob, kzg commitment and kzg proof.
|
||||
pub fn verify_blob_kzg_proof(
|
||||
&self,
|
||||
blob: &P::Blob,
|
||||
kzg_commitment: KzgCommitment,
|
||||
kzg_proof: KzgProof,
|
||||
) -> Result<bool, Error> {
|
||||
P::verify_blob_kzg_proof(blob, kzg_commitment, kzg_proof, &self.trusted_setup)
|
||||
.map_err(Error::InvalidKzgProof)
|
||||
}
|
||||
|
||||
/// Verify a batch of blob commitment proof triplets.
|
||||
///
|
||||
/// Note: This method is slightly faster than calling `Self::verify_blob_kzg_proof` in a loop sequentially.
|
||||
/// TODO(pawan): test performance against a parallelized rayon impl.
|
||||
pub fn verify_blob_kzg_proof_batch(
|
||||
&self,
|
||||
blobs: &[P::Blob],
|
||||
kzg_commitments: &[KzgCommitment],
|
||||
kzg_proofs: &[KzgProof],
|
||||
) -> Result<bool, Error> {
|
||||
let commitments_bytes = kzg_commitments
|
||||
.iter()
|
||||
.map(|comm| P::Bytes48::from(*comm))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let proofs_bytes = kzg_proofs
|
||||
.iter()
|
||||
.map(|proof| P::Bytes48::from(*proof))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
P::verify_blob_kzg_proof_batch(
|
||||
blobs,
|
||||
&commitments_bytes,
|
||||
&proofs_bytes,
|
||||
&self.trusted_setup,
|
||||
)
|
||||
.map_err(Error::InvalidKzgProof)
|
||||
}
|
||||
|
||||
/// Converts a blob to a kzg commitment.
|
||||
pub fn blob_to_kzg_commitment(&self, blob: &P::Blob) -> Result<KzgCommitment, Error> {
|
||||
P::blob_to_kzg_commitment(blob, &self.trusted_setup).map_err(Error::InvalidBlob)
|
||||
}
|
||||
|
||||
/// Computes the kzg proof for a given `blob` and an evaluation point `z`
|
||||
pub fn compute_kzg_proof(
|
||||
&self,
|
||||
blob: &P::Blob,
|
||||
z: &Bytes32,
|
||||
) -> Result<(KzgProof, Bytes32), Error> {
|
||||
P::compute_kzg_proof(blob, &P::bytes32_in(*z), &self.trusted_setup)
|
||||
.map_err(Error::KzgProofComputationFailed)
|
||||
.map(|(proof, y)| (proof, P::bytes32_out(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> {
|
||||
P::verify_kzg_proof(
|
||||
kzg_commitment,
|
||||
&P::bytes32_in(*z),
|
||||
&P::bytes32_in(*y),
|
||||
kzg_proof,
|
||||
&self.trusted_setup,
|
||||
)
|
||||
.map_err(Error::InvalidKzgProof)
|
||||
}
|
||||
}
|
||||
142
crypto/kzg/src/trusted_setup.rs
Normal file
142
crypto/kzg/src/trusted_setup.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use c_kzg::{BYTES_PER_G1_POINT, BYTES_PER_G2_POINT};
|
||||
use serde::{
|
||||
de::{self, Deserializer, Visitor},
|
||||
Deserialize, Serialize,
|
||||
};
|
||||
|
||||
/// Wrapper over a BLS G1 point's byte representation.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
struct G1Point([u8; BYTES_PER_G1_POINT]);
|
||||
|
||||
/// Wrapper over a BLS G2 point's byte representation.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
struct G2Point([u8; BYTES_PER_G2_POINT]);
|
||||
|
||||
/// Contains the trusted setup parameters that are required to instantiate a
|
||||
/// `c_kzg::KzgSettings` object.
|
||||
///
|
||||
/// The serialize/deserialize implementations are written according to
|
||||
/// the format specified in the the ethereum consensus specs trusted setup files.
|
||||
///
|
||||
/// See https://github.com/ethereum/consensus-specs/blob/dev/presets/mainnet/trusted_setups/testing_trusted_setups.json
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct TrustedSetup {
|
||||
#[serde(rename = "setup_G1_lagrange")]
|
||||
g1_points: Vec<G1Point>,
|
||||
#[serde(rename = "setup_G2")]
|
||||
g2_points: Vec<G2Point>,
|
||||
}
|
||||
|
||||
impl TrustedSetup {
|
||||
pub fn g1_points(&self) -> Vec<[u8; BYTES_PER_G1_POINT]> {
|
||||
self.g1_points.iter().map(|p| p.0).collect()
|
||||
}
|
||||
|
||||
pub fn g2_points(&self) -> Vec<[u8; BYTES_PER_G2_POINT]> {
|
||||
self.g2_points.iter().map(|p| p.0).collect()
|
||||
}
|
||||
|
||||
pub fn g1_len(&self) -> usize {
|
||||
self.g1_points.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for G1Point {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let point = hex::encode(self.0);
|
||||
serializer.serialize_str(&point)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for G2Point {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let point = hex::encode(self.0);
|
||||
serializer.serialize_str(&point)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for G1Point {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct G1PointVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for G1PointVisitor {
|
||||
type Value = G1Point;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("A 48 byte hex encoded string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
let point = hex::decode(strip_prefix(v))
|
||||
.map_err(|e| de::Error::custom(format!("Failed to decode G1 point: {}", e)))?;
|
||||
if point.len() != BYTES_PER_G1_POINT {
|
||||
return Err(de::Error::custom(format!(
|
||||
"G1 point has invalid length. Expected {} got {}",
|
||||
BYTES_PER_G1_POINT,
|
||||
point.len()
|
||||
)));
|
||||
}
|
||||
let mut res = [0; BYTES_PER_G1_POINT];
|
||||
res.copy_from_slice(&point);
|
||||
Ok(G1Point(res))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(G1PointVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for G2Point {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct G2PointVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for G2PointVisitor {
|
||||
type Value = G2Point;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("A 96 byte hex encoded string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
let point = hex::decode(strip_prefix(v))
|
||||
.map_err(|e| de::Error::custom(format!("Failed to decode G2 point: {}", e)))?;
|
||||
if point.len() != BYTES_PER_G2_POINT {
|
||||
return Err(de::Error::custom(format!(
|
||||
"G2 point has invalid length. Expected {} got {}",
|
||||
BYTES_PER_G2_POINT,
|
||||
point.len()
|
||||
)));
|
||||
}
|
||||
let mut res = [0; BYTES_PER_G2_POINT];
|
||||
res.copy_from_slice(&point);
|
||||
Ok(G2Point(res))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(G2PointVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_prefix(s: &str) -> &str {
|
||||
if let Some(stripped) = s.strip_prefix("0x") {
|
||||
stripped
|
||||
} else {
|
||||
s
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user