diff --git a/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts b/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts index 39c54d20..83be0dc6 100644 --- a/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts +++ b/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts @@ -143,7 +143,8 @@ class FunctionBoilerplateGenerator { : []), ...(newCommitments ? [` // this seems silly (it is) but its the only way to get the event to emit properly - emit EncryptedBackupData(BackupData);`] + emit EncryptedBackupData(BackupData); + `] : []), ]; }, diff --git a/src/codeGenerators/circuit/zokrates/toCircuit.ts b/src/codeGenerators/circuit/zokrates/toCircuit.ts index 62667eaa..d6c86ad5 100644 --- a/src/codeGenerators/circuit/zokrates/toCircuit.ts +++ b/src/codeGenerators/circuit/zokrates/toCircuit.ts @@ -160,7 +160,14 @@ function codeGenerator(node: any) { case 'VariableDeclarationStatement': { const declarations = node.declarations.map(codeGenerator).join(', '); if (!node.initialValue) return `${declarations} = ${node.declarations.map(n => n.typeName.name === 'bool' ? 'false' : 0)}`; + if(node.initialValue?.nodeType === 'InternalFunctionCall'){ + if(!declarations) return ; + if(node.initialValue?.expression?.nodeType === 'BinaryOperation') + return `${declarations} = ${codeGenerator(node.initialValue.expression)}`; + return `${declarations} = ${node.initialValue.name}`; + } const initialValue = codeGenerator(node.initialValue); + return `${declarations} = ${initialValue}`; } diff --git a/src/codeGenerators/contract/solidity/toContract.ts b/src/codeGenerators/contract/solidity/toContract.ts index 3d59e670..472ddb08 100644 --- a/src/codeGenerators/contract/solidity/toContract.ts +++ b/src/codeGenerators/contract/solidity/toContract.ts @@ -146,6 +146,11 @@ function codeGenerator(node: any) { if(node.initialValue) initialValue = codeGenerator(node.initialValue); if (!initialValue || initialValue === '') return `${declarations};`; + if(node.initialValue.nodeType === 'InternalFunctionCall'){ + if(node.interactsWithSecret) return ; + return ` + ${declarations} = ${initialValue.replace(/\s+/g,' ').trim()}`; + } return ` ${declarations} = ${initialValue};`; } diff --git a/src/codeGenerators/orchestration/nodejs/toOrchestration.ts b/src/codeGenerators/orchestration/nodejs/toOrchestration.ts index 66cc04a5..7d391b9e 100644 --- a/src/codeGenerators/orchestration/nodejs/toOrchestration.ts +++ b/src/codeGenerators/orchestration/nodejs/toOrchestration.ts @@ -107,6 +107,11 @@ export default function codeGenerator(node: any, options: any = {}): any { if (!node.initialValue.nodeType) return `\nlet ${codeGenerator(node.declarations[0])};` // local var dec if (node.initialValue.nodeType === 'Literal' && node.isInitializationExpression) return `\nlet ${codeGenerator(node.declarations[0])} = ${codeGenerator(node.initialValue)};`; + if(node.initialValue.nodeType === 'InternalFunctionCall'){ + if(node.initialValue?.expression?.nodeType === 'BinaryOperation') + return `\nlet ${codeGenerator(node.declarations[0])} = ${codeGenerator(node.initialValue.expression)};`; + return `\nlet ${codeGenerator(node.declarations[0])} = ${node.initialValue.name};`; + } return `\nlet ${codeGenerator(node.declarations[0])} = generalise(${codeGenerator(node.initialValue)});`; } return `\nlet ${codeGenerator(node.initialValue)};`; diff --git a/src/transformers/visitors/circuitInternalFunctionCallVisitor.ts b/src/transformers/visitors/circuitInternalFunctionCallVisitor.ts index 44f5fb59..c8e1bcba 100644 --- a/src/transformers/visitors/circuitInternalFunctionCallVisitor.ts +++ b/src/transformers/visitors/circuitInternalFunctionCallVisitor.ts @@ -7,6 +7,7 @@ import buildNode from '../../types/orchestration-types.js' // We need to ensure that parameters appear in the same order as in the .mjs file if the same state variables are used in multiple function calls. // All parameters relating to the same state variable should be grouped together. const reorderParameters = (parameterList: any) => { + let deletedIndexes = []; parameterList.forEach((param, index) => { parameterList.forEach((newParam, newIndex) => { if (param.name === newParam.name && param.bpType === 'nullification' && newParam.bpType === 'nullification') { @@ -19,8 +20,17 @@ const reorderParameters = (parameterList: any) => { parameterList[index] = newParam; } } + if(param.name === newParam.name && param.nodeType === 'VariableDeclaration' && newParam.nodeType === 'Boilerplate'){ + !deletedIndexes.includes(index)? deletedIndexes.push(index) : deletedIndexes; + } + }); }); + // sort the array in descending array so that we delete the correct elements + deletedIndexes = deletedIndexes.slice().sort((a, b) => b - a); + deletedIndexes.forEach(index => { + parameterList.splice(index, 1) + }); let newBPName: string; let currentIndex: number; let newCommitment = {}; @@ -59,6 +69,7 @@ const reorderParameters = (parameterList: any) => { elementsToAdd.forEach((element) => { parameterList.splice(element.NewIndex, 0, element.element); }); + } @@ -288,7 +299,11 @@ const internalCallVisitor = { if(file.fileName === callingFncName) { file.nodes.forEach(childNode => { if(childNode.nodeType === 'FunctionDefinition') { - childNode.body.statements.forEach(node => { + childNode.body.statements.forEach((node) => { + if(node.initialValue?.nodeType === 'InternalFunctionCall' && state.initNode) { + node.initialValue.expression = state.initNode[ node.initialValue.name ]; + } + if(node.nodeType==='BoilerplateStatement'){ callingFncbpType = node.bpType; } diff --git a/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts b/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts index 273174ad..d8e61447 100644 --- a/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts +++ b/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts @@ -98,8 +98,7 @@ const internalCallVisitor = { state.currentIndex = index; traverseNodesFast(node, adjustNamesVisitor, state); } - - }); + }); state.newPostStatementList = cloneDeep(childNode.body.postStatements); state.newPostStatementList.forEach(node => { if(node.nodeType === 'MembershipWitness'){ @@ -213,6 +212,14 @@ const internalCallVisitor = { file.nodes.forEach(childNode => { if(childNode.nodeType === 'FunctionDefinition') { childNode.parameters.modifiedStateVariables = joinWithoutDupes(childNode.parameters.modifiedStateVariables, state.newParametersList); + const modifiedNodes = childNode.parameters.modifiedStateVariables.map(node => node.name); + const declarationNode = childNode.body.preStatements.filter(node => node.nodeType === 'VariableDeclarationStatement'); + !JSON.stringify(declarationNode).includes(JSON.stringify(state.decNode)) ? + state.decNode?.forEach((decNode, decIndex) => { + // if the function doesn't modifies the returned variable don't include it + if(state.decNode && !(modifiedNodes.includes(decNode.declarations[0].name))) + childNode.body.preStatements.splice(decIndex+1, 0, decNode); + }): ''; if(childNode.decrementedSecretStates) childNode.decrementedSecretStates = [...new Set([...childNode.decrementedSecretStates, ...newdecrementedSecretStates])]; childNode.body.preStatements.forEach(node => { @@ -267,7 +274,8 @@ const internalCallVisitor = { let newVarDecs = []; childNode.body.statements.forEach((node1, index1)=> { state.varNames = []; - if (!(node1.expression && node1.expression?.nodeType === 'InternalFunctionCall')){ + // check the node except the InternalFunctionCall node and new created public node with id = 0 as we created it + if (!((node1.expression && node1.expression?.nodeType === 'InternalFunctionCall') || (node1.initialValue && node1.initialValue?.nodeType === 'InternalFunctionCall') || node1.id === 0)){ traverseNodesFast(node1, findVarVisitor, state); } state.varNames.forEach((varName) => { @@ -306,14 +314,52 @@ const internalCallVisitor = { childNode.body.statements[id-1] = statenode; node.body.statements.forEach(kidNode =>{ if(kidNode.nodeType === 'ExpressionStatement'&& kidNode.expression.name === state.internalFncName[index]) { - kidNode.expression = Object.assign(kidNode.expression,statenode.initialValue); + //When Internal function is inside for-loop, it exit under Expression Statement, we replace the function call with expression from the called function + if (kidNode.expression.operator) { + // for statement like + /* + for (statement) { + a += internalFuncCall(); + } here, kidNode will look like + expression: { + nodeType: 'Assignment', + name: 'add', + internalFunctionInteractsWithSecret: true, + operator: '+=', + leftHandSide: { nodeType: 'Identifier', name: 'a', subType: 'uint256' }, + }, and we need to get the other expression from the other function here. + */ + const newExpressionNode = Object.assign(cloneDeep(kidNode.expression), statenode.initialValue); + node.body.statements.push(newExpressionNode); + } else { + // for statement like + /* + for (statement) { + internalFuncCall(); + } here, kidNode will look like + expression: { + expression: { + nodeType: 'InternalFunctionCall', + name: 'add', + internalFunctionInteractsWithSecret: true + }, + and we need to get the other expression from the other function here. + */ + Object.assign(kidNode.expression, statenode.initialValue); + } } + }); - childNode.body.statements[id-1].initialValue =undefined; + childNode.body.statements[id-1].initialValue = undefined; } else{ - node.body.statements.forEach(kidNode =>{ - if(kidNode.nodeType === 'ExpressionStatement'&& kidNode.expression.name === state.internalFncName[index]) { - kidNode.expression = Object.assign(kidNode.expression,statenode.expression); + node.body.statements.forEach(kidNode => { + if(kidNode.nodeType === 'ExpressionStatement' && kidNode.expression.name === state.internalFncName[index]) { + if(kidNode.expression.operator) { + // If the internal function modifies the same state variable, we need to copy over the statements in the calling function + const newExpressionNode = Object.assign(cloneDeep(kidNode.expression), statenode.expression); + node.body.statements.push(newExpressionNode); + } + kidNode.expression = Object.assign(kidNode.expression, statenode.expression); } }); } @@ -321,7 +367,7 @@ const internalCallVisitor = { }); }); // remove multiple variable declarations - childNode.body.statements.forEach((node1, index1)=> { + childNode.body.statements.forEach((node1, index1) => { let isDecDeleted = false; if(node1.nodeType === 'VariableDeclarationStatement'){ childNode.body.statements.forEach((node2, index2)=> { @@ -342,7 +388,7 @@ const internalCallVisitor = { if(statenode.nodeType === 'VariableDeclarationStatement'){ childNode.body.statements[id-1] = statenode; node.body.statements.forEach(kidNode =>{ - if(kidNode.nodeType === 'ExpressionStatement'&& kidNode.expression.name === state.internalFncName[index]) { + if(kidNode.nodeType === 'ExpressionStatement' && kidNode.expression.name === state.internalFncName[index]) { kidNode.expression = Object.assign(kidNode.expression,statenode.initialValue); node.body.statements?.splice(node.body.statements.indexOf(kidNode)+1, 0, state.newStatementList[stateid+1]); } @@ -382,7 +428,7 @@ const internalCallVisitor = { } }); }); - node.privateStates = Object.assign(node.privateStates,statenode.privateStates); + node.privateStates = Object.assign(node.privateStates, statenode.privateStates); } }); break; @@ -397,7 +443,7 @@ const internalCallVisitor = { } }); }); - node.privateStates = Object.assign(node.privateStates,statenode.privateStates); + node.privateStates = Object.assign(node.privateStates, statenode.privateStates); } }); break; @@ -405,7 +451,7 @@ const internalCallVisitor = { case 'CalculateCommitment': { state.newPostStatementList.forEach(statenode => { if(statenode.nodeType === 'CalculateCommitment'){ - node.privateStates = Object.assign(node.privateStates,statenode.privateStates); + node.privateStates = Object.assign(node.privateStates, statenode.privateStates); } }); break; @@ -422,6 +468,7 @@ const internalCallVisitor = { }); node.privateStates = Object.assign(node.privateStates,generateProofNode.privateStates); node.parameters = [...new Set([...node.parameters ,...generateProofNode.parameters])]; + state.returnPara ? node.parameters = [...new Set([...node.parameters, ...state.returnPara])]: node.parameters; break; } case 'SendTransaction': { @@ -498,7 +545,7 @@ FunctionCall: { } } else - isCircuit = true; + isCircuit = true; } } }); @@ -507,10 +554,55 @@ FunctionCall: { state.circuitImport.push('true'); else state.circuitImport.push('false'); - const newNode = buildNode('InternalFunctionCall', { + let newNode; + if(parent.nodeType === 'VariableDeclarationStatement') { + if(!functionReferncedNode.node.returnParameters.parameters[0]._newASTPointer.isSecret) { + const decNode = buildNode('VariableDeclarationStatement') + decNode.declarations.push(functionReferncedNode.node.returnParameters.parameters[0]._newASTPointer); + decNode.interactsWithSecret = true; + decNode.declarations[0].declarationType = 'state'; + decNode.declarations[0].isAccessed = true; + decNode.declarations[0].interactsWithSecret = true; + state.decNode ??= []; + + const decNodeNames = state.decNode.map(node => node.declarations[0].name); + decNodeNames.includes(decNode.declarations[0].name) ? state.decNode : state.decNode.push(decNode); + } + const returnPara = functionReferncedNode.node.returnParameters.parameters[0].name; + let includeExpressionNode = false; + // this functions checks if the parent node interact with secret in the calling function or not + callingfnDefIndicators[parent.declarations[0].id].interactsWith.forEach( node => { + if(node.key != 'arguments' && node.interactsWithSecret) + includeExpressionNode = true; + }) + functionReferncedNode.node.body.statements.forEach(exp => { + // If the return para interacts with public only in the internal function but with secret in calling function we need this expression in calling function + if(exp?.expression.leftHandSide?.name === returnPara && !exp.expression.leftHandSide.interactsWithSecret){ + state.initNode = buildNode('BinaryOperation', { + leftExpression: exp._newASTPointer.expression.rightHandSide.leftExpression, + operator: exp._newASTPointer.expression.rightHandSide.operator, + rightExpression: exp._newASTPointer.expression.rightHandSide.rightExpression, + }); + } + newNode = buildNode('InternalFunctionCall', { + name: returnPara, + internalFunctionInteractsWithSecret: internalFunctionInteractsWithSecret, + }); + if(includeExpressionNode && state.initNode) { + newNode.expression = state.initNode; + } + + if(parent._newASTPointer.interactsWithSecret && !(state.returnPara?.includes(returnPara))) { + state.returnPara ??= []; + state.returnPara.push(returnPara); + } + }) + } else { + newNode = buildNode('InternalFunctionCall', { name: node.expression.name, internalFunctionInteractsWithSecret: internalFunctionInteractsWithSecret, }); + } node._newASTPointer = newNode ; if (Array.isArray(parent._newASTPointer[path.containerName])) { parent._newASTPointer[path.containerName].push(newNode); diff --git a/src/transformers/visitors/toCircuitVisitor.ts b/src/transformers/visitors/toCircuitVisitor.ts index 1aea3d5b..0fa2d06f 100644 --- a/src/transformers/visitors/toCircuitVisitor.ts +++ b/src/transformers/visitors/toCircuitVisitor.ts @@ -722,6 +722,7 @@ const visitor = { state.skipSubNodes = true; return; } + const newNode = buildNode('VariableDeclarationStatement'); node._newASTPointer = newNode; @@ -1574,19 +1575,70 @@ const visitor = { } } }); - state.circuitImport ??= []; - if(isCircuit) - state.circuitImport.push({isImported: 'true', modVars: modifiedVariables, callingFunction: callingfnDefPath.node.name}); - else - state.circuitImport.push({isImported: 'false', modVars: modifiedVariables, callingFunction: callingfnDefPath.node.name}); - - - const newNode = buildNode('InternalFunctionCall', { - name: node.expression.name, - internalFunctionInteractsWithSecret: internalFunctionInteractsWithSecret, - CircuitArguments: [], - CircuitReturn:[], - }); + state.circuitImport ??= []; + if(isCircuit) + state.circuitImport.push({isImported: 'true', modVars: modifiedVariables, callingFunction: callingfnDefPath.node.name}); + else + state.circuitImport.push({isImported: 'false', modVars: modifiedVariables, callingFunction: callingfnDefPath.node.name}); + let newNode: any; + if(parent.nodeType === 'VariableDeclarationStatement') { + state.isReturnInternalFunctionCall = true; + state.functionArgs = node.arguments.map(args => args.name); + const returnPara = functionReferncedNode.node.returnParameters.parameters[0].name; + newNode = buildNode('InternalFunctionCall', { + name: returnPara, + internalFunctionInteractsWithSecret: internalFunctionInteractsWithSecret, // return + }); + if(parent._newASTPointer.declarations.length > 0){ + const functionParams = callingfnDefPath.node._newASTPointer.parameters.parameters.map(param => param.name); + if(!functionParams.includes(returnPara)){ + callingfnDefPath.node._newASTPointer.parameters.parameters.push(functionReferncedNode.node.returnParameters.parameters[0]._newASTPointer); + callingfnDefPath.node._newASTPointer.parameters.parameters[functionParams.length].declarationType = 'parameter'; + callingfnDefPath.node._newASTPointer.parameters.parameters[functionParams.length].interactsWithSecret = true; + } + } + let includeExpressionNode = false; + // this functions checks if the parent node interact with secret in the calling function or not + callingfnDefIndicators[parent.declarations[0].id].interactsWith.forEach( node => { + if(node.key != 'arguments' && node.interactsWithSecret) + includeExpressionNode = true; + }) + functionReferncedNode.node.body.statements.forEach(exp => { + // If the return para interacts with public only in the internal function but with secret in calling function we need this expression in calling function + if(exp?.expression.leftHandSide?.name === returnPara && !exp.expression.leftHandSide.interactsWithSecret){ + let initNode: any; + if(['+=', '-=', '*=', '/='].includes(exp.expression.operator)) { + initNode = buildNode('BinaryOperation', { + leftExpression: exp.expression.leftHandSide, + operator: exp.expression.operator.slice(0,-1), + rightExpression: exp.expression.rightHandSide, + }); + } else + initNode = buildNode('BinaryOperation', { + leftExpression: exp.expression.rightHandSide.leftExpression, + operator: exp.expression.rightHandSide.operator, + rightExpression: exp.expression.rightHandSide.rightExpression, + }); + newNode = buildNode('InternalFunctionCall', { + name: returnPara, + internalFunctionInteractsWithSecret: internalFunctionInteractsWithSecret, + }); + if(includeExpressionNode) { + state.initNode ??= []; + state.initNode[ returnPara ] = initNode; + } + } + + }) + } else + { + newNode = buildNode('InternalFunctionCall', { + name: node.expression.name, + internalFunctionInteractsWithSecret: internalFunctionInteractsWithSecret, + CircuitArguments: [], + CircuitReturn:[], + }); + } const fnNode = buildNode('InternalFunctionBoilerplate', { name: node.expression.name, internalFunctionInteractsWithSecret: internalFunctionInteractsWithSecret, diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index 6e740950..460ee2b3 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -530,7 +530,7 @@ export default { // TODO interacts with secret AND public const subState = { interactsWithSecret: false }; path.traversePathsFast(interactsWithSecretVisitor, subState); - if (subState.interactsWithSecret) { + if (subState.interactsWithSecret && node.initialValue.nodeType != 'FunctionCall') { state.skipSubNodes = true; return; } @@ -851,7 +851,6 @@ DoWhileStatement: { enter(path: NodePath, state: any) { const { node, parent, scope } = path; let newNode: any; - // If this node is a require statement, it might include arguments which themselves are expressions which need to be traversed. So rather than build a corresponding 'assert' node upon entry, we'll first traverse into the arguments, build their nodes, and then upon _exit_ build the assert node. if (path.isRequireStatement() || path.isRevertStatement() || (node.expression.memberName && node.expression.memberName === 'push')) { // If the 'require' statement contains secret state variables, we'll presume the circuit will perform that logic, so we'll do nothing in the contract. @@ -892,7 +891,6 @@ DoWhileStatement: { if (path.isInternalFunctionCall()) { // External function calls are the fiddliest of things, because they must be retained in the Solidity contract, rather than brought into the circuit. With this in mind, it's easiest (from the pov of writing this transpiler) if External function calls appear at the very start or very end of a function. If they appear interspersed around the middle, we'd either need multiple circuits per Zolidity function, or we'd need a set of circuit parameters (non-secret params / return-params) per external function call, and both options are too painful for now. // TODO: need a warning message to this effect ^^^ - const functionReferncedNode = scope.getReferencedNode(node.expression); const params = functionReferncedNode.parameters.parameters; state.pubparams = []; diff --git a/test/contracts/Assign-Return1.zol b/test/contracts/Assign-Return1.zol index 5c836716..c0856039 100644 --- a/test/contracts/Assign-Return1.zol +++ b/test/contracts/Assign-Return1.zol @@ -5,14 +5,17 @@ pragma solidity ^0.8.0; contract Assign { secret uint256 private a; + secret uint256 private b; - function add( uint256 value) public returns (uint256) { + function add( secret uint256 value) public returns (uint256) { a += value; - return a; + b += value; + return b; } function remove(secret uint256 value) public { - a -= value; + secret uint256 value1 = add(value); + a -= value + value1; } } diff --git a/test/contracts/for-loop.zol b/test/contracts/for-loop.zol index 16d6c288..d2754a86 100644 --- a/test/contracts/for-loop.zol +++ b/test/contracts/for-loop.zol @@ -10,6 +10,7 @@ function add(uint256 j) public { uint256 i; for(i =0 ; i<5 ; i++) { z += j; + z += 1; } } } diff --git a/test/contracts/internalFunctionCall-Return3.zol b/test/contracts/internalFunctionCall-Return3.zol new file mode 100644 index 00000000..a9a38992 --- /dev/null +++ b/test/contracts/internalFunctionCall-Return3.zol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: CC0 + +pragma solidity ^0.8.0; + +contract Assign { + + secret uint256 private a; + secret uint256 private b; + uint256 public m; + + function add( secret uint256 value, uint256 value1) public returns (uint256) { + a += value; + b += value1; + return b; + } + + function add1( secret uint256 value, uint256 value1) public returns (uint256) { + a += value; + m += value1 + 2; + return m; + } + + function remove(secret uint256 value, uint256 value1) public { + uint256 c = add(value, value1); + uint256 d = add1(value, value1); + a -= c + d ; + } +} \ No newline at end of file diff --git a/test/contracts/internalFunctionCall-Return4.zol b/test/contracts/internalFunctionCall-Return4.zol new file mode 100644 index 00000000..94484d28 --- /dev/null +++ b/test/contracts/internalFunctionCall-Return4.zol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: CC0 + +pragma solidity ^0.8.0; + +contract Assign { + + secret uint256 private a; + uint256 public b; + uint256 public m; + + function add( secret uint256 value, uint256 value1) public returns (uint256) { + a += value; + b += value1; + return b; + } + + function add1( secret uint256 value, uint256 value1) public returns (uint256) { + a += value; + m += value1 + 2; + return m; + } + + function remove(secret uint256 value, uint256 value1) public { + uint256 c = add(value, value1); + uint256 d = add1(value, value1); + a -= c + d ; + } +} \ No newline at end of file diff --git a/test/contracts/internalFunctionCall-return.zol b/test/contracts/internalFunctionCall-return.zol new file mode 100644 index 00000000..0bcec965 --- /dev/null +++ b/test/contracts/internalFunctionCall-return.zol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: CC0 + +pragma solidity ^0.8.0; + +contract Assign { + + secret uint256 private a; + uint256 public b; + + function add( secret uint256 value, uint256 value1) public returns (uint256) { + a += value; + b += value1; + return b; + } + + function remove(secret uint256 value, uint256 value1) public { + uint256 c = add(value, value1); + b += c + 1; + a -= value1; + } +} \ No newline at end of file diff --git a/test/contracts/internalFunctionCall-return2.zol b/test/contracts/internalFunctionCall-return2.zol new file mode 100644 index 00000000..07bb82a7 --- /dev/null +++ b/test/contracts/internalFunctionCall-return2.zol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: CC0 + +pragma solidity ^0.8.0; + +contract Assign { + + secret uint256 private a; + uint256 public b; + + function add( secret uint256 value, uint256 value1) public returns (uint256) { + a += value; + b += value1; + return b; + } + + function add1( secret uint256 value, uint256 value1) public returns (uint256) { + a += value; + b += value1 + 2; + return b; + } + + function remove(secret uint256 value, uint256 value1) public { + uint256 c = add(value, value1); + uint256 d = add1(value, value1); + a -= c + d ; + } +} \ No newline at end of file