diff --git a/src/boilerplate/circuit/zokrates/nodes/BoilerplateGenerator.ts b/src/boilerplate/circuit/zokrates/nodes/BoilerplateGenerator.ts index 184774098..02300f8f1 100644 --- a/src/boilerplate/circuit/zokrates/nodes/BoilerplateGenerator.ts +++ b/src/boilerplate/circuit/zokrates/nodes/BoilerplateGenerator.ts @@ -181,7 +181,6 @@ class BoilerplateGenerator { if (!mappingKeyIndicator.keyPath.isMsg() && (mappingKeyIndicator.keyPath.node.nodeType === 'Literal'|| mappingKeyIndicator.keyPath.isLocalStackVariable() || !mappingKeyIndicator.keyPath.isSecret)) this.mappingKeyTypeName = 'local'; - this.mappingName = this.indicators.name; this.name = `${this.mappingName}_${mappingKeyName}`.replaceAll('.', 'dot').replace('[', '_').replace(']', ''); diff --git a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts index 528eb4ee4..773e21fa8 100644 --- a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts +++ b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts @@ -135,8 +135,18 @@ export const generateProofBoilerplate = (node: any) => { 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'); + + // Check if the mapping is already included in the parameters + let name: string; + let state: any; + let isIncluded = false; + for ([name, state] of Object.entries(node.privateStates)) { + if (stateNode.stateVarId[0] === state.stateVarId[0] && stateName != name && node.parameters.includes(state.stateVarId[1]) ) { + isIncluded = true; + } + } const stateVarIdLines = - stateNode.isMapping && !node.parameters.includes(stateNode.stateVarId[1]) && !msgSenderParamAndMappingKey && !msgValueParamAndMappingKey && !constantMappingKey + stateNode.isMapping && !(node.parameters.includes(stateNode.stateVarId[1])) && !(node.parameters.includes(stateNode.stateVarId[2])) && !isIncluded && !msgSenderParamAndMappingKey && !msgValueParamAndMappingKey && !constantMappingKey ? [`\n\t\t\t\t\t\t\t\t${stateName}_stateVarId_key.integer,`] : []; // we add any extra params the circuit needs diff --git a/src/codeGenerators/circuit/zokrates/toCircuit.ts b/src/codeGenerators/circuit/zokrates/toCircuit.ts index 7dfc6bc1b..e77d92131 100644 --- a/src/codeGenerators/circuit/zokrates/toCircuit.ts +++ b/src/codeGenerators/circuit/zokrates/toCircuit.ts @@ -118,7 +118,6 @@ function codeGenerator(node: any) { case 'ParameterList': { const paramList = CircuitBP.uniqueify(node.parameters.flatMap(codeGenerator)); - // we also need to identify and remove duplicate params prefixed with conflicting 'public'/'private' keywords (prioritising 'public') const slicedParamList = paramList.map(p => p.replace('public ', '').replace('private ', ''), @@ -170,7 +169,8 @@ function codeGenerator(node: any) { case 'Block': { const preStatements = CircuitBP.uniqueify(node.preStatements.flatMap(codeGenerator)); - const statements = CircuitBP.uniqueify(node.statements.flatMap(codeGenerator)); + // TO DO: We don't remove duplicate statements below because of duplicate statements in the contract. This could cause issues. + const statements = node.statements.flatMap(codeGenerator); const postStatements = CircuitBP.uniqueify(node.postStatements.flatMap(codeGenerator)); return [...preStatements, ...statements, ...postStatements].join('\n\n'); } diff --git a/src/codeGenerators/contract/solidity/toContract.ts b/src/codeGenerators/contract/solidity/toContract.ts index d3aad79d7..b09c89d88 100644 --- a/src/codeGenerators/contract/solidity/toContract.ts +++ b/src/codeGenerators/contract/solidity/toContract.ts @@ -143,7 +143,8 @@ function codeGenerator(node: any) { 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'); + //We have changed the order here so that statements is after poststatements because we need the statements to appear after proof generation. This could cause issues. + return [...preStatements, ...postStatements, ...statements].join('\n'); } case 'ExpressionStatement':{ return codeGenerator(node.expression); diff --git a/src/codeGenerators/orchestration/nodejs/toOrchestration.ts b/src/codeGenerators/orchestration/nodejs/toOrchestration.ts index 6b4ce4b83..877cd05bb 100644 --- a/src/codeGenerators/orchestration/nodejs/toOrchestration.ts +++ b/src/codeGenerators/orchestration/nodejs/toOrchestration.ts @@ -23,7 +23,8 @@ const getAccessedValue = (name: string) => { */ const getPublicValue = (node: any) => { if (node.nodeType !== 'IndexAccess') - return `\nlet ${node.name} = generalise(await instance.methods.${codeGenerator(node)}().call());`; + // 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());`; }; @@ -83,7 +84,7 @@ export default function codeGenerator(node: any, options: any = {}): any { } if (node.declarations[0].isStruct) return `\n let ${codeGenerator(node.declarations[0])} = {}; \n${codeGenerator(node.initialValue)};`; return `\nlet ${codeGenerator(node.initialValue)};`; - } else if (node.declarations[0].isAccessed && !node.declarations[0].isSecret) { + } else if (node.declarations[0].isAccessed && !node.declarations[0].isSecret) { return `${getPublicValue(node.declarations[0])}` } else if (node.declarations[0].isAccessed) { return `${getAccessedValue(node.declarations[0].name)}`; @@ -126,16 +127,31 @@ export default function codeGenerator(node: any, options: any = {}): any { return " "; case 'Assignment': - if (['+=', '-=', '*='].includes(node.operator)) { - return `${codeGenerator(node.leftHandSide, { - lhs: true, - })} = ${codeGenerator(node.leftHandSide)} ${node.operator.charAt( - 0, - )} ${codeGenerator(node.rightHandSide)}`; + // 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, { + lhs: true, + })} = generalise(${codeGenerator(node.leftHandSide)} ${node.operator.charAt( + 0, + )} ${codeGenerator(node.rightHandSide)})`; + } + return `${codeGenerator(node.leftHandSide, { lhs: true })} ${ + node.operator + } generalise(${codeGenerator(node.rightHandSide)})`; + } else { + if (['+=', '-=', '*='].includes(node.operator)) { + return `${codeGenerator(node.leftHandSide, { + lhs: true, + })} = ${codeGenerator(node.leftHandSide)} ${node.operator.charAt( + 0, + )} ${codeGenerator(node.rightHandSide)}`; + } + return `${codeGenerator(node.leftHandSide, { lhs: true })} ${ + node.operator + } ${codeGenerator(node.rightHandSide)}`; } - return `${codeGenerator(node.leftHandSide, { lhs: true })} ${ - node.operator - } ${codeGenerator(node.rightHandSide)}`; + case 'BinaryOperation': return `${codeGenerator(node.leftExpression, { lhs: options.condition })} ${ @@ -192,7 +208,7 @@ export default function codeGenerator(node: any, options: any = {}): any { case 'UnaryOperation': // ++ or -- on a parseInt() does not work - return `generalise(${node.subExpression.name}.integer${node.operator})`; + return `parseInt(${node.subExpression.name}.integer,10)${node.operator[0]}1`; case 'Literal': return node.value; diff --git a/src/transformers/visitors/checks/incrementedVisitor.ts b/src/transformers/visitors/checks/incrementedVisitor.ts index 7d99d4b75..4b10c8cff 100644 --- a/src/transformers/visitors/checks/incrementedVisitor.ts +++ b/src/transformers/visitors/checks/incrementedVisitor.ts @@ -238,6 +238,7 @@ export default { const { operator, leftHandSide, rightHandSide } = node; const lhsSecret = !!scope.getReferencedBinding(leftHandSide)?.isSecret; + if (['bool', 'address'].includes(leftHandSide.typeDescriptions.typeString)) { markParentIncrementation(path, state, false, false, leftHandSide); const lhsBinding = scope.getReferencedBinding(leftHandSide) @@ -272,19 +273,17 @@ export default { } // a *= something, a /= something - // OR lhs non-secret - we don't care about those if ( operator === '%=' || operator === '/=' || - operator === '*=' || - !lhsSecret + operator === '*=' ) { markParentIncrementation(path, state, false, false, leftHandSide); return; } // after +=, -=, %=, *=, /=, we can only deal with = - if (operator !== '=' && operator !== '+=' && operator !== '-=') + if ((operator !== '=' && operator !== '+=' && operator !== '-=') && lhsSecret) throw new TODOError( `Operator '${operator}' not yet supported. Please open an issue.`, node, @@ -292,7 +291,7 @@ export default { // then, it depends what's on the RHS of the assignment, so we continue // we save the LHS node to help us later - state.incrementedIdentifier = leftHandSide.baseExpression || leftHandSide; + if (lhsSecret) state.incrementedIdentifier = leftHandSide.baseExpression || leftHandSide; }, }, diff --git a/src/transformers/visitors/toCircuitVisitor.ts b/src/transformers/visitors/toCircuitVisitor.ts index 244ecaf78..4de792b53 100644 --- a/src/transformers/visitors/toCircuitVisitor.ts +++ b/src/transformers/visitors/toCircuitVisitor.ts @@ -9,8 +9,132 @@ import explode from './explode.js'; import internalCallVisitor from './circuitInternalFunctionCallVisitor.js'; import { VariableBinding } from '../../traverse/Binding.js'; import { StateVariableIndicator} from '../../traverse/Indicator.js'; +import { LocalVariableIndicator} from '../../traverse/Indicator.js'; import { interactsWithSecretVisitor, internalFunctionCallVisitor, parentnewASTPointer, getIndexAccessName } from './common.js'; + + +// public variables that interact with the secret also need to be modified within the circuit. +const publicVariablesVisitor = (path: NodePath, state: any, IDnode: any) => { + const {parent, node } = path; + // Break if the identifier is a mapping or array. + if ( parent.indexExpression && parent.baseExpression === node ) { + return;} + const binding = path.getReferencedBinding(node); + if (!['Identifier', 'IndexAccess'].includes(path.nodeType)) return; + + // If there is a statment where a secret variable interacts with a public one, we need to adjust previous statements where the public variable was modified. + + if ( + binding instanceof VariableBinding && + (node.interactsWithSecret || node.baseExpression?.interactsWithSecret) && + (node.interactsWithPublic || node.baseExpression?.interactsWithPublic) && + binding.stateVariable && !binding.isSecret + ) { + const fnDefNode = path.getAncestorOfType('FunctionDefinition'); + if (!fnDefNode) throw new Error(`Not in a function`); + + 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; + // 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) => { + const expressionId = p.getAncestorOfType('ExpressionStatement')?.node?.id; + if (expressionId) { + if (path.containerName !== 'indexExpression') { + num_modifiers++; + } + let expNode = statements.find((n:any) => n?.id === expressionId); + let index_expNode = fnDefNode.node._newASTPointer.body.statements.indexOf(expNode); + if (expNode && !expNode.isAccessed) { + expNode.isAccessed = true; + if((expNode.expression && expNode.expression.leftHandSide && expNode.expression.leftHandSide?.name === node.name) || + (expNode.initialValue && expNode.initialValue.leftHandSide && expNode.initialValue.leftHandSide?.name === node.name)){ + if (num_modifiers !=0){ + 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('ExpressionStatement', { + expression: initInnerNode, + interactsWithSecret: true, + isVarDec: true, + }); + if (index_expNode !== -1) { + fnDefNode.node._newASTPointer.body.statements.splice(index_expNode + 1, 0, newNode1); + } + } + } else{ + let modName = expNode.expression.initialValue?.leftHandSide?.name || expNode.expression.initialValue?.name || expNode.expression.leftHandSide?.name; + const InnerNode = buildNode('Assignment', { + leftHandSide: buildNode('Identifier', { name: `${node.name}`, subType: 'generalNumber' }), + operator: '=', + rightHandSide: buildNode('Identifier', { name: `${modName}`, subType: 'generalNumber' }) + }); + const newNode1 = buildNode('ExpressionStatement', { + expression: InnerNode, + interactsWithSecret: true, + }); + if (index_expNode !== -1) { + fnDefNode.node._newASTPointer.body.statements.splice(index_expNode + 1, 0, newNode1); + } + if (`${modName}` !== `${node.name}_${num_modifiers}` && num_modifiers !==0){ + const initInnerNode1 = buildNode('Assignment', { + leftHandSide: buildNode('Identifier', { name: `${node.name}_${num_modifiers}`, subType: 'generalNumber' }), + operator: '=', + rightHandSide: buildNode('Identifier', { name: `${node.name}`, subType: 'generalNumber' }) + }); + const newNode2 = buildNode('ExpressionStatement', { + expression: initInnerNode1, + interactsWithSecret: true, + isVarDec: true, + }); + if (index_expNode !== -1) { + fnDefNode.node._newASTPointer.body.statements.splice(index_expNode + 2, 0, 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}`; + } + } + // 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. + for (let i = fnDefNode.node._newASTPointer.body.statements.length - 1; i >= 0; i--) { + const p = fnDefNode.node._newASTPointer.body.statements[i]; + if (p.expression?.rightHandSide?.name === `${node.name}_init`) { + fnDefNode.node._newASTPointer.body.statements.splice(i, 1); + } + } + const endNodeInit = buildNode('Assignment', { + leftHandSide: buildNode('Identifier', { name: `${node.name}`, subType: 'generalNumber' }), + operator: '=', + rightHandSide: buildNode('Identifier', { name: `${node.name}_init`, subType: 'generalNumber' }), + }); + const endNode = buildNode('ExpressionStatement', { + expression: endNodeInit, + interactsWithSecret: true, + isVarDec: false, + }); + endNode.isEndInit = true; + fnDefNode.node._newASTPointer.body.statements.push(endNode); + } + // We no longer need this because index expression nodes are not input. + //if (['Identifier', 'IndexAccess'].includes(node.indexExpression?.nodeType)) publicVariablesVisitor(NodePath.getPath(node.indexExpression), state, null); +} + + // below stub will only work with a small subtree - passing a whole AST will always give true! // useful for subtrees like ExpressionStatements const publicInputsVisitor = (thisPath: NodePath, thisState: any) => { @@ -46,14 +170,29 @@ const publicInputsVisitor = (thisPath: NodePath, thisState: any) => { // TODO other types if (thisPath.isMapping() || thisPath.isArray()) name = name.replace('[', '_').replace(']', '').replace('.sender', 'Sender').replace('.value','Value'); - if (thisPath.containerName === 'indexExpression') - name = binding.getMappingKeyName(thisPath); + // We never need the input to the circuit to be the MappingKeyName + //if (thisPath.containerName === 'indexExpression'){ + // name = binding.getMappingKeyName(thisPath); + //} const parameterNode = buildNode('VariableDeclaration', { name, type: 'field', isSecret: false, declarationType: 'parameter'}); parameterNode.id = thisPath.isMapping() || thisPath.isArray() ? binding.id + thisPath.getAncestorOfType('IndexAccess')?.node.indexExpression.referencedDeclaration : binding.id; const fnDefNode = thisPath.getAncestorOfType('FunctionDefinition')?.node; const params = fnDefNode._newASTPointer.parameters.parameters; - if (!params.some(n => n.id === parameterNode.id)) + if (!params.some(n => n.id === parameterNode.id)){ params.push(parameterNode); + // For each non-secret variable that is input to the circuit, we need to ensure the initial value is stored for later. + const beginNodeInit = buildNode('Assignment', { + leftHandSide: buildNode('Identifier', { name: `${name}_init`, subType: 'generalNumber' }), + operator: '=', + rightHandSide: buildNode('Identifier', { name: `${name}`, subType: 'generalNumber' }), + }); + const beginNode = buildNode('ExpressionStatement', { + expression: beginNodeInit, + interactsWithSecret: true, + isVarDec: true, + }); + fnDefNode._newASTPointer.body.statements.unshift(beginNode); + } // even if the indexAccessNode is not a public input, we don't want to check its base and index expression nodes thisState.skipSubNodes = true; } @@ -174,6 +313,25 @@ const visitor = { const { indicators } = scope; const newFunctionDefinitionNode = node._newASTPointer; + //Ensure we do not have any statements of the form x = x_init where x is not a parameter input to the circuit. + for (let i = newFunctionDefinitionNode.body.statements.length - 1; i >= 0; i--) { + const statementNode = newFunctionDefinitionNode.body.statements[i]; + if ( statementNode.isEndInit && + newFunctionDefinitionNode.parameters.parameters.every(paramNode => paramNode.name !== statementNode.expression?.leftHandSide.name) + ) { + newFunctionDefinitionNode.body.statements.splice(i, 1); + } + } + + /// Ensure non-secret inputs to the circuit are not declared + for (let i = newFunctionDefinitionNode.body.statements.length - 1; i >= 0; i--) { + const statementNode = newFunctionDefinitionNode.body.statements[i]; + newFunctionDefinitionNode.parameters.parameters.forEach((paramNode) => { + if ( statementNode.isVarDec && statementNode.expression && (paramNode.name === statementNode.expression.leftHandSide?.name || paramNode.name === statementNode.expression.initialValue?.name)) { + statementNode.isVarDec =false; + } + }); + } const joinCommitmentsNode = buildNode('File', { fileName: `joinCommitments`, @@ -425,7 +583,6 @@ const visitor = { VariableDeclarationStatement: { enter(path: NodePath, state: any) { const { node, parent, scope } = path; - if (node.stateVariable) { throw new Error( `TODO: VariableDeclarationStatements of secret state variables are tricky to initialise because they're assigned-to outside of a function. Future enhancement.`, @@ -438,7 +595,7 @@ const visitor = { if ( declarationType === 'localStack' && - !node.isSecret && + !node.isSecret && !node.declarations[0].isSecret && !scope.getReferencedIndicator(node)?.interactsWithSecret && !path.getAncestorContainedWithin('initializationExpression') ) { @@ -496,7 +653,7 @@ const visitor = { const op = operator.charAt(0); const binOpNode = buildNode('BinaryOperation', { operator: op, - leftExpression: leftHandSide, + leftExpression: cloneDeep(leftHandSide), rightExpression: rightHandSide, }); const assNode = buildNode('Assignment', { @@ -504,6 +661,15 @@ const visitor = { leftHandSide, rightHandSide: binOpNode, }); + // We need to ensure that for non-secret variables the name used on the right hand side of the assignment + // is always the original name. (As the original variable is always updated we always get the right value.) + const binding = path.getReferencedBinding(path.node.leftHandSide); + if( (binding instanceof VariableBinding) && !binding.isSecret && + binding.stateVariable){ + binOpNode.leftExpression.name = path.node.leftHandSide.name; + } else { + binOpNode.leftExpression.name = path.scope.getIdentifierMappingKeyName(path.node.leftHandSide, true); + } return assNode; }; @@ -528,16 +694,26 @@ const visitor = { enter(path: NodePath, state: any) { const { node, parent } = path; const { operator, prefix, subExpression } = node; + const binding = path.getReferencedBinding(node.subExpression); const newNode = buildNode(node.nodeType, { operator, prefix, - subExpression: buildNode(subExpression.nodeType, { - name: path.scope.getIdentifierMappingKeyName(subExpression, true) - }), initialValue: buildNode(subExpression.nodeType, { name: path.scope.getIdentifierMappingKeyName(subExpression) }), }); + //We need to ensure that for non-secret variables the name used on the right hand side of the assignment + // is always the original name. (As the original variable is always updated we always get the right value.) + if ( (binding instanceof VariableBinding) && !binding.isSecret && + binding.stateVariable){ + newNode.subExpression = buildNode(subExpression.nodeType, { + name: subExpression.name, + }); + } else{ + newNode.subExpression = buildNode(subExpression.nodeType, { + name: path.scope.getIdentifierMappingKeyName(subExpression, true) + }); + } node._newASTPointer = newNode; parentnewASTPointer(parent, path, newNode, parent._newASTPointer[path.containerName]); state.skipSubNodes = true; @@ -653,7 +829,6 @@ let childOfSecret = path.getAncestorOfType('ForStatement')?.containsSecret; .replace('.value','Value') .replace('.', 'dot') : referencedIndicator?.name; - if (referencedIndicator?.isMapping && lhs.baseExpression) { lhs = lhs.baseExpression; } else if (lhs.nodeType === 'MemberAccess') { @@ -661,30 +836,55 @@ let childOfSecret = path.getAncestorOfType('ForStatement')?.containsSecret; if (lhs.baseExpression) lhs = lhs.baseExpression; } // collect all index names - const names = referencedIndicator?.referencingPaths.map((p: NodePath) => ({ name: scope.getIdentifierMappingKeyName(p.node), id: p.node.id })).filter(n => n.id <= lhs.id); - - // check whether this is the first instance of a new index name - const firstInstanceOfNewName = names && names.length > 1 && names[names.length - 1].name !== names[names.length - 2].name; + const names = referencedIndicator.referencingPaths.map((p: NodePath) => ({ name: p.getAncestorContainedWithin('rightHandSide') ? p.node.name : scope.getIdentifierMappingKeyName(p.node), id: p.node.id })).filter(n => n.id <= lhs.id); + // check whether this is the first instance of a new index name. We only care if the previous index name is on the left hand side, because this will lead to a double variable declaration. + let firstInstanceOfNewName = true; + let i =0; + // We check that the name has not been used previously, in this case we need to declare it. + // We ensure that variables are not declared when they are input to the circuit elsewhere. + names.forEach((elem) => { + if (i !== names.length - 1 && names[names.length - 1].name === elem.name){ + firstInstanceOfNewName = false; + } + i++; + }); + if (referencedIndicator instanceof StateVariableIndicator && - (firstInstanceOfNewName || (lhs.id === referencedIndicator.referencingPaths[0].node.id || - lhs.id === referencedIndicator.referencingPaths[0].parent.id)) && // the parent logic captures IndexAccess nodes whose IndexAccess.baseExpression was actually the referencingPath + (firstInstanceOfNewName + || (referencedIndicator.isSecret && (lhs.id === referencedIndicator.referencingPaths[0].node.id ||lhs.id === referencedIndicator.referencingPaths[0].parent.id)) + ) && // the parent logic captures IndexAccess nodes whose IndexAccess.baseExpression was actually the referencingPath !( referencedIndicator.isWhole && referencedIndicator.oldCommitmentAccessRequired ) // FIX - sometimes a variable will be declared twice when we insert oldCommitmentPreimage preStatements before an overwrite - we check here ) { isVarDec = true; - } + } + if (referencedIndicator instanceof LocalVariableIndicator && firstInstanceOfNewName && names[names.length - 1].name !== referencedIndicator.name){ + isVarDec = true; + } } - + let nodeID = node.id; newNode = buildNode('ExpressionStatement', { isVarDec }); + newNode.id = nodeID; + newNode.isAccessed = false; node._newASTPointer = newNode; if (Array.isArray(parent._newASTPointer)) { parent._newASTPointer.push(newNode); } else { parent._newASTPointer[path.containerName] = newNode; } - }, + const fnDefNode = path.getAncestorOfType('FunctionDefinition'); + // We ensure the original variable name is set to the initial value only at the end of the statements. + //E.g index = index_init should only appear at the end of all the modifying statements. + let ind = fnDefNode.node._newASTPointer.body.statements.length - 2; + while (ind >= 0 && fnDefNode.node._newASTPointer.body.statements[ind].expression?.rightHandSide?.name && fnDefNode.node._newASTPointer.body.statements[ind].expression?.rightHandSide?.name.includes("_init")){ + let temp = fnDefNode.node._newASTPointer.body.statements[ind+1]; + fnDefNode.node._newASTPointer.body.statements[ind+1] = fnDefNode.node._newASTPointer.body.statements[ind]; + fnDefNode.node._newASTPointer.body.statements[ind] = temp; + ind--; + } + } }, StructDefinition: { @@ -868,13 +1068,19 @@ let childOfSecret = path.getAncestorOfType('ForStatement')?.containsSecret; // local variable decs and parameters are dealt with elsewhere // secret state vars are input via commitment values if (!state.skipPublicInputs) path.traversePathsFast(publicInputsVisitor, {}); - name = path.scope.getIdentifierMappingKeyName(node); - + // Only use the mapping key name if it is on the left hand side. + const binding = path.getReferencedBinding(node); + if ( (binding instanceof VariableBinding) && !binding.isSecret && + binding.stateVariable && path.getAncestorContainedWithin('rightHandSide') ){ + } else{ + name = path.scope.getIdentifierMappingKeyName(node); + } const newNode = buildNode( node.nodeType, { name, type: node.typeDescriptions?.typeString }, ); if (path.isStruct(node)) addStructDefinition(path); + publicVariablesVisitor(path, state,newNode); if (path.getAncestorOfType('IfStatement')) node._newASTPointer = newNode; // no pointer needed, because this is a leaf, so we won't be recursing any further. // UNLESS we must add and rename if conditionals @@ -969,6 +1175,12 @@ let childOfSecret = path.getAncestorOfType('ForStatement')?.containsSecret; }; let identifiersInCond = { skipSubNodes: false, list: [] }; path.traversePathsFast(findConditionIdentifiers, identifiersInCond); + // Remove duplicates + identifiersInCond.list = identifiersInCond.list.filter((value, index, self) => + index === self.findIndex((t) => ( + t.name === value.name + )) + ); path.node._newASTPointer.conditionVars = identifiersInCond.list; // Determine whether each identifier in conditionVar is a new declaration or a redeclaration. path.node._newASTPointer.conditionVars.forEach((condVar) => { @@ -1067,6 +1279,8 @@ let childOfSecret = path.getAncestorOfType('ForStatement')?.containsSecret; const newNode = buildNode('IndexAccess'); if (path.isConstantArray(node) && (path.isLocalStackVariable(node) || path.isFunctionParameter(node))) newNode.isConstantArray = true; + // We don't need this because index access expressions always contain identifiers. + //publicVariablesVisitor(path, state,newNode); node._newASTPointer = newNode; parent._newASTPointer[path.containerName] = newNode; }, diff --git a/src/transformers/visitors/toOrchestrationVisitor.ts b/src/transformers/visitors/toOrchestrationVisitor.ts index 9bb1e5e3c..f995df9a4 100644 --- a/src/transformers/visitors/toOrchestrationVisitor.ts +++ b/src/transformers/visitors/toOrchestrationVisitor.ts @@ -83,7 +83,7 @@ const collectIncrements = (stateVarIndicator: StateVariableIndicator | MappingKe // gathers public inputs we need to extract from the contract // i.e. public 'accessed' variables -const addPublicInput = (path: NodePath, state: any) => { +const addPublicInput = (path: NodePath, state: any, IDnode: any) => { const { node } = path; let { name } = path.scope.getReferencedIndicator(node, true) || path.node; const binding = path.getReferencedBinding(node); @@ -96,6 +96,7 @@ const addPublicInput = (path: NodePath, state: any) => { // below: we have a public state variable we need as a public input to the circuit // local variable decs and parameters are dealt with elsewhere // secret state vars are input via commitment values + if ( binding instanceof VariableBinding && (node.interactsWithSecret || node.baseExpression?.interactsWithSecret || isCondition || isForCondition || isInitializationExpression || isLoopExpression) && @@ -141,40 +142,126 @@ const addPublicInput = (path: NodePath, state: any) => { fnDefNode.node._newASTPointer.body.preStatements ??= []; + // check we haven't already imported this node + if (!(fnDefNode.node._newASTPointer.body.preStatements.some((n: any) => n.nodeType === 'VariableDeclarationStatement' && n.declarations[0]?.name === name))) { + fnDefNode.node._newASTPointer.body.preStatements.unshift( + newNode, + ); + } + // below: we move statements into preStatementsif they are modified before the relevant secret state 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; + // 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) => { const expressionId = p.getAncestorOfType('ExpressionStatement')?.node?.id; - // if the public input is modified before here, it won't show up in the mjs file - // we have to go back and mark any editing statements as interactsWithSecret so they show up if (expressionId) { let expNode = statements.find((n:any) => n?.id === expressionId); + if (path.containerName !== 'indexExpression') { + num_modifiers++; + } if (expNode) { + // if the public input is modified before here, it won't show up in the mjs file + // we have to go back and mark any editing statements as interactsWithSecret so they show up expNode.interactsWithSecret = true; const moveExpNode = cloneDeep(expNode); - delete statements[statements.indexOf(expNode)]; fnDefNode.node._newASTPointer.body.preStatements.push(moveExpNode); + delete statements[statements.indexOf(expNode)]; + if( + (expNode.expression && expNode.expression.leftHandSide && expNode.expression.leftHandSide?.name === node.name) || + (expNode.initialValue && expNode.initialValue.leftHandSide && expNode.initialValue.leftHandSide?.name === node.name) + ) { + if (num_modifiers !=0){ + const decInnerNode = buildNode('VariableDeclaration', { + name: `${node.name}_${num_modifiers}`, + isAccessed: true, + 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, + interactsWithSecret: true, + isModifiedDeclaration: true, + }); + fnDefNode.node._newASTPointer.body.preStatements.push(newNode1); + } + } else{ + let name_new = expNode.expression?.initialValue?.leftHandSide?.name || expNode.initialValue?.leftHandSide.name || expNode.expression?.leftHandSide.name; + const InnerNode = buildNode('Assignment', { + leftHandSide: buildNode('Identifier', { name: `${node.name}`, subType: 'generalNumber' }), + operator: '=', + rightHandSide: buildNode('Identifier', { name: `${name_new}`, subType: 'generalNumber' }) + }); + const newNode1 = buildNode('ExpressionStatement', { + expression: InnerNode, + interactsWithSecret: true, + }); + fnDefNode.node._newASTPointer.body.preStatements.push(newNode1); + if (`${name_new}` !== `${node.name}_${num_modifiers}` && num_modifiers !==0){ + const decInnerNode1 = buildNode('VariableDeclaration', { + name: `${node.name}_${num_modifiers}`, + isAccessed: true, + isSecret: false, + interactsWithSecret: true, + }); + const initInnerNode1 = buildNode('Assignment', { + leftHandSide: buildNode('Identifier', { name: `${node.name}_${num_modifiers}`, subType: 'generalNumber' }), + operator: '=', + rightHandSide: buildNode('Identifier', { name: `${node.name}`, subType: 'generalNumber' }) + }); + const newNode2 = buildNode('VariableDeclarationStatement', { + declarations: [decInnerNode1], + initialValue: initInnerNode1, + interactsWithSecret: true, + isModifiedDeclaration: true, + }); + 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}`; + } + } + // 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' }), + operator: '=', + rightHandSide: buildNode('Identifier', { name: `${node.name}_init`, subType: 'generalNumber' }), + }); + const endNode = buildNode('ExpressionStatement', { + expression: endNodeInit, + 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); - - // check we haven't already imported this node - if (fnDefNode.node._newASTPointer.body.preStatements.some((n: any) => n.nodeType === 'VariableDeclarationStatement' && n.declarations[0]?.name === name)) return; + if (!(path.containerName === 'indexExpression' && !(path.parentPath.isSecret|| path.parent.containsSecret))) state.publicInputs.push(node); + } - fnDefNode.node._newASTPointer.body.preStatements.unshift( - newNode, - ); - } - - if (['Identifier', 'IndexAccess'].includes(node.indexExpression?.nodeType)) addPublicInput(NodePath.getPath(node.indexExpression), state); + if (['Identifier', 'IndexAccess'].includes(node.indexExpression?.nodeType)) addPublicInput(NodePath.getPath(node.indexExpression), state, null); } /** * @desc: @@ -439,7 +526,7 @@ const visitor = { stateVarIndicator.container?.isAccessed && !stateVarIndicator.container?.isModified; secretModified = stateVarIndicator.container?.isSecret && stateVarIndicator.container?.isModified; - id = [id, scope.getMappingKeyName(stateVarIndicator.keyPath.node) || ``]; + id = [id, scope.getMappingKeyName(stateVarIndicator.keyPath.node) || ``, stateVarIndicator.keyPath.node.name]; name = (accessedOnly ? getIndexAccessName(stateVarIndicator.accessedPaths[stateVarIndicator.accessedPaths.length -1]?.getAncestorOfType('IndexAccess')?.node) : @@ -936,6 +1023,49 @@ const visitor = { } }, + exit(path: NodePath) { + // Convert 'a += b' into 'a = a + b' for all operators, because otherwise we cannot get the correct name with getIdentifierMappingKeyName. + // For, example instead of a_3 += 5 we need a_3 = a_2 +5. + const expandAssignment = (node: any) => { + const { operator, leftHandSide, rightHandSide } = node; + const expandableOps = ['+=', '-=', '*=', '/=', '%=', '|=', '&=', '^=']; + if (!expandableOps.includes(operator)) return node; + const op = operator.charAt(0); + const binOpNode = buildNode('BinaryOperation', { + operator: op, + leftExpression: cloneDeep(leftHandSide), + rightExpression: rightHandSide, + }); + const assNode = buildNode('Assignment', { + operator: '=', + leftHandSide, + rightHandSide: binOpNode, + }); + binOpNode.leftExpression.name = path.node.leftHandSide.name; + return assNode; + }; + + const { parent } = path; + const binding = path.getReferencedBinding(path.node.leftHandSide); + if( (binding instanceof VariableBinding) && !binding.isSecret && + binding.stateVariable){ + if (parent._newASTPointer.nodeType === 'VariableDeclarationStatement') { + const circuitNode = parent._newASTPointer.initialValue; + const newNode = expandAssignment(circuitNode); + parent._newASTPointer.initialValue = newNode; + } else { + const circuitNode = parent._newASTPointer.expression; + const newNode = expandAssignment(circuitNode); + parent._newASTPointer.expression = newNode; + } + } + if (path.getAncestorContainedWithin('initializationExpression') && parent._newASTPointer.nodeType === 'VariableDeclarationStatement' ){ + parent._newASTPointer.initialValue.isInitializationAssignment = true; + } else if (path.getAncestorContainedWithin('initializationExpression')) { + parent._newASTPointer.expression.isInitializationAssignment = true; + } + // node._newASTPointer = newNode; // no need to ascribe the node._newASTPointer, because we're exiting. + }, }, TupleExpression: { @@ -966,10 +1096,20 @@ const visitor = { newRHS.traverse(visitor, {}); newNode.leftHandSide = newRHS.parent._newASTPointer.subExpression; - newNode.rightHandSide.subExpression = buildNode('Identifier', { - name: path.scope.getIdentifierMappingKeyName(subExpression, true), - subType: node.typeDescriptions.typeString, - }); + const binding = path.getReferencedBinding(node.subExpression); + // If the variable is non-secret we use the original variable name on the right hand side as this is always the correct value. + if ( (binding instanceof VariableBinding) && !binding.isSecret && + binding.stateVariable){ + newNode.rightHandSide.subExpression = buildNode('Identifier', { + name: subExpression.name, + subType: node.typeDescriptions.typeString, + }); + } else{ + newNode.rightHandSide.subExpression = buildNode('Identifier', { + name: path.scope.getIdentifierMappingKeyName(subExpression, true), + subType: node.typeDescriptions.typeString, + }); + } node._newASTPointer = newNode; if (parent._newASTPointer.nodeType === 'VariableDeclarationStatement') { @@ -991,7 +1131,6 @@ const visitor = { const newState: any = {}; path.traversePathsFast(interactsWithSecretVisitor, newState); const { interactsWithSecret } = newState; - let indicator; let name; // we mark this to grab anything we need from the db / contract @@ -1017,15 +1156,27 @@ const visitor = { lhs = lhs.expression; if (lhs.baseExpression) lhs = lhs.baseExpression; } - + // check whether this statement should be init separately in the constructor const requiresConstructorInit = state.constructorStatements?.some((node: any) => node.declarations[0].name === indicator.name) && scope.scopeName === ''; - // collect all index names - const names = indicator.referencingPaths.map((p: NodePath) => ({ name: scope.getIdentifierMappingKeyName(p.node), id: p.node.id })).filter(n => n.id <= lhs.id); - - // check whether this is the first instance of a new index name - const firstInstanceOfNewName = names.length > 1 && names[names.length - 1].name !== names[names.length - 2].name; + const names = indicator.referencingPaths.map((p: NodePath) => ({ name: p.getAncestorContainedWithin('rightHandSide') ? p.node.name : scope.getIdentifierMappingKeyName(p.node), id: p.node.id })).filter(n => n.id <= lhs.id); + // check whether this is the first instance of a new index name. We only care if the previous index name is on the left hand side, because this will lead to a double variable declaration. + // We first check if the relevant name has been modified from the original variable name as otherwise we don't need to declare the new name. + let firstInstanceOfNewName = true; + if (indicator.isMapping) { + firstInstanceOfNewName = (names.length > 1); + } else { + firstInstanceOfNewName = (names[names.length - 1].name !== indicator.name); + } + let i =0; + // We check that the name has not been used previously, in this case we need to declare it. + names.forEach((elem) => { + if (i !== names.length - 1 && names[names.length - 1].name === elem.name){ + firstInstanceOfNewName = false; + } + i++; + }); // check whether this should be a VariableDeclaration const firstEdit = @@ -1064,7 +1215,6 @@ const visitor = { interactsWithSecret: true, }); if (indicator.isStruct) newNode.declarations[0].isStruct = true; - if (accessedBeforeModification || path.isInSubScope()) { // we need to initialise an accessed state // or declare it outside of this subscope e.g. if statement @@ -1096,8 +1246,11 @@ const visitor = { } } if (node.expression.expression?.name !== 'require') { + // We no longer check indicator?.interactsWithSecret because in most cases interactsWithSecret is set to true in addPublicInput anyway. + // The cases where this doesn't happen in AddPublicInput are where we don't want to add the statement to the newAST anyway. const newNode = buildNode(node.nodeType, { - interactsWithSecret: interactsWithSecret || indicator?.interactsWithSecret, + interactsWithSecret: interactsWithSecret, + //|| indicator?.interactsWithSecret, oldASTId: node.id, }); @@ -1116,7 +1269,7 @@ const visitor = { const { node, scope } = path; const { leftHandSide: lhs } = node.expression; const indicator = scope.getReferencedIndicator(lhs, true); - const name = indicator?.isMapping + let name = indicator?.isMapping ? indicator.name .replace('[', '_') .replace(']', '') @@ -1131,6 +1284,8 @@ const visitor = { path.node._newASTPointer.increments = increments; } else if (indicator?.isWhole && node._newASTPointer) { // we add a general number statement after each whole state edit + const tempNode = node._newASTPointer; + name = tempNode.initialValue && tempNode.initialValue.leftHandSide ? tempNode.initialValue.leftHandSide.name : name; if (node._newASTPointer.interactsWithSecret) path.getAncestorOfType('FunctionDefinition')?.node._newASTPointer.body.statements.push( buildNode('Assignment', { leftHandSide: buildNode('Identifier', { name }), @@ -1316,15 +1471,21 @@ const visitor = { enter(path: NodePath, state: any) { const { node, parent } = path; let { name } = node; - name = path.scope.getIdentifierMappingKeyName(node); + const binding = path.getReferencedBinding(node); + // for non-secret variable we always use the original variable name on the right hand side as this is always the correct value. + if ( (binding instanceof VariableBinding) && !binding.isSecret && + binding.stateVariable && path.getAncestorContainedWithin('rightHandSide') ){ + } else{ + name = path.scope.getIdentifierMappingKeyName(node); + } const newNode = buildNode(node.nodeType, { name, subType: node.typeDescriptions.typeString, }); - parentnewASTPointer(parent, path, newNode , parent._newASTPointer[path.containerName]); // if this is a public state variable, this fn will add a public input - addPublicInput(path, state); + addPublicInput(path, state,newNode); + parentnewASTPointer(parent, path, newNode , parent._newASTPointer[path.containerName]); }, }, @@ -1339,7 +1500,7 @@ const visitor = { subType: node.typeDescriptions.typeString, }); // if this is a public state variable, this fn will add a public input - addPublicInput(path, state); + addPublicInput(path, state, null); state.skipSubNodes = true; // the subnodes are baseExpression and indexExpression - we skip them parent._newASTPointer[path.containerName] = newNode; diff --git a/src/traverse/NodePath.ts b/src/traverse/NodePath.ts index 9f066d2dd..7b68824f2 100644 --- a/src/traverse/NodePath.ts +++ b/src/traverse/NodePath.ts @@ -1030,11 +1030,12 @@ export default class NodePath { // Currently, the only state variable 'modifications' we're aware of are: // - when a state variable is referenced on the LHS of an assignment; // - a unary operator + // We now do this by checking if at any point the identifier is within a right hand side. If so it cannot be on the LHS of an assignment. // prettier-ignore return ( !(this.queryAncestors(path => path.containerName === 'indexExpression')) && !this.getAncestorOfType('FunctionCall') && !this.getAncestorContainedWithin('initialValue') && - this.getLhsAncestor(true) && !(this.queryAncestors(path => path.containerName === 'condition') || this.queryAncestors(path => path.containerName === 'initializationExpression') || this.queryAncestors(path => path.containerName === 'loopExpression')) + !this.getRhsAncestor(true) && !(this.queryAncestors(path => path.containerName === 'condition') || this.queryAncestors(path => path.containerName === 'initializationExpression') || this.queryAncestors(path => path.containerName === 'loopExpression')) ); default: return false; diff --git a/src/traverse/Scope.ts b/src/traverse/Scope.ts index c317fb584..aadd99a93 100644 --- a/src/traverse/Scope.ts +++ b/src/traverse/Scope.ts @@ -677,12 +677,10 @@ export class Scope { const refPaths = this.getReferencedIndicator(identifierNode)?.referencingPaths; const thisIndex = refPaths?.findIndex(p => p.node === identifierNode); if (refPaths && thisIndex && refPaths[thisIndex]?.key === 'indexExpression') return this.getMappingKeyName(refPaths[thisIndex].getAncestorOfType('IndexAccess')); - let { name } = identifierNode; - // we find the next indexExpression after this identifier - for (let i = thisIndex || 0; i < (refPaths?.length || 0); i++) { - if (refPaths?.[i].key !== 'indexExpression' || !thisIndex) continue; + for (let i = Math.max(thisIndex, 0) || 0; i < (refPaths?.length || 0); i++) { + if (refPaths?.[i].key !== 'indexExpression' || (!thisIndex && thisIndex !== 0 )) continue; if (refPaths[thisIndex].isModification() && !forceNotModification) { name = this.getMappingKeyName(refPaths[i].getAncestorOfType('IndexAccess')); break; diff --git a/test/contracts/Arrays1.zol b/test/contracts/Arrays1.zol new file mode 100644 index 000000000..2bd6f19c2 --- /dev/null +++ b/test/contracts/Arrays1.zol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: CC0 + +pragma solidity ^0.8.0; + +contract Assign { + + secret uint256 private a; + + secret uint256[] public b; + + uint256 public index; + + uint256 public j; + + function add(secret uint256 value) public { + index++; + b[index] = index; + index++; + a = a + index; + index++; + b[index] = value; + } + + function remove(secret uint256 value) public { + a -= value; + } +} \ No newline at end of file diff --git a/test/contracts/Arrays2.zol b/test/contracts/Arrays2.zol new file mode 100644 index 000000000..d85cc8a0d --- /dev/null +++ b/test/contracts/Arrays2.zol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: CC0 + +pragma solidity ^0.8.0; + +contract Assign { + + secret uint256 private a; + + secret uint256[] public b; + + uint256 public index; + + uint256 public j; + + function add(secret uint256 value) public { + index++; + known a += value + index; + b[index] = 0; + index = index +1; + b[index] = value; + index++; + j++; + b[index] = value +index +j; + index += 1; + a += value + index; + b[index] = value + index; + index++; + j++; + } + + function remove(secret uint256 value) public { + a -= value; + } +} \ No newline at end of file diff --git a/test/contracts/If-Statement3.zol b/test/contracts/If-Statement3.zol new file mode 100644 index 000000000..a24476de3 --- /dev/null +++ b/test/contracts/If-Statement3.zol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: CC0 + +pragma solidity ^0.8.0; + +contract Test { + +secret uint256 private z; +uint256 public a; +uint256 public b; + +function add(uint256 y) public { + if (y > 5 || y<1) { + z = y + 3; + } + if(y < 3) { + z = y; + } + if (a > 5) { + b += y; + } + } + +} \ No newline at end of file diff --git a/test/contracts/If-Statement4.zol b/test/contracts/If-Statement4.zol new file mode 100644 index 000000000..f1b6a5eaf --- /dev/null +++ b/test/contracts/If-Statement4.zol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: CC0 + +pragma solidity ^0.8.0; + +contract Test { + +secret uint256 private z; +secret uint256 private k; + + +function add(uint256 y) public { + if (y > 5) { + z = y + 3; + k = z + 4; + } else { + z = y + 1; + k = z + 5; + } + } + +} \ No newline at end of file diff --git a/test/contracts/PublicInteractsSecret.zol b/test/contracts/PublicInteractsSecret.zol new file mode 100644 index 000000000..0112556c7 --- /dev/null +++ b/test/contracts/PublicInteractsSecret.zol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: CC0 + +pragma solidity ^0.8.0; + +contract Assign { + + secret uint256 private a; + secret uint256 private c; + + secret uint256[] public b; + + uint256 public index; + uint256 public j; + + function add(secret uint256 value) public { + index++; + known a += value + index; + index++; + index++; + known c += value + j+index; + index++; + j++; + } + +} diff --git a/test/contracts/PublicInteractsSecret1.zol b/test/contracts/PublicInteractsSecret1.zol new file mode 100644 index 000000000..101a4226e --- /dev/null +++ b/test/contracts/PublicInteractsSecret1.zol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: CC0 + +pragma solidity ^0.8.0; + +contract Assign { + + secret uint256 private a; + secret uint256 private b; + uint256 public index; + + + function add() public { + index=0; + a = index; + index = 10; + b = index; + } + +} \ No newline at end of file