From 46d529cf0042c967271eb5598eca2dc9e44f18a5 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Mon, 20 Feb 2023 17:52:12 +0200 Subject: [PATCH] Interface changes to free the blobs (#123) * Update C files to new interface * Switch CHALLENGE_INPUT_SIZE from a macro to a `const int`. * Update README with the new public methods --------- Co-authored-by: Justin Traglia --- README.md | 5 +- src/c_kzg_4844.c | 545 +++++++++++++++++++++++++----------------- src/c_kzg_4844.h | 52 ++-- src/test_c_kzg_4844.c | 92 ++++++- 4 files changed, 443 insertions(+), 251 deletions(-) diff --git a/README.md b/README.md index e80ca87..c4af9d9 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/src/c_kzg_4844.c b/src/c_kzg_4844.c index 4827586..0aa2d0d 100644 --- a/src/c_kzg_4844.c +++ b/src/c_kzg_4844.c @@ -21,6 +21,7 @@ */ #include "c_kzg_4844.h" +#include #include #include #include @@ -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; } diff --git a/src/c_kzg_4844.h b/src/c_kzg_4844.h index b0e37ca..8b94896 100644 --- a/src/c_kzg_4844.h +++ b/src/c_kzg_4844.h @@ -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 diff --git a/src/test_c_kzg_4844.c b/src/test_c_kzg_4844.c index a7c3472..572f5b2 100644 --- a/src/test_c_kzg_4844.c +++ b/src/test_c_kzg_4844.c @@ -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