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

feat(rust): Add ethereum kzg settings #443

Merged
merged 7 commits into from
Jun 27, 2024
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
5 changes: 3 additions & 2 deletions Cargo.lock

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

16 changes: 13 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@ description = "A minimal implementation of the Polynomial Commitments API for EI
links = "ckzg"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
include = ["src", "inc", "bindings/rust/src", "bindings/rust/build.rs", "blst/bindings/*.h"]
include = [
"src",
"inc",
"bindings/rust/src",
"bindings/rust/build.rs",
"blst/bindings/*.h",
]
build = "bindings/rust/build.rs"

[lib]
path = "bindings/rust/src/lib.rs"

[features]
default = ["std", "portable"]
std = ["hex/std", "libc/std", "serde?/std"]
default = ["std", "portable", "ethereum_kzg_settings"]
std = ["hex/std", "libc/std", "serde?/std", "once_cell?/std"]
serde = ["dep:serde"]
generate-bindings = ["dep:bindgen"]
ethereum_kzg_settings = ["dep:once_cell"]

# This is a standalone feature so that crates that disable default features can
# enable blst/portable without having to add it as a dependency
Expand All @@ -36,6 +43,9 @@ serde = { version = "1.0", optional = true, default-features = false, features =
"alloc",
"derive",
] }
once_cell = { version = "1.19", default-features = false, features = [
"alloc",
], optional = true }

[dev-dependencies]
criterion = "0.5.1"
Expand Down
83 changes: 83 additions & 0 deletions bindings/rust/src/bindings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ use std::path::Path;
pub const BYTES_PER_G1_POINT: usize = 48;
pub const BYTES_PER_G2_POINT: usize = 96;

/// Number of G1 points required for the kzg trusted setup.
pub const NUM_G1_POINTS: usize = 4096;

/// Number of G2 points required for the kzg trusted setup.
/// 65 is fixed and is used for providing multiproofs up to 64 field elements.
pub const NUM_G2_POINTS: usize = 65;
Expand Down Expand Up @@ -58,6 +61,8 @@ pub enum Error {
InvalidTrustedSetup(String),
/// Paired arguments have different lengths.
MismatchLength(String),
/// Loading the trusted setup failed.
LoadingTrustedSetupFailed(KzgErrors),
/// The underlying c-kzg library returned an error.
CError(C_KZG_RET),
}
Expand All @@ -74,11 +79,36 @@ impl fmt::Display for Error {
| Self::InvalidKzgCommitment(s)
| Self::InvalidTrustedSetup(s)
| Self::MismatchLength(s) => f.write_str(s),
Self::LoadingTrustedSetupFailed(s) => write!(f, "KzgErrors: {:?}", s),
Self::CError(s) => fmt::Debug::fmt(s, f),
}
}
}

impl From<KzgErrors> for Error {
fn from(e: KzgErrors) -> Self {
Error::LoadingTrustedSetupFailed(e)
}
}

#[derive(Debug)]
pub enum KzgErrors {
/// Failed to get current directory.
FailedCurrentDirectory,
/// The specified path does not exist.
PathNotExists,
/// Problems related to I/O.
IOError,
/// Not a valid file.
NotValidFile,
/// File is not properly formatted.
FileFormatError,
/// Not able to parse to usize.
ParseError,
/// Number of points does not match what is expected.
MismatchedNumberOfPoints,
}

/// Converts a hex string (with or without the 0x prefix) to bytes.
pub fn hex_to_bytes(hex_str: &str) -> Result<Vec<u8>, Error> {
let trimmed_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
Expand Down Expand Up @@ -154,6 +184,51 @@ impl KZGSettings {
Self::load_trusted_setup_file_inner(&file_path)
}

/// Parses the contents of a KZG trusted setup file into a KzgSettings.
pub fn parse_kzg_trusted_setup(trusted_setup: &str) -> Result<Self, Error> {
let mut lines = trusted_setup.lines();

// load number of points
let n_g1 = lines
.next()
.ok_or(KzgErrors::FileFormatError)?
.parse::<usize>()
.map_err(|_| KzgErrors::ParseError)?;
let n_g2 = lines
.next()
.ok_or(KzgErrors::FileFormatError)?
.parse::<usize>()
.map_err(|_| KzgErrors::ParseError)?;

if n_g1 != NUM_G1_POINTS {
return Err(KzgErrors::MismatchedNumberOfPoints.into());
}

if n_g2 != NUM_G2_POINTS {
return Err(KzgErrors::MismatchedNumberOfPoints.into());
}

// load g1 points
let mut g1_points = alloc::boxed::Box::new([[0; BYTES_PER_G1_POINT]; NUM_G1_POINTS]);
for bytes in g1_points.iter_mut() {
let line = lines.next().ok_or(KzgErrors::FileFormatError)?;
hex::decode_to_slice(line, bytes).map_err(|_| KzgErrors::ParseError)?;
}

// load g2 points
let mut g2_points = alloc::boxed::Box::new([[0; BYTES_PER_G2_POINT]; NUM_G2_POINTS]);
for bytes in g2_points.iter_mut() {
let line = lines.next().ok_or(KzgErrors::FileFormatError)?;
hex::decode_to_slice(line, bytes).map_err(|_| KzgErrors::ParseError)?;
}

if lines.next().is_some() {
return Err(KzgErrors::FileFormatError.into());
}

Self::load_trusted_setup(g1_points.as_ref(), g2_points.as_ref())
}

/// Loads the trusted setup parameters from a file. The file format is as follows:
///
/// FIELD_ELEMENTS_PER_BLOB
Expand Down Expand Up @@ -697,6 +772,14 @@ mod tests {
}
}

