Merge pull request #147 from jtraglia/update-nodejs-bindings
Update nodejs bindings
This commit is contained in:
commit
d41a3b0060
|
@ -152,51 +152,6 @@ Napi::Value BlobToKzgCommitment(const Napi::CallbackInfo& info) {
|
|||
return napi_typed_array_from_bytes((uint8_t *)(&commitment), BYTES_PER_COMMITMENT, env);
|
||||
}
|
||||
|
||||
// computeAggregateKzgProof: (blobs: Blob[], setupHandle: SetupHandle) => KZGProof;
|
||||
Napi::Value ComputeAggregateKzgProof(const Napi::CallbackInfo& info) {
|
||||
auto env = info.Env();
|
||||
|
||||
size_t argument_count = info.Length();
|
||||
size_t expected_argument_count = 2;
|
||||
if (argument_count != expected_argument_count) {
|
||||
return throw_invalid_arguments_count(expected_argument_count, argument_count, env);
|
||||
}
|
||||
|
||||
auto blobs_param = info[0].As<Napi::Array>();
|
||||
auto kzg_settings = info[1].As<Napi::External<KZGSettings>>().Data();
|
||||
|
||||
auto blobs_count = blobs_param.Length();
|
||||
|
||||
auto blobs = (Blob*)calloc(blobs_count, sizeof(Blob));
|
||||
if (blobs == NULL) {
|
||||
Napi::Error::New(env, "Error while allocating memory for blobs").ThrowAsJavaScriptException();
|
||||
return env.Null();
|
||||
};
|
||||
|
||||
for (uint32_t blob_index = 0; blob_index < blobs_count; blob_index++) {
|
||||
Napi::Value blob = blobs_param[blob_index];
|
||||
auto blob_bytes = blob.As<Napi::Uint8Array>().Data();
|
||||
memcpy(blobs[blob_index].bytes, blob_bytes, BYTES_PER_BLOB);
|
||||
}
|
||||
|
||||
KZGProof proof;
|
||||
C_KZG_RET ret = compute_aggregate_kzg_proof(
|
||||
&proof,
|
||||
blobs,
|
||||
blobs_count,
|
||||
kzg_settings
|
||||
);
|
||||
free(blobs);
|
||||
|
||||
if (ret != C_KZG_OK) {
|
||||
Napi::Error::New(env, "Failed to compute aggregated proof")
|
||||
.ThrowAsJavaScriptException();
|
||||
return env.Undefined();
|
||||
};
|
||||
|
||||
return napi_typed_array_from_bytes((uint8_t *)(&proof), BYTES_PER_PROOF, env);
|
||||
}
|
||||
|
||||
// computeKzgProof: (blob: Blob, zBytes: Bytes32, setupHandle: SetupHandle) => KZGProof;
|
||||
Napi::Value ComputeKzgProof(const Napi::CallbackInfo& info) {
|
||||
auto env = info.Env();
|
||||
|
@ -211,6 +166,10 @@ Napi::Value ComputeKzgProof(const Napi::CallbackInfo& info) {
|
|||
auto z_bytes = extract_byte_array_from_param(info, 1, "zBytes");
|
||||
auto kzg_settings = info[2].As<Napi::External<KZGSettings>>().Data();
|
||||
|
||||
if (env.IsExceptionPending()) {
|
||||
return env.Null();
|
||||
}
|
||||
|
||||
KZGProof proof;
|
||||
C_KZG_RET ret = compute_kzg_proof(
|
||||
&proof,
|
||||
|
@ -228,80 +187,37 @@ Napi::Value ComputeKzgProof(const Napi::CallbackInfo& info) {
|
|||
return napi_typed_array_from_bytes((uint8_t *)(&proof), BYTES_PER_PROOF, env);
|
||||
}
|
||||
|
||||
// verifyAggregateKzgProof: (blobs: Blob[], commitmentsBytes: Bytes48[], aggregatedProofBytes: Bytes48, setupHandle: SetupHandle) => boolean;
|
||||
Napi::Value VerifyAggregateKzgProof(const Napi::CallbackInfo& info) {
|
||||
// computeBlobKzgProof: (blob: Blob, setupHandle: SetupHandle) => KZGProof;
|
||||
Napi::Value ComputeBlobKzgProof(const Napi::CallbackInfo& info) {
|
||||
auto env = info.Env();
|
||||
|
||||
size_t argument_count = info.Length();
|
||||
size_t expected_argument_count = 4;
|
||||
size_t expected_argument_count = 2;
|
||||
if (argument_count != expected_argument_count) {
|
||||
return throw_invalid_arguments_count(expected_argument_count, argument_count, env);
|
||||
}
|
||||
|
||||
auto blobs_param = info[0].As<Napi::Array>();
|
||||
auto commitments_param = info[1].As<Napi::Array>();
|
||||
auto proof_param = info[2].As<Napi::TypedArray>();
|
||||
auto kzg_settings = info[3].As<Napi::External<KZGSettings>>().Data();
|
||||
auto blob = extract_byte_array_from_param(info, 0, "blob");
|
||||
auto kzg_settings = info[1].As<Napi::External<KZGSettings>>().Data();
|
||||
|
||||
auto proof_bytes = proof_param.As<Napi::Uint8Array>().Data();
|
||||
auto blobs_count = blobs_param.Length();
|
||||
auto commitments_count = commitments_param.Length();
|
||||
|
||||
if (blobs_count != commitments_count) {
|
||||
Napi::Error::New(env, "verifyAggregateKzgProof requires blobs count to match expectedKzgCommitments count")
|
||||
.ThrowAsJavaScriptException();
|
||||
return env.Undefined();
|
||||
if (env.IsExceptionPending()) {
|
||||
return env.Null();
|
||||
}
|
||||
|
||||
auto blobs = (Blob*)calloc(blobs_count, sizeof(Blob));
|
||||
if (blobs == NULL) {
|
||||
Napi::Error::New(env, "Error while allocating memory for blobs").ThrowAsJavaScriptException();
|
||||
return env.Null();
|
||||
};
|
||||
|
||||
auto commitments = (Bytes48*)calloc(blobs_count, sizeof(Bytes48));
|
||||
if (commitments == NULL) {
|
||||
free(blobs);
|
||||
Napi::Error::New(env, "Error while allocating memory for commitments").ThrowAsJavaScriptException();
|
||||
return env.Null();
|
||||
};
|
||||
|
||||
C_KZG_RET ret;
|
||||
|
||||
for (uint32_t blob_index = 0; blob_index < blobs_count; blob_index++) {
|
||||
// Extract blob bytes from parameter
|
||||
Napi::Value blob = blobs_param[blob_index];
|
||||
auto blob_bytes = blob.As<Napi::Uint8Array>().Data();
|
||||
memcpy(blobs[blob_index].bytes, blob_bytes, BYTES_PER_BLOB);
|
||||
|
||||
// Extract commitment from parameter
|
||||
Napi::Value commitment = commitments_param[blob_index];
|
||||
auto commitment_bytes = commitment.As<Napi::Uint8Array>().Data();
|
||||
memcpy(&commitments[blob_index], commitment_bytes, BYTES_PER_COMMITMENT);
|
||||
}
|
||||
|
||||
bool verification_result;
|
||||
ret = verify_aggregate_kzg_proof(
|
||||
&verification_result,
|
||||
blobs,
|
||||
commitments,
|
||||
blobs_count,
|
||||
(Bytes48 *)proof_bytes,
|
||||
KZGProof proof;
|
||||
C_KZG_RET ret = compute_blob_kzg_proof(
|
||||
&proof,
|
||||
(Blob *)blob,
|
||||
kzg_settings
|
||||
);
|
||||
|
||||
free(commitments);
|
||||
free(blobs);
|
||||
|
||||
if (ret != C_KZG_OK) {
|
||||
Napi::Error::New(
|
||||
env,
|
||||
"verify_aggregate_kzg_proof failed with error code: " + std::to_string(ret)
|
||||
).ThrowAsJavaScriptException();
|
||||
return env.Null();
|
||||
}
|
||||
Napi::Error::New(env, "Error in computeBlobKzgProof")
|
||||
.ThrowAsJavaScriptException();
|
||||
return env.Undefined();
|
||||
};
|
||||
|
||||
return Napi::Boolean::New(env, verification_result);
|
||||
return napi_typed_array_from_bytes((uint8_t *)(&proof), BYTES_PER_PROOF, env);
|
||||
}
|
||||
|
||||
// verifyKzgProof: (commitmentBytes: Bytes48, zBytes: Bytes32, yBytes: Bytes32, proofBytes: Bytes48, setupHandle: SetupHandle) => boolean;
|
||||
|
@ -342,15 +258,148 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) {
|
|||
return Napi::Boolean::New(env, out);
|
||||
}
|
||||
|
||||
// verifyBlobKzgProof: (blob: Blob, commitmentBytes: Bytes48, proofBytes: Bytes48, setupHandle: SetupHandle) => boolean;
|
||||
Napi::Value VerifyBlobKzgProof(const Napi::CallbackInfo& info) {
|
||||
auto env = info.Env();
|
||||
|
||||
size_t argument_count = info.Length();
|
||||
size_t expected_argument_count = 4;
|
||||
if (argument_count != expected_argument_count) {
|
||||
return throw_invalid_arguments_count(expected_argument_count, argument_count, env);
|
||||
}
|
||||
|
||||
auto blob_bytes = extract_byte_array_from_param(info, 0, "blob");
|
||||
auto commitment_bytes = extract_byte_array_from_param(info, 1, "commitmentBytes");
|
||||
auto proof_bytes = extract_byte_array_from_param(info, 2, "proofBytes");
|
||||
auto kzg_settings = info[3].As<Napi::External<KZGSettings>>().Data();
|
||||
|
||||
if (env.IsExceptionPending()) {
|
||||
return env.Null();
|
||||
}
|
||||
|
||||
bool out;
|
||||
C_KZG_RET ret = verify_blob_kzg_proof(
|
||||
&out,
|
||||
(Blob *)blob_bytes,
|
||||
(Bytes48 *)commitment_bytes,
|
||||
(Bytes48 *)proof_bytes,
|
||||
kzg_settings
|
||||
);
|
||||
|
||||
if (ret != C_KZG_OK) {
|
||||
Napi::TypeError::New(env, "Error in verifyBlobKzgProof").ThrowAsJavaScriptException();
|
||||
return env.Null();
|
||||
}
|
||||
|
||||
return Napi::Boolean::New(env, out);
|
||||
}
|
||||
|
||||
// verifyBlobKzgProofBatch: (blobs: Blob[], commitmentsBytes: Bytes48[], proofsBytes: Bytes48[], setupHandle: SetupHandle) => boolean;
|
||||
Napi::Value VerifyBlobKzgProofBatch(const Napi::CallbackInfo& info) {
|
||||
auto env = info.Env();
|
||||
|
||||
C_KZG_RET ret;
|
||||
Blob *blobs = NULL;
|
||||
Bytes48 *commitments = NULL;
|
||||
Bytes48 *proofs = NULL;
|
||||
Napi::Value result = env.Null();
|
||||
|
||||
size_t argument_count = info.Length();
|
||||
size_t expected_argument_count = 4;
|
||||
if (argument_count != expected_argument_count) {
|
||||
return throw_invalid_arguments_count(expected_argument_count, argument_count, env);
|
||||
}
|
||||
|
||||
auto blobs_param = info[0].As<Napi::Array>();
|
||||
auto commitments_param = info[1].As<Napi::Array>();
|
||||
auto proofs_param = info[2].As<Napi::Array>();
|
||||
auto kzg_settings = info[3].As<Napi::External<KZGSettings>>().Data();
|
||||
|
||||
auto blobs_count = blobs_param.Length();
|
||||
auto commitments_count = commitments_param.Length();
|
||||
auto proofs_count = proofs_param.Length();
|
||||
|
||||
if (blobs_count != commitments_count || blobs_count != proofs_count) {
|
||||
Napi::Error::New(env, "verifyBlobKzgProofBatch requires equal number of blobs/commitments/proofs")
|
||||
.ThrowAsJavaScriptException();
|
||||
result = env.Null();
|
||||
goto out;
|
||||
}
|
||||
|
||||
blobs = (Blob *)calloc(blobs_count, sizeof(Blob));
|
||||
if (blobs == NULL) {
|
||||
Napi::Error::New(env, "Error while allocating memory for blobs").ThrowAsJavaScriptException();
|
||||
result = env.Null();
|
||||
goto out;
|
||||
};
|
||||
|
||||
commitments = (Bytes48 *)calloc(commitments_count, sizeof(Bytes48));
|
||||
if (commitments == NULL) {
|
||||
free(blobs);
|
||||
Napi::Error::New(env, "Error while allocating memory for commitments").ThrowAsJavaScriptException();
|
||||
result = env.Null();
|
||||
goto out;
|
||||
};
|
||||
|
||||
proofs = (Bytes48 *)calloc(proofs_count, sizeof(Bytes48));
|
||||
if (proofs == NULL) {
|
||||
Napi::Error::New(env, "Error while allocating memory for proofs").ThrowAsJavaScriptException();
|
||||
result = env.Null();
|
||||
goto out;
|
||||
};
|
||||
|
||||
for (uint32_t index = 0; index < blobs_count; index++) {
|
||||
// Extract blob bytes from parameter
|
||||
Napi::Value blob = blobs_param[index];
|
||||
auto blob_bytes = blob.As<Napi::Uint8Array>().Data();
|
||||
memcpy(blobs[index].bytes, blob_bytes, BYTES_PER_BLOB);
|
||||
|
||||
// Extract commitment from parameter
|
||||
Napi::Value commitment = commitments_param[index];
|
||||
auto commitment_bytes = commitment.As<Napi::Uint8Array>().Data();
|
||||
memcpy(&commitments[index], commitment_bytes, BYTES_PER_COMMITMENT);
|
||||
|
||||
// Extract proof from parameter
|
||||
Napi::Value proof = proofs_param[index];
|
||||
auto proof_bytes = proof.As<Napi::Uint8Array>().Data();
|
||||
memcpy(&proofs[index], proof_bytes, BYTES_PER_PROOF);
|
||||
}
|
||||
|
||||
bool out;
|
||||
ret = verify_blob_kzg_proof_batch(
|
||||
&out,
|
||||
blobs,
|
||||
commitments,
|
||||
proofs,
|
||||
blobs_count,
|
||||
kzg_settings
|
||||
);
|
||||
|
||||
if (ret != C_KZG_OK) {
|
||||
Napi::TypeError::New(env, "Error in verifyBlobKzgProofBatch").ThrowAsJavaScriptException();
|
||||
result = env.Null();
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = Napi::Boolean::New(env, out);
|
||||
|
||||
out:
|
||||
free(blobs);
|
||||
free(commitments);
|
||||
free(proofs);
|
||||
return result;
|
||||
}
|
||||
|
||||
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["blobToKzgCommitment"] = Napi::Function::New(env, BlobToKzgCommitment);
|
||||
exports["computeKzgProof"] = Napi::Function::New(env, ComputeKzgProof);
|
||||
exports["computeBlobKzgProof"] = Napi::Function::New(env, ComputeBlobKzgProof);
|
||||
exports["verifyKzgProof"] = Napi::Function::New(env, VerifyKzgProof);
|
||||
exports["computeAggregateKzgProof"] = Napi::Function::New(env, ComputeAggregateKzgProof);
|
||||
exports["verifyAggregateKzgProof"] = Napi::Function::New(env, VerifyAggregateKzgProof);
|
||||
exports["verifyBlobKzgProof"] = Napi::Function::New(env, VerifyBlobKzgProof);
|
||||
exports["verifyBlobKzgProofBatch"] = Napi::Function::New(env, VerifyBlobKzgProofBatch);
|
||||
|
||||
// Constants
|
||||
exports["BYTES_PER_BLOB"] = Napi::Number::New(env, BYTES_PER_BLOB);
|
||||
|
|
|
@ -33,17 +33,7 @@ type KZG = {
|
|||
setupHandle: SetupHandle,
|
||||
) => KZGProof;
|
||||
|
||||
computeAggregateKzgProof: (
|
||||
blobs: Blob[],
|
||||
setupHandle: SetupHandle,
|
||||
) => KZGProof;
|
||||
|
||||
verifyAggregateKzgProof: (
|
||||
blobs: Blob[],
|
||||
commitmentsBytes: Bytes48[],
|
||||
aggregatedProofBytes: Bytes48,
|
||||
setupHandle: SetupHandle,
|
||||
) => boolean;
|
||||
computeBlobKzgProof: (blob: Blob, setupHandle: SetupHandle) => KZGProof;
|
||||
|
||||
verifyKzgProof: (
|
||||
commitmentBytes: Bytes48,
|
||||
|
@ -52,6 +42,20 @@ type KZG = {
|
|||
proofBytes: Bytes48,
|
||||
setupHandle: SetupHandle,
|
||||
) => boolean;
|
||||
|
||||
verifyBlobKzgProof: (
|
||||
blob: Blob,
|
||||
commitmentBytes: Bytes48,
|
||||
proofBytes: Bytes48,
|
||||
setupHandle: SetupHandle,
|
||||
) => boolean;
|
||||
|
||||
verifyBlobKzgProofBatch: (
|
||||
blobs: Blob[],
|
||||
commitmentsBytes: Bytes48[],
|
||||
proofsBytes: Bytes48[],
|
||||
setupHandle: SetupHandle,
|
||||
) => boolean;
|
||||
};
|
||||
|
||||
type TrustedSetupJSON = {
|
||||
|
@ -78,10 +82,11 @@ function requireSetupHandle(): SetupHandle {
|
|||
}
|
||||
|
||||
function checkBlob(blob: Blob) {
|
||||
if (!(blob instanceof Uint8Array)) {
|
||||
throw new Error("Expected blob to be a UInt8Array.");
|
||||
}
|
||||
if (blob.length != BYTES_PER_BLOB) {
|
||||
throw new Error(
|
||||
`Expected blob to be UInt8Array of ${BYTES_PER_BLOB} bytes.`,
|
||||
);
|
||||
throw new Error(`Expected blob to be ${BYTES_PER_BLOB} bytes.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,10 +97,11 @@ function checkBlobs(blobs: Blob[]) {
|
|||
}
|
||||
|
||||
function checkCommitment(commitment: KZGCommitment) {
|
||||
if (!(commitment instanceof Uint8Array)) {
|
||||
throw new Error("Expected commitment to be a UInt8Array.");
|
||||
}
|
||||
if (commitment.length != BYTES_PER_COMMITMENT) {
|
||||
throw new Error(
|
||||
`Expected commitment to be UInt8Array of ${BYTES_PER_COMMITMENT} bytes.`,
|
||||
);
|
||||
throw new Error(`Expected commitment to be ${BYTES_PER_COMMITMENT} bytes.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,17 +112,27 @@ function checkCommitments(commitments: KZGCommitment[]) {
|
|||
}
|
||||
|
||||
function checkProof(proof: KZGProof) {
|
||||
if (!(proof instanceof Uint8Array)) {
|
||||
throw new Error("Expected proof to be a UInt8Array.");
|
||||
}
|
||||
if (proof.length != BYTES_PER_PROOF) {
|
||||
throw new Error(
|
||||
`Expected proof to be UInt8Array of ${BYTES_PER_PROOF} bytes.`,
|
||||
);
|
||||
throw new Error(`Expected proof to be ${BYTES_PER_PROOF} bytes.`);
|
||||
}
|
||||
}
|
||||
|
||||
function checkProofs(proofs: KZGProof[]) {
|
||||
for (let proof of proofs) {
|
||||
checkProof(proof);
|
||||
}
|
||||
}
|
||||
|
||||
function checkFieldElement(field: Bytes32) {
|
||||
if (!(field instanceof Uint8Array)) {
|
||||
throw new Error("Expected field element to be a UInt8Array.");
|
||||
}
|
||||
if (field.length != BYTES_PER_FIELD_ELEMENT) {
|
||||
throw new Error(
|
||||
`Expected field element to be UInt8Array of ${BYTES_PER_FIELD_ELEMENT} bytes.`,
|
||||
`Expected field element to be ${BYTES_PER_FIELD_ELEMENT} bytes.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -173,9 +189,9 @@ export function computeKzgProof(blob: Blob, zBytes: Bytes32): KZGProof {
|
|||
return kzg.computeKzgProof(blob, zBytes, requireSetupHandle());
|
||||
}
|
||||
|
||||
export function computeAggregateKzgProof(blobs: Blob[]): KZGProof {
|
||||
checkBlobs(blobs);
|
||||
return kzg.computeAggregateKzgProof(blobs, requireSetupHandle());
|
||||
export function computeBlobKzgProof(blob: Blob): KZGProof {
|
||||
checkBlob(blob);
|
||||
return kzg.computeBlobKzgProof(blob, requireSetupHandle());
|
||||
}
|
||||
|
||||
export function verifyKzgProof(
|
||||
|
@ -197,18 +213,34 @@ export function verifyKzgProof(
|
|||
);
|
||||
}
|
||||
|
||||
export function verifyAggregateKzgProof(
|
||||
blobs: Blob[],
|
||||
commitmentsBytes: Bytes48[],
|
||||
export function verifyBlobKzgProof(
|
||||
blob: Blob,
|
||||
commitmentBytes: Bytes48,
|
||||
proofBytes: Bytes48,
|
||||
): boolean {
|
||||
checkBlobs(blobs);
|
||||
checkCommitments(commitmentsBytes);
|
||||
checkBlob(blob);
|
||||
checkCommitment(commitmentBytes);
|
||||
checkProof(proofBytes);
|
||||
return kzg.verifyAggregateKzgProof(
|
||||
blobs,
|
||||
commitmentsBytes,
|
||||
return kzg.verifyBlobKzgProof(
|
||||
blob,
|
||||
commitmentBytes,
|
||||
proofBytes,
|
||||
requireSetupHandle(),
|
||||
);
|
||||
}
|
||||
|
||||
export function verifyBlobKzgProofBatch(
|
||||
blobs: Blob[],
|
||||
commitmentsBytes: Bytes48[],
|
||||
proofsBytes: Bytes48[],
|
||||
): boolean {
|
||||
checkBlobs(blobs);
|
||||
checkCommitments(commitmentsBytes);
|
||||
checkProofs(proofsBytes);
|
||||
return kzg.verifyBlobKzgProofBatch(
|
||||
blobs,
|
||||
commitmentsBytes,
|
||||
proofsBytes,
|
||||
requireSetupHandle(),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import { randomBytes } from "crypto";
|
||||
import { existsSync } from "fs";
|
||||
import path = require("path");
|
||||
import fs = require("fs");
|
||||
|
||||
import {
|
||||
loadTrustedSetup,
|
||||
freeTrustedSetup,
|
||||
blobToKzgCommitment,
|
||||
verifyKzgProof,
|
||||
computeKzgProof,
|
||||
computeAggregateKzgProof,
|
||||
verifyAggregateKzgProof,
|
||||
computeBlobKzgProof,
|
||||
verifyKzgProof,
|
||||
verifyBlobKzgProof,
|
||||
verifyBlobKzgProofBatch,
|
||||
BYTES_PER_BLOB,
|
||||
BYTES_PER_COMMITMENT,
|
||||
BYTES_PER_PROOF,
|
||||
|
@ -24,6 +27,36 @@ const SETUP_FILE_PATH = existsSync(setupFileName)
|
|||
|
||||
const MAX_TOP_BYTE = 114;
|
||||
|
||||
const TEST_DIR = "../../tests";
|
||||
const BLOB_TO_KZG_COMMITMENT_TESTS = path.join(
|
||||
TEST_DIR,
|
||||
"blob_to_kzg_commitment",
|
||||
);
|
||||
const COMPUTE_KZG_PROOF_TESTS = path.join(TEST_DIR, "compute_kzg_proof");
|
||||
const COMPUTE_BLOB_KZG_PROOF_TESTS = path.join(
|
||||
TEST_DIR,
|
||||
"compute_blob_kzg_proof",
|
||||
);
|
||||
const VERIFY_KZG_PROOF_TESTS = path.join(TEST_DIR, "verify_kzg_proof");
|
||||
const VERIFY_BLOB_KZG_PROOF_TESTS = path.join(
|
||||
TEST_DIR,
|
||||
"verify_blob_kzg_proof",
|
||||
);
|
||||
const VERIFY_BLOB_KZG_PROOF_BATCH_TESTS = path.join(
|
||||
TEST_DIR,
|
||||
"verify_blob_kzg_proof_batch",
|
||||
);
|
||||
|
||||
function getBytes(file: String): Uint8Array {
|
||||
const data = require("fs").readFileSync(file, "ascii");
|
||||
return Buffer.from(data, "hex");
|
||||
}
|
||||
|
||||
function getBoolean(file: String): boolean {
|
||||
const data = require("fs").readFileSync(file, "ascii");
|
||||
return data.includes("true");
|
||||
}
|
||||
|
||||
const generateRandomBlob = () => {
|
||||
return new Uint8Array(
|
||||
randomBytes(BYTES_PER_BLOB).map((x, i) => {
|
||||
|
@ -46,99 +79,241 @@ describe("C-KZG", () => {
|
|||
freeTrustedSetup();
|
||||
});
|
||||
|
||||
it("computes a proof from blob", () => {
|
||||
let blob = generateRandomBlob();
|
||||
const zBytes = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(0);
|
||||
computeKzgProof(blob, zBytes);
|
||||
// No check, just make sure it doesn't crash.
|
||||
describe("reference tests should pass", () => {
|
||||
it("reference tests for blobToKzgCommitment should pass", () => {
|
||||
let tests = fs.readdirSync(BLOB_TO_KZG_COMMITMENT_TESTS);
|
||||
tests.forEach((test) => {
|
||||
let testPath = path.join(BLOB_TO_KZG_COMMITMENT_TESTS, test);
|
||||
let blob = getBytes(path.join(testPath, "blob.txt"));
|
||||
try {
|
||||
let commitment = blobToKzgCommitment(blob);
|
||||
let expectedCommitment = getBytes(
|
||||
path.join(testPath, "commitment.txt"),
|
||||
);
|
||||
expect(commitment.buffer).toEqual(expectedCommitment.buffer);
|
||||
} catch (err) {
|
||||
expect(fs.existsSync(path.join(testPath, "commitment.txt"))).toBe(
|
||||
false,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("reference tests for computeKzgProof should pass", () => {
|
||||
let tests = fs.readdirSync(COMPUTE_KZG_PROOF_TESTS);
|
||||
tests.forEach((test) => {
|
||||
let testPath = path.join(COMPUTE_KZG_PROOF_TESTS, test);
|
||||
let blob = getBytes(path.join(testPath, "blob.txt"));
|
||||
let inputPoint = getBytes(path.join(testPath, "input_point.txt"));
|
||||
try {
|
||||
let proof = computeKzgProof(blob, inputPoint);
|
||||
let expectedProof = getBytes(path.join(testPath, "proof.txt"));
|
||||
expect(proof.buffer).toEqual(expectedProof.buffer);
|
||||
} catch (err) {
|
||||
expect(fs.existsSync(path.join(testPath, "proof.txt"))).toBe(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("reference tests for computeBlobKzgProof should pass", () => {
|
||||
let tests = fs.readdirSync(COMPUTE_BLOB_KZG_PROOF_TESTS);
|
||||
tests.forEach((test) => {
|
||||
let testPath = path.join(COMPUTE_BLOB_KZG_PROOF_TESTS, test);
|
||||
let blob = getBytes(path.join(testPath, "blob.txt"));
|
||||
try {
|
||||
let proof = computeBlobKzgProof(blob);
|
||||
let expectedProof = getBytes(path.join(testPath, "proof.txt"));
|
||||
expect(proof.buffer).toEqual(expectedProof.buffer);
|
||||
} catch (err) {
|
||||
expect(fs.existsSync(path.join(testPath, "proof.txt"))).toBe(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("reference tests for verifyKzgProof should pass", () => {
|
||||
let tests = fs.readdirSync(VERIFY_KZG_PROOF_TESTS);
|
||||
tests.forEach((test) => {
|
||||
let testPath = path.join(VERIFY_KZG_PROOF_TESTS, test);
|
||||
let commitment = getBytes(path.join(testPath, "commitment.txt"));
|
||||
let inputPoint = getBytes(path.join(testPath, "input_point.txt"));
|
||||
let claimedValue = getBytes(path.join(testPath, "claimed_value.txt"));
|
||||
let proof = getBytes(path.join(testPath, "proof.txt"));
|
||||
try {
|
||||
let ok = verifyKzgProof(commitment, inputPoint, claimedValue, proof);
|
||||
let expectedOk = getBoolean(path.join(testPath, "ok.txt"));
|
||||
expect(ok).toEqual(expectedOk);
|
||||
} catch (err) {
|
||||
expect(fs.existsSync(path.join(testPath, "ok.txt"))).toBe(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("reference tests for verifyBlobKzgProof should pass", () => {
|
||||
let tests = fs.readdirSync(VERIFY_BLOB_KZG_PROOF_TESTS);
|
||||
tests.forEach((test) => {
|
||||
let testPath = path.join(VERIFY_BLOB_KZG_PROOF_TESTS, test);
|
||||
let blob = getBytes(path.join(testPath, "blob.txt"));
|
||||
let commitment = getBytes(path.join(testPath, "commitment.txt"));
|
||||
let proof = getBytes(path.join(testPath, "proof.txt"));
|
||||
try {
|
||||
let ok = verifyBlobKzgProof(blob, commitment, proof);
|
||||
let expectedOk = getBoolean(path.join(testPath, "ok.txt"));
|
||||
expect(ok).toEqual(expectedOk);
|
||||
} catch (err) {
|
||||
expect(fs.existsSync(path.join(testPath, "ok.txt"))).toBe(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("reference tests for verifyBlobKzgProofBatch should pass", () => {
|
||||
let tests = fs.readdirSync(VERIFY_BLOB_KZG_PROOF_BATCH_TESTS);
|
||||
tests.forEach((test) => {
|
||||
let testPath = path.join(VERIFY_BLOB_KZG_PROOF_BATCH_TESTS, test);
|
||||
let blobs = fs
|
||||
.readdirSync(path.join(testPath, "blobs"))
|
||||
.sort()
|
||||
.map((filename) => {
|
||||
return path.join(testPath, "blobs", filename);
|
||||
})
|
||||
.map(getBytes);
|
||||
let commitments = fs
|
||||
.readdirSync(path.join(testPath, "commitments"))
|
||||
.sort()
|
||||
.map((filename) => {
|
||||
return path.join(testPath, "commitments", filename);
|
||||
})
|
||||
.map(getBytes);
|
||||
let proofs = fs
|
||||
.readdirSync(path.join(testPath, "proofs"))
|
||||
.sort()
|
||||
.map((filename) => {
|
||||
return path.join(testPath, "proofs", filename);
|
||||
})
|
||||
.map(getBytes);
|
||||
try {
|
||||
let ok = verifyBlobKzgProofBatch(blobs, commitments, proofs);
|
||||
let expectedOk = getBoolean(path.join(testPath, "ok.txt"));
|
||||
expect(ok).toEqual(expectedOk);
|
||||
} catch (err) {
|
||||
expect(fs.existsSync(path.join(testPath, "ok.txt"))).toBe(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("computes the correct commitments and aggregate proof from blobs", () => {
|
||||
let blobs = new Array(2).fill(0).map(generateRandomBlob);
|
||||
let commitments = blobs.map(blobToKzgCommitment);
|
||||
let proof = computeAggregateKzgProof(blobs);
|
||||
expect(verifyAggregateKzgProof(blobs, commitments, proof)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns the identity (aka zero, aka neutral) element when blobs is an empty array", () => {
|
||||
const aggregateProofOfNothing = computeAggregateKzgProof([]);
|
||||
expect(aggregateProofOfNothing.toString()).toEqual(
|
||||
[
|
||||
192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0,
|
||||
].toString(),
|
||||
);
|
||||
});
|
||||
|
||||
it("verifies the aggregate proof of empty blobs and commitments", () => {
|
||||
expect(verifyAggregateKzgProof([], [], computeAggregateKzgProof([]))).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("verifies a valid KZG proof", () => {
|
||||
const commitment = new Uint8Array(BYTES_PER_COMMITMENT).fill(0);
|
||||
commitment[0] = 0xc0;
|
||||
const z = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(0);
|
||||
const y = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(0);
|
||||
const proof = new Uint8Array(BYTES_PER_PROOF).fill(0);
|
||||
proof[0] = 0xc0;
|
||||
|
||||
expect(verifyKzgProof(commitment, z, y, proof)).toBe(true);
|
||||
});
|
||||
|
||||
it("verifies an invalid valid KZG proof", () => {
|
||||
const commitment = new Uint8Array(BYTES_PER_COMMITMENT).fill(0);
|
||||
commitment[0] = 0xc0;
|
||||
const z = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(1);
|
||||
const y = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(1);
|
||||
const proof = new Uint8Array(BYTES_PER_PROOF).fill(0);
|
||||
proof[0] = 0xc0;
|
||||
|
||||
expect(verifyKzgProof(commitment, z, y, proof)).toBe(false);
|
||||
});
|
||||
|
||||
it("computes the aggregate proof when for a single blob", () => {
|
||||
let blobs = new Array(1).fill(0).map(generateRandomBlob);
|
||||
let commitments = blobs.map(blobToKzgCommitment);
|
||||
let proof = computeAggregateKzgProof(blobs);
|
||||
expect(verifyAggregateKzgProof(blobs, commitments, proof)).toBe(true);
|
||||
});
|
||||
|
||||
it("fails when given incorrect commitments", () => {
|
||||
const blobs = new Array(2).fill(0).map(generateRandomBlob);
|
||||
const commitments = blobs.map(blobToKzgCommitment);
|
||||
commitments[0][0] = commitments[0][0] === 0 ? 1 : 0; // Mutate the commitment
|
||||
const proof = computeAggregateKzgProof(blobs);
|
||||
expect(() =>
|
||||
verifyAggregateKzgProof(blobs, commitments, proof),
|
||||
).toThrowError("verify_aggregate_kzg_proof failed with error code: 1");
|
||||
});
|
||||
|
||||
it("throws the expected error when given fewer commitments than blobs", () => {
|
||||
let blobs = new Array(1).fill(0).map(generateRandomBlob);
|
||||
let commitments = [] as Uint8Array[];
|
||||
let proof = computeAggregateKzgProof(blobs);
|
||||
expect(() =>
|
||||
verifyAggregateKzgProof(blobs, commitments, proof),
|
||||
).toThrowError(
|
||||
"verifyAggregateKzgProof requires blobs count to match expectedKzgCommitments count",
|
||||
);
|
||||
});
|
||||
|
||||
describe("computing commitment from blobs", () => {
|
||||
describe("edge cases for blobToKzgCommitment", () => {
|
||||
it("throws as expected when given an argument of invalid type", () => {
|
||||
// @ts-expect-error
|
||||
expect(() => blobToKzgCommitment("wrong type")).toThrowError(
|
||||
"Expected blob to be UInt8Array of 131072 bytes",
|
||||
"Expected blob to be a UInt8Array",
|
||||
);
|
||||
});
|
||||
|
||||
it("throws as expected when given an argument of invalid length", () => {
|
||||
expect(() =>
|
||||
blobToKzgCommitment(randomBytes(BYTES_PER_BLOB - 1)),
|
||||
).toThrowError("Expected blob to be UInt8Array of 131072 bytes");
|
||||
).toThrowError("Expected blob to be 131072 bytes");
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: add more tests for this function.
|
||||
describe("edge cases for computeKzgProof", () => {
|
||||
it("computes a proof from blob/field element", () => {
|
||||
let blob = generateRandomBlob();
|
||||
const zBytes = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(0);
|
||||
computeKzgProof(blob, zBytes);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: add more tests for this function.
|
||||
describe("edge cases for computeBlobKzgProof", () => {
|
||||
it("computes a proof from blob", () => {
|
||||
let blob = generateRandomBlob();
|
||||
computeBlobKzgProof(blob);
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge cases for verifyKzgProof", () => {
|
||||
it("valid proof should result in true", () => {
|
||||
const commitment = new Uint8Array(BYTES_PER_COMMITMENT).fill(0);
|
||||
commitment[0] = 0xc0;
|
||||
const z = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(0);
|
||||
const y = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(0);
|
||||
const proof = new Uint8Array(BYTES_PER_PROOF).fill(0);
|
||||
proof[0] = 0xc0;
|
||||
|
||||
expect(verifyKzgProof(commitment, z, y, proof)).toBe(true);
|
||||
});
|
||||
|
||||
it("invalid proof should result in false", () => {
|
||||
const commitment = new Uint8Array(BYTES_PER_COMMITMENT).fill(0);
|
||||
commitment[0] = 0xc0;
|
||||
const z = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(1);
|
||||
const y = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(1);
|
||||
const proof = new Uint8Array(BYTES_PER_PROOF).fill(0);
|
||||
proof[0] = 0xc0;
|
||||
|
||||
expect(verifyKzgProof(commitment, z, y, proof)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge cases for verifyBlobKzgProof", () => {
|
||||
it("correct blob/commitment/proof should verify as true", () => {
|
||||
let blob = generateRandomBlob();
|
||||
let commitment = blobToKzgCommitment(blob);
|
||||
let proof = computeBlobKzgProof(blob);
|
||||
expect(verifyBlobKzgProof(blob, commitment, proof)).toBe(true);
|
||||
});
|
||||
|
||||
it("incorrect commitment should verify as false", () => {
|
||||
let blob = generateRandomBlob();
|
||||
let commitment = blobToKzgCommitment(generateRandomBlob());
|
||||
let proof = computeBlobKzgProof(blob);
|
||||
expect(verifyBlobKzgProof(blob, commitment, proof)).toBe(false);
|
||||
});
|
||||
|
||||
it("incorrect proof should verify as false", () => {
|
||||
let blob = generateRandomBlob();
|
||||
let commitment = blobToKzgCommitment(blob);
|
||||
let proof = computeBlobKzgProof(generateRandomBlob());
|
||||
expect(verifyBlobKzgProof(blob, commitment, proof)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge cases for verifyBlobKzgProofBatch", () => {
|
||||
it("should reject non-bytearray blob", () => {
|
||||
expect(() =>
|
||||
// @ts-expect-error
|
||||
verifyBlobKzgProofBatch(["foo", "bar"], [], []),
|
||||
).toThrowError("Expected blob to be a UInt8Array");
|
||||
});
|
||||
|
||||
it("zero blobs/commitments/proofs should verify as true", () => {
|
||||
expect(verifyBlobKzgProofBatch([], [], [])).toBe(true);
|
||||
});
|
||||
|
||||
it("mismatching blobs/commitments/proofs should throw error", () => {
|
||||
let count = 3;
|
||||
let blobs = new Array(count);
|
||||
let commitments = new Array(count);
|
||||
let proofs = new Array(count);
|
||||
|
||||
for (let [i, _] of blobs.entries()) {
|
||||
blobs[i] = generateRandomBlob();
|
||||
commitments[i] = blobToKzgCommitment(blobs[i]);
|
||||
proofs[i] = computeBlobKzgProof(blobs[i]);
|
||||
}
|
||||
|
||||
expect(verifyBlobKzgProofBatch(blobs, commitments, proofs)).toBe(true);
|
||||
expect(() =>
|
||||
verifyBlobKzgProofBatch(blobs.slice(0, 1), commitments, proofs),
|
||||
).toThrowError("requires equal number of blobs/commitments/proofs");
|
||||
expect(() =>
|
||||
verifyBlobKzgProofBatch(blobs, commitments.slice(0, 1), proofs),
|
||||
).toThrowError("requires equal number of blobs/commitments/proofs");
|
||||
expect(() =>
|
||||
verifyBlobKzgProofBatch(blobs, commitments, proofs.slice(0, 1)),
|
||||
).toThrowError("requires equal number of blobs/commitments/proofs");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue