Skip to content

Commit 1275844

Browse files
Update KZG library according to latest consensus spec changes (#49)
* Replace kzg-related data_blob.go type methods Parse, ComputeCommitment, and Point, so we can move methods that depend on them into the kzg package. * Remove ComputeCommitments which is unused. * Migrate remaining EIP-4844 consensus spec code into kzg_new, updated to include the latest consensus spec updates. * Start implementing a bytes API with appropriate interfaces Also fix incorrect hash type (Keccac -> Sha256) when computing versioned hash. * pure bytes API for VerifyKZGProof per 3097, and move kzg_new back into kzg.go now that updates are near complete * rename verifyBlobs validateBlobTransactionWrapper per updated spec, and make implementation more closely follow it * fix tests & type bug in kzg verification * more consensus layer support in kzg package * remove aggregated proof from blobs bundle * propagate malformed blob errors when processing a sent transaction so that these get rejected
1 parent 9ea1faa commit 1275844

File tree

14 files changed

+511
-414
lines changed

14 files changed

+511
-414
lines changed

core/beacon/types.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,9 @@ type payloadAttributesMarshaling struct {
4343

4444
// BlobsBundleV1 holds the blobs of an execution payload, to be retrieved separately
4545
type BlobsBundleV1 struct {
46-
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
47-
KZGs []types.KZGCommitment `json:"kzgs" gencodec:"required"`
48-
Blobs []types.Blob `json:"blobs" gencodec:"required"`
49-
AggregatedProof types.KZGProof `json:"aggregatedProof" gencodec:"required"`
46+
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
47+
KZGs []types.KZGCommitment `json:"kzgs" gencodec:"required"`
48+
Blobs []types.Blob `json:"blobs" gencodec:"required"`
5049
}
5150

5251
//go:generate go run github.com/fjl/gencodec -type ExecutableDataV1 -field-override executableDataMarshaling -out gen_ed.go
@@ -236,11 +235,5 @@ func BlockToBlobData(block *types.Block) (*BlobsBundleV1, error) {
236235
blobsBundle.KZGs = append(blobsBundle.KZGs, kzgs...)
237236
}
238237
}
239-
240-
_, _, aggregatedProof, err := types.Blobs(blobsBundle.Blobs).ComputeCommitmentsAndAggregatedProof()
241-
if err != nil {
242-
return nil, err
243-
}
244-
blobsBundle.AggregatedProof = aggregatedProof
245238
return blobsBundle, nil
246239
}

core/types/data_blob.go

Lines changed: 53 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"github.com/ethereum/go-ethereum/common/hexutil"
1111
"github.com/ethereum/go-ethereum/crypto/kzg"
1212
"github.com/ethereum/go-ethereum/params"
13-
"github.com/protolambda/go-kzg/bls"
1413
"github.com/protolambda/ztyp/codec"
1514
"github.com/protolambda/ztyp/tree"
1615
)
@@ -103,6 +102,7 @@ func (p *KZGProof) UnmarshalText(text []byte) error {
103102
return hexutil.UnmarshalFixedText("KZGProof", text, p[:])
104103
}
105104

105+
// BLSFieldElement is the raw bytes representation of a field element
106106
type BLSFieldElement [32]byte
107107

