/*
Copyright 2018 0KIMS association.
This file is part of circom (Zero Knowledge Circuit Compiler).
circom is a free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
circom is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with circom. If not, see .
*/
const assert = require("assert");
const bigInt = require("big-integer");
const utils = require("./utils");
const gen = require("./c_gen").gen;
const createRefs = require("./c_gen").createRefs;
module.exports = buildC;
function buildC(ctx) {
ctx.definedFunctions = {};
ctx.functionCodes = [];
ctx.buildFunction = buildFunction;
ctx.code = "";
ctx.conditionalCodeHeader = "";
ctx.codes_sizes = [];
ctx.definedSizes = {};
ctx.addSizes = addSizes;
const entryTables = buildEntryTables(ctx);
ctx.globalNames = ctx.uniqueNames;
const code = buildCode(ctx);
const functions = buildFuncFunctions(ctx);
const compnentsArray = buildComponentsArray(ctx);
const headder = buildHeader(ctx);
const sizes = buildSizes(ctx);
const mapIsInput = buildMapIsInput(ctx);
const wit2Sig = buildWit2Sig(ctx);
const circuitVar = buildCircuitVar(ctx);
return "" +
headder + "\n" +
sizes + "\n" +
entryTables + "\n" +
functions + "\n" +
code + "\n" +
compnentsArray + "\n" +
mapIsInput + "\n" +
wit2Sig +"\n" +
circuitVar;
}
function buildEntryTables(ctx) {
const codes_hashMaps = [];
const codes_componentEntries = [];
const definedHashTables = {};
for (let i=0; i0 ? " ," : " ";
const sizeName = ctx.addSizes(entry.sizes);
const ty = entry.type == "S" ? "_typeSignal" : "_typeComponent";
code += `{${entry.offset},${sizeName}, ${ty}}\n`;
}
code += "};\n";
codes_componentEntries.push(code);
ctx.components[i].htName = htName;
ctx.components[i].etName = componentEntriesTableName;
}
return "" +
"// HashMaps\n" +
codes_hashMaps.join("\n") + "\n" +
"\n" +
"// Component Entries\n" +
codes_componentEntries.join("\n") + "\n" +
"\n";
function addHashTable(cIdx) {
const keys = Object.keys(ctx.components[cIdx].names.o);
assert(keys.length<128);
keys.sort((a,b) => ((a>b) ? 1 : -1));
const h = utils.fnvHash(keys.join(","));
if (definedHashTables[h]) return definedHashTables[h];
definedHashTables[h] = {};
definedHashTables[h].htName = ctx.getUniqueName("_ht"+ctx.components[cIdx].template);
definedHashTables[h].htMap = [];
const t = [];
for (let i=0; i0 ? "," : "";
if (t[i]) {
code += `{0x${t[i][0]}LL, ${t[i][1]}} /* ${keys[t[i][1]]} */`;
} else {
code += "{0,0}";
}
}
code += "};\n";
codes_hashMaps.push(code);
return definedHashTables[h];
}
}
function buildCode(ctx) {
const fDefined = {};
const fnComponents = [];
for (let i=0; i0 ? " ," : " ");
ccodes.push(`{${ctx.components[i].htName},${ctx.components[i].etName},${ctx.components[i].fnName}, ${ctx.components[i].nInSignals}}\n`);
}
ccodes.push("};\n");
const codeComponents = ccodes.join("");
return "" +
"// Components\n" +
codeComponents +
"\n";
}
function buildHeader(ctx) {
return "#include \"circom.h\"\n" +
"#include \"calcwit.h\"\n" +
`#define NSignals ${ctx.signals.length}\n` +
`#define NComponents ${ctx.components.length}\n` +
`#define NInputs ${ctx.components[ ctx.getComponentIdx("main") ].nInSignals}\n`+
`#define NOutputs ${ctx.totals[ ctx.stOUTPUT ]}\n`+
`#define NVars ${ctx.totals[ctx.stONE] + ctx.totals[ctx.stOUTPUT] + ctx.totals[ctx.stPUBINPUT] + ctx.totals[ctx.stPRVINPUT] + ctx.totals[ctx.stINTERNAL]}\n` +
`#define __P__ "${ctx.field.p.toString()}"\n` +
"\n";
}
function buildSizes(ctx) {
return "// Sizes\n" +
ctx.codes_sizes.join("\n");
}
function buildMapIsInput(ctx) {
const arr = [];
let line = "";
let acc = 0;
let i;
for (i=0; i31) ? "," : " ";
line += toHex(acc);
acc = 0;
if ( (i+1) % (32*64) == 0) {
arr.push(line);
line = "";
}
}
}
if ((i%32) != 0) {
line += (i>31) ? "," : " ";
line += toHex(acc);
}
if (line != "") {
arr.push(line);
}
return "// mapIsArray\n" +
`u32 _mapIsInput[${Math.floor((ctx.signals.length-1) / 32)+1}] = {\n`+
arr.join("\n") + "\n" +
"};\n";
function toHex(number) {
if (number < 0) number = 0xFFFFFFFF + number + 1;
let S=number.toString(16).toUpperCase();
while (S.length<8) S = "0" + S;
return "0x"+S;
}
}
function buildWit2Sig(ctx) {
const codes = [];
const NVars =
ctx.totals[ctx.stONE] +
ctx.totals[ctx.stOUTPUT] +
ctx.totals[ctx.stPUBINPUT] +
ctx.totals[ctx.stPRVINPUT] +
ctx.totals[ctx.stINTERNAL];
const arr = Array(NVars);
for (let i=0; i=0) continue; // If has an alias, continue..
assert(typeof outIdx != "undefined", `Signal ${i} does not have index`);
if (outIdx>=NVars) continue; // Is a constant or a discarded variable
if (typeof arr[ctx.signals[i].id] == "undefined") {
arr[outIdx] = i;
}
}
codes.push("// Signal Table\n");
codes.push(`int _wit2sig[${NVars}] = {\n`);
let code = "";
for (let i=0; i0) ? ",": " ";
code += arr[i];
if ((i>0)&&(i%64 == 0)) {
if (code != "") {
codes.push(code + "\n");
} else {
codes.push(code);
}
code ="";
}
}
if (code != "") codes.push(code + "\n");
codes.push("};\n");
return codes.join("");
}
function buildCircuitVar() {
return "Circom_Circuit _circuit = {\n" +
" NSignals,\n"+
" NComponents,\n"+
" NInputs,\n"+
" NOutputs,\n"+
" NVars,\n"+
" _wit2sig,\n"+
" _components,\n"+
" _mapIsInput,\n"+
" __P__\n" +
"};\n";
}
function addSizes(_sizes) {
const sizes = _sizes || [];
let name = "sizes";
for (let i=0; i0) code += ",";
code += accSizes[i];
}
code += "};\n";
this.codes_sizes.push(code);
return labelName;
}
function buildFunction(name, paramValues) {
const ctx = this;
const {h, instanceDef} = hashFunctionCall(ctx, name, paramValues);
if (ctx.definedFunctions[h]) return ctx.definedFunctions[h];
const res = {
fnName: `${name}_${h}`
};
const oldRefs = ctx.refs;
const oldConditionalCode = ctx.conditionalCode;
const oldCode = ctx.code;
const oldCodeHeader = ctx.codeHeader;
const oldCodeFooter = ctx.codeFooter;
const oldUniqueNames = ctx.uniqueNames;
const oldFileName = ctx.fileName;
const oldFilePath = ctx.oldFilePath;
ctx.scopes = [{}];
ctx.refs = [];
ctx.conditionalCode = false;
ctx.code = "";
ctx.codeHeader = "// Header\n";
ctx.codeFooter = "// Footer\n";
ctx.uniqueNames = Object.assign({},ctx.globalNames);
ctx.returnValue = null;
ctx.returnSizes = null;
ctx.fileName = ctx.functions[name].fileName;
ctx.filePath = ctx.functions[name].filePath;
let paramsStr = "";
for (let i=0; i {
if (utils.isDefined(ctx.signals[offset].v)) {
constParams.push(prefix + "=" + bigInt(ctx.signals[offset].value));
}
});
}
}
let instanceDef = ctx.components[cIdx].template;
if (constParams.length>0) {
instanceDef += "\n";
constParams.sort();
instanceDef += constParams.join("\n");
}
const h = utils.fnvHash(instanceDef);
return {h, instanceDef};
function travelSizes(prefix, offset, sizes, fn) {
if (sizes.length == 0) {
fn(prefix, offset);
return 1;
} else {
let o = offset;
for (let i=0; i0) {
instanceDef += "\n";
constParams.sort();
instanceDef += constParams.join("\n");
}
const h = utils.fnvHash(instanceDef);
return {h, instanceDef};
}
function value2str(v) {
if (Array.isArray(v)) {
let S="[";
for (let i=0; i0) S+=",";
S+=value2str(v[i]);
}
S+="]";
return S;
} else {
return bigInt(v).toString();
}
}