computeAggregateKzgProof + verifyAggregateKzgProof test passes
This commit is contained in:
parent
b8151db670
commit
1514d5b826
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue