Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add circomx #233

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,19 @@ keystore

.idea/
proof.json
output.json
output.json


.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

# Swap the comments on the following lines if you don't wish to use zero-installs
# Documentation here: https://yarnpkg.com/features/zero-installs
!.yarn/cache
#.pnp.*

node_modules
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@
],
"rust-analyzer.diagnostics.disabled": [
"unresolved-proc-macro"
],
],
"editor.defaultFormatter": null,

}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added .yarn/cache/fsevents-patch-21ad2b1333-8.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
37 changes: 37 additions & 0 deletions circomx/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@succinctlabs/circomx",
"version": "1.0.0",
"packageManager": "[email protected]",
"type": "module",
"main": "src/index.ts",
"types": "src/index.ts",
"dependencies": {
"@chainsafe/persistent-merkle-tree": "^0.6.1",
"@chainsafe/ssz": "^0.13.0",
"@lodestar/api": "1.9.2",
"@lodestar/config": "1.9.2",
"@lodestar/types": "^1.11.3",
"@noble/bls12-381": "^1.4.0",
"@types/fnv-plus": "^1.3.0",
"@types/node": "^20.4.6",
"@types/yargs": "^17.0.26",
"@vercel/ncc": "^0.38.0",
"axios": "^1.5.1",
"circomlib": "^2.0.5",
"dotenv": "^16.3.1",
"ethers": "5",
"fnv-plus": "^1.3.1",
"tsx": "^3.13.0",
"typescript": "^5.2.2",
"yargs": "^17.7.2"
},
"engines": {
"node": "18.x"
},
"devDependencies": {
"typescript": "^5.1.6"
},
"exports": {
".": "./src/index.ts"
}
}
21 changes: 21 additions & 0 deletions circomx/src/bigint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { PointG1 } from "@noble/bls12-381";

export function pointToBigInt(point: PointG1): [bigint, bigint] {
const [x, y] = point.toAffine();
return [x.value, y.value];
}

export function bigIntToArray(n: number, k: number, x: bigint) {
let mod = 1n;
for (let idx = 0; idx < n; idx++) {
mod = mod * 2n;
}

const ret: string[] = [];
let x_temp: bigint = x;
for (let idx = 0; idx < k; idx++) {
ret.push((x_temp % mod).toString());
x_temp = x_temp / mod;
}
return ret;
}
227 changes: 227 additions & 0 deletions circomx/src/circuit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import { toHexString } from "@chainsafe/ssz";
import dotenv from "dotenv";
import fs from "fs";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { encodeGroth16Proof, executeCommand } from "./util";
import { CircomInput } from "./serializer";

const NODE_OPTIONS =
"--huge-max-old-generation-size --max-old-space-size=2048000 --initial-old-space-size=2048000 --no-global-gc-scheduling --no-incremental-marking --max-semi-space-size=2048000 --initial-heap-size=2048000 --expose-gc";

// @ts-ignore
BigInt.prototype.toJSON = function () {
return this.toString();
};

export type ProofData = {
witness: CircomInput;
outputBytes: Uint8Array;
};

