[nodejs] Bundle and auto-load trusted setup (#422)
This commit is contained in:
parent
58b09bdafe
commit
8ab57f44db
|
@ -33,6 +33,7 @@ build: install clean
|
|||
@cp -r ../../blst deps
|
||||
@cp ../../src/c_kzg_4844.c deps/c-kzg
|
||||
@cp ../../src/c_kzg_4844.h deps/c-kzg
|
||||
@cp ../../src/trusted_setup.txt deps/c-kzg
|
||||
@# Build the bindings
|
||||
@$(YARN) node-gyp --loglevel=warn configure
|
||||
@$(YARN) node-gyp --loglevel=warn build
|
||||
|
|
|
@ -7,7 +7,7 @@ API. The core functionality was originally a stripped-down copy of
|
|||
since then. This package wraps that native `c-kzg` C code in C/C++ NAPI
|
||||
bindings for use in node.js applications.
|
||||
|
||||
Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md
|
||||
Spec: <https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/polynomial-commitments.md>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
@ -56,14 +56,22 @@ const isValid = verifyBlobKzgProofBatch(blobs, commitments, proofs);
|
|||
|
||||
```ts
|
||||
/**
|
||||
* 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 TrustedSetupJson
|
||||
* interface for more details, or as a properly formatted utf-8 encoded file.
|
||||
* Initialize the library with a trusted setup file.
|
||||
*
|
||||
* @remark This function must be run before any other functions in this
|
||||
* library can be run.
|
||||
* Can pass either a .txt or a .json file with setup configuration. Converts
|
||||
* JSON formatted trusted setup into the native format that the base library
|
||||
* requires. The created file will be in the same as the origin file but with a
|
||||
* ".txt" extension.
|
||||
*
|
||||
* @param {string} filePath - The absolute path of the trusted setup
|
||||
* Uses user provided location first. If one is not provided then defaults to
|
||||
* the official Ethereum mainnet setup from the KZG ceremony. Should only be
|
||||
* used for cases where the Ethereum official mainnet KZG setup is acceptable.
|
||||
*
|
||||
* @param {string | undefined} filePath - .txt/.json file with setup configuration
|
||||
* @default - If no string is passed the default trusted setup from the Ethereum KZG ceremony is used
|
||||
*
|
||||
* @throws {TypeError} - Non-String input
|
||||
* @throws {Error} - For all other errors. See error message for more info
|
||||
*/
|
||||
loadTrustedSetup(filePath: string): void;
|
||||
```
|
||||
|
|
|
@ -22,14 +22,24 @@ export const BYTES_PER_PROOF: number;
|
|||
export const FIELD_ELEMENTS_PER_BLOB: number;
|
||||
|
||||
/**
|
||||
* Factory function that passes trusted setup to the bindings
|
||||
* Initialize the library with a trusted setup file.
|
||||
*
|
||||
* @param {string} filePath
|
||||
* Can pass either a .txt or a .json file with setup configuration. Converts
|
||||
* JSON formatted trusted setup into the native format that the base library
|
||||
* requires. The created file will be in the same as the origin file but with a
|
||||
* ".txt" extension.
|
||||
*
|
||||
* Uses user provided location first. If one is not provided then defaults to
|
||||
* the official Ethereum mainnet setup from the KZG ceremony. Should only be
|
||||
* used for cases where the Ethereum official mainnet KZG setup is acceptable.
|
||||
*
|
||||
* @param {string | undefined} filePath
|
||||
* @default - If no string is passed the default trusted setup from the Ethereum KZG ceremony is used
|
||||
*
|
||||
* @throws {TypeError} - Non-String input
|
||||
* @throws {Error} - For all other errors. See error message for more info
|
||||
*/
|
||||
export function loadTrustedSetup(filePath: string): void;
|
||||
export function loadTrustedSetup(filePath?: string): void;
|
||||
|
||||
/**
|
||||
* Convert a blob to a KZG commitment.
|
||||
|
|
|
@ -6,6 +6,50 @@ const fs = require("fs");
|
|||
const path = require("path");
|
||||
const bindings = require("bindings")("kzg");
|
||||
|
||||
/**
|
||||
* NOTE: These two paths are only exported for testing purposes. They are not
|
||||
* announced in the type file.
|
||||
*
|
||||
* It is critical that these paths are kept in sync with where the trusted setup
|
||||
* files will be found. The root setup is in the base src directory with the
|
||||
* primary library code. The dist version is dictated by the `build` command in
|
||||
* the Makefile in the bindings/node.js folder.
|
||||
*/
|
||||
/**
|
||||
* Check the production bundle case first.
|
||||
* - this file in BUNDLE_ROOT/dist/lib/kzg.js
|
||||
* - trusted_setup in BUNDLE_ROOT/dist/deps/c-kzg/trusted_setup.txt
|
||||
*/
|
||||
bindings.TRUSTED_SETUP_PATH_IN_DIST = path.resolve(__dirname, "..", "deps", "c-kzg", "trusted_setup.txt");
|
||||
/**
|
||||
* Check the development case second.
|
||||
* - this file in REPO_ROOT/bindings/node.js/lib/kzg.js
|
||||
* - trusted_setup in REPO_ROOT/src/trusted_setup.txt
|
||||
*/
|
||||
bindings.TRUSTED_SETUP_PATH_IN_SRC = path.resolve(__dirname, "..", "..", "..", "src", "trusted_setup.txt");
|
||||
|
||||
/**
|
||||
* Looks in the default locations for the trusted setup file. This is for cases
|
||||
* where the library is loaded without passing a trusted setup. Should only be
|
||||
* used for cases where the Ethereum official mainnet KZG setup is acceptable.
|
||||
*
|
||||
* @returns {string | undefined} - Filepath for trusted_setup.txt if found
|
||||
*/
|
||||
function getDefaultTrustedSetupFilepath() {
|
||||
const locationsToSearch = [
|
||||
// check the production case first
|
||||
bindings.TRUSTED_SETUP_PATH_IN_DIST,
|
||||
// check the development in-repo case second
|
||||
bindings.TRUSTED_SETUP_PATH_IN_SRC,
|
||||
];
|
||||
|
||||
for (const filepath of locationsToSearch) {
|
||||
if (fs.existsSync(filepath)) {
|
||||
return filepath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts JSON formatted trusted setup into the native format that
|
||||
* the native library requires. Returns the absolute file path to
|
||||
|
@ -31,19 +75,49 @@ function transformTrustedSetupJson(filePath) {
|
|||
return outputPath;
|
||||
}
|
||||
|
||||
const originalLoadTrustedSetup = bindings.loadTrustedSetup;
|
||||
// docstring in ./kzg.d.ts with exported definition
|
||||
bindings.loadTrustedSetup = function loadTrustedSetup(filePath) {
|
||||
if (!(filePath && typeof filePath === "string")) {
|
||||
throw new TypeError("must initialize kzg with the filePath to a txt/json trusted setup");
|
||||
}
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(`no trusted setup found: ${filePath}`);
|
||||
/**
|
||||
* Gets location for trusted setup file. Uses user provided location first. If
|
||||
* one is not provided then defaults to the official Ethereum mainnet setup from
|
||||
* the KZG ceremony.
|
||||
*
|
||||
* @param {string} filePath - User provided filePath to check for trusted setup
|
||||
*
|
||||
* @returns {string} - Location of a trusted setup file. Validity is checked by
|
||||
* the native bindings.loadTrustedSetup
|
||||
*
|
||||
* @throws {TypeError} - Invalid file type
|
||||
* @throws {Error} - Invalid location or no default trusted setup found
|
||||
*
|
||||
* @remarks - This function is only exported for testing purposes. It should
|
||||
* not be used directly. Not included in the kzg.d.ts types for that
|
||||
* reason.
|
||||
*/
|
||||
bindings.getTrustedSetupFilepath = function getTrustedSetupFilepath(filePath) {
|
||||
if (filePath) {
|
||||
if (typeof filePath !== "string") {
|
||||
throw new TypeError("Must initialize kzg with the filePath to a txt/json trusted setup");
|
||||
}
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(`No trusted setup found: ${filePath}`);
|
||||
}
|
||||
} else {
|
||||
filePath = getDefaultTrustedSetupFilepath();
|
||||
if (!filePath) {
|
||||
throw new Error("Default trusted setup not found. Must pass a valid filepath to load c-kzg library");
|
||||
}
|
||||
}
|
||||
|
||||
if (path.parse(filePath).ext === ".json") {
|
||||
filePath = transformTrustedSetupJson(filePath);
|
||||
}
|
||||
originalLoadTrustedSetup(filePath);
|
||||
|
||||
return filePath;
|
||||
};
|
||||
|
||||
const originalLoadTrustedSetup = bindings.loadTrustedSetup;
|
||||
// docstring in ./kzg.d.ts with exported definition
|
||||
bindings.loadTrustedSetup = function loadTrustedSetup(filePath) {
|
||||
originalLoadTrustedSetup(bindings.getTrustedSetupFilepath(filePath));
|
||||
};
|
||||
|
||||
module.exports = exports = bindings;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {randomBytes} from "crypto";
|
||||
import {readFileSync} from "fs";
|
||||
import {readFileSync, existsSync, cpSync, rmSync} from "fs";
|
||||
import {resolve} from "path";
|
||||
import {globSync} from "glob";
|
||||
|
||||
|
@ -10,7 +10,9 @@ interface TestMeta<I extends Record<string, any>, O extends boolean | string | s
|
|||
output: O;
|
||||
}
|
||||
|
||||
import {
|
||||
import kzg from "../lib/kzg";
|
||||
import type {ProofResult} from "../lib/kzg";
|
||||
const {
|
||||
loadTrustedSetup,
|
||||
blobToKzgCommitment,
|
||||
computeKzgProof,
|
||||
|
@ -22,10 +24,14 @@ import {
|
|||
BYTES_PER_COMMITMENT,
|
||||
BYTES_PER_PROOF,
|
||||
BYTES_PER_FIELD_ELEMENT,
|
||||
ProofResult,
|
||||
} from "../lib/kzg";
|
||||
} = kzg;
|
||||
// not exported by types, only exported for testing purposes
|
||||
const getTrustedSetupFilepath = (kzg as any).getTrustedSetupFilepath as (filePath?: string) => string;
|
||||
const TRUSTED_SETUP_PATH_IN_DIST = (kzg as any).TRUSTED_SETUP_PATH_IN_DIST as string;
|
||||
const TRUSTED_SETUP_PATH_IN_SRC = (kzg as any).TRUSTED_SETUP_PATH_IN_SRC as string;
|
||||
|
||||
const SETUP_FILE_PATH = resolve(__dirname, "__fixtures__", "trusted_setup.json");
|
||||
const TEST_SETUP_FILE_PATH_JSON = resolve(__dirname, "__fixtures__", "trusted_setup.json");
|
||||
const TEST_SETUP_FILE_PATH_TXT = resolve(__dirname, "__fixtures__", "trusted_setup.txt");
|
||||
|
||||
const MAX_TOP_BYTE = 114;
|
||||
|
||||
|
@ -166,7 +172,38 @@ function testArgCount(fn: (...args: any[]) => any, validArgs: any[]): void {
|
|||
|
||||
describe("C-KZG", () => {
|
||||
beforeAll(async () => {
|
||||
loadTrustedSetup(SETUP_FILE_PATH);
|
||||
loadTrustedSetup(TEST_SETUP_FILE_PATH_JSON);
|
||||
});
|
||||
|
||||
describe("locating trusted setup file", () => {
|
||||
it("should return a txt path if a json file is provided and exists", () => {
|
||||
expect(getTrustedSetupFilepath(TEST_SETUP_FILE_PATH_JSON)).toEqual(TEST_SETUP_FILE_PATH_TXT);
|
||||
});
|
||||
/**
|
||||
* No guarantee that the test above runs first, however the json file should
|
||||
* have already been loaded by the beforeAll so a valid .txt test setup
|
||||
* should be available to expect
|
||||
*/
|
||||
it("should return the same txt path if provided and exists", () => {
|
||||
expect(getTrustedSetupFilepath(TEST_SETUP_FILE_PATH_TXT)).toEqual(TEST_SETUP_FILE_PATH_TXT);
|
||||
});
|
||||
describe("default setups", () => {
|
||||
beforeEach(() => {
|
||||
if (!existsSync(TRUSTED_SETUP_PATH_IN_DIST)) {
|
||||
cpSync(TRUSTED_SETUP_PATH_IN_SRC, TRUSTED_SETUP_PATH_IN_DIST);
|
||||
}
|
||||
});
|
||||
it("should return dist setup first", () => {
|
||||
// both files should be preset right now
|
||||
expect(getTrustedSetupFilepath()).toEqual(TRUSTED_SETUP_PATH_IN_DIST);
|
||||
});
|
||||
it("should return src setup if dist is missing", () => {
|
||||
// both files should be preset right now
|
||||
rmSync(TRUSTED_SETUP_PATH_IN_DIST);
|
||||
expect(getTrustedSetupFilepath()).toEqual(TRUSTED_SETUP_PATH_IN_SRC);
|
||||
cpSync(TRUSTED_SETUP_PATH_IN_SRC, TRUSTED_SETUP_PATH_IN_DIST);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("reference tests should pass", () => {
|
||||
|
|
Loading…
Reference in New Issue