@xorddotcom/shield
Version:
p align="center" > <img src="https://xord.notion.site/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F283b98b7-fdae-4e5a-acaf-248242084e4a%2FICON.png?table=block&id=5306223c-a4f7-45d1-9f54-b9a5f4004cd6&spaceId=49976899-64a1-40f
211 lines • 7.74 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Checker = void 0;
// @ts-ignore
const r1csfile_1 = require("r1csfile");
// @ts-ignore
const ffjavascript_1 = require("ffjavascript");
const logger_1 = require("./logger");
const assert_1 = __importDefault(require("assert"));
const nodefs = require("fs");
const fs = require("fs/promises");
const groupOrderPrimeStr = "21888242871839275222246405745257275088548364400416034343698204186575808495617";
const groupOrderPrime = BigInt(groupOrderPrimeStr);
class Checker {
r1csFilepath;
symFilepath;
r1cs;
symbols;
signals;
constructor(r1csFilepath, symFilepath) {
this.r1csFilepath = r1csFilepath;
this.symFilepath = symFilepath;
}
async load() {
this.r1cs = await (0, r1csfile_1.load)(this.r1csFilepath, true, false);
const { symbols, signals } = await readSymbols(this.symFilepath);
this.symbols = symbols;
this.signals = signals;
}
async checkConstraintsAndOutput(witnessFilePath) {
// 0. load r1cs and witness
try {
if (!this.r1cs) {
await this.load();
}
let witness;
if (witnessFilePath.endsWith("json")) {
witness = JSON.parse(nodefs.readFileSync(witnessFilePath).toString());
}
// 1. check constraints
const F = new ffjavascript_1.ZqField(this.r1cs?.prime);
const constraints = this.r1cs?.constraints;
if (this.r1cs.nConstraints !== 0) {
await checkConstraints(F, constraints, witness, this.signals);
}
else {
(0, logger_1.log)("No quadratic constraint signal found:\n", "info");
}
return true;
}
catch (err) {
console.log({ err });
}
}
}
exports.Checker = Checker;
async function readSymbols(path) {
let symbols = {};
let signals = {};
const symsStr = await fs.readFile(path, "utf8");
const lines = symsStr.split("\n");
for (let i = 0; i < lines.length; i++) {
const arr = lines[i].split(",");
if (arr.length != 4)
continue;
const symbol = arr[3];
const labelIdx = Number(arr[0]);
const varIdx = Number(arr[1]);
const componentIdx = Number(arr[2]);
symbols[symbol] = {
labelIdx,
varIdx,
componentIdx,
};
if (signals[varIdx] == null) {
signals[varIdx] = [symbol];
}
else {
signals[varIdx].push(symbol);
}
}
return { symbols, signals };
}
async function checkConstraints(F, constraints, witness, signals) {
if (!constraints) {
throw new Error("empty constraints");
}
for (let i = 0; i < constraints.length; i++) {
checkConstraint(constraints[i], i);
}
function checkConstraint(constraint, initializer) {
const a = evalLC(constraint[0]);
const b = evalLC(constraint[1]);
const c = evalLC(constraint[2]);
const passed = F.isZero(F.sub(F.mul(a, b), c));
(0, logger_1.log)(`Constraint no ${initializer + 1}: ${passed ? "passed" : "fail"}`, passed ? "success" : "error");
const equation = `${displayLc(constraint[0])} * ${displayLc(constraint[1])} = ${displayLc(constraint[2])}`;
(0, logger_1.log)(`=> ${equation}`, "normal");
if (!passed) {
(0, logger_1.log)("\nInvalid constraint:", "error");
(0, logger_1.log)(`=> ${equation}`, "error");
let sigs = new Set();
for (const c of constraint) {
for (const s in c) {
sigs.add(Number(s));
}
}
let calculatedEquation = equation;
for (const s of sigs) {
if (s != 0) {
calculatedEquation = calculatedEquation.replaceAll(`signal${s}`, witness[s]);
}
else {
calculatedEquation = calculatedEquation.replace(`signal${0}`, "1");
}
}
(0, logger_1.log)(`=> ${calculatedEquation}`, "error");
(0, logger_1.log)("Related signals:", "normal");
for (const s of sigs) {
// signal 0 is 'one'
if (s != 0) {
(0, logger_1.log)(`signal${s}: ${signals[s].join(" ")}, value: ${witness[s]}`, "normal");
}
}
(0, logger_1.log)(`please check your circuit and input at constraint ${initializer + 1} \n`, "error");
throw new Error("Constraint doesn't match");
}
else {
let sigs = new Set();
for (const c of constraint) {
for (const s in c) {
sigs.add(Number(s));
}
}
let calculatedEquation = equation;
for (const s of sigs) {
if (s != 0) {
calculatedEquation = calculatedEquation.replace(`signal${s}`, witness[s]);
}
else {
calculatedEquation = calculatedEquation.replaceAll(`signal${0}`, "1");
}
}
(0, logger_1.log)(`=> ${calculatedEquation}`, "normal");
(0, logger_1.log)("Related signals:", "normal");
for (const s of sigs) {
if (s != 0) {
(0, logger_1.log)(`signal${s}: ${signals[s].join(" ")}, value: ${witness[s]}`, "normal");
}
else {
(0, logger_1.log)(`signal0: constant, value: 1`, "normal");
}
}
}
}
function evalLC(lc) {
let v = F.zero;
for (let w in lc) {
v = F.add(v, F.mul(BigInt(lc[w]), BigInt(witness[w])));
}
return v;
}
function displayLc(lc) {
const entries = Object.entries(lc);
if (entries.length == 0) {
return "0";
}
function displayField(x) {
const f = BigInt(x);
// display some field element as negative int for better reading
if (f >= groupOrderPrime - 200n) {
return `(-${(groupOrderPrime - f).toString()})`;
}
return f.toString();
}
function displayMonomial(coef, signalIdx) {
return `${displayField(coef)}*signal${signalIdx}`;
}
return ("(" + entries.map((kv) => displayMonomial(kv[1], kv[0])).join(" + ") + ")");
}
}
async function assertOut(symbols, actualOut, expectedOut) {
if (!symbols) {
throw new Error("empty symbols");
}
checkObject("main", expectedOut);
function checkObject(prefix, eOut) {
if (Array.isArray(eOut)) {
for (let i = 0; i < eOut.length; i++) {
checkObject(prefix + "[" + i + "]", eOut[i]);
}
}
else if (typeof eOut == "object" && eOut.constructor.name == "Object") {
for (let k in eOut) {
checkObject(prefix + "." + k, eOut[k]);
}
}
else {
if (typeof symbols[prefix] == "undefined") {
(0, assert_1.default)(false, "Output variable not defined: " + prefix);
}
const ba = actualOut[symbols[prefix].varIdx].toString();
const be = eOut.toString();
assert_1.default.strictEqual(ba, be, prefix);
}
}
}
//# sourceMappingURL=checker.js.map