export abstract class Circuit {
constructor() {}

abstract generateProofData(inputBytes: Buffer): Promise<ProofData>;

abstract circuitName(): string;

build(snarkjsPath: string, circomPath: string, ptauPath: string) {
const circuit = this.circuitName();

// Create build dir if not exists
if (!fs.existsSync("build")) {
fs.mkdirSync("build");
}

executeCommand(
`${circomPath} circuits/${circuit}.circom --O1 --r1cs --sym --c --output build`
);
const circuitName = circuit === "main" ? "main_c" : circuit;
const buildDirName = `${circuitName}_cpp`;
executeCommand(`make -C build/${buildDirName}/`);
executeCommand(
`cp build/${buildDirName}/${circuitName} build/${circuitName}`
);
executeCommand(
`cp build/${buildDirName}/${circuitName}.dat build/${circuitName}.dat`
);
executeCommand(`rm -rf build/${buildDirName}`);
executeCommand(`rm -rf build/${circuit}_cpp`);
executeCommand(
`node ${NODE_OPTIONS} ${snarkjsPath} zkey new build/${circuitName}.r1cs ${ptauPath} build/p1.zkey`
);
executeCommand(
`node ${NODE_OPTIONS} ${snarkjsPath} zkey export verificationkey build/p1.zkey build/vkey.json`
);
executeCommand(
`node ${NODE_OPTIONS} ${snarkjsPath} zkey export solidityverifier build/p1.zkey build/FunctionVerifier.sol`
);

// Replace first line of FunctionVerifier.sol with "pragma solidity ^0.8.0;"
let solidityVerifier = fs.readFileSync(
"build/FunctionVerifier.sol",
"utf8"
);
solidityVerifier = solidityVerifier.replaceAll("calldataload", "mload");
solidityVerifier = solidityVerifier.replaceAll("calldata", "memory");
solidityVerifier = solidityVerifier.replaceAll(
"_pB, _pC",
// for some reason, uint256[2][2] memory _pB has two words (two lengths?) prepended to it
// and calldata doesn't have it
"add(_pB, 64), _pC"
);
solidityVerifier = solidityVerifier.replaceAll(
"pragma solidity >=0.7.0 <0.9.0;",
"pragma solidity ^0.8.16;"
);
solidityVerifier += `

interface IFunctionVerifier {
function verify(bytes32 _inputHash, bytes32 _outputHash, bytes memory _proof) external view returns (bool);

function verificationKeyHash() external pure returns (bytes32);
}

contract FunctionVerifier is IFunctionVerifier, Groth16Verifier {

function verify(bytes32 _inputHash, bytes32 _outputHash, bytes memory _proof) external view returns (bool) {
(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c) =
abi.decode(_proof, (uint256[2], uint256[2][2], uint256[2]));

uint256[2] memory input = [uint256(_outputHash), uint256(_inputHash)];
input[0] = input[0] & ((1 << 253) - 1);
input[1] = input[1] & ((1 << 253) - 1);

return verifyProof(a, b, c, input);
}

function verificationKeyHash() external pure returns (bytes32) {
bytes memory left;
bytes memory right;
{
left = abi.encode(alphax, alphay, betax1, betax2, betay1, betay2);
}
{
right = abi.encode(gammax1, gammax2, gammay1, gammay2, deltax1, deltax2, deltay1, deltay2);
}
return keccak256(abi.encode(left, right));
}
}
`;
fs.writeFileSync("build/FunctionVerifier.sol", solidityVerifier);
}

async prove(rapidsnarkPath: string, inputJsonPath: string) {
const circuit = this.circuitName();
const circuitName = circuit === "main" ? "main_c" : circuit;

const data = fs.readFileSync(inputJsonPath, "utf8");
const jsonData = JSON.parse(data);
console.log(jsonData);

let hexString = jsonData.data.input;

// Remove '0x' prefix if it exists
if (hexString.startsWith("0x")) {
hexString = hexString.slice(2);
}

const buffer = Buffer.from(hexString, "hex");

const proofData = await this.generateProofData(buffer);

fs.writeFileSync("witness.json", JSON.stringify(proofData.witness));

executeCommand(`./build/${circuitName} witness.json witness.wtns`);
executeCommand(
`${rapidsnarkPath} build/p1.zkey witness.wtns proof.json public.json`
);

const publicData = fs.readFileSync("public.json", "utf8");
const publicJsonData = JSON.parse(publicData);
console.log(publicJsonData);

const proofDataFile = fs.readFileSync("proof.json", "utf8");
const proofJsonData = JSON.parse(proofDataFile);

// // TODO: sanity check circuit inputs
// const circuitGeneratedInputs = publicJsonData.map((v: string) => {
// const hex = BigInt(v).toString(16);
// const paddedLen = Math.ceil(hex.length / 2) * 2;
// return hex.padStart(paddedLen, "0");
// });

const outputBytes = toHexString(proofData.outputBytes);

const proofBytes = encodeGroth16Proof(proofJsonData);
const outputJson = {
type: "res_bytes",
data: {
proof: proofBytes,
output: outputBytes,
},
};

fs.writeFileSync("output.json", JSON.stringify(outputJson));
console.log("Done");
}

entrypoint() {
dotenv.config();
yargs(hideBin(process.argv))
.command(
"build",
"Run build commands",
(yargs) => {
yargs
.option("snarkjs", {
describe: "Path to snarkjs cli.js",
type: "string",
default: "/root/snarkjs/cli.js",
})
.option("circom", {
describe: "circom executable",
type: "string",
default: "circom",
})
.option("ptau", {
describe: "Path to powersOfTau.ptau",
type: "string",
default: "/root/powersOfTau.ptau",
});
},
(args) => {
const snarkjsPath = args.snarkjs as string;
const circomPath = args.circom as string;
const ptauPath = args.ptau as string;
this.build(snarkjsPath, circomPath, ptauPath);
}
)
.command(
"prove",
"Run prove commands",
(yargs) => {
yargs
.option("input-json", {
describe: "Path to the input JSON file",
type: "string",
default: "input.json",
})
.option("rapidsnark", {
describe: "Rapidsnark command",
type: "string",
default: "rapidsnark",
});
},
async (args) => {
const rapidsnarkPath = args.rapidsnark as string;
const inputJsonPath = args["input-json"] as string;

await this.prove(rapidsnarkPath, inputJsonPath);
}
)
.demandCommand(1, "You need to provide a command (either build or prove)")
.parse();
}
}
Loading
Loading