Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create util to verify KZG commitment by generation and comparison #1042

Merged
merged 9 commits into from
Dec 20, 2024
59 changes: 59 additions & 0 deletions api/clients/v2/verification/verification_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package verification

import (
"fmt"
"github.com/Layr-Labs/eigenda/encoding"
"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark-crypto/ecc/bn254"

"github.com/Layr-Labs/eigenda/encoding/kzg/verifier"
"github.com/Layr-Labs/eigenda/encoding/rs"
)

// GenerateBlobCommitment computes a kzg-bn254 commitment of blob data using SRS
func GenerateBlobCommitment(
kzgVerifier *verifier.Verifier,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious: why didn’t you make these methods on the verifier?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unconscious hesitancy to modify an existing utility, I suppose.

You're right, it's where the methods belong. I will fix this in an upcoming PR

blob []byte) (*encoding.G1Commitment, error) {

inputFr, err := rs.ToFrArray(blob)
if err != nil {
return nil, fmt.Errorf("convert bytes to field elements, %w", err)
}

if len(kzgVerifier.Srs.G1) < len(inputFr) {
return nil, fmt.Errorf(
"insufficient SRS in memory: have %v, need %v",
len(kzgVerifier.Srs.G1),
len(inputFr))
}

var commitment bn254.G1Affine
_, err = commitment.MultiExp(kzgVerifier.Srs.G1[:len(inputFr)], inputFr, ecc.MultiExpConfig{})
if err != nil {
return nil, fmt.Errorf("MultiExp: %w", err)
}

return &encoding.G1Commitment{X: commitment.X, Y: commitment.Y}, nil
}

// GenerateAndCompareBlobCommitment generates the kzg-bn254 commitment of the blob, and compares it with a claimed
// commitment. An error is returned if there is a problem generating the commitment, or if the comparison fails.
func GenerateAndCompareBlobCommitment(
kzgVerifier *verifier.Verifier,
claimedCommitment *encoding.G1Commitment,
blobBytes []byte) error {

computedCommitment, err := GenerateBlobCommitment(kzgVerifier, blobBytes)
if err != nil {
return fmt.Errorf("compute commitment: %w", err)
}

if claimedCommitment.X.Equal(&computedCommitment.X) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i was going to say we'd need to check if claimedCommitment is on the curve but since its checked against what's computed locally, its fine.

claimedCommitment.Y.Equal(&computedCommitment.Y) {
return nil
}

return fmt.Errorf(
"commitment field elements do not match. computed commitment: (x: %x, y: %x), claimed commitment (x: %x, y: %x)",
computedCommitment.X, computedCommitment.Y, claimedCommitment.X, claimedCommitment.Y)
}
125 changes: 125 additions & 0 deletions api/clients/v2/verification/verification_utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package verification

import (
"github.com/Layr-Labs/eigenda/common/testutils/random"
"github.com/Layr-Labs/eigenda/encoding/kzg"
"github.com/Layr-Labs/eigenda/encoding/kzg/verifier"
"github.com/Layr-Labs/eigenda/encoding/utils/codec"
"github.com/stretchr/testify/require"
"runtime"
"testing"
)

func getKzgConfig() *kzg.KzgConfig {
return &kzg.KzgConfig{
G1Path: "../../../../inabox/resources/kzg/g1.point",
G2Path: "../../../../inabox/resources/kzg/g2.point",
G2PowerOf2Path: "../../../../inabox/resources/kzg/g2.point.powerOf2",
CacheDir: "../../../../inabox/resources/kzg/SRSTables",
SRSOrder: 3000,
SRSNumberToLoad: 2900,
NumWorker: uint64(runtime.GOMAXPROCS(0)),
LoadG2Points: false,
}
}

// randomlyModifyBytes picks a random byte from the input array, and increments it
func randomlyModifyBytes(testRandom *random.TestRandom, inputBytes []byte) {
indexToModify := testRandom.Intn(len(inputBytes))
inputBytes[indexToModify] = inputBytes[indexToModify] + 1
}

func getRandomPaddedBytes(testRandom *random.TestRandom, count int) []byte {
return codec.ConvertByPaddingEmptyByte(testRandom.Bytes(count))
}

func TestComputeAndCompareKzgCommitmentSuccess(t *testing.T) {
testRandom := random.NewTestRandom(t)
randomBytes := getRandomPaddedBytes(testRandom, 1000)

kzgVerifier, err := verifier.NewVerifier(getKzgConfig(), nil)
require.NotNil(t, kzgVerifier)
require.NoError(t, err)

commitment, err := GenerateBlobCommitment(kzgVerifier, randomBytes)
require.NotNil(t, commitment)
require.NoError(t, err)

// make sure the commitment verifies correctly
err = GenerateAndCompareBlobCommitment(
kzgVerifier,
commitment,
randomBytes)
require.NoError(t, err)
}

func TestComputeAndCompareKzgCommitmentFailure(t *testing.T) {
testRandom := random.NewTestRandom(t)
randomBytes := getRandomPaddedBytes(testRandom, 1000)

kzgVerifier, err := verifier.NewVerifier(getKzgConfig(), nil)
require.NotNil(t, kzgVerifier)
require.NoError(t, err)

commitment, err := GenerateBlobCommitment(kzgVerifier, randomBytes)
require.NotNil(t, commitment)
require.NoError(t, err)

// randomly modify the bytes, and make sure the commitment verification fails
randomlyModifyBytes(testRandom, randomBytes)
err = GenerateAndCompareBlobCommitment(
kzgVerifier,
commitment,
randomBytes)
require.NotNil(t, err)
}

func TestGenerateBlobCommitmentEquality(t *testing.T) {
testRandom := random.NewTestRandom(t)
randomBytes := getRandomPaddedBytes(testRandom, 1000)

kzgVerifier, err := verifier.NewVerifier(getKzgConfig(), nil)
require.NotNil(t, kzgVerifier)
require.NoError(t, err)

// generate two identical commitments
commitment1, err := GenerateBlobCommitment(kzgVerifier, randomBytes)
require.NotNil(t, commitment1)
require.NoError(t, err)
commitment2, err := GenerateBlobCommitment(kzgVerifier, randomBytes)
require.NotNil(t, commitment2)
require.NoError(t, err)

// commitments to identical bytes should be equal
require.Equal(t, commitment1, commitment2)

// randomly modify a byte
randomlyModifyBytes(testRandom, randomBytes)
commitmentA, err := GenerateBlobCommitment(kzgVerifier, randomBytes)
require.NotNil(t, commitmentA)
require.NoError(t, err)

// commitments to non-identical bytes should not be equal
require.NotEqual(t, commitment1, commitmentA)
}

func TestGenerateBlobCommitmentTooLong(t *testing.T) {
kzgVerifier, err := verifier.NewVerifier(getKzgConfig(), nil)
require.NotNil(t, kzgVerifier)
require.NoError(t, err)

// this is the absolute maximum number of bytes we can handle, given how the verifier was configured
almostTooLongByteCount := 2900 * 32

// an array of exactly this size should be fine
almostTooLongBytes := make([]byte, almostTooLongByteCount)
commitment1, err := GenerateBlobCommitment(kzgVerifier, almostTooLongBytes)
require.NotNil(t, commitment1)
require.NoError(t, err)

// but 1 more byte is more than we can handle
tooLongBytes := make([]byte, almostTooLongByteCount+1)
commitment2, err := GenerateBlobCommitment(kzgVerifier, tooLongBytes)
require.Nil(t, commitment2)
require.NotNil(t, err)
}
Loading