Closer to working

This commit is contained in:
dancoffman 2022-11-03 14:39:02 -07:00
parent a5ca06450f
commit 672346f017
No known key found for this signature in database
GPG Key ID: 47B1F53E36A9B3CC
4 changed files with 212 additions and 189 deletions

View File

@ -37,34 +37,7 @@ Napi::TypedArrayOf<uint8_t> napiTypedArrayFromByteArray(uint8_t* array, size_t a
return Napi::Uint8Array::New(env, arrayLength, arrayBuffer, 0); return Napi::Uint8Array::New(env, arrayLength, arrayBuffer, 0);
} }
int verifyAggregateKzgProof(const uint8_t blobs[], const uint8_t commitments[], size_t n, const uint8_t proof[48], const KZGSettings *s) { // loadTrustedSetup: (filePath: string) => SetupHandle;
Polynomial* p = (Polynomial*)calloc(n, sizeof(Polynomial));
if (p == NULL) return -1;
KZGCommitment* c = (KZGCommitment*)calloc(n, sizeof(KZGCommitment));
if (c == NULL) { free(p); return -1; }
C_KZG_RET ret;
for (size_t i = 0; i < n; i++) {
for (size_t j = 0; j < FIELD_ELEMENTS_PER_BLOB; j++)
bytes_to_bls_field(&p[i][j], &blobs[i * FIELD_ELEMENTS_PER_BLOB * 32 + j * 32]);
ret = bytes_to_g1(&c[i], &commitments[i * 48]);
if (ret != C_KZG_OK) { free(c); free(p); return -1; }
}
KZGProof f;
ret = bytes_to_g1(&f, proof);
if (ret != C_KZG_OK) { free(c); free(p); return -1; }
bool b;
ret = verify_aggregate_kzg_proof(&b, p, c, n, &f, s);
if (ret != C_KZG_OK) { free(c); free(p); return -1; }
free(c); free(p);
return b ? 0 : 1;
}
Napi::Value LoadTrustedSetup(const Napi::CallbackInfo& info) { Napi::Value LoadTrustedSetup(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env(); Napi::Env env = info.Env();
@ -106,17 +79,17 @@ Napi::Value LoadTrustedSetup(const Napi::CallbackInfo& info) {
return Napi::External<KZGSettings>::New(info.Env(), kzgSettings); return Napi::External<KZGSettings>::New(info.Env(), kzgSettings);
} }
// Maybe this can be done with a finalizer on the thing returned by LoadTrustedSetup, and then the JS garbage collector can just sort it out. // freeTrustedSetup: (setupHandle: SetupHandle) => void;
void FreeTrustedSetup(const Napi::CallbackInfo& info) { void FreeTrustedSetup(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env(); // Maybe this can be done with a finalizer on the thing returned by LoadTrustedSetup, and then the JS garbage collector can just sort it out.
KZGSettings* kzgSettings = info[0].As<Napi::External<KZGSettings>>().Data(); auto kzgSettings = info[0].As<Napi::External<KZGSettings>>().Data();
free_trusted_setup(kzgSettings); free_trusted_setup(kzgSettings);
free(kzgSettings); free(kzgSettings);
} }
// blobToKzgCommitment: (blob: Blob) => KZGCommitment; // blobToKzgCommitment: (blob: Blob, setupHandle: SetupHandle) => KZGCommitment;
Napi::Value BlobToKzgCommitment(const Napi::CallbackInfo& info) { Napi::Value BlobToKzgCommitment(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env(); auto env = info.Env();
if (info.Length() != 2) { if (info.Length() != 2) {
Napi::TypeError::New(env, "Wrong number of arguments") Napi::TypeError::New(env, "Wrong number of arguments")
@ -130,9 +103,9 @@ Napi::Value BlobToKzgCommitment(const Napi::CallbackInfo& info) {
.ThrowAsJavaScriptException(); .ThrowAsJavaScriptException();
return env.Undefined(); return env.Undefined();
} }
uint8_t* blob = typedArray.As<Napi::Uint8Array>().Data(); auto blob = typedArray.As<Napi::Uint8Array>().Data();
KZGSettings* kzgSettings = info[1].As<Napi::External<KZGSettings>>().Data(); auto kzgSettings = info[1].As<Napi::External<KZGSettings>>().Data();
Polynomial polynomial; Polynomial polynomial;
for (size_t i = 0; i < FIELD_ELEMENTS_PER_BLOB; i++) for (size_t i = 0; i < FIELD_ELEMENTS_PER_BLOB; i++)
@ -142,15 +115,12 @@ Napi::Value BlobToKzgCommitment(const Napi::CallbackInfo& info) {
blob_to_kzg_commitment(&commitment, polynomial, kzgSettings); blob_to_kzg_commitment(&commitment, polynomial, kzgSettings);
// Turn it into a byte array // Turn it into a byte array
uint8_t array[48]; uint8_t commitmentBytes[BYTES_PER_COMMITMENT];
bytes_from_g1(array, &commitment); bytes_from_g1(commitmentBytes, &commitment);
return napiTypedArrayFromByteArray(array, sizeof(array), env); return napiTypedArrayFromByteArray(commitmentBytes, BYTES_PER_COMMITMENT, env);
}
Napi::Value VerifyAggregateKzgProof(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
} }
// computeAggregateKzgProof: (blobs: Blob[], setupHandle: SetupHandle) => KZGProof;
Napi::Value ComputeAggregateKzgProof(const Napi::CallbackInfo& info) { Napi::Value ComputeAggregateKzgProof(const Napi::CallbackInfo& info) {
auto env = info.Env(); auto env = info.Env();
@ -164,34 +134,16 @@ Napi::Value ComputeAggregateKzgProof(const Napi::CallbackInfo& info) {
auto kzgSettings = info[1].As<Napi::External<KZGSettings>>().Data(); auto kzgSettings = info[1].As<Napi::External<KZGSettings>>().Data();
auto numberOfBlobs = blobs_param.Length(); auto numberOfBlobs = blobs_param.Length();
// auto blobs = blobs_param.As<Napi::Array<Napi::Uint8Array>>();
int BYTES_PER_FIELD = 32; printf("ComputeAggregateKzgProof called with %i blob(s)\n", numberOfBlobs);
int BYTES_PER_BLOB = FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD;
printf("ComputeAggregateKzgProof called with %i blobs", numberOfBlobs);
// uint8_t blobBytes[numberOfBlobs * FIELD_ELEMENTS_PER_BLOB];
// for(int i = 0; i < numberOfBlobs; i++)
// {
// Napi::Value blob = blobs_param[i];
// if (blob.IsTypedArray())
// {
// auto blobBytes = blob.As<Napi::Uint8Array>().Data();
// memcpy(blobBytes + i * BYTES_PER_BLOB, blobBytes, BYTES_PER_BLOB);
// }
// }
// printf("ComputeAggregateKzgProof copied %i bytes", sizeof(blobBytes));
auto polynomial = (Polynomial*)calloc(numberOfBlobs, sizeof(Polynomial)); auto polynomial = (Polynomial*)calloc(numberOfBlobs, sizeof(Polynomial));
// if (p == NULL) return C_KZG_ERROR;
for (uint32_t blobIndex = 0; blobIndex < numberOfBlobs; blobIndex++) { for (uint32_t blobIndex = 0; blobIndex < numberOfBlobs; blobIndex++) {
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();
printf("Iterating blob index: %i", blobIndex); printf("Iterating blob index: %i\n", blobIndex);
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( bytes_to_bls_field(
@ -201,13 +153,6 @@ Napi::Value ComputeAggregateKzgProof(const Napi::CallbackInfo& info) {
} }
} }
printf("ComputeAggregateKzgProof 1");
printf("ComputeAggregateKzgProof 1");
printf("ComputeAggregateKzgProof 1");
printf("ComputeAggregateKzgProof 1");
// return env.Null();
KZGProof proof; KZGProof proof;
C_KZG_RET ret = compute_aggregate_kzg_proof( C_KZG_RET ret = compute_aggregate_kzg_proof(
&proof, &proof,
@ -215,33 +160,112 @@ Napi::Value ComputeAggregateKzgProof(const Napi::CallbackInfo& info) {
numberOfBlobs, numberOfBlobs,
kzgSettings kzgSettings
); );
Napi::TypeError::New(env, "STOPPING HERE")
.ThrowAsJavaScriptException();
return env.Null();
printf("ComputeAggregateKzgProof 2");
free(polynomial); free(polynomial);
printf("ComputeAggregateKzgProof 3");
if (ret != C_KZG_OK) { if (ret != C_KZG_OK) {
Napi::TypeError::New(env, "Failed to compute proof") Napi::TypeError::New(env, "Failed to compute proof")
.ThrowAsJavaScriptException(); .ThrowAsJavaScriptException();
return env.Null(); return env.Undefined();
}; };
printf("ComputeAggregateKzgProof 4"); printf("proof generated: %llu y: %llu z: %llu\n", proof.x, proof.y, proof.z);
printf("compute_aggregate_kzg_proof ret was %i\n", ret);
uint8_t* bytes; uint8_t array[48];
bytes_from_g1(bytes, &proof); bytes_from_g1(array, &proof);
printf("ComputeAggregateKzgProof 5"); printf("Turned proof into bytes: [");
for (int i = 0; i < sizeof(array); i++) {
printf("%x ", array[i]);
}
printf("]\n");
return napiTypedArrayFromByteArray(bytes, sizeof(bytes), env); return napiTypedArrayFromByteArray(array, sizeof(array), env);
} }
// verifyAggregateKzgProof: (blobs: Blob[], expectedKzgCommitments: KZGCommitment[], kzgAggregatedProof: KZGProof) => boolean;
Napi::Value VerifyAggregateKzgProof(const Napi::CallbackInfo& info) {
auto env = info.Env();
if (info.Length() != 4) {
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
return env.Null();
}
auto blobs_param = info[0].As<Napi::Array>();
auto comittments_param = info[1].As<Napi::Array>();
auto proof_param = info[2].As<Napi::TypedArray>();
auto proofBytes = proof_param.As<Napi::Uint8Array>().Data();
auto kzgSettings = info[3].As<Napi::External<KZGSettings>>().Data();
auto numberOfBlobs = blobs_param.Length();
auto polynomial = (Polynomial*)calloc(numberOfBlobs, sizeof(Polynomial));
auto commitments = (KZGCommitment*)calloc(numberOfBlobs, sizeof(KZGCommitment));
C_KZG_RET ret;
for (uint32_t blobIndex = 0; blobIndex < numberOfBlobs; blobIndex++) {
Napi::Value blob = blobs_param[blobIndex];
auto blobBytes = blob.As<Napi::Uint8Array>().Data();
Napi::Value commitment = comittments_param[blobIndex];
auto commitmentBytes = commitment.As<Napi::Uint8Array>().Data();
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]);
if (ret != C_KZG_OK) {
free(commitments);
free(polynomial);
Napi::TypeError::New(env, "Error parsing blobs and commitments")
.ThrowAsJavaScriptException();
return env.Null();
}
}
KZGProof proof;
ret = bytes_to_g1(&proof, proofBytes);
if (ret != C_KZG_OK) {
free(commitments);
free(polynomial);
Napi::TypeError::New(env, "Error converting proof parameter to KZGProof")
.ThrowAsJavaScriptException();
return env.Null();
}
bool verificationResult;
ret = verify_aggregate_kzg_proof(
&verificationResult,
polynomial,
commitments,
numberOfBlobs,
&proof,
kzgSettings
);
if (ret != C_KZG_OK) {
free(commitments);
free(polynomial);
Napi::TypeError::New(env, "Error calling verify_aggregate_kzg_proof")
.ThrowAsJavaScriptException();
return env.Null();
}
free(commitments);
free(polynomial);
return Napi::Boolean::New(env, verificationResult);
}
// verifyKzgProof: (polynomialKzg: KZGCommitment, z: BLSFieldElement, y: BLSFieldElement, kzgProof: KZGProof, setupHandle: SetupHandle) => boolean;
Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) { Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) {
auto env = info.Env(); auto env = info.Env();
@ -304,19 +328,18 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) {
std::copy(c, c+sizeof(c), std::ostream_iterator<int>(ss, ",")); std::copy(c, c+sizeof(c), 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();
// return -1;
}; };
if (bytes_to_g1(&proof, p) != C_KZG_OK) { if (bytes_to_g1(&proof, p) != C_KZG_OK) {
Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException(); Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
return env.Null(); return env.Null();
// return -1;
} }
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 env.Null(); return Napi::Boolean::New(env, false);
} }
return env.Null(); return Napi::Boolean::New(env, true);
} }
Napi::Object Init(Napi::Env env, Napi::Object exports) { Napi::Object Init(Napi::Env env, Napi::Object exports) {
@ -332,19 +355,3 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
} }
NODE_API_MODULE(addon, Init) NODE_API_MODULE(addon, Init)
// SCRATCH
// Napi::Object obj = Napi::Object::New(env);
// // Assign values to properties
// obj.Set("hello", "world");
// // obj.Set(uint32_t(42), "The Answer to Life, the Universe, and Everything");
// // obj.Set("Douglas Adams", true);
// // obj.Set("fftSettings", kzgSettings->fs);
// // Napi::Array::Array(napi_env env, napi_value value);
// const Napi::Array g1Values = Napi::Array::New(env, )
// obj.Set('g1Values', kzgSettings->g1_values);
// obj.Set('g2Values', kzgSettings->g2_values);

