@@ -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
1920const 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
6381const 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+
6889const 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.
0 commit comments