diff --git a/api/clients/v2/verification/verification_utils.go b/api/clients/v2/verification/verification_utils.go new file mode 100644 index 000000000..3246e26b2 --- /dev/null +++ b/api/clients/v2/verification/verification_utils.go @@ -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, + 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) && + 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) +} diff --git a/api/clients/v2/verification/verification_utils_test.go b/api/clients/v2/verification/verification_utils_test.go new file mode 100644 index 000000000..3ebb01ba6 --- /dev/null +++ b/api/clients/v2/verification/verification_utils_test.go @@ -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) +}