Skip to content

Commit

Permalink
Start implementing a bytes API with appropriate interfaces
Browse files Browse the repository at this point in the history
Also fix incorrect hash type (Keccac -> Sha256) when
computing versioned hash.
  • Loading branch information
roberto-bayardo committed Nov 12, 2022
1 parent 594fcbb commit 0cc40fe
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 170 deletions.
95 changes: 30 additions & 65 deletions core/types/data_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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) {
Expand All @@ -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")
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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))
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
6 changes: 6 additions & 0 deletions crypto/kzg/kzg.go
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down
122 changes: 122 additions & 0 deletions crypto/kzg/kzg_bytes.go
Original file line number Diff line number Diff line change
@@ -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)
}
Loading

0 comments on commit 0cc40fe

Please sign in to comment.