Skip to content

Commit

Permalink
feat: make halo2-lib-js context global
Browse files Browse the repository at this point in the history
  • Loading branch information
rpalakkal committed Nov 22, 2023
1 parent fa904ad commit 927131e
Show file tree
Hide file tree
Showing 20 changed files with 742 additions and 757 deletions.
2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "halo2-wasm-cli",
"version": "0.1.0",
"version": "0.1.1",
"description": "Halo2 Javascript library",
"main": "index.js",
"scripts": {
Expand Down
5 changes: 3 additions & 2 deletions cli/src/examples/circuit.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//@ts-ignore -- to avoid halo2-lib-js being a dependency of the cli
export const circuit = async (halo2Lib: Halo2Lib, inputs: {x: number}) => {
const {add, sub, mul, constant, witness, log, rangeCheck, makePublic, isLessThan} = halo2Lib;
import {add, sub, mul, constant, witness, log, rangeCheck, makePublic, isLessThan} from "@axiom-crypto/halo2-lib-js";

export const circuit = async (inputs: {x: number}) => {
const x = witness(inputs.x);
const a = witness(1);
const b = witness(2);
Expand Down
2 changes: 1 addition & 1 deletion cli/src/examples/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ export const run = async (halo2wasm: any, halo2Lib: any, config: any, circuit: a
//@ts-ignore -- to avoid halo2-lib-js being a dependency of the cli
const { Halo2CircuitRunner } = await import("@axiom-crypto/halo2-lib-js");
await Halo2CircuitRunner(halo2wasm, halo2Lib, config).run(circuit, inputs);
}
}
13 changes: 13 additions & 0 deletions cli/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,21 @@ export async function getFunctionFromTs(relativePath: string, shouldCircuitFunct
compilerOptions: { module: ts.ModuleKind.CommonJS }
});
const script = new vm.Script(result.outputText);
const customRequire = (moduleName: string) => {
try {
const npmRoot = execSync('npm root -g').toString().trim();
return require(`${npmRoot}/${moduleName}`);
} catch (e) {
throw new Error(`Cannot find module '${moduleName}'.\n Try installing it globally with 'npm install -g ${moduleName}'`);
}
};
const context = vm.createContext({
exports: {},
require: customRequire,
module: module,
console: console,
__filename: __filename,
__dirname: __dirname,
});
script.runInContext(context);
if (shouldCircuitFunctionExist && !context.exports.circuit) throw new Error("File does not export a `circuit` function");
Expand Down
2 changes: 1 addition & 1 deletion halo2-lib-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@axiom-crypto/halo2-lib-js",
"version": "0.2.9",
"version": "0.2.10",
"description": "Halo2 Javascript library",
"main": "index.js",
"scripts": {
Expand Down
21 changes: 11 additions & 10 deletions halo2-lib-js/src/circuit/run.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Halo2LibWasm, Halo2Wasm } from "@axiom-crypto/halo2-wasm/web";
import { getInputFunctionSignature } from "../shared/utils";
import { Halo2Lib } from "../halo2lib"
// import { Halo2Lib } from "../halo2lib"
import { CircuitConfig } from "./types";

const parseInputs = (inputs: string) => {
Expand All @@ -10,7 +10,8 @@ const parseInputs = (inputs: string) => {

const BLINDING_FACTOR = 20;

export const autoConfigCircuit = (circuit: Halo2Wasm, config: CircuitConfig) => {
export const autoConfigCircuit = (config: CircuitConfig) => {
let circuit = globalThis.circuit.halo2wasm;
const stats = circuit.getCircuitStats();

for (let i = 6; i < 20; i++) {
Expand All @@ -25,9 +26,12 @@ export const autoConfigCircuit = (circuit: Halo2Wasm, config: CircuitConfig) =>
}


export function Halo2CircuitRunner(circuit: Halo2Wasm, halo2LibWasm: Halo2LibWasm, config: CircuitConfig) {
export function Halo2CircuitRunner(halo2wasm: Halo2Wasm, halo2lib: Halo2LibWasm, config: CircuitConfig, silent?: boolean) {

config = { ...config };
globalThis.circuit = { halo2wasm, halo2lib, silent: silent ?? false };
let circuit = globalThis.circuit.halo2wasm;
let halo2LibWasm = globalThis.circuit.halo2lib;

const clear = () => {
circuit.clear();
Expand All @@ -36,19 +40,17 @@ export function Halo2CircuitRunner(circuit: Halo2Wasm, halo2LibWasm: Halo2LibWas

async function runFromString(code: string, inputs: string) {
clear();
const halo2Lib = new Halo2Lib(circuit, halo2LibWasm, { firstPass: true });
const halo2Lib = await import("../halo2lib/functions");
const halo2LibFns = Object.keys(halo2Lib).filter(key => !(typeof key === 'string' && key.charAt(0) === '_'));
const functionInputs = getInputFunctionSignature(inputs);
const parsedInputs = parseInputs(inputs);
const fn = eval(`let {${halo2LibFns.join(", ")}} = halo2Lib; (async function({${functionInputs}}) { ${code} })`);
await fn(parsedInputs);
circuit.assignInstances();

autoConfigCircuit(circuit, config);
autoConfigCircuit(config);
clear();
{
const halo2Lib = new Halo2Lib(circuit, halo2LibWasm);
const halo2LibFns = Object.keys(halo2Lib).filter(key => !(typeof key === 'string' && key.charAt(0) === '_'));
const fn = eval(`let {${halo2LibFns.join(", ")}} = halo2Lib; (async function({${functionInputs}}) { ${code} })`);
await fn(parsedInputs);
}
Expand All @@ -57,12 +59,11 @@ export function Halo2CircuitRunner(circuit: Halo2Wasm, halo2LibWasm: Halo2LibWas
}
}

async function run<T extends { [key: string]: number | string | bigint }>(f: (halo2Lib: Halo2Lib, inputs: T) => Promise<void>, inputs: T) {
async function run<T extends { [key: string]: number | string | bigint }>(f: (inputs: T) => Promise<void>, inputs: T) {
clear();
let halo2Lib = new Halo2Lib(circuit, halo2LibWasm);
let stringifiedInputs = JSON.stringify(inputs);
let parsedInputs = parseInputs(stringifiedInputs);
await f(halo2Lib, parsedInputs);
await f(parsedInputs);
circuit.assignInstances();
}

Expand Down
9 changes: 9 additions & 0 deletions halo2-lib-js/src/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Halo2LibWasm, Halo2Wasm } from "@axiom-crypto/halo2-wasm/web";

declare global {
namespace circuit {
var halo2wasm: Halo2Wasm;
var halo2lib: Halo2LibWasm;
var silent: boolean;
}
}
12 changes: 6 additions & 6 deletions halo2-lib-js/src/halo2lib/CircuitValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ export class CircuitValue {
private _circuit: Halo2LibWasm;

constructor(
circuit: Halo2LibWasm,
{ value, cell }: { value?: bigint | number | string; cell?: number }
) {
this._circuit = circuit;
//@ts-ignore
this._circuit = globalThis.circuit.halo2lib;
if (value !== undefined) {
this._value = BigInt(value);
this._cell = this._circuit.constant(value.toString());
} else if (cell !== undefined) {
this._cell = cell;
const val = BigInt(circuit.value(cell));
const val = BigInt(this._circuit.value(cell));
this._value = val;
} else {
throw new Error("Invalid input");
Expand Down Expand Up @@ -48,9 +48,9 @@ export class CircuitValue {
b.toString(),
paddedNumBits.toString()
);
const hi128CircuitValue = new CircuitValue(this._circuit, { cell: hi });
const lo128CircuitValue = new CircuitValue(this._circuit, { cell: lo });
const halo2LibValue256 = new CircuitValue256(this._circuit, {
const hi128CircuitValue = new CircuitValue({ cell: hi });
const lo128CircuitValue = new CircuitValue({ cell: lo });
const halo2LibValue256 = new CircuitValue256({
hi: hi128CircuitValue,
lo: lo128CircuitValue,
});
Expand Down
18 changes: 9 additions & 9 deletions halo2-lib-js/src/halo2lib/CircuitValue256.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export class CircuitValue256 {
private _halo2Lib: Halo2LibWasm;

constructor(
_halo2Lib: Halo2LibWasm,
{
value,
hi,
Expand All @@ -19,7 +18,8 @@ export class CircuitValue256 {
lo?: CircuitValue;
}
) {
this._halo2Lib = _halo2Lib;
//@ts-ignore
this._halo2Lib = globalThis.circuit.halo2lib;
if (value !== undefined) {
if (BigInt(value) < 0n) {
throw new Error("Value cannot be negative.");
Expand All @@ -31,11 +31,11 @@ export class CircuitValue256 {
let hi128 = input.slice(0, 32);
let lo128 = input.slice(32);

const hi128CircuitValue = new CircuitValue(_halo2Lib, {
cell: _halo2Lib.constant(convertInput("0x" + hi128)),
const hi128CircuitValue = new CircuitValue({
cell: this._halo2Lib.constant(convertInput("0x" + hi128)),
});
const lo128CircuitValue = new CircuitValue(_halo2Lib, {
cell: _halo2Lib.constant(convertInput("0x" + lo128)),
const lo128CircuitValue = new CircuitValue({
cell: this._halo2Lib.constant(convertInput("0x" + lo128)),
});
this._circuitValue = [hi128CircuitValue, lo128CircuitValue];
} else if (
Expand All @@ -45,8 +45,8 @@ export class CircuitValue256 {
lo instanceof CircuitValue
) {
this._circuitValue = [hi, lo];
const hiVal = BigInt(_halo2Lib.value(hi.cell()));
const loVal = BigInt(_halo2Lib.value(lo.cell()));
const hiVal = BigInt(this._halo2Lib.value(hi.cell()));
const loVal = BigInt(this._halo2Lib.value(lo.cell()));
const value = hiVal * 2n ** 128n + loVal;
this._value = value;
} else {
Expand Down Expand Up @@ -88,6 +88,6 @@ export class CircuitValue256 {
"Cannot convert to CircuitValue (value is > 253 bits). Please use .hi()/.lo() instead."
);
}
return new CircuitValue(this._halo2Lib, { cell });
return new CircuitValue({ cell });
}
}
146 changes: 146 additions & 0 deletions halo2-lib-js/src/halo2lib/ecc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { Bn254FqPoint, Bn254G1AffinePoint, Bn254G2AffinePoint, Secp256k1AffinePoint } from "@axiom-crypto/halo2-wasm/web";
import { CircuitValue } from "./CircuitValue";
import { CircuitValue256 } from "./CircuitValue256";
import { Cell } from "./functions";

export interface CircuitBn254Fq2 {
c0: CircuitValue256;
Expand All @@ -19,3 +22,146 @@ export interface CircuitSecp256k1Affine {
x: CircuitValue256;
y: CircuitValue256;
}

const toJsCircuitValue256 = (val: CircuitValue256) => {
return globalThis.circuit.halo2lib.to_js_circuit_value_256(val.hi().cell(), val.lo().cell());
}

const toJsCircuitBn254G1Affine = (point: CircuitBn254G1Affine) => {
return globalThis.circuit.halo2lib.to_js_circuit_bn254_g1_affine(toJsCircuitValue256(point.x), toJsCircuitValue256(point.y));
}

const toJsCircuitBn254Fq2 = (point: CircuitBn254Fq2) => {
return globalThis.circuit.halo2lib.to_js_circuit_bn254_fq2(toJsCircuitValue256(point.c0), toJsCircuitValue256(point.c1));
}

const toJsCircuitBn254G2Affine = (point: CircuitBn254G2Affine) => {
return globalThis.circuit.halo2lib.to_js_circuit_bn254_g2_affine(toJsCircuitBn254Fq2(point.x), toJsCircuitBn254Fq2(point.y));
}

const toJsCircuitSecp256k1Affine = (point: CircuitSecp256k1Affine) => {
return globalThis.circuit.halo2lib.to_js_circuit_secp256k1_affine(toJsCircuitValue256(point.x), toJsCircuitValue256(point.y));
}

/**
*
* @param val The field point to load, in hi-lo form. The hi, lo values must have been constrained to be `uint128`s.
* @returns `Bn254FqPoint` whose internals are opaque to the user.
*/
const loadBn254Fq = (val: CircuitValue256): Bn254FqPoint => {
return globalThis.circuit.halo2lib.load_bn254_fq(toJsCircuitValue256(val));
}

/**
*
* @param val
* @returns `val` in hi-lo form
*/
const convertBn254FqToCircuitValue256 = (val: Bn254FqPoint) => {
const _val = val.to_circuit_value_256(globalThis.circuit.halo2lib);
return new CircuitValue256({ hi: Cell(_val.hi), lo: Cell(_val.lo) });
}

/**
* @param point The affine point to load, with coordinates `CircuitValue256`. The hi, lo values must have been constrained to be `uint128`s.
* @returns `Bn254G1AffinePoint`, which has been constrained to lie on the curve. Currently this point is not allowed to be identity (0, 0).
*/
const loadBn254G1 = (point: CircuitBn254G1Affine): Bn254G1AffinePoint => {
return globalThis.circuit.halo2lib.load_bn254_g1(toJsCircuitBn254G1Affine(point));
}

/**
* Sums the values of the provided G1 affine points
*
* @param points - The array of `CircuitBn254G1Affine` points. All coordinates are in hi, lo form, and we assume they have been range checked to be `uint128`s.
* @returns The sum of all these points as `Bn254G1AffinePoint`.
*/
const bn254G1Sum = (points: Array<CircuitBn254G1Affine>): Bn254G1AffinePoint => {
const _points = [];
for (let i = 0; i < points.length; i++) {
_points.push(toJsCircuitBn254G1Affine(points[i]));
}
return globalThis.circuit.halo2lib.bn254_g1_sum(_points);
};

/**
* Subtracts the 2 points and returns the value. Constrains that the points are not equal and also one is not the negative of the other (this would be a point doubling, which requires a different formula).
*
* @returns The subtraction of these points.
* @param g1Point1 - G1 point, x,y in hi lo format for each coordinate
* @param g1Point2 - G1 point, x,y in hi lo format for each coordinate
*/

const bn254G1SubUnequal = (g1Point1: CircuitBn254G1Affine, g1Point2: CircuitBn254G1Affine): Bn254G1AffinePoint => {
return globalThis.circuit.halo2lib.bn254_g1_sub_unequal(toJsCircuitBn254G1Affine(g1Point1), toJsCircuitBn254G1Affine(g1Point2));
};

/**
* @param point The affine point to load, with coordinates `CircuitBn254Fq2`. The hi, lo values must have been constrained to be `uint128`s.
* @returns `Bn254G2AffinePoint`, which has been constrained to lie on the curve. Currently this point is not allowed to be identity (Fq2(0), Fq2(0)).
*/
const loadBn254G2 = (point: CircuitBn254G2Affine): Bn254G2AffinePoint => {
return globalThis.circuit.halo2lib.load_bn254_g2(toJsCircuitBn254G2Affine(point));
}

/**
* Sums the values of the provided G2 affine points
*
* @param points - The array of `CircuitBn254G2Affine` points. All coordinates are `CircuitBn254Fq2`, whose coordinates are in hi, lo form, and we assume the hi, lo's have been range checked to be `uint128`s.
* @returns The sum of all these points as `Bn254G2AffinePoint`.
*/
const bn254G2Sum = (points: Array<CircuitBn254G2Affine>): Bn254G2AffinePoint => {
const _points = [];
for (let i = 0; i < points.length; i++) {
_points.push(toJsCircuitBn254G2Affine(points[i]));
}
return globalThis.circuit.halo2lib.bn254_g2_sum(_points);
}

/**
* Verifies that e(lhsG1, lhsG2) = e(rhsG1, rhsG2) by checking e(lhsG1, lhsG2)*e(-rhsG1, rhsG2) === 1
* None of the points should be identity.
*
* @param lhsG1
* @param lhsG2
* @param rhsG1
* @param rhsG2
* @returns [CircuitValue] for the result as a boolean (1 if signature verification is successful).
*/
const bn254PairingCheck = (lhsG1: Bn254G1AffinePoint, lhsG2: Bn254G2AffinePoint, rhsG1: Bn254G1AffinePoint, rhsG2: Bn254G2AffinePoint): CircuitValue => {
return Cell(globalThis.circuit.halo2lib.bn254_pairing_check(lhsG1, lhsG2, rhsG1, rhsG2));
}

/**
* @param pubkey The public key to load, in the form of an affine elliptic curve point `(x, y)` where `x, y` have type `CircuitValue256`. The hi, lo values of each `CircuitValue256` must have been constrained to be `uint128`s.
* @returns `Secp256k1AffinePoint`, the public key as a loaded elliptic curve point. This has been constrained to lie on the curve. The public key is constrained to not be the identity (0, 0).
*/
const loadSecp256k1Pubkey = (pubkey: CircuitSecp256k1Affine): Secp256k1AffinePoint => {
return globalThis.circuit.halo2lib.load_secp256k1_pubkey(toJsCircuitSecp256k1Affine(pubkey));
}

/**
*
* Verifies the ECDSA signature `(r, s)` with message hash `msgHash` using the secp256k1 public key `pubkey`. Returns 1 if the signature is valid, 0 otherwise.
* @param pubkey
* @param r
* @param s
* @param msgHash
* @returns
*/
const verifySecp256k1ECDSASignature = (pubkey: Secp256k1AffinePoint, r: CircuitValue256, s: CircuitValue256, msgHash: CircuitValue256): CircuitValue => {
return Cell(globalThis.circuit.halo2lib.verify_secp256k1_ecdsa_signature(pubkey, toJsCircuitValue256(r), toJsCircuitValue256(s), toJsCircuitValue256(msgHash)));
}

export {
loadBn254Fq,
convertBn254FqToCircuitValue256,
loadBn254G1,
bn254G1Sum,
bn254G1SubUnequal,
loadBn254G2,
bn254G2Sum,
bn254PairingCheck,
loadSecp256k1Pubkey,
verifySecp256k1ECDSASignature
}
Loading

0 comments on commit 927131e

Please sign in to comment.