Move setup handle to C in node bindings (#177)
* feat(node-bindings): move KzgSettings to c * fix(node-bindings): typo in comment * fix(node-bindings): remove unnecessary SetInstanceData * feat(node-bindings): use C for setting instance data * docs(node-bindings): fix comment on struct * refactor(node-bindings): revert export order to minimize diff
This commit is contained in:
parent
599ae2fe21
commit
87a3e4148d
|
@ -9,55 +9,67 @@
|
||||||
#include "c_kzg_4844.h"
|
#include "c_kzg_4844.h"
|
||||||
#include "blst.h"
|
#include "blst.h"
|
||||||
|
|
||||||
Napi::Value throw_invalid_arguments_count(
|
/**
|
||||||
const unsigned int expected,
|
* Structure containing information needed for the lifetime of the bindings
|
||||||
const unsigned int actual,
|
* instance. It is not safe to use global static data with worker instances.
|
||||||
const Napi::Env env
|
* Native node addons are loaded as a dll's once no matter how many node
|
||||||
) {
|
* instances are using the library. Each node instance will initialize an
|
||||||
Napi::RangeError::New(
|
* instance of the bindings and workers share memory space. In addition
|
||||||
env,
|
* the worker JS thread will be independent of the main JS thread. Global
|
||||||
"Wrong number of arguments. Expected: "
|
* statics are not thread safe and have the potential for initialization and
|
||||||
+ std::to_string(expected)
|
* clean-up overwrites which results in segfault or undefined behavior.
|
||||||
+ ", received " + std::to_string(actual)
|
*
|
||||||
).ThrowAsJavaScriptException();
|
* An instance of this struct will get created during initialization and it
|
||||||
|
* will be available from the runtime. It can be retrieved via
|
||||||
|
* `napi_get_instance_data` or `Napi::Env::GetInstanceData`.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
bool is_setup;
|
||||||
|
KZGSettings settings;
|
||||||
|
} KzgAddonData;
|
||||||
|
|
||||||
return env.Null();
|
/**
|
||||||
}
|
* This cleanup function follows the `napi_finalize` interface and will be
|
||||||
|
* called by the runtime when the exports object is garbage collected. Is
|
||||||
Napi::Value throw_invalid_argument_type(const Napi::Env env, std::string name, std::string expectedType) {
|
* passed with napi_set_instance_data call when data is set.
|
||||||
Napi::TypeError::New(
|
*
|
||||||
env,
|
* @remark This function should not be called, only the runtime should do
|
||||||
"Invalid argument type: " + name + ". Expected " + expectedType
|
* the cleanup.
|
||||||
).ThrowAsJavaScriptException();
|
*
|
||||||
|
* @param[in] env (unused)
|
||||||
return env.Null();
|
* @param[in] data Pointer KzgAddonData stored by the runtime
|
||||||
|
* @param[in] hint (unused)
|
||||||
|
*/
|
||||||
|
void delete_kzg_addon_data(napi_env /*env*/, void *data, void* /*hint*/) {
|
||||||
|
if (((KzgAddonData*)data)->is_setup) {
|
||||||
|
free_trusted_setup(&((KzgAddonData*)data)->settings);
|
||||||
|
}
|
||||||
|
free(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get kzg_settings from a Napi::External
|
* Get kzg_settings from bindings instance data
|
||||||
*
|
*
|
||||||
* Checks for:
|
* Checks for:
|
||||||
* - arg IsExternal
|
* - loadTrustedSetup has been run
|
||||||
*
|
|
||||||
* Built to pass in a raw Napi::Value so it can be used like
|
|
||||||
* `get_kzg_settings(env, info[0])`.
|
|
||||||
*
|
*
|
||||||
* Designed to raise the correct javascript exception and return a
|
* Designed to raise the correct javascript exception and return a
|
||||||
* valid pointer to the calling context to avoid native stack-frame
|
* valid pointer to the calling context to avoid native stack-frame
|
||||||
* unwinds. Calling context can check for `nullptr` to see if an
|
* unwinds. Calling context can check for `nullptr` to see if an
|
||||||
* exception was raised or a valid pointer was returned from V8.
|
* exception was raised or a valid KZGSettings was returned.
|
||||||
*
|
*
|
||||||
* @param[in] env Passed from calling context
|
* @param[in] env Passed from calling context
|
||||||
* @param[in] val Napi::Value to validate and get pointer from
|
* @param[in] val Napi::Value to validate and get pointer from
|
||||||
*
|
*
|
||||||
* @return - Pointer to the KZGSettings
|
* @return - Pointer to the KZGSettings
|
||||||
*/
|
*/
|
||||||
KZGSettings *get_kzg_settings(const Napi::Env &env, const Napi::Value &val) {
|
KZGSettings *get_kzg_settings(Napi::Env &env, const Napi::CallbackInfo &info) {
|
||||||
if (!val.IsExternal()) {
|
KzgAddonData *data = env.GetInstanceData<KzgAddonData>();
|
||||||
Napi::TypeError::New(env, "Must pass setupHandle as the last function argument").ThrowAsJavaScriptException();
|
if (!data->is_setup) {
|
||||||
return nullptr;
|
Napi::Error::New(env, "Must run loadTrustedSetup before running any other c-kzg functions").ThrowAsJavaScriptException();
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
return val.As<Napi::External<KZGSettings>>().Data();
|
return &(data->settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -118,62 +130,25 @@ inline Bytes48 *get_bytes48(const Napi::Env &env, const Napi::Value &val, std::s
|
||||||
return reinterpret_cast<Bytes48 *>(get_bytes(env, val, BYTES_PER_COMMITMENT, name));
|
return reinterpret_cast<Bytes48 *>(get_bytes(env, val, BYTES_PER_COMMITMENT, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadTrustedSetup: (filePath: string) => SetupHandle;
|
|
||||||
Napi::Value LoadTrustedSetup(const Napi::CallbackInfo& info) {
|
Napi::Value LoadTrustedSetup(const Napi::CallbackInfo& info) {
|
||||||
auto env = info.Env();
|
Napi::Env env = info.Env();
|
||||||
|
KzgAddonData *data = env.GetInstanceData<KzgAddonData>();
|
||||||
size_t argument_count = info.Length();
|
if (data->is_setup) {
|
||||||
size_t expected_argument_count = 1;
|
Napi::Error::New(env, "kzg bindings are already setup").ThrowAsJavaScriptException();
|
||||||
if (argument_count != expected_argument_count) {
|
return env.Undefined();
|
||||||
return throw_invalid_arguments_count(expected_argument_count, argument_count, env);
|
|
||||||
}
|
}
|
||||||
|
// the validation checks for this happen in JS
|
||||||
if (!info[0].IsString()) {
|
const std::string file_path = info[0].As<Napi::String>().Utf8Value();
|
||||||
return throw_invalid_argument_type(env, "filePath", "string");
|
FILE *file_handle = fopen(file_path.c_str(), "r");
|
||||||
|
if (file_handle == NULL) {
|
||||||
|
Napi::Error::New(env, "Error opening trusted setup file: " + file_path).ThrowAsJavaScriptException();
|
||||||
|
return env.Undefined();
|
||||||
}
|
}
|
||||||
|
if (load_trusted_setup_file(&(data->settings), file_handle) != C_KZG_OK) {
|
||||||
const std::string file_path = info[0].ToString().Utf8Value();
|
Napi::Error::New(env, "Error loading trusted setup file: " + file_path).ThrowAsJavaScriptException();
|
||||||
|
return env.Undefined();
|
||||||
KZGSettings* kzg_settings = (KZGSettings*)malloc(sizeof(KZGSettings));
|
|
||||||
|
|
||||||
if (kzg_settings == NULL) {
|
|
||||||
Napi::Error::New(env, "Error while allocating memory for KZG settings").ThrowAsJavaScriptException();
|
|
||||||
return env.Null();
|
|
||||||
}
|
}
|
||||||
|
data->is_setup = true;
|
||||||
FILE* f = fopen(file_path.c_str(), "r");
|
|
||||||
|
|
||||||
if (f == NULL) {
|
|
||||||
free(kzg_settings);
|
|
||||||
Napi::Error::New(env, "Error opening trusted setup file: " + file_path).ThrowAsJavaScriptException();
|
|
||||||
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();
|
|
||||||
return env.Null();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Napi::External<KZGSettings>::New(info.Env(), kzg_settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
KZGSettings *kzg_settings = get_kzg_settings(env, info[0]);
|
|
||||||
if (kzg_settings == nullptr) {
|
|
||||||
return env.Null();
|
|
||||||
}
|
|
||||||
|
|
||||||
free_trusted_setup(kzg_settings);
|
|
||||||
free(kzg_settings);
|
|
||||||
return env.Undefined();
|
return env.Undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,16 +163,11 @@ Napi::Value FreeTrustedSetup(const Napi::CallbackInfo& info) {
|
||||||
*/
|
*/
|
||||||
Napi::Value BlobToKzgCommitment(const Napi::CallbackInfo& info) {
|
Napi::Value BlobToKzgCommitment(const Napi::CallbackInfo& info) {
|
||||||
Napi::Env env = info.Env();
|
Napi::Env 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);
|
|
||||||
}
|
|
||||||
Blob *blob = get_blob(env, info[0]);
|
Blob *blob = get_blob(env, info[0]);
|
||||||
if (blob == nullptr) {
|
if (blob == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
}
|
}
|
||||||
KZGSettings *kzg_settings = get_kzg_settings(env, info[1]);
|
KZGSettings *kzg_settings = get_kzg_settings(env, info);
|
||||||
if (kzg_settings == nullptr) {
|
if (kzg_settings == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
}
|
}
|
||||||
|
@ -225,11 +195,6 @@ Napi::Value BlobToKzgCommitment(const Napi::CallbackInfo& info) {
|
||||||
*/
|
*/
|
||||||
Napi::Value ComputeKzgProof(const Napi::CallbackInfo& info) {
|
Napi::Value ComputeKzgProof(const Napi::CallbackInfo& info) {
|
||||||
Napi::Env env = info.Env();
|
Napi::Env env = info.Env();
|
||||||
size_t argument_count = info.Length();
|
|
||||||
size_t expected_argument_count = 3;
|
|
||||||
if (argument_count != expected_argument_count) {
|
|
||||||
return throw_invalid_arguments_count(expected_argument_count, argument_count, env);
|
|
||||||
}
|
|
||||||
Blob *blob = get_blob(env, info[0]);
|
Blob *blob = get_blob(env, info[0]);
|
||||||
if (blob == nullptr) {
|
if (blob == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
|
@ -238,7 +203,7 @@ Napi::Value ComputeKzgProof(const Napi::CallbackInfo& info) {
|
||||||
if (z_bytes == nullptr) {
|
if (z_bytes == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
}
|
}
|
||||||
KZGSettings *kzg_settings = get_kzg_settings(env, info[2]);
|
KZGSettings *kzg_settings = get_kzg_settings(env, info);
|
||||||
if (kzg_settings == nullptr) {
|
if (kzg_settings == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
}
|
}
|
||||||
|
@ -273,16 +238,11 @@ Napi::Value ComputeKzgProof(const Napi::CallbackInfo& info) {
|
||||||
*/
|
*/
|
||||||
Napi::Value ComputeBlobKzgProof(const Napi::CallbackInfo& info) {
|
Napi::Value ComputeBlobKzgProof(const Napi::CallbackInfo& info) {
|
||||||
Napi::Env env = info.Env();
|
Napi::Env 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);
|
|
||||||
}
|
|
||||||
Blob *blob = get_blob(env, info[0]);
|
Blob *blob = get_blob(env, info[0]);
|
||||||
if (blob == nullptr) {
|
if (blob == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
}
|
}
|
||||||
KZGSettings *kzg_settings = get_kzg_settings(env, info[1]);
|
KZGSettings *kzg_settings = get_kzg_settings(env, info);
|
||||||
if (kzg_settings == nullptr) {
|
if (kzg_settings == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
}
|
}
|
||||||
|
@ -317,11 +277,6 @@ Napi::Value ComputeBlobKzgProof(const Napi::CallbackInfo& info) {
|
||||||
*/
|
*/
|
||||||
Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) {
|
Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) {
|
||||||
Napi::Env env = info.Env();
|
Napi::Env 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);
|
|
||||||
}
|
|
||||||
Bytes48 *commitment_bytes = get_bytes48(env, info[0], "commitmentBytes");
|
Bytes48 *commitment_bytes = get_bytes48(env, info[0], "commitmentBytes");
|
||||||
if (commitment_bytes == nullptr) {
|
if (commitment_bytes == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
|
@ -338,7 +293,7 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) {
|
||||||
if (proof_bytes == nullptr) {
|
if (proof_bytes == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
}
|
}
|
||||||
KZGSettings *kzg_settings = get_kzg_settings(env, info[4]);
|
KZGSettings *kzg_settings = get_kzg_settings(env, info);
|
||||||
if (kzg_settings == nullptr) {
|
if (kzg_settings == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
}
|
}
|
||||||
|
@ -375,11 +330,6 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) {
|
||||||
*/
|
*/
|
||||||
Napi::Value VerifyBlobKzgProof(const Napi::CallbackInfo& info) {
|
Napi::Value VerifyBlobKzgProof(const Napi::CallbackInfo& info) {
|
||||||
Napi::Env env = info.Env();
|
Napi::Env 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);
|
|
||||||
}
|
|
||||||
Blob *blob_bytes = get_blob(env, info[0]);
|
Blob *blob_bytes = get_blob(env, info[0]);
|
||||||
if (blob_bytes == nullptr) {
|
if (blob_bytes == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
|
@ -392,7 +342,7 @@ Napi::Value VerifyBlobKzgProof(const Napi::CallbackInfo& info) {
|
||||||
if (proof_bytes == nullptr) {
|
if (proof_bytes == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
}
|
}
|
||||||
KZGSettings *kzg_settings = get_kzg_settings(env, info[3]);
|
KZGSettings *kzg_settings = get_kzg_settings(env, info);
|
||||||
if (kzg_settings == nullptr) {
|
if (kzg_settings == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
}
|
}
|
||||||
|
@ -429,11 +379,6 @@ Napi::Value VerifyBlobKzgProof(const Napi::CallbackInfo& info) {
|
||||||
*/
|
*/
|
||||||
Napi::Value VerifyBlobKzgProofBatch(const Napi::CallbackInfo& info) {
|
Napi::Value VerifyBlobKzgProofBatch(const Napi::CallbackInfo& info) {
|
||||||
Napi::Env env = info.Env();
|
Napi::Env 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);
|
|
||||||
}
|
|
||||||
C_KZG_RET ret;
|
C_KZG_RET ret;
|
||||||
Blob *blobs = NULL;
|
Blob *blobs = NULL;
|
||||||
Bytes48 *commitments = NULL;
|
Bytes48 *commitments = NULL;
|
||||||
|
@ -446,7 +391,7 @@ Napi::Value VerifyBlobKzgProofBatch(const Napi::CallbackInfo& info) {
|
||||||
Napi::Array blobs_param = info[0].As<Napi::Array>();
|
Napi::Array blobs_param = info[0].As<Napi::Array>();
|
||||||
Napi::Array commitments_param = info[1].As<Napi::Array>();
|
Napi::Array commitments_param = info[1].As<Napi::Array>();
|
||||||
Napi::Array proofs_param = info[2].As<Napi::Array>();
|
Napi::Array proofs_param = info[2].As<Napi::Array>();
|
||||||
KZGSettings *kzg_settings = get_kzg_settings(env, info[3]);
|
KZGSettings *kzg_settings = get_kzg_settings(env, info);
|
||||||
if (kzg_settings == nullptr) {
|
if (kzg_settings == nullptr) {
|
||||||
return env.Null();
|
return env.Null();
|
||||||
}
|
}
|
||||||
|
@ -516,16 +461,28 @@ out:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
||||||
|
Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
||||||
|
KzgAddonData* data = (KzgAddonData*)malloc(sizeof(KzgAddonData));
|
||||||
|
if (data == nullptr) {
|
||||||
|
Napi::Error::New(env, "error allocating memory for kzg setup handle").ThrowAsJavaScriptException();
|
||||||
|
return exports;
|
||||||
|
}
|
||||||
|
data->is_setup = false;
|
||||||
|
napi_status status = napi_set_instance_data(env, data, delete_kzg_addon_data, NULL);
|
||||||
|
if (status != napi_ok) {
|
||||||
|
Napi::Error::New(env, "error setting kzg bindings instance data").ThrowAsJavaScriptException();
|
||||||
|
return exports;
|
||||||
|
}
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
exports["loadTrustedSetup"] = Napi::Function::New(env, LoadTrustedSetup);
|
exports["loadTrustedSetup"] = Napi::Function::New(env, LoadTrustedSetup, "setup");
|
||||||
exports["freeTrustedSetup"] = Napi::Function::New(env, FreeTrustedSetup);
|
exports["blobToKzgCommitment"] = Napi::Function::New(env, BlobToKzgCommitment, "blobToKzgCommitment");
|
||||||
exports["blobToKzgCommitment"] = Napi::Function::New(env, BlobToKzgCommitment);
|
exports["computeKzgProof"] = Napi::Function::New(env, ComputeKzgProof, "computeKzgProof");
|
||||||
exports["computeKzgProof"] = Napi::Function::New(env, ComputeKzgProof);
|
exports["computeBlobKzgProof"] = Napi::Function::New(env, ComputeBlobKzgProof, "computeBlobKzgProof");
|
||||||
exports["computeBlobKzgProof"] = Napi::Function::New(env, ComputeBlobKzgProof);
|
exports["verifyKzgProof"] = Napi::Function::New(env, VerifyKzgProof, "verifyKzgProof");
|
||||||
exports["verifyKzgProof"] = Napi::Function::New(env, VerifyKzgProof);
|
exports["verifyBlobKzgProof"] = Napi::Function::New(env, VerifyBlobKzgProof, "verifyBlobKzgProof");
|
||||||
exports["verifyBlobKzgProof"] = Napi::Function::New(env, VerifyBlobKzgProof);
|
exports["verifyBlobKzgProofBatch"] = Napi::Function::New(env, VerifyBlobKzgProofBatch, "verifyBlobKzgProofBatch");
|
||||||
exports["verifyBlobKzgProofBatch"] = Napi::Function::New(env, VerifyBlobKzgProofBatch);
|
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
exports["BYTES_PER_BLOB"] = Napi::Number::New(env, BYTES_PER_BLOB);
|
exports["BYTES_PER_BLOB"] = Napi::Number::New(env, BYTES_PER_BLOB);
|
||||||
|
@ -533,8 +490,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
||||||
exports["BYTES_PER_FIELD_ELEMENT"] = Napi::Number::New(env, BYTES_PER_FIELD_ELEMENT);
|
exports["BYTES_PER_FIELD_ELEMENT"] = Napi::Number::New(env, BYTES_PER_FIELD_ELEMENT);
|
||||||
exports["BYTES_PER_PROOF"] = Napi::Number::New(env, BYTES_PER_PROOF);
|
exports["BYTES_PER_PROOF"] = Napi::Number::New(env, BYTES_PER_PROOF);
|
||||||
exports["FIELD_ELEMENTS_PER_BLOB"] = Napi::Number::New(env, FIELD_ELEMENTS_PER_BLOB);
|
exports["FIELD_ELEMENTS_PER_BLOB"] = Napi::Number::New(env, FIELD_ELEMENTS_PER_BLOB);
|
||||||
|
|
||||||
return exports;
|
return exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
NODE_API_MODULE(addon, Init)
|
NODE_API_MODULE(addon, Init)
|
|
@ -3,67 +3,55 @@
|
||||||
* https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#kzg
|
* https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#kzg
|
||||||
*/
|
*/
|
||||||
const kzg: KZG = require("./kzg.node");
|
const kzg: KZG = require("./kzg.node");
|
||||||
const fs = require("fs");
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
|
||||||
export type Bytes32 = Uint8Array; // 32 bytes
|
export type Bytes32 = Uint8Array; // 32 bytes
|
||||||
export type Bytes48 = Uint8Array; // 48 bytes
|
export type Bytes48 = Uint8Array; // 48 bytes
|
||||||
export type KZGProof = Buffer; // 48 bytes
|
export type KZGProof = Buffer; // 48 bytes
|
||||||
export type KZGCommitment = Buffer; // 48 bytes
|
export type KZGCommitment = Buffer; // 48 bytes
|
||||||
export type Blob = Uint8Array; // 4096 * 32 bytes
|
export type Blob = Uint8Array; // 4096 * 32 bytes
|
||||||
|
export interface TrustedSetupJson {
|
||||||
type SetupHandle = Object;
|
setup_G1: string[];
|
||||||
|
setup_G2: string[];
|
||||||
|
setup_G1_lagrange: string[];
|
||||||
|
roots_of_unity: string[];
|
||||||
|
}
|
||||||
// The C++ native addon interface
|
// The C++ native addon interface
|
||||||
type KZG = {
|
interface KZG {
|
||||||
BYTES_PER_BLOB: number;
|
BYTES_PER_BLOB: number;
|
||||||
BYTES_PER_COMMITMENT: number;
|
BYTES_PER_COMMITMENT: number;
|
||||||
BYTES_PER_FIELD_ELEMENT: number;
|
BYTES_PER_FIELD_ELEMENT: number;
|
||||||
BYTES_PER_PROOF: number;
|
BYTES_PER_PROOF: number;
|
||||||
FIELD_ELEMENTS_PER_BLOB: number;
|
FIELD_ELEMENTS_PER_BLOB: number;
|
||||||
|
|
||||||
loadTrustedSetup: (filePath: string) => SetupHandle;
|
loadTrustedSetup: (filePath: string) => void;
|
||||||
|
|
||||||
freeTrustedSetup: (setupHandle: SetupHandle) => void;
|
blobToKzgCommitment: (blob: Blob) => KZGCommitment;
|
||||||
|
|
||||||
blobToKzgCommitment: (blob: Blob, setupHandle: SetupHandle) => KZGCommitment;
|
computeKzgProof: (blob: Blob, zBytes: Bytes32) => KZGProof;
|
||||||
|
|
||||||
computeKzgProof: (
|
computeBlobKzgProof: (blob: Blob) => KZGProof;
|
||||||
blob: Blob,
|
|
||||||
zBytes: Bytes32,
|
|
||||||
setupHandle: SetupHandle,
|
|
||||||
) => KZGProof;
|
|
||||||
|
|
||||||
computeBlobKzgProof: (blob: Blob, setupHandle: SetupHandle) => KZGProof;
|
|
||||||
|
|
||||||
verifyKzgProof: (
|
verifyKzgProof: (
|
||||||
commitmentBytes: Bytes48,
|
commitmentBytes: Bytes48,
|
||||||
zBytes: Bytes32,
|
zBytes: Bytes32,
|
||||||
yBytes: Bytes32,
|
yBytes: Bytes32,
|
||||||
proofBytes: Bytes48,
|
proofBytes: Bytes48,
|
||||||
setupHandle: SetupHandle,
|
|
||||||
) => boolean;
|
) => boolean;
|
||||||
|
|
||||||
verifyBlobKzgProof: (
|
verifyBlobKzgProof: (
|
||||||
blob: Blob,
|
blob: Blob,
|
||||||
commitmentBytes: Bytes48,
|
commitmentBytes: Bytes48,
|
||||||
proofBytes: Bytes48,
|
proofBytes: Bytes48,
|
||||||
setupHandle: SetupHandle,
|
|
||||||
) => boolean;
|
) => boolean;
|
||||||
|
|
||||||
verifyBlobKzgProofBatch: (
|
verifyBlobKzgProofBatch: (
|
||||||
blobs: Blob[],
|
blobs: Blob[],
|
||||||
commitmentsBytes: Bytes48[],
|
commitmentsBytes: Bytes48[],
|
||||||
proofsBytes: Bytes48[],
|
proofsBytes: Bytes48[],
|
||||||
setupHandle: SetupHandle,
|
|
||||||
) => boolean;
|
) => boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
type TrustedSetupJSON = {
|
|
||||||
setup_G1: string[];
|
|
||||||
setup_G2: string[];
|
|
||||||
setup_G1_lagrange: string[];
|
|
||||||
roots_of_unity: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const BYTES_PER_BLOB = kzg.BYTES_PER_BLOB;
|
export const BYTES_PER_BLOB = kzg.BYTES_PER_BLOB;
|
||||||
export const BYTES_PER_COMMITMENT = kzg.BYTES_PER_COMMITMENT;
|
export const BYTES_PER_COMMITMENT = kzg.BYTES_PER_COMMITMENT;
|
||||||
|
@ -71,55 +59,58 @@ export const BYTES_PER_FIELD_ELEMENT = kzg.BYTES_PER_FIELD_ELEMENT;
|
||||||
export const BYTES_PER_PROOF = kzg.BYTES_PER_PROOF;
|
export const BYTES_PER_PROOF = kzg.BYTES_PER_PROOF;
|
||||||
export const FIELD_ELEMENTS_PER_BLOB = kzg.FIELD_ELEMENTS_PER_BLOB;
|
export const FIELD_ELEMENTS_PER_BLOB = kzg.FIELD_ELEMENTS_PER_BLOB;
|
||||||
|
|
||||||
// Stored as internal state
|
/**
|
||||||
let setupHandle: SetupHandle | undefined;
|
* Converts JSON formatted trusted setup into the native format that
|
||||||
|
* the native library requires. Returns the absolute file path to the
|
||||||
function requireSetupHandle(): SetupHandle {
|
* the formatted file. The path will be the same as the origin
|
||||||
if (!setupHandle) {
|
* file but with a ".txt" extension.
|
||||||
throw new Error("You must call loadTrustedSetup to initialize KZG.");
|
*
|
||||||
}
|
* @param {string} filePath - The absolute path of JSON formatted trusted setup
|
||||||
return setupHandle;
|
*
|
||||||
}
|
* @return {string} - The absolute path of the re-formatted trusted setup
|
||||||
|
*
|
||||||
export async function transformTrustedSetupJSON(
|
* @throws {Error} - For invalid file operations
|
||||||
filePath: string,
|
*/
|
||||||
): Promise<string> {
|
function transformTrustedSetupJson(filePath: string): string {
|
||||||
const data: TrustedSetupJSON = JSON.parse(fs.readFileSync(filePath));
|
const data: TrustedSetupJson = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
||||||
|
const textFilePath = filePath.replace(".json", ".txt");
|
||||||
const textFilePath = filePath.replace(".json", "") + ".txt";
|
const setupText =
|
||||||
|
kzg.FIELD_ELEMENTS_PER_BLOB +
|
||||||
try {
|
"\n65\n" +
|
||||||
fs.unlinkSync(textFilePath);
|
data.setup_G1.map((p) => p.substring(2)).join("\n") +
|
||||||
} catch {}
|
"\n" +
|
||||||
|
data.setup_G2.map((p) => p.substring(2)).join("\n");
|
||||||
const file = fs.createWriteStream(textFilePath);
|
fs.writeFileSync(textFilePath, setupText);
|
||||||
file.write(`${FIELD_ELEMENTS_PER_BLOB}\n65\n`);
|
|
||||||
file.write(data.setup_G1.map((p) => p.replace("0x", "")).join("\n"));
|
|
||||||
file.write("\n");
|
|
||||||
file.write(data.setup_G2.map((p) => p.replace("0x", "")).join("\n"));
|
|
||||||
file.end();
|
|
||||||
|
|
||||||
const p = new Promise((resolve) => {
|
|
||||||
file.close(resolve);
|
|
||||||
});
|
|
||||||
|
|
||||||
await p;
|
|
||||||
return textFilePath;
|
return textFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the c-kzg library. Pass in a properly formatted trusted setup file
|
||||||
|
* to configure the library. File must be in json format, see or {@link TrustedSetupJson}
|
||||||
|
* interface for more details, or as a properly formatted utf-8 encoded file.
|
||||||
|
*
|
||||||
|
* @remark This function must be run before any other functions in this
|
||||||
|
* library can be run.
|
||||||
|
*
|
||||||
|
* @param {string} filePath - The absolute path of the trusted setup
|
||||||
|
*
|
||||||
|
* @return {void}
|
||||||
|
*
|
||||||
|
* @throws {Error} - For invalid file operations
|
||||||
|
*/
|
||||||
export function loadTrustedSetup(filePath: string): void {
|
export function loadTrustedSetup(filePath: string): void {
|
||||||
if (setupHandle) {
|
if (!(filePath && typeof filePath === "string")) {
|
||||||
throw new Error(
|
throw new TypeError(
|
||||||
"Call freeTrustedSetup before loading a new trusted setup.",
|
"must initialize kzg with the filePath to a txt/json trusted setup",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
setupHandle = kzg.loadTrustedSetup(filePath);
|
throw new Error(`no trusted setup found: ${filePath}`);
|
||||||
}
|
}
|
||||||
|
if (path.parse(filePath).ext === ".json") {
|
||||||
export function freeTrustedSetup(): void {
|
filePath = transformTrustedSetupJson(filePath);
|
||||||
kzg.freeTrustedSetup(requireSetupHandle());
|
}
|
||||||
setupHandle = undefined;
|
return kzg.loadTrustedSetup(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,7 +123,7 @@ export function freeTrustedSetup(): void {
|
||||||
* @throws {TypeError} - For invalid arguments or failure of the native library
|
* @throws {TypeError} - For invalid arguments or failure of the native library
|
||||||
*/
|
*/
|
||||||
export function blobToKzgCommitment(blob: Blob): KZGCommitment {
|
export function blobToKzgCommitment(blob: Blob): KZGCommitment {
|
||||||
return kzg.blobToKzgCommitment(blob, requireSetupHandle());
|
return kzg.blobToKzgCommitment(blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -146,7 +137,7 @@ export function blobToKzgCommitment(blob: Blob): KZGCommitment {
|
||||||
* @throws {TypeError} - For invalid arguments or failure of the native library
|
* @throws {TypeError} - For invalid arguments or failure of the native library
|
||||||
*/
|
*/
|
||||||
export function computeKzgProof(blob: Blob, zBytes: Bytes32): KZGProof {
|
export function computeKzgProof(blob: Blob, zBytes: Bytes32): KZGProof {
|
||||||
return kzg.computeKzgProof(blob, zBytes, requireSetupHandle());
|
return kzg.computeKzgProof(blob, zBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,7 +151,7 @@ export function computeKzgProof(blob: Blob, zBytes: Bytes32): KZGProof {
|
||||||
* @throws {TypeError} - For invalid arguments or failure of the native library
|
* @throws {TypeError} - For invalid arguments or failure of the native library
|
||||||
*/
|
*/
|
||||||
export function computeBlobKzgProof(blob: Blob): KZGProof {
|
export function computeBlobKzgProof(blob: Blob): KZGProof {
|
||||||
return kzg.computeBlobKzgProof(blob, requireSetupHandle());
|
return kzg.computeBlobKzgProof(blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -181,13 +172,7 @@ export function verifyKzgProof(
|
||||||
yBytes: Bytes32,
|
yBytes: Bytes32,
|
||||||
proofBytes: Bytes48,
|
proofBytes: Bytes48,
|
||||||
): boolean {
|
): boolean {
|
||||||
return kzg.verifyKzgProof(
|
return kzg.verifyKzgProof(commitmentBytes, zBytes, yBytes, proofBytes);
|
||||||
commitmentBytes,
|
|
||||||
zBytes,
|
|
||||||
yBytes,
|
|
||||||
proofBytes,
|
|
||||||
requireSetupHandle(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -207,12 +192,7 @@ export function verifyBlobKzgProof(
|
||||||
commitmentBytes: Bytes48,
|
commitmentBytes: Bytes48,
|
||||||
proofBytes: Bytes48,
|
proofBytes: Bytes48,
|
||||||
): boolean {
|
): boolean {
|
||||||
return kzg.verifyBlobKzgProof(
|
return kzg.verifyBlobKzgProof(blob, commitmentBytes, proofBytes);
|
||||||
blob,
|
|
||||||
commitmentBytes,
|
|
||||||
proofBytes,
|
|
||||||
requireSetupHandle(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -234,10 +214,5 @@ export function verifyBlobKzgProofBatch(
|
||||||
commitmentsBytes: Bytes48[],
|
commitmentsBytes: Bytes48[],
|
||||||
proofsBytes: Bytes48[],
|
proofsBytes: Bytes48[],
|
||||||
): boolean {
|
): boolean {
|
||||||
return kzg.verifyBlobKzgProofBatch(
|
return kzg.verifyBlobKzgProofBatch(blobs, commitmentsBytes, proofsBytes);
|
||||||
blobs,
|
|
||||||
commitmentsBytes,
|
|
||||||
proofsBytes,
|
|
||||||
requireSetupHandle(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ const yaml = require("js-yaml");
|
||||||
|
|
||||||
import {
|
import {
|
||||||
loadTrustedSetup,
|
loadTrustedSetup,
|
||||||
freeTrustedSetup,
|
|
||||||
blobToKzgCommitment,
|
blobToKzgCommitment,
|
||||||
computeKzgProof,
|
computeKzgProof,
|
||||||
computeBlobKzgProof,
|
computeBlobKzgProof,
|
||||||
|
@ -18,7 +17,6 @@ import {
|
||||||
BYTES_PER_COMMITMENT,
|
BYTES_PER_COMMITMENT,
|
||||||
BYTES_PER_PROOF,
|
BYTES_PER_PROOF,
|
||||||
BYTES_PER_FIELD_ELEMENT,
|
BYTES_PER_FIELD_ELEMENT,
|
||||||
transformTrustedSetupJSON,
|
|
||||||
} from "./kzg";
|
} from "./kzg";
|
||||||
|
|
||||||
const setupFileName = "testing_trusted_setups.json";
|
const setupFileName = "testing_trusted_setups.json";
|
||||||
|
@ -79,12 +77,7 @@ function bytesFromHex(hexString: string): Buffer {
|
||||||
|
|
||||||
describe("C-KZG", () => {
|
describe("C-KZG", () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const file = await transformTrustedSetupJSON(SETUP_FILE_PATH);
|
loadTrustedSetup(SETUP_FILE_PATH);
|
||||||
loadTrustedSetup(file);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
freeTrustedSetup();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("reference tests should pass", () => {
|
describe("reference tests should pass", () => {
|
||||||
|
|
Loading…
Reference in New Issue