Browse Source

All operators finished

feature/witness_bin
Jordi Baylina 5 years ago
parent
commit
afa8201c2c
No known key found for this signature in database GPG Key ID: 7480C80C1BE43112
8 changed files with 178 additions and 72 deletions
  1. +8
    -1
      c/main.cpp
  2. +82
    -71
      src/c_gen.js
  3. +3
    -0
      src/c_tester.js
  4. +1
    -0
      src/zqfield.js
  5. +41
    -0
      test/basiccases.js
  6. +15
    -0
      test/circuits/condternary.circom
  7. +16
    -0
      test/circuits/whilerolled.circom
  8. +12
    -0
      test/circuits/whileunrolled.circom

+ 8
- 1
c/main.cpp

@ -100,7 +100,14 @@ void loadJson(Circom_CalcWit *ctx, std::string filename) {
for (json::iterator it = j.begin(); it != j.end(); ++it) { for (json::iterator it = j.begin(); it != j.end(); ++it) {
// std::cout << it.key() << " => " << it.value() << '\n'; // std::cout << it.key() << " => " << it.value() << '\n';
u64 h = fnv1a(it.key()); u64 h = fnv1a(it.key());
int o = ctx->getSignalOffset(0, h);
int o;
try {
o = ctx->getSignalOffset(0, h);
} catch (std::runtime_error e) {
std::ostringstream errStrStream;
errStrStream << "Error loadin variable: " << it.key() << "\n" << e.what();
throw std::runtime_error(errStrStream.str() );
}
Circom_Sizes sizes = ctx->getSignalSizes(0, h); Circom_Sizes sizes = ctx->getSignalSizes(0, h);
iterateArr(ctx, o, sizes, it.value(), itFunc); iterateArr(ctx, o, sizes, it.value(), itFunc);
} }

+ 82
- 71
src/c_gen.js

@ -229,11 +229,11 @@ function gen(ctx, ast) {
} else if (ast.type == "BLOCK") { } else if (ast.type == "BLOCK") {
return genBlock(ctx, ast); return genBlock(ctx, ast);
} else if (ast.type == "COMPUTE") { } else if (ast.type == "COMPUTE") {
return genCompute(ctx, ast);
return gen(ctx, ast.body);
} else if (ast.type == "FOR") { } else if (ast.type == "FOR") {
return genFor(ctx, ast);
return genLoop(ctx, ast);
} else if (ast.type == "WHILE") { } else if (ast.type == "WHILE") {
return genWhile(ctx, ast);
return genLoop(ctx, ast);
} else if (ast.type == "IF") { } else if (ast.type == "IF") {
return genIf(ctx, ast); return genIf(ctx, ast);
} else if (ast.type == "RETURN") { } else if (ast.type == "RETURN") {
@ -283,11 +283,18 @@ function genSrcComment(ctx, ast) {
ctx.code += "\n/* "+code+" */\n"; ctx.code += "\n/* "+code+" */\n";
} }
function genForSrcComment(ctx, ast) {
const init = getSource(ctx, ast.init);
const condition = getSource(ctx, ast.condition);
const step = getSource(ctx, ast.step);
ctx.code += `\n/* for (${init},${condition},${step}) */\n`;
function genLoopSrcComment(ctx, ast) {
if (ast.type == "FOR") {
const init = getSource(ctx, ast.init);
const condition = getSource(ctx, ast.condition);
const step = getSource(ctx, ast.step);
ctx.code += `\n/* for (${init},${condition},${step}) */\n`;
} else if (ast.type == "WHILE") {
const condition = getSource(ctx, ast.condition);
ctx.code += `\n/* while (${condition}) */\n`;
} else {
assert(false, "Invalid loop type: "+ ast.type);
}
} }
function genIfSrcComment(ctx, ast) { function genIfSrcComment(ctx, ast) {
@ -835,12 +842,14 @@ function leaveConditionalCode(ctx) {
} }
} }
function genFor(ctx, ast) {
genForSrcComment(ctx, ast);
function genLoop(ctx, ast) {
genLoopSrcComment(ctx, ast);
let inLoop = false; let inLoop = false;
ctx.scopes.push({});
gen(ctx, ast.init);
if (ctx.error) return;
if (ast.init) {
gen(ctx, ast.init);
if (ctx.error) return;
}
let end=false; let end=false;
let condVarRef; let condVarRef;
@ -878,8 +887,10 @@ function genFor(ctx, ast) {
gen(ctx, ast.body); gen(ctx, ast.body);
if (ctx.error) return; if (ctx.error) return;
gen(ctx, ast.step);
if (ctx.error) return;
if (ast.step) {
gen(ctx, ast.step);
if (ctx.error) return;
}
const condRef2 = gen(ctx, ast.condition); const condRef2 = gen(ctx, ast.condition);
if (ctx.error) return; if (ctx.error) return;
@ -919,20 +930,6 @@ function genFor(ctx, ast) {
ctx.scopes.pop(); ctx.scopes.pop();
} }
function genWhile(ctx, ast) {
const condition = gen(ctx, ast.condition);
if (ctx.error) return;
const body = gen(ctx, ast.body);
if (ctx.error) return;
return `while (bigInt(${condition}).neq(bigInt(0))) {\n${body}\n}\n`;
}
function genCompute(ctx, ast) {
const body = gen(ctx, ast.body);
if (ctx.error) return;
return `{\n${body}\n}\n`;
}
function genIf(ctx, ast) { function genIf(ctx, ast) {
genIfSrcComment(ctx, ast); genIfSrcComment(ctx, ast);
const condRef = gen(ctx, ast.condition); const condRef = gen(ctx, ast.condition);
@ -1095,54 +1092,68 @@ function genOp(ctx, ast, op, nOps) {
return rRef; return rRef;
} }
function genBAnd(ctx, ast) {
const a = gen(ctx, ast.values[0]);
if (ctx.error) return;
const b = gen(ctx, ast.values[1]);
if (ctx.error) return;
return `bigInt(${a}).and(bigInt(${b})).and(__MASK__)`;
}
function genAnd(ctx, ast) {
const a = gen(ctx, ast.values[0]);
if (ctx.error) return;
const b = gen(ctx, ast.values[1]);
function genTerCon(ctx, ast) {
const condRef = gen(ctx, ast.values[0]);
if (ctx.error) return; if (ctx.error) return;
return `((bigInt(${a}).neq(bigInt(0)) && bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`;
}
const cond = ctx.refs[condRef];
if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
function genOr(ctx, ast) {
const a = gen(ctx, ast.values[0]);
if (ctx.error) return;
const b = gen(ctx, ast.values[1]);
if (ctx.error) return;
return `((bigInt(${a}).neq(bigInt(0)) || bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`;
}
let oldCode;
if (cond.used) {
enterConditionalCode(ctx, ast);
function genShl(ctx, ast) {
const a = gen(ctx, ast.values[0]);
if (ctx.error) return;
const b = gen(ctx, ast.values[1]);
if (ctx.error) return;
return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shl(bigInt(${b})).and(__MASK__)`;
}
const rLabel = ctx.getUniqueName("_ter");
function genShr(ctx, ast) {
const a = gen(ctx, ast.values[0]);
if (ctx.error) return;
const b = gen(ctx, ast.values[1]);
if (ctx.error) return;
return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shr(bigInt(${b})).and(__MASK__)`;
}
ctx.codeHeader += `PBigInt ${rLabel};\n`;
ctx.code += `if (ctx->field->isTrue(${cond.label})) {\n`;
function genTerCon(ctx, ast) {
const a = gen(ctx, ast.values[0]);
if (ctx.error) return;
const b = gen(ctx, ast.values[1]);
if (ctx.error) return;
const c = gen(ctx, ast.values[2]);
if (ctx.error) return;
return `bigInt(${a}).neq(bigInt(0)) ? (${b}) : (${c})`;
oldCode = ctx.code;
ctx.code = "";
const thenRef = gen(ctx, ast.values[1]);
if (ctx.error) return;
const then = ctx.refs[thenRef];
ctx.code = oldCode + utils.ident(ctx.code);
ctx.code += `${rLabel} = ${then.label};\n`;
ctx.code += "} else {\n";
oldCode = ctx.code;
ctx.code = "";
const elseRef = gen(ctx, ast.values[2]);
if (ctx.error) return;
const els = ctx.refs[elseRef];
ctx.code = oldCode + utils.ident(ctx.code);
ctx.code += `${rLabel} = ${els.label};\n`;
ctx.code += "}\n";
if (!utils.sameSizes(then.sizes, els.sizes)) return ctx.throwError(ast, "Ternary op must return the same sizes");
const refId = ctx.refs.length;
ctx.refs.push({
type: "BIGINT",
sizes: then.sizes,
used: true,
label: rLabel
});
return refId;
} else {
if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
if (!cond.value[0].isZero()) {
return gen(ctx, ast.values[1]);
} else {
return gen(ctx, ast.values[2]);
}
}
} }
function genInclude(ctx, ast) { function genInclude(ctx, ast) {

+ 3
- 0
src/c_tester.js

@ -108,6 +108,9 @@ class CTester {
checkObject(prefix + "."+k, eOut[k]); checkObject(prefix + "."+k, eOut[k]);
} }
} else { } else {
if (typeof self.symbols[prefix] == "undefined") {
assert(false, "Output variable not defined: "+ prefix);
}
const ba = bigInt(actualOut[self.symbols[prefix].idxWit]).toString(); const ba = bigInt(actualOut[self.symbols[prefix].idxWit]).toString();
const be = bigInt(eOut).toString(); const be = bigInt(eOut).toString();
assert.strictEqual(ba, be, prefix); assert.strictEqual(ba, be, prefix);

+ 1
- 0
src/zqfield.js

@ -58,6 +58,7 @@ module.exports = class ZqField {
} }
div(a, b) { div(a, b) {
assert(!b.isZero(), "Division by zero");
return a.mul(b.modInv(this.p)).mod(this.p); return a.mul(b.modInv(this.p)).mod(this.p);
} }

+ 41
- 0
test/basiccases.js

@ -92,6 +92,25 @@ describe("basic cases", function () {
] ]
); );
}); });
it("while unrolled", async () => {
await doTest(
"whileunrolled.circom",
[
[{in: 0}, {out: [0,1,2]}],
[{in: 10}, {out: [10, 11, 12]}],
[{in: __P__.minus(2)}, {out: [__P__.minus(2), __P__.minus(1), 0]}],
]
);
});
it("while rolled", async () => {
await doTest(
"whilerolled.circom",
[
[{in: 0}, {out: 0}],
[{in: 10}, {out: 10}],
]
);
});
it("function1", async () => { it("function1", async () => {
await doTest( await doTest(
"function1.circom", "function1.circom",
@ -246,4 +265,26 @@ describe("basic cases", function () {
] ]
); );
}); });
it("Conditional Ternary operator", async () => {
await doTest(
"condternary.circom",
[
[{in: 0}, {out: 21}],
[{in: 1}, {out: 1}],
[{in: 2}, {out: 23}],
[{in:-1}, {out: 20}],
]
);
});
it("Compute block", async () => {
await doTest(
"compute.circom",
[
[{x: 1}, {y: 7}],
[{x: 2}, {y: 7}],
[{x: 3}, {y: 11}],
[{x:-1}, {y: -5}],
]
);
});
}); });

+ 15
- 0
test/circuits/condternary.circom

@ -0,0 +1,15 @@
template CondTernary() {
signal input in;
signal output out;
var a = 3;
var b = a==3 ? 1 : 2; // b is 1
var c = a!=3 ? 10 : 20; // c is 20
var d = b+c; // d is 21
out <-- ((in & 1) != 1) ? in + d : in; // Add 21 if in is pair
}
component main = CondTernary()

+ 16
- 0
test/circuits/whilerolled.circom

@ -0,0 +1,16 @@
template WhileRolled() {
signal input in;
signal output out;
var acc = 0;
var i=0;
while (i<in) {
acc = acc + 1;
i++
}
out <== acc;
}
component main = WhileRolled();

+ 12
- 0
test/circuits/whileunrolled.circom

@ -0,0 +1,12 @@
template WhileUnrolled(n) {
signal input in;
signal output out[n];
var i=0;
while (i<n) {
out[i] <== in + i;
i++;
}
}
component main = WhileUnrolled(3);

Loading…
Cancel
Save