mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-12 01:41:36 +00:00
crypto/kzg4844: add cell-related functions
This commit is contained in:
parent
29e0a6f404
commit
14c0f1d507
5 changed files with 350 additions and 1 deletions
|
|
@ -34,9 +34,27 @@ var (
|
|||
blobT = reflect.TypeFor[Blob]()
|
||||
commitmentT = reflect.TypeFor[Commitment]()
|
||||
proofT = reflect.TypeFor[Proof]()
|
||||
cellT = reflect.TypeFor[Cell]()
|
||||
)
|
||||
|
||||
const CellProofsPerBlob = 128
|
||||
const (
|
||||
CellProofsPerBlob = 128
|
||||
CellsPerBlob = 128
|
||||
DataPerBlob = 64
|
||||
)
|
||||
|
||||
// Cell represents a single cell in a blob.
|
||||
type Cell [2048]byte
|
||||
|
||||
// UnmarshalJSON parses a cell in hex syntax.
|
||||
func (c *Cell) UnmarshalJSON(input []byte) error {
|
||||
return hexutil.UnmarshalFixedJSON(cellT, input, c[:])
|
||||
}
|
||||
|
||||
// MarshalText returns the hex representation of c.
|
||||
func (c *Cell) MarshalText() ([]byte, error) {
|
||||
return hexutil.Bytes(c[:]).MarshalText()
|
||||
}
|
||||
|
||||
// Blob represents a 4844 data blob.
|
||||
type Blob [131072]byte
|
||||
|
|
@ -189,3 +207,27 @@ func CalcBlobHashV1(hasher hash.Hash, commit *Commitment) (vh [32]byte) {
|
|||
func IsValidVersionedHash(h []byte) bool {
|
||||
return len(h) == 32 && h[0] == 0x01
|
||||
}
|
||||
|
||||
// VerifyCells verifies a batch of proofs corresponding to the cells and commitments.
|
||||
func VerifyCells(cells []Cell, commitments []Commitment, proofs []Proof, cellIndices []uint64) error {
|
||||
if useCKZG.Load() {
|
||||
return ckzgVerifyCells(cells, commitments, proofs, cellIndices)
|
||||
}
|
||||
return gokzgVerifyCells(cells, commitments, proofs, cellIndices)
|
||||
}
|
||||
|
||||
// ComputeCells computes the cells from the given blobs.
|
||||
func ComputeCells(blobs []Blob) ([]Cell, error) {
|
||||
if useCKZG.Load() {
|
||||
return ckzgComputeCells(blobs)
|
||||
}
|
||||
return gokzgComputeCells(blobs)
|
||||
}
|
||||
|
||||
// RecoverBlobs recovers blobs from the given cells and cell indices.
|
||||
func RecoverBlobs(cells []Cell, cellIndices []uint64) ([]Blob, error) {
|
||||
if useCKZG.Load() {
|
||||
return ckzgRecoverBlobs(cells, cellIndices)
|
||||
}
|
||||
return gokzgRecoverBlobs(cells, cellIndices)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,3 +190,90 @@ func ckzgVerifyCellProofBatch(blobs []Blob, commitments []Commitment, cellProofs
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ckzgVerifyCells(cells []Cell, commitments []Commitment, cellProofs []Proof, cellIndices []uint64) error {
|
||||
ckzgIniter.Do(ckzgInit)
|
||||
var (
|
||||
proofs = make([]ckzg4844.Bytes48, len(cellProofs))
|
||||
commits = make([]ckzg4844.Bytes48, 0, len(cellProofs))
|
||||
indices = make([]uint64, 0, len(cellProofs))
|
||||
kzgcells = make([]ckzg4844.Cell, 0, len(cellProofs))
|
||||
)
|
||||
for i := range cellProofs {
|
||||
proofs[i] = (ckzg4844.Bytes48)(cellProofs[i])
|
||||
kzgcells = append(kzgcells, (ckzg4844.Cell)(cells[i]))
|
||||
}
|
||||
if len(cellProofs)%len(commitments) != 0 {
|
||||
return errors.New("wrong cell proofs and commitments length")
|
||||
}
|
||||
cellCounts := len(cellProofs) / len(commitments)
|
||||
for _, commitment := range commitments {
|
||||
for j := 0; j < cellCounts; j++ {
|
||||
commits = append(commits, (ckzg4844.Bytes48)(commitment))
|
||||
}
|
||||
}
|
||||
blobCounts := len(cellProofs) / len(cellIndices)
|
||||
for j := 0; j < blobCounts; j++ {
|
||||
indices = append(indices, cellIndices...)
|
||||
}
|
||||
|
||||
valid, err := ckzg4844.VerifyCellKZGProofBatch(commits, indices, kzgcells, proofs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !valid {
|
||||
return errors.New("invalid proof")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ckzgComputeCells(blobs []Blob) ([]Cell, error) {
|
||||
ckzgIniter.Do(ckzgInit)
|
||||
cells := make([]Cell, 0, ckzg4844.CellsPerExtBlob*len(blobs))
|
||||
|
||||
for i := range blobs {
|
||||
cellsI, err := ckzg4844.ComputeCells((*ckzg4844.Blob)(&blobs[i]))
|
||||
if err != nil {
|
||||
return []Cell{}, err
|
||||
}
|
||||
for _, c := range cellsI {
|
||||
cells = append(cells, Cell(c))
|
||||
}
|
||||
}
|
||||
return cells, nil
|
||||
}
|
||||
|
||||
func ckzgRecoverBlobs(cells []Cell, cellIndices []uint64) ([]Blob, error) {
|
||||
ckzgIniter.Do(ckzgInit)
|
||||
|
||||
if len(cellIndices) == 0 || len(cells)%len(cellIndices) != 0 {
|
||||
return []Blob{}, errors.New("cells with wrong length")
|
||||
}
|
||||
|
||||
blobCount := len(cells) / len(cellIndices)
|
||||
blobs := make([]Blob, 0, blobCount)
|
||||
|
||||
offset := 0
|
||||
for range blobCount {
|
||||
kzgcells := make([]ckzg4844.Cell, 0, len(cellIndices))
|
||||
|
||||
for _, cell := range cells[offset : offset+len(cellIndices)] {
|
||||
kzgcells = append(kzgcells, ckzg4844.Cell(cell))
|
||||
}
|
||||
|
||||
extCells, err := ckzg4844.RecoverCells(cellIndices, kzgcells)
|
||||
if err != nil {
|
||||
return []Blob{}, err
|
||||
}
|
||||
|
||||
var blob Blob
|
||||
for i, cell := range extCells[:DataPerBlob] {
|
||||
copy(blob[i*len(cell):], cell[:])
|
||||
}
|
||||
blobs = append(blobs, blob)
|
||||
|
||||
offset = offset + len(cellIndices)
|
||||
}
|
||||
|
||||
return blobs, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,3 +73,15 @@ func ckzgVerifyCellProofBatch(blobs []Blob, commitments []Commitment, proof []Pr
|
|||
func ckzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||
panic("unsupported platform")
|
||||
}
|
||||
|
||||
func ckzgVerifyCells(cells []Cell, commitments []Commitment, cellProofs []Proof, cellIndices []uint64) error {
|
||||
panic("unsupported platform")
|
||||
}
|
||||
|
||||
func ckzgComputeCells(blobs []Blob) ([]Cell, error) {
|
||||
panic("unsupported platform")
|
||||
}
|
||||
|
||||
func ckzgRecoverBlobs(cells []Cell, cellIndices []uint64) ([]Blob, error) {
|
||||
panic("unsupported platform")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package kzg4844
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
gokzg4844 "github.com/crate-crypto/go-eth-kzg"
|
||||
|
|
@ -148,3 +149,94 @@ func gokzgVerifyCellProofBatch(blobs []Blob, commitments []Commitment, cellProof
|
|||
}
|
||||
return context.VerifyCellKZGProofBatch(commits, cellIndices, cells[:], proofs)
|
||||
}
|
||||
|
||||
// gokzgVerifyCells verifies that the cell data corresponds to the provided commitment.
|
||||
func gokzgVerifyCells(cells []Cell, commitments []Commitment, cellProofs []Proof, cellIndices []uint64) error {
|
||||
gokzgIniter.Do(gokzgInit)
|
||||
if len(commitments) == 0 || len(cellProofs)%len(commitments) != 0 ||
|
||||
len(cellProofs) != len(cells) || len(cellIndices) == 0 {
|
||||
return errors.New("wrong cell proofs and commitments length")
|
||||
}
|
||||
|
||||
var (
|
||||
proofs = make([]gokzg4844.KZGProof, len(cellProofs))
|
||||
commits = make([]gokzg4844.KZGCommitment, 0, len(cellProofs))
|
||||
indices = make([]uint64, 0, len(cellProofs))
|
||||
kzgcells = make([]*gokzg4844.Cell, 0, len(cellProofs))
|
||||
)
|
||||
// Copy over the cell proofs and cells
|
||||
for i := range cellProofs {
|
||||
proofs[i] = gokzg4844.KZGProof(cellProofs[i])
|
||||
gc := gokzg4844.Cell(cells[i])
|
||||
kzgcells = append(kzgcells, &gc)
|
||||
}
|
||||
cellCounts := len(cellProofs) / len(commitments)
|
||||
// Blow up the commitments to be the same length as the proofs
|
||||
for _, commitment := range commitments {
|
||||
for j := 0; j < cellCounts; j++ {
|
||||
commits = append(commits, gokzg4844.KZGCommitment(commitment))
|
||||
}
|
||||
}
|
||||
blobCounts := len(cellProofs) / len(cellIndices)
|
||||
for j := 0; j < blobCounts; j++ {
|
||||
indices = append(indices, cellIndices...)
|
||||
}
|
||||
|
||||
return context.VerifyCellKZGProofBatch(commits, indices, kzgcells, proofs)
|
||||
}
|
||||
|
||||
// gokzgComputeCells computes cells from blobs.
|
||||
func gokzgComputeCells(blobs []Blob) ([]Cell, error) {
|
||||
gokzgIniter.Do(gokzgInit)
|
||||
cells := make([]Cell, 0, gokzg4844.CellsPerExtBlob*len(blobs))
|
||||
|
||||
for i := range blobs {
|
||||
cellsI, err := context.ComputeCells((*gokzg4844.Blob)(&blobs[i]), 2)
|
||||
if err != nil {
|
||||
return []Cell{}, err
|
||||
}
|
||||
for _, c := range cellsI {
|
||||
if c != nil {
|
||||
cells = append(cells, Cell(*c))
|
||||
}
|
||||
}
|
||||
}
|
||||
return cells, nil
|
||||
}
|
||||
|
||||
// gokzgRecoverBlobs recovers blobs from cells and cell indices.
|
||||
func gokzgRecoverBlobs(cells []Cell, cellIndices []uint64) ([]Blob, error) {
|
||||
gokzgIniter.Do(gokzgInit)
|
||||
|
||||
if len(cellIndices) == 0 || len(cells)%len(cellIndices) != 0 {
|
||||
return []Blob{}, errors.New("cells with wrong length")
|
||||
}
|
||||
|
||||
blobCount := len(cells) / len(cellIndices)
|
||||
blobs := make([]Blob, 0, blobCount)
|
||||
|
||||
offset := 0
|
||||
for range blobCount {
|
||||
kzgcells := make([]*gokzg4844.Cell, 0, len(cellIndices))
|
||||
|
||||
for _, cell := range cells[offset : offset+len(cellIndices)] {
|
||||
gc := gokzg4844.Cell(cell)
|
||||
kzgcells = append(kzgcells, &gc)
|
||||
}
|
||||
|
||||
extCells, err := context.RecoverCells(cellIndices, kzgcells, 2)
|
||||
if err != nil {
|
||||
return []Blob{}, err
|
||||
}
|
||||
|
||||
var blob Blob
|
||||
for i, cell := range extCells[:DataPerBlob] {
|
||||
copy(blob[i*len(cell):], cell[:])
|
||||
}
|
||||
blobs = append(blobs, blob)
|
||||
|
||||
offset = offset + len(cellIndices)
|
||||
}
|
||||
|
||||
return blobs, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,3 +253,119 @@ func benchmarkComputeCellProofs(b *testing.B, ckzg bool) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCKZGVerifyPartialCells(t *testing.T) { testVerifyPartialCells(t, true) }
|
||||
func TestGoKZGVerifyPartialCells(t *testing.T) { testVerifyPartialCells(t, false) }
|
||||
|
||||
func testVerifyPartialCells(t *testing.T, ckzg bool) {
|
||||
if ckzg && !ckzgAvailable {
|
||||
t.Skip("CKZG unavailable in this test build")
|
||||
}
|
||||
defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load())
|
||||
useCKZG.Store(ckzg)
|
||||
|
||||
const blobCount = 3
|
||||
var blobs []*Blob
|
||||
var commitments []Commitment
|
||||
for range blobCount {
|
||||
blob := randBlob()
|
||||
commitment, err := BlobToCommitment(blob)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to commit blob: %v", err)
|
||||
}
|
||||
blobs = append(blobs, blob)
|
||||
commitments = append(commitments, commitment)
|
||||
}
|
||||
|
||||
var (
|
||||
partialCells []Cell
|
||||
partialProofs []Proof
|
||||
commits []Commitment
|
||||
indices []uint64
|
||||
)
|
||||
|
||||
for bi, blob := range blobs {
|
||||
proofs, err := ComputeCellProofs(blob)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compute cell proofs: %v", err)
|
||||
}
|
||||
cells, err := ComputeCells([]Blob{*blob})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compute cells: %v", err)
|
||||
}
|
||||
commits = append(commits, commitments[bi])
|
||||
|
||||
// sample 0, 31, 63, 95 cells
|
||||
step := len(cells) / 4
|
||||
|
||||
indices = []uint64{0, uint64(step - 1), uint64(2*step - 1), uint64(3*step - 1)}
|
||||
for _, idx := range indices {
|
||||
partialCells = append(partialCells, cells[idx])
|
||||
partialProofs = append(partialProofs, proofs[idx])
|
||||
}
|
||||
}
|
||||
if err := VerifyCells(partialCells, commits, partialProofs, indices); err != nil {
|
||||
t.Fatalf("failed to verify partial cell proofs: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCKZGRecoverBlob(t *testing.T) { testRecoverBlob(t, true) }
|
||||
func TestGoKZGRecoverBlob(t *testing.T) { testRecoverBlob(t, false) }
|
||||
|
||||
func testRecoverBlob(t *testing.T, ckzg bool) {
|
||||
if ckzg && !ckzgAvailable {
|
||||
t.Skip("CKZG unavailable in this test build")
|
||||
}
|
||||
defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load())
|
||||
useCKZG.Store(ckzg)
|
||||
|
||||
blobs := []Blob{}
|
||||
blobs = append(blobs, *randBlob())
|
||||
blobs = append(blobs, *randBlob())
|
||||
blobs = append(blobs, *randBlob())
|
||||
|
||||
cells, err := ComputeCells(blobs)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compute cells: %v", err)
|
||||
}
|
||||
proofs := make([]Proof, 0)
|
||||
commitments := make([]Commitment, len(blobs))
|
||||
for i, blob := range blobs {
|
||||
proof, err := ComputeCellProofs(&blob)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compute proof: %v", err)
|
||||
}
|
||||
proofs = append(proofs, proof...)
|
||||
|
||||
commitment, err := BlobToCommitment(&blob)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compute commitment: %v", err)
|
||||
}
|
||||
commitments[i] = commitment
|
||||
}
|
||||
|
||||
var (
|
||||
partialCells []Cell
|
||||
indices []uint64
|
||||
)
|
||||
|
||||
for ci := 64; ci < 128; ci++ {
|
||||
indices = append(indices, uint64(ci))
|
||||
}
|
||||
|
||||
for i := 0; i < len(cells); i += 128 {
|
||||
start := i + 64
|
||||
end := i + 128
|
||||
partialCells = append(partialCells, cells[start:end]...)
|
||||
}
|
||||
|
||||
recoverBlobs, err := RecoverBlobs(partialCells, indices)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to recover blob: %v", err)
|
||||
}
|
||||
|
||||
if err := VerifyCellProofs(recoverBlobs, commitments, proofs); err != nil {
|
||||
t.Fatalf("failed to verify recovered blob: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue