KZG Polynomial Commitments (EIP-4844)

Kate-Zaverucha-Goldberg commitments for blob transactions and data availability

KZG Polynomial Commitments (EIP-4844)

KZG (Kate-Zaverucha-Goldberg) polynomial commitments enable efficient verification of polynomial evaluations, critical for EIP-4844 blob transactions and data availability sampling.

Overview

KZG commitments provide:

  • Constant-Size Commitments: 48-byte commitment regardless of data size
  • Efficient Verification: Single pairing check for proof verification
  • Data Availability: Enable efficient data availability sampling
  • Blob Transactions: Foundation for Ethereum's scaling roadmap

Technical Details

Types and Sizes

TypeSizeDescription
Blob131,072 bytesData blob (128 KB)
Commitment48 bytesBLS12-381 G1 point
Proof48 bytesBLS12-381 G1 point
Point32 bytesField element
Claim32 bytesEvaluation result

Cell Proofs

ParameterValue
Cell Proofs Per Blob128
Cells Per Blob128
Cell Size1,024 bytes

Implementation

Blob to Commitment

Create a commitment from blob data:

package main

import (
    "fmt"
    "github.com/luxfi/crypto/kzg4844"
)

func main() {
    // Create a blob (128 KB)
    var blob kzg4844.Blob
    copy(blob[:], []byte("Your data here..."))

    // Generate commitment
    commitment, err := kzg4844.BlobToCommitment(&blob)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Commitment: %x\n", commitment[:])
    fmt.Printf("Commitment size: %d bytes\n", len(commitment))
}

Computing Proofs

Generate KZG proofs for blob evaluation:

func computeKZGProof(blob *kzg4844.Blob, point kzg4844.Point) (kzg4844.Proof, kzg4844.Claim, error) {
    // Compute proof at specific point
    proof, claim, err := kzg4844.ComputeProof(blob, point)
    if err != nil {
        return kzg4844.Proof{}, kzg4844.Claim{}, err
    }

    fmt.Printf("Proof: %x\n", proof[:])
    fmt.Printf("Claimed value: %x\n", claim[:])

    return proof, claim, nil
}

Blob Proofs

Generate proof that blob matches commitment:

func computeBlobProof(blob *kzg4844.Blob, commitment kzg4844.Commitment) (kzg4844.Proof, error) {
    // Compute proof for entire blob
    proof, err := kzg4844.ComputeBlobProof(blob, commitment)
    if err != nil {
        return kzg4844.Proof{}, err
    }

    return proof, nil
}

Verification

Verify KZG proofs:

// Verify point evaluation proof
func verifyPointProof(
    commitment kzg4844.Commitment,
    point kzg4844.Point,
    claim kzg4844.Claim,
    proof kzg4844.Proof,
) bool {
    err := kzg4844.VerifyProof(commitment, point, claim, proof)
    return err == nil
}

// Verify blob matches commitment
func verifyBlobProof(
    blob *kzg4844.Blob,
    commitment kzg4844.Commitment,
    proof kzg4844.Proof,
) bool {
    err := kzg4844.VerifyBlobProof(blob, commitment, proof)
    return err == nil
}

Cell Proofs

For data availability sampling:

// Compute all cell proofs for a blob
func computeCellProofs(blob *kzg4844.Blob) ([]kzg4844.Proof, error) {
    proofs, err := kzg4844.ComputeCellProofs(blob)
    if err != nil {
        return nil, err
    }

    fmt.Printf("Generated %d cell proofs\n", len(proofs))
    return proofs, nil
}

// Batch verify cell proofs
func verifyCellProofs(
    blobs []kzg4844.Blob,
    commitments []kzg4844.Commitment,
    proofs []kzg4844.Proof,
) bool {
    err := kzg4844.VerifyCellProofs(blobs, commitments, proofs)
    return err == nil
}

Versioned Blob Hashes

Calculate versioned hashes for blob transactions:

import (
    "crypto/sha256"
    "github.com/luxfi/crypto/kzg4844"
)

func calculateBlobHash(commitment *kzg4844.Commitment) [32]byte {
    hasher := sha256.New()
    return kzg4844.CalcBlobHashV1(hasher, commitment)
}

func isValidBlobHash(hash []byte) bool {
    return kzg4844.IsValidVersionedHash(hash)
}

Backend Selection

Choose between Go and C implementations:

// Use C-KZG backend (if available)
func useCBackend() error {
    return kzg4844.UseCKZG(true)
}

// Use Go-KZG backend
func useGoBackend() error {
    return kzg4844.UseCKZG(false)
}

Blob Transactions

Transaction Structure

type BlobTransaction struct {
    // Standard transaction fields
    ChainID    *big.Int
    Nonce      uint64
    GasTipCap  *big.Int
    GasFeeCap  *big.Int
    Gas        uint64
    To         *common.Address
    Value      *big.Int
    Data       []byte

    // Blob-specific fields
    BlobFeeCap *big.Int
    BlobHashes []common.Hash

    // Sidecar (not part of signed tx)
    Blobs       []kzg4844.Blob
    Commitments []kzg4844.Commitment
    Proofs      []kzg4844.Proof
}

