Merge branch '4844'

This commit is contained in:
Alexey Osipov 2022-11-09 11:40:52 +03:00
commit 68897b375c
17 changed files with 203 additions and 75 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ inc/blst_aux.h*
*bindings/csharp/*.exe *bindings/csharp/*.exe
*bindings/csharp/*.dll *bindings/csharp/*.dll
__pycache__ __pycache__
.DS_Store

View File

@ -1,6 +1,7 @@
# C-KZG-4844: A minimal library for EIP-4844 Polynomial Commitments # 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: 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` - `compute_aggregate_kzg_proof`
- `verify_aggregate_kzg_proof` - `verify_aggregate_kzg_proof`
- `blob_to_kzg_commitment` - `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 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 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. 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
```

View File

@ -1,3 +1,4 @@
build build
dist
*.node *.node
node_modules node_modules

View File

@ -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

View File

@ -2,7 +2,7 @@
"trailingComma": "all", "trailingComma": "all",
"overrides": [ "overrides": [
{ {
"files": "binding.gyp", "files": "*.gyp",
"options": { "parser": "json" } "options": { "parser": "json" }
} }
] ]

View File

@ -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"]

View File

@ -3,8 +3,8 @@ all: clean build format test bundle
clean: clean:
yarn node-gyp clean yarn node-gyp clean
rm -rf build rm -rf build
rm -rf dist
rm -f *.node rm -f *.node
rm -f dist/kzg.node
rm -f *.a rm -f *.a
rm -f *.o rm -f *.o
@ -18,5 +18,21 @@ test: build
format: format:
yarn prettier --write . yarn prettier --write .
bundle: bundle: clean
yarn rollup --config rollup.config.js --bundleConfigAsCjs 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`

View File

@ -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",
"<!@(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\")"],
"defines": ["NAPI_DISABLE_CPP_EXCEPTIONS"],
"actions": [
{
"action_name": "build_blst",
"inputs": ["<(module_root_dir)/dist/deps/blst/build.sh"],
"outputs": ["<(module_root_dir)/libblst.a"],
"action": ["<(module_root_dir)/dist/deps/blst/build.sh"]
},
{
"action_name": "build_ckzg",
"inputs": [
"<(module_root_dir)/dist/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)/dist/deps/blst/bindings",
"-O2",
"-c",
"<(module_root_dir)/dist/deps/c-kzg/c_kzg_4844.c"
]
}
]
},
{
"target_name": "action_after_build",
"type": "none",
"dependencies": ["kzg"],
"copies": [
{
"files": ["./build/Release/kzg.node"],
"destination": "./dist"
},
{
"files": ["./build/Release/kzg.node"],
"destination": "./"
}
]
}
]
}

View File

@ -30,10 +30,6 @@
{ {
"files": ["./build/Release/kzg.node"], "files": ["./build/Release/kzg.node"],
"destination": "." "destination": "."
},
{
"files": ["./build/Release/kzg.node"],
"destination": "./dist"
} }
] ]
} }

View File

@ -1,11 +0,0 @@
export declare type BLSFieldElement = Uint8Array;
export declare type KZGProof = Uint8Array;
export declare type KZGCommitment = Uint8Array;
export declare type Blob = Uint8Array;
export declare const FIELD_ELEMENTS_PER_BLOB: number;
export declare const BYTES_PER_FIELD_ELEMENT: number;
export declare function loadTrustedSetup(filePath: string): void;
export declare function freeTrustedSetup(): void;
export declare function blobToKzgCommitment(blob: Blob): KZGCommitment;
export declare function computeAggregateKzgProof(blobs: Blob[]): KZGProof;
export declare function verifyAggregateKzgProof(blobs: Blob[], expectedKzgCommitments: KZGCommitment[], kzgAggregatedProof: KZGProof): boolean;

View File

@ -1 +0,0 @@
export {};

View File

@ -1,44 +0,0 @@
'use strict';
/**
* The public interface of this module exposes the functions as specified by
* https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#kzg
*/
const kzg = require("./kzg.node");
const FIELD_ELEMENTS_PER_BLOB = kzg.FIELD_ELEMENTS_PER_BLOB;
const BYTES_PER_FIELD_ELEMENT = kzg.BYTES_PER_FIELD_ELEMENT;
// Stored as internal state
let setupHandle;
function requireSetupHandle() {
if (!setupHandle) {
throw new Error("You must call loadTrustedSetup to initialize KZG.");
}
return setupHandle;
}
function loadTrustedSetup(filePath) {
if (setupHandle) {
throw new Error("Call freeTrustedSetup before loading a new trusted setup.");
}
setupHandle = kzg.loadTrustedSetup(filePath);
}
function freeTrustedSetup() {
kzg.freeTrustedSetup(requireSetupHandle());
setupHandle = undefined;
}
function blobToKzgCommitment(blob) {
return kzg.blobToKzgCommitment(blob, requireSetupHandle());
}
function computeAggregateKzgProof(blobs) {
return kzg.computeAggregateKzgProof(blobs, requireSetupHandle());
}
function verifyAggregateKzgProof(blobs, expectedKzgCommitments, kzgAggregatedProof) {
return kzg.verifyAggregateKzgProof(blobs, expectedKzgCommitments, kzgAggregatedProof, requireSetupHandle());
}
exports.BYTES_PER_FIELD_ELEMENT = BYTES_PER_FIELD_ELEMENT;
exports.FIELD_ELEMENTS_PER_BLOB = FIELD_ELEMENTS_PER_BLOB;
exports.blobToKzgCommitment = blobToKzgCommitment;
exports.computeAggregateKzgProof = computeAggregateKzgProof;
exports.freeTrustedSetup = freeTrustedSetup;
exports.loadTrustedSetup = loadTrustedSetup;
exports.verifyAggregateKzgProof = verifyAggregateKzgProof;

