diff --git a/.gitignore b/.gitignore index 4523091..370b496 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ inc/blst_aux.h* *bindings/csharp/*.exe *bindings/csharp/*.dll __pycache__ +.DS_Store diff --git a/README.md b/README.md index 1f2be84..ec0668e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # C-KZG-4844: A minimal library for EIP-4844 Polynomial Commitments This is a copy of C-KZG stripped down to support the [Polynomial Commitments](https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md) API: + - `compute_aggregate_kzg_proof` - `verify_aggregate_kzg_proof` - `blob_to_kzg_commitment` @@ -9,3 +10,25 @@ This is a copy of C-KZG stripped down to support the [Polynomial Commitments](ht We also provide `load_trusted_setup` and `free_trusted_setup` to load the trusted setup data from a file into an object that can be passed to the API functions, and functions for converting commitments/proofs/points to/from bytes. + +## Installation + +Install the blst submodule + +``` +git submodule update --init +``` + +Build blst + +``` +cd src +make blst +``` + +Build the C-KZG code + +``` +cd src +make +``` diff --git a/bindings/node.js/.gitignore b/bindings/node.js/.gitignore index 6a76a2b..38f45ff 100644 --- a/bindings/node.js/.gitignore +++ b/bindings/node.js/.gitignore @@ -1,3 +1,4 @@ build +dist *.node node_modules diff --git a/bindings/node.js/.npmignore b/bindings/node.js/.npmignore new file mode 100644 index 0000000..415dcf4 --- /dev/null +++ b/bindings/node.js/.npmignore @@ -0,0 +1,20 @@ +.vscode +build +!dist/deps/blst/build +node_modules +.gitignore +.npmignore +.prettierignore +.prettierrc.json +*.o +*.node +test.ts +kzg.ts +jest.config.js +rollup.config.js +tsconfig.json +babel.config.js +*.bak +Dockerfile +binding.dist.gyp +Makefile diff --git a/bindings/node.js/.prettierrc.json b/bindings/node.js/.prettierrc.json index 6b2adca..3ec457f 100644 --- a/bindings/node.js/.prettierrc.json +++ b/bindings/node.js/.prettierrc.json @@ -2,7 +2,7 @@ "trailingComma": "all", "overrides": [ { - "files": "binding.gyp", + "files": "*.gyp", "options": { "parser": "json" } } ] diff --git a/bindings/node.js/Dockerfile b/bindings/node.js/Dockerfile new file mode 100644 index 0000000..7c05cdf --- /dev/null +++ b/bindings/node.js/Dockerfile @@ -0,0 +1,20 @@ +# Exists as a test harness for building and running tests in Linux + +FROM node:16 + +COPY ./dist/ /app/dist/ +COPY test.ts /app +COPY trusted_setup.txt /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 + +CMD ["yarn", "jest"] diff --git a/bindings/node.js/Makefile b/bindings/node.js/Makefile index 9aae264..b87ce55 100644 --- a/bindings/node.js/Makefile +++ b/bindings/node.js/Makefile @@ -3,8 +3,8 @@ all: clean build format test bundle clean: yarn node-gyp clean rm -rf build + rm -rf dist rm -f *.node - rm -f dist/kzg.node rm -f *.a rm -f *.o @@ -18,5 +18,21 @@ test: build format: yarn prettier --write . -bundle: +bundle: clean yarn rollup --config rollup.config.js --bundleConfigAsCjs + mkdir -p dist/deps/c-kzg + 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 + mv binding.gyp binding.gyp.bak + cp binding.dist.gyp binding.gyp + npm publish && mv binding.gyp.bak binding.gyp || mv binding.gyp.bak binding.gyp + + +linux-test: bundle + cp ../../src/trusted_setup.txt . + docker build -t "linux-test" . + rm trusted_setup.txt + docker logs --follow `docker run -d linux-test` diff --git a/bindings/node.js/binding.dist.gyp b/bindings/node.js/binding.dist.gyp new file mode 100644 index 0000000..89851df --- /dev/null +++ b/bindings/node.js/binding.dist.gyp @@ -0,0 +1,64 @@ +{ + "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": "13.0" + }, + "sources": ["kzg.cxx"], + "include_dirs": [ + "<(module_root_dir)/dist/deps/blst/bindings", + "<(module_root_dir)/dist/deps/c-kzg", + ">().Data(); auto blobs_count = blobs_param.Length(); + auto blobs = (Blob*)calloc(blobs_count, sizeof(Blob)); for (uint32_t blob_index = 0; blob_index < blobs_count; blob_index++) { diff --git a/bindings/node.js/package.json b/bindings/node.js/package.json index 5c15850..488d4a5 100644 --- a/bindings/node.js/package.json +++ b/bindings/node.js/package.json @@ -1,6 +1,6 @@ { "name": "c-kzg", - "version": "0.0.1", + "version": "0.0.9", "description": "NodeJS bindings for C-KZG", "author": "Dan Coffman", "license": "MIT", @@ -12,12 +12,14 @@ "@rollup/plugin-typescript": "^9.0.2", "@types/jest": "^29.2.1", "jest": "^29.2.2", - "node-addon-api": "^5.0.0", "node-gyp": "^9.3.0", "prettier": "2.7.1", "rollup": "^3.2.5", "ts-jest": "^29.0.3", "tslib": "^2.4.1", "typescript": "^4.8.4" + }, + "dependencies": { + "node-addon-api": "^5.0.0" } } diff --git a/bindings/node.js/rollup.config.js b/bindings/node.js/rollup.config.js index 383c4bc..5d0528d 100644 --- a/bindings/node.js/rollup.config.js +++ b/bindings/node.js/rollup.config.js @@ -6,5 +6,9 @@ export default { dir: "dist", format: "cjs", }, - plugins: [typescript()], + plugins: [ + typescript({ + exclude: ["test.ts"], + }), + ], }; diff --git a/bindings/node.js/test.ts b/bindings/node.js/test.ts index 0b61441..da4ecc3 100644 --- a/bindings/node.js/test.ts +++ b/bindings/node.js/test.ts @@ -1,4 +1,6 @@ import { randomBytes } from "crypto"; +import { existsSync } from "fs"; + import { loadTrustedSetup, freeTrustedSetup, @@ -9,7 +11,12 @@ import { FIELD_ELEMENTS_PER_BLOB, } from "./kzg"; -const SETUP_FILE_PATH = "../../src/trusted_setup.txt"; +const setupFileName = "trusted_setup.txt"; + +const SETUP_FILE_PATH = existsSync(setupFileName) + ? setupFileName + : `../../src/${setupFileName}`; + const BLOB_BYTE_COUNT = FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD_ELEMENT; const generateRandomBlob = () => new Uint8Array(randomBytes(BLOB_BYTE_COUNT)); @@ -23,10 +30,23 @@ describe("C-KZG", () => { freeTrustedSetup(); }); - it("computes the correct commitments and aggregate proofs from blobs", () => { - const blobs = new Array(2).fill(0).map(generateRandomBlob); - const commitments = blobs.map(blobToKzgCommitment); - const proof = computeAggregateKzgProof(blobs); + 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("throws an error when blobs is an empty array", () => { + expect(() => computeAggregateKzgProof([])).toThrowError( + "Failed to compute proof", + ); + }); + + 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); }); diff --git a/src/c_kzg_4844.c b/src/c_kzg_4844.c index 89dcc42..a55e153 100644 --- a/src/c_kzg_4844.c +++ b/src/c_kzg_4844.c @@ -1109,14 +1109,25 @@ static C_KZG_RET compute_aggregated_poly_and_commitment(Polynomial poly_out, KZG const Polynomial polys[], const KZGCommitment kzg_commitments[], size_t n) { - BLSFieldElement* r_powers = calloc(n, sizeof(BLSFieldElement)); - if (r_powers == NULL) return C_KZG_MALLOC; + if (n == 0) return C_KZG_BADARGS; C_KZG_RET ret; uint8_t* hash = calloc(32, sizeof(uint8_t)); - if (hash == NULL) { free(r_powers); return C_KZG_MALLOC; } + if (hash == NULL) return C_KZG_MALLOC; ret = hash_to_bytes(hash, polys, kzg_commitments, n); - if (ret != C_KZG_OK) { free(r_powers); free(hash); return ret; } + if (ret != C_KZG_OK) { free(hash); return ret; } + + if (n == 1) { + bytes_to_bls_field(chal_out, hash); + poly_lincomb(poly_out, polys, chal_out, n); + g1_lincomb(comm_out, kzg_commitments, chal_out, n); + free(hash); + return C_KZG_OK; + } + + BLSFieldElement* r_powers = calloc(n, sizeof(BLSFieldElement)); + if (r_powers == NULL) { free(hash); return C_KZG_MALLOC; } + bytes_to_bls_field(&r_powers[1], hash); free(hash); @@ -1135,6 +1146,11 @@ C_KZG_RET compute_aggregate_kzg_proof(KZGProof *out, const Blob blobs[], size_t n, const KZGSettings *s) { + if (n == 0) { + *out = g1_identity; + return C_KZG_OK; + } + KZGCommitment* commitments = calloc(n, sizeof(KZGCommitment)); if (commitments == NULL) return C_KZG_MALLOC;