From bf19039a622ac9a5f542b6c850fd0785723ab09d Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Wed, 9 Nov 2022 17:33:13 -0800 Subject: [PATCH 01/10] 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. --- core/types/data_blob.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/types/data_blob.go b/core/types/data_blob.go index 558ea344dfff..25976c090a47 100644 --- a/core/types/data_blob.go +++ b/core/types/data_blob.go @@ -296,6 +296,21 @@ func (blobs Blobs) copy() Blobs { return cpy } +// Return KZG commitments and versioned hashes that correspond to these blobs +func (blobs Blobs) ComputeCommitments() ([]KZGCommitment, []common.Hash, bool) { + commitments := make([]KZGCommitment, len(blobs)) + versionedHashes := make([]common.Hash, len(blobs)) + for i, blob := range blobs { + frs, ok := blob.ToKZGBlob() + if !ok { + return nil, nil, false + } + commitments[i] = KZGCommitment(kzg.BlobToKZGCommitment(frs)) + versionedHashes[i] = common.Hash(kzg.KZGToVersionedHash(kzg.KZGCommitment(commitments[i]))) + } + return commitments, versionedHashes, true +} + // Return KZG commitments, versioned hashes and the aggregated KZG proof that correspond to these blobs func (blobs Blobs) ComputeCommitmentsAndAggregatedProof() (commitments []KZGCommitment, versionedHashes []common.Hash, aggregatedProof KZGProof, err error) { commitments = make([]KZGCommitment, len(blobs)) From 7601916ab5905a88e7a2241406e3e2402db10647 Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Thu, 10 Nov 2022 09:26:19 -0800 Subject: [PATCH 02/10] Remove ComputeCommitments which is unused. --- core/types/data_blob.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/core/types/data_blob.go b/core/types/data_blob.go index 25976c090a47..558ea344dfff 100644 --- a/core/types/data_blob.go +++ b/core/types/data_blob.go @@ -296,21 +296,6 @@ func (blobs Blobs) copy() Blobs { return cpy } -// Return KZG commitments and versioned hashes that correspond to these blobs -func (blobs Blobs) ComputeCommitments() ([]KZGCommitment, []common.Hash, bool) { - commitments := make([]KZGCommitment, len(blobs)) - versionedHashes := make([]common.Hash, len(blobs)) - for i, blob := range blobs { - frs, ok := blob.ToKZGBlob() - if !ok { - return nil, nil, false - } - commitments[i] = KZGCommitment(kzg.BlobToKZGCommitment(frs)) - versionedHashes[i] = common.Hash(kzg.KZGToVersionedHash(kzg.KZGCommitment(commitments[i]))) - } - return commitments, versionedHashes, true -} - // Return KZG commitments, versioned hashes and the aggregated KZG proof that correspond to these blobs func (blobs Blobs) ComputeCommitmentsAndAggregatedProof() (commitments []KZGCommitment, versionedHashes []common.Hash, aggregatedProof KZGProof, err error) { commitments = make([]KZGCommitment, len(blobs)) From 594fcbb62db04ef4e1bff536b94cfaaa399aca26 Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Thu, 10 Nov 2022 13:24:17 -0800 Subject: [PATCH 03/10] Migrate remaining EIP-4844 consensus spec code into kzg_new, updated to include the latest consensus spec updates. --- core/types/data_blob.go | 102 ++++++++--------------------- crypto/kzg/kzg.go | 49 -------------- crypto/kzg/kzg_new.go | 139 +++++++++++++++++++++++++++++++++++++++- crypto/kzg/util.go | 5 -- 4 files changed, 162 insertions(+), 133 deletions(-) diff --git a/core/types/data_blob.go b/core/types/data_blob.go index 558ea344dfff..8860e81d181a 100644 --- a/core/types/data_blob.go +++ b/core/types/data_blob.go @@ -209,6 +209,15 @@ func (blob *Blob) UnmarshalText(text []byte) error { type BlobKzgs []KZGCommitment +func (li BlobKzgs) toKZGCommitmentSequence() []kzg.KZGCommitment { + l := len(li) + kzgs := make([]kzg.KZGCommitment, l) + for i := 0; i < l; i++ { + kzgs[i] = kzg.KZGCommitment(li[i]) + } + return kzgs +} + func (li *BlobKzgs) Deserialize(dr *codec.DecodingReader) error { return dr.List(func() codec.Deserializable { i := len(*li) @@ -227,7 +236,7 @@ func (li BlobKzgs) ByteLength() uint64 { return uint64(len(li)) * 48 } -func (li *BlobKzgs) FixedLength() uint64 { +func (li BlobKzgs) FixedLength() uint64 { return 0 } @@ -305,38 +314,24 @@ func (blobs Blobs) ComputeCommitmentsAndAggregatedProof() (commitments []KZGComm if !ok { return nil, nil, KZGProof{}, errors.New("invalid blob for commitment") } - commitments[i] = KZGCommitment(kzg.BlobToKZGCommitment(frs)) - versionedHashes[i] = common.Hash(kzg.KZGToVersionedHash(kzg.KZGCommitment(commitments[i]))) + c := kzg.BlobToKZGCommitment(frs) + commitments[i] = KZGCommitment(c) + versionedHashes[i] = common.Hash(kzg.KZGToVersionedHash(c)) } var kzgProof KZGProof if len(blobs) != 0 { - aggregatePoly, aggregateCommitmentG1, err := computeAggregateKzgCommitment(blobs, commitments) - if err != nil { - return nil, nil, KZGProof{}, err - } - - var aggregateCommitment KZGCommitment - copy(aggregateCommitment[:], bls.ToCompressedG1(aggregateCommitmentG1)) - - var aggregateBlob Blob - for i := range aggregatePoly { - aggregateBlob[i] = bls.FrTo32(&aggregatePoly[i]) - } - sum, err := sszHash(&PolynomialAndCommitment{aggregateBlob, aggregateCommitment}) - if err != nil { + // TODO: Execution layer shouldn't be responsible for computing the proof, it should + // be done in the CL. + polys, ok := blobs.toKZGBlobSequence() + if !ok { return nil, nil, KZGProof{}, err } - z := kzg.BytesToBLSField(sum) - - var y bls.Fr - kzg.EvaluatePolyInEvaluationForm(&y, aggregatePoly[:], z) - - aggProofG1, err := kzg.ComputeProof(aggregatePoly, z) + proof, err := kzg.ComputeAggregateKZGProof(polys) if err != nil { return nil, nil, KZGProof{}, err } - copy(kzgProof[:], bls.ToCompressedG1(aggProofG1)) + kzgProof = KZGProof(proof) } return commitments, versionedHashes, kzgProof, nil @@ -448,31 +443,16 @@ func (b *BlobTxWrapData) verifyBlobs(inner TxData) error { if err := b.verifyVersionedHash(inner); err != nil { return err } - - aggregatePoly, aggregateCommitmentG1, err := computeAggregateKzgCommitment(b.Blobs, b.BlobKzgs) - if err != nil { - return fmt.Errorf("failed to compute aggregate commitment: %v", err) - } - var aggregateBlob Blob - for i := range aggregatePoly { - aggregateBlob[i] = bls.FrTo32(&aggregatePoly[i]) - } - var aggregateCommitment KZGCommitment - copy(aggregateCommitment[:], bls.ToCompressedG1(aggregateCommitmentG1)) - sum, err := sszHash(&PolynomialAndCommitment{aggregateBlob, aggregateCommitment}) - if err != nil { - return err + polys, ok := b.Blobs.toKZGBlobSequence() + if !ok { + return errors.New("could not convert blobs to blob sequence") } - z := kzg.BytesToBLSField(sum) - - var y bls.Fr - kzg.EvaluatePolyInEvaluationForm(&y, aggregatePoly[:], z) - - aggregateProofG1, err := bls.FromCompressedG1(b.KzgAggregatedProof[:]) + kzgs := b.BlobKzgs.toKZGCommitmentSequence() + ok, err := kzg.VerifyAggregateKZGProof(polys, kzgs, kzg.KZGProof(b.KzgAggregatedProof)) if err != nil { - return fmt.Errorf("aggregate proof parse error: %v", err) + return fmt.Errorf("error during proof verification: %v", err) } - if !kzg.VerifyKZGProofFromPoints(aggregateCommitmentG1, z, &y, aggregateProofG1) { + if !ok { return errors.New("failed to verify kzg") } return nil @@ -514,33 +494,3 @@ func (b *BlobTxWrapData) encodeTyped(w io.Writer, txdata TxData) error { } return EncodeSSZ(w, &wrapped) } - -func computeAggregateKzgCommitment(blobs Blobs, commitments []KZGCommitment) ([]bls.Fr, *bls.G1Point, error) { - // create challenges - sum, err := sszHash(&BlobsAndCommitments{blobs, commitments}) - if err != nil { - return nil, nil, err - } - r := kzg.BytesToBLSField(sum) - - powers := kzg.ComputePowers(r, len(blobs)) - - commitmentsG1 := make([]bls.G1Point, len(commitments)) - for i := 0; i < len(commitmentsG1); i++ { - p, _ := bls.FromCompressedG1(commitments[i][:]) - bls.CopyG1(&commitmentsG1[i], p) - } - aggregateCommitmentG1 := bls.LinCombG1(commitmentsG1, powers) - var aggregateCommitment KZGCommitment - copy(aggregateCommitment[:], bls.ToCompressedG1(aggregateCommitmentG1)) - - polys, ok := blobs.toKZGBlobSequence() - if !ok { - return nil, nil, err - } - aggregatePoly, err := bls.PolyLinComb(polys, powers) - if err != nil { - return nil, nil, err - } - return aggregatePoly, aggregateCommitmentG1, nil -} diff --git a/crypto/kzg/kzg.go b/crypto/kzg/kzg.go index 0d3a2cac10ed..740e311f68fa 100644 --- a/crypto/kzg/kzg.go +++ b/crypto/kzg/kzg.go @@ -2,12 +2,8 @@ package kzg import ( "encoding/json" - "errors" - "math/big" "math/bits" - "github.com/ethereum/go-ethereum/params" - "github.com/protolambda/go-kzg/bls" ) @@ -71,48 +67,3 @@ func bitReversalPermutation(l []bls.G1Point) []bls.G1Point { return out } - -// Compute KZG proof at point `z` with `polynomial` being in evaluation form. -// compute_kzg_proof from the EIP-4844 spec. -func ComputeProof(eval []bls.Fr, z *bls.Fr) (*bls.G1Point, error) { - if len(eval) != params.FieldElementsPerBlob { - return nil, errors.New("invalid eval polynomial for proof") - } - - // To avoid overflow/underflow, convert elements into int - var poly [params.FieldElementsPerBlob]big.Int - for i := range poly { - frToBig(&poly[i], &eval[i]) - } - var zB big.Int - frToBig(&zB, z) - - // Shift our polynomial first (in evaluation form we can't handle the division remainder) - var yB big.Int - var y bls.Fr - EvaluatePolyInEvaluationForm(&y, eval, z) - frToBig(&yB, &y) - var polyShifted [params.FieldElementsPerBlob]big.Int - - for i := range polyShifted { - polyShifted[i].Mod(new(big.Int).Sub(&poly[i], &yB), BLSModulus) - } - - var denomPoly [params.FieldElementsPerBlob]big.Int - for i := range denomPoly { - // Make sure we won't induce a division by zero later. Shouldn't happen if using Fiat-Shamir challenges - if Domain[i].Cmp(&zB) == 0 { - return nil, errors.New("inavlid z challenge") - } - denomPoly[i].Mod(new(big.Int).Sub(Domain[i], &zB), BLSModulus) - } - - // Calculate quotient polynomial by doing point-by-point division - var quotientPoly [params.FieldElementsPerBlob]bls.Fr - for i := range quotientPoly { - var tmp big.Int - blsDiv(&tmp, &polyShifted[i], &denomPoly[i]) - _ = BigToFr("ientPoly[i], &tmp) - } - return bls.LinCombG1(kzgSetupLagrange, quotientPoly[:]), nil -} diff --git a/crypto/kzg/kzg_new.go b/crypto/kzg/kzg_new.go index 8a32223b5458..047b9459202c 100644 --- a/crypto/kzg/kzg_new.go +++ b/crypto/kzg/kzg_new.go @@ -1,22 +1,28 @@ package kzg import ( + "crypto/sha256" "errors" "fmt" "math/big" "github.com/protolambda/go-kzg/bls" + "github.com/protolambda/ztyp/codec" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) +const ( + FIAT_SHAMIR_PROTOCOL_DOMAIN = "FSBLOBVERIFY_V1_" +) + // The custom types from EIP-4844 consensus spec: // https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#custom-types // We deviate from the spec slightly in that we use: -// *bls.Fr for BLSFieldElement -// *bls.G1Point for G1Point -// *bls.G2Point for G2Point +// bls.Fr for BLSFieldElement +// bls.G1Point for G1Point +// bls.G2Point for G2Point type Blob []bls.Fr type KZGCommitment [48]byte type KZGProof [48]byte @@ -50,6 +56,18 @@ func VerifyKZGProofFromPoints(polynomialKZG *bls.G1Point, z *bls.Fr, y *bls.Fr, return bls.PairingsVerify(&pMinusY, &bls.GenG2, kzgProof, &xMinusZ) } +// VerifyAggregateKZGProof implements verify_aggregate_kzg_proof from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#verify_aggregate_kzg_proof +func VerifyAggregateKZGProof(blobs [][]bls.Fr, expectedKZGCommitments []KZGCommitment, kzgAggregatedProof KZGProof) (bool, error) { + aggregatedPoly, aggregatedPolyCommitment, evaluationChallenge, err := + ComputeAggregatedPolyAndCommitment(blobs, expectedKZGCommitments) + if err != nil { + return false, err + } + y := EvaluatePolynomialInEvaluationForm(aggregatedPoly, evaluationChallenge) + return VerifyKZGProof(aggregatedPolyCommitment, evaluationChallenge, y, kzgAggregatedProof) +} + // KZGToVersionedHash implements kzg_to_versioned_hash from EIP-4844 func KZGToVersionedHash(kzg KZGCommitment) VersionedHash { h := crypto.Keccak256Hash(kzg[:]) @@ -140,3 +158,118 @@ func BytesToBLSField(h [32]byte) *bls.Fr { BigToFr(out, zB) return out } + +// ComputeAggregatedPolyAndcommitment implements compute_aggregated_poly_and_commitment from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_aggregated_poly_and_commitment +func ComputeAggregatedPolyAndCommitment(blobs [][]bls.Fr, commitments []KZGCommitment) ([]bls.Fr, KZGCommitment, *bls.Fr, error) { + // create challenges + r, err := HashToBLSField(blobs, commitments) + powers := ComputePowers(r, len(blobs)) + if len(powers) == 0 { + return nil, KZGCommitment{}, nil, errors.New("powers can't be 0 length") + } + + var evaluationChallenge bls.Fr + bls.MulModFr(&evaluationChallenge, r, &powers[len(powers)-1]) + + aggregatedPoly, err := bls.PolyLinComb(blobs, powers) + if err != nil { + return nil, KZGCommitment{}, nil, err + } + + commitmentsG1 := make([]bls.G1Point, len(commitments)) + for i := 0; i < len(commitmentsG1); i++ { + p, err := bls.FromCompressedG1(commitments[i][:]) + if err != nil { + return nil, KZGCommitment{}, nil, err + } + bls.CopyG1(&commitmentsG1[i], p) + } + aggregatedCommitmentG1 := bls.LinCombG1(commitmentsG1, powers) + var aggregatedCommitment KZGCommitment + copy(aggregatedCommitment[:], bls.ToCompressedG1(aggregatedCommitmentG1)) + + return aggregatedPoly, aggregatedCommitment, &evaluationChallenge, nil +} + +// ComputeAggregateKZGProof implements compute_aggregate_kzg_proof from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_aggregate_kzg_proof +func ComputeAggregateKZGProof(blobs [][]bls.Fr) (KZGProof, error) { + commitments := make([]KZGCommitment, len(blobs)) + for i, b := range blobs { + commitments[i] = BlobToKZGCommitment(Blob(b)) + } + aggregatedPoly, _, evaluationChallenge, err := ComputeAggregatedPolyAndCommitment(blobs, commitments) + if err != nil { + return KZGProof{}, err + } + return ComputeKZGProof(aggregatedPoly, evaluationChallenge) +} + +// ComputeAggregateKZGProof implements compute_kzg_proof from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_kzg_proof +func ComputeKZGProof(polynomial []bls.Fr, z *bls.Fr) (KZGProof, error) { + y := EvaluatePolynomialInEvaluationForm(polynomial, z) + polynomialShifted := make([]bls.Fr, len(polynomial)) + for i := range polynomial { + bls.SubModFr(&polynomialShifted[i], &polynomial[i], y) + } + denominatorPoly := make([]bls.Fr, len(polynomial)) + if len(polynomial) != len(Domain) { + return KZGProof{}, errors.New("polynomial has invalid length") + } + for i := range polynomial { + if bls.EqualFr(&DomainFr[i], z) { + return KZGProof{}, errors.New("invalid z challenge") + } + bls.SubModFr(&denominatorPoly[i], &DomainFr[i], z) + } + quotientPolynomial := make([]bls.Fr, len(polynomial)) + for i := range polynomial { + bls.DivModFr("ientPolynomial[i], &polynomialShifted[i], &denominatorPoly[i]) + } + rG1 := bls.LinCombG1(kzgSetupLagrange, quotientPolynomial) + var proof KZGProof + copy(proof[:], bls.ToCompressedG1(rG1)) + return proof, nil +} + +// EvaluatePolynomialInEvaluationForm implements evaluate_polynomial_in_evaluation_form from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#evaluate_polynomial_in_evaluation_form +func EvaluatePolynomialInEvaluationForm(poly []bls.Fr, x *bls.Fr) *bls.Fr { + var result bls.Fr + bls.EvaluatePolyInEvaluationForm(&result, poly, x, DomainFr, 0) + return &result +} + +// HashToBLSField implements hash_to_bls_field from the EIP-4844 consensus specs: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#hash_to_bls_field +func HashToBLSField(polys [][]bls.Fr, comms []KZGCommitment) (*bls.Fr, error) { + sha := sha256.New() + w := codec.NewEncodingWriter(sha) + if err := w.Write([]byte(FIAT_SHAMIR_PROTOCOL_DOMAIN)); err != nil { + return nil, err + } + if err := w.WriteUint64(params.FieldElementsPerBlob); err != nil { + return nil, err + } + if err := w.WriteUint64(uint64(len(polys))); err != nil { + return nil, err + } + for _, poly := range polys { + for _, fe := range poly { + b32 := bls.FrTo32(&fe) + if err := w.Write(b32[:]); err != nil { + return nil, err + } + } + } + for _, commitment := range comms { + if err := w.Write(commitment[:]); err != nil { + return nil, err + } + } + var hash [32]byte + copy(hash[:], sha.Sum(nil)) + return BytesToBLSField(hash), nil +} diff --git a/crypto/kzg/util.go b/crypto/kzg/util.go index 3c66202487de..569fca7beb23 100644 --- a/crypto/kzg/util.go +++ b/crypto/kzg/util.go @@ -32,11 +32,6 @@ func initDomain() { } } -// EvaluatePolyInEvaluationForm evaluates the polynomial using the barycentric formula -func EvaluatePolyInEvaluationForm(yFr *bls.Fr, poly []bls.Fr, x *bls.Fr) { - bls.EvaluatePolyInEvaluationForm(yFr, poly, x, DomainFr, 0) -} - func frToBig(b *big.Int, val *bls.Fr) { //b.SetBytes((*kilicbls.Fr)(val).RedToBytes()) // silly double conversion From 0cc40feae3088f4842801254af3b3156be99a9af Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Fri, 11 Nov 2022 15:09:44 -0800 Subject: [PATCH 04/10] Start implementing a bytes API with appropriate interfaces Also fix incorrect hash type (Keccac -> Sha256) when computing versioned hash. --- core/types/data_blob.go | 95 ++++++++--------------- crypto/kzg/kzg.go | 6 ++ crypto/kzg/kzg_bytes.go | 122 +++++++++++++++++++++++++++++ crypto/kzg/kzg_new.go | 139 ++++++++++++++-------------------- signer/core/apitypes/types.go | 17 ++--- tests/kzg_bench_test.go | 6 +- tests/kzg_test.go | 13 ++-- 7 files changed, 228 insertions(+), 170 deletions(-) create mode 100644 crypto/kzg/kzg_bytes.go diff --git a/core/types/data_blob.go b/core/types/data_blob.go index 8860e81d181a..4aa2806227de 100644 --- a/core/types/data_blob.go +++ b/core/types/data_blob.go @@ -10,7 +10,6 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto/kzg" "github.com/ethereum/go-ethereum/params" - "github.com/protolambda/go-kzg/bls" "github.com/protolambda/ztyp/codec" "github.com/protolambda/ztyp/tree" ) @@ -103,6 +102,7 @@ func (p *KZGProof) UnmarshalText(text []byte) error { return hexutil.UnmarshalFixedText("KZGProof", text, p[:]) } +// BLSFieldElement is the raw bytes representation of a field element type BLSFieldElement [32]byte func (p BLSFieldElement) MarshalText() ([]byte, error) { @@ -120,6 +120,16 @@ func (p *BLSFieldElement) UnmarshalText(text []byte) error { // Blob data type Blob [params.FieldElementsPerBlob]BLSFieldElement +// kzg.Blob interface +func (blob Blob) Len() int { + return len(blob) +} + +// kzg.Blob interface +func (blob Blob) At(i int) [32]byte { + return [32]byte(blob[i]) +} + func (blob *Blob) Deserialize(dr *codec.DecodingReader) error { if blob == nil { return errors.New("cannot decode ssz into nil Blob") @@ -156,17 +166,6 @@ func (blob *Blob) HashTreeRoot(hFn tree.HashFn) tree.Root { }, params.FieldElementsPerBlob) } -// Convert a blob to kzg.Blob -func (blob *Blob) ToKZGBlob() (kzg.Blob, bool) { - frs := make([]bls.Fr, len(blob)) - for i, elem := range blob { - if !bls.FrFrom32(&frs[i], elem) { - return []bls.Fr{}, false - } - } - return kzg.Blob(frs), true -} - func (blob *Blob) MarshalText() ([]byte, error) { out := make([]byte, 2+params.FieldElementsPerBlob*32*2) copy(out[:2], "0x") @@ -209,13 +208,13 @@ func (blob *Blob) UnmarshalText(text []byte) error { type BlobKzgs []KZGCommitment -func (li BlobKzgs) toKZGCommitmentSequence() []kzg.KZGCommitment { - l := len(li) - kzgs := make([]kzg.KZGCommitment, l) - for i := 0; i < l; i++ { - kzgs[i] = kzg.KZGCommitment(li[i]) - } - return kzgs +// kzg.KZGCommitmentSequence interface +func (bk BlobKzgs) Len() int { + return len(bk) +} + +func (bk BlobKzgs) At(i int) kzg.KZGCommitment { + return kzg.KZGCommitment(bk[i]) } func (li *BlobKzgs) Deserialize(dr *codec.DecodingReader) error { @@ -254,17 +253,14 @@ func (li BlobKzgs) copy() BlobKzgs { type Blobs []Blob -// Extract the crypto material underlying these blobs -func (blobs Blobs) toKZGBlobSequence() ([][]bls.Fr, bool) { - out := make([][]bls.Fr, len(blobs)) - for i, b := range blobs { - blob, ok := b.ToKZGBlob() - if !ok { - return nil, false - } - out[i] = blob - } - return out, true +// kzg.BlobSequence interface +func (blobs Blobs) Len() int { + return len(blobs) +} + +// kzg.BlobSequence interface +func (blobs Blobs) At(i int) kzg.Blob { + return blobs[i] } func (a *Blobs) Deserialize(dr *codec.DecodingReader) error { @@ -310,11 +306,10 @@ func (blobs Blobs) ComputeCommitmentsAndAggregatedProof() (commitments []KZGComm commitments = make([]KZGCommitment, len(blobs)) versionedHashes = make([]common.Hash, len(blobs)) for i, blob := range blobs { - frs, ok := blob.ToKZGBlob() + c, ok := kzg.BlobToKZGCommitment(blob) if !ok { - return nil, nil, KZGProof{}, errors.New("invalid blob for commitment") + return nil, nil, KZGProof{}, errors.New("could not convert blob to commitment") } - c := kzg.BlobToKZGCommitment(frs) commitments[i] = KZGCommitment(c) versionedHashes[i] = common.Hash(kzg.KZGToVersionedHash(c)) } @@ -323,11 +318,7 @@ func (blobs Blobs) ComputeCommitmentsAndAggregatedProof() (commitments []KZGComm if len(blobs) != 0 { // TODO: Execution layer shouldn't be responsible for computing the proof, it should // be done in the CL. - polys, ok := blobs.toKZGBlobSequence() - if !ok { - return nil, nil, KZGProof{}, err - } - proof, err := kzg.ComputeAggregateKZGProof(polys) + proof, err := kzg.ComputeAggregateKZGProof(blobs) if err != nil { return nil, nil, KZGProof{}, err } @@ -358,27 +349,6 @@ func (b *BlobsAndCommitments) FixedLength() uint64 { return 0 } -type PolynomialAndCommitment struct { - b Blob - c KZGCommitment -} - -func (p *PolynomialAndCommitment) HashTreeRoot(hFn tree.HashFn) tree.Root { - return hFn.HashTreeRoot(&p.b, &p.c) -} - -func (p *PolynomialAndCommitment) Serialize(w *codec.EncodingWriter) error { - return w.Container(&p.b, &p.c) -} - -func (p *PolynomialAndCommitment) ByteLength() uint64 { - return codec.ContainerLength(&p.b, &p.c) -} - -func (p *PolynomialAndCommitment) FixedLength() uint64 { - return 0 -} - type BlobTxWrapper struct { Tx SignedBlobTx BlobKzgs BlobKzgs @@ -443,12 +413,7 @@ func (b *BlobTxWrapData) verifyBlobs(inner TxData) error { if err := b.verifyVersionedHash(inner); err != nil { return err } - polys, ok := b.Blobs.toKZGBlobSequence() - if !ok { - return errors.New("could not convert blobs to blob sequence") - } - kzgs := b.BlobKzgs.toKZGCommitmentSequence() - ok, err := kzg.VerifyAggregateKZGProof(polys, kzgs, kzg.KZGProof(b.KzgAggregatedProof)) + ok, err := kzg.VerifyAggregateKZGProof(b.Blobs, b.BlobKzgs, kzg.KZGProof(b.KzgAggregatedProof)) if err != nil { return fmt.Errorf("error during proof verification: %v", err) } diff --git a/crypto/kzg/kzg.go b/crypto/kzg/kzg.go index 740e311f68fa..e2fc018401b0 100644 --- a/crypto/kzg/kzg.go +++ b/crypto/kzg/kzg.go @@ -1,3 +1,9 @@ +// Package kzg implements the various EIP-4844 function specifications as defined +// in the EIP-4844 proposal and the EIP-4844 consensus specs: +// https://eips.ethereum.org/EIPS/eip-4844 +// https://github.com/roberto-bayardo/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md +// +// Most users of this package will want to use the bytes API in kzg_bytes.go package kzg import ( diff --git a/crypto/kzg/kzg_bytes.go b/crypto/kzg/kzg_bytes.go new file mode 100644 index 000000000000..cad7f1cd364d --- /dev/null +++ b/crypto/kzg/kzg_bytes.go @@ -0,0 +1,122 @@ +package kzg + +import ( + "crypto/sha256" + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/params" + "github.com/protolambda/go-kzg/bls" +) + +// The custom types from EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#custom-types +type KZGCommitment [48]byte +type KZGProof [48]byte +type VersionedHash [32]byte + +type BlobSequence interface { + Len() int + At(int) Blob +} + +type Blob interface { + Len() int + At(int) [32]byte +} + +type KZGCommitmentSequence interface { + Len() int + At(int) KZGCommitment +} + +// PointEvaluationPrecompile implements point_evaluation_precompile from EIP-4844 +func PointEvaluationPrecompile(input []byte) ([]byte, error) { + if len(input) != 192 { + return nil, errors.New("invalid input length") + } + + // versioned hash: first 32 bytes + var versionedHash [32]byte + copy(versionedHash[:], input[:32]) + + var x, y [32]byte + // Evaluation point: next 32 bytes + copy(x[:], input[32:64]) + // Expected output: next 32 bytes + copy(y[:], input[64:96]) + + // successfully converting x and y to bls.Fr confirms they are < MODULUS per the spec + var xFr, yFr bls.Fr + ok := bls.FrFrom32(&xFr, x) + if !ok { + return nil, errors.New("invalid evaluation point") + } + ok = bls.FrFrom32(&yFr, y) + if !ok { + return nil, errors.New("invalid expected output") + } + + // input kzg point: next 48 bytes + var dataKZG [48]byte + copy(dataKZG[:], input[96:144]) + if KZGToVersionedHash(KZGCommitment(dataKZG)) != VersionedHash(versionedHash) { + return nil, errors.New("mismatched versioned hash") + } + + // Quotient kzg: next 48 bytes + var quotientKZG [48]byte + copy(quotientKZG[:], input[144:192]) + + ok, err := VerifyKZGProof(KZGCommitment(dataKZG), &xFr, &yFr, KZGProof(quotientKZG)) + if err != nil { + return nil, fmt.Errorf("verify_kzg_proof error: %v", err) + } + if !ok { + return nil, errors.New("failed to verify kzg proof") + } + return []byte{}, nil +} + +// KZGToVersionedHash implements kzg_to_versioned_hash from EIP-4844 +func KZGToVersionedHash(kzg KZGCommitment) VersionedHash { + h := sha256.Sum256(kzg[:]) + h[0] = params.BlobCommitmentVersionKZG + return VersionedHash([32]byte(h)) +} + +// BlobToKZGCommitment implements blob_to_kzg_commitment from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#blob_to_kzg_commitment +func BlobToKZGCommitment(blob Blob) (KZGCommitment, bool) { + poly, ok := BlobToPolynomial(blob) + if !ok { + return KZGCommitment{}, false + } + return PolynomialToKZGCommitment(poly), true +} + +// VerifyAggregateKZGProof implements verify_aggregate_kzg_proof from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#verify_aggregate_kzg_proof +func VerifyAggregateKZGProof(blobs BlobSequence, expectedKZGCommitments KZGCommitmentSequence, kzgAggregatedProof KZGProof) (bool, error) { + polynomials, ok := BlobsToPolynomials(blobs) + if !ok { + return false, errors.New("could not convert blobs to polynomials") + } + aggregatedPoly, aggregatedPolyCommitment, evaluationChallenge, err := + ComputeAggregatedPolyAndCommitment(polynomials, expectedKZGCommitments) + if err != nil { + return false, err + } + y := EvaluatePolynomialInEvaluationForm(aggregatedPoly, evaluationChallenge) + return VerifyKZGProof(aggregatedPolyCommitment, evaluationChallenge, y, kzgAggregatedProof) +} + +// ComputeAggregateKZGProof implements compute_aggregate_kzg_proof from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_aggregate_kzg_proof +func ComputeAggregateKZGProof(blobs BlobSequence) (KZGProof, error) { + polynomials, ok := BlobsToPolynomials(blobs) + if !ok { + return KZGProof{}, errors.New("could not convert blobs to polynomials") + } + return ComputeAggregateKZGProofFromPolynomials(polynomials) +} diff --git a/crypto/kzg/kzg_new.go b/crypto/kzg/kzg_new.go index 047b9459202c..8f2661707d11 100644 --- a/crypto/kzg/kzg_new.go +++ b/crypto/kzg/kzg_new.go @@ -9,7 +9,6 @@ import ( "github.com/protolambda/go-kzg/bls" "github.com/protolambda/ztyp/codec" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) @@ -17,16 +16,17 @@ const ( FIAT_SHAMIR_PROTOCOL_DOMAIN = "FSBLOBVERIFY_V1_" ) -// The custom types from EIP-4844 consensus spec: -// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#custom-types -// We deviate from the spec slightly in that we use: -// bls.Fr for BLSFieldElement -// bls.G1Point for G1Point -// bls.G2Point for G2Point -type Blob []bls.Fr -type KZGCommitment [48]byte -type KZGProof [48]byte -type VersionedHash [32]byte +type Polynomial []bls.Fr +type Polynomials [][]bls.Fr +type CommitmentSequenceImpl []KZGCommitment + +func (s CommitmentSequenceImpl) At(i int) KZGCommitment { + return s[i] +} + +func (s CommitmentSequenceImpl) Len() int { + return len(s) +} // VerifyKZGProof implements verify_kzg_proof from the EIP-4844 consensus spec: // https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#verify_kzg_proof @@ -56,9 +56,9 @@ func VerifyKZGProofFromPoints(polynomialKZG *bls.G1Point, z *bls.Fr, y *bls.Fr, return bls.PairingsVerify(&pMinusY, &bls.GenG2, kzgProof, &xMinusZ) } -// VerifyAggregateKZGProof implements verify_aggregate_kzg_proof from the EIP-4844 consensus spec: -// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#verify_aggregate_kzg_proof -func VerifyAggregateKZGProof(blobs [][]bls.Fr, expectedKZGCommitments []KZGCommitment, kzgAggregatedProof KZGProof) (bool, error) { +// VerifyAggregateKZGProof implements verify_aggregate_kzg_proof from the EIP-4844 consensus spec, +// only operating on blobs that have already been converted into polynomials. +func VerifyAggregateKZGProofFromPolynomials(blobs Polynomials, expectedKZGCommitments KZGCommitmentSequence, kzgAggregatedProof KZGProof) (bool, error) { aggregatedPoly, aggregatedPolyCommitment, evaluationChallenge, err := ComputeAggregatedPolyAndCommitment(blobs, expectedKZGCommitments) if err != nil { @@ -68,61 +68,6 @@ func VerifyAggregateKZGProof(blobs [][]bls.Fr, expectedKZGCommitments []KZGCommi return VerifyKZGProof(aggregatedPolyCommitment, evaluationChallenge, y, kzgAggregatedProof) } -// KZGToVersionedHash implements kzg_to_versioned_hash from EIP-4844 -func KZGToVersionedHash(kzg KZGCommitment) VersionedHash { - h := crypto.Keccak256Hash(kzg[:]) - h[0] = params.BlobCommitmentVersionKZG - return VersionedHash([32]byte(h)) -} - -// PointEvaluationPrecompile implements point_evaluation_precompile from EIP-4844 -func PointEvaluationPrecompile(input []byte) ([]byte, error) { - if len(input) != 192 { - return nil, errors.New("invalid input length") - } - - // versioned hash: first 32 bytes - var versionedHash [32]byte - copy(versionedHash[:], input[:32]) - - var x, y [32]byte - // Evaluation point: next 32 bytes - copy(x[:], input[32:64]) - // Expected output: next 32 bytes - copy(y[:], input[64:96]) - - // successfully converting x and y to bls.Fr confirms they are < MODULUS per the spec - var xFr, yFr bls.Fr - ok := bls.FrFrom32(&xFr, x) - if !ok { - return nil, errors.New("invalid evaluation point") - } - ok = bls.FrFrom32(&yFr, y) - if !ok { - return nil, errors.New("invalid expected output") - } - - // input kzg point: next 48 bytes - var dataKZG [48]byte - copy(dataKZG[:], input[96:144]) - if KZGToVersionedHash(KZGCommitment(dataKZG)) != VersionedHash(versionedHash) { - return nil, errors.New("mismatched versioned hash") - } - - // Quotient kzg: next 48 bytes - var quotientKZG [48]byte - copy(quotientKZG[:], input[144:192]) - - ok, err := VerifyKZGProof(KZGCommitment(dataKZG), &xFr, &yFr, KZGProof(quotientKZG)) - if err != nil { - return nil, fmt.Errorf("verify_kzg_proof error: %v", err) - } - if !ok { - return nil, errors.New("failed to verify kzg proof") - } - return []byte{}, nil -} - // ComputePowers implements compute_powers from the EIP-4844 consensus spec: // https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_powers func ComputePowers(r *bls.Fr, n int) []bls.Fr { @@ -136,9 +81,7 @@ func ComputePowers(r *bls.Fr, n int) []bls.Fr { return powers } -// BlobToKZGCommitment implements blob_to_kzg_commitment from the EIP-4844 consensus spec: -// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#blob_to_kzg_commitment -func BlobToKZGCommitment(eval Blob) KZGCommitment { +func PolynomialToKZGCommitment(eval Polynomial) KZGCommitment { g1 := bls.LinCombG1(kzgSetupLagrange, []bls.Fr(eval)) var out KZGCommitment copy(out[:], bls.ToCompressedG1(g1)) @@ -161,7 +104,7 @@ func BytesToBLSField(h [32]byte) *bls.Fr { // ComputeAggregatedPolyAndcommitment implements compute_aggregated_poly_and_commitment from the EIP-4844 consensus spec: // https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_aggregated_poly_and_commitment -func ComputeAggregatedPolyAndCommitment(blobs [][]bls.Fr, commitments []KZGCommitment) ([]bls.Fr, KZGCommitment, *bls.Fr, error) { +func ComputeAggregatedPolyAndCommitment(blobs Polynomials, commitments KZGCommitmentSequence) ([]bls.Fr, KZGCommitment, *bls.Fr, error) { // create challenges r, err := HashToBLSField(blobs, commitments) powers := ComputePowers(r, len(blobs)) @@ -177,9 +120,11 @@ func ComputeAggregatedPolyAndCommitment(blobs [][]bls.Fr, commitments []KZGCommi return nil, KZGCommitment{}, nil, err } - commitmentsG1 := make([]bls.G1Point, len(commitments)) - for i := 0; i < len(commitmentsG1); i++ { - p, err := bls.FromCompressedG1(commitments[i][:]) + l := commitments.Len() + commitmentsG1 := make([]bls.G1Point, l) + for i := 0; i < l; i++ { + c := commitments.At(i) + p, err := bls.FromCompressedG1(c[:]) if err != nil { return nil, KZGCommitment{}, nil, err } @@ -192,12 +137,12 @@ func ComputeAggregatedPolyAndCommitment(blobs [][]bls.Fr, commitments []KZGCommi return aggregatedPoly, aggregatedCommitment, &evaluationChallenge, nil } -// ComputeAggregateKZGProof implements compute_aggregate_kzg_proof from the EIP-4844 consensus spec: -// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_aggregate_kzg_proof -func ComputeAggregateKZGProof(blobs [][]bls.Fr) (KZGProof, error) { - commitments := make([]KZGCommitment, len(blobs)) +// ComputeAggregateKZGProofFromPolynomials implements compute_aggregate_kzg_proof from the EIP-4844 +// consensus spec, only operating over blobs that are already parsed into a polynomial. +func ComputeAggregateKZGProofFromPolynomials(blobs Polynomials) (KZGProof, error) { + commitments := make(CommitmentSequenceImpl, len(blobs)) for i, b := range blobs { - commitments[i] = BlobToKZGCommitment(Blob(b)) + commitments[i] = PolynomialToKZGCommitment(Polynomial(b)) } aggregatedPoly, _, evaluationChallenge, err := ComputeAggregatedPolyAndCommitment(blobs, commitments) if err != nil { @@ -244,7 +189,7 @@ func EvaluatePolynomialInEvaluationForm(poly []bls.Fr, x *bls.Fr) *bls.Fr { // HashToBLSField implements hash_to_bls_field from the EIP-4844 consensus specs: // https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#hash_to_bls_field -func HashToBLSField(polys [][]bls.Fr, comms []KZGCommitment) (*bls.Fr, error) { +func HashToBLSField(polys Polynomials, comms KZGCommitmentSequence) (*bls.Fr, error) { sha := sha256.New() w := codec.NewEncodingWriter(sha) if err := w.Write([]byte(FIAT_SHAMIR_PROTOCOL_DOMAIN)); err != nil { @@ -264,8 +209,10 @@ func HashToBLSField(polys [][]bls.Fr, comms []KZGCommitment) (*bls.Fr, error) { } } } - for _, commitment := range comms { - if err := w.Write(commitment[:]); err != nil { + l := comms.Len() + for i := 0; i < l; i++ { + c := comms.At(i) + if err := w.Write(c[:]); err != nil { return nil, err } } @@ -273,3 +220,27 @@ func HashToBLSField(polys [][]bls.Fr, comms []KZGCommitment) (*bls.Fr, error) { copy(hash[:], sha.Sum(nil)) return BytesToBLSField(hash), nil } + +func BlobToPolynomial(b Blob) (Polynomial, bool) { + l := b.Len() + frs := make(Polynomial, l) + for i := 0; i < l; i++ { + if !bls.FrFrom32(&frs[i], b.At(i)) { + return []bls.Fr{}, false + } + } + return frs, true +} + +func BlobsToPolynomials(blobs BlobSequence) ([][]bls.Fr, bool) { + l := blobs.Len() + out := make(Polynomials, l) + for i := 0; i < l; i++ { + blob, ok := BlobToPolynomial(blobs.At(i)) + if !ok { + return nil, false + } + out[i] = blob + } + return out, true +} diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go index 8c4e672348df..c71178794bc9 100644 --- a/signer/core/apitypes/types.go +++ b/signer/core/apitypes/types.go @@ -149,17 +149,14 @@ func (args *SendTxArgs) ToTransaction() *types.Transaction { msg.Data = input msg.AccessList = types.AccessListView(al) wrapData := types.BlobTxWrapData{} - for _, bl := range args.Blobs { - frs, ok := bl.ToKZGBlob() - if !ok { - // invalid BLS blob data (e.g. element not within field element range) - continue // can't error, so ignore the malformed blob + for _, blob := range args.Blobs { + c, ok := kzg.BlobToKZGCommitment(blob) + if ok { + versionedHash := common.Hash(kzg.KZGToVersionedHash(c)) + msg.BlobVersionedHashes = append(msg.BlobVersionedHashes, versionedHash) + wrapData.BlobKzgs = append(wrapData.BlobKzgs, types.KZGCommitment(c)) + wrapData.Blobs = append(wrapData.Blobs, blob) } - commitment := types.KZGCommitment(kzg.BlobToKZGCommitment(frs)) - versionedHash := common.Hash(kzg.KZGToVersionedHash(kzg.KZGCommitment(commitment))) - msg.BlobVersionedHashes = append(msg.BlobVersionedHashes, versionedHash) - wrapData.BlobKzgs = append(wrapData.BlobKzgs, commitment) - wrapData.Blobs = append(wrapData.Blobs, bl) } _, _, aggProof, err := types.Blobs(args.Blobs).ComputeCommitmentsAndAggregatedProof() if err == nil { diff --git a/tests/kzg_bench_test.go b/tests/kzg_bench_test.go index 56239dea1f03..57b0b0db80f1 100644 --- a/tests/kzg_bench_test.go +++ b/tests/kzg_bench_test.go @@ -15,8 +15,8 @@ import ( "github.com/protolambda/ztyp/view" ) -func randomBlob() kzg.Blob { - blob := make(kzg.Blob, params.FieldElementsPerBlob) +func randomBlob() kzg.Polynomial { + blob := make(kzg.Polynomial, params.FieldElementsPerBlob) for i := 0; i < len(blob); i++ { blob[i] = *bls.RandomFr() } @@ -27,7 +27,7 @@ func BenchmarkBlobToKzg(b *testing.B) { blob := randomBlob() b.ResetTimer() for i := 0; i < b.N; i++ { - kzg.BlobToKZGCommitment(blob) + kzg.PolynomialToKZGCommitment(blob) } } diff --git a/tests/kzg_test.go b/tests/kzg_test.go index 621bbe35cc10..60116006a373 100644 --- a/tests/kzg_test.go +++ b/tests/kzg_test.go @@ -156,20 +156,17 @@ func TestVerifyBlobs(t *testing.T) { copy(blob1[i][:], jsonBlobs.KzgBlob1[i*31:(i+1)*31]) copy(blob2[i][:], jsonBlobs.KzgBlob2[i*31:(i+1)*31]) } - // Compute KZG commitments for both of the blobs above - frs1, ok1 := blob1.ToKZGBlob() - frs2, ok2 := blob2.ToKZGBlob() + kzg1, ok1 := kzg.BlobToKZGCommitment(blob1.ToCryptoBlob()) + kzg2, ok2 := kzg.BlobToKZGCommitment(blob2.ToCryptoBlob()) if ok1 == false || ok2 == false { panic("failed to convert blobs") } - kzg1 := types.KZGCommitment(kzg.BlobToKZGCommitment(frs1)) - kzg2 := types.KZGCommitment(kzg.BlobToKZGCommitment(frs2)) // Create the dummy object with all that data we prepared blobData := types.BlobTxWrapData{ - BlobKzgs: []types.KZGCommitment{kzg1, kzg2}, - Blobs: []types.Blob{blob1, blob2}, + BlobKzgs: []types.KZGCommitment{types.KZGCommitment(kzg1), types.KZGCommitment(kzg2)}, + Blobs: []types.Blob{types.Blob(blob1), types.Blob(blob2)}, } var hashes []common.Hash @@ -225,7 +222,7 @@ func TestPointEvaluationTestVector(t *testing.T) { } // Create a commitment - commitment := kzg.BlobToKZGCommitment(evalPoly) + commitment := kzg.PolynomialToKZGCommitment(evalPoly) // Create proof for testing x := uint64(0x42) From 6cbb79e1af513e49dc239cecd5715d8fcae2e221 Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Sat, 12 Nov 2022 07:39:54 -0800 Subject: [PATCH 05/10] pure bytes API for VerifyKZGProof per 3097, and move kzg_new back into kzg.go now that updates are near complete --- crypto/kzg/kzg.go | 229 +++++++++++++++++++++++++++++++++++++ crypto/kzg/kzg_bytes.go | 44 ++++--- crypto/kzg/kzg_new.go | 246 ---------------------------------------- tests/kzg_bench_test.go | 9 +- tests/kzg_test.go | 5 +- 5 files changed, 267 insertions(+), 266 deletions(-) delete mode 100644 crypto/kzg/kzg_new.go diff --git a/crypto/kzg/kzg.go b/crypto/kzg/kzg.go index e2fc018401b0..b0a22d42d04f 100644 --- a/crypto/kzg/kzg.go +++ b/crypto/kzg/kzg.go @@ -7,12 +7,25 @@ package kzg import ( + "crypto/sha256" "encoding/json" + "errors" + "fmt" + "math/big" "math/bits" + "github.com/ethereum/go-ethereum/params" "github.com/protolambda/go-kzg/bls" + "github.com/protolambda/ztyp/codec" ) +const ( + FIAT_SHAMIR_PROTOCOL_DOMAIN = "FSBLOBVERIFY_V1_" +) + +type Polynomial []bls.Fr +type Polynomials [][]bls.Fr + // KZG CRS for G2 var kzgSetupG2 []bls.G2Point @@ -73,3 +86,219 @@ func bitReversalPermutation(l []bls.G1Point) []bls.G1Point { return out } + +// VerifyKZGProof implements verify_kzg_proof from the EIP-4844 consensus spec, +// only with the byte inputs already parsed into points & field elements. +func VerifyKZGProofFromPoints(polynomialKZG *bls.G1Point, z *bls.Fr, y *bls.Fr, kzgProof *bls.G1Point) bool { + var zG2 bls.G2Point + bls.MulG2(&zG2, &bls.GenG2, z) + var yG1 bls.G1Point + bls.MulG1(&yG1, &bls.GenG1, y) + + var xMinusZ bls.G2Point + bls.SubG2(&xMinusZ, &kzgSetupG2[1], &zG2) + var pMinusY bls.G1Point + bls.SubG1(&pMinusY, polynomialKZG, &yG1) + + return bls.PairingsVerify(&pMinusY, &bls.GenG2, kzgProof, &xMinusZ) +} + +// VerifyAggregateKZGProof implements verify_aggregate_kzg_proof from the EIP-4844 consensus spec, +// only operating on blobs that have already been converted into polynomials. +func VerifyAggregateKZGProofFromPolynomials(blobs Polynomials, expectedKZGCommitments KZGCommitmentSequence, kzgAggregatedProof KZGProof) (bool, error) { + aggregatedPoly, aggregatedPolyCommitment, evaluationChallenge, err := + ComputeAggregatedPolyAndCommitment(blobs, expectedKZGCommitments) + if err != nil { + return false, err + } + y := EvaluatePolynomialInEvaluationForm(aggregatedPoly, evaluationChallenge) + kzgProofG1, err := bls.FromCompressedG1(kzgAggregatedProof[:]) + if err != nil { + return false, fmt.Errorf("failed to decode kzgProof: %v", err) + } + return VerifyKZGProofFromPoints(aggregatedPolyCommitment, evaluationChallenge, y, kzgProofG1), nil +} + +// ComputePowers implements compute_powers from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_powers +func ComputePowers(r *bls.Fr, n int) []bls.Fr { + var currentPower bls.Fr + bls.AsFr(¤tPower, 1) + powers := make([]bls.Fr, n) + for i := range powers { + powers[i] = currentPower + bls.MulModFr(¤tPower, ¤tPower, r) + } + return powers +} + +func PolynomialToKZGCommitment(eval Polynomial) KZGCommitment { + g1 := bls.LinCombG1(kzgSetupLagrange, []bls.Fr(eval)) + var out KZGCommitment + copy(out[:], bls.ToCompressedG1(g1)) + return out +} + +// BytesToBLSField implements bytes_to_bls_field from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#bytes_to_bls_field +func BytesToBLSField(h [32]byte) *bls.Fr { + // re-interpret as little-endian + var b [32]byte = h + for i := 0; i < 16; i++ { + b[31-i], b[i] = b[i], b[31-i] + } + zB := new(big.Int).Mod(new(big.Int).SetBytes(b[:]), BLSModulus) + out := new(bls.Fr) + BigToFr(out, zB) + return out +} + +// ComputeAggregatedPolyAndcommitment implements compute_aggregated_poly_and_commitment from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_aggregated_poly_and_commitment +func ComputeAggregatedPolyAndCommitment(blobs Polynomials, commitments KZGCommitmentSequence) ([]bls.Fr, *bls.G1Point, *bls.Fr, error) { + // create challenges + r, err := HashToBLSField(blobs, commitments) + powers := ComputePowers(r, len(blobs)) + if len(powers) == 0 { + return nil, nil, nil, errors.New("powers can't be 0 length") + } + + var evaluationChallenge bls.Fr + bls.MulModFr(&evaluationChallenge, r, &powers[len(powers)-1]) + + aggregatedPoly, err := bls.PolyLinComb(blobs, powers) + if err != nil { + return nil, nil, nil, err + } + + l := commitments.Len() + commitmentsG1 := make([]bls.G1Point, l) + for i := 0; i < l; i++ { + c := commitments.At(i) + p, err := bls.FromCompressedG1(c[:]) + if err != nil { + return nil, nil, nil, err + } + bls.CopyG1(&commitmentsG1[i], p) + } + aggregatedCommitmentG1 := bls.LinCombG1(commitmentsG1, powers) + return aggregatedPoly, aggregatedCommitmentG1, &evaluationChallenge, nil +} + +type commitmentSequenceImpl []KZGCommitment + +func (s commitmentSequenceImpl) At(i int) KZGCommitment { + return s[i] +} + +func (s commitmentSequenceImpl) Len() int { + return len(s) +} + +// ComputeAggregateKZGProofFromPolynomials implements compute_aggregate_kzg_proof from the EIP-4844 +// consensus spec, only operating over blobs that are already parsed into a polynomial. +func ComputeAggregateKZGProofFromPolynomials(blobs Polynomials) (KZGProof, error) { + commitments := make(commitmentSequenceImpl, len(blobs)) + for i, b := range blobs { + commitments[i] = PolynomialToKZGCommitment(Polynomial(b)) + } + aggregatedPoly, _, evaluationChallenge, err := ComputeAggregatedPolyAndCommitment(blobs, commitments) + if err != nil { + return KZGProof{}, err + } + return ComputeKZGProof(aggregatedPoly, evaluationChallenge) +} + +// ComputeAggregateKZGProof implements compute_kzg_proof from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_kzg_proof +func ComputeKZGProof(polynomial []bls.Fr, z *bls.Fr) (KZGProof, error) { + y := EvaluatePolynomialInEvaluationForm(polynomial, z) + polynomialShifted := make([]bls.Fr, len(polynomial)) + for i := range polynomial { + bls.SubModFr(&polynomialShifted[i], &polynomial[i], y) + } + denominatorPoly := make([]bls.Fr, len(polynomial)) + if len(polynomial) != len(Domain) { + return KZGProof{}, errors.New("polynomial has invalid length") + } + for i := range polynomial { + if bls.EqualFr(&DomainFr[i], z) { + return KZGProof{}, errors.New("invalid z challenge") + } + bls.SubModFr(&denominatorPoly[i], &DomainFr[i], z) + } + quotientPolynomial := make([]bls.Fr, len(polynomial)) + for i := range polynomial { + bls.DivModFr("ientPolynomial[i], &polynomialShifted[i], &denominatorPoly[i]) + } + rG1 := bls.LinCombG1(kzgSetupLagrange, quotientPolynomial) + var proof KZGProof + copy(proof[:], bls.ToCompressedG1(rG1)) + return proof, nil +} + +// EvaluatePolynomialInEvaluationForm implements evaluate_polynomial_in_evaluation_form from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#evaluate_polynomial_in_evaluation_form +func EvaluatePolynomialInEvaluationForm(poly []bls.Fr, x *bls.Fr) *bls.Fr { + var result bls.Fr + bls.EvaluatePolyInEvaluationForm(&result, poly, x, DomainFr, 0) + return &result +} + +// HashToBLSField implements hash_to_bls_field from the EIP-4844 consensus specs: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#hash_to_bls_field +func HashToBLSField(polys Polynomials, comms KZGCommitmentSequence) (*bls.Fr, error) { + sha := sha256.New() + w := codec.NewEncodingWriter(sha) + if err := w.Write([]byte(FIAT_SHAMIR_PROTOCOL_DOMAIN)); err != nil { + return nil, err + } + if err := w.WriteUint64(params.FieldElementsPerBlob); err != nil { + return nil, err + } + if err := w.WriteUint64(uint64(len(polys))); err != nil { + return nil, err + } + for _, poly := range polys { + for _, fe := range poly { + b32 := bls.FrTo32(&fe) + if err := w.Write(b32[:]); err != nil { + return nil, err + } + } + } + l := comms.Len() + for i := 0; i < l; i++ { + c := comms.At(i) + if err := w.Write(c[:]); err != nil { + return nil, err + } + } + var hash [32]byte + copy(hash[:], sha.Sum(nil)) + return BytesToBLSField(hash), nil +} + +func BlobToPolynomial(b Blob) (Polynomial, bool) { + l := b.Len() + frs := make(Polynomial, l) + for i := 0; i < l; i++ { + if !bls.FrFrom32(&frs[i], b.At(i)) { + return []bls.Fr{}, false + } + } + return frs, true +} + +func BlobsToPolynomials(blobs BlobSequence) ([][]bls.Fr, bool) { + l := blobs.Len() + out := make(Polynomials, l) + for i := 0; i < l; i++ { + blob, ok := BlobToPolynomial(blobs.At(i)) + if !ok { + return nil, false + } + out[i] = blob + } + return out, true +} diff --git a/crypto/kzg/kzg_bytes.go b/crypto/kzg/kzg_bytes.go index cad7f1cd364d..b0263565b4c7 100644 --- a/crypto/kzg/kzg_bytes.go +++ b/crypto/kzg/kzg_bytes.go @@ -35,7 +35,6 @@ func PointEvaluationPrecompile(input []byte) ([]byte, error) { if len(input) != 192 { return nil, errors.New("invalid input length") } - // versioned hash: first 32 bytes var versionedHash [32]byte copy(versionedHash[:], input[:32]) @@ -46,17 +45,6 @@ func PointEvaluationPrecompile(input []byte) ([]byte, error) { // Expected output: next 32 bytes copy(y[:], input[64:96]) - // successfully converting x and y to bls.Fr confirms they are < MODULUS per the spec - var xFr, yFr bls.Fr - ok := bls.FrFrom32(&xFr, x) - if !ok { - return nil, errors.New("invalid evaluation point") - } - ok = bls.FrFrom32(&yFr, y) - if !ok { - return nil, errors.New("invalid expected output") - } - // input kzg point: next 48 bytes var dataKZG [48]byte copy(dataKZG[:], input[96:144]) @@ -68,7 +56,7 @@ func PointEvaluationPrecompile(input []byte) ([]byte, error) { var quotientKZG [48]byte copy(quotientKZG[:], input[144:192]) - ok, err := VerifyKZGProof(KZGCommitment(dataKZG), &xFr, &yFr, KZGProof(quotientKZG)) + ok, err := VerifyKZGProof(KZGCommitment(dataKZG), x, y, KZGProof(quotientKZG)) if err != nil { return nil, fmt.Errorf("verify_kzg_proof error: %v", err) } @@ -78,6 +66,30 @@ func PointEvaluationPrecompile(input []byte) ([]byte, error) { return []byte{}, nil } +// VerifyKZGProof implements verify_kzg_proof from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#verify_kzg_proof +func VerifyKZGProof(polynomialKZG KZGCommitment, z, y [32]byte, kzgProof KZGProof) (bool, error) { + // successfully converting z and y to bls.Fr confirms they are < MODULUS per the spec + var zFr, yFr bls.Fr + ok := bls.FrFrom32(&yFr, z) + if !ok { + return false, errors.New("invalid evaluation point") + } + ok = bls.FrFrom32(&yFr, y) + if !ok { + return false, errors.New("invalid expected output") + } + polynomialKZGG1, err := bls.FromCompressedG1(polynomialKZG[:]) + if err != nil { + return false, fmt.Errorf("failed to decode polynomialKZG: %v", err) + } + kzgProofG1, err := bls.FromCompressedG1(kzgProof[:]) + if err != nil { + return false, fmt.Errorf("failed to decode kzgProof: %v", err) + } + return VerifyKZGProofFromPoints(polynomialKZGG1, &zFr, &yFr, kzgProofG1), nil +} + // KZGToVersionedHash implements kzg_to_versioned_hash from EIP-4844 func KZGToVersionedHash(kzg KZGCommitment) VersionedHash { h := sha256.Sum256(kzg[:]) @@ -108,7 +120,11 @@ func VerifyAggregateKZGProof(blobs BlobSequence, expectedKZGCommitments KZGCommi return false, err } y := EvaluatePolynomialInEvaluationForm(aggregatedPoly, evaluationChallenge) - return VerifyKZGProof(aggregatedPolyCommitment, evaluationChallenge, y, kzgAggregatedProof) + kzgProofG1, err := bls.FromCompressedG1(kzgAggregatedProof[:]) + if err != nil { + return false, fmt.Errorf("failed to decode kzgProof: %v", err) + } + return VerifyKZGProofFromPoints(aggregatedPolyCommitment, evaluationChallenge, y, kzgProofG1), nil } // ComputeAggregateKZGProof implements compute_aggregate_kzg_proof from the EIP-4844 consensus spec: diff --git a/crypto/kzg/kzg_new.go b/crypto/kzg/kzg_new.go deleted file mode 100644 index 8f2661707d11..000000000000 --- a/crypto/kzg/kzg_new.go +++ /dev/null @@ -1,246 +0,0 @@ -package kzg - -import ( - "crypto/sha256" - "errors" - "fmt" - "math/big" - - "github.com/protolambda/go-kzg/bls" - "github.com/protolambda/ztyp/codec" - - "github.com/ethereum/go-ethereum/params" -) - -const ( - FIAT_SHAMIR_PROTOCOL_DOMAIN = "FSBLOBVERIFY_V1_" -) - -type Polynomial []bls.Fr -type Polynomials [][]bls.Fr -type CommitmentSequenceImpl []KZGCommitment - -func (s CommitmentSequenceImpl) At(i int) KZGCommitment { - return s[i] -} - -func (s CommitmentSequenceImpl) Len() int { - return len(s) -} - -// VerifyKZGProof implements verify_kzg_proof from the EIP-4844 consensus spec: -// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#verify_kzg_proof -func VerifyKZGProof(polynomialKZG KZGCommitment, z *bls.Fr, y *bls.Fr, kzgProof KZGProof) (bool, error) { - polynomialKZGG1, err := bls.FromCompressedG1(polynomialKZG[:]) - if err != nil { - return false, fmt.Errorf("failed to decode polynomialKZG: %v", err) - } - kzgProofG1, err := bls.FromCompressedG1(kzgProof[:]) - if err != nil { - return false, fmt.Errorf("failed to decode kzgProof: %v", err) - } - return VerifyKZGProofFromPoints(polynomialKZGG1, z, y, kzgProofG1), nil -} - -func VerifyKZGProofFromPoints(polynomialKZG *bls.G1Point, z *bls.Fr, y *bls.Fr, kzgProof *bls.G1Point) bool { - var zG2 bls.G2Point - bls.MulG2(&zG2, &bls.GenG2, z) - var yG1 bls.G1Point - bls.MulG1(&yG1, &bls.GenG1, y) - - var xMinusZ bls.G2Point - bls.SubG2(&xMinusZ, &kzgSetupG2[1], &zG2) - var pMinusY bls.G1Point - bls.SubG1(&pMinusY, polynomialKZG, &yG1) - - return bls.PairingsVerify(&pMinusY, &bls.GenG2, kzgProof, &xMinusZ) -} - -// VerifyAggregateKZGProof implements verify_aggregate_kzg_proof from the EIP-4844 consensus spec, -// only operating on blobs that have already been converted into polynomials. -func VerifyAggregateKZGProofFromPolynomials(blobs Polynomials, expectedKZGCommitments KZGCommitmentSequence, kzgAggregatedProof KZGProof) (bool, error) { - aggregatedPoly, aggregatedPolyCommitment, evaluationChallenge, err := - ComputeAggregatedPolyAndCommitment(blobs, expectedKZGCommitments) - if err != nil { - return false, err - } - y := EvaluatePolynomialInEvaluationForm(aggregatedPoly, evaluationChallenge) - return VerifyKZGProof(aggregatedPolyCommitment, evaluationChallenge, y, kzgAggregatedProof) -} - -// ComputePowers implements compute_powers from the EIP-4844 consensus spec: -// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_powers -func ComputePowers(r *bls.Fr, n int) []bls.Fr { - var currentPower bls.Fr - bls.AsFr(¤tPower, 1) - powers := make([]bls.Fr, n) - for i := range powers { - powers[i] = currentPower - bls.MulModFr(¤tPower, ¤tPower, r) - } - return powers -} - -func PolynomialToKZGCommitment(eval Polynomial) KZGCommitment { - g1 := bls.LinCombG1(kzgSetupLagrange, []bls.Fr(eval)) - var out KZGCommitment - copy(out[:], bls.ToCompressedG1(g1)) - return out -} - -// BytesToBLSField implements bytes_to_bls_field from the EIP-4844 consensus spec: -// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#bytes_to_bls_field -func BytesToBLSField(h [32]byte) *bls.Fr { - // re-interpret as little-endian - var b [32]byte = h - for i := 0; i < 16; i++ { - b[31-i], b[i] = b[i], b[31-i] - } - zB := new(big.Int).Mod(new(big.Int).SetBytes(b[:]), BLSModulus) - out := new(bls.Fr) - BigToFr(out, zB) - return out -} - -// ComputeAggregatedPolyAndcommitment implements compute_aggregated_poly_and_commitment from the EIP-4844 consensus spec: -// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_aggregated_poly_and_commitment -func ComputeAggregatedPolyAndCommitment(blobs Polynomials, commitments KZGCommitmentSequence) ([]bls.Fr, KZGCommitment, *bls.Fr, error) { - // create challenges - r, err := HashToBLSField(blobs, commitments) - powers := ComputePowers(r, len(blobs)) - if len(powers) == 0 { - return nil, KZGCommitment{}, nil, errors.New("powers can't be 0 length") - } - - var evaluationChallenge bls.Fr - bls.MulModFr(&evaluationChallenge, r, &powers[len(powers)-1]) - - aggregatedPoly, err := bls.PolyLinComb(blobs, powers) - if err != nil { - return nil, KZGCommitment{}, nil, err - } - - l := commitments.Len() - commitmentsG1 := make([]bls.G1Point, l) - for i := 0; i < l; i++ { - c := commitments.At(i) - p, err := bls.FromCompressedG1(c[:]) - if err != nil { - return nil, KZGCommitment{}, nil, err - } - bls.CopyG1(&commitmentsG1[i], p) - } - aggregatedCommitmentG1 := bls.LinCombG1(commitmentsG1, powers) - var aggregatedCommitment KZGCommitment - copy(aggregatedCommitment[:], bls.ToCompressedG1(aggregatedCommitmentG1)) - - return aggregatedPoly, aggregatedCommitment, &evaluationChallenge, nil -} - -// ComputeAggregateKZGProofFromPolynomials implements compute_aggregate_kzg_proof from the EIP-4844 -// consensus spec, only operating over blobs that are already parsed into a polynomial. -func ComputeAggregateKZGProofFromPolynomials(blobs Polynomials) (KZGProof, error) { - commitments := make(CommitmentSequenceImpl, len(blobs)) - for i, b := range blobs { - commitments[i] = PolynomialToKZGCommitment(Polynomial(b)) - } - aggregatedPoly, _, evaluationChallenge, err := ComputeAggregatedPolyAndCommitment(blobs, commitments) - if err != nil { - return KZGProof{}, err - } - return ComputeKZGProof(aggregatedPoly, evaluationChallenge) -} - -// ComputeAggregateKZGProof implements compute_kzg_proof from the EIP-4844 consensus spec: -// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_kzg_proof -func ComputeKZGProof(polynomial []bls.Fr, z *bls.Fr) (KZGProof, error) { - y := EvaluatePolynomialInEvaluationForm(polynomial, z) - polynomialShifted := make([]bls.Fr, len(polynomial)) - for i := range polynomial { - bls.SubModFr(&polynomialShifted[i], &polynomial[i], y) - } - denominatorPoly := make([]bls.Fr, len(polynomial)) - if len(polynomial) != len(Domain) { - return KZGProof{}, errors.New("polynomial has invalid length") - } - for i := range polynomial { - if bls.EqualFr(&DomainFr[i], z) { - return KZGProof{}, errors.New("invalid z challenge") - } - bls.SubModFr(&denominatorPoly[i], &DomainFr[i], z) - } - quotientPolynomial := make([]bls.Fr, len(polynomial)) - for i := range polynomial { - bls.DivModFr("ientPolynomial[i], &polynomialShifted[i], &denominatorPoly[i]) - } - rG1 := bls.LinCombG1(kzgSetupLagrange, quotientPolynomial) - var proof KZGProof - copy(proof[:], bls.ToCompressedG1(rG1)) - return proof, nil -} - -// EvaluatePolynomialInEvaluationForm implements evaluate_polynomial_in_evaluation_form from the EIP-4844 consensus spec: -// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#evaluate_polynomial_in_evaluation_form -func EvaluatePolynomialInEvaluationForm(poly []bls.Fr, x *bls.Fr) *bls.Fr { - var result bls.Fr - bls.EvaluatePolyInEvaluationForm(&result, poly, x, DomainFr, 0) - return &result -} - -// HashToBLSField implements hash_to_bls_field from the EIP-4844 consensus specs: -// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#hash_to_bls_field -func HashToBLSField(polys Polynomials, comms KZGCommitmentSequence) (*bls.Fr, error) { - sha := sha256.New() - w := codec.NewEncodingWriter(sha) - if err := w.Write([]byte(FIAT_SHAMIR_PROTOCOL_DOMAIN)); err != nil { - return nil, err - } - if err := w.WriteUint64(params.FieldElementsPerBlob); err != nil { - return nil, err - } - if err := w.WriteUint64(uint64(len(polys))); err != nil { - return nil, err - } - for _, poly := range polys { - for _, fe := range poly { - b32 := bls.FrTo32(&fe) - if err := w.Write(b32[:]); err != nil { - return nil, err - } - } - } - l := comms.Len() - for i := 0; i < l; i++ { - c := comms.At(i) - if err := w.Write(c[:]); err != nil { - return nil, err - } - } - var hash [32]byte - copy(hash[:], sha.Sum(nil)) - return BytesToBLSField(hash), nil -} - -func BlobToPolynomial(b Blob) (Polynomial, bool) { - l := b.Len() - frs := make(Polynomial, l) - for i := 0; i < l; i++ { - if !bls.FrFrom32(&frs[i], b.At(i)) { - return []bls.Fr{}, false - } - } - return frs, true -} - -func BlobsToPolynomials(blobs BlobSequence) ([][]bls.Fr, bool) { - l := blobs.Len() - out := make(Polynomials, l) - for i := 0; i < l; i++ { - blob, ok := BlobToPolynomial(blobs.At(i)) - if !ok { - return nil, false - } - out[i] = blob - } - return out, true -} diff --git a/tests/kzg_bench_test.go b/tests/kzg_bench_test.go index 57b0b0db80f1..8570b2ca39c9 100644 --- a/tests/kzg_bench_test.go +++ b/tests/kzg_bench_test.go @@ -1,3 +1,4 @@ +// TODO: Migrate these to crypto/kzg package tests import ( @@ -40,11 +41,11 @@ func BenchmarkVerifyBlobs(b *testing.B) { for j := range tmp { blobs[i][j] = bls.FrTo32(&tmp[j]) } - frs, ok := blobs[i].ToKZGBlob() + frs, ok := kzg.BlobToPolynomial(blobs[i]) if !ok { b.Fatal("Could not compute commitment") } - c := types.KZGCommitment(kzg.BlobToKZGCommitment(frs)) + c := types.KZGCommitment(kzg.PolynomialToKZGCommitment(frs)) commitments = append(commitments, c) h := common.Hash(kzg.KZGToVersionedHash(kzg.KZGCommitment(c))) hashes = append(hashes, h) @@ -96,7 +97,7 @@ func BenchmarkVerifyKZGProof(b *testing.B) { // Now let's start testing the kzg module // Create a commitment - k := kzg.BlobToKZGCommitment(evalPoly) + k := kzg.PolynomialToKZGCommitment(evalPoly) commitment, _ := bls.FromCompressedG1(k[:]) // Create proof for testing @@ -135,7 +136,7 @@ func BenchmarkVerifyMultiple(b *testing.B) { blobElements[j] = bls.FrTo32(&blob[j]) } blobs = append(blobs, blobElements) - c := types.KZGCommitment(kzg.BlobToKZGCommitment(blob)) + c := types.KZGCommitment(kzg.PolynomialToKZGCommitment(blob)) commitments = append(commitments, c) h := common.Hash(kzg.KZGToVersionedHash(kzg.KZGCommitment(c))) hashes = append(hashes, h) diff --git a/tests/kzg_test.go b/tests/kzg_test.go index 60116006a373..be323c810254 100644 --- a/tests/kzg_test.go +++ b/tests/kzg_test.go @@ -1,3 +1,4 @@ +// TODO: Migrate these to crypto/kzg package tests import ( @@ -157,8 +158,8 @@ func TestVerifyBlobs(t *testing.T) { copy(blob2[i][:], jsonBlobs.KzgBlob2[i*31:(i+1)*31]) } // Compute KZG commitments for both of the blobs above - kzg1, ok1 := kzg.BlobToKZGCommitment(blob1.ToCryptoBlob()) - kzg2, ok2 := kzg.BlobToKZGCommitment(blob2.ToCryptoBlob()) + kzg1, ok1 := kzg.BlobToKZGCommitment(blob1) + kzg2, ok2 := kzg.BlobToKZGCommitment(blob2) if ok1 == false || ok2 == false { panic("failed to convert blobs") } From bcfd80a96ce5314f67600e3ad9396fb81e3dc27c Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Sun, 13 Nov 2022 08:12:44 -0800 Subject: [PATCH 06/10] rename verifyBlobs validateBlobTransactionWrapper per updated spec, and make implementation more closely follow it --- core/types/data_blob.go | 38 ++++++++++++--------------- core/types/transaction.go | 4 +-- core/types/transaction_marshalling.go | 2 +- core/types/transaction_test.go | 2 +- 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/core/types/data_blob.go b/core/types/data_blob.go index 4aa2806227de..836466a8cd9f 100644 --- a/core/types/data_blob.go +++ b/core/types/data_blob.go @@ -386,32 +386,23 @@ func (b *BlobTxWrapData) sizeWrapData() common.StorageSize { return common.StorageSize(4 + 4 + b.BlobKzgs.ByteLength() + b.Blobs.ByteLength() + b.KzgAggregatedProof.ByteLength()) } -func (b *BlobTxWrapData) verifyVersionedHash(inner TxData) error { +// validateBlobTransactionWrapper implements validate_blob_transaction_wrapper from EIP-4844 +func (b *BlobTxWrapData) validateBlobTransactionWrapper(inner TxData) error { blobTx, ok := inner.(*SignedBlobTx) if !ok { return fmt.Errorf("expected signed blob tx, got %T", inner) } - if a, b := len(blobTx.Message.BlobVersionedHashes), params.MaxBlobsPerBlock; a > b { - return fmt.Errorf("too many blobs in blob tx, got %d, expected no more than %d", a, b) + l1 := len(b.BlobKzgs) + l2 := len(blobTx.Message.BlobVersionedHashes) + l3 := len(b.Blobs) + if l1 != l2 || l2 != l3 { + return fmt.Errorf("lengths don't match %v %v %v", l1, l2, l3) } - if a, b := len(b.BlobKzgs), len(b.Blobs); a != b { - return fmt.Errorf("expected equal amount but got %d kzgs and %d blobs", a, b) - } - if a, b := len(b.BlobKzgs), len(blobTx.Message.BlobVersionedHashes); a != b { - return fmt.Errorf("expected equal amount but got %d kzgs and %d versioned hashes", a, b) - } - for i, h := range blobTx.Message.BlobVersionedHashes { - if computed := b.BlobKzgs[i].ComputeVersionedHash(); computed != h { - return fmt.Errorf("versioned hash %d supposedly %s but does not match computed %s", i, h, computed) - } - } - return nil -} - -// Blob verification using KZG proofs -func (b *BlobTxWrapData) verifyBlobs(inner TxData) error { - if err := b.verifyVersionedHash(inner); err != nil { - return err + // the following check isn't strictly necessary as it would be caught by data gas processing + // (and hence it is not explicitly in the spec for this function), but it doesn't hurt to fail + // early in case we are getting spammed with too many blobs or there is a bug somewhere: + if l1 > params.MaxBlobsPerBlock { + return fmt.Errorf("number of blobs exceeds max: %v", l1) } ok, err := kzg.VerifyAggregateKZGProof(b.Blobs, b.BlobKzgs, kzg.KZGProof(b.KzgAggregatedProof)) if err != nil { @@ -420,6 +411,11 @@ func (b *BlobTxWrapData) verifyBlobs(inner TxData) error { if !ok { return errors.New("failed to verify kzg") } + for i, h := range blobTx.Message.BlobVersionedHashes { + if computed := b.BlobKzgs[i].ComputeVersionedHash(); computed != h { + return fmt.Errorf("versioned hash %d supposedly %s but does not match computed %s", i, h, computed) + } + } return nil } diff --git a/core/types/transaction.go b/core/types/transaction.go index 83b1edca4699..e64c34c6980a 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -98,7 +98,7 @@ type TxWrapData interface { aggregatedProof() KZGProof encodeTyped(w io.Writer, txdata TxData) error sizeWrapData() common.StorageSize - verifyBlobs(inner TxData) error + validateBlobTransactionWrapper(inner TxData) error } // TxData is the underlying data of a transaction. @@ -543,7 +543,7 @@ func (tx *Transaction) IsIncomplete() bool { // VerifyBlobs verifies the blob transaction func (tx *Transaction) VerifyBlobs() error { if tx.wrapData != nil { - return tx.wrapData.verifyBlobs(tx.inner) + return tx.wrapData.validateBlobTransactionWrapper(tx.inner) } return nil } diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index 46063660ab79..2c124b8ede06 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -362,7 +362,7 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { KzgAggregatedProof: dec.KzgAggregatedProof, } // Verify that versioned hashes match kzgs, and kzgs match blobs. - if err := tx.wrapData.verifyBlobs(&itx); err != nil { + if err := tx.wrapData.validateBlobTransactionWrapper(&itx); err != nil { return fmt.Errorf("blob wrapping data is invalid: %v", err) } } diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index ce88c4b94b74..81a5cc9cf7dd 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -490,7 +490,7 @@ func TestTransactionCoding(t *testing.T) { GasTipCap: view.Uint256View(*uint256.NewInt(42)), GasFeeCap: view.Uint256View(*uint256.NewInt(10)), AccessList: AccessListView(accesses), - BlobVersionedHashes: VersionedHashesView{common.HexToHash("0x01624652859a6e98ffc1608e2af0147ca4e86e1ce27672d8d3f3c9d4ffd6ef7e")}, + BlobVersionedHashes: VersionedHashesView{common.HexToHash("0x010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014")}, }, } var kzgProof KZGProof From 88913676d0bc04972eeabca4176d0f02f2b62f54 Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Sun, 13 Nov 2022 11:13:47 -0800 Subject: [PATCH 07/10] fix tests & type bug in kzg verification --- .../testdata/precompiles/pointEvaluation.json | 2 +- crypto/kzg/kzg_bytes.go | 2 +- tests/kzg_test.go | 62 +++++++------------ 3 files changed, 23 insertions(+), 43 deletions(-) diff --git a/core/vm/testdata/precompiles/pointEvaluation.json b/core/vm/testdata/precompiles/pointEvaluation.json index 6e79d2cfb67f..af18ec4bc1d6 100644 --- a/core/vm/testdata/precompiles/pointEvaluation.json +++ b/core/vm/testdata/precompiles/pointEvaluation.json @@ -1,6 +1,6 @@ [ { - "Input": "01342233e6ebb423c766d3a0f8d183e84c453865b392f5ab1f8a8218506e89d842000000000000000000000000000000000000000000000000000000000000002b2f0b0a19cbe19b4c9dbc32af755539fec08bae3eeecbe0ec625037fe3f0a6fa3cfbde6cf9875270479e0e2290726d150412591e07b4fad36472fa1ad38c19eb232cd2ebd3738ea1d9a0a3be07764a8b2faf3776cf5fb7bea8263ab92181326b898c4dc5da95e76e6977c4e204a94f1a3fe5033e19435fa51a8c70b272c06ac", + "Input": "01d0db71b458e8955efa3ef62f1b6b45a2d9c8633dc59ed0b995cecf8b7bb48442000000000000000000000000000000000000000000000000000000000000003b11ebd59d5d12d6ef8e5e48f71770515e032595a0e55eaf80e906b65b2a625282bead0f31f58ee4fd81e93f796bb57acd6f8f6e4def04182c8e949c71ea00d85a44c12102bd817bc97696a6b8fd75618fcd8c2030080c9602e08e935cdf6f779d0d89e7764d855edfbaa730eddfff836fc324957db4f74d1565503bcfcaf157", "Expected": "", "Name": "pointEvaluation1", "Gas": 50000, diff --git a/crypto/kzg/kzg_bytes.go b/crypto/kzg/kzg_bytes.go index b0263565b4c7..215ac1014f8e 100644 --- a/crypto/kzg/kzg_bytes.go +++ b/crypto/kzg/kzg_bytes.go @@ -71,7 +71,7 @@ func PointEvaluationPrecompile(input []byte) ([]byte, error) { func VerifyKZGProof(polynomialKZG KZGCommitment, z, y [32]byte, kzgProof KZGProof) (bool, error) { // successfully converting z and y to bls.Fr confirms they are < MODULUS per the spec var zFr, yFr bls.Fr - ok := bls.FrFrom32(&yFr, z) + ok := bls.FrFrom32(&zFr, z) if !ok { return false, errors.New("invalid evaluation point") } diff --git a/tests/kzg_test.go b/tests/kzg_test.go index be323c810254..bc8541328a9b 100644 --- a/tests/kzg_test.go +++ b/tests/kzg_test.go @@ -19,14 +19,6 @@ import ( "github.com/protolambda/go-kzg/bls" ) -// Helper: invert the divisor, then multiply -func polyFactorDiv(dst *bls.Fr, a *bls.Fr, b *bls.Fr) { - // TODO: use divmod instead. - var tmp bls.Fr - bls.InvModFr(&tmp, b) - bls.MulModFr(dst, &tmp, a) -} - // Helper: Long polynomial division for two polynomials in coefficient form func polyLongDiv(dividend []bls.Fr, divisor []bls.Fr) []bls.Fr { a := make([]bls.Fr, len(dividend)) @@ -39,7 +31,7 @@ func polyLongDiv(dividend []bls.Fr, divisor []bls.Fr) []bls.Fr { out := make([]bls.Fr, diff+1) for diff >= 0 { quot := &out[diff] - polyFactorDiv(quot, &a[aPos], &divisor[bPos]) + bls.DivModFr(quot, &a[aPos], &divisor[bPos]) var tmp, tmp2 bls.Fr for i := bPos; i >= 0; i-- { // In steps: a[diff + i] -= b[i] * quot @@ -208,58 +200,46 @@ func TestVerifyBlobs(t *testing.T) { // Helper: Create test vector for the PointEvaluation precompile func TestPointEvaluationTestVector(t *testing.T) { - fs := gokzg.NewFFTSettings(uint8(math.Log2(params.FieldElementsPerBlob))) - // Create testing polynomial polynomial := make([]bls.Fr, params.FieldElementsPerBlob) for i := uint64(0); i < params.FieldElementsPerBlob; i++ { bls.CopyFr(&polynomial[i], bls.RandomFr()) } - // Get polynomial in evaluation form - evalPoly, err := fs.FFT(polynomial, false) - if err != nil { - t.Fatal(err) - } - // Create a commitment - commitment := kzg.PolynomialToKZGCommitment(evalPoly) + commitmentArray := kzg.PolynomialToKZGCommitment(polynomial) // Create proof for testing x := uint64(0x42) - proof := ComputeProof(polynomial, x, kzg.KzgSetupG1) + xFr := new(bls.Fr) + bls.AsFr(xFr, x) + proofArray, err := kzg.ComputeKZGProof(polynomial, xFr) // Get actual evaluation at x - var xFr bls.Fr - bls.AsFr(&xFr, x) - var y bls.Fr - bls.EvalPolyAt(&y, polynomial, &xFr) + yFr := kzg.EvaluatePolynomialInEvaluationForm(polynomial, xFr) + yArray := bls.FrTo32(yFr) + xArray := bls.FrTo32(xFr) // Verify kzg proof - // TODO fix - //if kzg.VerifyKzgProof(commitment, &xFr, &y, proof) != true { - // panic("failed proof verification") - //} - - commitmentBytes := types.KZGCommitment(commitment) - versionedHash := commitmentBytes.ComputeVersionedHash() - - proofBytes := bls.ToCompressedG1(proof) - - xBytes := bls.FrTo32(&xFr) - yBytes := bls.FrTo32(&y) + ok, err := kzg.VerifyKZGProof(commitmentArray, xArray, yArray, proofArray) + if err != nil { + t.Fatal(err) + } + if !ok { + t.Fatal("failed proof verification") + } + versionedHash := types.KZGCommitment(commitmentArray).ComputeVersionedHash() - calldata := append(versionedHash[:], xBytes[:]...) - calldata = append(calldata, yBytes[:]...) - calldata = append(calldata, commitmentBytes[:]...) - calldata = append(calldata, proofBytes...) + calldata := append(versionedHash[:], xArray[:]...) + calldata = append(calldata, yArray[:]...) + calldata = append(calldata, commitmentArray[:]...) + calldata = append(calldata, proofArray[:]...) t.Logf("test-vector: %x", calldata) precompile := vm.PrecompiledContractsDanksharding[common.BytesToAddress([]byte{0x14})] if _, err := precompile.Run(calldata); err != nil { - // TODO fix - //t.Fatalf("expected point verification to succeed") + t.Fatalf("expected point verification to succeed") } // change a byte of the proof calldata[144+7] ^= 42 From b4e09c8948db550ffb0745657bc04988a6bef4d5 Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Sun, 13 Nov 2022 14:08:09 -0800 Subject: [PATCH 08/10] more consensus layer support in kzg package --- crypto/kzg/kzg_bytes.go | 42 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/crypto/kzg/kzg_bytes.go b/crypto/kzg/kzg_bytes.go index 215ac1014f8e..bb966abc40f6 100644 --- a/crypto/kzg/kzg_bytes.go +++ b/crypto/kzg/kzg_bytes.go @@ -14,6 +14,15 @@ import ( type KZGCommitment [48]byte type KZGProof [48]byte type VersionedHash [32]byte +type Root [32]byte +type Slot uint64 + +type BlobsSidecar struct { + BeaconBlockRoot Root + BeaconBlockSlot Slot + Blobs BlobSequence + KZGAggregatedProof KZGProof +} type BlobSequence interface { Len() int @@ -30,6 +39,10 @@ type KZGCommitmentSequence interface { At(int) KZGCommitment } +var ( + invalidKZGProofError = errors.New("invalid kzg proof") +) + // PointEvaluationPrecompile implements point_evaluation_precompile from EIP-4844 func PointEvaluationPrecompile(input []byte) ([]byte, error) { if len(input) != 192 { @@ -61,7 +74,7 @@ func PointEvaluationPrecompile(input []byte) ([]byte, error) { return nil, fmt.Errorf("verify_kzg_proof error: %v", err) } if !ok { - return nil, errors.New("failed to verify kzg proof") + return nil, invalidKZGProofError } return []byte{}, nil } @@ -136,3 +149,30 @@ func ComputeAggregateKZGProof(blobs BlobSequence) (KZGProof, error) { } return ComputeAggregateKZGProofFromPolynomials(polynomials) } + +// ValidateBlobsSidecar implements validate_blobs_sidecar from the EIP-4844 consensus spec: +// https://github.com/roberto-bayardo/consensus-specs/blob/dev/specs/eip4844/beacon-chain.md#validate_blobs_sidecar +func ValidateBlobsSidecar(slot Slot, beaconBlockRoot Root, expectedKZGCommitments KZGCommitmentSequence, blobsSidecar BlobsSidecar) error { + if slot != blobsSidecar.BeaconBlockSlot { + return fmt.Errorf( + "slot doesn't match sidecar's beacon block slot (%v != %v)", + slot, blobsSidecar.BeaconBlockSlot) + } + if beaconBlockRoot != blobsSidecar.BeaconBlockRoot { + return errors.New("roots not equal") + } + blobs := blobsSidecar.Blobs + if blobs.Len() != expectedKZGCommitments.Len() { + return fmt.Errorf( + "blob len doesn't match expected kzg commitments len (%v != %v)", + blobs.Len(), expectedKZGCommitments.Len()) + } + ok, err := VerifyAggregateKZGProof(blobs, expectedKZGCommitments, blobsSidecar.KZGAggregatedProof) + if err != nil { + return fmt.Errorf("verify_aggregate_kzg_proof error: %v", err) + } + if !ok { + return invalidKZGProofError + } + return nil +} From 42a69e6e01aed62b687cd42a994fd7976f01ea99 Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Sun, 13 Nov 2022 17:16:13 -0800 Subject: [PATCH 09/10] remove aggregated proof from blobs bundle --- core/beacon/types.go | 13 +++---------- core/types/data_blob.go | 2 -- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/core/beacon/types.go b/core/beacon/types.go index 1e7f51fe311f..799f043681c7 100644 --- a/core/beacon/types.go +++ b/core/beacon/types.go @@ -43,10 +43,9 @@ type payloadAttributesMarshaling struct { // BlobsBundleV1 holds the blobs of an execution payload, to be retrieved separately type BlobsBundleV1 struct { - BlockHash common.Hash `json:"blockHash" gencodec:"required"` - KZGs []types.KZGCommitment `json:"kzgs" gencodec:"required"` - Blobs []types.Blob `json:"blobs" gencodec:"required"` - AggregatedProof types.KZGProof `json:"aggregatedProof" gencodec:"required"` + BlockHash common.Hash `json:"blockHash" gencodec:"required"` + KZGs []types.KZGCommitment `json:"kzgs" gencodec:"required"` + Blobs []types.Blob `json:"blobs" gencodec:"required"` } //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) { blobsBundle.KZGs = append(blobsBundle.KZGs, kzgs...) } } - - _, _, aggregatedProof, err := types.Blobs(blobsBundle.Blobs).ComputeCommitmentsAndAggregatedProof() - if err != nil { - return nil, err - } - blobsBundle.AggregatedProof = aggregatedProof return blobsBundle, nil } diff --git a/core/types/data_blob.go b/core/types/data_blob.go index 836466a8cd9f..e9581eef8db3 100644 --- a/core/types/data_blob.go +++ b/core/types/data_blob.go @@ -316,8 +316,6 @@ func (blobs Blobs) ComputeCommitmentsAndAggregatedProof() (commitments []KZGComm var kzgProof KZGProof if len(blobs) != 0 { - // TODO: Execution layer shouldn't be responsible for computing the proof, it should - // be done in the CL. proof, err := kzg.ComputeAggregateKZGProof(blobs) if err != nil { return nil, nil, KZGProof{}, err From 6018bd6fe719292aaee88896a2b35bf1eceb8978 Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Mon, 14 Nov 2022 10:18:04 -0800 Subject: [PATCH 10/10] propagate malformed blob errors when processing a sent transaction so that these get rejected --- crypto/kzg/kzg_bytes.go | 8 ++++++-- signer/core/api.go | 5 ++++- signer/core/apitypes/types.go | 27 +++++++++++---------------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/crypto/kzg/kzg_bytes.go b/crypto/kzg/kzg_bytes.go index bb966abc40f6..16f0357388ef 100644 --- a/crypto/kzg/kzg_bytes.go +++ b/crypto/kzg/kzg_bytes.go @@ -39,13 +39,17 @@ type KZGCommitmentSequence interface { At(int) KZGCommitment } +const ( + PrecompileInputLength = 192 +) + var ( invalidKZGProofError = errors.New("invalid kzg proof") ) // PointEvaluationPrecompile implements point_evaluation_precompile from EIP-4844 func PointEvaluationPrecompile(input []byte) ([]byte, error) { - if len(input) != 192 { + if len(input) != PrecompileInputLength { return nil, errors.New("invalid input length") } // versioned hash: first 32 bytes @@ -67,7 +71,7 @@ func PointEvaluationPrecompile(input []byte) ([]byte, error) { // Quotient kzg: next 48 bytes var quotientKZG [48]byte - copy(quotientKZG[:], input[144:192]) + copy(quotientKZG[:], input[144:PrecompileInputLength]) ok, err := VerifyKZGProof(KZGCommitment(dataKZG), x, y, KZGProof(quotientKZG)) if err != nil { diff --git a/signer/core/api.go b/signer/core/api.go index 61793a0e51c3..d706fcb094a0 100644 --- a/signer/core/api.go +++ b/signer/core/api.go @@ -587,7 +587,10 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args apitypes.SendTxA return nil, err } // Convert fields into a real transaction - var unsignedTx = result.Transaction.ToTransaction() + unsignedTx, err := result.Transaction.ToTransaction() + if err != nil { + return nil, err + } // Get the password for the transaction pw, err := api.lookupOrQueryPassword(acc.Address, "Account password", fmt.Sprintf("Please enter the password for account %s", acc.Address.String())) diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go index c71178794bc9..4c8ef1a2e568 100644 --- a/signer/core/apitypes/types.go +++ b/signer/core/apitypes/types.go @@ -38,7 +38,6 @@ import ( "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/kzg" ) var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[\])?$`) @@ -116,7 +115,7 @@ func (args SendTxArgs) String() string { } // ToTransaction converts the arguments to a transaction. -func (args *SendTxArgs) ToTransaction() *types.Transaction { +func (args *SendTxArgs) ToTransaction() (*types.Transaction, error) { // Add the To-field, if specified var to *common.Address if args.To != nil { @@ -148,22 +147,18 @@ func (args *SendTxArgs) ToTransaction() *types.Transaction { msg.Value.SetFromBig((*big.Int)(&args.Value)) msg.Data = input msg.AccessList = types.AccessListView(al) - wrapData := types.BlobTxWrapData{} - for _, blob := range args.Blobs { - c, ok := kzg.BlobToKZGCommitment(blob) - if ok { - versionedHash := common.Hash(kzg.KZGToVersionedHash(c)) - msg.BlobVersionedHashes = append(msg.BlobVersionedHashes, versionedHash) - wrapData.BlobKzgs = append(wrapData.BlobKzgs, types.KZGCommitment(c)) - wrapData.Blobs = append(wrapData.Blobs, blob) - } + commitments, hashes, aggProof, err := types.Blobs(args.Blobs).ComputeCommitmentsAndAggregatedProof() + if err != nil { + return nil, fmt.Errorf("invalid blobs: %v", err) } - _, _, aggProof, err := types.Blobs(args.Blobs).ComputeCommitmentsAndAggregatedProof() - if err == nil { - wrapData.KzgAggregatedProof = aggProof + msg.BlobVersionedHashes = hashes + wrapData := types.BlobTxWrapData{ + Blobs: args.Blobs, + KzgAggregatedProof: aggProof, + BlobKzgs: commitments, } data = &types.SignedBlobTx{Message: msg} - return types.NewTx(data, types.WithTxWrapData(&wrapData)) + return types.NewTx(data, types.WithTxWrapData(&wrapData)), nil case args.MaxFeePerGas != nil: al := types.AccessList{} if args.AccessList != nil { @@ -201,7 +196,7 @@ func (args *SendTxArgs) ToTransaction() *types.Transaction { Data: input, } } - return types.NewTx(data) + return types.NewTx(data), nil } type SigFormat struct {