-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fulu block proposals with datacolumn broadcast #15628
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 25 commits
99cf1ef
63c3fb1
6a18d77
ac13ad5
aff6169
50ad7df
87da155
c53fd25
8b993a4
a21f153
f6a7550
ceca53d
0f471c1
4434e53
c72a137
c46ac42
adad25f
0fd759a
07d4389
e3884cf
409d8d3
1174c8a
85dd4e5
ca27f91
d4dcd5f
a463332
05fb71c
96ad094
9d885fd
6504c6e
8c59358
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,26 @@ package kzg | |
import ( | ||
"github.com/OffchainLabs/prysm/v6/consensus-types/blocks" | ||
GoKZG "github.com/crate-crypto/go-kzg-4844" | ||
ckzg4844 "github.com/ethereum/c-kzg-4844/v2/bindings/go" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
func bytesToBlob(blob []byte) *GoKZG.Blob { | ||
var ret GoKZG.Blob | ||
copy(ret[:], blob) | ||
return &ret | ||
} | ||
|
||
func bytesToCommitment(commitment []byte) (ret GoKZG.KZGCommitment) { | ||
copy(ret[:], commitment) | ||
return | ||
} | ||
|
||
func bytesToKZGProof(proof []byte) (ret GoKZG.KZGProof) { | ||
copy(ret[:], proof) | ||
return | ||
} | ||
|
||
// Verify performs single or batch verification of commitments depending on the number of given BlobSidecars. | ||
func Verify(blobSidecars ...blocks.ROBlob) error { | ||
if len(blobSidecars) == 0 { | ||
|
@@ -27,18 +45,107 @@ func Verify(blobSidecars ...blocks.ROBlob) error { | |
return kzgContext.VerifyBlobKZGProofBatch(blobs, cmts, proofs) | ||
} | ||
|
||
func bytesToBlob(blob []byte) *GoKZG.Blob { | ||
var ret GoKZG.Blob | ||
copy(ret[:], blob) | ||
return &ret | ||
} | ||
// VerifyBlobKZGProofBatch verifies KZG proofs for multiple blobs using batch verification. | ||
// This is more efficient than verifying each blob individually when len(blobs) > 1. | ||
// For single blob verification, it uses the optimized single verification path. | ||
func VerifyBlobKZGProofBatch(blobs [][]byte, commitments [][]byte, proofs [][]byte) error { | ||
if len(blobs) != len(commitments) || len(blobs) != len(proofs) { | ||
return errors.New("number of blobs, commitments, and proofs must match") | ||
james-prysm marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
} | ||
|
||
func bytesToCommitment(commitment []byte) (ret GoKZG.KZGCommitment) { | ||
copy(ret[:], commitment) | ||
return | ||
if len(blobs) == 0 { | ||
return nil | ||
} | ||
|
||
// Optimize for single blob case - use single verification to avoid batch overhead | ||
if len(blobs) == 1 { | ||
return kzgContext.VerifyBlobKZGProof( | ||
bytesToBlob(blobs[0]), | ||
bytesToCommitment(commitments[0]), | ||
bytesToKZGProof(proofs[0])) | ||
} | ||
|
||
// Use batch verification for multiple blobs | ||
ckzgBlobs := make([]ckzg4844.Blob, len(blobs)) | ||
ckzgCommitments := make([]ckzg4844.Bytes48, len(commitments)) | ||
ckzgProofs := make([]ckzg4844.Bytes48, len(proofs)) | ||
|
||
for i := range blobs { | ||
copy(ckzgBlobs[i][:], blobs[i]) | ||
copy(ckzgCommitments[i][:], commitments[i]) | ||
copy(ckzgProofs[i][:], proofs[i]) | ||
|
||
} | ||
|
||
valid, err := ckzg4844.VerifyBlobKZGProofBatch(ckzgBlobs, ckzgCommitments, ckzgProofs) | ||
if err != nil { | ||
return errors.Wrap(err, "batch verification") | ||
} | ||
if !valid { | ||
return errors.New("batch KZG proof verification failed") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it could be useful to test this case, because this very case ( |
||
} | ||
|
||
return nil | ||
} | ||
|
||
func bytesToKZGProof(proof []byte) (ret GoKZG.KZGProof) { | ||
copy(ret[:], proof) | ||
return | ||
// VerifyCellKZGProofBatchFromBlobData verifies cell KZG proofs in batch format directly from blob data. | ||
// This is more efficient than reconstructing data column sidecars when you have the raw blob data and cell proofs. | ||
// For PeerDAS/Fulu, the execution client provides cell proofs in flattened format via BlobsBundleV2. | ||
// For single blob verification, it optimizes by computing cells once and verifying efficiently. | ||
func VerifyCellKZGProofBatchFromBlobData(blobs [][]byte, commitments [][]byte, cellProofs [][]byte, numberOfColumns uint64) error { | ||
blobCount := uint64(len(blobs)) | ||
expectedCellProofs := blobCount * numberOfColumns | ||
|
||
if uint64(len(cellProofs)) != expectedCellProofs { | ||
return errors.Errorf("expected %d cell proofs, got %d", expectedCellProofs, len(cellProofs)) | ||
} | ||
|
||
if len(commitments) != len(blobs) { | ||
return errors.Errorf("number of commitments (%d) must match number of blobs (%d)", len(commitments), len(blobs)) | ||
} | ||
|
||
if blobCount == 0 { | ||
return nil | ||
} | ||
|
||
// Handle multiple blobs - compute cells for all blobs | ||
allCells := make([]Cell, 0, expectedCellProofs) | ||
allCommitments := make([]Bytes48, 0, expectedCellProofs) | ||
allIndices := make([]uint64, 0, expectedCellProofs) | ||
allProofs := make([]Bytes48, 0, expectedCellProofs) | ||
|
||
for blobIndex, blobData := range blobs { | ||
// Convert blob to kzg.Blob type | ||
var blob Blob | ||
copy(blob[:], blobData) | ||
|
||
|
||
// Compute cells for this blob | ||
cells, err := ComputeCells(&blob) | ||
if err != nil { | ||
return errors.Wrapf(err, "failed to compute cells for blob %d", blobIndex) | ||
} | ||
|
||
// Add cells and corresponding data for each column | ||
for columnIndex := range numberOfColumns { | ||
cellProofIndex := uint64(blobIndex)*numberOfColumns + columnIndex | ||
|
||
allCells = append(allCells, cells[columnIndex]) | ||
allCommitments = append(allCommitments, Bytes48(commitments[blobIndex])) | ||
allIndices = append(allIndices, columnIndex) | ||
|
||
var proof Bytes48 | ||
copy(proof[:], cellProofs[cellProofIndex]) | ||
|
||
allProofs = append(allProofs, proof) | ||
} | ||
} | ||
|
||
// Batch verify all cells | ||
valid, err := VerifyCellKZGProofBatch(allCommitments, allIndices, allCells, allProofs) | ||
if err != nil { | ||
return errors.Wrap(err, "cell batch verification") | ||
} | ||
if !valid { | ||
return errors.New("cell KZG proof batch verification failed") | ||
} | ||
|
||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: Why do the other functions use a named return value and this one uses a variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that one is a pointer the other ones are not