View File

@ -155,6 +155,7 @@ Napi::Value ComputeAggregateKzgProof(const Napi::CallbackInfo& info) {
auto kzg_settings = info[1].As<Napi::External<KZGSettings>>().Data(); auto kzg_settings = info[1].As<Napi::External<KZGSettings>>().Data();
auto blobs_count = blobs_param.Length(); auto blobs_count = blobs_param.Length();
auto blobs = (Blob*)calloc(blobs_count, sizeof(Blob)); auto blobs = (Blob*)calloc(blobs_count, sizeof(Blob));
for (uint32_t blob_index = 0; blob_index < blobs_count; blob_index++) { for (uint32_t blob_index = 0; blob_index < blobs_count; blob_index++) {

View File

@ -1,6 +1,6 @@
{ {
"name": "c-kzg", "name": "c-kzg",
"version": "0.0.1", "version": "0.0.9",
"description": "NodeJS bindings for C-KZG", "description": "NodeJS bindings for C-KZG",
"author": "Dan Coffman", "author": "Dan Coffman",
"license": "MIT", "license": "MIT",
@ -12,12 +12,14 @@
"@rollup/plugin-typescript": "^9.0.2", "@rollup/plugin-typescript": "^9.0.2",
"@types/jest": "^29.2.1", "@types/jest": "^29.2.1",
"jest": "^29.2.2", "jest": "^29.2.2",
"node-addon-api": "^5.0.0",
"node-gyp": "^9.3.0", "node-gyp": "^9.3.0",
"prettier": "2.7.1", "prettier": "2.7.1",
"rollup": "^3.2.5", "rollup": "^3.2.5",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"tslib": "^2.4.1", "tslib": "^2.4.1",
"typescript": "^4.8.4" "typescript": "^4.8.4"
},
"dependencies": {
"node-addon-api": "^5.0.0"
} }
} }

View File

@ -6,5 +6,9 @@ export default {
dir: "dist", dir: "dist",
format: "cjs", format: "cjs",
}, },
plugins: [typescript()], plugins: [
typescript({
exclude: ["test.ts"],
}),
],
}; };

View File

@ -1,4 +1,6 @@
import { randomBytes } from "crypto"; import { randomBytes } from "crypto";
import { existsSync } from "fs";
import { import {
loadTrustedSetup, loadTrustedSetup,
freeTrustedSetup, freeTrustedSetup,
@ -9,7 +11,12 @@ import {
FIELD_ELEMENTS_PER_BLOB, FIELD_ELEMENTS_PER_BLOB,
} from "./kzg"; } 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 BLOB_BYTE_COUNT = FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD_ELEMENT;
const generateRandomBlob = () => new Uint8Array(randomBytes(BLOB_BYTE_COUNT)); const generateRandomBlob = () => new Uint8Array(randomBytes(BLOB_BYTE_COUNT));
@ -23,10 +30,23 @@ describe("C-KZG", () => {
freeTrustedSetup(); freeTrustedSetup();
}); });
it("computes the correct commitments and aggregate proofs from blobs", () => { it("computes the correct commitments and aggregate proof from blobs", () => {
const blobs = new Array(2).fill(0).map(generateRandomBlob); let blobs = new Array(2).fill(0).map(generateRandomBlob);
const commitments = blobs.map(blobToKzgCommitment); let commitments = blobs.map(blobToKzgCommitment);
const proof = computeAggregateKzgProof(blobs); 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); expect(verifyAggregateKzgProof(blobs, commitments, proof)).toBe(true);
}); });

View File

@ -1109,14 +1109,25 @@ static C_KZG_RET compute_aggregated_poly_and_commitment(Polynomial poly_out, KZG
const Polynomial polys[], const Polynomial polys[],
const KZGCommitment kzg_commitments[], const KZGCommitment kzg_commitments[],
size_t n) { size_t n) {
BLSFieldElement* r_powers = calloc(n, sizeof(BLSFieldElement)); if (n == 0) return C_KZG_BADARGS;
if (r_powers == NULL) return C_KZG_MALLOC;
C_KZG_RET ret; C_KZG_RET ret;
uint8_t* hash = calloc(32, sizeof(uint8_t)); 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); 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); bytes_to_bls_field(&r_powers[1], hash);
free(hash); free(hash);
@ -1135,6 +1146,11 @@ C_KZG_RET compute_aggregate_kzg_proof(KZGProof *out,
const Blob blobs[], const Blob blobs[],
size_t n, size_t n,
const KZGSettings *s) { const KZGSettings *s) {
if (n == 0) {
*out = g1_identity;
return C_KZG_OK;
}
KZGCommitment* commitments = calloc(n, sizeof(KZGCommitment)); KZGCommitment* commitments = calloc(n, sizeof(KZGCommitment));
if (commitments == NULL) return C_KZG_MALLOC; if (commitments == NULL) return C_KZG_MALLOC;