diff --git a/src/boilerplate/contract/solidity/nodes/FunctionBoilerplateGenerator.ts b/src/boilerplate/contract/solidity/nodes/FunctionBoilerplateGenerator.ts index d19e7877f..abd6d1ea2 100644 --- a/src/boilerplate/contract/solidity/nodes/FunctionBoilerplateGenerator.ts +++ b/src/boilerplate/contract/solidity/nodes/FunctionBoilerplateGenerator.ts @@ -98,7 +98,7 @@ class FunctionBoilerplateGenerator { }); return { structName: structDef.name, properties: names, isParam: path.isFunctionParameter(node), isConstantArray: path.isConstantArray(node) ? node.typeName.length.value : false, inCircuit: node.interactsWithSecret }; } - return { name: node.name, type: node.typeName.name || node.typeName.baseType?.name || node.typeName.pathNode?.name, isParam: path.isFunctionParameter(node), isConstantArray: path.isConstantArray(node) ? node.typeName.length.value : false, inCircuit: node.interactsWithSecret }; + return { name: node.name, type: node.typeName.name || node.typeName.baseType?.name || node.typeName.pathNode?.name, isParam: path.isFunctionParameter(node), isConstantArray: path.isConstantArray(node) ? node.typeName.length.value : false, inCircuit: node.interactsWithSecret || scope.getReferencedIndicator(node)?.interactsWithSecret }; } const params = path.getFunctionParameters(); diff --git a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts index a8d141a8f..0c68387cd 100644 --- a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts +++ b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts @@ -109,7 +109,6 @@ export const sendTransactionBoilerplate = (node: any) => { output[5].push(`${privateStateName}_cipherText`); output[6].push(`${privateStateName}_encKey`); } - break; } } @@ -139,7 +138,6 @@ export const generateProofBoilerplate = (node: any) => { // we include the state variable key (mapping key) if its not a param (we include params separately) const msgSenderParamAndMappingKey = stateNode.isMapping && (node.parameters.includes('msgSender') || output.join().includes('_msg_stateVarId_key.integer')) && stateNode.stateVarId[1] === 'msg'; const msgValueParamAndMappingKey = stateNode.isMapping && (node.parameters.includes('msgValue') || output.join().includes('_msg_stateVarId_key.integer')) && stateNode.stateVarId[1] === 'msg'; - const constantMappingKey = stateNode.isMapping && (+stateNode.stateVarId[1] || stateNode.stateVarId[1] === '0'); // We are keeping this code in comments, for future if have any issue with extra mapping keys getting added for a zapp we can come to this @@ -176,8 +174,8 @@ export const generateProofBoilerplate = (node: any) => { else { parameters.push(`\t${param}.integer,`); } - }); + // then we build boilerplate code per state switch (stateNode.isWhole) { case true: @@ -321,7 +319,6 @@ export const preimageBoilerPlate = (node: any) => { accessedOnly: true, stateVarIds, })); - continue; } @@ -503,21 +500,18 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { lines.push(`\nconst ${param} = generalise(_${param});`); params.push(`_${param}`); }); - node.parameters.modifiedStateVariables.forEach((param: any) => { states.push(`_${param.name}_newOwnerPublicKey = 0`); lines.push( `\nlet ${param.name}_newOwnerPublicKey = generalise(_${param.name}_newOwnerPublicKey);`, ); }); - if (node.decrementsSecretState) { node.decrementedSecretStates.forEach((decrementedState: string) => { states.push(` _${decrementedState}_0_oldCommitment = 0`); states.push(` _${decrementedState}_1_oldCommitment = 0`); }); } - node.returnParameters.forEach( (param, index) => { if(param === 'true') rtnparams?.push('bool: bool'); @@ -525,7 +519,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { rtnparams?.push( ` ${param.replace('_change', '')}_newCommitmentValue : ${param}.integer `); }); if (params) params[params.length - 1] += `,`; - if (node.name === 'cnstrctr') return { signature: [ @@ -547,7 +540,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { statements: lines, }; } - if(rtnparams.includes('bool: bool')) { return { signature: [ @@ -560,7 +552,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { statements: lines, }; } - return { signature: [ ` ${functionSig} @@ -603,7 +594,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { mappingName: stateNode.mappingName || stateName, structProperties: stateNode.structProperties })); - } return { statements: lines, @@ -749,7 +739,7 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { statements: [`\n// Extract set membership witness: \n\n`, ...lines], }; - case 'CalculateNullifier': + case 'CalculateNullifier': for ([stateName, stateNode] of Object.entries(node.privateStates)) { if (stateNode.isPartitioned) { lines.push( @@ -759,7 +749,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { accessedOnly: stateNode.accessedOnly, stateType: 'partitioned', })); - } else { lines.push( Orchestrationbp.calculateNullifier.postStatements({ @@ -770,7 +759,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { })); } } - for ([stateName, stateNode] of Object.entries(node.privateStates)) { if (stateNode.isPartitioned) { lines.push( @@ -779,7 +767,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { accessedOnly: stateNode.accessedOnly, stateType: 'partitioned', })); - } else { lines.push( Orchestrationbp.temporaryUpdatedNullifier.postStatements({ @@ -789,7 +776,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { })); } } - for ([stateName, stateNode] of Object.entries(node.privateStates)) { if (stateNode.isPartitioned) { lines.push( @@ -808,7 +794,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { })); } } - return { statements: [`\n// Calculate nullifier(s): \n`, ...lines], }; @@ -825,7 +810,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { isSharedSecret: stateNode.isSharedSecret, structProperties: stateNode.structProperties, })); - break; case true: default: @@ -839,7 +823,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { isSharedSecret: stateNode.isSharedSecret, structProperties: stateNode.structProperties, })); - break; case false: default: @@ -851,7 +834,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { isSharedSecret: stateNode.isSharedSecret, structProperties: stateNode.structProperties, })); - } } } @@ -874,7 +856,7 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { ], }; - case 'EncryptBackupPreimage': + case 'EncryptBackupPreimage': lines.push(`let BackupData = [];\n`) for ([stateName, stateNode] of Object.entries(node.privateStates)) { let stateType; @@ -949,8 +931,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { ] } - - return { statements: [ `\n\n// Send transaction to the blockchain: @@ -993,8 +973,7 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { ], }; - - case 'SendPublicTransaction': + case 'SendPublicTransaction': if (node.publicInputs[0]) { node.publicInputs.forEach((input: any) => { if (input.properties) { @@ -1011,6 +990,7 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { } }); } + return { statements: [ `\n\n// Send transaction to the blockchain: diff --git a/src/codeGenerators/orchestration/nodejs/toOrchestration.ts b/src/codeGenerators/orchestration/nodejs/toOrchestration.ts index 9a432fdec..b0dce7227 100644 --- a/src/codeGenerators/orchestration/nodejs/toOrchestration.ts +++ b/src/codeGenerators/orchestration/nodejs/toOrchestration.ts @@ -24,9 +24,9 @@ const getAccessedValue = (name: string) => { */ const getPublicValue = (node: any) => { if (node.nodeType !== 'IndexAccess') - // In the _init variable we save the initial value of the variable for use later. + // In the _init variable we save the initial value of the variable for use later. return `\nlet ${node.name} = generalise(await instance.methods.${codeGenerator(node)}().call());\n let ${node.name}_init = ${node.name};`; - return `\nconst ${node.name} = generalise(await instance.methods.${codeGenerator(node.baseExpression, { lhs: true} )}(${codeGenerator(node.indexExpression, { contractCall: true })}).call());`; + return `\nlet ${node.name} = generalise(await instance.methods.${codeGenerator(node.baseExpression, { lhs: true} )}(${codeGenerator(node.indexExpression, { contractCall: true })}).call()); \n let ${node.name}_init = ${node.name}`; }; /** @@ -63,7 +63,6 @@ export default function codeGenerator(node: any, options: any = {}): any { const fn = OrchestrationCodeBoilerPlate(node); const statements = codeGenerator(node.body); fn.statements.push(statements); - return `${fn.signature[0]}\n\t${fn.statements.join('')}\n${ fn.signature[1] }`; @@ -96,7 +95,6 @@ export default function codeGenerator(node: any, options: any = {}): any { } else if (node.declarations[0].isAccessed) { return `${getAccessedValue(node.declarations[0].name)}`; } - if (!node.initialValue && !node.declarations[0].isAccessed) return `\nlet ${codeGenerator(node.declarations[0])};`; if (node.initialValue && node.initialValue.operator && @@ -111,24 +109,24 @@ export default function codeGenerator(node: any, options: any = {}): any { if (node.initialValue.nodeType === 'Literal' && node.isInitializationExpression) return `\nlet ${codeGenerator(node.declarations[0])} = ${codeGenerator(node.initialValue)};`; return `\nlet ${codeGenerator(node.declarations[0])} = generalise(${codeGenerator(node.initialValue)});`; } - return `\nlet ${codeGenerator(node.initialValue)};`; + return `\nlet ${codeGenerator(node.initialValue)};`; } case 'ElementaryTypeName': return; - case 'Block': { - const preStatements: string = (node.preStatements.flatMap(codeGenerator)); - const statements:string = (node.statements.flatMap(codeGenerator)); - const postStatements: string = (node.postStatements.flatMap(codeGenerator)); - return [...preStatements, ...statements, ...postStatements].join('\n\n'); + case 'Block': { + const preStatements: string = (node.preStatements.flatMap(codeGenerator)); + const statements:string = (node.statements.flatMap(codeGenerator)); + const postStatements: string = (node.postStatements.flatMap(codeGenerator)); + return [...preStatements, ...statements, ...postStatements].join('\n\n'); } case 'ExpressionStatement': if (!node.incrementsSecretState && (node.interactsWithSecret || node.expression?.internalFunctionInteractsWithSecret)){ return `\n${codeGenerator(node.expression)};`; } - if (node.incrementsSecretState && (node.interactsWithSecret || node.expression?.internalFunctionInteractsWithSecret)){ + if (node.incrementsSecretState && (node.interactsWithSecret ||node.containsPublic || node.expression?.internalFunctionInteractsWithSecret)){ let privateStateName = node.privateStateName.replace(/\./g, '_'); let increments; if (node.expression.operator === '+='){ @@ -157,16 +155,15 @@ export default function codeGenerator(node: any, options: any = {}): any { } } } - if (!node.interactsWithSecret) - return `\n// non-secret line would go here but has been filtered out`; - return `\n// increment would go here but has been filtered out`; + return `\n// non-secret line would go here but has been filtered out`; + return `\n// increment would go here but has been filtered out`; case 'InternalFunctionCall': return " "; case 'Assignment': - // To ensure the left hand side is always a general number, we generalise it here (excluding the initialisation in a for loop). + // To ensure the left hand side is always a general number, we generalise it here (excluding the initialisation in a for loop). if (!node.isInitializationAssignment && node.rightHandSide.subType !== 'generalNumber'){ if (['+=', '-=', '*='].includes(node.operator)) { return `${codeGenerator(node.leftHandSide, { @@ -175,7 +172,7 @@ export default function codeGenerator(node: any, options: any = {}): any { 0, )} ${codeGenerator(node.rightHandSide)})`; } - return `${codeGenerator(node.leftHandSide, { lhs: true })} ${ + return `${codeGenerator(node.leftHandSide, { lhs: true })} ${ node.operator } generalise(${codeGenerator(node.rightHandSide)})`; } else { @@ -190,7 +187,6 @@ export default function codeGenerator(node: any, options: any = {}): any { node.operator } ${codeGenerator(node.rightHandSide)}`; } - case 'BinaryOperation': return `${codeGenerator(node.leftExpression, { lhs: options.condition })} ${ @@ -202,9 +198,8 @@ export default function codeGenerator(node: any, options: any = {}): any { return `(${node.components.map(codeGenerator).join(` `)})`; return ` `; - case 'IfStatement': { + case 'IfStatement': { let comment = (node.inPreStatements) ? "// some public statements of this if statement have been moved to pre-statements here, any other statements appear later" : ''; - // We need to declare some variables before the if statement begins (because they are used outside the if statement). let preIfStatements = node.trueBody.filter((node: any) => node.outsideIf).concat(node.falseBody.filter((node: any) => node.outsideIf)); let newPreIfStatements = []; @@ -213,7 +208,6 @@ export default function codeGenerator(node: any, options: any = {}): any { newPreIfStatements[newPreIfStatements.length - 1].outsideIf = false; }); let preIfStatementsString = newPreIfStatements.flatMap(codeGenerator).join('\n'); - if(node.falseBody.length) return `${comment} ${preIfStatementsString} @@ -230,12 +224,12 @@ export default function codeGenerator(node: any, options: any = {}): any { }` } - case 'Conditional': { - return ` ${codeGenerator(node.condition)} ? - ${node.trueExpression.flatMap(codeGenerator).join('\n')} : ${node.falseExpression.flatMap(codeGenerator).join('\n')}` + case 'Conditional': { + return ` ${codeGenerator(node.condition)} ? + ${node.trueExpression.flatMap(codeGenerator).join('\n')} : ${node.falseExpression.flatMap(codeGenerator).join('\n')}` } - case 'ForStatement': { + case 'ForStatement': { if(node.interactsWithSecret) { let initializationExpression = `${codeGenerator(node.initializationExpression).trim()}`; let condition = `${codeGenerator(node.condition, { condition: true })};`; @@ -243,9 +237,8 @@ export default function codeGenerator(node: any, options: any = {}): any { return `for( ${node.initializationExpression.nodeType === 'VariableDeclarationStatement' ? `` : `let`} ${initializationExpression} ${condition} ${loopExpression}) { ${codeGenerator(node.body)} }` - } - else - return ''; + } else + return ''; } case 'MsgSender': diff --git a/src/transformers/visitors/checks/interactsWithSecretVisitor.ts b/src/transformers/visitors/checks/interactsWithSecretVisitor.ts index 3bea6a785..d223686b8 100644 --- a/src/transformers/visitors/checks/interactsWithSecretVisitor.ts +++ b/src/transformers/visitors/checks/interactsWithSecretVisitor.ts @@ -34,6 +34,29 @@ const markSubtreeInteractsWithPublic = (thisPath: any, thisState: any) => { indicator.addPublicInteractingPath(thisState.publicPath); }; +const markIndicatorSubtreeInteractsWithSecret = (thisPath: any, thisState: any) => { + const { node, scope } = thisPath; + if (!['Identifier', 'VariableDeclarationStatement'].includes(node.nodeType)) + return; + const indicator = scope.getReferencedIndicator(node, true); + // we don't want to add itself as an interacted with path + if (indicator && thisState.secretPath.node.id !== node.id) + indicator.addSecretInteractingPath(thisState.secretPath); +}; + +const inferInteractsWithSecret = (thisPath: any, thisState: any) => { + const { node, scope } = thisPath; + if (node.nodeType === 'ExpressionStatement') { + const leftHandSideIndicator = scope.getReferencedIndicator(node.expression.leftHandSide, true); + if (leftHandSideIndicator?.interactsWithSecret) { + thisPath.traversePathsFast(markIndicatorSubtreeInteractsWithSecret, { + secretPath: thisPath, + }); + } + } +}; + + export default { FunctionCall: { @@ -42,7 +65,6 @@ export default { path.getAncestorOfType('ExpressionStatement') || path.parentPath; if (path.isExternalFunctionCall()) { path.markContainsPublic(); - // below ensures that the return value and args are marked as interactsWithPublic expressionPath.traversePathsFast(markSubtreeInteractsWithPublic, { publicPath: path, }); @@ -50,6 +72,14 @@ export default { }, }, + FunctionDefinition: { + exit(path: NodePath) { + path.traversePathsFast(inferInteractsWithSecret, { + publicPath: path, + }); + }, + }, + Identifier: { exit(path: NodePath) { const { node, scope } = path; diff --git a/src/transformers/visitors/toCircuitVisitor.ts b/src/transformers/visitors/toCircuitVisitor.ts index db99291ac..1aea3d5bb 100644 --- a/src/transformers/visitors/toCircuitVisitor.ts +++ b/src/transformers/visitors/toCircuitVisitor.ts @@ -1090,7 +1090,7 @@ const visitor = { state.skipSubNodes = true; return; } - let interactsWithSecret = false ; + let interactsWithSecret = scope.getReferencedIndicator(node)?.interactsWithSecret ; scope.bindings[node.id].referencingPaths.forEach(refPath => { const newState: any = {}; refPath.parentPath.traversePathsFast( diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index 06f10742d..6e7409509 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -59,8 +59,18 @@ const findCustomInputsVisitor = (thisPath: NodePath, thisState: any) => { ) { thisState.customInputs ??= []; const type = binding.node.typeName.nodeType === 'Mapping' ? binding.node.typeName.valueType.name : binding.node.typeName.name; + const isConstantArray = thisPath.isConstantArray(); + const arrayLength = isConstantArray && thisPath.node.typeName?.length ? thisPath.node.typeName.length.value : false; + if (!thisState.customInputs.some((input: any) => input.name === indicator?.name)) - thisState.customInputs.push({name: indicator?.name, typeName: {name: type}, isConstantArray: thisPath.isConstantArray() ? thisPath.node.typeName.length.value : false, inCircuit: true, isReturn: false }); + thisState.customInputs.push({ + name: indicator?.name, + typeName: { name: type }, + isConstantArray: arrayLength, + inCircuit: true, + isReturn: false + }); + } }; diff --git a/src/transformers/visitors/toOrchestrationVisitor.ts b/src/transformers/visitors/toOrchestrationVisitor.ts index e59982690..76deb3d44 100644 --- a/src/transformers/visitors/toOrchestrationVisitor.ts +++ b/src/transformers/visitors/toOrchestrationVisitor.ts @@ -115,9 +115,20 @@ const findStatementId = (statements: any, ID: number) => { // i.e. public 'accessed' variables const addPublicInput = (path: NodePath, state: any, IDnode: any) => { const { node } = path; + let { name } = path.scope.getReferencedIndicator(node, true) || path.node; + + name = name.replace(/\[([^\]]+)\]/g, '_$1'); + +// Ensure there is no trailing underscore +if (name.endsWith('_')) { + name = name.slice(0, -1); +} + const binding = path.getReferencedBinding(node); if (!['Identifier', 'IndexAccess'].includes(path.nodeType)) return; + + const isCondition = !!path.getAncestorContainedWithin('condition') && path.getAncestorOfType('IfStatement')?.containsSecret; const isForCondition = !!path.getAncestorContainedWithin('condition') && path.getAncestorOfType('ForStatement')?.containsSecret; const isInitializationExpression = !!path.getAncestorContainedWithin('initializationExpression') && path.getAncestorOfType('ForStatement')?.containsSecret; @@ -133,10 +144,12 @@ const addPublicInput = (path: NodePath, state: any, IDnode: any) => { (node.interactsWithPublic || node.baseExpression?.interactsWithPublic || isCondition || isForCondition || isInitializationExpression || isLoopExpression) && binding.stateVariable && !binding.isSecret ) { + + const fnDefNode = path.getAncestorOfType('FunctionDefinition'); if (!fnDefNode) throw new Error(`Not in a function`); let innerNode: any; - if (path.isMapping(node)) { + if (path.isMapping(node) || (node.nodeType === 'IndexAccess' && node.baseExpression.nodeType === 'Identifier')) { name = getIndexAccessName(node); node.name = name; let indexExpressionNode: any; @@ -148,7 +161,8 @@ const addPublicInput = (path: NodePath, state: any, IDnode: any) => { indexExpressionNode = buildNode(node.indexExpression.nodeType, { name: node.indexExpression.name, value: node.indexExpression.value, - subType: node.indexExpression.typeDescriptions?.typeString,}); + subType: node.indexExpression.typeDescriptions?.typeString,}); + innerNode = buildNode('IndexAccess', { name, baseExpression: buildNode('Identifier', { name: node.baseExpression.name }), @@ -184,8 +198,9 @@ const addPublicInput = (path: NodePath, state: any, IDnode: any) => { const modifiedBeforePaths = path.scope.getReferencedIndicator(node, true)?.modifyingPaths?.filter((p: NodePath) => p.node.id < node.id); const statements = fnDefNode.node._newASTPointer.body.statements; + - let num_modifiers=0; + let num_modifiers=0; // For each statement that modifies the public variable previously, we need to ensure that the modified variable is stored for later. // We also need that the original public variable is updated, e.g if the statement is index_2 = index +1, we need an extra statement index = index_2. modifiedBeforePaths?.forEach((p: NodePath) => { @@ -200,6 +215,7 @@ const addPublicInput = (path: NodePath, state: any, IDnode: any) => { // we have to go back and mark any editing statements as interactsWithSecret so they show up expNode.interactsWithSecret = true; const moveExpNode = cloneDeep(expNode); + // We now move the statement in expNode to preStatements. //If the statement is within an if statement we need to find the correct if statement in preStatements or create a new one. let ifPreIndex = null; @@ -232,7 +248,7 @@ const addPublicInput = (path: NodePath, state: any, IDnode: any) => { if ((statements[location.index]?.trueBody && statements[location.index].trueBody.every(element => element === null || element === undefined)) && (statements[location.index]?.falseBody && statements[location.index].falseBody.every(element => element === null || element === undefined))) { delete statements[location.index]; } - + if( (expNode.expression && expNode.expression.leftHandSide && expNode.expression.leftHandSide?.name === node.name) || (expNode.initialValue && expNode.initialValue.leftHandSide && expNode.initialValue.leftHandSide?.name === node.name) @@ -244,11 +260,16 @@ const addPublicInput = (path: NodePath, state: any, IDnode: any) => { isSecret: false, interactsWithSecret: true, }); + const initInnerNode = buildNode('Assignment', { leftHandSide: buildNode('Identifier', { name: `${node.name}_${num_modifiers}`, subType: 'generalNumber' }), operator: '=', rightHandSide: buildNode('Identifier', { name: `${node.name}`, subType: 'generalNumber' }) + }); + + + const newNode1 = buildNode('VariableDeclarationStatement', { declarations: [decInnerNode], initialValue: initInnerNode, @@ -272,6 +293,7 @@ const addPublicInput = (path: NodePath, state: any, IDnode: any) => { expression: InnerNode, interactsWithSecret: true, }); + if (location.trueIndex !== -1){ fnDefNode.node._newASTPointer.body.preStatements[ifPreIndex].trueBody.push(newNode1); } else if (location.falseIndex !== -1){ fnDefNode.node._newASTPointer.body.preStatements[ifPreIndex].falseBody.push(newNode1); } else {fnDefNode.node._newASTPointer.body.preStatements.push(newNode1);} @@ -299,21 +321,25 @@ const addPublicInput = (path: NodePath, state: any, IDnode: any) => { else if (location.falseIndex !== -1){ fnDefNode.node._newASTPointer.body.preStatements[ifPreIndex].falseBody.push(newNode2); } else {fnDefNode.node._newASTPointer.body.preStatements.push(newNode2);} } + } } } }); - // We ensure here that the public variable used has the correct name, e.g index_2 instead of index. - if (num_modifiers != 0) { - if (IDnode.name === node.name){ - IDnode.name += `_${num_modifiers}`; - } else { - IDnode.name = `${node.name}_${num_modifiers}`; - } + + //We ensure here that the public variable used has the correct name, e.g index_2 instead of index. + if(IDnode) { + if (num_modifiers != 0) { + if (IDnode.name === node.name){ + IDnode.name += `_${num_modifiers}`; + } else { + IDnode.name = `${node.name}_${num_modifiers}`; + } + } } + // After the non-secret variables have been modified we need to reset the original variable name to its initial value. // e.g. index = index_init. - if (node.nodeType !== 'IndexAccess') { fnDefNode.node._newASTPointer.body.preStatements = fnDefNode.node._newASTPointer.body.preStatements.filter(p => p.expression?.rightHandSide?.name !== `${node.name}_init`); const endNodeInit = buildNode('Assignment', { leftHandSide: buildNode('Identifier', { name: `${node.name}`, subType: 'generalNumber' }), @@ -325,13 +351,11 @@ const addPublicInput = (path: NodePath, state: any, IDnode: any) => { interactsWithSecret: true, }); fnDefNode.node._newASTPointer.body.preStatements.push(endNode); - } // if the node is the indexExpression, we dont need its value in the circuit state.publicInputs ??= []; if (!(path.containerName === 'indexExpression' && !(path.parentPath.isSecret|| path.parent.containsSecret))) state.publicInputs.push(node); } - if (['Identifier', 'IndexAccess'].includes(node.indexExpression?.nodeType)) addPublicInput(NodePath.getPath(node.indexExpression), state, null); } /** @@ -347,7 +371,6 @@ const visitor = { enter(path: NodePath, state: any) { const { node, parent, scope } = path; node._newASTPointer = parent._newASTPointer; - const contractName = `${node.name}Shield`; if (scope.indicators.zkSnarkVerificationRequired) { const newNode = buildNode('File', { @@ -362,7 +385,6 @@ const visitor = { }); node._newASTPointer.push(newNode); } - let newNode = buildNode('SetupCommonFilesBoilerplate', { contractName, contractImports: state.contractImports, @@ -413,8 +435,6 @@ const visitor = { }); node._newASTPointer.push(newNode); } - - if (scope.indicators.newCommitmentsRequired) { const newNode = buildNode('EditableCommitmentCommonFilesBoilerplate'); node._newASTPointer.push(newNode); @@ -432,7 +452,6 @@ const visitor = { file.functionNames.push('joinCommitments'); file.functionNames.push('splitCommitments'); } - } if (file.nodes?.[0].nodeType === 'IntegrationTestBoilerplate') { file.nodes[0].constructorParams = state.constructorParams; @@ -443,6 +462,7 @@ const visitor = { path.traverse(explode(internalCallVisitor), state); }, }, + ImportDirective: { enter(path: NodePath, state: any) { const { node } = path; @@ -453,11 +473,9 @@ const visitor = { }); // we assume all import statements come before all functions }, - }, FunctionDefinition: { - enter(path: NodePath, state: any) { const { node, parent, scope } = path; if (scope.modifiesSecretState()) { @@ -466,7 +484,6 @@ const visitor = { node.fileName = fnName; // After getting an appropriate Name , we build the node - const newNode = buildNode('File', { fileName: fnName, // the name of this function fileExtension: '.mjs', @@ -475,8 +492,6 @@ const visitor = { buildNode('FunctionDefinition', { name: node.name, contractName }), ], }); - - node._newASTPointer = newNode.nodes[1]; // eslint-disable-line prefer-destructuring parent._newASTPointer.push(newNode); for (const file of parent._newASTPointer) { @@ -511,7 +526,6 @@ const visitor = { state.skipSubNodes = true; return; } - const contractName = `${parent.name}Shield`; const fnName = path.getUniqueFunctionName(); node.fileName = fnName; @@ -574,8 +588,6 @@ const visitor = { node._newASTPointer.msgSenderParam ??= state.msgSenderParam; node._newASTPointer.msgValueParam ??= state.msgValueParam; - - if(node.containsPublic && !scope.modifiesSecretState()){ interface PublicParam { name: string; @@ -584,14 +596,10 @@ const visitor = { isBool?: boolean; isAddress?: boolean; } - const sendPublicTransactionNode = buildNode('SendPublicTransaction', { functionName: node.fileName, publicInputs: [], }); - - - node.parameters.parameters.forEach((para: { isSecret: any; typeName: { name: string; }; name: any; _newASTPointer: { typeName: { properties: any[]; }; }; }) => { if (!para.isSecret) { if (path.isStructDeclaration(para) || path.isConstantArray(para) || (para.typeName && para.typeName.name === 'bool') || (para.typeName && para.typeName.name === 'address')) { @@ -615,18 +623,14 @@ const visitor = { } }); - // Add publics parametres to sendTransactionNode node._newASTPointer.body.postStatements.push(sendPublicTransactionNode); - node.parameters.parameters.forEach(para => { node._newASTPointer.publicInputs ??= []; node._newASTPointer.publicInputs.push(para.name); }); - } - - + // By this point, we've added a corresponding FunctionDefinition node to the newAST, with the same nodes as the original Solidity function, with some renaming here and there, and stripping out unused data from the oldAST. const functionIndicator: FunctionDefinitionIndicator = scope.indicators; for(const [, indicators ] of Object.entries(functionIndicator)){ @@ -657,7 +661,6 @@ const visitor = { } } } - let thisIntegrationTestFunction: any = {}; let thisIntegrationApiServiceFunction: any = {}; for (const file of parent._newASTPointer) { @@ -676,10 +679,8 @@ const visitor = { if(scope.modifiesSecretState()){ file.functionNames.push(node.fileName); } - } } - thisIntegrationTestFunction.parameters = node._newASTPointer.parameters; thisIntegrationTestFunction.newCommitmentsRequired = functionIndicator.newCommitmentsRequired; @@ -711,7 +712,6 @@ const visitor = { newNodes.generateProofNode.parameters.push(`msgValue`); delete state.msgValueParam; // reset } - const allIndicators: (StateVariableIndicator | MappingKey)[] = []; let stateVarIndicator: StateVariableIndicator | MappingKey; for ([, stateVarIndicator] of Object.entries( @@ -863,7 +863,6 @@ const visitor = { } if (secretModified || accessedOnly) { - newNodes.sendTransactionNode.privateStates[ name ] = buildPrivateStateNode('SendTransaction', { @@ -910,13 +909,10 @@ const visitor = { }); } } - - if (node.kind === 'constructor') { newNodes.writePreimageNode.isConstructor = true; newNodes.membershipWitnessNode.isConstructor = true; } - const newFunctionDefinitionNode = node._newASTPointer; // In If Statements we might have non-secret statements editing variables that later interact with a secret variable. @@ -953,14 +949,19 @@ const visitor = { // this adds other values we need in the circuit for (const param of node._newASTPointer.parameters.parameters) { - if (param.isPrivate || param.isSecret || param.interactsWithSecret) { + let oldParam : any ; + for(const para of node.parameters.parameters) { + if ( para?.name === param?.name ) + oldParam = para ; + break; + } + if (param.isPrivate || param.isSecret || param.interactsWithSecret || scope.getReferencedIndicator(oldParam)?.interactsWithSecret) { if (param.typeName.isStruct) { param.typeName.properties.forEach((prop: any) => { newNodes.generateProofNode.parameters.push(`${param.name}.${prop.name}${param.typeName.isConstantArray ? '.all' : ''}`); }); } else newNodes.generateProofNode.parameters.push(`${param.name}${param.typeName.isConstantArray ? '.all' : ''}`); } - } if (state.publicInputs) { state.publicInputs.forEach((input: any) => { @@ -987,7 +988,6 @@ const visitor = { // this adds the return parameters which are marked as secret in the tx let returnPara = node._newASTPointer.returnParameters.parameters.filter((paramnode: any) => (paramnode.isSecret || paramnode.typeName.name === 'bool')).map(paramnode => (paramnode.name)) || []; - let returnIsSecret: string[] = []; const decStates = node._newASTPointer.decrementedSecretStates; if( node._newASTPointer.returnParameters.parameters) { @@ -1296,7 +1296,6 @@ const visitor = { node._newASTPointer = newNode.statements; parent._newASTPointer.body = newNode; }, - }, VariableDeclarationStatement: { @@ -1307,7 +1306,6 @@ const visitor = { node._newASTPointer = newNode; path.inList ? parent._newASTPointer.push(newNode) : parent._newASTPointer[path.containerName] = newNode; }, - }, BinaryOperation: { @@ -1317,7 +1315,6 @@ const visitor = { node._newASTPointer = newNode; path.inList ? parent._newASTPointer.push(newNode) : parent._newASTPointer[path.containerName] = newNode; }, - }, Assignment: { @@ -1350,7 +1347,11 @@ const visitor = { leftHandSide, rightHandSide: binOpNode, }); - binOpNode.leftExpression.name = path.node.leftHandSide.name; + if (path.node.leftHandSide.nodeType === 'IndexAccess') { + binOpNode.leftExpression.name = path.node.leftHandSide.baseExpression.name+'_'+ path.node.leftHandSide.indexExpression.name; + } else { + binOpNode.leftExpression.name = path.node.leftHandSide.name; + } return assNode; }; @@ -1388,7 +1389,6 @@ const visitor = { node._newASTPointer = newNode.components; parent._newASTPointer[path.containerName] = newNode; } - }, }, @@ -1515,7 +1515,6 @@ const visitor = { const accessedBeforeModification = indicator.isAccessed && indicator.accessedPaths[0].node.id < lhs.id && !indicator.accessedPaths[0].isModification(); if (accessedBeforeModification || path.isInSubScope()) accessed = true; - const newNode = buildNode('VariableDeclarationStatement', { oldASTId: node.id, declarations: [ @@ -1570,6 +1569,7 @@ const visitor = { //|| indicator?.interactsWithSecret, oldASTId: node.id, }); + node._newASTPointer = newNode; if (Array.isArray(parent._newASTPointer) || (!path.isInSubScope() && Array.isArray(parent._newASTPointer[path.containerName]))) { parent._newASTPointer.push(newNode); @@ -1659,7 +1659,6 @@ const visitor = { // we now have a param or a local var dec let interactsWithSecret = false; - scope.bindings[node.id].referencingPaths.forEach(refPath => { interactsWithSecret ||= refPath.node.interactsWithSecret; // check for internal function call if the parameter passed in the function call interacts with secret or not @@ -1728,7 +1727,6 @@ const visitor = { }); const dec = path.getAncestorOfType('VariableDeclaration').node; if (node.length.value && (path.isLocalStackVariable(dec) || path.isFunctionParameter(dec))) newNode.isConstantArray = true; - node._newASTPointer = newNode; if (Array.isArray(parent._newASTPointer)) { parent._newASTPointer.push(newNode); @@ -1745,10 +1743,8 @@ const visitor = { if(!!path.getAncestorOfType('EventDefinition')) return; if(!!path.getAncestorOfType('EmitStatement')) return; const newNode = buildNode(node.nodeType, { name: node.name }); - parent._newASTPointer[path.containerName] = newNode; }, - }, UserDefinedTypeName: { @@ -1816,9 +1812,8 @@ const visitor = { subType: node.typeDescriptions.typeString, }); // if this is a public state variable, this fn will add a public input - addPublicInput(path, state, null); + addPublicInput(path, state, newNode); state.skipSubNodes = true; // the subnodes are baseExpression and indexExpression - we skip them - parent._newASTPointer[path.containerName] = newNode; }, diff --git a/src/traverse/Scope.ts b/src/traverse/Scope.ts index 52b8e176b..03486be58 100644 --- a/src/traverse/Scope.ts +++ b/src/traverse/Scope.ts @@ -341,7 +341,6 @@ export class Scope { if (scope.scopeType === scopeType) return true; scope = scope.parentScope; } - return false; } @@ -361,7 +360,6 @@ export class Scope { const id = this.path.getReferencedDeclarationId(node); if (!id) return null; // if the node doesn't refer to another variable return this.queryAncestors((s: Scope) => { - const binding = s.bindings[id] if (!mappingKeyIndicatorOnly) return binding; if (binding instanceof VariableBinding) return binding.mappingKeys[this.getMappingKeyName(referencingNode)]; @@ -393,54 +391,47 @@ export class Scope { */ getReferencedIndicator(referencingNode: any, mappingKeyIndicatorOnly: boolean = false): StateVariableIndicator | MappingKey | null { const { path } = this; - if (!referencingNode) return null; + if (!referencingNode) + return null; const indicator = this.getIndicatorById( - path.getReferencedDeclarationId(referencingNode) || referencingNode.id + path.getReferencedDeclarationId(referencingNode) || referencingNode?.id ); - if (!path.isMapping(referencingNode) && !path.isArray(referencingNode) && !path.isStruct(referencingNode)) return indicator; - if (path.isStruct(referencingNode) && NodePath.getPath(referencingNode).getAncestorOfType('MemberAccess') && path.isMapping(referencingNode)) { const memberAccessNode = referencingNode.nodeType === 'MemberAccess' ? referencingNode : NodePath.getPath(referencingNode).getAncestorOfType('MemberAccess') .node; - const indexAccessNode = - memberAccessNode.expression.nodeType === 'IndexAccess' + memberAccessNode.expression?.nodeType === 'IndexAccess' ? memberAccessNode.expression : NodePath.getPath(memberAccessNode).getAncestorOfType('IndexAccess') - .node; - + ?.node; return mappingKeyIndicatorOnly ? indicator.mappingKeys[this.getMappingKeyName(indexAccessNode)] : indicator; } - - if (path.isStruct(referencingNode) && NodePath.getPath(referencingNode).getAncestorOfType('MemberAccess')) { + if (path.isStruct(referencingNode) && NodePath.getPath(referencingNode)?.getAncestorOfType('MemberAccess')) { const memberAccessNode = referencingNode.nodeType === 'MemberAccess' ? referencingNode - : NodePath.getPath(referencingNode).getAncestorOfType('MemberAccess') + : NodePath.getPath(referencingNode)?.getAncestorOfType('MemberAccess') .node; return mappingKeyIndicatorOnly ? indicator.structProperties[memberAccessNode.memberName] : indicator; } - if ((path.isConstantArray(referencingNode) || referencingNode.memberName === 'length') && !NodePath.getPath(referencingNode).getAncestorOfType('IndexAccess')) return indicator; - // getMappingKeyName requires an indexAccessNode - referencingNode may be a baseExpression or indexExpression contained Identifier const indexAccessNode = referencingNode.nodeType === 'IndexAccess' ? referencingNode - : NodePath.getPath(referencingNode).getAncestorOfType('IndexAccess') - .node; - + : NodePath.getPath(referencingNode)?.getAncestorOfType('IndexAccess') + ?.node; return mappingKeyIndicatorOnly - ? indicator.mappingKeys[this.getMappingKeyName(indexAccessNode)] + ? indicator?.mappingKeys[this.getMappingKeyName(indexAccessNode)] : indicator; } - + /** * @returns {Node || null} - the node (VariableDeclaration) being referred-to by the input referencingNode. */ diff --git a/test/contracts/Arrays-input.zol b/test/contracts/Arrays-input.zol index 2385c9aa5..0bf6f7fe6 100644 --- a/test/contracts/Arrays-input.zol +++ b/test/contracts/Arrays-input.zol @@ -5,14 +5,13 @@ pragma solidity ^0.8.0; contract Assign { secret uint256 private a; - uint256[5] public b; function add(secret uint256[5] calldata value, uint256[5] calldata publicValue) public { b = publicValue; for (uint256 index = 0; index < 5; index++) { - known a += value[index]; + known a += value[index]; } } diff --git a/test/contracts/Non-Secret-Array-Incrementation.zol b/test/contracts/Non-Secret-Array-Incrementation.zol new file mode 100644 index 000000000..add58b758 --- /dev/null +++ b/test/contracts/Non-Secret-Array-Incrementation.zol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: CC0 +pragma solidity ^0.8.0; + +contract Assign { +secret uint256 private a; +secret uint256 private b; +mapping(uint256 => uint256) public c; + +function add( uint256 value ) public { +a+= value; +unknown b += value +a; +c[0] += value; +} + +function add1(secret uint256 value ) public { +unknown b += value +a; +unknown b += c[0]; +} + +} \ No newline at end of file diff --git a/test/contracts/Non-Secret-Array.zol b/test/contracts/Non-Secret-Array.zol new file mode 100644 index 000000000..2df978650 --- /dev/null +++ b/test/contracts/Non-Secret-Array.zol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: CC0 + +pragma solidity ^0.8.0; +contract Assign { +secret uint256 private a; +secret uint256 private b; +uint256 public index; +uint256[3] public c; + + +function add( uint256 value) public { +c[index] += value; +} + +function add1( uint256 value) public { +c[index] += value; +known a += c[index]; +} + +function add2( uint256 value) public { +c[index] += value; +known a += value; +} + +} \ No newline at end of file