View File

@ -4,56 +4,104 @@ import bindings from 'bindings';
export const BLOB_SIZE = 4096; export const BLOB_SIZE = 4096;
export const NUMBER_OF_FIELDS = 32; export const NUMBER_OF_FIELDS = 32;
// Consider making this internal state of the native code
// so we don't have to pass it around in the application layer
export type SetupHandle = Object; export type SetupHandle = Object;
export enum ReturnValue { export type BLSFieldElement = Uint8Array;
/** Success! */
OK = 0,
/** The supplied data is invalid in some way */
BADARGS,
/** Internal error - this should never occur and may indicate a bug in the library */
ERROR,
/** Could not allocate memory */
MALLOC,
}
export type Point = Uint8Array;
export type KZGProof = Uint8Array; export type KZGProof = Uint8Array;
export type KZGCommitment = Uint8Array; export type KZGCommitment = Uint8Array;
export type Blob = Uint8Array; export type Blob = Uint8Array;
export type Blobs = Blob[];
type KZG = { type KZG = {
loadTrustedSetup: (path: string) => SetupHandle; loadTrustedSetup: (filePath: string) => SetupHandle;
freeTrustedSetup: (setupHandle: SetupHandle) => void; freeTrustedSetup: (setupHandle: SetupHandle) => void;
blobToKzgCommitment: (blob: Blob, setupHandle: SetupHandle) => KZGCommitment; blobToKzgCommitment: (blob: Blob, setupHandle: SetupHandle) => KZGCommitment;
verifyAggregateKzgProof: (blobs: Blobs) => ReturnValue;
computeAggregateKzgProof: ( computeAggregateKzgProof: (
blobs: Blobs, blobs: Blob[],
setupHandle: SetupHandle, setupHandle: SetupHandle,
) => KZGProof; ) => KZGProof;
verifyKzgProof: ( verifyAggregateKzgProof: (
commitment: KZGCommitment, blobs: Blob[],
x: Point, expectedKzgCommitments: KZGCommitment[],
y: Point, kzgAggregatedProof: KZGProof,
proof: KZGProof,
setupHandle: SetupHandle, setupHandle: SetupHandle,
) => ReturnValue; ) => boolean;
verifyKzgProof: (
polynomialKzg: KZGCommitment,
z: BLSFieldElement,
y: BLSFieldElement,
kzgProof: KZGProof,
setupHandle: SetupHandle,
) => boolean;
}; };
const kzg: KZG = bindings('kzg.node'); const kzg: KZG = bindings('kzg.node');
export const loadTrustedSetup = kzg.loadTrustedSetup; // Stored as internal state
export const freeTrustedSetup = kzg.freeTrustedSetup; let setupHandle: SetupHandle | undefined;
export const blobToKzgCommitment = kzg.blobToKzgCommitment;
export const verifyAggregateKzgProof = kzg.verifyAggregateKzgProof; export function loadTrustedSetup(filePath: string) {
export const computeAggregateKzgProof = kzg.computeAggregateKzgProof; if (setupHandle) {
export const verifyKzgProof = kzg.verifyKzgProof; throw new Error(
export default kzg; 'Call freeTrustedSetup before loading a new trusted setup.',
);
}
setupHandle = kzg.loadTrustedSetup(filePath);
}
export function freeTrustedSetup() {
if (!setupHandle) {
throw new Error('You must call loadTrustedSetup before freeTrustedSetup.');
}
kzg.freeTrustedSetup(setupHandle);
setupHandle = undefined;
}
export function blobToKzgCommitment(blob: Blob) {
if (!setupHandle) {
throw new Error('You must call loadTrustedSetup to initialize KZG.');
}
return kzg.blobToKzgCommitment(blob, setupHandle);
}
export function computeAggregateKzgProof(blobs: Blob[]) {
if (!setupHandle) {
throw new Error('You must call loadTrustedSetup to initialize KZG.');
}
return kzg.computeAggregateKzgProof(blobs, setupHandle);
}
/**
* Verify KZG proof that ``p(z) == y`` where ``p(z)`` is the polynomial represented by ``polynomialKzg``.
*/
export function verifyKzgProof(
polynomialKzg: KZGCommitment,
z: BLSFieldElement,
y: BLSFieldElement,
kzgProof: KZGProof,
) {
if (!setupHandle) {
throw new Error('You must call loadTrustedSetup to initialize KZG.');
}
return kzg.verifyKzgProof(polynomialKzg, z, y, kzgProof, setupHandle);
}
export function verifyAggregateKzgProof(
blobs: Blob[],
expectedKzgCommitments: KZGCommitment[],
kzgAggregatedProof: KZGProof,
) {
if (!setupHandle) {
throw new Error('You must call loadTrustedSetup to initialize KZG.');
}
return kzg.verifyAggregateKzgProof(
blobs,
expectedKzgCommitments,
kzgAggregatedProof,
setupHandle,
);
}

