LPsLux Proposals
Precompiles
LP-3665

KZG4844 Blob Commitments Precompile

Review

Native EVM precompile for EIP-4844 KZG polynomial commitments for blob data availability

Category
Core
Created
2025-12-24

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:

  1. Provide cheap data availability for L2 rollups
  2. Use KZG commitments for data integrity
  3. Enable future data availability sampling (DAS)
  4. Reduce L1 calldata costs by 10-100x

Use Cases on Lux

Use CaseDescription
Z-Chain RollupsZK rollup data availability via blobs
L2 BridgesCommit to bridge transaction batches
Data AvailabilityProve data was published without storing it
Fraud ProofsOptimistic rollup state verification
zkVM IntegrationZK proof data commitment

Why a Precompile?

While EIP-4844 includes a point evaluation precompile at 0x0A, we extend functionality:

  1. Full API: Not just point evaluation, but commitment, proof generation
  2. Batch Operations: Verify multiple proofs efficiently
  3. Cell Proofs: Support for DAS cell-level proofs
  4. 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

SelectorOperationDescription
0x01BlobToCommitmentCompute commitment from blob
0x02ComputeProofCompute KZG proof at a point
0x03VerifyProofVerify proof at a point
0x04ComputeBlobProofCompute proof for entire blob
0x05VerifyBlobProofVerify blob against commitment
0x10ComputeCellProofsCompute all 128 cell proofs
0x11VerifyCellProofsVerify cell proofs (batch)
0x20BatchVerifyProofsVerify multiple proofs
0x30CalcBlobHashCompute 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

OperationBase GasPer BlobPer Proof
BlobToCommitment50,000--
ComputeProof50,000--
VerifyProof50,000--
ComputeBlobProof50,000--
VerifyBlobProof50,000--
ComputeCellProofs100,000--
VerifyCellProofs50,00010,0001,000
BatchVerifyProofs10,000-3,000
CalcBlobHash500--

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:

  1. Full Commitment: Compute commitments from raw blob data
  2. Proof Generation: Generate proofs, not just verify them
  3. Cell Proofs: Support for Data Availability Sampling
  4. 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-kzg
  • github.com/luxfi/coreth/precompile/contracts/kzg4844: EVM precompile

Security Considerations

Trusted Setup

  1. Ceremony Security: Uses Ethereum's ceremony with 4096+ participants
  2. Toxic Waste: At least one honest participant destroys their secret
  3. Setup Verification: Precompile verifies setup parameters on initialization

Proof Security

  1. Binding: Computationally binding under DLOG assumption
  2. Hiding: Not hiding - commitments reveal polynomial structure
  3. Soundness: Computationally sound under KEA assumption

Side Channels

  1. Constant-Time: All field operations are constant-time
  2. No Secret Branches: Verification doesn't depend on secret data
  3. Memory Safety: Bounds checked on all inputs

Known Limitations

  1. Not Post-Quantum: Relies on discrete log hardness
  2. Large Proofs: 48 bytes per proof (vs. ~32 for hash-based)
  3. Trusted Setup: Requires ceremony for security

Blob Data Availability

  1. Ephemeral Storage: Blobs are not stored forever
  2. Reconstruction: Must fetch from network during availability window
  3. Light Client: Can verify availability without full blob

Copyright and related rights waived via CC0.