#[test]
fn test_parse_kzg_trusted_setup() {
let trusted_setup_file = Path::new("src/trusted_setup.txt");
assert!(trusted_setup_file.exists());
let trusted_setup = fs::read_to_string(trusted_setup_file).unwrap();
let _ = KZGSettings::parse_kzg_trusted_setup(&trusted_setup).unwrap();
}

#[test]
fn test_compute_kzg_proof() {
let trusted_setup_file = Path::new("src/trusted_setup.txt");
Expand Down
Binary file not shown.
Binary file not shown.
80 changes: 80 additions & 0 deletions bindings/rust/src/ethereum_kzg_settings/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::{
bindings::{BYTES_PER_G1_POINT, BYTES_PER_G2_POINT, NUM_G1_POINTS, NUM_G2_POINTS},
KzgSettings,
};
use alloc::{boxed::Box, sync::Arc};
use once_cell::race::OnceBox;

/// Returns default Ethereum mainnet KZG settings.
///
/// If you need a cloneable settings use `ethereum_kzg_settings_arc` instead.
pub fn ethereum_kzg_settings() -> &'static KzgSettings {
ethereum_kzg_settings_inner().as_ref()
}

/// Returns default Ethereum mainnet KZG settings as an `Arc`.
///
/// It is useful for sharing the settings in multiple places.
pub fn ethereum_kzg_settings_arc() -> Arc<KzgSettings> {
ethereum_kzg_settings_inner().clone()
}

fn ethereum_kzg_settings_inner() -> &'static Arc<KzgSettings> {
static DEFAULT: OnceBox<Arc<KzgSettings>> = OnceBox::new();
DEFAULT.get_or_init(|| {
let settings =
KzgSettings::load_trusted_setup(ETH_G1_POINTS.as_ref(), ETH_G2_POINTS.as_ref())
.expect("failed to load default trusted setup");
Box::new(Arc::new(settings))
})
}

type G1Points = [[u8; BYTES_PER_G1_POINT]; NUM_G1_POINTS];
type G2Points = [[u8; BYTES_PER_G2_POINT]; NUM_G2_POINTS];

/// Default G1 points.
const ETH_G1_POINTS: &G1Points = {
const BYTES: &[u8] = include_bytes!("./g1_points.bin");
assert!(BYTES.len() == core::mem::size_of::<G1Points>());
unsafe { &*BYTES.as_ptr().cast::<G1Points>() }
};

/// Default G2 points.
const ETH_G2_POINTS: &G2Points = {
const BYTES: &[u8] = include_bytes!("./g2_points.bin");
assert!(BYTES.len() == core::mem::size_of::<G2Points>());
unsafe { &*BYTES.as_ptr().cast::<G2Points>() }
};

#[cfg(test)]
mod tests {
use super::*;
use crate::{bindings::BYTES_PER_BLOB, Blob, KzgCommitment, KzgProof, KzgSettings};
use std::path::Path;

#[test]
pub fn compare_default_with_file() {
let ts_settings =
KzgSettings::load_trusted_setup_file(Path::new("src/trusted_setup.txt")).unwrap();
let eth_settings = ethereum_kzg_settings();
let blob = Blob::new([1u8; BYTES_PER_BLOB]);

// generate commitment
let ts_commitment = KzgCommitment::blob_to_kzg_commitment(&blob, &ts_settings)
.unwrap()
.to_bytes();
let eth_commitment = KzgCommitment::blob_to_kzg_commitment(&blob, &eth_settings)
.unwrap()
.to_bytes();
assert_eq!(ts_commitment, eth_commitment);

// generate proof
let ts_proof = KzgProof::compute_blob_kzg_proof(&blob, &ts_commitment, &ts_settings)
.unwrap()
.to_bytes();
let eth_proof = KzgProof::compute_blob_kzg_proof(&blob, &eth_commitment, &eth_settings)
.unwrap()
.to_bytes();
assert_eq!(ts_proof, eth_proof);
}
}
8 changes: 8 additions & 0 deletions bindings/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,19 @@ extern crate blst;

mod bindings;

#[cfg(feature = "ethereum_kzg_settings")]
mod ethereum_kzg_settings;

// Expose relevant types with idiomatic names.
pub use bindings::{
KZGCommitment as KzgCommitment, KZGProof as KzgProof, KZGSettings as KzgSettings,
C_KZG_RET as CkzgError,
};

#[cfg(feature = "ethereum_kzg_settings")]
// Expose the default settings.
pub use ethereum_kzg_settings::{ethereum_kzg_settings, ethereum_kzg_settings_arc};

// Expose the constants.
pub use bindings::{
BYTES_PER_BLOB, BYTES_PER_COMMITMENT, BYTES_PER_FIELD_ELEMENT, BYTES_PER_G1_POINT,
Expand Down
Loading