diff --git a/bls/bls_test.go b/bls/bls_test.go index cf02c84..6de8cf0 100644 --- a/bls/bls_test.go +++ b/bls/bls_test.go @@ -66,6 +66,16 @@ func TestPointG2Marshalling(t *testing.T) { } } +func TestEmptyG1Lincomb(t *testing.T) { + out := LinCombG1([]G1Point{}, []Fr{}) + if out == nil { + t.Fatal("got nil, expected result when given 0 points and 0 scalars should be the zero group element") + } + + if !EqualG1(out, &ZeroG1) { + t.Fatalf("Expected zero group element, got:\n%s", StrG1(out)) + } +} func TestPolyLincomb(t *testing.T) { var x1, x2, x3, x4 Fr SetFr(&x1, "1") @@ -73,31 +83,37 @@ func TestPolyLincomb(t *testing.T) { SetFr(&x3, "3") SetFr(&x4, "4") vec := []Fr{x1, x2, x3, x4} + degree := len(vec) // Happy path: valid inputs - r, err := PolyLinComb([][]Fr{vec, vec, vec, vec}, vec) + r, err := PolyLinComb([][]Fr{vec, vec, vec, vec}, vec, degree) if err != nil { t.Fatal(err) } - if len(r) != 4 { - t.Fatalf("Expected result of length 4, got %v", len(r)) + if len(r) != degree { + t.Fatalf("Expected result of length %v, got %v", degree, len(r)) } // Error path: empty input - r, err = PolyLinComb([][]Fr{}, []Fr{}) - if err == nil { - t.Fatal("Expected error, got none") + r, err = PolyLinComb([][]Fr{}, []Fr{}, degree) + if err != nil { + t.Fatalf("Expected the zero polynomial of degree %v \ngot an error: %v", degree, err) + } + for i := 0; i < degree; i++ { + if !EqualFr(&r[i], &ZERO) { + t.Fatal("Expected the zero polynomial") + } } // Error path: vectors not same length shortVec := []Fr{x1, x2, x3} - r, err = PolyLinComb([][]Fr{vec, vec, shortVec, vec}, vec) + _, err = PolyLinComb([][]Fr{vec, vec, shortVec, vec}, vec, degree) if err == nil { t.Fatal("Expected error, got none") } // Error path: Scalar vector size doesn't match - r, err = PolyLinComb([][]Fr{vec, vec, vec, vec}, shortVec) + _, err = PolyLinComb([][]Fr{vec, vec, vec, vec}, shortVec, degree) if err == nil { t.Fatal("Expected error, got none") } diff --git a/bls/globals.go b/bls/globals.go index dd6d1bf..dad60de 100644 --- a/bls/globals.go +++ b/bls/globals.go @@ -152,20 +152,19 @@ func EvaluatePolyInEvaluationForm(yFr *Fr, poly []Fr, x *Fr, rootsOfUnity []Fr, MulModFr(yFr, &y, &tmp) } -func PolyLinComb(vectors [][]Fr, scalars []Fr) ([]Fr, error) { +func PolyLinComb(vectors [][]Fr, scalars []Fr, polyDegree int) ([]Fr, error) { l := len(vectors) if l == 0 { - return nil, errors.New("input vectors can't be empty") + return make([]Fr, polyDegree), nil } if len(scalars) != l { return nil, errors.New("scalars should have same length as input vectors") } - vlen := len(vectors[0]) - r := make([]Fr, vlen) + r := make([]Fr, polyDegree) for j, v := range vectors { - if len(v) != vlen { + if len(v) != polyDegree { return nil, errors.New("input vectors should all be of identical length") } s := &scalars[j] diff --git a/eth/helpers.go b/eth/helpers.go index 1448137..36ed442 100644 --- a/eth/helpers.go +++ b/eth/helpers.go @@ -157,20 +157,12 @@ func BytesToBLSField(h [32]byte) *bls.Fr { // 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, evaluationChallenge, err := ComputeChallenges(blobs, commitments) if err != nil { return nil, nil, nil, err } - 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) + aggregatedPoly, err := bls.PolyLinComb(blobs, powers, FieldElementsPerBlob) if err != nil { return nil, nil, nil, err } @@ -186,7 +178,7 @@ func ComputeAggregatedPolyAndCommitment(blobs Polynomials, commitments KZGCommit bls.CopyG1(&commitmentsG1[i], p) } aggregatedCommitmentG1 := bls.LinCombG1(commitmentsG1, powers) - return aggregatedPoly, aggregatedCommitmentG1, &evaluationChallenge, nil + return aggregatedPoly, aggregatedCommitmentG1, evaluationChallenge, nil } // ComputeAggregateKZGProofFromPolynomials implements compute_aggregate_kzg_proof from the EIP-4844 @@ -239,50 +231,65 @@ func EvaluatePolynomialInEvaluationForm(poly []bls.Fr, x *bls.Fr) *bls.Fr { 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() - - _, err := sha.Write([]byte(FIAT_SHAMIR_PROTOCOL_DOMAIN)) +// ComputeChallenges implements compute_challenges from the EIP-4844 consensus spec: +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#compute_challenges +func ComputeChallenges(polys Polynomials, comms KZGCommitmentSequence) ([]bls.Fr, *bls.Fr, error) { + hash, err := hashPolysComms(polys, comms) if err != nil { - return nil, err + return nil, nil, err } + var linCombChallengeTranscript = append(hash[:], 0) + var evalChallengeTranscript = append(hash[:], 1) + + shaHashToField := func(input []byte) *bls.Fr { + sha := sha256.New() + sha.Write(input) + + var hash32 [32]byte + copy(hash32[:], sha.Sum(nil)) + + return BytesToBLSField(hash32) + } + + linCombChallenge := shaHashToField(linCombChallengeTranscript) + evalChallenge := shaHashToField(evalChallengeTranscript) + + rPowers := ComputePowers(linCombChallenge, len(polys)) + + return rPowers, evalChallenge, nil + +} + +// Adds the domain separator, polynomials and commitments into a buffer, returning the +// hash of this buffer +func hashPolysComms(polys Polynomials, comms KZGCommitmentSequence) ([32]byte, error) { + sha := sha256.New() + var hash [32]byte + + sha.Write([]byte(FIAT_SHAMIR_PROTOCOL_DOMAIN)) + bytes := make([]byte, 8) binary.LittleEndian.PutUint64(bytes, uint64(FieldElementsPerBlob)) - _, err = sha.Write(bytes) - if err != nil { - return nil, err - } + sha.Write(bytes) bytes = make([]byte, 8) binary.LittleEndian.PutUint64(bytes, uint64(len(polys))) - _, err = sha.Write(bytes) - if err != nil { - return nil, err - } + sha.Write(bytes) for _, poly := range polys { for _, fe := range poly { b32 := bls.FrTo32(&fe) - _, err := sha.Write(b32[:]) - if err != nil { - return nil, err - } + sha.Write(b32[:]) } } l := comms.Len() for i := 0; i < l; i++ { c := comms.At(i) - _, err := sha.Write(c[:]) - if err != nil { - return nil, err - } + sha.Write(c[:]) } - var hash [32]byte copy(hash[:], sha.Sum(nil)) - return BytesToBLSField(hash), nil + return hash, nil } func BlobToPolynomial(b Blob) (Polynomial, bool) {