108108
func (p BLSFieldElement) MarshalText() ([]byte, error) {
@@ -120,6 +120,16 @@ func (p *BLSFieldElement) UnmarshalText(text []byte) error {
120120
// Blob data
121121
type Blob [params.FieldElementsPerBlob]BLSFieldElement
122122

123+
// kzg.Blob interface
124+
func (blob Blob) Len() int {
125+
return len(blob)
126+
}
127+
128+
// kzg.Blob interface
129+
func (blob Blob) At(i int) [32]byte {
130+
return [32]byte(blob[i])
131+
}
132+
123133
func (blob *Blob) Deserialize(dr *codec.DecodingReader) error {
124134
if blob == nil {
125135
return errors.New("cannot decode ssz into nil Blob")
@@ -156,17 +166,6 @@ func (blob *Blob) HashTreeRoot(hFn tree.HashFn) tree.Root {
156166
}, params.FieldElementsPerBlob)
157167
}
158168

159-
// Convert a blob to kzg.Blob
160-
func (blob *Blob) ToKZGBlob() (kzg.Blob, bool) {
161-
frs := make([]bls.Fr, len(blob))
162-
for i, elem := range blob {
163-
if !bls.FrFrom32(&frs[i], elem) {
164-
return []bls.Fr{}, false
165-
}
166-
}
167-
return kzg.Blob(frs), true
168-
}
169-
170169
func (blob *Blob) MarshalText() ([]byte, error) {
171170
out := make([]byte, 2+params.FieldElementsPerBlob*32*2)
172171
copy(out[:2], "0x")
@@ -209,6 +208,15 @@ func (blob *Blob) UnmarshalText(text []byte) error {
209208

210209
type BlobKzgs []KZGCommitment
211210

211+
// kzg.KZGCommitmentSequence interface
212+
func (bk BlobKzgs) Len() int {
213+
return len(bk)
214+
}
215+
216+
func (bk BlobKzgs) At(i int) kzg.KZGCommitment {
217+
return kzg.KZGCommitment(bk[i])
218+
}
219+
212220
func (li *BlobKzgs) Deserialize(dr *codec.DecodingReader) error {
213221
return dr.List(func() codec.Deserializable {
214222
i := len(*li)
@@ -227,7 +235,7 @@ func (li BlobKzgs) ByteLength() uint64 {
227235
return uint64(len(li)) * 48
228236
}
229237

230-
func (li *BlobKzgs) FixedLength() uint64 {
238+
func (li BlobKzgs) FixedLength() uint64 {
231239
return 0
232240
}
233241

@@ -245,17 +253,14 @@ func (li BlobKzgs) copy() BlobKzgs {
245253

246254
type Blobs []Blob
247255

248-
// Extract the crypto material underlying these blobs
249-
func (blobs Blobs) toKZGBlobSequence() ([][]bls.Fr, bool) {
250-
out := make([][]bls.Fr, len(blobs))
251-
for i, b := range blobs {
252-
blob, ok := b.ToKZGBlob()
253-
if !ok {
254-
return nil, false
255-
}
256-
out[i] = blob
257-
}
258-
return out, true
256+
// kzg.BlobSequence interface
257+
func (blobs Blobs) Len() int {
258+
return len(blobs)
259+
}
260+
261+
// kzg.BlobSequence interface
262+
func (blobs Blobs) At(i int) kzg.Blob {
263+
return blobs[i]
259264
}
260265

261266
func (a *Blobs) Deserialize(dr *codec.DecodingReader) error {
@@ -301,42 +306,21 @@ func (blobs Blobs) ComputeCommitmentsAndAggregatedProof() (commitments []KZGComm
301306
commitments = make([]KZGCommitment, len(blobs))
302307
versionedHashes = make([]common.Hash, len(blobs))
303308
for i, blob := range blobs {
304-
frs, ok := blob.ToKZGBlob()
309+
c, ok := kzg.BlobToKZGCommitment(blob)
305310
if !ok {
306-
return nil, nil, KZGProof{}, errors.New("invalid blob for commitment")
311+
return nil, nil, KZGProof{}, errors.New("could not convert blob to commitment")
307312
}
308-
commitments[i] = KZGCommitment(kzg.BlobToKZGCommitment(frs))
309-
versionedHashes[i] = common.Hash(kzg.KZGToVersionedHash(kzg.KZGCommitment(commitments[i])))
313+
commitments[i] = KZGCommitment(c)
314+
versionedHashes[i] = common.Hash(kzg.KZGToVersionedHash(c))
310315
}
311316

312317
var kzgProof KZGProof
313318
if len(blobs) != 0 {
314-
aggregatePoly, aggregateCommitmentG1, err := computeAggregateKzgCommitment(blobs, commitments)
319+
proof, err := kzg.ComputeAggregateKZGProof(blobs)
315320
if err != nil {
316321
return nil, nil, KZGProof{}, err
317322
}
318-
319-
var aggregateCommitment KZGCommitment
320-
copy(aggregateCommitment[:], bls.ToCompressedG1(aggregateCommitmentG1))
321-
322-
var aggregateBlob Blob
323-
for i := range aggregatePoly {
324-
aggregateBlob[i] = bls.FrTo32(&aggregatePoly[i])
325-
}
326-
sum, err := sszHash(&PolynomialAndCommitment{aggregateBlob, aggregateCommitment})
327-
if err != nil {
328-
return nil, nil, KZGProof{}, err
329-
}
330-
z := kzg.BytesToBLSField(sum)
331-
332-
var y bls.Fr
333-
kzg.EvaluatePolyInEvaluationForm(&y, aggregatePoly[:], z)
334-
335-
aggProofG1, err := kzg.ComputeProof(aggregatePoly, z)
336-
if err != nil {
337-
return nil, nil, KZGProof{}, err
338-
}
339-
copy(kzgProof[:], bls.ToCompressedG1(aggProofG1))
323+
kzgProof = KZGProof(proof)
340324
}
341325

342326
return commitments, versionedHashes, kzgProof, nil
@@ -363,27 +347,6 @@ func (b *BlobsAndCommitments) FixedLength() uint64 {
363347
return 0
364348
}
365349

366-
type PolynomialAndCommitment struct {
367-
b Blob
368-
c KZGCommitment
369-
}
370-
371-
func (p *PolynomialAndCommitment) HashTreeRoot(hFn tree.HashFn) tree.Root {
372-
return hFn.HashTreeRoot(&p.b, &p.c)
373-
}
374-
375-
func (p *PolynomialAndCommitment) Serialize(w *codec.EncodingWriter) error {
376-
return w.Container(&p.b, &p.c)
377-
}
378-
379-
func (p *PolynomialAndCommitment) ByteLength() uint64 {
380-
return codec.ContainerLength(&p.b, &p.c)
381-
}
382-
383-
func (p *PolynomialAndCommitment) FixedLength() uint64 {
384-
return 0
385-
}
386-
387350
type BlobTxWrapper struct {
388351
Tx SignedBlobTx
389352
BlobKzgs BlobKzgs
@@ -421,19 +384,30 @@ func (b *BlobTxWrapData) sizeWrapData() common.StorageSize {
421384
return common.StorageSize(4 + 4 + b.BlobKzgs.ByteLength() + b.Blobs.ByteLength() + b.KzgAggregatedProof.ByteLength())
422385
}
423386

424-
func (b *BlobTxWrapData) verifyVersionedHash(inner TxData) error {
387+
// validateBlobTransactionWrapper implements validate_blob_transaction_wrapper from EIP-4844
388+
func (b *BlobTxWrapData) validateBlobTransactionWrapper(inner TxData) error {
425389
blobTx, ok := inner.(*SignedBlobTx)
426390
if !ok {
427391
return fmt.Errorf("expected signed blob tx, got %T", inner)
428392
}
429-
if a, b := len(blobTx.Message.BlobVersionedHashes), params.MaxBlobsPerBlock; a > b {
430-
return fmt.Errorf("too many blobs in blob tx, got %d, expected no more than %d", a, b)
393+
l1 := len(b.BlobKzgs)
394+
l2 := len(blobTx.Message.BlobVersionedHashes)
395+
l3 := len(b.Blobs)
396+
if l1 != l2 || l2 != l3 {
397+
return fmt.Errorf("lengths don't match %v %v %v", l1, l2, l3)
431398
}
432-
if a, b := len(b.BlobKzgs), len(b.Blobs); a != b {
433-
return fmt.Errorf("expected equal amount but got %d kzgs and %d blobs", a, b)
399+
// the following check isn't strictly necessary as it would be caught by data gas processing
400+
// (and hence it is not explicitly in the spec for this function), but it doesn't hurt to fail
401+
// early in case we are getting spammed with too many blobs or there is a bug somewhere:
402+
if l1 > params.MaxBlobsPerBlock {
403+
return fmt.Errorf("number of blobs exceeds max: %v", l1)
434404
}
435-
if a, b := len(b.BlobKzgs), len(blobTx.Message.BlobVersionedHashes); a != b {
436-
return fmt.Errorf("expected equal amount but got %d kzgs and %d versioned hashes", a, b)
405+
ok, err := kzg.VerifyAggregateKZGProof(b.Blobs, b.BlobKzgs, kzg.KZGProof(b.KzgAggregatedProof))
406+
if err != nil {
407+
return fmt.Errorf("error during proof verification: %v", err)
408+
}
409+
if !ok {
410+
return errors.New("failed to verify kzg")
437411
}
438412
for i, h := range blobTx.Message.BlobVersionedHashes {
439413
if computed := b.BlobKzgs[i].ComputeVersionedHash(); computed != h {
@@ -443,41 +417,6 @@ func (b *BlobTxWrapData) verifyVersionedHash(inner TxData) error {
443417
return nil
444418
}
445419

446-
// Blob verification using KZG proofs
447-
func (b *BlobTxWrapData) verifyBlobs(inner TxData) error {
448-
if err := b.verifyVersionedHash(inner); err != nil {
449-
return err
450-
}
451-
452-
aggregatePoly, aggregateCommitmentG1, err := computeAggregateKzgCommitment(b.Blobs, b.BlobKzgs)
453-
if err != nil {
454-
return fmt.Errorf("failed to compute aggregate commitment: %v", err)
455-
}
456-
var aggregateBlob Blob
457-
for i := range aggregatePoly {
458-
aggregateBlob[i] = bls.FrTo32(&aggregatePoly[i])
459-
}
460-
var aggregateCommitment KZGCommitment
461-
copy(aggregateCommitment[:], bls.ToCompressedG1(aggregateCommitmentG1))
462-
sum, err := sszHash(&PolynomialAndCommitment{aggregateBlob, aggregateCommitment})
463-
if err != nil {
464-
return err
465-
}
466-
z := kzg.BytesToBLSField(sum)
467-
468-
var y bls.Fr
469-
kzg.EvaluatePolyInEvaluationForm(&y, aggregatePoly[:], z)
470-
471-
aggregateProofG1, err := bls.FromCompressedG1(b.KzgAggregatedProof[:])
472-
if err != nil {
473-
return fmt.Errorf("aggregate proof parse error: %v", err)
474-
}
475-
if !kzg.VerifyKZGProofFromPoints(aggregateCommitmentG1, z, &y, aggregateProofG1) {
476-
return errors.New("failed to verify kzg")
477-
}
478-
return nil
479-
}
480-
481420
func (b *BlobTxWrapData) copy() TxWrapData {
482421
return &BlobTxWrapData{
483422
BlobKzgs: b.BlobKzgs.copy(),
@@ -514,33 +453,3 @@ func (b *BlobTxWrapData) encodeTyped(w io.Writer, txdata TxData) error {
514453
}
515454
return EncodeSSZ(w, &wrapped)
516455
}
517-
518-
func computeAggregateKzgCommitment(blobs Blobs, commitments []KZGCommitment) ([]bls.Fr, *bls.G1Point, error) {
519-
// create challenges
520-
sum, err := sszHash(&BlobsAndCommitments{blobs, commitments})
521-
if err != nil {
522-
return nil, nil, err
523-
}
524-
r := kzg.BytesToBLSField(sum)
525-
526-
powers := kzg.ComputePowers(r, len(blobs))
527-
528-
commitmentsG1 := make([]bls.G1Point, len(commitments))
529-
for i := 0; i < len(commitmentsG1); i++ {
530-
p, _ := bls.FromCompressedG1(commitments[i][:])
531-
bls.CopyG1(&commitmentsG1[i], p)
532-
}
533-
aggregateCommitmentG1 := bls.LinCombG1(commitmentsG1, powers)
534-
var aggregateCommitment KZGCommitment
535-
copy(aggregateCommitment[:], bls.ToCompressedG1(aggregateCommitmentG1))
536-
537-
polys, ok := blobs.toKZGBlobSequence()
538-
if !ok {
539-
return nil, nil, err
540-
}
541-
aggregatePoly, err := bls.PolyLinComb(polys, powers)
542-
if err != nil {
543-
return nil, nil, err
544-
}
545-
return aggregatePoly, aggregateCommitmentG1, nil
546-
}

core/types/transaction.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ type TxWrapData interface {
9898
aggregatedProof() KZGProof
9999
encodeTyped(w io.Writer, txdata TxData) error
100100
sizeWrapData() common.StorageSize
101-
verifyBlobs(inner TxData) error
101+
validateBlobTransactionWrapper(inner TxData) error
102102
}
103103

104104
// TxData is the underlying data of a transaction.
@@ -543,7 +543,7 @@ func (tx *Transaction) IsIncomplete() bool {
543543
// VerifyBlobs verifies the blob transaction
544544
func (tx *Transaction) VerifyBlobs() error {
545545
if tx.wrapData != nil {
546-
return tx.wrapData.verifyBlobs(tx.inner)
546+
return tx.wrapData.validateBlobTransactionWrapper(tx.inner)
547547
}
548548
return nil
549549
}

core/types/transaction_marshalling.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
362362
KzgAggregatedProof: dec.KzgAggregatedProof,
363363
}
364364
// Verify that versioned hashes match kzgs, and kzgs match blobs.
365-
if err := tx.wrapData.verifyBlobs(&itx); err != nil {
365+
if err := tx.wrapData.validateBlobTransactionWrapper(&itx); err != nil {
366366
return fmt.Errorf("blob wrapping data is invalid: %v", err)
367367
}
368368
}

core/types/transaction_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ func TestTransactionCoding(t *testing.T) {
490490
GasTipCap: view.Uint256View(*uint256.NewInt(42)),
491491
GasFeeCap: view.Uint256View(*uint256.NewInt(10)),
492492
AccessList: AccessListView(accesses),
493-
BlobVersionedHashes: VersionedHashesView{common.HexToHash("0x01624652859a6e98ffc1608e2af0147ca4e86e1ce27672d8d3f3c9d4ffd6ef7e")},
493+
BlobVersionedHashes: VersionedHashesView{common.HexToHash("0x010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014")},
494494
},
495495
}
496496
var kzgProof KZGProof

core/vm/testdata/precompiles/pointEvaluation.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[
22
{
3-
"Input": "01342233e6ebb423c766d3a0f8d183e84c453865b392f5ab1f8a8218506e89d842000000000000000000000000000000000000000000000000000000000000002b2f0b0a19cbe19b4c9dbc32af755539fec08bae3eeecbe0ec625037fe3f0a6fa3cfbde6cf9875270479e0e2290726d150412591e07b4fad36472fa1ad38c19eb232cd2ebd3738ea1d9a0a3be07764a8b2faf3776cf5fb7bea8263ab92181326b898c4dc5da95e76e6977c4e204a94f1a3fe5033e19435fa51a8c70b272c06ac",
3+
"Input": "01d0db71b458e8955efa3ef62f1b6b45a2d9c8633dc59ed0b995cecf8b7bb48442000000000000000000000000000000000000000000000000000000000000003b11ebd59d5d12d6ef8e5e48f71770515e032595a0e55eaf80e906b65b2a625282bead0f31f58ee4fd81e93f796bb57acd6f8f6e4def04182c8e949c71ea00d85a44c12102bd817bc97696a6b8fd75618fcd8c2030080c9602e08e935cdf6f779d0d89e7764d855edfbaa730eddfff836fc324957db4f74d1565503bcfcaf157",
44
"Expected": "",
55
"Name": "pointEvaluation1",
66
"Gas": 50000,

0 commit comments

Comments
 (0)