mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Add RecoverCells, which returns all CellsPerBlob cells per blob from a sufficient subset. RecoverBlobs only exposes recovered blobs; serving or persisting the extension cells of a sparse-blobpool transaction needs the full 128-cell set. Add RecoverCells, which returns all CellsPerBlob cells per blob from a sufficient subset. When the full data domain (cell indices 0..DataPerBlob-1) is present (the common case for pooled transactions) the blobs are a free concatenation of the data cells and every cell follows from a systematic extension via ComputeCells (~2-6ms/blob), skipping the KZG erasure solve. Otherwise it falls back to the erasure recovery path (~15-20ms/blob), which now surfaces the library's full extended cell set instead of discarding it down to blobs as RecoverBlobs does. Both paths return byte-identical cells in canonical order. Cells only; cell proofs are never recomputed (callers retain the proofs shipped with the transaction). Tested on both the gokzg and ckzg backends, fast and slow paths, against the original cells. Note: the same fast-path optimization could also be pushed down to gokzg and ckzg. There are both arguments for (the primitive becomes faster in some cases, same for the rest of cases) and against (it is not the role of the crypto lib to be intelligent here), so I went with the wrapper for now.
263 lines
8.2 KiB
Go
263 lines
8.2 KiB
Go
// Copyright 2023 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package kzg4844
|
|
|
|
import (
|
|
"encoding/json"
|
|
"sync"
|
|
|
|
gokzg4844 "github.com/crate-crypto/go-eth-kzg"
|
|
)
|
|
|
|
// context is the crypto primitive pre-seeded with the trusted setup parameters.
|
|
var context *gokzg4844.Context
|
|
|
|
// gokzgIniter ensures that we initialize the KZG library once before using it.
|
|
var gokzgIniter sync.Once
|
|
|
|
// gokzgInit initializes the KZG library with the provided trusted setup.
|
|
func gokzgInit() {
|
|
config, err := content.ReadFile("trusted_setup.json")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
params := new(gokzg4844.JSONTrustedSetup)
|
|
if err = json.Unmarshal(config, params); err != nil {
|
|
panic(err)
|
|
}
|
|
context, err = gokzg4844.NewContext4096(params)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// gokzgBlobToCommitment creates a small commitment out of a data blob.
|
|
func gokzgBlobToCommitment(blob *Blob) (Commitment, error) {
|
|
gokzgIniter.Do(gokzgInit)
|
|
|
|
commitment, err := context.BlobToKZGCommitment((*gokzg4844.Blob)(blob), 0)
|
|
if err != nil {
|
|
return Commitment{}, err
|
|
}
|
|
return (Commitment)(commitment), nil
|
|
}
|
|
|
|
// gokzgComputeProof computes the KZG proof at the given point for the polynomial
|
|
// represented by the blob.
|
|
func gokzgComputeProof(blob *Blob, point Point) (Proof, Claim, error) {
|
|
gokzgIniter.Do(gokzgInit)
|
|
|
|
proof, claim, err := context.ComputeKZGProof((*gokzg4844.Blob)(blob), (gokzg4844.Scalar)(point), 0)
|
|
if err != nil {
|
|
return Proof{}, Claim{}, err
|
|
}
|
|
return (Proof)(proof), (Claim)(claim), nil
|
|
}
|
|
|
|
// gokzgVerifyProof verifies the KZG proof that the polynomial represented by the blob
|
|
// evaluated at the given point is the claimed value.
|
|
func gokzgVerifyProof(commitment Commitment, point Point, claim Claim, proof Proof) error {
|
|
gokzgIniter.Do(gokzgInit)
|
|
|
|
return context.VerifyKZGProof((gokzg4844.KZGCommitment)(commitment), (gokzg4844.Scalar)(point), (gokzg4844.Scalar)(claim), (gokzg4844.KZGProof)(proof))
|
|
}
|
|
|
|
// gokzgComputeBlobProof returns the KZG proof that is used to verify the blob against
|
|
// the commitment.
|
|
//
|
|
// This method does not verify that the commitment is correct with respect to blob.
|
|
func gokzgComputeBlobProof(blob *Blob, commitment Commitment) (Proof, error) {
|
|
gokzgIniter.Do(gokzgInit)
|
|
|
|
proof, err := context.ComputeBlobKZGProof((*gokzg4844.Blob)(blob), (gokzg4844.KZGCommitment)(commitment), 0)
|
|
if err != nil {
|
|
return Proof{}, err
|
|
}
|
|
return (Proof)(proof), nil
|
|
}
|
|
|
|
// gokzgVerifyBlobProof verifies that the blob data corresponds to the provided commitment.
|
|
func gokzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
|
gokzgIniter.Do(gokzgInit)
|
|
|
|
return context.VerifyBlobKZGProof((*gokzg4844.Blob)(blob), (gokzg4844.KZGCommitment)(commitment), (gokzg4844.KZGProof)(proof))
|
|
}
|
|
|
|
// gokzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
|
// the commitment.
|
|
//
|
|
// This method does not verify that the commitment is correct with respect to blob.
|
|
func gokzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
|
gokzgIniter.Do(gokzgInit)
|
|
|
|
_, proofs, err := context.ComputeCellsAndKZGProofs((*gokzg4844.Blob)(blob), 0)
|
|
if err != nil {
|
|
return []Proof{}, err
|
|
}
|
|
p := make([]Proof, len(proofs))
|
|
for i, proof := range proofs {
|
|
p[i] = (Proof)(proof)
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
// gokzgVerifyCellProofBatch verifies that the blob data corresponds to the provided commitment.
|
|
func gokzgVerifyCellProofBatch(blobs []Blob, commitments []Commitment, cellProofs []Proof) error {
|
|
gokzgIniter.Do(gokzgInit)
|
|
|
|
var (
|
|
proofs = make([]gokzg4844.KZGProof, len(cellProofs))
|
|
commits = make([]gokzg4844.KZGCommitment, 0, len(cellProofs))
|
|
cellIndices = make([]uint64, 0, len(cellProofs))
|
|
cells = make([]*gokzg4844.Cell, 0, len(cellProofs))
|
|
)
|
|
// Copy over the cell proofs
|
|
for i, proof := range cellProofs {
|
|
proofs[i] = gokzg4844.KZGProof(proof)
|
|
}
|
|
// Blow up the commitments to be the same length as the proofs
|
|
for _, commitment := range commitments {
|
|
for range gokzg4844.CellsPerExtBlob {
|
|
commits = append(commits, gokzg4844.KZGCommitment(commitment))
|
|
}
|
|
}
|
|
// Compute the cell and cell indices
|
|
for i := range blobs {
|
|
cellsI, err := context.ComputeCells((*gokzg4844.Blob)(&blobs[i]), 2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cells = append(cells, cellsI[:]...)
|
|
for idx := range len(cellsI) {
|
|
cellIndices = append(cellIndices, uint64(idx))
|
|
}
|
|
}
|
|
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)
|
|
|
|
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))
|
|
}
|
|
}
|
|
for j := 0; j < len(commitments); 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)
|
|
|
|
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
|
|
}
|
|
|
|
// gokzgRecoverCells recovers all cells for each blob from a sufficient subset via
|
|
// KZG erasure recovery. Unlike gokzgRecoverBlobs it keeps the full extended cell
|
|
// set rather than reducing it to blobs.
|
|
func gokzgRecoverCells(cells []Cell, cellIndices []uint64) ([]Cell, error) {
|
|
gokzgIniter.Do(gokzgInit)
|
|
|
|
blobCount := len(cells) / len(cellIndices)
|
|
out := make([]Cell, 0, blobCount*CellsPerBlob)
|
|
|
|
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 nil, err
|
|
}
|
|
for _, cell := range extCells {
|
|
out = append(out, Cell(*cell))
|
|
}
|
|
|
|
offset = offset + len(cellIndices)
|
|
}
|
|
|
|
return out, nil
|
|
}
|