Skip to content

Commit 9b4e2b6

Browse files
committed
fixup: underflows
1 parent 1ee9021 commit 9b4e2b6

2 files changed

Lines changed: 69 additions & 24 deletions

File tree

src/codeGenerators/circuit/zokrates/toCircuit.ts

Lines changed: 69 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const keepOneTrailingSemicolon = (code: string) => {
1616
return code.endsWith('}') ? code : code.replace(/;+$/, '') + ';';
1717
};
1818

19+
// Traverses a node to check for any guards that are necessary to prevent underflows
1920
const underflowGuardConditions = (
2021
node: any,
2122
codeGeneratorState: any,
@@ -28,18 +29,21 @@ const underflowGuardConditions = (
2829
seen.add(node);
2930

3031
switch (node.nodeType) {
32+
// If the node is a binary operation with operator "-", we return a guard to prevent underflows, analogously to solidity,
33+
// otherwise we traverse the child nodes
3134
case 'BinaryOperation': {
3235
const subExpressionConditions = [
3336
...underflowGuardConditions(node.leftExpression, codeGeneratorState, seen),
3437
...underflowGuardConditions(node.rightExpression, codeGeneratorState, seen),
3538
];
36-
if (!node.underflowCheckRequired) return subExpressionConditions;
39+
if (node.operator !== '-') return subExpressionConditions;
3740
return [
3841
...subExpressionConditions,
3942
`${codeGenerator(node.leftExpression, codeGeneratorState)} >= ${codeGenerator(node.rightExpression, codeGeneratorState)}`,
4043
];
4144
}
4245

46+
// We only need to prevent underflows in if statements if the branch of the if statement is taken
4347
case 'Conditional': {
4448
const condition = removeTrailingSemicolon(codeGenerator(node.condition, codeGeneratorState));
4549
return [
@@ -49,6 +53,19 @@ const underflowGuardConditions = (
4953
];
5054
}
5155

56+
case 'UnaryOperation': {
57+
const subExpressionConditions = underflowGuardConditions(
58+
node.subExpression,
59+
codeGeneratorState,
60+
seen,
61+
);
62+
if (node.operator !== '--') return subExpressionConditions;
63+
return [
64+
...subExpressionConditions,
65+
`${codeGenerator(node.subExpression, codeGeneratorState)} >= 1`,
66+
];
67+
}
68+
5269
default:
5370
return Object.values(node).flatMap(child => underflowGuardConditions(child, codeGeneratorState, seen));
5471
}
@@ -60,11 +77,15 @@ const underflowGuardAssertions = (node: any, codeGeneratorState: any) => {
6077
.join('\n');
6178
};
6279

80+
// Adds a guard to the statement to prevent underflows
6381
const withUnderflowGuardAssertions = (node: any, statement: string, codeGeneratorState: any) => {
6482
const guards = underflowGuardAssertions(node, codeGeneratorState);
6583
return guards ? `${guards}\n${statement}` : statement;
6684
};
6785

86+
const assignmentStatement = (node: any, codeGeneratorState: any) =>
87+
`${codeGenerator(node.leftHandSide, codeGeneratorState)} ${node.operator} ${codeGenerator(node.rightHandSide, codeGeneratorState)};`;
88+
6889
const conditionalUnderflowGuardAssertions = (
6990
node: any,
7091
condition: string,
@@ -326,6 +347,21 @@ codeGeneratorState.wrapperFunctions.set(functionName, wrapperFunction);
326347

327348
case 'ExpressionStatement': {
328349
if (node.isVarDec) {
350+
if (node.expression?.nodeType === 'Assignment') {
351+
const declarationType =
352+
node.expression?.leftHandSide?.typeName === 'bool'
353+
? 'bool'
354+
: 'field';
355+
return withUnderflowGuardAssertions(
356+
node.expression,
357+
`
358+
${declarationType} mut ${assignmentStatement(
359+
node.expression,
360+
codeGeneratorState,
361+
)}`,
362+
codeGeneratorState,
363+
);
364+
}
329365
if (node.expression?.leftHandSide?.typeName === 'bool'){
330366
return `
331367
bool mut ${codeGenerator(node.expression, codeGeneratorState)}`;
@@ -361,15 +397,19 @@ codeGeneratorState.wrapperFunctions.set(functionName, wrapperFunction);
361397
case 'Assignment':
362398
return withUnderflowGuardAssertions(
363399
node,
364-
`${codeGenerator(node.leftHandSide, codeGeneratorState)} ${node.operator} ${codeGenerator(node.rightHandSide, codeGeneratorState)};`,
400+
assignmentStatement(node, codeGeneratorState),
365401
codeGeneratorState,
366402
);
367403

368404
case 'UnaryOperation':
369405
if (node.subExpression?.typeName?.name === 'bool' && node.operator === '!'){
370406
return `${node.operator}${node.subExpression.name};`;
371407
}
372-
return `${codeGenerator(node.initialValue, codeGeneratorState)} = ${codeGenerator(node.subExpression, codeGeneratorState)} ${node.operator[0]} 1;`;
408+
return withUnderflowGuardAssertions(
409+
node,
410+
`${codeGenerator(node.initialValue, codeGeneratorState)} = ${codeGenerator(node.subExpression, codeGeneratorState)} ${node.operator[0]} 1;`,
411+
codeGeneratorState,
412+
);
373413

374414
case 'BinaryOperation':
375415
if (node.operator === '/') {
@@ -429,6 +469,8 @@ codeGeneratorState.wrapperFunctions.set(functionName, wrapperFunction);
429469
node.condition.rightExpression.name = node.condition.rightExpression.name.replace('_temp','');
430470
if(node.condition.leftExpression.nodeType == 'Identifier')
431471
node.condition.leftExpression.name = node.condition.leftExpression.name.replace('_temp','');
472+
const conditionUnderflowGuards = underflowGuardAssertions(node.condition, codeGeneratorState);
473+
if (conditionUnderflowGuards) initialStatements += `\n${conditionUnderflowGuards}`;
432474
initialStatements+= `
433475
assert(!(${codeGenerator(node.condition, codeGeneratorState)}));`;
434476
return initialStatements;
@@ -443,8 +485,6 @@ codeGeneratorState.wrapperFunctions.set(functionName, wrapperFunction);
443485
}
444486
});
445487
const condition = removeTrailingSemicolon(codeGenerator(node.condition, codeGeneratorState));
446-
const assertCondition = (assertNode: any) =>
447-
assertNode.arguments.map((arg: any) => removeTrailingSemicolon(codeGenerator(arg, codeGeneratorState))).join(' && ');
448488
const conditionUnderflowGuards = underflowGuardAssertions(node.condition, codeGeneratorState);
449489
if (conditionUnderflowGuards) initialStatements += `\n${conditionUnderflowGuards}`;
450490
for (let i =0; i<node.trueBody.length; i++) {
@@ -453,10 +493,7 @@ codeGeneratorState.wrapperFunctions.set(functionName, wrapperFunction);
453493
trueStatements += `${codeGenerator(node.trueBody[i], codeGeneratorState)}`;
454494
} else {
455495
trueStatements += conditionalUnderflowGuardAssertions(node.trueBody[i], condition, true, codeGeneratorState);
456-
if (node.trueBody[i].nodeType === 'Assert') {
457-
trueStatements+= `
458-
assert(!(${condition}) || (${assertCondition(node.trueBody[i])}));`;
459-
} else if (node.trueBody[i].expression.nodeType === 'UnaryOperation'){
496+
if (node.trueBody[i].expression.nodeType === 'UnaryOperation'){
460497
trueStatements+= `
461498
${codeGenerator(node.trueBody[i].expression.subExpression, codeGeneratorState)} = if (${removeTrailingSemicolon(codeGenerator(node.condition, codeGeneratorState))}) { ${removeTrailingSemicolon(codeGenerator(node.trueBody[i].expression.subExpression, codeGeneratorState))} ${node.trueBody[i].expression.operator[0]} 1 } else { ${removeTrailingSemicolon(codeGenerator(node.trueBody[i].expression.subExpression, codeGeneratorState))} };`
462499
} else {
@@ -470,10 +507,7 @@ codeGeneratorState.wrapperFunctions.set(functionName, wrapperFunction);
470507
falseStatements += `${codeGenerator(node.falseBody[j], codeGeneratorState)}`;
471508
} else {
472509
falseStatements += conditionalUnderflowGuardAssertions(node.falseBody[j], condition, false, codeGeneratorState);
473-
if (node.falseBody[j].nodeType === 'Assert') {
474-
falseStatements+= `
475-
assert((${condition}) || (${assertCondition(node.falseBody[j])}));`;
476-
} else if (node.falseBody[j].expression.nodeType === 'UnaryOperation'){
510+
if (node.falseBody[j].expression.nodeType === 'UnaryOperation'){
477511
falseStatements+= `
478512
${codeGenerator(node.falseBody[j].expression.subExpression, codeGeneratorState)} = if (${removeTrailingSemicolon(codeGenerator(node.condition, codeGeneratorState))}) { ${removeTrailingSemicolon(codeGenerator(node.falseBody[j].expression.subExpression, codeGeneratorState))} } else { ${codeGenerator(node.falseBody[j].expression.subExpression, codeGeneratorState)} ${node.falseBody[j].expression.operator[0]} 1 };`;
479513
} else {
@@ -489,14 +523,18 @@ codeGeneratorState.wrapperFunctions.set(functionName, wrapperFunction);
489523

490524
case 'ForStatement':
491525
switch (node.initializationExpression.nodeType) {
492-
case 'ExpressionStatement':
493-
return `for u32 ${codeGenerator(node.condition.leftExpression, codeGeneratorState)} in ${codeGenerator(node.initializationExpression.expression.rightHandSide, codeGeneratorState)}..${node.condition.rightExpression.value} {
526+
case 'ExpressionStatement': {
527+
const initialValue = node.initializationExpression.expression.rightHandSide;
528+
return withUnderflowGuardAssertions(initialValue, `for u32 ${codeGenerator(node.condition.leftExpression, codeGeneratorState)} in ${codeGenerator(initialValue, codeGeneratorState)}..${node.condition.rightExpression.value} {
494529
${keepOneTrailingSemicolon(codeGenerator(node.body, codeGeneratorState))}
495-
}`;
496-
case 'VariableDeclarationStatement':
497-
return `for u32 ${codeGenerator(node.condition.leftExpression, codeGeneratorState)} in ${codeGenerator(node.initializationExpression.initialValue, codeGeneratorState)}..${node.condition.rightExpression.value} {
530+
}`, codeGeneratorState);
531+
}
532+
case 'VariableDeclarationStatement': {
533+
const initialValue = node.initializationExpression.initialValue;
534+
return withUnderflowGuardAssertions(initialValue, `for u32 ${codeGenerator(node.condition.leftExpression, codeGeneratorState)} in ${codeGenerator(initialValue, codeGeneratorState)}..${node.condition.rightExpression.value} {
498535
${keepOneTrailingSemicolon(codeGenerator(node.body, codeGeneratorState))}
499-
}`;
536+
}`, codeGeneratorState);
537+
}
500538
default:
501539
break;
502540
}
@@ -534,10 +572,19 @@ codeGeneratorState.wrapperFunctions.set(functionName, wrapperFunction);
534572

535573
case 'BoilerplateStatement': {
536574
let newComValue = '';
537-
if (node.bpType === 'incrementation') newComValue = codeGenerator(node.addend, codeGeneratorState);
538-
if (node.bpType === 'decrementation') newComValue = codeGenerator(node.subtrahend, codeGeneratorState);
575+
let guardNode;
576+
if (node.bpType === 'incrementation') {
577+
guardNode = node.addend;
578+
newComValue = codeGenerator(node.addend, codeGeneratorState);
579+
}
580+
if (node.bpType === 'decrementation') {
581+
guardNode = node.subtrahend;
582+
newComValue = codeGenerator(node.subtrahend, codeGeneratorState);
583+
}
539584
node.newCommitmentValue = newComValue;
540-
return Circuitbp.generateBoilerplate(node);
585+
return guardNode
586+
? withUnderflowGuardAssertions(guardNode, Circuitbp.generateBoilerplate(node), codeGeneratorState)
587+
: Circuitbp.generateBoilerplate(node);
541588
}
542589

543590
// And if we haven't recognized the node, we'll throw an error.

src/transformers/visitors/toCircuitVisitor.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,6 @@ const visitor = {
743743
const { operator } = node;
744744

745745
const newNode = buildNode('BinaryOperation', { operator });
746-
if (operator === '-') newNode.underflowCheckRequired = true;
747746
node._newASTPointer = newNode;
748747
if (parent.nodeType === "TupleExpression") {
749748
path.inList ? parent._newASTPointer.push(newNode) : parent._newASTPointer = newNode;
@@ -775,7 +774,6 @@ const visitor = {
775774
leftExpression: cloneDeep(leftHandSide),
776775
rightExpression: rightHandSide,
777776
});
778-
if (op === '-') binOpNode.underflowCheckRequired = true;
779777
const assNode = buildNode('Assignment', {
780778
operator: '=',
781779
leftHandSide,

0 commit comments

Comments
 (0)