diff --git a/core/beacon/types.go b/core/beacon/types.go index 1e7f51fe311..799f043681c 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 558ea344dff..e9581eef8db 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,6 +208,15 @@ func (blob *Blob) UnmarshalText(text []byte) error { type BlobKzgs []KZGCommitment +// 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 { return dr.List(func() codec.Deserializable { i := len(*li) @@ -227,7 +235,7 @@ func (li BlobKzgs) ByteLength() uint64 { return uint64(len(li)) * 48 } -func (li *BlobKzgs) FixedLength() uint64 { +func (li BlobKzgs) FixedLength() uint64 { return 0 } @@ -245,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 { @@ -301,42 +306,21 @@ 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") } - commitments[i] = KZGCommitment(kzg.BlobToKZGCommitment(frs)) - versionedHashes[i] = common.Hash(kzg.KZGToVersionedHash(kzg.KZGCommitment(commitments[i]))) + commitments[i] = KZGCommitment(c) + versionedHashes[i] = common.Hash(kzg.KZGToVersionedHash(c)) } var kzgProof KZGProof if len(blobs) != 0 { - aggregatePoly, aggregateCommitmentG1, err := computeAggregateKzgCommitment(blobs, commitments) + proof, err := kzg.ComputeAggregateKZGProof(blobs) 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 { - return nil, nil, KZGProof{}, err - } - z := kzg.BytesToBLSField(sum) - - var y bls.Fr - kzg.EvaluatePolyInEvaluationForm(&y, aggregatePoly[:], z) - - aggProofG1, err := kzg.ComputeProof(aggregatePoly, z) - if err != nil { - return nil, nil, KZGProof{}, err - } - copy(kzgProof[:], bls.ToCompressedG1(aggProofG1)) + kzgProof = KZGProof(proof) } return commitments, versionedHashes, kzgProof, nil @@ -363,27 +347,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 @@ -421,19 +384,30 @@ 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) + // 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) } - 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) + ok, err := kzg.VerifyAggregateKZGProof(b.Blobs, b.BlobKzgs, kzg.KZGProof(b.KzgAggregatedProof)) + if err != nil { + return fmt.Errorf("error during proof verification: %v", err) + } + if !ok { + return errors.New("failed to verify kzg") } for i, h := range blobTx.Message.BlobVersionedHashes { if computed := b.BlobKzgs[i].ComputeVersionedHash(); computed != h { @@ -443,41 +417,6 @@ func (b *BlobTxWrapData) verifyVersionedHash(inner TxData) error { return nil } -// Blob verification using KZG proofs -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 - } - z := kzg.BytesToBLSField(sum) - - var y bls.Fr - kzg.EvaluatePolyInEvaluationForm(&y, aggregatePoly[:], z) - - aggregateProofG1, err := bls.FromCompressedG1(b.KzgAggregatedProof[:]) - if err != nil { - return fmt.Errorf("aggregate proof parse error: %v", err) - } - if !kzg.VerifyKZGProofFromPoints(aggregateCommitmentG1, z, &y, aggregateProofG1) { - return errors.New("failed to verify kzg") - } - return nil -} - func (b *BlobTxWrapData) copy() TxWrapData { return &BlobTxWrapData{ BlobKzgs: b.BlobKzgs.copy(), @@ -514,33 +453,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/core/types/transaction.go b/core/types/transaction.go index 83b1edca469..e64c34c6980 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 46063660ab7..2c124b8ede0 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 ce88c4b94b7..81a5cc9cf7d 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 diff --git a/core/vm/testdata/precompiles/pointEvaluation.json b/core/vm/testdata/precompiles/pointEvaluation.json index 6e79d2cfb67..af18ec4bc1d 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.go b/crypto/kzg/kzg.go index 0d3a2cac10e..b0a22d42d04 100644 --- a/crypto/kzg/kzg.go +++ b/crypto/kzg/kzg.go @@ -1,16 +1,31 @@ +// 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 ( + "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 @@ -72,47 +87,218 @@ 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") +// 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 +} - // To avoid overflow/underflow, convert elements into int - var poly [params.FieldElementsPerBlob]big.Int - for i := range poly { - frToBig(&poly[i], &eval[i]) +// 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) } - var zB big.Int - frToBig(&zB, z) + return powers +} - // 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 +func PolynomialToKZGCommitment(eval Polynomial) KZGCommitment { + g1 := bls.LinCombG1(kzgSetupLagrange, []bls.Fr(eval)) + var out KZGCommitment + copy(out[:], bls.ToCompressedG1(g1)) + return out +} - for i := range polyShifted { - polyShifted[i].Mod(new(big.Int).Sub(&poly[i], &yB), BLSModulus) +// 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 } - 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") + 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 } - denomPoly[i].Mod(new(big.Int).Sub(Domain[i], &zB), BLSModulus) + 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) +} - // 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) +// 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 bls.LinCombG1(kzgSetupLagrange, quotientPoly[:]), nil + return out, true } diff --git a/crypto/kzg/kzg_bytes.go b/crypto/kzg/kzg_bytes.go new file mode 100644 index 00000000000..16f0357388e --- /dev/null +++ b/crypto/kzg/kzg_bytes.go @@ -0,0 +1,182 @@ +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 Root [32]byte +type Slot uint64 + +type BlobsSidecar struct { + BeaconBlockRoot Root + BeaconBlockSlot Slot + Blobs BlobSequence + KZGAggregatedProof KZGProof +} + +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 +} + +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) != PrecompileInputLength { + 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]) + + // 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:PrecompileInputLength]) + + ok, err := VerifyKZGProof(KZGCommitment(dataKZG), x, y, KZGProof(quotientKZG)) + if err != nil { + return nil, fmt.Errorf("verify_kzg_proof error: %v", err) + } + if !ok { + return nil, invalidKZGProofError + } + 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(&zFr, 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[:]) + 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) + 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: +// 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) +} + +// 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 +} diff --git a/crypto/kzg/kzg_new.go b/crypto/kzg/kzg_new.go deleted file mode 100644 index 8a32223b545..00000000000 --- a/crypto/kzg/kzg_new.go +++ /dev/null @@ -1,142 +0,0 @@ -package kzg - -import ( - "errors" - "fmt" - "math/big" - - "github.com/protolambda/go-kzg/bls" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/params" -) - -// 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 - -// 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) -} - -// 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 { - 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 -} - -// 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 { - 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 -} diff --git a/crypto/kzg/util.go b/crypto/kzg/util.go index 3c66202487d..569fca7beb2 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 diff --git a/signer/core/api.go b/signer/core/api.go index 61793a0e51c..d706fcb094a 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 8c4e672348d..4c8ef1a2e56 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,25 +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 _, 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 - } - 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) + 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 { @@ -204,7 +196,7 @@ func (args *SendTxArgs) ToTransaction() *types.Transaction { Data: input, } } - return types.NewTx(data) + return types.NewTx(data), nil } type SigFormat struct { diff --git a/tests/kzg_bench_test.go b/tests/kzg_bench_test.go index 56239dea1f0..8570b2ca39c 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 ( @@ -15,8 +16,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 +28,7 @@ func BenchmarkBlobToKzg(b *testing.B) { blob := randomBlob() b.ResetTimer() for i := 0; i < b.N; i++ { - kzg.BlobToKZGCommitment(blob) + kzg.PolynomialToKZGCommitment(blob) } } @@ -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 621bbe35cc1..bc8541328a9 100644 --- a/tests/kzg_test.go +++ b/tests/kzg_test.go @@ -1,3 +1,4 @@ +// TODO: Migrate these to crypto/kzg package tests import ( @@ -18,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)) @@ -38,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 @@ -156,20 +149,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) + kzg2, ok2 := kzg.BlobToKZGCommitment(blob2) 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 @@ -210,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.BlobToKZGCommitment(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