computeAggregateKzgProof + verifyAggregateKzgProof test passes

This commit is contained in:
dancoffman 2022-11-03 16:20:33 -07:00
parent b8151db670
commit 1514d5b826
No known key found for this signature in database
GPG Key ID: 47B1F53E36A9B3CC
4 changed files with 72 additions and 55 deletions

View File

@ -163,7 +163,7 @@ Napi::Value ComputeAggregateKzgProof(const Napi::CallbackInfo& info) {
return napi_typed_array_from_bytes(proofBytes, BYTES_PER_PROOF, env); 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) { Napi::Value VerifyAggregateKzgProof(const Napi::CallbackInfo& info) {
auto env = info.Env(); auto env = info.Env();
@ -189,23 +189,29 @@ Napi::Value VerifyAggregateKzgProof(const Napi::CallbackInfo& info) {
C_KZG_RET ret; C_KZG_RET ret;
for (uint32_t blobIndex = 0; blobIndex < numberOfBlobs; blobIndex++) { for (uint32_t blobIndex = 0; blobIndex < numberOfBlobs; blobIndex++) {
// Extract blob bytes from parameter
Napi::Value blob = blobs_param[blobIndex]; Napi::Value blob = blobs_param[blobIndex];
auto blobBytes = blob.As<Napi::Uint8Array>().Data(); auto blobBytes = blob.As<Napi::Uint8Array>().Data();
Napi::Value commitment = comittments_param[blobIndex]; // Populate the polynomial with a BLS field for each field element in the blob
auto commitmentBytes = commitment.As<Napi::Uint8Array>().Data();
for (uint32_t fieldIndex = 0; fieldIndex < FIELD_ELEMENTS_PER_BLOB; fieldIndex++) { for (uint32_t fieldIndex = 0; fieldIndex < FIELD_ELEMENTS_PER_BLOB; fieldIndex++) {
bytes_to_bls_field(&polynomial[blobIndex][fieldIndex], &blobBytes[fieldIndex * BYTES_PER_FIELD]); 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<Napi::Uint8Array>().Data();
ret = bytes_to_g1(&commitments[blobIndex], commitmentBytes);
if (ret != C_KZG_OK) { if (ret != C_KZG_OK) {
std::ostringstream ss;
std::copy(commitmentBytes, commitmentBytes + BYTES_PER_COMMITMENT, std::ostream_iterator<int>(ss, ","));
Napi::TypeError::New(env, "Error parsing commitment. Error code was: " + std::to_string(ret) + ". Commitment bytes: " + ss.str()).ThrowAsJavaScriptException();
free(commitments); free(commitments);
free(polynomial); free(polynomial);
Napi::TypeError::New(env, "Error parsing blobs and commitments")
.ThrowAsJavaScriptException();
return env.Null(); return env.Null();
} }
} }
@ -260,15 +266,15 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) {
.ThrowAsJavaScriptException(); .ThrowAsJavaScriptException();
return env.Undefined(); return env.Undefined();
} }
auto c = c_param.As<Napi::Uint8Array>().Data(); auto polynomialKzg = c_param.As<Napi::Uint8Array>().Data();
auto x_param = info[0].As<Napi::TypedArray>(); auto z_param = info[0].As<Napi::TypedArray>();
if (x_param.TypedArrayType() != napi_uint8_array) { if (z_param.TypedArrayType() != napi_uint8_array) {
Napi::Error::New(env, "Expected an Uint8Array") Napi::Error::New(env, "Expected an Uint8Array")
.ThrowAsJavaScriptException(); .ThrowAsJavaScriptException();
return env.Undefined(); return env.Undefined();
} }
auto x = x_param.As<Napi::Uint8Array>().Data(); auto z = z_param.As<Napi::Uint8Array>().Data();
auto y_param = info[0].As<Napi::TypedArray>(); auto y_param = info[0].As<Napi::TypedArray>();
if (y_param.TypedArrayType() != napi_uint8_array) { if (y_param.TypedArrayType() != napi_uint8_array) {
@ -278,37 +284,36 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) {
} }
auto y = y_param.As<Napi::Uint8Array>().Data(); auto y = y_param.As<Napi::Uint8Array>().Data();
auto p_param = info[0].As<Napi::TypedArray>(); auto proof_param = info[0].As<Napi::TypedArray>();
if (p_param.TypedArrayType() != napi_uint8_array) { if (proof_param.TypedArrayType() != napi_uint8_array) {
Napi::Error::New(info.Env(), "Expected an Uint8Array") Napi::Error::New(info.Env(), "Expected an Uint8Array")
.ThrowAsJavaScriptException(); .ThrowAsJavaScriptException();
return info.Env().Undefined(); return info.Env().Undefined();
} }
auto p = p_param.As<Napi::Uint8Array>().Data(); auto kzgProof = proof_param.As<Napi::Uint8Array>().Data();
auto kzgSettings = info[4].As<Napi::External<KZGSettings>>().Data(); auto kzgSettings = info[4].As<Napi::External<KZGSettings>>().Data();
KZGCommitment commitment;
KZGProof proof;
BLSFieldElement fx, fy; BLSFieldElement fx, fy;
bool out; bytes_to_bls_field(&fx, z);
bytes_to_bls_field(&fx, x);
bytes_to_bls_field(&fy, y); 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) { if (ret != C_KZG_OK) {
std::ostringstream ss; std::ostringstream ss;
std::copy(c, c+sizeof(c), std::ostream_iterator<int>(ss, ",")); std::copy(polynomialKzg, polynomialKzg + BYTES_PER_COMMITMENT, std::ostream_iterator<int>(ss, ","));
Napi::TypeError::New(env, "Failed to parse argument commitment: " + ss.str() + " Return code was: " + std::to_string(ret)).ThrowAsJavaScriptException(); Napi::TypeError::New(env, "Failed to parse argument commitment: " + ss.str() + " Return code was: " + std::to_string(ret)).ThrowAsJavaScriptException();
return env.Null(); return env.Null();
}; };
if (bytes_to_g1(&proof, p) != C_KZG_OK) { KZGProof proof;
Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException(); if (bytes_to_g1(&proof, kzgProof) != C_KZG_OK) {
Napi::TypeError::New(env, "Invalid kzgProof").ThrowAsJavaScriptException();
return env.Null(); return env.Null();
} }
bool out;
if (verify_kzg_proof(&out, &commitment, &fx, &fy, &proof, kzgSettings) != C_KZG_OK) { if (verify_kzg_proof(&out, &commitment, &fx, &fy, &proof, kzgSettings) != C_KZG_OK) {
return Napi::Boolean::New(env, false); 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) { Napi::Object Init(Napi::Env env, Napi::Object exports) {
// Functions
exports["loadTrustedSetup"] = Napi::Function::New(env, LoadTrustedSetup); exports["loadTrustedSetup"] = Napi::Function::New(env, LoadTrustedSetup);
exports["freeTrustedSetup"] = Napi::Function::New(env, FreeTrustedSetup); exports["freeTrustedSetup"] = Napi::Function::New(env, FreeTrustedSetup);
exports["verifyKzgProof"] = Napi::Function::New(env, VerifyKzgProof); exports["verifyKzgProof"] = Napi::Function::New(env, VerifyKzgProof);
exports["blobToKzgCommitment"] = Napi::Function::New(env, BlobToKzgCommitment); exports["blobToKzgCommitment"] = Napi::Function::New(env, BlobToKzgCommitment);
exports["computeAggregateKzgProof"] = Napi::Function::New(env, ComputeAggregateKzgProof); exports["computeAggregateKzgProof"] = Napi::Function::New(env, ComputeAggregateKzgProof);
exports["verifyAggregateKzgProof"] = Napi::Function::New(env, VerifyAggregateKzgProof); 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; return exports;
} }

View File

@ -1,17 +1,23 @@
// @ts-expect-error // @ts-expect-error
import bindings from "bindings"; 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; type SetupHandle = Object;
export type KZGProof = Uint8Array;
export type KZGCommitment = Uint8Array;
export type Blob = Uint8Array;
// The C++ native addon interface
type KZG = { type KZG = {
FIELD_ELEMENTS_PER_BLOB: number;
BYTES_PER_FIELD: number;
loadTrustedSetup: (filePath: string) => SetupHandle; loadTrustedSetup: (filePath: string) => SetupHandle;
freeTrustedSetup: (setupHandle: SetupHandle) => void; freeTrustedSetup: (setupHandle: SetupHandle) => void;
@ -41,6 +47,9 @@ type KZG = {
const kzg: KZG = bindings("kzg.node"); 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 // Stored as internal state
let setupHandle: SetupHandle | undefined; let setupHandle: SetupHandle | undefined;
@ -83,7 +92,7 @@ export function verifyKzgProof(
z: BLSFieldElement, z: BLSFieldElement,
y: BLSFieldElement, y: BLSFieldElement,
kzgProof: KZGProof kzgProof: KZGProof
) { ): boolean {
if (!setupHandle) { if (!setupHandle) {
throw new Error("You must call loadTrustedSetup to initialize KZG."); throw new Error("You must call loadTrustedSetup to initialize KZG.");
} }

View File

@ -2,46 +2,43 @@ import { randomBytes } from "crypto";
import { import {
loadTrustedSetup, loadTrustedSetup,
freeTrustedSetup, freeTrustedSetup,
verifyKzgProof,
blobToKzgCommitment, blobToKzgCommitment,
Blob,
BLOB_SIZE,
NUMBER_OF_FIELDS,
computeAggregateKzgProof, computeAggregateKzgProof,
verifyAggregateKzgProof, verifyAggregateKzgProof,
BYTES_PER_FIELD,
FIELD_ELEMENTS_PER_BLOB,
verifyKzgProof,
} from "./kzg"; } from "./kzg";
const SETUP_FILE_PATH = "../../src/trusted_setup.txt"; const SETUP_FILE_PATH = "../../src/trusted_setup.txt";
const BLOB_BYTE_COUNT = FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD;
function generateRandomBlob(): Blob { const generateRandomBlob = () => new Uint8Array(randomBytes(BLOB_BYTE_COUNT));
return new Uint8Array(randomBytes(BLOB_SIZE * NUMBER_OF_FIELDS));
}
describe("C-KZG", () => { describe("C-KZG", () => {
beforeEach(() => { beforeAll(() => {
loadTrustedSetup(SETUP_FILE_PATH); loadTrustedSetup(SETUP_FILE_PATH);
}); });
afterEach(() => { afterAll(() => {
freeTrustedSetup(); freeTrustedSetup();
}); });
it("computes and verifies an aggregate KZG proof", async () => { it.skip("verifies a proof at a given commitment point", async () => {
const blob1 = generateRandomBlob(); const blob = generateRandomBlob();
const blob2 = generateRandomBlob(); const commitment = blobToKzgCommitment(blob);
const blobs = [blob1, blob2]; const proof = computeAggregateKzgProof([blob]);
const commitments = blobs.map(blobToKzgCommitment); const z = Uint8Array.from(new Array(32).fill(0));
const y = Uint8Array.from(new Array(32).fill(0));
const proof = computeAggregateKzgProof(blobs); expect(verifyKzgProof(commitment, z, y, proof)).toBe(true);
console.log({
commitments,
proof,
}); });
const result = verifyAggregateKzgProof(blobs, commitments, proof); it("computes the correct aggregate commitment from blobs", async () => {
const blobs = new Array(2).fill(0).map(generateRandomBlob);
expect(result).toBe(true); const commitments = blobs.map(blobToKzgCommitment);
const proof = computeAggregateKzgProof(blobs);
expect(verifyAggregateKzgProof(blobs, commitments, proof)).toBe(true);
}); });
}); });

View File

@ -1,7 +1,7 @@
INCLUDE_DIRS = ../inc INCLUDE_DIRS = ../inc
CFLAGS += -O2 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 clang -Wall -I$(INCLUDE_DIRS) $(CFLAGS) -c c_kzg_4844.c sha256.c
lib: c_kzg_4844.o Makefile lib: c_kzg_4844.o Makefile