diff --git a/core/types/data_blob.go b/core/types/data_blob.go index 89e92acb6785..d127aebf77c5 100644 --- a/core/types/data_blob.go +++ b/core/types/data_blob.go @@ -9,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/kzg" "github.com/ethereum/go-ethereum/params" "github.com/protolambda/go-kzg/bls" @@ -63,10 +62,8 @@ func (p *KZGCommitment) Point() (*bls.G1Point, error) { return bls.FromCompressedG1(p[:]) } -func (kzg KZGCommitment) ComputeVersionedHash() common.Hash { - h := crypto.Keccak256Hash(kzg[:]) - h[0] = params.BlobCommitmentVersionKZG - return h +func (c KZGCommitment) ComputeVersionedHash() common.Hash { + return kzg.KZGToVersionedHash(c) } // Compressed BLS12-381 G1 element @@ -528,7 +525,7 @@ func (b *BlobTxWrapData) verifyBlobs(inner TxData) error { if err != nil { return fmt.Errorf("aggregate proof parse error: %v", err) } - if !kzg.VerifyKzgProof(aggregateCommitmentG1, &z, &y, aggregateProofG1) { + if !kzg.VerifyKZGProofFromPoints(aggregateCommitmentG1, &z, &y, aggregateProofG1) { return errors.New("failed to verify kzg") } return nil diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 268ffc2acc43..e45ede94cb19 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -22,8 +22,6 @@ import ( "errors" "math/big" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" @@ -33,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/crypto/kzg" "github.com/ethereum/go-ethereum/params" big2 "github.com/holiman/big" - "github.com/protolambda/go-kzg/bls" "golang.org/x/crypto/ripemd160" ) @@ -1077,70 +1074,11 @@ func (c *bls12381MapG2) Run(input []byte) ([]byte, error) { // to check if a value is part of a blob at a specific point with a KZG proof. type pointEvaluation struct{} -var ( - errPointEvaluationInputLength = errors.New("invalid input length") - errPointEvaluationInvalidVersionedHash = errors.New("invalid versioned hash") - errPointEvaluationInvalidX = errors.New("invalid evaluation point") - errPointEvaluationInvalidY = errors.New("invalid expected output") - errPointEvaluationInvalidKzg = errors.New("invalid data kzg") - errPointEvaluationInvalidProof = errors.New("invalid proof") - errPointEvaluationMismatchVersionedHash = errors.New("mismatched versioned hash") - errPointEvaluationBadProof = errors.New("bad proof") -) - // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *pointEvaluation) RequiredGas(input []byte) uint64 { return params.PointEvaluationGas } func (c *pointEvaluation) Run(input []byte) ([]byte, error) { - if len(input) != 192 { - return nil, errPointEvaluationInputLength - } - - var versionedHash common.Hash - copy(versionedHash[:], input[:32]) - // XXX Should we version check the hash? - if versionedHash[0] != params.BlobCommitmentVersionKZG { - return nil, errPointEvaluationInvalidVersionedHash - } - - var x bls.Fr - var data [32]byte - copy(data[:], input[32:64]) - ok := bls.FrFrom32(&x, data) - if !ok { - return nil, errPointEvaluationInvalidX - } - - var y bls.Fr - copy(data[:], input[64:96]) - ok = bls.FrFrom32(&y, data) - if !ok { - return nil, errPointEvaluationInvalidY - } - - var commitment types.KZGCommitment - copy(commitment[:], input[96:144]) - if commitment.ComputeVersionedHash() != versionedHash { - return nil, errPointEvaluationMismatchVersionedHash - } - - parsedCommitment, err := commitment.Point() - if err != nil { - return nil, errPointEvaluationInvalidKzg - } - - var proof types.KZGCommitment - copy(proof[:], input[144:192]) - parsedProof, err := proof.Point() - if err != nil { - return nil, errPointEvaluationInvalidProof - } - - if !kzg.VerifyKzgProof(parsedCommitment, &x, &y, parsedProof) { - return nil, errPointEvaluationBadProof - } - - return []byte{}, nil + return kzg.PointEvaluationPrecompile(input) } diff --git a/crypto/kzg/kzg.go b/crypto/kzg/kzg.go index 9036fea3f78b..bb855ddca6ab 100644 --- a/crypto/kzg/kzg.go +++ b/crypto/kzg/kzg.go @@ -50,21 +50,6 @@ func BlobToKzg(eval []bls.Fr) *bls.G1Point { return bls.LinCombG1(kzgSetupLagrange, eval) } -// Verify a KZG proof -func VerifyKzgProof(commitment *bls.G1Point, x *bls.Fr, y *bls.Fr, proof *bls.G1Point) bool { - // Verify the pairing equation - var xG2 bls.G2Point - bls.MulG2(&xG2, &bls.GenG2, x) - var sMinuxX bls.G2Point - bls.SubG2(&sMinuxX, &kzgSetupG2[1], &xG2) - var yG1 bls.G1Point - bls.MulG1(&yG1, &bls.GenG1, y) - var commitmentMinusY bls.G1Point - bls.SubG1(&commitmentMinusY, commitment, &yG1) - - return bls.PairingsVerify(&commitmentMinusY, &bls.GenG2, proof, &sMinuxX) -} - type BlobsBatch struct { sync.Mutex init bool diff --git a/crypto/kzg/kzg_new.go b/crypto/kzg/kzg_new.go new file mode 100644 index 000000000000..aefc5505c0f6 --- /dev/null +++ b/crypto/kzg/kzg_new.go @@ -0,0 +1,94 @@ +package kzg + +import ( + "errors" + "fmt" + + "github.com/protolambda/go-kzg/bls" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" +) + +// 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 [48]byte, z *bls.Fr, y *bls.Fr, kzgProof [48]byte) (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 [48]byte) [32]byte { + h := crypto.Keccak256Hash(kzg[:]) + h[0] = params.BlobCommitmentVersionKZG + return 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(dataKZG) != 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(dataKZG, &xFr, &yFr, 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 +} diff --git a/tests/kzg_bench_test.go b/tests/kzg_bench_test.go index ecef998dead1..b5e837c3de1c 100644 --- a/tests/kzg_bench_test.go +++ b/tests/kzg_bench_test.go @@ -76,7 +76,7 @@ func BenchmarkVerifyBlobs(b *testing.B) { } } -func BenchmarkVerifyKzgProof(b *testing.B) { +func BenchmarkVerifyKZGProof(b *testing.B) { // First let's do some go-kzg preparations to be able to convert polynomial between coefficient and evaluation form fs := gokzg.NewFFTSettings(uint8(math.Log2(params.FieldElementsPerBlob))) @@ -109,7 +109,7 @@ func BenchmarkVerifyKzgProof(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { // Verify kzg proof - if kzg.VerifyKzgProof(commitment, &xFr, &value, proof) != true { + if kzg.VerifyKZGProofFromPoints(commitment, &xFr, &value, proof) != true { b.Fatal("failed proof verification") } }