mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-08 17:26:04 +00:00
Embed trusted setup in network config (#3851)
* Load trusted setup in network config * Fix trusted setup serialize and deserialize * Load trusted setup from hardcoded preset instead of a file * Truncate after deserialising trusted setup * Fix beacon node script * Remove hardcoded setup file * Add length checks
This commit is contained in:
@@ -1,20 +1,17 @@
|
||||
mod kzg_commitment;
|
||||
mod kzg_proof;
|
||||
mod trusted_setup;
|
||||
|
||||
pub use crate::{kzg_commitment::KzgCommitment, kzg_proof::KzgProof};
|
||||
pub use crate::{kzg_commitment::KzgCommitment, kzg_proof::KzgProof, trusted_setup::TrustedSetup};
|
||||
pub use c_kzg::bytes_to_g1;
|
||||
pub use c_kzg::{
|
||||
Error as CKzgError, KZGSettings, BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT,
|
||||
Blob, Error as CKzgError, KzgSettings, BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT,
|
||||
FIELD_ELEMENTS_PER_BLOB,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// The consensus type `Blob` is generic over EthSpec, so it cannot be imported
|
||||
/// in this crate without creating a cyclic dependency between the kzg and consensus/types crates.
|
||||
/// So need to use a Vec here unless we think of a smarter way of doing this
|
||||
type Blob = [u8; BYTES_PER_BLOB];
|
||||
|
||||
#[derive(Debug)]
|
||||
/// TODO(pawan): add docs after the c_kzg interface changes to bytes only.
|
||||
pub enum Error {
|
||||
InvalidTrustedSetup(CKzgError),
|
||||
InvalidKzgCommitment(CKzgError),
|
||||
@@ -27,23 +24,46 @@ pub enum Error {
|
||||
|
||||
/// A wrapper over a kzg library that holds the trusted setup parameters.
|
||||
pub struct Kzg {
|
||||
trusted_setup: KZGSettings,
|
||||
trusted_setup: KzgSettings,
|
||||
}
|
||||
|
||||
impl Kzg {
|
||||
/// 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: KzgSettings::load_trusted_setup(
|
||||
trusted_setup.g1_points(),
|
||||
trusted_setup.g2_points(),
|
||||
)
|
||||
.map_err(Error::InvalidTrustedSetup)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Loads a trusted setup given the path to the file containing the trusted setup values.
|
||||
/// The format is specified in `c_kzg::KzgSettings::load_trusted_setup_file`.
|
||||
///
|
||||
/// Note: This function will likely be deprecated. Use `Kzg::new_from_trusted_setup` instead.
|
||||
#[deprecated]
|
||||
pub fn new_from_file(file_path: PathBuf) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
trusted_setup: KZGSettings::load_trusted_setup_file(file_path)
|
||||
trusted_setup: KzgSettings::load_trusted_setup_file(file_path)
|
||||
.map_err(Error::InvalidTrustedSetup)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute the aggregated kzg proof given an array of blobs.
|
||||
pub fn compute_aggregate_kzg_proof(&self, blobs: &[Blob]) -> Result<KzgProof, Error> {
|
||||
c_kzg::KZGProof::compute_aggregate_kzg_proof(blobs, &self.trusted_setup)
|
||||
c_kzg::KzgProof::compute_aggregate_kzg_proof(blobs, &self.trusted_setup)
|
||||
.map_err(Error::KzgProofComputationFailed)
|
||||
.map(|proof| KzgProof(proof.to_bytes()))
|
||||
}
|
||||
|
||||
/// Verify an aggregate kzg proof given the blobs that generated the proof, the kzg commitments
|
||||
/// and the kzg proof.
|
||||
pub fn verify_aggregate_kzg_proof(
|
||||
&self,
|
||||
blobs: &[Blob],
|
||||
@@ -58,19 +78,20 @@ impl Kzg {
|
||||
let commitments = expected_kzg_commitments
|
||||
.into_iter()
|
||||
.map(|comm| {
|
||||
c_kzg::KZGCommitment::from_bytes(&comm.0).map_err(Error::InvalidKzgCommitment)
|
||||
c_kzg::KzgCommitment::from_bytes(&comm.0).map_err(Error::InvalidKzgCommitment)
|
||||
})
|
||||
.collect::<Result<Vec<c_kzg::KZGCommitment>, Error>>()?;
|
||||
.collect::<Result<Vec<c_kzg::KzgCommitment>, Error>>()?;
|
||||
let proof =
|
||||
c_kzg::KZGProof::from_bytes(&kzg_aggregated_proof.0).map_err(Error::InvalidKzgProof)?;
|
||||
c_kzg::KzgProof::from_bytes(&kzg_aggregated_proof.0).map_err(Error::InvalidKzgProof)?;
|
||||
proof
|
||||
.verify_aggregate_kzg_proof(blobs, &commitments, &self.trusted_setup)
|
||||
.map_err(Error::InvalidKzgProof)
|
||||
}
|
||||
|
||||
/// Converts a blob to a kzg commitment.
|
||||
pub fn blob_to_kzg_commitment(&self, blob: Blob) -> KzgCommitment {
|
||||
KzgCommitment(
|
||||
c_kzg::KZGCommitment::blob_to_kzg_commitment(blob, &self.trusted_setup).to_bytes(),
|
||||
c_kzg::KzgCommitment::blob_to_kzg_commitment(blob, &self.trusted_setup).to_bytes(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
160
crypto/kzg/src/trusted_setup.rs
Normal file
160
crypto/kzg/src/trusted_setup.rs
Normal file
@@ -0,0 +1,160 @@
|
||||
use c_kzg::{BYTES_PER_G1_POINT, BYTES_PER_G2_POINT, FIELD_ELEMENTS_PER_BLOB};
|
||||
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")]
|
||||
#[serde(deserialize_with = "deserialize_g1_points")]
|
||||
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 deserialize_g1_points<'de, D>(deserializer: D) -> Result<Vec<G1Point>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let mut decoded: Vec<G1Point> = serde::de::Deserialize::deserialize(deserializer)?;
|
||||
// FIELD_ELEMENTS_PER_BLOB is a compile time parameter that
|
||||
// depends on whether lighthouse is compiled with minimal or mainnet features.
|
||||
// Minimal and mainnet trusted setup parameters differ only by the
|
||||
// number of G1 points they contain.
|
||||
//
|
||||
// Hence, we truncate the number of G1 points after deserialisation
|
||||
// to ensure that we have the right number of g1 points in the
|
||||
// trusted setup.
|
||||
decoded.truncate(FIELD_ELEMENTS_PER_BLOB);
|
||||
Ok(decoded)
|
||||
}
|
||||
|
||||
fn strip_prefix(s: &str) -> &str {
|
||||
if let Some(stripped) = s.strip_prefix("0x") {
|
||||
stripped
|
||||
} else {
|
||||
s
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user