Merge branch 'ethereum:main' into update-nodejs-bindings

This commit is contained in:
Justin Traglia 2023-02-20 10:22:50 -06:00 committed by GitHub
commit 9be420c0f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1005 additions and 406 deletions

View File

@ -5,9 +5,10 @@ This is a copy of [C-KZG](https://github.com/benjaminion/c-kzg) stripped-down to
- `blob_to_kzg_commitment`
- `compute_kzg_proof`
- `compute_aggregate_kzg_proof`
- `verify_kzg_proof`
- `verify_aggregate_kzg_proof`
- `compute_blob_kzg_proof`
- `verify_blob_kzg_proof`
- `verify_blob_kzg_proof_batch`
We also provide functions for loading/freeing the trusted setup:

View File

@ -159,6 +159,26 @@ func ComputeKZGProof(blob Blob, zBytes Bytes32) (KZGProof, CKzgRet) {
return proof, CKzgRet(ret)
}
/*
ComputeBlobKZGProof is the binding for:
C_KZG_RET compute_blob_kzg_proof(
KZGProof *out,
const Blob *blob,
const KZGSettings *s);
*/
func ComputeBlobKZGProof(blob Blob) (KZGProof, CKzgRet) {
if !loaded {
panic("trusted setup isn't loaded")
}
proof := KZGProof{}
ret := C.compute_blob_kzg_proof(
(*C.KZGProof)(unsafe.Pointer(&proof)),
(*C.Blob)(unsafe.Pointer(&blob)),
&settings)
return proof, CKzgRet(ret)
}
/*
VerifyKZGProof is the binding for:
@ -186,52 +206,54 @@ func VerifyKZGProof(commitmentBytes Bytes48, zBytes, yBytes Bytes32, proofBytes
}
/*
ComputeAggregateKZGProof is the binding for:
VerifyBlobKZGProof is the binding for:
C_KZG_RET compute_aggregate_kzg_proof(
KZGProof *out,
const Blob *blobs,
size_t n,
C_KZG_RET verify_blob_kzg_proof(
bool *out,
const Blob *blob,
const Bytes48 *commitment_bytes,
const Bytes48 *proof_bytes,
const KZGSettings *s);
*/
func ComputeAggregateKZGProof(blobs []Blob) (KZGProof, CKzgRet) {
func VerifyBlobKZGProof(blob Blob, commitmentBytes, proofBytes Bytes48) (bool, CKzgRet) {
if !loaded {
panic("trusted setup isn't loaded")
}
proof := KZGProof{}
ret := C.compute_aggregate_kzg_proof(
(*C.KZGProof)(unsafe.Pointer(&proof)),
*(**C.Blob)(unsafe.Pointer(&blobs)),
(C.size_t)(len(blobs)),
var result C.bool
ret := C.verify_blob_kzg_proof(
&result,
(*C.Blob)(unsafe.Pointer(&blob)),
(*C.Bytes48)(unsafe.Pointer(&commitmentBytes)),
(*C.Bytes48)(unsafe.Pointer(&proofBytes)),
&settings)
return proof, CKzgRet(ret)
return bool(result), CKzgRet(ret)
}
/*
VerifyAggregateKZGProof is the binding for:
VerifyBlobKZGProofBatch is the binding for:
C_KZG_RET verify_aggregate_kzg_proof(
C_KZG_RET verify_blob_kzg_proof_batch(
bool *out,
const Blob *blobs,
const Bytes48 *commitments_bytes,
size_t n,
const Bytes48 *aggregated_proof_bytes,
const Bytes48 *proofs_bytes,
const KZGSettings *s);
*/
func VerifyAggregateKZGProof(blobs []Blob, commitmentsBytes []Bytes48, aggregatedProofBytes Bytes48) (bool, CKzgRet) {
func VerifyBlobKZGProofBatch(blobs []Blob, commitmentsBytes, proofsBytes []Bytes48) (bool, CKzgRet) {
if !loaded {
panic("trusted setup isn't loaded")
}
if len(blobs) != len(commitmentsBytes) {
panic("len(blobs) != len(commitments)")
if len(blobs) != len(commitmentsBytes) || len(blobs) != len(proofsBytes) {
panic("the number of blobs/commitments/proofs should be equal")
}
var result C.bool
ret := C.verify_aggregate_kzg_proof(
ret := C.verify_blob_kzg_proof_batch(
&result,
*(**C.Blob)(unsafe.Pointer(&blobs)),
*(**C.Bytes48)(unsafe.Pointer(&commitmentsBytes)),
*(**C.Bytes48)(unsafe.Pointer(&proofsBytes)),
(C.size_t)(len(blobs)),
(*C.Bytes48)(unsafe.Pointer(&aggregatedProofBytes)),
&settings)
return bool(result), CKzgRet(ret)
}

View File

@ -2,11 +2,12 @@ package cgokzg4844
import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"math/rand"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/require"
@ -103,60 +104,189 @@ func HumanBytes(b int64) string {
return fmt.Sprintf("%v%cB", float64(b)/float64(div), "KMGTPE"[exp])
}
///////////////////////////////////////////////////////////////////////////////
// Test Helper Functions
///////////////////////////////////////////////////////////////////////////////
func getBlob(path string) Blob {
inputBytes, err := os.ReadFile(path)
if err != nil {
panic(err)
}
var blob Blob
err = blob.UnmarshalText(inputBytes)
if err != nil {
panic(err)
}
return blob
}
func getBytes32(path string) Bytes32 {
inputBytes, err := os.ReadFile(path)
if err != nil {
panic(err)
}
var bytes32 Bytes32
err = bytes32.UnmarshalText(inputBytes)
if err != nil {
panic(err)
}
return bytes32
}
func getBytes48(path string) Bytes48 {
inputBytes, err := os.ReadFile(path)
if err != nil {
panic(err)
}
var bytes48 Bytes48
err = bytes48.UnmarshalText(inputBytes)
if err != nil {
panic(err)
}
return bytes48
}
func getBoolean(path string) bool {
inputBytes, err := os.ReadFile(path)
if err != nil {
panic(err)
}
return strings.Contains(string(inputBytes), "true")
}
///////////////////////////////////////////////////////////////////////////////
// Tests
///////////////////////////////////////////////////////////////////////////////
func TestComputeAggregateKZGProof(t *testing.T) {
type Test struct {
TestCases []struct {
Polynomials []Blob `json:"Polynomials"`
Proof Bytes48 `json:"Proof"`
Commitments []Bytes48 `json:"Commitments"`
var (
testDir = "../../tests"
blobToKZGCommitmentTests = filepath.Join(testDir, "blob_to_kzg_commitment/*")
computeKZGProofTests = filepath.Join(testDir, "compute_kzg_proof/*")
computeBlobKZGProofTests = filepath.Join(testDir, "compute_blob_kzg_proof/*")
verifyKZGProofTests = filepath.Join(testDir, "verify_kzg_proof/*")
verifyBlobKZGProofTests = filepath.Join(testDir, "verify_blob_kzg_proof/*")
verifyBlobKZGProofBatchTests = filepath.Join(testDir, "verify_blob_kzg_proof_batch/*")
)
func TestBlobToKZGCommitment(t *testing.T) {
tests, err := filepath.Glob(blobToKZGCommitmentTests)
require.NoError(t, err)
for _, test := range tests {
blob := getBlob(filepath.Join(test, "blob.txt"))
commitment, ret := BlobToKZGCommitment(blob)
if ret == C_KZG_OK {
expectedCommitment := KZGCommitment(getBytes48(filepath.Join(test, "commitment.txt")))
require.Equal(t, commitment, expectedCommitment, test)
} else {
require.NoFileExists(t, filepath.Join(test, "commitment.txt"))
}
}
}
testFile, err := os.Open("../rust/test_vectors/public_agg_proof.json")
require.NoError(t, err)
defer testFile.Close()
test := Test{}
err = json.NewDecoder(testFile).Decode(&test)
func TestComputeKZGProof(t *testing.T) {
tests, err := filepath.Glob(computeKZGProofTests)
require.NoError(t, err)
for _, test := range tests {
blob := getBlob(filepath.Join(test, "blob.txt"))
inputPoint := getBytes32(filepath.Join(test, "input_point.txt"))
for _, tc := range test.TestCases {
proof, ret := ComputeAggregateKZGProof(tc.Polynomials)
require.Zero(t, ret)
require.Equal(t, tc.Proof[:], proof[:])
for i := range tc.Polynomials {
commitment, ret := BlobToKZGCommitment(tc.Polynomials[i])
require.Equal(t, C_KZG_OK, ret)
require.Equal(t, tc.Commitments[i][:], commitment[:])
proof, ret := ComputeKZGProof(blob, inputPoint)
if ret == C_KZG_OK {
expectedProof := KZGProof(getBytes48(filepath.Join(test, "proof.txt")))
require.Equal(t, proof, expectedProof, test)
} else {
require.NoFileExists(t, filepath.Join(test, "proof.txt"))
}
}
}
func TestComputeBlobKZGProof(t *testing.T) {
tests, err := filepath.Glob(computeBlobKZGProofTests)
require.NoError(t, err)
for _, test := range tests {
blob := getBlob(filepath.Join(test, "blob.txt"))
proof, ret := ComputeBlobKZGProof(blob)
if ret == C_KZG_OK {
expectedProof := KZGProof(getBytes48(filepath.Join(test, "proof.txt")))
require.Equal(t, proof, expectedProof, test)
} else {
require.NoFileExists(t, filepath.Join(test, "proof.txt"))
}
}
}
func TestVerifyKZGProof(t *testing.T) {
type Test struct {
TestCases []struct {
Polynomial Blob `json:"Polynomial"`
Proof Bytes48 `json:"Proof"`
Commitment Bytes48 `json:"Commitment"`
InputPoint Bytes32 `json:"InputPoint"`
ClaimedValue Bytes32 `json:"ClaimedValue"`
tests, err := filepath.Glob(verifyKZGProofTests)
require.NoError(t, err)
for _, test := range tests {
commitment := getBytes48(filepath.Join(test, "commitment.txt"))
inputPoint := getBytes32(filepath.Join(test, "input_point.txt"))
claimedValue := getBytes32(filepath.Join(test, "claimed_value.txt"))
proof := getBytes48(filepath.Join(test, "proof.txt"))
ok, ret := VerifyKZGProof(commitment, inputPoint, claimedValue, proof)
if ret == C_KZG_OK {
expectedOk := getBoolean(filepath.Join(test, "ok.txt"))
require.Equal(t, ok, expectedOk, test)
} else {
require.NoFileExists(t, filepath.Join(test, "ok.txt"))
}
}
}
testFile, err := os.Open("../rust/test_vectors/public_verify_kzg_proof.json")
require.NoError(t, err)
defer testFile.Close()
test := Test{}
err = json.NewDecoder(testFile).Decode(&test)
func TestVerifyBlobKZGProof(t *testing.T) {
tests, err := filepath.Glob(verifyBlobKZGProofTests)
require.NoError(t, err)
for _, test := range tests {
blob := getBlob(filepath.Join(test, "blob.txt"))
commitment := getBytes48(filepath.Join(test, "commitment.txt"))
proof := getBytes48(filepath.Join(test, "proof.txt"))
for _, tc := range test.TestCases {
result, ret := VerifyKZGProof(tc.Commitment, tc.InputPoint, tc.ClaimedValue, tc.Proof)
require.Equal(t, C_KZG_OK, ret)
require.True(t, result)
ok, ret := VerifyBlobKZGProof(blob, commitment, proof)
if ret == C_KZG_OK {
expectedOk := getBoolean(filepath.Join(test, "ok.txt"))
require.Equal(t, ok, expectedOk, test)
} else {
require.NoFileExists(t, filepath.Join(test, "ok.txt"))
}
}
}
func TestVerifyBlobKZGProofBatch(t *testing.T) {
tests, err := filepath.Glob(verifyBlobKZGProofBatchTests)
require.NoError(t, err)
for _, test := range tests {
blobFiles, err := filepath.Glob(filepath.Join(test, "blobs/*"))
require.NoError(t, err)
blobs := make([]Blob, len(blobFiles))
for i, blobFile := range blobFiles {
blobs[i] = getBlob(blobFile)
}
commitmentFiles, err := filepath.Glob(filepath.Join(test, "commitments/*"))
require.NoError(t, err)
commitments := make([]Bytes48, len(commitmentFiles))
for i, commitmentFile := range commitmentFiles {
commitments[i] = getBytes48(commitmentFile)
}
proofFiles, err := filepath.Glob(filepath.Join(test, "proofs/*"))
require.NoError(t, err)
proofs := make([]Bytes48, len(proofFiles))
for i, proofFile := range proofFiles {
proofs[i] = getBytes48(proofFile)
}
require.Len(t, commitments, len(blobs))
require.Len(t, proofs, len(blobs))
ok, ret := VerifyBlobKZGProofBatch(blobs, commitments, proofs)
if ret == C_KZG_OK {
expectedOk := getBoolean(filepath.Join(test, "ok.txt"))
require.Equal(t, ok, expectedOk, test)
} else {
require.NoFileExists(t, filepath.Join(test, "ok.txt"))
}
}
}
@ -168,15 +298,20 @@ func Benchmark(b *testing.B) {
const length = 64
blobs := [length]Blob{}
commitments := [length]Bytes48{}
proofs := [length]Bytes48{}
fields := [length]Bytes32{}
for i := 0; i < length; i++ {
blobs[i] = GetRandBlob(int64(i))
commitment, _ := BlobToKZGCommitment(blobs[i])
blob := GetRandBlob(int64(i))
commitment, ret := BlobToKZGCommitment(blob)
require.Equal(b, ret, C_KZG_OK)
proof, ret := ComputeBlobKZGProof(blob)
require.Equal(b, ret, C_KZG_OK)
blobs[i] = blob
commitments[i] = Bytes48(commitment)
proofs[i] = Bytes48(proof)
fields[i] = GetRandFieldElement(int64(i))
}
z := Bytes32{1, 2, 3}
y := Bytes32{4, 5, 6}
trustedProof, _ := ComputeAggregateKZGProof(blobs[:1])
proof := Bytes48(trustedProof)
///////////////////////////////////////////////////////////////////////////
// Public functions
@ -184,39 +319,38 @@ func Benchmark(b *testing.B) {
b.Run("BlobToKZGCommitment", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_, ret := BlobToKZGCommitment(blobs[0])
require.Equal(b, C_KZG_OK, ret)
BlobToKZGCommitment(blobs[0])
}
})
b.Run("ComputeKZGProof", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_, ret := ComputeKZGProof(blobs[0], z)
require.Equal(b, C_KZG_OK, ret)
ComputeKZGProof(blobs[0], fields[0])
}
})
b.Run("ComputeBlobKZGProof", func(b *testing.B) {
for n := 0; n < b.N; n++ {
ComputeBlobKZGProof(blobs[0])
}
})
b.Run("VerifyKZGProof", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_, ret := VerifyKZGProof(commitments[0], z, y, proof)
require.Equal(b, C_KZG_OK, ret)
VerifyKZGProof(commitments[0], fields[0], fields[1], proofs[0])
}
})
b.Run("VerifyBlobKZGProof", func(b *testing.B) {
for n := 0; n < b.N; n++ {
VerifyBlobKZGProof(blobs[0], commitments[0], proofs[0])
}
})
for i := 1; i <= len(blobs); i *= 2 {
b.Run(fmt.Sprintf("ComputeAggregateKZGProof(blobs=%v)", i), func(b *testing.B) {
b.Run(fmt.Sprintf("VerifyBlobKZGProofBatch(count=%v)", i), func(b *testing.B) {
for n := 0; n < b.N; n++ {
_, ret := ComputeAggregateKZGProof(blobs[:i])
require.Equal(b, C_KZG_OK, ret)
}
})
}
for i := 1; i <= len(blobs); i *= 2 {
b.Run(fmt.Sprintf("VerifyAggregateKZGProof(blobs=%v)", i), func(b *testing.B) {
for n := 0; n < b.N; n++ {
_, ret := VerifyAggregateKZGProof(blobs[:i], commitments[:i], proof)
require.Equal(b, C_KZG_OK, ret)
VerifyBlobKZGProofBatch(blobs[:i], commitments[:i], proofs[:i])
}
})
}
@ -226,8 +360,8 @@ func Benchmark(b *testing.B) {
///////////////////////////////////////////////////////////////////////////
for i := 2; i <= 20; i += 2 {
var numBytes = int64(1 << i)
var bytes = make([]byte, numBytes)
numBytes := int64(1 << i)
bytes := make([]byte, numBytes)
b.Run(fmt.Sprintf("sha256(size=%v)", HumanBytes(numBytes)), func(b *testing.B) {
b.SetBytes(numBytes)
for n := 0; n < b.N; n++ {

View File

@ -11,7 +11,10 @@ void reset_trusted_setup()
{
if (settings)
{
free_trusted_setup(settings);
if (settings->fs)
{
free_trusted_setup(settings);
}
free(settings);
settings = NULL;
}
@ -39,6 +42,21 @@ void throw_invalid_size_exception(JNIEnv *env, const char *prefix, size_t size,
throw_c_kzg_exception(env, C_KZG_BADARGS, message);
}
KZGSettings *allocate_settings(JNIEnv *env) {
KZGSettings *s = malloc(sizeof(KZGSettings));
if (s == NULL)
{
throw_exception(env, "Failed to allocate memory for the Trusted Setup.");
}
else
{
s->fs = NULL;
s->g1_values = NULL;
s->g2_values = NULL;
}
return s;
}
JNIEXPORT jint JNICALL Java_ethereum_ckzg4844_CKZG4844JNI_getFieldElementsPerBlob(JNIEnv *env, jclass thisCls)
{
return (jint)FIELD_ELEMENTS_PER_BLOB;
@ -52,12 +70,7 @@ JNIEXPORT void JNICALL Java_ethereum_ckzg4844_CKZG4844JNI_loadTrustedSetup__Ljav
return;
}
settings = malloc(sizeof(KZGSettings));
if (settings == NULL)
{
throw_exception(env, "Failed to allocate memory for the Trusted Setup.");
return;
}
settings = allocate_settings(env);
const char *file_native = (*env)->GetStringUTFChars(env, file, 0);
@ -92,12 +105,7 @@ JNIEXPORT void JNICALL Java_ethereum_ckzg4844_CKZG4844JNI_loadTrustedSetup___3BJ
return;
}
settings = malloc(sizeof(KZGSettings));
if (settings == NULL)
{
throw_exception(env, "Failed to allocate memory for the Trusted Setup.");
return;
}
settings = allocate_settings(env);
jbyte *g1_native = (*env)->GetByteArrayElements(env, g1, NULL);
jbyte *g2_native = (*env)->GetByteArrayElements(env, g2, NULL);

View File

@ -20,7 +20,7 @@ public class CKZG4844JNITest {
private enum TrustedSetupSource {
FILE,
PARAMETERS,
RESOURCE;
RESOURCE
}
private static final Preset PRESET;
@ -239,6 +239,32 @@ public class CKZG4844JNITest {
assertExceptionIsTrustedSetupIsNotLoaded(exception);
}
@Test
public void shouldThrowExceptionOnIncorrectTrustedSetupParameters() {
final LoadTrustedSetupParameters parameters =
TestUtils.createLoadTrustedSetupParameters(TRUSTED_SETUP_FILE_BY_PRESET.get(PRESET));
final CKZGException ckzgException =
assertThrows(
CKZGException.class,
() ->
CKZG4844JNI.loadTrustedSetup(
parameters.getG1(),
parameters.getG1Count() + 1,
parameters.getG2(),
parameters.getG2Count()));
assertTrue(ckzgException.getMessage().contains("C_KZG_BADARGS"));
}
@Test
public void shouldThrowExceptionOnIncorrectTrustedSetupFromFile() {
final Preset incorrectPreset = PRESET == Preset.MAINNET ? Preset.MINIMAL : Preset.MAINNET;
final CKZGException ckzgException =
assertThrows(
CKZGException.class,
() -> CKZG4844JNI.loadTrustedSetup(TRUSTED_SETUP_FILE_BY_PRESET.get(incorrectPreset)));
assertTrue(ckzgException.getMessage().contains("C_KZG_BADARGS"));
}
private void assertExceptionIsTrustedSetupIsNotLoaded(final RuntimeException exception) {
assertEquals("Trusted Setup is not loaded.", exception.getMessage());
}

View File

@ -51,37 +51,98 @@ static PyObject* blob_to_kzg_commitment_wrap(PyObject *self, PyObject *args) {
return out;
}
static PyObject* compute_aggregate_kzg_proof_wrap(PyObject *self, PyObject *args) {
PyObject *b, *s;
static PyObject* compute_kzg_proof_wrap(PyObject *self, PyObject *args) {
PyObject *b, *z, *s;
if (!PyArg_UnpackTuple(args, "compute_aggregate_kzg_proof", 2, 2, &b, &s) ||
if (!PyArg_UnpackTuple(args, "compute_kzg_proof_wrap", 3, 3, &b, &z, &s) ||
!PyBytes_Check(b) ||
!PyBytes_Check(z) ||
!PyCapsule_IsValid(s, "KZGSettings"))
return PyErr_Format(PyExc_ValueError, "expected bytes, trusted setup");
return PyErr_Format(PyExc_ValueError, "expected bytes, bytes, trusted setup");
Py_ssize_t n = PyBytes_Size(b);
if (n % BYTES_PER_BLOB != 0)
return PyErr_Format(PyExc_ValueError, "expected blobs to be a multiple of BYTES_PER_BLOB bytes");
n = n / BYTES_PER_BLOB;
if (PyBytes_Size(b) != BYTES_PER_BLOB)
return PyErr_Format(PyExc_ValueError, "expected blobs to be BYTES_PER_BLOB bytes");
if (PyBytes_Size(z) != BYTES_PER_FIELD_ELEMENT)
return PyErr_Format(PyExc_ValueError, "expected blobs to be BYTES_PER_FIELD_ELEMENT bytes");
PyObject *out = PyBytes_FromStringAndSize(NULL, BYTES_PER_PROOF);
if (out == NULL) return PyErr_NoMemory();
Blob *blobs = (Blob *)PyBytes_AsString(b);
KZGProof *k = (KZGProof *)PyBytes_AsString(out);
if (compute_aggregate_kzg_proof(k, blobs, n,
PyCapsule_GetPointer(s, "KZGSettings")) != C_KZG_OK) {
Blob *blob = (Blob *)PyBytes_AsString(b);
Bytes32 *z_bytes = (Bytes32 *)PyBytes_AsString(z);
KZGProof *proof = (KZGProof *)PyBytes_AsString(out);
if (compute_kzg_proof(proof, blob, z_bytes, PyCapsule_GetPointer(s, "KZGSettings")) != C_KZG_OK) {
Py_DECREF(out);
return PyErr_Format(PyExc_RuntimeError, "compute_aggregate_kzg_proof failed");
return PyErr_Format(PyExc_RuntimeError, "compute_kzg_proof failed");
}
return out;
}
static PyObject* verify_aggregate_kzg_proof_wrap(PyObject *self, PyObject *args) {
static PyObject* compute_blob_kzg_proof_wrap(PyObject *self, PyObject *args) {
PyObject *b, *s;
if (!PyArg_UnpackTuple(args, "compute_kzg_proof_wrap", 2, 2, &b, &s) ||
!PyBytes_Check(b) ||
!PyCapsule_IsValid(s, "KZGSettings"))
return PyErr_Format(PyExc_ValueError, "expected bytes, trusted setup");
if (PyBytes_Size(b) != BYTES_PER_BLOB)
return PyErr_Format(PyExc_ValueError, "expected blobs to be BYTES_PER_BLOB bytes");
PyObject *out = PyBytes_FromStringAndSize(NULL, BYTES_PER_PROOF);
if (out == NULL) return PyErr_NoMemory();
Blob *blob = (Blob *)PyBytes_AsString(b);
KZGProof *proof = (KZGProof *)PyBytes_AsString(out);
if (compute_blob_kzg_proof(proof, blob, PyCapsule_GetPointer(s, "KZGSettings")) != C_KZG_OK) {
Py_DECREF(out);
return PyErr_Format(PyExc_RuntimeError, "compute_blob_kzg_proof failed");
}
return out;
}
static PyObject* verify_kzg_proof_wrap(PyObject *self, PyObject *args) {
PyObject *c, *z, *y, *p, *s;
if (!PyArg_UnpackTuple(args, "verify_kzg_proof", 5, 5, &c, &z, &y, &p, &s) ||
!PyBytes_Check(c) ||
!PyBytes_Check(z) ||
!PyBytes_Check(y) ||
!PyBytes_Check(p) ||
!PyCapsule_IsValid(s, "KZGSettings"))
return PyErr_Format(PyExc_ValueError,
"expected bytes, bytes, bytes, bytes, trusted setup");
if (PyBytes_Size(c) != BYTES_PER_COMMITMENT)
return PyErr_Format(PyExc_ValueError, "expected commitment to be BYTES_PER_COMMITMENT bytes");
if (PyBytes_Size(z) != BYTES_PER_FIELD_ELEMENT)
return PyErr_Format(PyExc_ValueError, "expected z to be BYTES_PER_FIELD_ELEMENT bytes");
if (PyBytes_Size(y) != BYTES_PER_FIELD_ELEMENT)
return PyErr_Format(PyExc_ValueError, "expected y to be BYTES_PER_FIELD_ELEMENT bytes");
if (PyBytes_Size(p) != BYTES_PER_PROOF)
return PyErr_Format(PyExc_ValueError, "expected proof to be BYTES_PER_PROOF bytes");
const Bytes48 *commitment_bytes = (Bytes48 *)PyBytes_AsString(c);
const Bytes32 *z_bytes = (Bytes32 *)PyBytes_AsString(z);
const Bytes32 *y_bytes = (Bytes32 *)PyBytes_AsString(y);
const Bytes48 *proof_bytes = (Bytes48 *)PyBytes_AsString(p);
bool ok;
if (verify_kzg_proof(&ok,
commitment_bytes, z_bytes, y_bytes, proof_bytes,
PyCapsule_GetPointer(s, "KZGSettings")) != C_KZG_OK) {
return PyErr_Format(PyExc_RuntimeError, "verify_kzg_proof failed");
}
if (ok) Py_RETURN_TRUE; else Py_RETURN_FALSE;
}
static PyObject* verify_blob_kzg_proof_wrap(PyObject *self, PyObject *args) {
PyObject *b, *c, *p, *s;
if (!PyArg_UnpackTuple(args, "verify_aggregate_kzg_proof", 4, 4, &b, &c, &p, &s) ||
if (!PyArg_UnpackTuple(args, "verify_blob_kzg_proof", 4, 4, &b, &c, &p, &s) ||
!PyBytes_Check(b) ||
!PyBytes_Check(c) ||
!PyBytes_Check(p) ||
@ -89,41 +150,79 @@ static PyObject* verify_aggregate_kzg_proof_wrap(PyObject *self, PyObject *args)
return PyErr_Format(PyExc_ValueError,
"expected bytes, bytes, bytes, trusted setup");
if (PyBytes_Size(b) != BYTES_PER_BLOB)
return PyErr_Format(PyExc_ValueError, "expected blob to be BYTES_PER_BLOB bytes");
if (PyBytes_Size(c) != BYTES_PER_COMMITMENT)
return PyErr_Format(PyExc_ValueError, "expected commitment to be BYTES_PER_COMMITMENT bytes");
if (PyBytes_Size(p) != BYTES_PER_PROOF)
return PyErr_Format(PyExc_ValueError, "expected proof to be BYTES_PER_PROOF bytes");
Py_ssize_t n = PyBytes_Size(b);
if (n % BYTES_PER_BLOB != 0)
return PyErr_Format(PyExc_ValueError, "expected blobs to be a multiple of BYTES_PER_BLOB bytes");
n = n / BYTES_PER_BLOB;
Py_ssize_t m = PyBytes_Size(c);
if (m % BYTES_PER_COMMITMENT != 0)
return PyErr_Format(PyExc_ValueError, "expected commitments to be a multiple of BYTES_PER_COMMITMENT bytes");
m = m / BYTES_PER_COMMITMENT;
if (m != n)
return PyErr_Format(PyExc_ValueError, "expected same number of commitments as polynomials");
const Blob* blobs = (Blob *)PyBytes_AsString(b);
const Blob *blob_bytes = (Blob *)PyBytes_AsString(b);
const Bytes48 *commitment_bytes = (Bytes48 *)PyBytes_AsString(c);
const Bytes48 *proof_bytes = (Bytes48 *)PyBytes_AsString(p);
const Bytes48 *commitments_bytes = (Bytes48 *)PyBytes_AsString(c);
bool out;
if (verify_aggregate_kzg_proof(&out,
blobs, commitments_bytes, n, proof_bytes,
bool ok;
if (verify_blob_kzg_proof(&ok,
blob_bytes, commitment_bytes, proof_bytes,
PyCapsule_GetPointer(s, "KZGSettings")) != C_KZG_OK) {
return PyErr_Format(PyExc_RuntimeError, "verify_aggregate_kzg_proof failed");
return PyErr_Format(PyExc_RuntimeError, "verify_blob_kzg_proof failed");
}
if (out) Py_RETURN_TRUE; else Py_RETURN_FALSE;
if (ok) Py_RETURN_TRUE; else Py_RETURN_FALSE;
}
static PyObject* verify_blob_kzg_proof_batch_wrap(PyObject *self, PyObject *args) {
PyObject *b, *c, *p, *s;
if (!PyArg_UnpackTuple(args, "verify_blob_kzg_proof_batch", 4, 4, &b, &c, &p, &s) ||
!PyBytes_Check(b) ||
!PyBytes_Check(c) ||
!PyBytes_Check(p) ||
!PyCapsule_IsValid(s, "KZGSettings"))
return PyErr_Format(PyExc_ValueError,
"expected bytes, bytes, bytes, trusted setup");
Py_ssize_t blobs_count = PyBytes_Size(b);
if (blobs_count % BYTES_PER_BLOB != 0)
return PyErr_Format(PyExc_ValueError, "expected blobs to be a multiple of BYTES_PER_BLOB bytes");
blobs_count = blobs_count / BYTES_PER_BLOB;
Py_ssize_t commitments_count = PyBytes_Size(c);
if (commitments_count % BYTES_PER_COMMITMENT != 0)
return PyErr_Format(PyExc_ValueError, "expected commitments to be a multiple of BYTES_PER_COMMITMENT bytes");
commitments_count = commitments_count / BYTES_PER_COMMITMENT;
Py_ssize_t proofs_count = PyBytes_Size(p);
if (proofs_count % BYTES_PER_PROOF != 0)
return PyErr_Format(PyExc_ValueError, "expected blobs to be a multiple of BYTES_PER_PROOF bytes");
proofs_count = proofs_count / BYTES_PER_PROOF;
if (blobs_count != commitments_count || blobs_count != proofs_count) {
return PyErr_Format(PyExc_ValueError, "expected same number of blobs/commitments/proofs");
}
const Blob *blobs_bytes = (Blob *)PyBytes_AsString(b);
const Bytes48 *commitments_bytes = (Bytes48 *)PyBytes_AsString(c);
const Bytes48 *proofs_bytes = (Bytes48 *)PyBytes_AsString(p);
bool ok;
if (verify_blob_kzg_proof_batch(&ok,
blobs_bytes, commitments_bytes, proofs_bytes, blobs_count,
PyCapsule_GetPointer(s, "KZGSettings")) != C_KZG_OK) {
return PyErr_Format(PyExc_RuntimeError, "verify_blob_kzg_proof_batch failed");
}
if (ok) Py_RETURN_TRUE; else Py_RETURN_FALSE;
}
static PyMethodDef ckzgmethods[] = {
{"load_trusted_setup", load_trusted_setup_wrap, METH_VARARGS, "Load trusted setup from file path"},
{"blob_to_kzg_commitment", blob_to_kzg_commitment_wrap, METH_VARARGS, "Create a commitment from a blob"},
{"compute_aggregate_kzg_proof", compute_aggregate_kzg_proof_wrap, METH_VARARGS, "Compute aggregate KZG proof"},
{"verify_aggregate_kzg_proof", verify_aggregate_kzg_proof_wrap, METH_VARARGS, "Verify aggregate KZG proof"},
{"compute_kzg_proof", compute_kzg_proof_wrap, METH_VARARGS, "Compute a proof for a blob/field"},
{"compute_blob_kzg_proof", compute_blob_kzg_proof_wrap, METH_VARARGS, "Compute a proof for a blob"},
{"verify_kzg_proof", verify_kzg_proof_wrap, METH_VARARGS, "Verify a proof for the given inputs"},
{"verify_blob_kzg_proof", verify_blob_kzg_proof_wrap, METH_VARARGS, "Verify a blob/commitment/proof combo"},
{"verify_blob_kzg_proof_batch", verify_blob_kzg_proof_batch_wrap, METH_VARARGS, "Verify multiple blob/commitment/proof combos"},
{NULL, NULL, 0, NULL}
};

View File

@ -1,36 +1,128 @@
import glob
from os.path import join
from os.path import isfile
import ckzg
import random
# Commit to a few random blobs
###############################################################################
# Constants
###############################################################################
BLOB_SIZE = 4096
MAX_BLOBS_PER_BLOCK = 16
blob_to_kzg_commitment_tests = "../../tests/blob_to_kzg_commitment/*"
compute_kzg_proof_tests = "../../tests/compute_kzg_proof/*"
compute_blob_kzg_proof_tests = "../../tests/compute_blob_kzg_proof/*"
verify_kzg_proof_tests = "../../tests/verify_kzg_proof/*"
verify_blob_kzg_proof_tests = "../../tests/verify_blob_kzg_proof/*"
verify_blob_kzg_proof_batch_tests = "../../tests/verify_blob_kzg_proof_batch/*"
blobs = [
# use zero final bytes to easily ensure the encodings are valid
b''.join([b''.join([random.randbytes(31), bytes(1)]) for _ in range(BLOB_SIZE)])
for _ in range(3)
]
###############################################################################
# Helper Functions
###############################################################################
ts = ckzg.load_trusted_setup("../../src/trusted_setup.txt")
def get_blob(path):
with open(path, "r") as f:
return bytes.fromhex(f.read())
kzg_commitments = b''.join([ckzg.blob_to_kzg_commitment(blob, ts) for blob in blobs])
def get_bytes32(path):
with open(path, "r") as f:
return bytes.fromhex(f.read())
# Compute proof for these blobs
def get_bytes48(path):
with open(path, "r") as f:
return bytes.fromhex(f.read())
blobs_bytes = b''.join(blobs)
def get_boolean(path):
with open(path, "r") as f:
return "true" in f.read()
proof = ckzg.compute_aggregate_kzg_proof(blobs_bytes, ts)
###############################################################################
# Tests
###############################################################################
# Verify proof
def test_blob_to_kzg_commitment(ts):
for test in glob.glob(blob_to_kzg_commitment_tests):
blob = get_blob(join(test, "blob.txt"))
try:
commitment = ckzg.blob_to_kzg_commitment(blob, ts)
expected_commitment = get_bytes48(join(test, "commitment.txt"))
assert commitment == expected_commitment
except:
assert not isfile(join(test, "commitment.txt"))
assert ckzg.verify_aggregate_kzg_proof(blobs_bytes, kzg_commitments, proof, ts), 'verify failed'
def test_compute_kzg_proof(ts):
for test in glob.glob(compute_kzg_proof_tests):
blob = get_blob(join(test, "blob.txt"))
input_point = get_bytes32(join(test, "input_point.txt"))
try:
proof = ckzg.compute_kzg_proof(blob, input_point, ts)
expected_proof = get_bytes48(join(test, "proof.txt"))
assert proof == expected_proof
except:
assert not isfile(join(test, "proof.txt"))
# Verification fails at wrong value
def test_compute_blob_kzg_proof(ts):
for test in glob.glob(compute_blob_kzg_proof_tests):
blob = get_blob(join(test, "blob.txt"))
try:
proof = ckzg.compute_blob_kzg_proof(blob, ts)
expected_proof = get_bytes48(join(test, "proof.txt"))
assert proof == expected_proof
except:
assert not isfile(join(test, "proof.txt"))
other = b'x' if not blobs_bytes.startswith(b'x') else b'y'
other_bytes = other + blobs_bytes[1:]
def test_verify_kzg_proof(ts):
for test in glob.glob(verify_kzg_proof_tests):
commitment = get_bytes48(join(test, "commitment.txt"))
input_point = get_bytes32(join(test, "input_point.txt"))
claimed_value = get_bytes32(join(test, "claimed_value.txt"))
proof = get_bytes48(join(test, "proof.txt"))
try:
ok = ckzg.verify_kzg_proof(commitment, input_point, claimed_value, proof, ts)
expected_ok = get_boolean(join(test, "ok.txt"))
assert ok == expected_ok
except:
assert not isfile(join(test, "ok.txt"))
assert not ckzg.verify_aggregate_kzg_proof(other_bytes, kzg_commitments, proof, ts), 'verify succeeded incorrectly'
def test_verify_blob_kzg_proof(ts):
for test in glob.glob(verify_blob_kzg_proof_tests):
blob = get_bytes32(join(test, "blob.txt"))
commitment = get_bytes48(join(test, "commitment.txt"))
proof = get_bytes48(join(test, "proof.txt"))
try:
ok = ckzg.verify_blob_kzg_proof(blob, commitment, proof, ts)
expected_ok = get_boolean(join(test, "ok.txt"))
assert ok == expected_ok
except:
assert not isfile(join(test, "ok.txt"))
print('tests passed')
def test_verify_blob_kzg_proof_batch(ts):
for test in glob.glob(verify_blob_kzg_proof_batch_tests):
blob_files = sorted(glob.glob(join(test, "blobs/*")))
blobs = b"".join([get_blob(b) for b in blob_files])
commitment_files = sorted(glob.glob(join(test, "commitments/*")))
commitments = b"".join([get_bytes48(c) for c in commitment_files])
proof_files = sorted(glob.glob(join(test, "proofs/*")))
proofs = b"".join([get_bytes48(p) for p in proof_files])
try:
ok = ckzg.verify_blob_kzg_proof_batch(blobs, commitments, proofs, ts)
expected_ok = get_boolean(join(test, "ok.txt"))
assert ok == expected_ok
except:
assert not isfile(join(test, "ok.txt"))
###############################################################################
# Main Logic
###############################################################################
if __name__ == "__main__":
ts = ckzg.load_trusted_setup("../../src/trusted_setup.txt")
test_blob_to_kzg_commitment(ts)
test_compute_kzg_proof(ts)
test_compute_blob_kzg_proof(ts)
test_verify_kzg_proof(ts)
test_verify_blob_kzg_proof(ts)
test_verify_blob_kzg_proof_batch(ts)
print('tests passed')

View File

@ -21,6 +21,7 @@
*/
#include "c_kzg_4844.h"
#include <assert.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
@ -38,8 +39,9 @@
// clang-format off
/** The Fiat-Shamir protocol domain. */
/** The Fiat-Shamir protocol domains. */
static const char *FIAT_SHAMIR_PROTOCOL_DOMAIN = "FSBLOBVERIFY_V1_";
static const char *RANDOM_CHALLENGE_KZG_BATCH_DOMAIN = "RCKZGBATCH___V1_";
/** Deserialized form of the G1 identity/infinity point. */
static const g1_t G1_IDENTITY = {
@ -650,10 +652,8 @@ static C_KZG_RET validate_kzg_g1(g1_t *out, const Bytes48 *b) {
/* The point at infinity is accepted! */
if (blst_p1_is_inf(out)) return C_KZG_OK;
/* The point must be on the curve */
if (!blst_p1_on_curve(out)) return C_KZG_BADARGS;
/* The point must be on the right subgroup */
if (!blst_p1_in_g1(out)) return C_KZG_BADARGS;
@ -698,37 +698,24 @@ static C_KZG_RET blob_to_polynomial(Polynomial *p, const Blob *blob) {
return C_KZG_OK;
}
/* Forward function definition */
static void compute_powers(fr_t *out, fr_t *x, uint64_t n);
/* Input size to the Fiat-Shamir challenge computation */
static const int CHALLENGE_INPUT_SIZE = 32 + BYTES_PER_BLOB + 48;
/**
* Return the Fiat-Shamir challenges required by the rest of the protocol.
* Return the Fiat-Shamir challenge required to verify `blob` and `commitment`.
*
* @remark This function should compute challenges even if `n==0`.
*
* @param[out] eval_challenge_out The evaluation challenge
* @param[out] r_powers_out The powers of r, where r is a randomly
* generated scalar
* @param[in] polys The array of polynomials
* @param[in] comms The array of commitments
* @param[in] blob A blob
* @param[in] commitment A commitment
* @param[in] n The number of polynomials and commitments
*/
static C_KZG_RET compute_challenges(
fr_t *eval_challenge_out,
fr_t *r_powers_out,
const Polynomial *polys,
const g1_t *comms,
uint64_t n
static void compute_challenge(
fr_t *eval_challenge_out, const Blob *blob, const g1_t *commitment
) {
C_KZG_RET ret;
size_t i;
uint64_t j;
uint8_t *bytes = NULL;
// len(FIAT_SHAMIR_PROTOCOL_DOMAIN) + 8 + 8 + n blobs + n commitments
size_t input_size = 32 + (n * BYTES_PER_BLOB) + (n * 48);
ret = c_kzg_malloc((void **)&bytes, input_size);
if (ret != C_KZG_OK) goto out;
Bytes32 eval_challenge;
uint8_t bytes[CHALLENGE_INPUT_SIZE];
/* Pointer tracking `bytes` for writing on top of it */
uint8_t *offset = bytes;
@ -736,52 +723,27 @@ static C_KZG_RET compute_challenges(
/* Copy domain separator */
memcpy(offset, FIAT_SHAMIR_PROTOCOL_DOMAIN, 16);
offset += 16;
bytes_from_uint64(
offset, 0
); /* need to fill 16 bytes with the degree in little-endian */
offset += 8;
bytes_from_uint64(offset, FIELD_ELEMENTS_PER_BLOB);
offset += 8;
bytes_from_uint64(offset, n);
offset += 8;
/* Copy polynomials */
for (i = 0; i < n; i++) {
for (j = 0; j < FIELD_ELEMENTS_PER_BLOB; j++) {
bytes_from_bls_field((Bytes32 *)offset, &polys[i].evals[j]);
offset += BYTES_PER_FIELD_ELEMENT;
}
}
/* Copy blob */
memcpy(offset, blob->bytes, BYTES_PER_BLOB);
offset += BYTES_PER_BLOB;
/* Copy commitments */
for (i = 0; i < n; i++) {
bytes_from_g1((Bytes48 *)offset, &comms[i]);
offset += BYTES_PER_COMMITMENT;
}
/* Copy commitment */
bytes_from_g1((Bytes48 *)offset, commitment);
offset += BYTES_PER_COMMITMENT;
/* Now let's create challenges! */
uint8_t hashed_data[32] = {0};
blst_sha256(hashed_data, bytes, input_size);
/* Make sure we wrote the entire buffer */
assert(offset == bytes + CHALLENGE_INPUT_SIZE);
/* We will use hash_input in the computation of both challenges */
uint8_t hash_input[33];
/* Compute r */
Bytes32 r_bytes;
memcpy(hash_input, hashed_data, 32);
hash_input[32] = 0x0;
blst_sha256(r_bytes.bytes, hash_input, 33);
/* Compute r_powers */
fr_t r;
hash_to_bls_field(&r, &r_bytes);
compute_powers(r_powers_out, &r, n);
/* Compute eval_challenge */
Bytes32 eval_challenge;
hash_input[32] = 0x1;
blst_sha256(eval_challenge.bytes, hash_input, 33);
/* Now let's create the challenge! */
blst_sha256(eval_challenge.bytes, bytes, CHALLENGE_INPUT_SIZE);
hash_to_bls_field(eval_challenge_out, &eval_challenge);
out:
free(bytes);
return ret;
}
/**
@ -859,33 +821,6 @@ out:
return ret;
}
/**
* Given an array of polynomials, interpret it as a 2D matrix and compute
* the linear combination of each column with a set of scalars: return the
* resulting polynomial.
*
* @remark If `n==0` then this function should return the zero polynomial.
*
* @param[out] out The result polynomial
* @param[in] vectors The array of polynomials to be combined
* @param[in] scalars The array of scalars to multiply the polynomials with
* @param[in] n The number of polynomials and scalars
*/
static void poly_lincomb(
Polynomial *out, const Polynomial *vectors, const fr_t *scalars, uint64_t n
) {
fr_t tmp;
uint64_t i, j;
for (j = 0; j < FIELD_ELEMENTS_PER_BLOB; j++)
out->evals[j] = FR_ZERO;
for (i = 0; i < n; i++) {
for (j = 0; j < FIELD_ELEMENTS_PER_BLOB; j++) {
blst_fr_mul(&tmp, &scalars[i], &vectors[i].evals[j]);
blst_fr_add(&out->evals[j], &out->evals[j], &tmp);
}
}
}
/**
* Compute and return [ x^0, x^1, ..., x^{n-1} ].
*
@ -1090,7 +1025,7 @@ static C_KZG_RET verify_kzg_proof_impl(
}
/* Forward function declaration */
C_KZG_RET compute_kzg_proof_impl(
static C_KZG_RET compute_kzg_proof_impl(
KZGProof *out,
const Polynomial *polynomial,
const fr_t *z,
@ -1137,7 +1072,7 @@ out:
* @param[in] s The settings containing the secrets, previously
* initialised with #new_kzg_settings
*/
C_KZG_RET compute_kzg_proof_impl(
static C_KZG_RET compute_kzg_proof_impl(
KZGProof *out,
const Polynomial *polynomial,
const fr_t *z,
@ -1218,170 +1153,330 @@ out:
}
/**
* Given a list of polynomials and commitments, compute and return:
* 1. the aggregated polynomial
* 2. the aggregated KZG commitment,
* 3. the polynomial evaluation random challenge.
* Given a blob, return the KZG proof that is used to verify it against the
* commitment.
*
* @remark This function should work even if `n==0`.
*
* @param[out] poly_out The output aggregated polynomial
* @param[out] comm_out The output aggregated commitment
* @param[out] chal_out The output evaluation challenge
* @param[in] polys Array of polynomials
* @param[in] kzg_commitments Array of KZG commitments
* @param[in] n Number of polynomials and commitments
* @param[out] out The resulting proof
* @param[in] blob A blob
* @param[in] s The trusted setup
*/
static C_KZG_RET compute_aggregated_poly_and_commitment(
Polynomial *poly_out,
g1_t *comm_out,
fr_t *chal_out,
const Polynomial *polys,
const g1_t *kzg_commitments,
size_t n
C_KZG_RET compute_blob_kzg_proof(
KZGProof *out, const Blob *blob, const KZGSettings *s
) {
C_KZG_RET ret;
fr_t *r_powers = NULL;
Polynomial polynomial;
g1_t commitment_g1;
fr_t evaluation_challenge_fr;
if (n > 0) {
ret = new_fr_array(&r_powers, n);
if (ret != C_KZG_OK) goto out;
}
ret = compute_challenges(chal_out, r_powers, polys, kzg_commitments, n);
ret = blob_to_polynomial(&polynomial, blob);
if (ret != C_KZG_OK) goto out;
poly_lincomb(poly_out, polys, r_powers, n);
ret = poly_to_kzg_commitment(&commitment_g1, &polynomial, s);
if (ret != C_KZG_OK) goto out;
ret = g1_lincomb(comm_out, kzg_commitments, r_powers, n);
compute_challenge(&evaluation_challenge_fr, blob, &commitment_g1);
ret = compute_kzg_proof_impl(out, &polynomial, &evaluation_challenge_fr, s);
if (ret != C_KZG_OK) goto out;
out:
free(r_powers);
return ret;
}
/**
* Computes aggregate KZG proof given for multiple blobs.
* Given a blob and its proof, verify that it corresponds to the provided
* commitment.
*
* @remark This function should work even if `n==0`.
*
* @param[out] out The output aggregate KZG proof.
* @param[in] blobs Array of blobs to compute the aggregate proof for
* @param[in] n The number of blobs in the array
* @param[in] s The settings struct containing the commitment key
* (i.e. the trusted setup)
* @param[out] ok `true` if the proof is valid, `false` if not
* @param[in] blob Blob to verify
* @param[in] commitment_bytes Commitment to verify
* @param[in] proof_bytes Proof used for verification
* @param[in] s The settings struct containing the commitment
* verification key (i.e. the trusted setup)
*/
C_KZG_RET compute_aggregate_kzg_proof(
KZGProof *out, const Blob *blobs, size_t n, const KZGSettings *s
) {
C_KZG_RET ret;
g1_t *commitments = NULL;
Polynomial *polys = NULL;
if (n > 0) {
ret = new_g1_array(&commitments, n);
if (ret != C_KZG_OK) goto out;
ret = c_kzg_calloc((void **)&polys, n, sizeof(Polynomial));
if (ret != C_KZG_OK) goto out;
}
for (size_t i = 0; i < n; i++) {
ret = blob_to_polynomial(&polys[i], &blobs[i]);
if (ret != C_KZG_OK) goto out;
ret = poly_to_kzg_commitment(&commitments[i], &polys[i], s);
if (ret != C_KZG_OK) goto out;
}
Polynomial aggregated_poly;
g1_t aggregated_poly_commitment;
fr_t evaluation_challenge;
ret = compute_aggregated_poly_and_commitment(
&aggregated_poly,
&aggregated_poly_commitment,
&evaluation_challenge,
polys,
commitments,
n
);
if (ret != C_KZG_OK) goto out;
ret = compute_kzg_proof_impl(
out, &aggregated_poly, &evaluation_challenge, s
);
if (ret != C_KZG_OK) goto out;
out:
free(commitments);
free(polys);
return ret;
}
/**
* Computes the aggregate KZG proof for multiple blobs.
*
* @param[out] out `true` if the proof is valid, `false` if not
* @param[in] blobs Array of blobs to compute the aggregate proof for
* @param[in] n The number of blobs in the array
* @param[in] s The settings struct containing the commitment
* verification key (i.e. the trusted setup)
*/
C_KZG_RET verify_aggregate_kzg_proof(
bool *out,
const Blob *blobs,
const Bytes48 *commitments_bytes,
size_t n,
const Bytes48 *aggregated_proof_bytes,
C_KZG_RET verify_blob_kzg_proof(
bool *ok,
const Blob *blob,
const Bytes48 *commitment_bytes,
const Bytes48 *proof_bytes,
const KZGSettings *s
) {
C_KZG_RET ret;
g1_t *commitments = NULL;
Polynomial *polys = NULL;
Polynomial polynomial;
fr_t evaluation_challenge_fr, y_fr;
g1_t commitment_g1, proof_g1;
g1_t proof;
ret = bytes_to_kzg_proof(&proof, aggregated_proof_bytes);
ret = bytes_to_kzg_commitment(&commitment_g1, commitment_bytes);
if (ret != C_KZG_OK) return ret;
ret = blob_to_polynomial(&polynomial, blob);
if (ret != C_KZG_OK) return ret;
compute_challenge(&evaluation_challenge_fr, blob, &commitment_g1);
ret = evaluate_polynomial_in_evaluation_form(
&y_fr, &polynomial, &evaluation_challenge_fr, s
);
if (ret != C_KZG_OK) return ret;
ret = bytes_to_kzg_proof(&proof_g1, proof_bytes);
if (ret != C_KZG_OK) return ret;
return verify_kzg_proof_impl(
ok, &commitment_g1, &evaluation_challenge_fr, &y_fr, &proof_g1, s
);
}
/**
*
*
* @param[out] r_powers_out
* @param[in] commitments_g1
* @param[in] evaluation_challenges_fr
* @param[in] ys_fr
* @param[in] proofs_g1
*/
static C_KZG_RET compute_r_powers(
fr_t *r_powers_out,
const g1_t *commitments_g1,
const fr_t *evaluation_challenges_fr,
const fr_t *ys_fr,
const g1_t *proofs_g1,
size_t n
) {
C_KZG_RET ret;
uint8_t *bytes = NULL;
Bytes32 r_bytes;
fr_t r;
size_t input_size = 32 +
n * (BYTES_PER_COMMITMENT +
2 * BYTES_PER_FIELD_ELEMENT + BYTES_PER_PROOF);
ret = c_kzg_malloc((void **)&bytes, input_size);
if (ret != C_KZG_OK) goto out;
if (n > 0) {
ret = new_g1_array(&commitments, n);
if (ret != C_KZG_OK) goto out;
ret = c_kzg_calloc((void **)&polys, n, sizeof(Polynomial));
if (ret != C_KZG_OK) goto out;
}
/* Pointer tracking `bytes` for writing on top of it */
uint8_t *offset = bytes;
/* Copy domain separator */
memcpy(offset, RANDOM_CHALLENGE_KZG_BATCH_DOMAIN, 16);
offset += 16;
bytes_from_uint64(offset, FIELD_ELEMENTS_PER_BLOB);
offset += 8;
bytes_from_uint64(offset, n);
offset += 8;
for (size_t i = 0; i < n; i++) {
ret = bytes_to_kzg_commitment(&commitments[i], &commitments_bytes[i]);
bytes_from_g1((Bytes48 *)offset, &commitments_g1[i]);
offset += BYTES_PER_COMMITMENT;
bytes_from_bls_field((Bytes32 *)offset, &evaluation_challenges_fr[i]);
offset += BYTES_PER_FIELD_ELEMENT;
bytes_from_bls_field((Bytes32 *)offset, &ys_fr[i]);
offset += BYTES_PER_FIELD_ELEMENT;
bytes_from_g1((Bytes48 *)offset, &proofs_g1[i]);
offset += BYTES_PER_PROOF;
}
/* Now let's create the challenge! */
blst_sha256(r_bytes.bytes, bytes, input_size);
hash_to_bls_field(&r, &r_bytes);
compute_powers(r_powers_out, &r, n);
/* Make sure we wrote the entire buffer */
assert(offset == bytes + input_size);
ret = C_KZG_OK;
out:
free(bytes);
return ret;
}
/**
* Helper function for verify_blob_kzg_proof_batch(): actually perform the
* verification.
*
* @remark This function assumes that `n` is trusted and that all input arrays
* contain `n` elements. `n` should be the actual size of the arrays and not
* read off a length field in the protocol.
*
* @remark This function only works for `n > 0`.
*
* @param[out] ok `true` if the proofs are valid,
* `false` if not
* @param[in] commitments_g1 Array of commitments to verify
* @param[in] evaluation_challenges_fr Array of evaluation of points for the
* KZG proofs
* @param[in] ys_fr Array of evaluation results for the
* KZG proofs
* @param[in] proofs_g1 Array of proofs used for verification
* @param[in] n The number of
* blobs/commitments/proofs in the
* arrays
* @param[in] s The settings struct containing the
* commitment verification key (i.e. the
* trusted setup)
*/
static C_KZG_RET verify_kzg_proof_batch(
bool *ok,
const g1_t *commitments_g1,
const fr_t *evaluation_challenges_fr,
const fr_t *ys_fr,
const g1_t *proofs_g1,
size_t n,
const KZGSettings *s
) {
C_KZG_RET ret;
g1_t proof_lincomb, proof_z_lincomb, C_minus_y_lincomb, rhs_g1;
fr_t *r_powers = NULL;
g1_t *C_minus_y = NULL;
fr_t *r_times_z = NULL;
assert(n > 0);
/* First let's allocate our arrays */
ret = new_fr_array(&r_powers, n);
if (ret != C_KZG_OK) goto out;
ret = new_g1_array(&C_minus_y, n);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&r_times_z, n);
if (ret != C_KZG_OK) goto out;
/* Compute the random lincomb challenges */
ret = compute_r_powers(
r_powers, commitments_g1, evaluation_challenges_fr, ys_fr, proofs_g1, n
);
if (ret != C_KZG_OK) goto out;
/* Compute \sum r^i * Proof_i */
ret = g1_lincomb(&proof_lincomb, proofs_g1, r_powers, n);
if (ret != C_KZG_OK) goto out;
for (size_t i = 0; i < n; i++) {
g1_t ys_encrypted;
/* Get [y_i] */
g1_mul(&ys_encrypted, &G1_GENERATOR, &ys_fr[i]);
/* Get C_i - [y_i] */
g1_sub(&C_minus_y[i], &commitments_g1[i], &ys_encrypted);
/* Get r^i * z_i */
blst_fr_mul(&r_times_z[i], &r_powers[i], &evaluation_challenges_fr[i]);
}
/* Get \sum r^i z_i Proof_i */
ret = g1_lincomb(&proof_z_lincomb, proofs_g1, r_times_z, n);
if (ret != C_KZG_OK) goto out;
/* Get \sum r^i (C_i - [y_i]) */
ret = g1_lincomb(&C_minus_y_lincomb, C_minus_y, r_powers, n);
if (ret != C_KZG_OK) goto out;
/* Get C_minus_y_lincomb + proof_z_lincomb */
blst_p1_add_or_double(&rhs_g1, &C_minus_y_lincomb, &proof_z_lincomb);
/* Do the pairing check! */
*ok = pairings_verify(
&proof_lincomb, &s->g2_values[1], &rhs_g1, &G2_GENERATOR
);
ret = C_KZG_OK;
out:
free(r_powers);
free(C_minus_y);
free(r_times_z);
return ret;
}
/**
* Given a list of blobs and blob KZG proofs, verify that they correspond to the
* provided commitments.
*
* @remark This function assumes that `n` is trusted and that all input arrays
* contain `n` elements. `n` should be the actual size of the arrays and not
* read off a length field in the protocol.
*
* @remark This function accepts if called with `n==0`.
*
* @param[out] ok `true` if the proofs are valid, `false` if not
* @param[in] blobs Array of blobs to verify
* @param[in] commitments_bytes Array of commitments to verify
* @param[in] proofs_bytes Array of proofs used for verification
* @param[in] n The number of blobs/commitments/proofs in the
* arrays
* @param[in] s The settings struct containing the commitment
* verification key (i.e. the trusted setup)
*/
C_KZG_RET verify_blob_kzg_proof_batch(
bool *ok,
const Blob *blobs,
const Bytes48 *commitments_bytes,
const Bytes48 *proofs_bytes,
size_t n,
const KZGSettings *s
) {
C_KZG_RET ret;
g1_t *commitments_g1 = NULL;
g1_t *proofs_g1 = NULL;
fr_t *evaluation_challenges_fr = NULL;
fr_t *ys_fr = NULL;
Polynomial *polynomials = NULL;
/* Exit early if we are given zero blobs */
if (n == 0) {
*ok = true;
return C_KZG_OK;
}
/* We will need a bunch of arrays to store our objects... */
ret = new_g1_array(&commitments_g1, n);
if (ret != C_KZG_OK) goto out;
ret = new_g1_array(&proofs_g1, n);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&evaluation_challenges_fr, n);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&ys_fr, n);
if (ret != C_KZG_OK) goto out;
ret = c_kzg_calloc((void **)&polynomials, n, sizeof(Polynomial));
if (ret != C_KZG_OK) goto out;
for (size_t i = 0; i < n; i++) {
ret = bytes_to_kzg_commitment(
&commitments_g1[i], &commitments_bytes[i]
);
if (ret != C_KZG_OK) goto out;
ret = blob_to_polynomial(&polys[i], &blobs[i]);
ret = blob_to_polynomial(&polynomials[i], &blobs[i]);
if (ret != C_KZG_OK) goto out;
compute_challenge(
&evaluation_challenges_fr[i], &blobs[i], &commitments_g1[i]
);
ret = evaluate_polynomial_in_evaluation_form(
&ys_fr[i], &polynomials[i], &evaluation_challenges_fr[i], s
);
if (ret != C_KZG_OK) goto out;
ret = bytes_to_kzg_proof(&proofs_g1[i], &proofs_bytes[i]);
if (ret != C_KZG_OK) goto out;
}
Polynomial aggregated_poly;
g1_t aggregated_poly_commitment;
fr_t evaluation_challenge;
ret = compute_aggregated_poly_and_commitment(
&aggregated_poly,
&aggregated_poly_commitment,
&evaluation_challenge,
polys,
commitments,
n
);
if (ret != C_KZG_OK) goto out;
fr_t y;
ret = evaluate_polynomial_in_evaluation_form(
&y, &aggregated_poly, &evaluation_challenge, s
);
if (ret != C_KZG_OK) goto out;
ret = verify_kzg_proof_impl(
out, &aggregated_poly_commitment, &evaluation_challenge, &y, &proof, s
ret = verify_kzg_proof_batch(
ok, commitments_g1, evaluation_challenges_fr, ys_fr, proofs_g1, n, s
);
out:
free(commitments);
free(polys);
free(commitments_g1);
free(proofs_g1);
free(evaluation_challenges_fr);
free(ys_fr);
free(polynomials);
return ret;
}

View File

@ -144,32 +144,10 @@ C_KZG_RET load_trusted_setup_file(KZGSettings *out, FILE *in);
void free_trusted_setup(KZGSettings *s);
C_KZG_RET compute_aggregate_kzg_proof(
KZGProof *out, const Blob *blobs, size_t n, const KZGSettings *s
);
C_KZG_RET verify_aggregate_kzg_proof(
bool *out,
const Blob *blobs,
const Bytes48 *commitments_bytes,
size_t n,
const Bytes48 *aggregated_proof_bytes,
const KZGSettings *s
);
C_KZG_RET blob_to_kzg_commitment(
KZGCommitment *out, const Blob *blob, const KZGSettings *s
);
C_KZG_RET verify_kzg_proof(
bool *out,
const Bytes48 *commitment_bytes,
const Bytes32 *z_bytes,
const Bytes32 *y_bytes,
const Bytes48 *proof_bytes,
const KZGSettings *s
);
C_KZG_RET compute_kzg_proof(
KZGProof *out,
const Blob *blob,
@ -177,6 +155,36 @@ C_KZG_RET compute_kzg_proof(
const KZGSettings *s
);
C_KZG_RET compute_blob_kzg_proof(
KZGProof *out, const Blob *blob, const KZGSettings *s
);
C_KZG_RET verify_kzg_proof(
bool *ok,
const Bytes48 *commitment_bytes,
const Bytes32 *z_bytes,
const Bytes32 *y_bytes,
const Bytes48 *proof_bytes,
const KZGSettings *s
);
C_KZG_RET verify_blob_kzg_proof(
bool *ok,
const Blob *blob,
const Bytes48 *commitment_bytes,
const Bytes48 *proof_bytes,
const KZGSettings *s
);
C_KZG_RET verify_blob_kzg_proof_batch(
bool *ok,
const Blob *blobs,
const Bytes48 *commitments_bytes,
const Bytes48 *proofs_bytes,
size_t n,
const KZGSettings *s
);
#ifdef __cplusplus
}
#endif

View File

@ -643,7 +643,7 @@ static void test_compute_and_verify_kzg_proof__succeeds_round_trip(void) {
/* Finally verify the proof */
ret = verify_kzg_proof(&ok, &c, &z, &y, &proof, &s);
ASSERT_EQUALS(ret, C_KZG_OK);
ASSERT_EQUALS(ok, 1);
ASSERT_EQUALS(ok, true);
}
static void test_compute_and_verify_kzg_proof__succeeds_within_domain(void) {
@ -684,10 +684,95 @@ static void test_compute_and_verify_kzg_proof__succeeds_within_domain(void) {
/* Finally verify the proof */
ret = verify_kzg_proof(&ok, &c, &z, &y, &proof, &s);
ASSERT_EQUALS(ret, C_KZG_OK);
ASSERT_EQUALS(ok, 1);
ASSERT_EQUALS(ok, true);
}
}
///////////////////////////////////////////////////////////////////////////////
// Tests for compute_blob_kzg_proof
///////////////////////////////////////////////////////////////////////////////
static void test_compute_and_verify_blob_kzg_proof__succeeds_round_trip(void) {
C_KZG_RET ret;
Bytes48 proof;
KZGCommitment c;
Blob blob;
bool ok;
/* Some preparation */
get_rand_blob(&blob);
ret = blob_to_kzg_commitment(&c, &blob, &s);
ASSERT_EQUALS(ret, C_KZG_OK);
/* Compute the proof */
ret = compute_blob_kzg_proof(&proof, &blob, &s);
ASSERT_EQUALS(ret, C_KZG_OK);
/* Finally verify the proof */
ret = verify_blob_kzg_proof(&ok, &blob, &c, &proof, &s);
ASSERT_EQUALS(ret, C_KZG_OK);
ASSERT_EQUALS(ok, true);
}
///////////////////////////////////////////////////////////////////////////////
// Tests for verify_kzg_proof_batch
///////////////////////////////////////////////////////////////////////////////
static void test_verify_kzg_proof_batch__succeeds_round_trip(void) {
C_KZG_RET ret;
const int n_samples = 16;
Bytes48 proofs[n_samples];
KZGCommitment commitments[n_samples];
Blob blobs[n_samples];
bool ok;
/* Some preparation */
for (int i = 0; i < n_samples; i++) {
get_rand_blob(&blobs[i]);
ret = blob_to_kzg_commitment(&commitments[i], &blobs[i], &s);
ASSERT_EQUALS(ret, C_KZG_OK);
ret = compute_blob_kzg_proof(&proofs[i], &blobs[i], &s);
ASSERT_EQUALS(ret, C_KZG_OK);
}
/* Verify batched proofs for 0,1,2..16 blobs */
/* This should still work with zero blobs */
for (int count = 0; count <= 16; count++) {
ret = verify_blob_kzg_proof_batch(
&ok, blobs, commitments, proofs, count, &s
);
ASSERT_EQUALS(ret, C_KZG_OK);
ASSERT_EQUALS(ok, true);
}
}
static void test_verify_kzg_proof_batch__fails_with_incorrect_proof(void) {
C_KZG_RET ret;
const int n_samples = 2;
Bytes48 proofs[n_samples];
KZGCommitment commitments[n_samples];
Blob blobs[n_samples];
bool ok;
/* Some preparation */
for (int i = 0; i < n_samples; i++) {
get_rand_blob(&blobs[i]);
ret = blob_to_kzg_commitment(&commitments[i], &blobs[i], &s);
ASSERT_EQUALS(ret, C_KZG_OK);
ret = compute_blob_kzg_proof(&proofs[i], &blobs[i], &s);
ASSERT_EQUALS(ret, C_KZG_OK);
}
/* Overwrite second proof with an incorrect one */
proofs[1] = proofs[0];
ret = verify_blob_kzg_proof_batch(
&ok, blobs, commitments, proofs, n_samples, &s
);
ASSERT_EQUALS(ret, C_KZG_OK);
ASSERT_EQUALS(ok, false);
}
///////////////////////////////////////////////////////////////////////////////
// Profiling Functions
///////////////////////////////////////////////////////////////////////////////
@ -833,6 +918,9 @@ int main(void) {
RUN(test_compute_kzg_proof__succeeds_expected_proof);
RUN(test_compute_and_verify_kzg_proof__succeeds_round_trip);
RUN(test_compute_and_verify_kzg_proof__succeeds_within_domain);
RUN(test_compute_and_verify_blob_kzg_proof__succeeds_round_trip);
RUN(test_verify_kzg_proof_batch__succeeds_round_trip);
RUN(test_verify_kzg_proof_batch__fails_with_incorrect_proof);
/*
* These functions are only executed if we're profiling. To me, it makes

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
ae7f556958b0cd039248478cf7294c097e48c9f10c9c47c97bd17b868029e6071ba940184d1b248f5e8bb06621b5d8b1

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
a1b505e8e7e1779eb2077464f5d793e4f36e5060970797c936370266095d048976557826ae0397d7d70256aff13cc6b0

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
0ad346f9e6923ab1d2f091785e9ca0ea15c7281ff4bd6f7e011584f186c38300

View File

@ -0,0 +1 @@
ae35b301551f80bd14f9cfc961a7cd7585d5e17539b809bf0a16f4890363d28f23382e37ab95329d358f5b64322af939

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
ae7f556958b0cd039248478cf7294c097e48c9f10c9c47c97bd17b868029e6071ba940184d1b248f5e8bb06621b5d8b1

View File

@ -0,0 +1 @@
true

View File

@ -0,0 +1 @@
a1b505e8e7e1779eb2077464f5d793e4f36e5060970797c936370266095d048976557826ae0397d7d70256aff13cc6b0

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
ae7f556958b0cd039248478cf7294c097e48c9f10c9c47c97bd17b868029e6071ba940184d1b248f5e8bb06621b5d8b1

View File

@ -0,0 +1 @@
8860f10f6ae4fbc32ed64d6de46d4b24041211a7098c200b47d8c54159043553702884793d4105f3dafde91cd48f7628

View File

@ -0,0 +1 @@
895c7aa7a1fa5bf9f0d1accc1db012f2733913d31d0f8eb264f82feab7c4bea05bde5ae77a1827bf5d5413a3867b6c17

View File

@ -0,0 +1 @@
true

View File

@ -0,0 +1 @@
a1b505e8e7e1779eb2077464f5d793e4f36e5060970797c936370266095d048976557826ae0397d7d70256aff13cc6b0

View File

@ -0,0 +1 @@
946ed3374ba1ae5f148c8971af9bee4b1774de8cb44ee2ef2349deb4269d2b380b7c1caa7d019ef4a3749584554e6511

View File

@ -0,0 +1 @@
9139792997abe8542892976fe8b205c4da35463d133cd9108c30cf0f03fbcff91eb70c798dc583cbc85b3db1f37f1b82

View File

@ -0,0 +1 @@
5cf0915a508d1d174d41618271bb8244b3abad27844cf8f121de6a99f527ae6c

View File

@ -0,0 +1 @@
a97456b8097baed6e90ce381d2b21c970a3f9ad4f6c92b1bb26337f919bd639dd43bd470839153db09115e2862051f33

View File

@ -0,0 +1 @@
0020000000000000000000000000000000000000000000000000000000000000

View File

@ -0,0 +1 @@
true

View File

@ -0,0 +1 @@
a02259f9ef800813c1c9b0e85536564eda2eb9dabc837f3b72b27348e83c570d3539adc475c5cf6cd80c9f4ac0989f1a