diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 64e85a0e6e7c..ab6d93af8da3 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -202,6 +202,9 @@ func validateBlobSidecarOsaka(hashes []common.Hash, sidecar *types.BlobTxSidecar if len(sidecar.Blobs) != len(hashes) { return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(sidecar.Blobs), len(hashes)) } + if len(sidecar.Commitments) != len(hashes) { + return fmt.Errorf("invalid number of %d commitments compared to %d blob hashes", len(sidecar.Commitments), len(hashes)) + } if len(sidecar.Proofs) != len(hashes)*kzg4844.CellProofsPerBlob { return fmt.Errorf("invalid number of %d blob proofs expected %d", len(sidecar.Proofs), len(hashes)*kzg4844.CellProofsPerBlob) } @@ -210,14 +213,13 @@ func validateBlobSidecarOsaka(hashes []common.Hash, sidecar *types.BlobTxSidecar } // Blob commitments match with the hashes in the transaction, verify the // blobs themselves via KZG - for i := range sidecar.Blobs { - // TODO verify the cell proof here - _ = i - /* - if err := kzg4844.VerifyBlobProof(&sidecar.Blobs[i], sidecar.Commitments[i], sidecar.Proofs[i]); err != nil { - return fmt.Errorf("invalid blob %d: %v", i, err) - } - */ + var blobs []*kzg4844.Blob + for _, blob := range sidecar.Blobs { + blobs = append(blobs, &blob) + } + + if err := kzg4844.VerifyCellProofs(blobs, sidecar.Commitments, sidecar.Proofs); err != nil { + return err } return nil } diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go index 5e14fbbd5e08..6637a589c62f 100644 --- a/crypto/kzg4844/kzg4844.go +++ b/crypto/kzg4844/kzg4844.go @@ -155,6 +155,16 @@ func VerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error { return gokzgVerifyBlobProof(blob, commitment, proof) } +// VerifyCellProofs verifies a batch of proofs corresponding to the blobs and commitments. +// Expects length of blobs and commitments to be equal. +// Expects length of proofs be 128 * length of blobs. +func VerifyCellProofs(blobs []*Blob, commitments []Commitment, proofs []Proof) error { + if useCKZG.Load() { + return ckzgVerifyCellProofBatch(blobs, commitments, proofs) + } + return gokzgVerifyCellProofBatch(blobs, commitments, proofs) +} + // ComputeCellProofs returns the KZG cell proofs that are used to verify the blob against // the commitment. // diff --git a/crypto/kzg4844/kzg4844_ckzg_cgo.go b/crypto/kzg4844/kzg4844_ckzg_cgo.go index 49a7046fe0d7..2404a1a9a52d 100644 --- a/crypto/kzg4844/kzg4844_ckzg_cgo.go +++ b/crypto/kzg4844/kzg4844_ckzg_cgo.go @@ -149,3 +149,44 @@ func ckzgComputeCellProofs(blob *Blob) ([]Proof, error) { } return p, nil } + +// ckzgVerifyCellProofs verifies that the blob data corresponds to the provided commitment. +func ckzgVerifyCellProofBatch(blobs []*Blob, commitments []Commitment, cellProofs []Proof) error { + ckzgIniter.Do(ckzgInit) + var ( + proofs = make([]ckzg4844.Bytes48, len(cellProofs)) + commits = make([]ckzg4844.Bytes48, 0, len(cellProofs)) + cellIndices = make([]uint64, 0, len(cellProofs)) + cells = make([]ckzg4844.Cell, 0, len(cellProofs)) + ) + // Copy over the cell proofs + for i, proof := range cellProofs { + proofs[i] = (ckzg4844.Bytes48)(proof) + } + // Blow up the commitments to be the same length as the proofs + for _, commitment := range commitments { + for range gokzg4844.CellsPerExtBlob { + commits = append(commits, (ckzg4844.Bytes48)(commitment)) + } + } + // Compute the cells and cell indices + for _, blob := range blobs { + cellsI, err := ckzg4844.ComputeCells((*ckzg4844.Blob)(blob)) + if err != nil { + return err + } + cells = append(cells, cellsI[:]...) + for idx := range len(cellsI) { + cellIndices = append(cellIndices, uint64(idx)) + } + } + + valid, err := ckzg4844.VerifyCellKZGProofBatch(commits, cellIndices, cells, proofs) + if err != nil { + return err + } + if !valid { + return errors.New("invalid proof") + } + return nil +} diff --git a/crypto/kzg4844/kzg4844_ckzg_nocgo.go b/crypto/kzg4844/kzg4844_ckzg_nocgo.go index 6f4bd3b8236f..3b12e1c2ad0a 100644 --- a/crypto/kzg4844/kzg4844_ckzg_nocgo.go +++ b/crypto/kzg4844/kzg4844_ckzg_nocgo.go @@ -61,6 +61,11 @@ func ckzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error { panic("unsupported platform") } +// ckzgVerifyCellProofBatch verifies that the blob data corresponds to the provided commitment. +func ckzgVerifyCellProofBatch(blobs []*Blob, commitments []Commitment, proof []Proof) error { + panic("unsupported platform") +} + // ckzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against // the commitment. // diff --git a/crypto/kzg4844/kzg4844_gokzg.go b/crypto/kzg4844/kzg4844_gokzg.go index 46a38a89136d..9fbd6f654af3 100644 --- a/crypto/kzg4844/kzg4844_gokzg.go +++ b/crypto/kzg4844/kzg4844_gokzg.go @@ -114,3 +114,37 @@ func gokzgComputeCellProofs(blob *Blob) ([]Proof, error) { } return p, nil } + +// gokzgVerifyCellProofs 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 _, blob := range blobs { + cellsI, err := context.ComputeCells((*gokzg4844.Blob)(blob), 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) +} diff --git a/crypto/kzg4844/kzg4844_test.go b/crypto/kzg4844/kzg4844_test.go index a6782d4768ad..868d7fb868ba 100644 --- a/crypto/kzg4844/kzg4844_test.go +++ b/crypto/kzg4844/kzg4844_test.go @@ -193,3 +193,39 @@ func benchmarkVerifyBlobProof(b *testing.B, ckzg bool) { VerifyBlobProof(blob, commitment, proof) } } + +func TestCKZGCells(t *testing.T) { testKZGCells(t, true) } +func TestGoKZGCells(t *testing.T) { testKZGCells(t, false) } +func testKZGCells(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) + + blob1 := randBlob() + blob2 := randBlob() + + commitment1, err := BlobToCommitment(blob1) + if err != nil { + t.Fatalf("failed to create KZG commitment from blob: %v", err) + } + commitment2, err := BlobToCommitment(blob2) + if err != nil { + t.Fatalf("failed to create KZG commitment from blob: %v", err) + } + + proofs1, err := ComputeCellProofs(blob1) + if err != nil { + t.Fatalf("failed to create KZG proof at point: %v", err) + } + + proofs2, err := ComputeCellProofs(blob2) + if err != nil { + t.Fatalf("failed to create KZG proof at point: %v", err) + } + proofs := append(proofs1, proofs2...) + if err := VerifyCellProofs([]*Blob{blob1, blob2}, []Commitment{commitment1, commitment2}, proofs); err != nil { + t.Fatalf("failed to verify KZG proof at point: %v", err) + } +}