diff --git a/bindings/node.js/.prettierignore b/bindings/node.js/.prettierignore new file mode 100644 index 0000000..342ac64 --- /dev/null +++ b/bindings/node.js/.prettierignore @@ -0,0 +1,2 @@ +# Ignore artifacts: +build diff --git a/bindings/node.js/.prettierrc.json b/bindings/node.js/.prettierrc.json new file mode 100644 index 0000000..f59b41f --- /dev/null +++ b/bindings/node.js/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "overrides": [ + { + "files": "binding.gyp", + "options": { "parser": "json" } + } + ] +} diff --git a/bindings/node.js/Makefile b/bindings/node.js/Makefile index 19efbe9..7311de7 100644 --- a/bindings/node.js/Makefile +++ b/bindings/node.js/Makefile @@ -1,7 +1,3 @@ -install: - brew install llvm - yarn install - clean: yarn clean rm -rf build @@ -9,15 +5,6 @@ clean: rm -f *.a rm -f *.o -# objcopy --globalize-symbol=symbolname -# Give symbol symbolname global scoping so that it is visible -# outside of the file in which it is defined. This option may -# be given more than once. -# c_kzg_4844's static void hash() calls sha256_... which are private -# We use objcopy's globalize-symbol to make them public. -# libblst.a: ../../lib/libblst.a -# $$(brew --prefix llvm)/bin/llvm-objcopy --globalize-symbol=sha256_init --globalize-symbol=sha256_update --globalize-symbol=sha256_final $< $@ - build: kzg.cxx Makefile cd ../../src; make lib yarn build diff --git a/bindings/node.js/README.md b/bindings/node.js/README.md new file mode 100644 index 0000000..6a76169 --- /dev/null +++ b/bindings/node.js/README.md @@ -0,0 +1,19 @@ +This directory contains the code necessary to generate NodeJS bindings for C-KZG. + +First, install: + +``` +yarn install +``` + +Then build + +``` +make build +``` + +Run the TypeScript tests + +``` +yarn test +``` diff --git a/bindings/node.js/babel.config.js b/bindings/node.js/babel.config.js index 8165fe4..dd242dc 100644 --- a/bindings/node.js/babel.config.js +++ b/bindings/node.js/babel.config.js @@ -1,6 +1,6 @@ module.exports = { presets: [ - ['@babel/preset-env', { targets: { node: 'current' } }], - '@babel/preset-typescript', + ["@babel/preset-env", { targets: { node: "current" } }], + "@babel/preset-typescript", ], }; diff --git a/bindings/node.js/binding.gyp b/bindings/node.js/binding.gyp index ff3df33..86f4c29 100644 --- a/bindings/node.js/binding.gyp +++ b/bindings/node.js/binding.gyp @@ -1,27 +1,27 @@ { - 'targets': [ + "targets": [ { - 'target_name': 'kzg', - "cflags!": [ "-fno-exceptions" ], - "cflags_cc!": [ "-fno-exceptions" ], - 'xcode_settings': { - 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', - 'CLANG_CXX_LIBRARY': 'libc++', - 'MACOSX_DEPLOYMENT_TARGET': '10.7' + "target_name": "kzg", + "cflags!": ["-fno-exceptions"], + "cflags_cc!": ["-fno-exceptions"], + "xcode_settings": { + "GCC_ENABLE_CPP_EXCEPTIONS": "YES", + "CLANG_CXX_LIBRARY": "libc++", + "MACOSX_DEPLOYMENT_TARGET": "13.0" }, - 'sources': [ - 'kzg.cxx', + "sources": ["kzg.cxx"], + "include_dirs": [ + "../../inc", + "../../src", + " #include #include -#define NAPI_EXPERIMENTAL +#include // std::ostringstream +#include // std::copy +#include // std::ostream_iterator #include #include "c_kzg_4844.h" #include "blst.h" -#include // std::ostringstream -#include // std::copy -#include // std::ostream_iterator - -Napi::TypedArrayOf napiTypedArrayFromByteArray(uint8_t* array, size_t arrayLength, Napi::Env env) { +Napi::TypedArrayOf napi_typed_array_from_bytes(uint8_t* array, size_t arrayLength, Napi::Env env) { // Create std::vector out of array. // We allocate it on the heap to allow wrapping it up into ArrayBuffer. std::unique_ptr> nativeArray = @@ -75,13 +73,11 @@ Napi::Value LoadTrustedSetup(const Napi::CallbackInfo& info) { return env.Null(); } - // Consider making this internal state intead return Napi::External::New(info.Env(), kzgSettings); } // freeTrustedSetup: (setupHandle: SetupHandle) => void; void FreeTrustedSetup(const Napi::CallbackInfo& info) { - // 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. auto kzgSettings = info[0].As>().Data(); free_trusted_setup(kzgSettings); free(kzgSettings); @@ -109,15 +105,14 @@ Napi::Value BlobToKzgCommitment(const Napi::CallbackInfo& info) { Polynomial polynomial; for (size_t i = 0; i < FIELD_ELEMENTS_PER_BLOB; i++) - bytes_to_bls_field(&polynomial[i], &blob[i * 32]); + bytes_to_bls_field(&polynomial[i], &blob[i * BYTES_PER_FIELD]); KZGCommitment commitment; blob_to_kzg_commitment(&commitment, polynomial, kzgSettings); - // Turn it into a byte array uint8_t commitmentBytes[BYTES_PER_COMMITMENT]; bytes_from_g1(commitmentBytes, &commitment); - return napiTypedArrayFromByteArray(commitmentBytes, BYTES_PER_COMMITMENT, env); + return napi_typed_array_from_bytes(commitmentBytes, BYTES_PER_COMMITMENT, env); } // computeAggregateKzgProof: (blobs: Blob[], setupHandle: SetupHandle) => KZGProof; @@ -134,17 +129,12 @@ Napi::Value ComputeAggregateKzgProof(const Napi::CallbackInfo& info) { auto kzgSettings = info[1].As>().Data(); auto numberOfBlobs = blobs_param.Length(); - - printf("ComputeAggregateKzgProof called with %i blob(s)\n", numberOfBlobs); - auto polynomial = (Polynomial*)calloc(numberOfBlobs, sizeof(Polynomial)); for (uint32_t blobIndex = 0; blobIndex < numberOfBlobs; blobIndex++) { Napi::Value blob = blobs_param[blobIndex]; auto blobBytes = blob.As().Data(); - printf("Iterating blob index: %i\n", blobIndex); - for (uint32_t fieldIndex = 0; fieldIndex < FIELD_ELEMENTS_PER_BLOB; fieldIndex++) { bytes_to_bls_field( &polynomial[blobIndex][fieldIndex], @@ -168,19 +158,9 @@ Napi::Value ComputeAggregateKzgProof(const Napi::CallbackInfo& info) { return env.Undefined(); }; - 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 array[48]; - bytes_from_g1(array, &proof); - - printf("Turned proof into bytes: ["); - for (int i = 0; i < sizeof(array); i++) { - printf("%x ", array[i]); - } - printf("]\n"); - - return napiTypedArrayFromByteArray(array, sizeof(array), env); + uint8_t proofBytes[BYTES_PER_PROOF]; + bytes_from_g1(proofBytes, &proof); + return napi_typed_array_from_bytes(proofBytes, BYTES_PER_PROOF, env); } // verifyAggregateKzgProof: (blobs: Blob[], expectedKzgCommitments: KZGCommitment[], kzgAggregatedProof: KZGProof) => boolean; @@ -242,15 +222,14 @@ Napi::Value VerifyAggregateKzgProof(const Napi::CallbackInfo& info) { } bool verificationResult; - ret = verify_aggregate_kzg_proof( + if (verify_aggregate_kzg_proof( &verificationResult, polynomial, commitments, numberOfBlobs, &proof, kzgSettings - ); - if (ret != C_KZG_OK) { + ) != C_KZG_OK) { free(commitments); free(polynomial); @@ -275,7 +254,6 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) { return env.Null(); } - // const uint8_t c[48] auto c_param = info[0].As(); if (c_param.TypedArrayType() != napi_uint8_array) { Napi::Error::New(env, "Expected an Uint8Array") @@ -284,7 +262,6 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) { } auto c = c_param.As().Data(); - // const uint8_t x[32] auto x_param = info[0].As(); if (x_param.TypedArrayType() != napi_uint8_array) { Napi::Error::New(env, "Expected an Uint8Array") @@ -293,7 +270,6 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) { } auto x = x_param.As().Data(); - // const uint8_t y[32] auto y_param = info[0].As(); if (y_param.TypedArrayType() != napi_uint8_array) { Napi::Error::New(env, "Expected an Uint8Array") @@ -302,7 +278,6 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) { } auto y = y_param.As().Data(); - // const uint8_t p[48] auto p_param = info[0].As(); if (p_param.TypedArrayType() != napi_uint8_array) { Napi::Error::New(info.Env(), "Expected an Uint8Array") @@ -311,7 +286,6 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) { } auto p = p_param.As().Data(); - // KZGSettings *s auto kzgSettings = info[4].As>().Data(); KZGCommitment commitment; @@ -343,14 +317,12 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) { } Napi::Object Init(Napi::Env env, Napi::Object exports) { - exports.Set(Napi::String::New(env, "loadTrustedSetup"), Napi::Function::New(env, LoadTrustedSetup)); - exports.Set(Napi::String::New(env, "freeTrustedSetup"), Napi::Function::New(env, FreeTrustedSetup)); - exports.Set(Napi::String::New(env, "verifyKzgProof"), Napi::Function::New(env, VerifyKzgProof)); - - - exports.Set(Napi::String::New(env, "blobToKzgCommitment"), Napi::Function::New(env, BlobToKzgCommitment)); - exports.Set(Napi::String::New(env, "verifyAggregateKzgProof"), Napi::Function::New(env, VerifyAggregateKzgProof)); - exports.Set(Napi::String::New(env, "computeAggregateKzgProof"), Napi::Function::New(env, ComputeAggregateKzgProof)); + 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); return exports; } diff --git a/bindings/node.js/kzg.ts b/bindings/node.js/kzg.ts index 7238bd0..0162fac 100644 --- a/bindings/node.js/kzg.ts +++ b/bindings/node.js/kzg.ts @@ -1,5 +1,5 @@ // @ts-expect-error -import bindings from 'bindings'; +import bindings from "bindings"; export const BLOB_SIZE = 4096; export const NUMBER_OF_FIELDS = 32; @@ -20,14 +20,14 @@ type KZG = { computeAggregateKzgProof: ( blobs: Blob[], - setupHandle: SetupHandle, + setupHandle: SetupHandle ) => KZGProof; verifyAggregateKzgProof: ( blobs: Blob[], expectedKzgCommitments: KZGCommitment[], kzgAggregatedProof: KZGProof, - setupHandle: SetupHandle, + setupHandle: SetupHandle ) => boolean; verifyKzgProof: ( @@ -35,11 +35,11 @@ type KZG = { z: BLSFieldElement, y: BLSFieldElement, kzgProof: KZGProof, - setupHandle: SetupHandle, + setupHandle: SetupHandle ) => boolean; }; -const kzg: KZG = bindings('kzg.node'); +const kzg: KZG = bindings("kzg.node"); // Stored as internal state let setupHandle: SetupHandle | undefined; @@ -47,7 +47,7 @@ let setupHandle: SetupHandle | undefined; export function loadTrustedSetup(filePath: string) { if (setupHandle) { throw new Error( - 'Call freeTrustedSetup before loading a new trusted setup.', + "Call freeTrustedSetup before loading a new trusted setup." ); } setupHandle = kzg.loadTrustedSetup(filePath); @@ -55,7 +55,7 @@ export function loadTrustedSetup(filePath: string) { export function freeTrustedSetup() { if (!setupHandle) { - throw new Error('You must call loadTrustedSetup before freeTrustedSetup.'); + throw new Error("You must call loadTrustedSetup before freeTrustedSetup."); } kzg.freeTrustedSetup(setupHandle); setupHandle = undefined; @@ -63,14 +63,14 @@ export function freeTrustedSetup() { export function blobToKzgCommitment(blob: Blob) { if (!setupHandle) { - throw new Error('You must call loadTrustedSetup to initialize KZG.'); + 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.'); + throw new Error("You must call loadTrustedSetup to initialize KZG."); } return kzg.computeAggregateKzgProof(blobs, setupHandle); } @@ -82,10 +82,10 @@ export function verifyKzgProof( polynomialKzg: KZGCommitment, z: BLSFieldElement, y: BLSFieldElement, - kzgProof: KZGProof, + kzgProof: KZGProof ) { if (!setupHandle) { - throw new Error('You must call loadTrustedSetup to initialize KZG.'); + throw new Error("You must call loadTrustedSetup to initialize KZG."); } return kzg.verifyKzgProof(polynomialKzg, z, y, kzgProof, setupHandle); } @@ -93,15 +93,15 @@ export function verifyKzgProof( export function verifyAggregateKzgProof( blobs: Blob[], expectedKzgCommitments: KZGCommitment[], - kzgAggregatedProof: KZGProof, + kzgAggregatedProof: KZGProof ) { if (!setupHandle) { - throw new Error('You must call loadTrustedSetup to initialize KZG.'); + throw new Error("You must call loadTrustedSetup to initialize KZG."); } return kzg.verifyAggregateKzgProof( blobs, expectedKzgCommitments, kzgAggregatedProof, - setupHandle, + setupHandle ); } diff --git a/bindings/node.js/package.json b/bindings/node.js/package.json index 0938f44..b6069bc 100644 --- a/bindings/node.js/package.json +++ b/bindings/node.js/package.json @@ -1,11 +1,10 @@ { "name": "c-kzg", "version": "0.0.1", - "description": "", + "description": "NodeJS bindings for C-KZG", "author": "Dan Coffman", "license": "MIT", - "main": "test.ts", - "gypfile": true, + "main": "kzg.ts", "scripts": { "clean": "node-gyp clean", "build": "node-gyp rebuild", @@ -15,12 +14,13 @@ "@babel/preset-typescript": "^7.18.6", "@types/jest": "^29.2.1", "jest": "^29.2.2", + "node-addon-api": "^5.0.0", "node-gyp": "^9.3.0", + "prettier": "2.7.1", "ts-jest": "^29.0.3", "typescript": "^4.8.4" }, "dependencies": { - "bindings": "^1.5.0", - "node-addon-api": "^5.0.0" + "bindings": "^1.5.0" } } diff --git a/bindings/node.js/test.ts b/bindings/node.js/test.ts index 7636887..d7cc143 100644 --- a/bindings/node.js/test.ts +++ b/bindings/node.js/test.ts @@ -1,4 +1,4 @@ -import { randomBytes } from 'crypto'; +import { randomBytes } from "crypto"; import { loadTrustedSetup, freeTrustedSetup, @@ -9,15 +9,15 @@ import { NUMBER_OF_FIELDS, computeAggregateKzgProof, verifyAggregateKzgProof, -} from './kzg'; +} from "./kzg"; -const SETUP_FILE_PATH = '../../src/trusted_setup.txt'; +const SETUP_FILE_PATH = "../../src/trusted_setup.txt"; function generateRandomBlob(): Blob { return new Uint8Array(randomBytes(BLOB_SIZE * NUMBER_OF_FIELDS)); } -describe('C-KZG', () => { +describe("C-KZG", () => { beforeEach(() => { loadTrustedSetup(SETUP_FILE_PATH); }); @@ -26,7 +26,7 @@ describe('C-KZG', () => { freeTrustedSetup(); }); - it('computes and verifies an aggregate KZG proof', async () => { + it("computes and verifies an aggregate KZG proof", async () => { const blob1 = generateRandomBlob(); const blob2 = generateRandomBlob(); const blobs = [blob1, blob2]; diff --git a/bindings/node.js/yarn.lock b/bindings/node.js/yarn.lock index 07ffbb9..78401e8 100644 --- a/bindings/node.js/yarn.lock +++ b/bindings/node.js/yarn.lock @@ -2296,6 +2296,11 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +prettier@2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + pretty-format@^29.0.0, pretty-format@^29.2.1: version "29.2.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.2.1.tgz#86e7748fe8bbc96a6a4e04fa99172630907a9611" diff --git a/src/c_kzg_4844.h b/src/c_kzg_4844.h index 683eb0d..b2b5070 100644 --- a/src/c_kzg_4844.h +++ b/src/c_kzg_4844.h @@ -35,6 +35,7 @@ extern "C" { #endif #define BYTES_PER_COMMITMENT 48 +#define BYTES_PER_PROOF 48 #define BYTES_PER_FIELD 32 #define FIELD_ELEMENTS_PER_BLOB 4096