Creating Blob Transactions

func createBlobTx(data [][]byte) (*BlobTransaction, error) {
    tx := &BlobTransaction{
        Blobs:       make([]kzg4844.Blob, len(data)),
        Commitments: make([]kzg4844.Commitment, len(data)),
        Proofs:      make([]kzg4844.Proof, len(data)),
        BlobHashes:  make([]common.Hash, len(data)),
    }

    for i, d := range data {
        // Copy data to blob
        copy(tx.Blobs[i][:], d)

        // Compute commitment
        commitment, err := kzg4844.BlobToCommitment(&tx.Blobs[i])
        if err != nil {
            return nil, err
        }
        tx.Commitments[i] = commitment

        // Compute proof
        proof, err := kzg4844.ComputeBlobProof(&tx.Blobs[i], commitment)
        if err != nil {
            return nil, err
        }
        tx.Proofs[i] = proof

        // Calculate versioned hash
        hasher := sha256.New()
        tx.BlobHashes[i] = kzg4844.CalcBlobHashV1(hasher, &commitment)
    }

    return tx, nil
}

Validating Blob Transactions

func validateBlobTx(tx *BlobTransaction) error {
    if len(tx.Blobs) != len(tx.Commitments) ||
       len(tx.Blobs) != len(tx.Proofs) ||
       len(tx.Blobs) != len(tx.BlobHashes) {
        return errors.New("mismatched blob sidecar lengths")
    }

    for i := range tx.Blobs {
        // Verify commitment matches blob
        expectedCommitment, err := kzg4844.BlobToCommitment(&tx.Blobs[i])
        if err != nil {
            return err
        }
        if expectedCommitment != tx.Commitments[i] {
            return errors.New("commitment mismatch")
        }

        // Verify proof
        if err := kzg4844.VerifyBlobProof(&tx.Blobs[i], tx.Commitments[i], tx.Proofs[i]); err != nil {
            return fmt.Errorf("proof verification failed: %w", err)
        }

        // Verify versioned hash
        hasher := sha256.New()
        expectedHash := kzg4844.CalcBlobHashV1(hasher, &tx.Commitments[i])
        if tx.BlobHashes[i] != expectedHash {
            return errors.New("blob hash mismatch")
        }
    }

    return nil
}

Data Availability Sampling

Random Sampling

type DataAvailabilitySampler struct {
    commitments []kzg4844.Commitment
    cellProofs  [][]kzg4844.Proof
}

func (das *DataAvailabilitySampler) Sample(numSamples int) (bool, error) {
    for i := 0; i < numSamples; i++ {
        // Random blob index
        blobIdx := rand.Intn(len(das.commitments))

        // Random cell index
        cellIdx := rand.Intn(kzg4844.CellProofsPerBlob)

        // Verify cell proof
        // This would require the cell data and use VerifyCellProofs
        // Simplified here for illustration
    }

    return true, nil
}

Performance Benchmarks

OperationGo BackendC BackendNotes
BlobToCommitment50 ms25 msSingle blob
ComputeProof55 ms30 msPoint evaluation
ComputeBlobProof60 ms35 msFull blob proof
VerifyProof3 ms2 msSingle verification
VerifyBlobProof3 ms2 msBlob verification
ComputeCellProofs1.5 s0.8 s128 proofs

Trusted Setup

KZG requires a trusted setup ceremony:

// The trusted setup is embedded in the package
//go:embed trusted_setup.json
var content embed.FS

// Powers of tau ceremony parameters:
// - 4096 G1 points
// - 65 G2 points
// - Secure multi-party computation ceremony

Security Considerations

Commitment Binding

// KZG commitments are binding - cannot find two different
// polynomials that hash to the same commitment
func demonstrateBinding() {
    var blob1, blob2 kzg4844.Blob

    // Different data produces different commitments
    copy(blob1[:], []byte("data1"))
    copy(blob2[:], []byte("data2"))

    c1, _ := kzg4844.BlobToCommitment(&blob1)
    c2, _ := kzg4844.BlobToCommitment(&blob2)

    // c1 != c2 (with overwhelming probability)
}

Proof Soundness

// Cannot forge proofs for incorrect evaluations
func verifyWithSecurityChecks(
    commitment kzg4844.Commitment,
    point kzg4844.Point,
    claim kzg4844.Claim,
    proof kzg4844.Proof,
) error {
    // Verify commitment is on curve
    if !isValidG1Point(commitment[:]) {
        return errors.New("invalid commitment point")
    }

    // Verify proof is on curve
    if !isValidG1Point(proof[:]) {
        return errors.New("invalid proof point")
    }

    // Verify the actual proof
    return kzg4844.VerifyProof(commitment, point, claim, proof)
}

Integration with Lux

Blob-Carrying Blocks

type BlobCarryingBlock struct {
    Header        *BlockHeader
    Transactions  []*Transaction
    BlobSidecars  []*BlobSidecar
}

type BlobSidecar struct {
    BlobIndex   uint64
    Blob        kzg4844.Blob
    Commitment  kzg4844.Commitment
    Proof       kzg4844.Proof
    TxHash      common.Hash
}

References