View File

@ -4,77 +4,44 @@ import {
freeTrustedSetup, freeTrustedSetup,
verifyKzgProof, verifyKzgProof,
blobToKzgCommitment, blobToKzgCommitment,
ReturnValue,
SetupHandle,
Blob, Blob,
BLOB_SIZE, BLOB_SIZE,
NUMBER_OF_FIELDS, NUMBER_OF_FIELDS,
computeAggregateKzgProof, computeAggregateKzgProof,
verifyAggregateKzgProof,
} from './kzg'; } from './kzg';
const SETUP_FILE_PATH = '../../src/trusted_setup.txt'; const SETUP_FILE_PATH = '../../src/trusted_setup.txt';
const COMMITMENT_BYTE_LENGTH = 48;
function generateRandomBlob(): Blob { function generateRandomBlob(): Blob {
return new Uint8Array(randomBytes(NUMBER_OF_FIELDS * BLOB_SIZE)); return new Uint8Array(randomBytes(BLOB_SIZE * NUMBER_OF_FIELDS));
} }
describe('C-KZG', () => { describe('C-KZG', () => {
let sharedSetupHandle: SetupHandle; beforeEach(() => {
loadTrustedSetup(SETUP_FILE_PATH);
beforeAll(() => {
sharedSetupHandle = loadTrustedSetup(SETUP_FILE_PATH);
}); });
describe('setup', () => { afterEach(() => {
it('can both load and free', () => { freeTrustedSetup();
expect(
freeTrustedSetup(loadTrustedSetup(SETUP_FILE_PATH)),
).toBeUndefined();
});
}); });
describe('computing a KZG commitment from a blob', () => { it('computes and verifies an aggregate KZG proof', async () => {
it('returns data with the correct length', () => { const blob1 = generateRandomBlob();
const blob = generateRandomBlob(); const blob2 = generateRandomBlob();
const commitment = blobToKzgCommitment(blob, sharedSetupHandle); const blobs = [blob1, blob2];
expect(commitment.length).toBe(COMMITMENT_BYTE_LENGTH);
}); const commitments = blobs.map(blobToKzgCommitment);
const proof = computeAggregateKzgProof(blobs);
console.log({
commitments,
proof,
}); });
describe('verifying a KZG proof', () => { const result = verifyAggregateKzgProof(blobs, commitments, proof);
it.only('returns the expected value', () => {
const byteEncoder = new TextEncoder();
const blob = generateRandomBlob(); expect(result).toBe(true);
const commitment = blobToKzgCommitment(blob, sharedSetupHandle);
const proof = computeAggregateKzgProof([blob], sharedSetupHandle);
const x = byteEncoder.encode(
'0345f802a75a6c0d9cc5b8a1e71642b8fa80b0a78938edc6da1e591149578d1a',
);
const y = byteEncoder.encode(
'3b17cab634c3795d311380f3bc93ce8e768efc0e2b9e79496cfc8f351594b472',
);
const result = verifyKzgProof(commitment, y, x, proof, sharedSetupHandle);
console.log({ result });
expect(result).toBe(ReturnValue.OK);
});
});
describe('computing an aggregate KZG proof', () => {
it('returns the expected value', () => {
const blob = generateRandomBlob();
const commitment = blobToKzgCommitment(blob, sharedSetupHandle);
const proof = computeAggregateKzgProof([blob], sharedSetupHandle);
});
});
describe('verifying an aggregate KZG proof', () => {
it('returns the expected value', () => {
expect(true).toBe(false);
});
}); });
}); });

View File

@ -34,6 +34,7 @@
extern "C" { extern "C" {
#endif #endif
#define BYTES_PER_COMMITMENT 48
#define BYTES_PER_FIELD 32 #define BYTES_PER_FIELD 32
#define FIELD_ELEMENTS_PER_BLOB 4096 #define FIELD_ELEMENTS_PER_BLOB 4096