KZG4844 Blob Commitments Precompile
Native EVM precompile for EIP-4844 KZG polynomial commitments for blob data availability
Abstract
This LP specifies a native EVM precompile for KZG (Kate-Zaverucha-Goldberg) polynomial commitments as used in EIP-4844 (Proto-Danksharding) for blob data availability. The precompile enables smart contracts to verify blob commitments, compute proofs, and validate point evaluations on the BLS12-381 curve. Deployed at address 0x031D, this precompile is essential for Layer 2 rollups, data availability sampling, and the Lux Z-Chain zkVM integration.
Motivation
EIP-4844 Blob Transactions
EIP-4844 introduces "blob-carrying transactions" that:
- Provide cheap data availability for L2 rollups
- Use KZG commitments for data integrity
- Enable future data availability sampling (DAS)
- Reduce L1 calldata costs by 10-100x
Use Cases on Lux
| Use Case | Description |
|---|---|
| Z-Chain Rollups | ZK rollup data availability via blobs |
| L2 Bridges | Commit to bridge transaction batches |
| Data Availability | Prove data was published without storing it |
| Fraud Proofs | Optimistic rollup state verification |
| zkVM Integration | ZK proof data commitment |
Why a Precompile?
While EIP-4844 includes a point evaluation precompile at 0x0A, we extend functionality:
- Full API: Not just point evaluation, but commitment, proof generation
- Batch Operations: Verify multiple proofs efficiently
- Cell Proofs: Support for DAS cell-level proofs
- Lux Integration: Native support in Z-Chain and L2 framework
Specification
Precompile Address
KZG4844_PRECOMPILE = 0x031D
Note: This extends the EIP-4844 point evaluation precompile (0x0A) with additional operations.
Constants
FIELD_ELEMENTS_PER_BLOB = 4096
BLOB_SIZE = 131072 // 128 KB
COMMITMENT_SIZE = 48 // G1 point, compressed
PROOF_SIZE = 48 // G1 point, compressed
POINT_SIZE = 32 // BLS12-381 scalar
CLAIM_SIZE = 32 // BLS12-381 scalar
CELLS_PER_BLOB = 128
Operation Selectors
| Selector | Operation | Description |
|---|---|---|
0x01 | BlobToCommitment | Compute commitment from blob |
0x02 | ComputeProof | Compute KZG proof at a point |
0x03 | VerifyProof | Verify proof at a point |
0x04 | ComputeBlobProof | Compute proof for entire blob |
0x05 | VerifyBlobProof | Verify blob against commitment |
0x10 | ComputeCellProofs | Compute all 128 cell proofs |
0x11 | VerifyCellProofs | Verify cell proofs (batch) |
0x20 | BatchVerifyProofs | Verify multiple proofs |
0x30 | CalcBlobHash | Compute versioned blob hash |
Input Format
BlobToCommitment
┌────────┬─────────────────────────────────────┐
│ 1 byte │ 131072 bytes │
│ 0x01 │ blob │
└────────┴─────────────────────────────────────┘
Output: commitment (48 bytes)
ComputeProof
┌────────┬─────────────────────────────────────┬────────────────┐
│ 1 byte │ 131072 bytes │ 32 bytes │
│ 0x02 │ blob │ point (z) │
└────────┴─────────────────────────────────────┴────────────────┘
Output: proof (48 bytes) || claim (32 bytes)
VerifyProof
┌────────┬────────────────┬────────────────┬────────────────┬────────────────┐
│ 1 byte │ 48 bytes │ 32 bytes │ 32 bytes │ 48 bytes │
│ 0x03 │ commitment │ point (z) │ claim (y) │ proof │
└────────┴────────────────┴────────────────┴────────────────┴────────────────┘
Output: 0x01 (valid) or 0x00 (invalid)
ComputeBlobProof
┌────────┬─────────────────────────────────────┬────────────────┐
│ 1 byte │ 131072 bytes │ 48 bytes │
│ 0x04 │ blob │ commitment │
└────────┴─────────────────────────────────────┴────────────────┘
Output: proof (48 bytes)
VerifyBlobProof
┌────────┬─────────────────────────────────────┬────────────────┬────────────────┐
│ 1 byte │ 131072 bytes │ 48 bytes │ 48 bytes │
│ 0x05 │ blob │ commitment │ proof │
└────────┴─────────────────────────────────────┴────────────────┴────────────────┘
Output: 0x01 (valid) or 0x00 (invalid)
ComputeCellProofs
┌────────┬─────────────────────────────────────┐
│ 1 byte │ 131072 bytes │
│ 0x10 │ blob │
└────────┴─────────────────────────────────────┘
Output: proofs[128] (128 * 48 = 6144 bytes)
VerifyCellProofs (Batch)
┌────────┬─────────┬──────────────────────────────────────────────────────────────┐
│ 1 byte │ 2 bytes │ Variable │
│ 0x11 │ n_blobs │ [blob, commitment, proofs[128]] * n_blobs │
└────────┴─────────┴──────────────────────────────────────────────────────────────┘
Output: 0x01 (all valid) or 0x00 (any invalid)
BatchVerifyProofs
┌────────┬─────────┬──────────────────────────────────────────────────────────────┐
│ 1 byte │ 2 bytes │ [commitment, point, claim, proof] * n │
│ 0x20 │ n_proofs│ Each: 48 + 32 + 32 + 48 = 160 bytes │
└────────┴─────────┴──────────────────────────────────────────────────────────────┘
Output: 0x01 (all valid) or 0x00 (any invalid)
CalcBlobHash
┌────────┬────────────────┐
│ 1 byte │ 48 bytes │
│ 0x30 │ commitment │
└────────┴────────────────┘
Output: versioned_hash (32 bytes, first byte = 0x01)
Gas Costs
| Operation | Base Gas | Per Blob | Per Proof |
|---|---|---|---|
| BlobToCommitment | 50,000 | - | - |
| ComputeProof | 50,000 | - | - |
| VerifyProof | 50,000 | - | - |
| ComputeBlobProof | 50,000 | - | - |
| VerifyBlobProof | 50,000 | - | - |
| ComputeCellProofs | 100,000 | - | - |
| VerifyCellProofs | 50,000 | 10,000 | 1,000 |
| BatchVerifyProofs | 10,000 | - | 3,000 |
| CalcBlobHash | 500 | - | - |
Solidity Interface
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/// @title IKZG4844 - KZG Polynomial Commitment Precompile for EIP-4844
/// @notice Native support for blob commitments and proofs
/// @dev Deployed at address 0x031D
interface IKZG4844 {
// ============ Constants ============
uint256 constant FIELD_ELEMENTS_PER_BLOB = 4096;
uint256 constant BLOB_SIZE = 131072;
uint256 constant COMMITMENT_SIZE = 48;
uint256 constant PROOF_SIZE = 48;
uint256 constant POINT_SIZE = 32;
uint256 constant CELLS_PER_BLOB = 128;
// ============ Blob Operations ============
/// @notice Compute KZG commitment from blob data
/// @param blob The 128KB blob data
/// @return commitment The 48-byte KZG commitment
function blobToCommitment(
bytes calldata blob
) external view returns (bytes memory commitment);
/// @notice Compute KZG proof at a specific point
/// @param blob The 128KB blob data
/// @param z The evaluation point
/// @return proof The 48-byte KZG proof
/// @return y The evaluation result (claim)
function computeProof(
bytes calldata blob,
bytes32 z
) external view returns (bytes memory proof, bytes32 y);
/// @notice Verify a KZG proof
/// @param commitment The blob commitment
/// @param z The evaluation point
/// @param y The claimed evaluation result
/// @param proof The KZG proof
/// @return valid True if proof is valid
function verifyProof(
bytes calldata commitment,
bytes32 z,
bytes32 y,
bytes calldata proof
) external view returns (bool valid);
/// @notice Compute blob proof (proves blob matches commitment)
/// @param blob The 128KB blob data
/// @param commitment The expected commitment
/// @return proof The 48-byte blob proof
function computeBlobProof(
bytes calldata blob,
bytes calldata commitment
) external view returns (bytes memory proof);
/// @notice Verify blob against commitment
/// @param blob The 128KB blob data
/// @param commitment The blob commitment
/// @param proof The blob proof
/// @return valid True if blob matches commitment
function verifyBlobProof(
bytes calldata blob,
bytes calldata commitment,
bytes calldata proof
) external view returns (bool valid);
// ============ Cell Operations (DAS) ============
/// @notice Compute all 128 cell proofs for a blob
/// @param blob The 128KB blob data
/// @return proofs Array of 128 cell proofs (48 bytes each)
function computeCellProofs(
bytes calldata blob
) external view returns (bytes memory proofs);
/// @notice Verify cell proofs for multiple blobs
/// @param blobs Array of blob data
/// @param commitments Array of commitments
/// @param proofs Array of cell proofs (128 per blob)
/// @return valid True if all cell proofs are valid
function verifyCellProofs(
bytes[] calldata blobs,
bytes[] calldata commitments,
bytes[] calldata proofs
) external view returns (bool valid);
// ============ Batch Operations ============
/// @notice Verify multiple proofs efficiently
/// @param commitments Array of commitments
/// @param points Array of evaluation points
/// @param claims Array of claimed values
/// @param proofs Array of proofs
/// @return valid True if all proofs are valid
function batchVerifyProofs(
bytes[] calldata commitments,
bytes32[] calldata points,
bytes32[] calldata claims,
bytes[] calldata proofs
) external view returns (bool valid);
// ============ Hash Operations ============
/// @notice Calculate versioned blob hash from commitment
/// @param commitment The blob commitment
/// @return versionedHash The versioned hash (0x01 || sha256(commitment)[1:])
function calcBlobHash(
bytes calldata commitment
) external view returns (bytes32 versionedHash);
}
/// @title KZG4844 - Library for KZG commitment operations
library KZG4844 {
address constant PRECOMPILE = address(0x031D);
// EIP-4844 Point Evaluation precompile (for compatibility)
address constant POINT_EVALUATION_PRECOMPILE = address(0x0A);
error BlobCommitmentFailed();
error ProofComputationFailed();
error ProofVerificationFailed();
error InvalidBlobSize();
/// @notice Compute commitment for blob data
function computeCommitment(
bytes memory blob
) internal view returns (bytes memory) {
require(blob.length == 131072, "Invalid blob size");
bytes memory input = abi.encodePacked(uint8(0x01), blob);
(bool success, bytes memory result) = PRECOMPILE.staticcall(input);
if (!success) revert BlobCommitmentFailed();
return result;
}
/// @notice Verify a point evaluation using EIP-4844 precompile
/// @dev Compatible with the standard 0x0A precompile format
function verifyPointEvaluation(
bytes32 versionedHash,
bytes32 z,
bytes32 y,
bytes memory commitment,
bytes memory proof
) internal view returns (bool) {
bytes memory input = abi.encodePacked(
versionedHash,
z,
y,
commitment,
proof
);
(bool success, bytes memory result) = POINT_EVALUATION_PRECOMPILE.staticcall(input);
if (!success) return false;
// EIP-4844 returns field modulus and BLS modulus on success
return result.length == 64;
}
/// @notice Verify blob matches commitment
function verifyBlob(
bytes memory blob,
bytes memory commitment,
bytes memory proof
) internal view returns (bool) {
require(blob.length == 131072, "Invalid blob size");
require(commitment.length == 48, "Invalid commitment size");
require(proof.length == 48, "Invalid proof size");
bytes memory input = abi.encodePacked(
uint8(0x05), // VerifyBlobProof
blob,
commitment,
proof
);
(bool success, bytes memory result) = PRECOMPILE.staticcall(input);
if (!success) return false;
return result[0] == 0x01;
}
/// @notice Calculate versioned blob hash
function calcVersionedHash(
bytes memory commitment
) internal view returns (bytes32) {
require(commitment.length == 48, "Invalid commitment size");
bytes memory input = abi.encodePacked(uint8(0x30), commitment);
(bool success, bytes memory result) = PRECOMPILE.staticcall(input);
if (!success) revert BlobCommitmentFailed();
return bytes32(result);
}
/// @notice Validate versioned hash format
function isValidVersionedHash(bytes32 h) internal pure returns (bool) {
return uint8(h[0]) == 0x01;
}
/// @notice Batch verify multiple point proofs
function batchVerify(
bytes[] memory commitments,
bytes32[] memory points,
bytes32[] memory claims,
bytes[] memory proofs
) internal view returns (bool) {
require(
commitments.length == points.length &&
points.length == claims.length &&
claims.length == proofs.length,
"Array length mismatch"
);
uint256 n = commitments.length;
// Build input: selector + count + [commitment, point, claim, proof] * n
bytes memory input = abi.encodePacked(
uint8(0x20),
uint16(n)
);
for (uint256 i = 0; i < n; i++) {
input = abi.encodePacked(
input,
commitments[i],
points[i],
claims[i],
proofs[i]
);
}
(bool success, bytes memory result) = PRECOMPILE.staticcall(input);
if (!success) return false;
return result[0] == 0x01;
}
}
/// @title BlobDataManager - Manage blob data for rollups
contract BlobDataManager {
struct BlobRecord {
bytes32 versionedHash;
bytes48 commitment;
uint256 timestamp;
address submitter;
}
mapping(bytes32 => BlobRecord) public blobs;
event BlobSubmitted(
bytes32 indexed versionedHash,
address indexed submitter,
uint256 timestamp
);
/// @notice Record a blob submission
function recordBlob(
bytes calldata commitment
) external returns (bytes32 versionedHash) {
versionedHash = KZG4844.calcVersionedHash(commitment);
blobs[versionedHash] = BlobRecord({
versionedHash: versionedHash,
commitment: bytes48(commitment),
timestamp: block.timestamp,
submitter: msg.sender
});
emit BlobSubmitted(versionedHash, msg.sender, block.timestamp);
}
/// @notice Verify a point evaluation against recorded blob
function verifyPoint(
bytes32 versionedHash,
bytes32 z,
bytes32 y,
bytes calldata proof
) external view returns (bool) {
BlobRecord storage record = blobs[versionedHash];
require(record.timestamp > 0, "Blob not found");
return KZG4844.verifyPointEvaluation(
versionedHash,
z,
y,
abi.encodePacked(record.commitment),
proof
);
}
}
Go Implementation
// Package kzg4844 implements the KZG commitment precompile for Lux EVM
package kzg4844
import (
"crypto/sha256"
"errors"
"github.com/luxfi/crypto/kzg4844"
)
const (
PrecompileAddress = 0x031D
// Operation selectors
OpBlobToCommitment = 0x01
OpComputeProof = 0x02
OpVerifyProof = 0x03
OpComputeBlobProof = 0x04
OpVerifyBlobProof = 0x05
OpComputeCellProofs = 0x10
OpVerifyCellProofs = 0x11
OpBatchVerifyProofs = 0x20
OpCalcBlobHash = 0x30
// Sizes
BlobSize = 131072
CommitmentSize = 48
ProofSize = 48
PointSize = 32
ClaimSize = 32
CellsPerBlob = 128
)
// Gas costs
const (
GasBlobToCommitment = 50000
GasComputeProof = 50000
GasVerifyProof = 50000
GasComputeBlobProof = 50000
GasVerifyBlobProof = 50000
GasComputeCellProofs = 100000
GasVerifyCellProofsBase = 50000
GasVerifyCellProofsPerBlob = 10000
GasVerifyCellProofsPerProof = 1000
GasBatchVerifyBase = 10000
GasBatchVerifyPerProof = 3000
GasCalcBlobHash = 500
)
var (
ErrInvalidInput = errors.New("invalid KZG4844 input")
ErrInvalidBlobSize = errors.New("invalid blob size")
ErrInvalidCommitment = errors.New("invalid commitment")
ErrInvalidProof = errors.New("invalid proof")
ErrVerificationFailed = errors.New("proof verification failed")
)
// KZG4844Precompile implements the KZG4844 precompile
type KZG4844Precompile struct{}
// RequiredGas calculates gas for KZG4844 operations
func (p *KZG4844Precompile) RequiredGas(input []byte) uint64 {
if len(input) < 1 {
return 0
}
op := input[0]
switch op {
case OpBlobToCommitment:
return GasBlobToCommitment
case OpComputeProof:
return GasComputeProof
case OpVerifyProof:
return GasVerifyProof
case OpComputeBlobProof:
return GasComputeBlobProof
case OpVerifyBlobProof:
return GasVerifyBlobProof
case OpComputeCellProofs:
return GasComputeCellProofs
case OpVerifyCellProofs:
if len(input) < 3 {
return 0
}
nBlobs := int(input[1])<<8 | int(input[2])
return GasVerifyCellProofsBase +
uint64(nBlobs)*GasVerifyCellProofsPerBlob +
uint64(nBlobs)*CellsPerBlob*GasVerifyCellProofsPerProof
case OpBatchVerifyProofs:
if len(input) < 3 {
return 0
}
nProofs := int(input[1])<<8 | int(input[2])
return GasBatchVerifyBase + uint64(nProofs)*GasBatchVerifyPerProof
case OpCalcBlobHash:
return GasCalcBlobHash
default:
return 0
}
}
// Run executes the KZG4844 precompile
func (p *KZG4844Precompile) Run(input []byte) ([]byte, error) {
if len(input) < 1 {
return nil, ErrInvalidInput
}
op := input[0]
switch op {
case OpBlobToCommitment:
return p.blobToCommitment(input[1:])
case OpComputeProof:
return p.computeProof(input[1:])
case OpVerifyProof:
return p.verifyProof(input[1:])
case OpComputeBlobProof:
return p.computeBlobProof(input[1:])
case OpVerifyBlobProof:
return p.verifyBlobProof(input[1:])
case OpComputeCellProofs:
return p.computeCellProofs(input[1:])
case OpBatchVerifyProofs:
return p.batchVerifyProofs(input[1:])
case OpCalcBlobHash:
return p.calcBlobHash(input[1:])
default:
return nil, ErrInvalidInput
}
}
func (p *KZG4844Precompile) blobToCommitment(input []byte) ([]byte, error) {
if len(input) < BlobSize {
return nil, ErrInvalidBlobSize
}
var blob kzg4844.Blob
copy(blob[:], input[:BlobSize])
commitment, err := kzg4844.BlobToCommitment(&blob)
if err != nil {
return nil, err
}
return commitment[:], nil
}
func (p *KZG4844Precompile) computeProof(input []byte) ([]byte, error) {
if len(input) < BlobSize+PointSize {
return nil, ErrInvalidInput
}
var blob kzg4844.Blob
copy(blob[:], input[:BlobSize])
var point kzg4844.Point
copy(point[:], input[BlobSize:BlobSize+PointSize])
proof, claim, err := kzg4844.ComputeProof(&blob, point)
if err != nil {
return nil, err
}
result := make([]byte, ProofSize+ClaimSize)
copy(result[:ProofSize], proof[:])
copy(result[ProofSize:], claim[:])
return result, nil
}
func (p *KZG4844Precompile) verifyProof(input []byte) ([]byte, error) {
if len(input) < CommitmentSize+PointSize+ClaimSize+ProofSize {
return nil, ErrInvalidInput
}
var commitment kzg4844.Commitment
copy(commitment[:], input[:CommitmentSize])
var point kzg4844.Point
copy(point[:], input[CommitmentSize:CommitmentSize+PointSize])
var claim kzg4844.Claim
copy(claim[:], input[CommitmentSize+PointSize:CommitmentSize+PointSize+ClaimSize])
var proof kzg4844.Proof
copy(proof[:], input[CommitmentSize+PointSize+ClaimSize:])
err := kzg4844.VerifyProof(commitment, point, claim, proof)
if err != nil {
return []byte{0x00}, nil
}
return []byte{0x01}, nil
}
func (p *KZG4844Precompile) computeBlobProof(input []byte) ([]byte, error) {
if len(input) < BlobSize+CommitmentSize {
return nil, ErrInvalidInput
}
var blob kzg4844.Blob
copy(blob[:], input[:BlobSize])
var commitment kzg4844.Commitment
copy(commitment[:], input[BlobSize:BlobSize+CommitmentSize])
proof, err := kzg4844.ComputeBlobProof(&blob, commitment)
if err != nil {
return nil, err
}
return proof[:], nil
}
func (p *KZG4844Precompile) verifyBlobProof(input []byte) ([]byte, error) {
if len(input) < BlobSize+CommitmentSize+ProofSize {
return nil, ErrInvalidInput
}
var blob kzg4844.Blob
copy(blob[:], input[:BlobSize])
var commitment kzg4844.Commitment
copy(commitment[:], input[BlobSize:BlobSize+CommitmentSize])
var proof kzg4844.Proof
copy(proof[:], input[BlobSize+CommitmentSize:])
err := kzg4844.VerifyBlobProof(&blob, commitment, proof)
if err != nil {
return []byte{0x00}, nil
}
return []byte{0x01}, nil
}
func (p *KZG4844Precompile) computeCellProofs(input []byte) ([]byte, error) {
if len(input) < BlobSize {
return nil, ErrInvalidBlobSize
}
var blob kzg4844.Blob
copy(blob[:], input[:BlobSize])
proofs, err := kzg4844.ComputeCellProofs(&blob)
if err != nil {
return nil, err
}
result := make([]byte, len(proofs)*ProofSize)
for i, proof := range proofs {
copy(result[i*ProofSize:], proof[:])
}
return result, nil
}
func (p *KZG4844Precompile) batchVerifyProofs(input []byte) ([]byte, error) {
if len(input) < 2 {
return nil, ErrInvalidInput
}
nProofs := int(input[0])<<8 | int(input[1])
offset := 2
proofDataSize := CommitmentSize + PointSize + ClaimSize + ProofSize
if len(input) < offset+nProofs*proofDataSize {
return nil, ErrInvalidInput
}
for i := 0; i < nProofs; i++ {
var commitment kzg4844.Commitment
copy(commitment[:], input[offset:offset+CommitmentSize])
offset += CommitmentSize
var point kzg4844.Point
copy(point[:], input[offset:offset+PointSize])
offset += PointSize
var claim kzg4844.Claim
copy(claim[:], input[offset:offset+ClaimSize])
offset += ClaimSize
var proof kzg4844.Proof
copy(proof[:], input[offset:offset+ProofSize])
offset += ProofSize
err := kzg4844.VerifyProof(commitment, point, claim, proof)
if err != nil {
return []byte{0x00}, nil
}
}
return []byte{0x01}, nil
}
func (p *KZG4844Precompile) calcBlobHash(input []byte) ([]byte, error) {
if len(input) < CommitmentSize {
return nil, ErrInvalidInput
}
var commitment kzg4844.Commitment
copy(commitment[:], input[:CommitmentSize])
// Version 1 hash: 0x01 || SHA256(commitment)[1:]
h := sha256.New()
h.Write(commitment[:])
hash := h.Sum(nil)
hash[0] = 0x01
return hash, nil
}
Rationale
Extension of EIP-4844
While EIP-4844 includes a point evaluation precompile (0x0A), we provide:
- Full Commitment: Compute commitments from raw blob data
- Proof Generation: Generate proofs, not just verify them
- Cell Proofs: Support for Data Availability Sampling
- Batch Operations: Efficient verification of multiple proofs
Trusted Setup
KZG requires a trusted setup (powers of tau ceremony). We use:
- Ethereum's mainnet KZG ceremony (4096 participants)
- Setup parameters embedded in precompile
- Same trusted setup as Ethereum for compatibility
Cell Proofs for DAS
Data Availability Sampling requires cell-level proofs:
- Blob divided into 128 cells
- Each cell can be individually verified
- Enables light client data availability checks
Backwards Compatibility
This precompile is compatible with:
- EIP-4844 blob format (128KB blobs)
- Ethereum's KZG trusted setup
- Standard BLS12-381 curve operations
The 0x0A point evaluation precompile remains unchanged for compatibility.
Test Cases
Test Vector 1: Blob Commitment
Blob: 0x00000000... (128KB zeros)
Expected Commitment:
0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Test Vector 2: Point Evaluation
Commitment: 0xc00000...
Point (z): 0x0000000000000000000000000000000000000000000000000000000000000001
Claim (y): 0x0000000000000000000000000000000000000000000000000000000000000000
Proof: 0xc00000...
Expected: Valid (0x01)
Solidity Test
function testBlobCommitment() public {
// Create a simple blob (128KB)
bytes memory blob = new bytes(131072);
// Compute commitment
bytes memory commitment = KZG4844.computeCommitment(blob);
// Compute versioned hash
bytes32 versionedHash = KZG4844.calcVersionedHash(commitment);
// Verify version byte
assertTrue(uint8(versionedHash[0]) == 0x01);
}
function testPointEvaluation() public {
bytes memory blob = new bytes(131072);
// Set some data
blob[0] = 0x42;
bytes memory commitment = KZG4844.computeCommitment(blob);
bytes32 versionedHash = KZG4844.calcVersionedHash(commitment);
// Compute proof at z = 1
bytes32 z = bytes32(uint256(1));
bytes memory input = abi.encodePacked(uint8(0x02), blob, z);
(bool success, bytes memory result) = address(0x031D).staticcall(input);
assertTrue(success);
// Parse proof and claim
bytes memory proof = new bytes(48);
bytes32 y;
// ... parse result ...
// Verify using EIP-4844 precompile
bool valid = KZG4844.verifyPointEvaluation(
versionedHash,
z,
y,
commitment,
proof
);
assertTrue(valid);
}
Reference Implementation
Implementation exists in:
github.com/luxfi/crypto/kzg4844: Go wrapper around c-kzg and go-kzggithub.com/luxfi/coreth/precompile/contracts/kzg4844: EVM precompile
Security Considerations
Trusted Setup
- Ceremony Security: Uses Ethereum's ceremony with 4096+ participants
- Toxic Waste: At least one honest participant destroys their secret
- Setup Verification: Precompile verifies setup parameters on initialization
Proof Security
- Binding: Computationally binding under DLOG assumption
- Hiding: Not hiding - commitments reveal polynomial structure
- Soundness: Computationally sound under KEA assumption
Side Channels
- Constant-Time: All field operations are constant-time
- No Secret Branches: Verification doesn't depend on secret data
- Memory Safety: Bounds checked on all inputs
Known Limitations
- Not Post-Quantum: Relies on discrete log hardness
- Large Proofs: 48 bytes per proof (vs. ~32 for hash-based)
- Trusted Setup: Requires ceremony for security
Blob Data Availability
- Ephemeral Storage: Blobs are not stored forever
- Reconstruction: Must fetch from network during availability window
- Light Client: Can verify availability without full blob
Copyright
Copyright and related rights waived via CC0.