c-kzg-4844/bindings/node.js/kzg.cxx

348 lines
11 KiB
C++
Raw Normal View History

2022-11-02 20:21:41 +00:00
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
2022-11-03 22:13:49 +00:00
#include <sstream> // std::ostringstream
#include <algorithm> // std::copy
#include <iterator> // std::ostream_iterator
2022-11-02 20:21:41 +00:00
#include <napi.h>
#include "c_kzg_4844.h"
#include "blst.h"
Napi::Value throw_invalid_arguments_count(
2022-11-22 20:27:32 +00:00
const unsigned int expected,
const unsigned int actual,
const Napi::Env env
) {
Napi::RangeError::New(
env,
"Wrong number of arguments. Expected: "
+ std::to_string(expected)
+ ", received " + std::to_string(actual)
).ThrowAsJavaScriptException();
return env.Null();
}
Napi::Value throw_invalid_argument_type(const Napi::Env env, std::string name, std::string expectedType) {
Napi::TypeError::New(
env,
"Invalid argument type: " + name + ". Expected " + expectedType
).ThrowAsJavaScriptException();
return env.Null();
}
Napi::TypedArrayOf<uint8_t> napi_typed_array_from_bytes(uint8_t* array, size_t length, Napi::Env env) {
2022-11-03 19:57:46 +00:00
// Create std::vector<uint8_t> out of array.
// We allocate it on the heap to allow wrapping it up into ArrayBuffer.
std::unique_ptr<std::vector<uint8_t>> vector =
std::make_unique<std::vector<uint8_t>>(length, 0);
2022-11-03 19:57:46 +00:00
for (size_t i = 0; i < length; ++i) {
(*vector)[i] = array[i];
2022-11-03 19:57:46 +00:00
}
// Wrap up the std::vector into the ArrayBuffer.
Napi::ArrayBuffer buffer = Napi::ArrayBuffer::New(
2022-11-03 19:57:46 +00:00
env,
vector->data(),
length /* size in bytes */,
2022-11-03 19:57:46 +00:00
[](Napi::Env /*env*/, void* /*data*/, std::vector<uint8_t>* hint) {
std::unique_ptr<std::vector<uint8_t>> vectorPtrToDelete(hint);
},
vector.get());
2022-11-03 19:57:46 +00:00
// The finalizer is responsible for deleting the vector: release the
// unique_ptr ownership.
vector.release();
2022-11-03 19:57:46 +00:00
return Napi::Uint8Array::New(env, length, buffer, 0);
2022-11-03 19:57:46 +00:00
}
2022-12-13 19:21:43 +00:00
const uint8_t * extract_byte_array_from_param(const Napi::CallbackInfo& info, const int index, const std::string name) {
auto param = info[index].As<Napi::TypedArray>();
if (!param.IsTypedArray() || param.TypedArrayType() != napi_uint8_array) {
throw_invalid_argument_type(info.Env(), name, "UInt8Array");
}
return param.As<Napi::Uint8Array>().Data();
}
2022-11-03 21:39:02 +00:00
// loadTrustedSetup: (filePath: string) => SetupHandle;
2022-11-02 20:21:41 +00:00
Napi::Value LoadTrustedSetup(const Napi::CallbackInfo& info) {
auto env = info.Env();
size_t argument_count = info.Length();
size_t expected_argument_count = 1;
if (argument_count != expected_argument_count) {
return throw_invalid_arguments_count(expected_argument_count, argument_count, env);
2022-11-02 20:21:41 +00:00
}
if (!info[0].IsString()) {
return throw_invalid_argument_type(env, "filePath", "string");
2022-11-02 20:21:41 +00:00
}
const std::string file_path = info[0].ToString().Utf8Value();
2022-11-02 20:21:41 +00:00
KZGSettings* kzg_settings = (KZGSettings*)malloc(sizeof(KZGSettings));
2022-11-02 20:21:41 +00:00
if (kzg_settings == NULL) {
Napi::Error::New(env, "Error while allocating memory for KZG settings").ThrowAsJavaScriptException();
2022-11-02 20:21:41 +00:00
return env.Null();
};
FILE* f = fopen(file_path.c_str(), "r");
2022-11-02 20:21:41 +00:00
if (f == NULL) {
free(kzg_settings);
Napi::Error::New(env, "Error opening trusted setup file: " + file_path).ThrowAsJavaScriptException();
2022-11-02 20:21:41 +00:00
return env.Null();
}
if (load_trusted_setup_file(kzg_settings, f) != C_KZG_OK) {
free(kzg_settings);
Napi::Error::New(env, "Error loading trusted setup file").ThrowAsJavaScriptException();
2022-11-02 20:21:41 +00:00
return env.Null();
}
return Napi::External<KZGSettings>::New(info.Env(), kzg_settings);
2022-11-02 20:21:41 +00:00
}
2022-11-03 21:39:02 +00:00
// freeTrustedSetup: (setupHandle: SetupHandle) => void;
Napi::Value FreeTrustedSetup(const Napi::CallbackInfo& info) {
auto env = info.Env();
size_t argument_count = info.Length();
size_t expected_argument_count = 1;
if (argument_count != expected_argument_count) {
return throw_invalid_arguments_count(expected_argument_count, argument_count, env);
}
auto kzg_settings = info[0].As<Napi::External<KZGSettings>>().Data();
free_trusted_setup(kzg_settings);
free(kzg_settings);
return env.Undefined();
2022-11-02 20:21:41 +00:00
}
2022-11-02 20:27:00 +00:00
2022-11-03 21:39:02 +00:00
// blobToKzgCommitment: (blob: Blob, setupHandle: SetupHandle) => KZGCommitment;
2022-11-02 20:21:41 +00:00
Napi::Value BlobToKzgCommitment(const Napi::CallbackInfo& info) {
2022-11-03 21:39:02 +00:00
auto env = info.Env();
2022-11-03 00:17:17 +00:00
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);
2022-11-03 00:17:17 +00:00
}
2022-12-13 19:21:43 +00:00
auto blob = extract_byte_array_from_param(info, 0, "blob");
if (env.IsExceptionPending()) {
return env.Null();
2022-11-03 00:17:17 +00:00
}
2022-11-03 19:57:46 +00:00
auto kzg_settings = info[1].As<Napi::External<KZGSettings>>().Data();
2022-11-03 00:17:17 +00:00
KZGCommitment commitment;
C_KZG_RET ret = blob_to_kzg_commitment(&commitment, blob, kzg_settings);
if (ret != C_KZG_OK) {
Napi::Error::New(env, "Failed to convert blob to commitment")
.ThrowAsJavaScriptException();
return env.Undefined();
};
2022-11-03 00:17:17 +00:00
uint8_t commitment_bytes[BYTES_PER_COMMITMENT];
bytes_from_g1(commitment_bytes, &commitment);
return napi_typed_array_from_bytes(commitment_bytes, BYTES_PER_COMMITMENT, env);
2022-11-03 19:57:46 +00:00
}
2022-11-03 00:17:17 +00:00
2022-11-03 21:39:02 +00:00
// computeAggregateKzgProof: (blobs: Blob[], setupHandle: SetupHandle) => KZGProof;
2022-11-03 19:57:46 +00:00
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);
2022-11-03 00:17:17 +00:00
}
2022-11-03 19:57:46 +00:00
auto blobs_param = info[0].As<Napi::Array>();
auto kzg_settings = info[1].As<Napi::External<KZGSettings>>().Data();
2022-11-03 00:17:17 +00:00
auto blobs_count = blobs_param.Length();
2022-11-04 18:56:41 +00:00
auto blobs = (Blob*)calloc(blobs_count, sizeof(Blob));
2022-11-03 19:57:46 +00:00
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();
2022-11-04 18:56:41 +00:00
memcpy(blobs[blob_index], blob_bytes, BYTES_PER_BLOB);
2022-11-03 19:57:46 +00:00
}
KZGProof proof;
C_KZG_RET ret = compute_aggregate_kzg_proof(
&proof,
2022-11-04 18:56:41 +00:00
blobs,
blobs_count,
kzg_settings
2022-11-03 19:57:46 +00:00
);
free(blobs);
2022-11-03 19:57:46 +00:00
2022-11-03 21:39:02 +00:00
if (ret != C_KZG_OK) {
Napi::Error::New(env, "Failed to compute proof")
2022-11-03 19:57:46 +00:00
.ThrowAsJavaScriptException();
2022-11-03 21:39:02 +00:00
return env.Undefined();
};
2022-11-03 19:57:46 +00:00
uint8_t proof_bytes[BYTES_PER_PROOF];
bytes_from_g1(proof_bytes, &proof);
return napi_typed_array_from_bytes(proof_bytes, BYTES_PER_PROOF, env);
2022-11-03 21:39:02 +00:00
}
// verifyAggregateKzgProof: (blobs: Blob[], expectedKzgCommitments: KZGCommitment[], kzgAggregatedProof: KZGProof, setupHandle: SetupHandle) => boolean;
2022-11-03 21:39:02 +00:00
Napi::Value VerifyAggregateKzgProof(const Napi::CallbackInfo& info) {
auto env = info.Env();
2022-11-03 19:57:46 +00:00
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);
2022-11-03 21:39:02 +00:00
}
auto blobs_param = info[0].As<Napi::Array>();
auto commitments_param = info[1].As<Napi::Array>();
2022-11-03 21:39:02 +00:00
auto proof_param = info[2].As<Napi::TypedArray>();
auto kzg_settings = info[3].As<Napi::External<KZGSettings>>().Data();
2022-11-03 21:39:02 +00:00
auto proof_bytes = proof_param.As<Napi::Uint8Array>().Data();
auto blobs_count = blobs_param.Length();
2022-11-04 18:56:41 +00:00
auto blobs = (Blob*)calloc(blobs_count, sizeof(Blob));
auto commitments = (KZGCommitment*)calloc(blobs_count, sizeof(KZGCommitment));
2022-11-03 21:39:02 +00:00
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();
2022-11-03 21:39:02 +00:00
2022-11-04 18:56:41 +00:00
memcpy(blobs[blob_index], blob_bytes, BYTES_PER_BLOB);
2022-11-03 21:39:02 +00:00
// Extract a G1 point for each commitment
Napi::Value commitment = commitments_param[blob_index];
auto commitment_bytes = commitment.As<Napi::Uint8Array>().Data();
ret = bytes_to_g1(&commitments[blob_index], commitment_bytes);
2022-11-03 21:39:02 +00:00
if (ret != C_KZG_OK) {
std::ostringstream ss;
std::copy(commitment_bytes, commitment_bytes + BYTES_PER_COMMITMENT, std::ostream_iterator<int>(ss, ","));
Napi::TypeError::New(
env,
"Invalid commitment data"
).ThrowAsJavaScriptException();
2022-11-03 21:39:02 +00:00
free(commitments);
2022-11-04 18:56:41 +00:00
free(blobs);
2022-11-03 21:39:02 +00:00
return env.Null();
}
}
KZGProof proof;
ret = bytes_to_g1(&proof, proof_bytes);
2022-11-03 19:57:46 +00:00
if (ret != C_KZG_OK) {
2022-11-03 21:39:02 +00:00
free(commitments);
2022-11-04 18:56:41 +00:00
free(blobs);
2022-11-03 21:39:02 +00:00
Napi::Error::New(env, "Invalid proof data")
2022-11-03 19:57:46 +00:00
.ThrowAsJavaScriptException();
return env.Null();
2022-11-03 21:39:02 +00:00
}
2022-11-03 19:57:46 +00:00
bool verification_result;
ret = verify_aggregate_kzg_proof(
&verification_result,
2022-11-04 18:56:41 +00:00
blobs,
2022-11-03 21:39:02 +00:00
commitments,
blobs_count,
2022-11-03 21:39:02 +00:00
&proof,
kzg_settings
);
2022-11-03 19:57:46 +00:00
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();
2022-11-03 21:39:02 +00:00
return env.Null();
}
2022-11-03 19:57:46 +00:00
return Napi::Boolean::New(env, verification_result);
2022-11-02 20:21:41 +00:00
}
2022-11-03 21:39:02 +00:00
// verifyKzgProof: (polynomialKzg: KZGCommitment, z: BLSFieldElement, y: BLSFieldElement, kzgProof: KZGProof, setupHandle: SetupHandle) => boolean;
2022-11-02 20:21:41 +00:00
Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) {
2022-11-03 19:57:46 +00:00
auto env = info.Env();
size_t argument_count = info.Length();
size_t expected_argument_count = 5;
if (argument_count != expected_argument_count) {
return throw_invalid_arguments_count(expected_argument_count, argument_count, env);
}
2022-12-13 19:21:43 +00:00
auto polynomial_kzg = extract_byte_array_from_param(info, 0, "polynomialKzg");
auto z = extract_byte_array_from_param(info, 1, "z");
auto y = extract_byte_array_from_param(info, 2, "y");
auto kzg_proof = extract_byte_array_from_param(info, 3, "kzgProof");
auto kzg_settings = info[4].As<Napi::External<KZGSettings>>().Data();
2022-12-13 19:21:43 +00:00
if (env.IsExceptionPending()) {
return env.Null();
2022-11-03 19:57:46 +00:00
}
KZGCommitment commitment;
auto ret = bytes_to_g1(&commitment, polynomial_kzg);
if (ret != C_KZG_OK) {
std::ostringstream ss;
std::copy(polynomial_kzg, polynomial_kzg + BYTES_PER_COMMITMENT, std::ostream_iterator<int>(ss, ","));
2022-11-03 23:27:56 +00:00
Napi::TypeError::New(env, "Failed to parse argument commitment: " + ss.str() + " Return code was: " + std::to_string(ret)).ThrowAsJavaScriptException();
return env.Null();
};
2022-11-03 21:39:02 +00:00
KZGProof proof;
if (bytes_to_g1(&proof, kzg_proof) != C_KZG_OK) {
Napi::TypeError::New(env, "Invalid kzgProof").ThrowAsJavaScriptException();
return env.Null();
}
bool out;
2022-11-19 09:35:55 +00:00
if (verify_kzg_proof(&out, &commitment, z, y, &proof, kzg_settings) != C_KZG_OK) {
Napi::TypeError::New(env, "Failed to verify KZG proof").ThrowAsJavaScriptException();
return env.Null();
}
return Napi::Boolean::New(env, out);
2022-11-02 20:21:41 +00:00
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
// Functions
2022-11-03 22:13:49 +00:00
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);
2022-11-04 18:44:57 +00:00
exports["BYTES_PER_FIELD_ELEMENT"] = Napi::Number::New(env, BYTES_PER_FIELD_ELEMENT);
2022-11-02 20:21:41 +00:00
return exports;
}
NODE_API_MODULE(addon, Init)