From 1514d5b8263fe77645163f3d3b513ad6d11e001b Mon Sep 17 00:00:00 2001 From: dancoffman Date: Thu, 3 Nov 2022 16:20:33 -0700 Subject: [PATCH] computeAggregateKzgProof + verifyAggregateKzgProof test passes --- bindings/node.js/kzg.cxx | 57 ++++++++++++++++++++++++---------------- bindings/node.js/kzg.ts | 25 ++++++++++++------ bindings/node.js/test.ts | 43 ++++++++++++++---------------- src/Makefile | 2 +- 4 files changed, 72 insertions(+), 55 deletions(-) diff --git a/bindings/node.js/kzg.cxx b/bindings/node.js/kzg.cxx index 953a325..e00b881 100644 --- a/bindings/node.js/kzg.cxx +++ b/bindings/node.js/kzg.cxx @@ -163,7 +163,7 @@ Napi::Value ComputeAggregateKzgProof(const Napi::CallbackInfo& info) { return napi_typed_array_from_bytes(proofBytes, BYTES_PER_PROOF, env); } -// verifyAggregateKzgProof: (blobs: Blob[], expectedKzgCommitments: KZGCommitment[], kzgAggregatedProof: KZGProof) => boolean; +// verifyAggregateKzgProof: (blobs: Blob[], expectedKzgCommitments: KZGCommitment[], kzgAggregatedProof: KZGProof, setupHandle: SetupHandle) => boolean; Napi::Value VerifyAggregateKzgProof(const Napi::CallbackInfo& info) { auto env = info.Env(); @@ -189,23 +189,29 @@ Napi::Value VerifyAggregateKzgProof(const Napi::CallbackInfo& info) { C_KZG_RET ret; for (uint32_t blobIndex = 0; blobIndex < numberOfBlobs; blobIndex++) { + // Extract blob bytes from parameter Napi::Value blob = blobs_param[blobIndex]; auto blobBytes = blob.As().Data(); - Napi::Value commitment = comittments_param[blobIndex]; - auto commitmentBytes = commitment.As().Data(); - + // Populate the polynomial with a BLS field for each field element in the blob for (uint32_t fieldIndex = 0; fieldIndex < FIELD_ELEMENTS_PER_BLOB; fieldIndex++) { bytes_to_bls_field(&polynomial[blobIndex][fieldIndex], &blobBytes[fieldIndex * BYTES_PER_FIELD]); } - ret = bytes_to_g1(&commitments[blobIndex], &commitmentBytes[blobIndex * BYTES_PER_COMMITMENT]); + // Extract a G1 point for each commitment + Napi::Value commitment = comittments_param[blobIndex]; + auto commitmentBytes = commitment.As().Data(); + + ret = bytes_to_g1(&commitments[blobIndex], commitmentBytes); if (ret != C_KZG_OK) { + std::ostringstream ss; + std::copy(commitmentBytes, commitmentBytes + BYTES_PER_COMMITMENT, std::ostream_iterator(ss, ",")); + + Napi::TypeError::New(env, "Error parsing commitment. Error code was: " + std::to_string(ret) + ". Commitment bytes: " + ss.str()).ThrowAsJavaScriptException(); + free(commitments); free(polynomial); - Napi::TypeError::New(env, "Error parsing blobs and commitments") - .ThrowAsJavaScriptException(); return env.Null(); } } @@ -260,15 +266,15 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) { .ThrowAsJavaScriptException(); return env.Undefined(); } - auto c = c_param.As().Data(); + auto polynomialKzg = c_param.As().Data(); - auto x_param = info[0].As(); - if (x_param.TypedArrayType() != napi_uint8_array) { + auto z_param = info[0].As(); + if (z_param.TypedArrayType() != napi_uint8_array) { Napi::Error::New(env, "Expected an Uint8Array") .ThrowAsJavaScriptException(); return env.Undefined(); } - auto x = x_param.As().Data(); + auto z = z_param.As().Data(); auto y_param = info[0].As(); if (y_param.TypedArrayType() != napi_uint8_array) { @@ -278,37 +284,36 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) { } auto y = y_param.As().Data(); - auto p_param = info[0].As(); - if (p_param.TypedArrayType() != napi_uint8_array) { + auto proof_param = info[0].As(); + if (proof_param.TypedArrayType() != napi_uint8_array) { Napi::Error::New(info.Env(), "Expected an Uint8Array") .ThrowAsJavaScriptException(); return info.Env().Undefined(); } - auto p = p_param.As().Data(); + auto kzgProof = proof_param.As().Data(); auto kzgSettings = info[4].As>().Data(); - KZGCommitment commitment; - KZGProof proof; BLSFieldElement fx, fy; - bool out; - - bytes_to_bls_field(&fx, x); + bytes_to_bls_field(&fx, z); bytes_to_bls_field(&fy, y); - auto ret = bytes_to_g1(&commitment, c); + KZGCommitment commitment; + auto ret = bytes_to_g1(&commitment, polynomialKzg); if (ret != C_KZG_OK) { std::ostringstream ss; - std::copy(c, c+sizeof(c), std::ostream_iterator(ss, ",")); + std::copy(polynomialKzg, polynomialKzg + BYTES_PER_COMMITMENT, std::ostream_iterator(ss, ",")); Napi::TypeError::New(env, "Failed to parse argument commitment: " + ss.str() + " Return code was: " + std::to_string(ret)).ThrowAsJavaScriptException(); return env.Null(); }; - if (bytes_to_g1(&proof, p) != C_KZG_OK) { - Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException(); + KZGProof proof; + if (bytes_to_g1(&proof, kzgProof) != C_KZG_OK) { + Napi::TypeError::New(env, "Invalid kzgProof").ThrowAsJavaScriptException(); return env.Null(); } + bool out; if (verify_kzg_proof(&out, &commitment, &fx, &fy, &proof, kzgSettings) != C_KZG_OK) { return Napi::Boolean::New(env, false); } @@ -317,12 +322,18 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) { } Napi::Object Init(Napi::Env env, Napi::Object exports) { + // Functions exports["loadTrustedSetup"] = Napi::Function::New(env, LoadTrustedSetup); exports["freeTrustedSetup"] = Napi::Function::New(env, FreeTrustedSetup); exports["verifyKzgProof"] = Napi::Function::New(env, VerifyKzgProof); exports["blobToKzgCommitment"] = Napi::Function::New(env, BlobToKzgCommitment); exports["computeAggregateKzgProof"] = Napi::Function::New(env, ComputeAggregateKzgProof); exports["verifyAggregateKzgProof"] = Napi::Function::New(env, VerifyAggregateKzgProof); + + // Constants + exports["FIELD_ELEMENTS_PER_BLOB"] = Napi::Number::New(env, FIELD_ELEMENTS_PER_BLOB); + exports["BYTES_PER_FIELD"] = Napi::Number::New(env, BYTES_PER_FIELD); + return exports; } diff --git a/bindings/node.js/kzg.ts b/bindings/node.js/kzg.ts index 0162fac..b379194 100644 --- a/bindings/node.js/kzg.ts +++ b/bindings/node.js/kzg.ts @@ -1,17 +1,23 @@ // @ts-expect-error import bindings from "bindings"; -export const BLOB_SIZE = 4096; -export const NUMBER_OF_FIELDS = 32; +/** + * The public interface of this module exposes the functions as specified by + * https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#kzg + */ -export type SetupHandle = Object; +export type BLSFieldElement = Uint8Array; // 32 bytes +export type KZGProof = Uint8Array; // 48 bytes +export type KZGCommitment = Uint8Array; // 48 bytes +export type Blob = Uint8Array; // 4096 * 32 bytes -export type BLSFieldElement = Uint8Array; -export type KZGProof = Uint8Array; -export type KZGCommitment = Uint8Array; -export type Blob = Uint8Array; +type SetupHandle = Object; +// The C++ native addon interface type KZG = { + FIELD_ELEMENTS_PER_BLOB: number; + BYTES_PER_FIELD: number; + loadTrustedSetup: (filePath: string) => SetupHandle; freeTrustedSetup: (setupHandle: SetupHandle) => void; @@ -41,6 +47,9 @@ type KZG = { const kzg: KZG = bindings("kzg.node"); +export const FIELD_ELEMENTS_PER_BLOB = kzg.FIELD_ELEMENTS_PER_BLOB; +export const BYTES_PER_FIELD = kzg.BYTES_PER_FIELD; + // Stored as internal state let setupHandle: SetupHandle | undefined; @@ -83,7 +92,7 @@ export function verifyKzgProof( z: BLSFieldElement, y: BLSFieldElement, kzgProof: KZGProof -) { +): boolean { if (!setupHandle) { throw new Error("You must call loadTrustedSetup to initialize KZG."); } diff --git a/bindings/node.js/test.ts b/bindings/node.js/test.ts index d7cc143..1a93c2e 100644 --- a/bindings/node.js/test.ts +++ b/bindings/node.js/test.ts @@ -2,46 +2,43 @@ import { randomBytes } from "crypto"; import { loadTrustedSetup, freeTrustedSetup, - verifyKzgProof, blobToKzgCommitment, - Blob, - BLOB_SIZE, - NUMBER_OF_FIELDS, computeAggregateKzgProof, verifyAggregateKzgProof, + BYTES_PER_FIELD, + FIELD_ELEMENTS_PER_BLOB, + verifyKzgProof, } from "./kzg"; const SETUP_FILE_PATH = "../../src/trusted_setup.txt"; +const BLOB_BYTE_COUNT = FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD; -function generateRandomBlob(): Blob { - return new Uint8Array(randomBytes(BLOB_SIZE * NUMBER_OF_FIELDS)); -} +const generateRandomBlob = () => new Uint8Array(randomBytes(BLOB_BYTE_COUNT)); describe("C-KZG", () => { - beforeEach(() => { + beforeAll(() => { loadTrustedSetup(SETUP_FILE_PATH); }); - afterEach(() => { + afterAll(() => { freeTrustedSetup(); }); - it("computes and verifies an aggregate KZG proof", async () => { - const blob1 = generateRandomBlob(); - const blob2 = generateRandomBlob(); - const blobs = [blob1, blob2]; + it.skip("verifies a proof at a given commitment point", async () => { + const blob = generateRandomBlob(); + const commitment = blobToKzgCommitment(blob); + const proof = computeAggregateKzgProof([blob]); + const z = Uint8Array.from(new Array(32).fill(0)); + const y = Uint8Array.from(new Array(32).fill(0)); + + expect(verifyKzgProof(commitment, z, y, proof)).toBe(true); + }); + + it("computes the correct aggregate commitment from blobs", async () => { + const blobs = new Array(2).fill(0).map(generateRandomBlob); const commitments = blobs.map(blobToKzgCommitment); - const proof = computeAggregateKzgProof(blobs); - - console.log({ - commitments, - proof, - }); - - const result = verifyAggregateKzgProof(blobs, commitments, proof); - - expect(result).toBe(true); + expect(verifyAggregateKzgProof(blobs, commitments, proof)).toBe(true); }); }); diff --git a/src/Makefile b/src/Makefile index 54b6127..7c3260b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,7 @@ INCLUDE_DIRS = ../inc CFLAGS += -O2 -c_kzg_4844.o: c_kzg_4844.c Makefile +c_kzg_4844.o: c_kzg_4844.c sha256.c Makefile clang -Wall -I$(INCLUDE_DIRS) $(CFLAGS) -c c_kzg_4844.c sha256.c lib: c_kzg_4844.o Makefile