Multi-platform nodejs bindings (#242)

This commit is contained in:
Justin Traglia 2023-03-28 11:01:07 -05:00 committed by GitHub
parent 3c6b9346b3
commit c5920c4ef4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 162 additions and 166 deletions

View File

@ -8,13 +8,28 @@ on:
- main - main
jobs: jobs:
tests: test:
runs-on: ubuntu-latest runs-on: ${{matrix.os}}
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
node:
- 16
- 18
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
- name: Setup - name: Setup Node.js ${{matrix.node}}
run: cd src && make blst uses: actions/setup-node@v1
- name: Test with:
run: cd bindings/node.js && make build test node-version: ${{matrix.node}}
- name: Build/test bindings
working-directory: bindings/node.js
run: make
- name: Install distribution
working-directory: bindings/node.js/dist
run: npm install

View File

@ -24,22 +24,22 @@ make # Build bindings and verify build worked
## Project Commands ## Project Commands
* `make clean` - cleans artifacts - `make clean` - cleans artifacts
* `make build` - prepares assets and builds bindings - `make build` - prepares assets and builds bindings
* `make test` - runs unit tests - `make test` - runs unit tests
* `make format` - lints code - `make format` - lints code
* `make bundle` - builds `dist` for publishing - `make bundle` - builds `dist` for publishing
* `make publish` - runs `npm publish` - `make publish` - runs `npm publish`
## `n-api` and `node-addon-api` ## `n-api` and `node-addon-api`
There are two different flavors of abi-stable node addons. There are two different flavors of abi-stable node addons.
[n-api](https://nodejs.org/api/n-api.html) is the `C` api that is natively [n-api](https://nodejs.org/api/n-api.html) is the `C` api that is natively
exported by `node.js`. There is also a header-only `C++` implementation of the exported by `node.js`. There is also a header-only `C++` implementation of the
`n-api` called [node-addon-api](https://github.com/nodejs/node-addon-api). `n-api` called [node-addon-api](https://github.com/nodejs/node-addon-api).
There is mixed usage of the two in this library. There is mixed usage of the two in this library.
The addon was built to be The addon was built to be
[context-aware](https://nodejs.github.io/node-addon-examples/special-topics/context-awareness/) [context-aware](https://nodejs.github.io/node-addon-examples/special-topics/context-awareness/)
so it will be safe to run on a worker thread. Be sure not to use any so it will be safe to run on a worker thread. Be sure not to use any
static/global variables as those are not thread safe. static/global variables as those are not thread safe.

View File

@ -1,23 +1,12 @@
# Exists as a test harness for building and running tests in Linux
FROM node:16-alpine FROM node:16-alpine
RUN apk update && apk add --no-cache g++ make python3 RUN apk update && apk add --no-cache g++ make python3
WORKDIR /app/bindings/node.js
COPY ./dist/ /app/dist/ COPY dist .
COPY test.ts /app
COPY testing_trusted_setups.json /app
COPY kzg.ts /app
COPY kzg.cxx /app
COPY package.json /app
COPY tsconfig.json /app
COPY babel.config.js /app
COPY jest.config.js /app
COPY binding.dist.gyp /app/binding.gyp
WORKDIR /app
RUN yarn install RUN yarn install
COPY test ./test
COPY jest.config.js .
COPY ref-tests ../../tests
CMD ["yarn", "jest"] CMD ["yarn", "jest"]

View File

@ -1,40 +1,78 @@
all: clean build format test bundle # For a less verbose yarn.
YARN = yarn --silent
.PHONY: all
all: format build test bundle
# Cleans native dependency, bindings and typescript artifacts
.PHONY: clean
clean: clean:
rm -rf build @rm -rf build
rm -rf dist @rm -rf dist
rm -f *.node @rm -rf deps
rm -f *.a @rm -f *.node
rm -f *.o @rm -f *.a
rm -rf node_modules @rm -f *.o
@rm -rf ref-tests
build: src/kzg.cxx lib/kzg.ts package.json binding.gyp Makefile # Cleans typescript dependencies
cd ../../src && make c_kzg_4844.o && cp c_kzg_4844.o ../bindings/node.js .PHONY: clean-install
yarn install clean-install:
yarn node-gyp rebuild @rm -rf node_modules
test: build # Installs typescript dependencies
yarn jest .PHONY: install
install:
@$(YARN) install --ignore-scripts
format: # Cleans and rebuilds native dependencies, bindings and typescript wrapper
yarn prettier --write . .PHONY: build
build: install clean
@# Prepare the dependencies directory
@mkdir -p deps/c-kzg
@cp -r ../../blst deps
@cp ../../src/c_kzg_4844.c deps/c-kzg
@cp ../../src/c_kzg_4844.h deps/c-kzg
@# Patch the blst_aux.h to fix a compiler error
@awk '{gsub(/typedef struct \{\} blst_uniq;/, "typedef struct { int dummy; } blst_uniq;")}1' \
deps/blst/bindings/blst_aux.h > blst_aux_temp.h
@mv blst_aux_temp.h deps/blst/bindings/blst_aux.h
@# Build the bindings
@$(YARN) node-gyp --loglevel=warn configure
@$(YARN) node-gyp --loglevel=warn build
@$(YARN) tsc -p tsconfig.build.json
bundle: clean # Bundle the distribution, also helpful for cross compatibility checks
mkdir dist .PHONY: bundle
cp README.md dist/README.md bundle: build
cp package.json dist/package.json @mv deps dist
cp binding.dist.gyp dist/binding.gyp @cp -r src dist/src
node_modules/.bin/tsc -p tsconfig.build.json @cp README.md dist/README.md
cp -r src dist/src @cp package.json dist/package.json
mkdir -p dist/deps/c-kzg @cp binding.gyp dist/binding.gyp
cp -r ../../blst dist/deps
cp ../../src/c_kzg_4844.c dist/deps/c-kzg
cp ../../src/c_kzg_4844.h dist/deps/c-kzg
publish: bundle # Run unit tests and ref-tests
cd dist .PHONY: test
npm publish test: install
@echo
@$(YARN) jest
linux-test: bundle # Lint js/ts code
docker build -t "linux-test" . .PHONY: format
docker logs --follow `docker run -d linux-test` format: install
@$(YARN) prettier --loglevel=warn --write .
# Publish package to npm (requires an auth token)
.PHONY: publish
publish: build
@cd dist
@npm publish
# Run ref-tests in linux environment for cross-compatability check
.PHONY: linux-test
linux-test: build
# Docker cannot copy from outside this dir
@cp -r ../../tests ref-tests
@docker build -t "linux-test" .
@docker logs --follow `docker run -d linux-test`
@rm -rf ref-tests

View File

@ -1,52 +0,0 @@
{
"targets": [
{
"target_name": "kzg",
"cflags!": ["-fno-exceptions"],
"cflags_cc!": ["-fno-exceptions"],
"xcode_settings": {
"CLANG_CXX_LIBRARY": "libc++",
"MACOSX_DEPLOYMENT_TARGET": "13.0"
},
"defines": [
"NAPI_DISABLE_CPP_EXCEPTIONS",
"FIELD_ELEMENTS_PER_BLOB=<!(echo ${FIELD_ELEMENTS_PER_BLOB:-4096})"
],
"sources": ["src/kzg.cxx"],
"include_dirs": [
"<(module_root_dir)/deps/blst/bindings",
"<(module_root_dir)/deps/c-kzg",
"<!@(node -p \"require('node-addon-api').include\")"
],
"libraries": [
"<(module_root_dir)/c_kzg_4844.o",
"<(module_root_dir)/libblst.a"
],
"dependencies": ["<!(node -p \"require('node-addon-api').gyp\")"],
"actions": [
{
"action_name": "build_blst",
"inputs": ["<(module_root_dir)/deps/blst/build.sh"],
"outputs": ["<(module_root_dir)/libblst.a"],
"action": ["<(module_root_dir)/deps/blst/build.sh"]
},
{
"action_name": "build_ckzg",
"inputs": [
"<(module_root_dir)/deps/c-kzg/c_kzg_4844.c",
"<(module_root_dir)/libblst.a"
],
"outputs": ["<(module_root_dir)/c_kzg_4844.o"],
"action": [
"cc",
"-I<(module_root_dir)/deps/blst/bindings",
"-DFIELD_ELEMENTS_PER_BLOB=<!(echo ${FIELD_ELEMENTS_PER_BLOB:-4096})",
"-O2",
"-c",
"<(module_root_dir)/deps/c-kzg/c_kzg_4844.c"
]
}
]
}
]
}

View File

@ -2,27 +2,45 @@
"targets": [ "targets": [
{ {
"target_name": "kzg", "target_name": "kzg",
"cflags!": ["-fno-exceptions"], "sources": [
"cflags_cc!": ["-fno-exceptions"], "src/kzg.cxx",
"xcode_settings": { "deps/blst/src/server.c",
"CLANG_CXX_LIBRARY": "libc++", "deps/c-kzg/c_kzg_4844.c"
"MACOSX_DEPLOYMENT_TARGET": "13.0"
},
"defines": [
"NAPI_DISABLE_CPP_EXCEPTIONS",
"FIELD_ELEMENTS_PER_BLOB=<!(echo ${FIELD_ELEMENTS_PER_BLOB:-4096})"
], ],
"sources": ["src/kzg.cxx"],
"include_dirs": [ "include_dirs": [
"../../inc", "<(module_root_dir)/deps/blst/bindings",
"../../src", "<(module_root_dir)/deps/c-kzg",
"<!@(node -p \"require('node-addon-api').include\")" "<!@(node -p \"require('node-addon-api').include\")"
], ],
"libraries": [ "defines": ["NAPI_DISABLE_CPP_EXCEPTIONS"],
"<(module_root_dir)/c_kzg_4844.o", "conditions": [
"<(module_root_dir)/../../lib/libblst.a" ["OS!='win'", {
], "sources": ["deps/blst/build/assembly.S"],
"dependencies": ["<!(node -p \"require('node-addon-api').gyp\")"] "defines": ["FIELD_ELEMENTS_PER_BLOB=<!(echo ${FIELD_ELEMENTS_PER_BLOB:-4096})"],
"cflags_cc": [
"-std=c++17",
"-fPIC"
]
}],
["OS=='win'", {
"sources": ["deps/blst/build/win64/*-x86_64.asm"],
"defines": [
"_CRT_SECURE_NO_WARNINGS",
"FIELD_ELEMENTS_PER_BLOB=<!(powershell -Command \"if ($env:FIELD_ELEMENTS_PER_BLOB) { $env:FIELD_ELEMENTS_PER_BLOB } else { 4096 }\")"
],
"msbuild_settings": {
"ClCompile": {
"AdditionalOptions": ["/std:c++17"]
}
}
}],
["OS=='mac'", {
"xcode_settings": {
"CLANG_CXX_LIBRARY": "libc++",
"MACOSX_DEPLOYMENT_TARGET": "13.0"
}
}]
]
} }
] ]
} }

View File

@ -36,46 +36,35 @@ const SETUP_FILE_PATH = resolve(
const MAX_TOP_BYTE = 114; const MAX_TOP_BYTE = 114;
const TEST_DIR = "../../tests"; const BLOB_TO_KZG_COMMITMENT_TESTS =
"../../tests/blob_to_kzg_commitment/*/*/data.yaml";
const COMPUTE_KZG_PROOF_TESTS = "../../tests/compute_kzg_proof/*/*/data.yaml";
const COMPUTE_BLOB_KZG_PROOF_TESTS =
"../../tests/compute_blob_kzg_proof/*/*/data.yaml";
const VERIFY_KZG_PROOF_TESTS = "../../tests/verify_kzg_proof/*/*/data.yaml";
const VERIFY_BLOB_KZG_PROOF_TESTS =
"../../tests/verify_blob_kzg_proof/*/*/data.yaml";
const VERIFY_BLOB_KZG_PROOF_BATCH_TESTS =
"../../tests/verify_blob_kzg_proof_batch/*/*/data.yaml";
type BlobToKzgCommitmentTest = TestMeta<{ blob: string }, string>; type BlobToKzgCommitmentTest = TestMeta<{ blob: string }, string>;
const BLOB_TO_KZG_COMMITMENT_TESTS = join(
TEST_DIR,
"blob_to_kzg_commitment/*/*/data.yaml",
);
type ComputeKzgProofTest = TestMeta<{ blob: string; z: string }, string[]>; type ComputeKzgProofTest = TestMeta<{ blob: string; z: string }, string[]>;
const COMPUTE_KZG_PROOF_TESTS = join(
TEST_DIR,
"compute_kzg_proof/*/*/data.yaml",
);
type ComputeBlobKzgProofTest = TestMeta< type ComputeBlobKzgProofTest = TestMeta<
{ blob: string; commitment: string }, { blob: string; commitment: string },
string string
>; >;
const COMPUTE_BLOB_KZG_PROOF_TESTS = join(
TEST_DIR,
"compute_blob_kzg_proof/*/*/data.yaml",
);
type VerifyKzgProofTest = TestMeta< type VerifyKzgProofTest = TestMeta<
{ commitment: string; y: string; z: string; proof: string }, { commitment: string; y: string; z: string; proof: string },
boolean boolean
>; >;
const VERIFY_KZG_PROOF_TESTS = join(TEST_DIR, "verify_kzg_proof/*/*/data.yaml");
type VerifyBlobKzgProofTest = TestMeta< type VerifyBlobKzgProofTest = TestMeta<
{ blob: string; commitment: string; proof: string }, { blob: string; commitment: string; proof: string },
boolean boolean
>; >;
const VERIFY_BLOB_KZG_PROOF_TESTS = join(
TEST_DIR,
"verify_blob_kzg_proof/*/*/data.yaml",
);
type VerifyBatchKzgProofTest = TestMeta< type VerifyBatchKzgProofTest = TestMeta<
{ blobs: string[]; commitments: string[]; proofs: string[] }, { blobs: string[]; commitments: string[]; proofs: string[] },
boolean boolean
>; >;
const VERIFY_BLOB_KZG_PROOF_BATCH_TESTS = join(
TEST_DIR,
"verify_blob_kzg_proof_batch/*/*/data.yaml",
);
const generateRandomBlob = () => { const generateRandomBlob = () => {
return new Uint8Array( return new Uint8Array(

View File

@ -64,16 +64,16 @@ static const char *FIAT_SHAMIR_PROTOCOL_DOMAIN = "FSBLOBVERIFY_V1_";
static const char *RANDOM_CHALLENGE_KZG_BATCH_DOMAIN = "RCKZGBATCH___V1_"; static const char *RANDOM_CHALLENGE_KZG_BATCH_DOMAIN = "RCKZGBATCH___V1_";
/** Length of the domain strings above. */ /** Length of the domain strings above. */
static const size_t DOMAIN_STR_LENGTH = 16; #define DOMAIN_STR_LENGTH 16
/** The number of bytes in a g1 point. */ /** The number of bytes in a g1 point. */
static const size_t BYTES_PER_G1 = 48; #define BYTES_PER_G1 48
/** The number of bytes in a g2 point. */ /** The number of bytes in a g2 point. */
static const size_t BYTES_PER_G2 = 96; #define BYTES_PER_G2 96
/** The number of g2 points in a trusted setup. */ /** The number of g2 points in a trusted setup. */
static const size_t TRUSTED_SETUP_NUM_G2_POINTS = 65; #define TRUSTED_SETUP_NUM_G2_POINTS 65
// clang-format off // clang-format off
@ -734,13 +734,12 @@ static C_KZG_RET blob_to_polynomial(Polynomial *p, const Blob *blob) {
} }
/* Input size to the Fiat-Shamir challenge computation. */ /* Input size to the Fiat-Shamir challenge computation. */
static const size_t CHALLENGE_INPUT_SIZE = DOMAIN_STR_LENGTH + #define CHALLENGE_INPUT_SIZE \
sizeof(uint64_t) + sizeof(uint64_t) + (DOMAIN_STR_LENGTH + 16 + BYTES_PER_BLOB + BYTES_PER_COMMITMENT)
BYTES_PER_BLOB +
BYTES_PER_COMMITMENT;
/** /**
* Return the Fiat-Shamir challenge required to verify `blob` and `commitment`. * Return the Fiat-Shamir challenge required to verify `blob` and
* `commitment`.
* *
* @remark This function should compute challenges even if `n==0`. * @remark This function should compute challenges even if `n==0`.
* *

View File

@ -37,16 +37,16 @@ extern "C" {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#ifndef FIELD_ELEMENTS_PER_BLOB #ifndef FIELD_ELEMENTS_PER_BLOB
#error FIELD_ELEMENTS_PER_BLOB is undefined. This value must be externally supplied. #error FIELD_ELEMENTS_PER_BLOB must be defined
#endif // FIELD_ELEMENTS_PER_BLOB #endif // FIELD_ELEMENTS_PER_BLOB
/** /**
* There are only 1<<32 2-adic roots of unity in the field, limiting the * There are only 1<<32 2-adic roots of unity in the field, limiting the
* possible values of FIELD_ELEMENTS_PER_BLOB. The restriction to 1<<31 is a * possible values of FIELD_ELEMENTS_PER_BLOB. The restriction to 1<<31 is a
* current implementation limitation. Notably, the size of the FFT setup would * current implementation limitation. Notably, the size of the FFT setup would
* overflow uint32_t, which would casues issues. * overflow uint32_t, which would cause issues.
*/ */
#if ((FIELD_ELEMENTS_PER_BLOB) <= 0) || ((FIELD_ELEMENTS_PER_BLOB) > (1 << 31)) #if (FIELD_ELEMENTS_PER_BLOB <= 0) || (FIELD_ELEMENTS_PER_BLOB > (1UL << 31))
#error Invalid value of FIELD_ELEMENTS_PER_BLOB #error FIELD_ELEMENTS_PER_BLOB must be between 1 and 2^31
#endif // FIELD_ELEMENTS_PER_BLOB #endif // FIELD_ELEMENTS_PER_BLOB
/** /**