Checkpoint

This commit is contained in:
dancoffman 2022-11-03 12:57:46 -07:00
parent dbf2a1d905
commit fcd7fbd9e7
No known key found for this signature in database
GPG Key ID: 47B1F53E36A9B3CC
10 changed files with 425 additions and 207 deletions

View File

@ -1,7 +1,22 @@
install:
brew install llvm
yarn install
clean:
yarn clean
rm -rf build
rm -f kzg.node
rm -f *.node
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

View File

@ -15,6 +15,7 @@
'include_dirs': ['../../inc', '../../src', "<!@(node -p \"require('node-addon-api').include\")"],
'libraries': [
'/Users/coffman@coinbase.com/src/c-kzg/bindings/node.js/c_kzg_4844.o',
'/Users/coffman@coinbase.com/src/c-kzg/bindings/node.js/sha256.o',
'/Users/coffman@coinbase.com/src/c-kzg/lib/libblst.a'
],
'dependencies': [

View File

@ -10,6 +10,33 @@
#include <algorithm> // std::copy
#include <iterator> // std::ostream_iterator
Napi::TypedArrayOf<uint8_t> napiTypedArrayFromByteArray(uint8_t* array, size_t arrayLength, Napi::Env env) {
// Create std::vector<uint8_t> out of array.
// We allocate it on the heap to allow wrapping it up into ArrayBuffer.
std::unique_ptr<std::vector<uint8_t>> nativeArray =
std::make_unique<std::vector<uint8_t>>(arrayLength, 0);
for (size_t i = 0; i < arrayLength; ++i) {
(*nativeArray)[i] = array[i];
}
// Wrap up the std::vector into the ArrayBuffer.
Napi::ArrayBuffer arrayBuffer = Napi::ArrayBuffer::New(
env,
nativeArray->data(),
arrayLength /* size in bytes */,
[](Napi::Env /*env*/, void* /*data*/, std::vector<uint8_t>* hint) {
std::unique_ptr<std::vector<uint8_t>> vectorPtrToDelete(hint);
},
nativeArray.get());
// The finalizer is responsible for deleting the vector: release the
// unique_ptr ownership.
nativeArray.release();
return Napi::Uint8Array::New(env, arrayLength, arrayBuffer, 0);
}
int verifyAggregateKzgProof(const uint8_t blobs[], const uint8_t commitments[], size_t n, const uint8_t proof[48], const KZGSettings *s) {
Polynomial* p = (Polynomial*)calloc(n, sizeof(Polynomial));
if (p == NULL) return -1;
@ -38,24 +65,6 @@ int verifyAggregateKzgProof(const uint8_t blobs[], const uint8_t commitments[],
return b ? 0 : 1;
}
C_KZG_RET computeAggregateKzgProof(uint8_t out[48], const uint8_t blobs[], size_t n, const KZGSettings *s) {
Polynomial* p = (Polynomial*)calloc(n, sizeof(Polynomial));
if (p == NULL) return C_KZG_ERROR;
for (size_t i = 0; i < n; i++)
for (size_t j = 0; j < FIELD_ELEMENTS_PER_BLOB; j++)
bytes_to_bls_field(&p[i][j], &blobs[i * FIELD_ELEMENTS_PER_BLOB * 32 + j * 32]);
KZGProof f;
C_KZG_RET ret = compute_aggregate_kzg_proof(&f, p, n, s);
free(p);
if (ret != C_KZG_OK) return ret;
bytes_from_g1(out, &f);
return C_KZG_OK;
}
Napi::Value LoadTrustedSetup(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
@ -105,9 +114,6 @@ void FreeTrustedSetup(const Napi::CallbackInfo& info) {
free(kzgSettings);
}
// https://github.com/nodejs/node-addon-examples/blob/35c714f95b0674a7415ca7c166e9e981f5a77cf9/typed_array_to_native/node-addon-api/typed_array_to_native.cc
// blobToKzgCommitment: (blob: Blob) => KZGCommitment;
Napi::Value BlobToKzgCommitment(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
@ -119,14 +125,13 @@ Napi::Value BlobToKzgCommitment(const Napi::CallbackInfo& info) {
}
Napi::TypedArray typedArray = info[0].As<Napi::TypedArray>();
if (typedArray.TypedArrayType() != napi_uint8_array) {
Napi::Error::New(info.Env(), "Expected an Uint8Array")
Napi::Error::New(env, "Expected an Uint8Array")
.ThrowAsJavaScriptException();
return info.Env().Undefined();
return env.Undefined();
}
uint8_t* blob = typedArray.As<Napi::Uint8Array>().Data();
KZGSettings* kzgSettings = info[1].As<Napi::External<KZGSettings>>().Data();
Polynomial polynomial;
@ -139,32 +144,7 @@ Napi::Value BlobToKzgCommitment(const Napi::CallbackInfo& info) {
// Turn it into a byte array
uint8_t array[48];
bytes_from_g1(array, &commitment);
size_t arrayLength = sizeof(array);
// Create std::vector<uint8_t> out of array.
// We allocate it on the heap to allow wrapping it up into ArrayBuffer.
std::unique_ptr<std::vector<uint8_t>> nativeArray =
std::make_unique<std::vector<uint8_t>>(arrayLength, 0);
for (size_t i = 0; i < arrayLength; ++i) {
(*nativeArray)[i] = array[i];
}
// Wrap up the std::vector into the ArrayBuffer.
Napi::ArrayBuffer arrayBuffer = Napi::ArrayBuffer::New(
info.Env(),
nativeArray->data(),
arrayLength /* size in bytes */,
[](Napi::Env /*env*/, void* /*data*/, std::vector<uint8_t>* hint) {
std::unique_ptr<std::vector<uint8_t>> vectorPtrToDelete(hint);
},
nativeArray.get());
// The finalizer is responsible for deleting the vector: release the
// unique_ptr ownership.
nativeArray.release();
return Napi::Uint8Array::New(info.Env(), arrayLength, arrayBuffer, 0);
return napiTypedArrayFromByteArray(array, sizeof(array), env);
}
Napi::Value VerifyAggregateKzgProof(const Napi::CallbackInfo& info) {
@ -172,11 +152,98 @@ Napi::Value VerifyAggregateKzgProof(const Napi::CallbackInfo& info) {
}
Napi::Value ComputeAggregateKzgProof(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
auto env = info.Env();
if (info.Length() != 2) {
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
return env.Null();
}
auto blobs_param = info[0].As<Napi::Array>();
auto kzgSettings = info[1].As<Napi::External<KZGSettings>>().Data();
auto numberOfBlobs = blobs_param.Length();
// auto blobs = blobs_param.As<Napi::Array<Napi::Uint8Array>>();
int BYTES_PER_FIELD = 32;
int BYTES_PER_BLOB = FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD;
printf("ComputeAggregateKzgProof called with %i blobs", numberOfBlobs);
// uint8_t blobBytes[numberOfBlobs * FIELD_ELEMENTS_PER_BLOB];
// for(int i = 0; i < numberOfBlobs; i++)
// {
// Napi::Value blob = blobs_param[i];
// if (blob.IsTypedArray())
// {
// auto blobBytes = blob.As<Napi::Uint8Array>().Data();
// memcpy(blobBytes + i * BYTES_PER_BLOB, blobBytes, BYTES_PER_BLOB);
// }
// }
// printf("ComputeAggregateKzgProof copied %i bytes", sizeof(blobBytes));
auto polynomial = (Polynomial*)calloc(numberOfBlobs, sizeof(Polynomial));
// if (p == NULL) return C_KZG_ERROR;
for (uint32_t blobIndex = 0; blobIndex < numberOfBlobs; blobIndex++) {
Napi::Value blob = blobs_param[blobIndex];
auto blobBytes = blob.As<Napi::Uint8Array>().Data();
printf("Iterating blob index: %i", blobIndex);
for (uint32_t fieldIndex = 0; fieldIndex < FIELD_ELEMENTS_PER_BLOB; fieldIndex++) {
bytes_to_bls_field(
&polynomial[blobIndex][fieldIndex],
&blobBytes[fieldIndex * BYTES_PER_FIELD]
);
}
}
printf("ComputeAggregateKzgProof 1");
printf("ComputeAggregateKzgProof 1");
printf("ComputeAggregateKzgProof 1");
printf("ComputeAggregateKzgProof 1");
// return env.Null();
KZGProof proof;
C_KZG_RET ret = compute_aggregate_kzg_proof(
&proof,
polynomial,
numberOfBlobs,
kzgSettings
);
Napi::TypeError::New(env, "STOPPING HERE")
.ThrowAsJavaScriptException();
return env.Null();
printf("ComputeAggregateKzgProof 2");
free(polynomial);
printf("ComputeAggregateKzgProof 3");
if (ret != C_KZG_OK) {
Napi::TypeError::New(env, "Failed to compute proof")
.ThrowAsJavaScriptException();
return env.Null();
};
printf("ComputeAggregateKzgProof 4");
uint8_t* bytes;
bytes_from_g1(bytes, &proof);
printf("ComputeAggregateKzgProof 5");
return napiTypedArrayFromByteArray(bytes, sizeof(bytes), env);
}
Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
auto env = info.Env();
if (info.Length() != 5) {
Napi::TypeError::New(env, "Wrong number of arguments")
@ -185,19 +252,43 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) {
}
// const uint8_t c[48]
uint8_t* c = reinterpret_cast<uint8_t*>(info[0].As<Napi::Int8Array>().Data());
auto c_param = info[0].As<Napi::TypedArray>();
if (c_param.TypedArrayType() != napi_uint8_array) {
Napi::Error::New(env, "Expected an Uint8Array")
.ThrowAsJavaScriptException();
return env.Undefined();
}
auto c = c_param.As<Napi::Uint8Array>().Data();
// const uint8_t x[32]
uint8_t* x = reinterpret_cast<uint8_t*>(info[1].As<Napi::Int8Array>().Data());
auto x_param = info[0].As<Napi::TypedArray>();
if (x_param.TypedArrayType() != napi_uint8_array) {
Napi::Error::New(env, "Expected an Uint8Array")
.ThrowAsJavaScriptException();
return env.Undefined();
}
auto x = x_param.As<Napi::Uint8Array>().Data();
// const uint8_t y[32]
uint8_t* y = reinterpret_cast<uint8_t*>(info[2].As<Napi::Int8Array>().Data());
auto y_param = info[0].As<Napi::TypedArray>();
if (y_param.TypedArrayType() != napi_uint8_array) {
Napi::Error::New(env, "Expected an Uint8Array")
.ThrowAsJavaScriptException();
return env.Undefined();
}
auto y = y_param.As<Napi::Uint8Array>().Data();
// const uint8_t p[48]
uint8_t* p = reinterpret_cast<uint8_t*>(info[3].As<Napi::Int8Array>().Data());
auto p_param = info[0].As<Napi::TypedArray>();
if (p_param.TypedArrayType() != napi_uint8_array) {
Napi::Error::New(info.Env(), "Expected an Uint8Array")
.ThrowAsJavaScriptException();
return info.Env().Undefined();
}
auto p = p_param.As<Napi::Uint8Array>().Data();
// KZGSettings *s
KZGSettings* kzgSettings = info[4].As<Napi::External<KZGSettings>>().Data();
auto kzgSettings = info[4].As<Napi::External<KZGSettings>>().Data();
KZGCommitment commitment;
KZGProof proof;
@ -207,11 +298,10 @@ Napi::Value VerifyKzgProof(const Napi::CallbackInfo& info) {
bytes_to_bls_field(&fx, x);
bytes_to_bls_field(&fy, y);
C_KZG_RET ret = bytes_to_g1(&commitment, c);
auto ret = bytes_to_g1(&commitment, c);
if (ret != C_KZG_OK) {
std::ostringstream ss;
std::copy(c, c+sizeof(c), std::ostream_iterator<int>(ss, ","));
Napi::TypeError::New(env, "Failed to parse argument commitment: " + ss.str() + " Return code was: " + std::to_string(ret)).ThrowAsJavaScriptException();
return env.Null();
// return -1;

View File

@ -1,6 +1,9 @@
// @ts-expect-error
import bindings from 'bindings';
export const BLOB_SIZE = 4096;
export const NUMBER_OF_FIELDS = 32;
// Consider making this internal state of the native code
// so we don't have to pass it around in the application layer
export type SetupHandle = Object;
@ -16,9 +19,6 @@ export enum ReturnValue {
MALLOC,
}
export const BLOB_SIZE = 4096;
export const NUMBER_OF_FIELDS = 32;
export type Point = Uint8Array;
export type KZGProof = Uint8Array;
export type KZGCommitment = Uint8Array;
@ -27,14 +27,18 @@ export type Blobs = Blob[];
type KZG = {
loadTrustedSetup: (path: string) => SetupHandle;
freeTrustedSetup: (setupHandle: SetupHandle) => void;
blobToKzgCommitment: (blob: Blob, setupHandle: SetupHandle) => KZGCommitment;
verifyAggregateKzgProof: (blobs: Blobs) => ReturnValue;
computeAggregateKzgProof: (
blobs: Blobs,
size: number,
setupHandle: SetupHandle,
) => KZGProof;
verifyKzgProof: (
commitment: KZGCommitment,
x: Point,
@ -52,5 +56,4 @@ export const blobToKzgCommitment = kzg.blobToKzgCommitment;
export const verifyAggregateKzgProof = kzg.verifyAggregateKzgProof;
export const computeAggregateKzgProof = kzg.computeAggregateKzgProof;
export const verifyKzgProof = kzg.verifyKzgProof;
export default kzg;

View File

@ -1,3 +1,4 @@
import { randomBytes } from 'crypto';
import {
loadTrustedSetup,
freeTrustedSetup,
@ -8,54 +9,56 @@ import {
Blob,
BLOB_SIZE,
NUMBER_OF_FIELDS,
computeAggregateKzgProof,
} from './kzg';
import { randomBytes } from 'crypto';
const SETUP_FILE_PATH = '../../src/trusted_setup.txt';
const COMMITMENT_BYTE_LENGTH = 48;
function generateRandomBlob(): Blob {
return new Uint8Array(randomBytes(NUMBER_OF_FIELDS * BLOB_SIZE));
}
describe('C-KZG', () => {
let setupHandle: SetupHandle;
let sharedSetupHandle: SetupHandle;
beforeAll(() => {
setupHandle = loadTrustedSetup('../../src/trusted_setup.txt');
sharedSetupHandle = loadTrustedSetup(SETUP_FILE_PATH);
});
describe('setup', () => {
it('can both load and free', () => {
expect(freeTrustedSetup(setupHandle)).toBeUndefined();
expect(
freeTrustedSetup(loadTrustedSetup(SETUP_FILE_PATH)),
).toBeUndefined();
});
});
describe('computing a KZG commitment from a blob', () => {
it.only('returns data with the correct length', () => {
it('returns data with the correct length', () => {
const blob = generateRandomBlob();
const commitment = blobToKzgCommitment(blob, setupHandle);
expect(commitment.length).toBe(48);
const commitment = blobToKzgCommitment(blob, sharedSetupHandle);
expect(commitment.length).toBe(COMMITMENT_BYTE_LENGTH);
});
});
describe('verifying a KZG proof', () => {
it.skip('returns the expected value', () => {
it.only('returns the expected value', () => {
const byteEncoder = new TextEncoder();
const commitment = byteEncoder.encode(
'b91c022acf7bd3b63be69a4c19b781ea7a3d5df1cd66ceb7dd0f399610f0ee04695dace82e04bfb83af2b17d7319f87f',
);
console.log({ commitment });
const blob = generateRandomBlob();
const commitment = blobToKzgCommitment(blob, sharedSetupHandle);
const proof = computeAggregateKzgProof([blob], sharedSetupHandle);
const x = byteEncoder.encode(
'0345f802a75a6c0d9cc5b8a1e71642b8fa80b0a78938edc6da1e591149578d1a',
);
const y = byteEncoder.encode(
'3b17cab634c3795d311380f3bc93ce8e768efc0e2b9e79496cfc8f351594b472',
);
const proof = byteEncoder.encode(
'a5ddd6da04c47a9cd4628beb8d55ebd2e930a64dfa29f876ebf393cfd6574d48a3ce96ac5a2af4a4f9ec9caa47d304d3',
);
const result = verifyKzgProof(commitment, y, x, proof, setupHandle);
const result = verifyKzgProof(commitment, y, x, proof, sharedSetupHandle);
console.log({ result });
expect(result).toBe(ReturnValue.OK);
});
@ -63,7 +66,9 @@ describe('C-KZG', () => {
describe('computing an aggregate KZG proof', () => {
it('returns the expected value', () => {
expect(true).toBe(false);
const blob = generateRandomBlob();
const commitment = blobToKzgCommitment(blob, sharedSetupHandle);
const proof = computeAggregateKzgProof([blob], sharedSetupHandle);
});
});

View File

@ -1,103 +1,9 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
"target": "esnext",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}

View File

@ -2,8 +2,7 @@ INCLUDE_DIRS = ../inc
CFLAGS += -O2
c_kzg_4844.o: c_kzg_4844.c Makefile
clang -Wall -I$(INCLUDE_DIRS) $(CFLAGS) -c $<
clang -Wall -I$(INCLUDE_DIRS) $(CFLAGS) -c c_kzg_4844.c sha256.c
lib: c_kzg_4844.c Makefile
clang -Wall -I$(INCLUDE_DIRS) $(CFLAGS) -c c_kzg_4844.c
mv c_kzg_4844.o ../bindings/node.js
lib: c_kzg_4844.o Makefile
mv *.o ../bindings/node.js

View File

@ -19,6 +19,7 @@
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include "sha256.h"
/**
* Wrapped `malloc()` that reports failures to allocate.
@ -1041,44 +1042,34 @@ static C_KZG_RET compute_kzg_proof(KZGProof *out, const Polynomial p, const BLSF
return C_KZG_OK;
}
typedef struct {
unsigned int h[8];
unsigned long long N;
unsigned char buf[64];
size_t off;
} SHA256_CTX;
void sha256_init(SHA256_CTX *ctx);
void sha256_update(SHA256_CTX *ctx, const void *_inp, size_t len);
void sha256_final(unsigned char md[32], SHA256_CTX *ctx);
static void hash(uint8_t md[32], uint8_t input[], size_t n) {
SHA256_CTX ctx;
sha256_init(&ctx);
sha256_update(&ctx, input, n);
sha256_final(md, &ctx);
sha256_final(&ctx, md);
}
static C_KZG_RET hash_to_bytes(uint8_t out[32],
const uint8_t *initializer, const Polynomial polys[], const KZGCommitment comms[], size_t n) {
printf("hash_to_bytes 1\n");
size_t i; uint64_t j;
size_t ni = initializer == NULL ? 0 : 32;
size_t np = ni + n * FIELD_ELEMENTS_PER_BLOB * 32;
printf("hash_to_bytes 2\n");
uint8_t* bytes = calloc(np + n * 48, sizeof(uint8_t));
if (bytes == NULL) return C_KZG_MALLOC;
if (ni) memcpy(bytes, initializer, ni);
printf("hash_to_bytes 3\n");
for (i = 0; i < n; i++)
for (j = 0; j < FIELD_ELEMENTS_PER_BLOB; j++)
bytes_from_bls_field(&bytes[ni + i * 32], &polys[i][j]);
printf("hash_to_bytes 5\n");
for (i = 0; i < n; i++)
bytes_from_g1(&bytes[np + i * 48], &comms[i]);
printf("hash_to_bytes 6\n");
hash(out, bytes, np + n * 48);
printf("hash_to_bytes 7\n");
free(bytes);
return C_KZG_OK;
}
@ -1087,18 +1078,20 @@ static C_KZG_RET compute_aggregated_poly_and_commitment(Polynomial poly_out, KZG
const Polynomial blobs[],
const KZGCommitment kzg_commitments[],
size_t n) {
printf("compute_aggregated_poly_and_commitment 1\n");
BLSFieldElement* r_powers = calloc(n, sizeof(BLSFieldElement));
if (r_powers == NULL) return C_KZG_MALLOC;
printf("compute_aggregated_poly_and_commitment 2\n");
C_KZG_RET ret;
ret = hash_to_bytes(hash_out, NULL, blobs, kzg_commitments, n);
if (ret != C_KZG_OK) { free(r_powers); return ret; }
printf("compute_aggregated_poly_and_commitment 3\n");
bytes_to_bls_field(&r_powers[1], hash_out);
printf("compute_aggregated_poly_and_commitment 4\n");
compute_powers(r_powers, n);
printf("compute_aggregated_poly_and_commitment 5\n");
vector_lincomb(poly_out, blobs, r_powers, n);
printf("compute_aggregated_poly_and_commitment 6\n");
g1_lincomb(comm_out, kzg_commitments, r_powers, n);
free(r_powers);
@ -1109,24 +1102,37 @@ C_KZG_RET compute_aggregate_kzg_proof(KZGProof *out,
const Polynomial blobs[],
size_t n,
const KZGSettings *s) {
printf("RUNNING compute_aggregate_kzg_proof 1");
KZGCommitment* commitments = calloc(n, sizeof(KZGCommitment));
if (commitments == NULL) return C_KZG_MALLOC;
printf("RUNNING compute_aggregate_kzg_proof 2");
for (size_t i = 0; i < n; i++)
blob_to_kzg_commitment(&commitments[i], blobs[i], s);
printf("RUNNING compute_aggregate_kzg_proof 3");
Polynomial aggregated_poly;
KZGCommitment aggregated_poly_commitment;
C_KZG_RET ret;
uint8_t hash[32];
ret = compute_aggregated_poly_and_commitment(aggregated_poly, &aggregated_poly_commitment, hash, blobs, commitments, n);
free(commitments);
printf("RUNNING compute_aggregate_kzg_proof 4");
if (ret != C_KZG_OK) return ret;
TRY(hash_to_bytes(hash, hash, &aggregated_poly, &aggregated_poly_commitment, 1));
printf("RUNNING compute_aggregate_kzg_proof 5");
BLSFieldElement x;
bytes_to_bls_field(&x, hash);
printf("RUNNING compute_aggregate_kzg_proof 6");
return compute_kzg_proof(out, aggregated_poly, &x, s);
}

159
src/sha256.c Normal file
View File

@ -0,0 +1,159 @@
// https://github.com/B-Con/crypto-algorithms/blob/master/sha256.c
/*********************************************************************
* Filename: sha256.c
* Author: Brad Conte (brad AT bradconte.com)
* Copyright:
* Disclaimer: This code is presented "as is" without any guarantees.
* Details: Implementation of the SHA-256 hashing algorithm.
SHA-256 is one of the three algorithms in the SHA2
specification. The others, SHA-384 and SHA-512, are not
offered in this implementation.
Algorithm specification can be found here:
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
This implementation uses little endian byte order.
*********************************************************************/
/*************************** HEADER FILES ***************************/
#include <stdlib.h>
#include <memory.h>
#include "sha256.h"
/****************************** MACROS ******************************/
#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
/**************************** VARIABLES *****************************/
static const WORD k[64] = {
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};
/*********************** FUNCTION DEFINITIONS ***********************/
void sha256_transform(SHA256_CTX *ctx, const BYTE data[])
{
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
for (i = 0, j = 0; i < 16; ++i, j += 4)
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
for ( ; i < 64; ++i)
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
a = ctx->state[0];
b = ctx->state[1];
c = ctx->state[2];
d = ctx->state[3];
e = ctx->state[4];
f = ctx->state[5];
g = ctx->state[6];
h = ctx->state[7];
for (i = 0; i < 64; ++i) {
t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];
t2 = EP0(a) + MAJ(a,b,c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
ctx->state[0] += a;
ctx->state[1] += b;
ctx->state[2] += c;
ctx->state[3] += d;
ctx->state[4] += e;
ctx->state[5] += f;
ctx->state[6] += g;
ctx->state[7] += h;
}
void sha256_init(SHA256_CTX *ctx)
{
ctx->datalen = 0;
ctx->bitlen = 0;
ctx->state[0] = 0x6a09e667;
ctx->state[1] = 0xbb67ae85;
ctx->state[2] = 0x3c6ef372;
ctx->state[3] = 0xa54ff53a;
ctx->state[4] = 0x510e527f;
ctx->state[5] = 0x9b05688c;
ctx->state[6] = 0x1f83d9ab;
ctx->state[7] = 0x5be0cd19;
}
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)
{
WORD i;
for (i = 0; i < len; ++i) {
ctx->data[ctx->datalen] = data[i];
ctx->datalen++;
if (ctx->datalen == 64) {
sha256_transform(ctx, ctx->data);
ctx->bitlen += 512;
ctx->datalen = 0;
}
}
}
void sha256_final(SHA256_CTX *ctx, BYTE hash[])
{
WORD i;
i = ctx->datalen;
// Pad whatever data is left in the buffer.
if (ctx->datalen < 56) {
ctx->data[i++] = 0x80;
while (i < 56)
ctx->data[i++] = 0x00;
}
else {
ctx->data[i++] = 0x80;
while (i < 64)
ctx->data[i++] = 0x00;
sha256_transform(ctx, ctx->data);
memset(ctx->data, 0, 56);
}
// Append to the padding the total message's length in bits and transform.
ctx->bitlen += ctx->datalen * 8;
ctx->data[63] = ctx->bitlen;
ctx->data[62] = ctx->bitlen >> 8;
ctx->data[61] = ctx->bitlen >> 16;
ctx->data[60] = ctx->bitlen >> 24;
ctx->data[59] = ctx->bitlen >> 32;
ctx->data[58] = ctx->bitlen >> 40;
ctx->data[57] = ctx->bitlen >> 48;
ctx->data[56] = ctx->bitlen >> 56;
sha256_transform(ctx, ctx->data);
// Since this implementation uses little endian byte ordering and SHA uses big endian,
// reverse all the bytes when copying the final state to the output hash.
for (i = 0; i < 4; ++i) {
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
}
}

34
src/sha256.h Normal file
View File

@ -0,0 +1,34 @@
/*********************************************************************
* Filename: sha256.h
* Author: Brad Conte (brad AT bradconte.com)
* Copyright:
* Disclaimer: This code is presented "as is" without any guarantees.
* Details: Defines the API for the corresponding SHA1 implementation.
*********************************************************************/
#ifndef SHA256_H
#define SHA256_H
/*************************** HEADER FILES ***************************/
#include <stddef.h>
/****************************** MACROS ******************************/
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
/**************************** DATA TYPES ****************************/
typedef unsigned char BYTE; // 8-bit byte
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
typedef struct {
BYTE data[64];
WORD datalen;
unsigned long long bitlen;
WORD state[8];
} SHA256_CTX;
/*********************** FUNCTION DECLARATIONS **********************/
void sha256_init(SHA256_CTX *ctx);
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);
void sha256_final(SHA256_CTX *ctx, BYTE hash[]);
#endif // SHA256_H