From fb37242f8b687909cdb69cec27ebd7aa9c5fe385 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Fri, 7 Jul 2023 18:54:12 +0100 Subject: [PATCH 1/6] fix: small fixes and cleanliness --- .github/workflows/main.yml | 2 +- src/boilerplate/common/boilerplate-docker-compose.yml | 2 +- .../contract/solidity/raw/FunctionBoilerplateGenerator.ts | 3 ++- .../orchestration/javascript/raw/toOrchestration.ts | 8 +------- src/codeGenerators/circuit/zokrates/toCircuit.ts | 1 + .../orchestration/nodejs/toOrchestration.ts | 3 +-- src/transformers/visitors/ownership/errorChecksVisitor.ts | 1 + src/transformers/visitors/toOrchestrationVisitor.ts | 3 ++- 8 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7d7e5e9db..f1243c981 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,7 +56,7 @@ jobs: - name: replace zokrates image for actions test uses: jacobtomlinson/gha-find-replace@v2 with: - find: "ghcr.io/eyblockchain/zokrates-worker-starlight:v0.1" + find: "ghcr.io/eyblockchain/zokrates-worker-starlight:v0.2" replace: "ghcr.io/eyblockchain/zokrates-worker-m1:v0.1" include: "**docker-compose.zapp.yml" diff --git a/src/boilerplate/common/boilerplate-docker-compose.yml b/src/boilerplate/common/boilerplate-docker-compose.yml index a136b0b4c..79a00b2d7 100644 --- a/src/boilerplate/common/boilerplate-docker-compose.yml +++ b/src/boilerplate/common/boilerplate-docker-compose.yml @@ -53,7 +53,7 @@ services: - zapp_network zokrates: - image: ghcr.io/eyblockchain/zokrates-worker-starlight:v0.1 + image: ghcr.io/eyblockchain/zokrates-worker-starlight:v0.2 # platform: linux/arm64/v8 volumes: - ./circuits/:/app/circuits:delegated diff --git a/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts b/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts index ec3385736..6869f17c7 100644 --- a/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts +++ b/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts @@ -24,7 +24,8 @@ class FunctionBoilerplateGenerator { `verifier = IVerifier(verifierAddress); for (uint i = 0; i < vk.length; i++) { vks[i] = vk[i]; - }`, + } + newNullifierRoot = Initial_NullifierRoot;`, ]; }, diff --git a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts index c85f3cc20..d9762f0b4 100644 --- a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts +++ b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts @@ -56,13 +56,7 @@ const stateVariableIds = (node: any) => { const Orchestrationbp = new OrchestrationBP(); export const sendTransactionBoilerplate = (node: any) => { const { privateStates } = node; - const output: string[][] = []; - output[0] = []; - output[1] = []; - output[2] = []; - output[3] = []; - output[4] = []; - output[5] = []; + const output: string[][] = [[],[],[],[],[],[]]; // output[0] = nullifier root(s) // output[1] = arr of nullifiers // output[2] = commitments root(s) diff --git a/src/codeGenerators/circuit/zokrates/toCircuit.ts b/src/codeGenerators/circuit/zokrates/toCircuit.ts index d34859b78..f63801080 100644 --- a/src/codeGenerators/circuit/zokrates/toCircuit.ts +++ b/src/codeGenerators/circuit/zokrates/toCircuit.ts @@ -157,6 +157,7 @@ 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)}`; const initialValue = codeGenerator(node.initialValue); return `${declarations} = ${initialValue}`; } diff --git a/src/codeGenerators/orchestration/nodejs/toOrchestration.ts b/src/codeGenerators/orchestration/nodejs/toOrchestration.ts index 3a36b0cd4..ca6e46746 100644 --- a/src/codeGenerators/orchestration/nodejs/toOrchestration.ts +++ b/src/codeGenerators/orchestration/nodejs/toOrchestration.ts @@ -99,6 +99,7 @@ export default function codeGenerator(node: any, options: any = {}): any { if (!node.initialValue.operator) { 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])} = generalise(${codeGenerator(node.initialValue)});`; return `\nlet ${codeGenerator(node.declarations[0])} = ${codeGenerator(node.initialValue)};`; } return `\nlet ${codeGenerator(node.initialValue)};`; @@ -166,8 +167,6 @@ export default function codeGenerator(node: any, options: any = {}): any { case 'ForStatement': { if(node.interactsWithSecret) { - node.initializationExpression.interactsWithSecret = true; - node.loopExpression.interactsWithSecret = true; let initializationExpression = `${codeGenerator(node.initializationExpression).trim()}`; let condition = `${codeGenerator(node.condition, { condition: true })};`; let loopExpression = ` ${node.loopExpression.expression.rightHandSide.subExpression.name} ${node.loopExpression.expression.rightHandSide.operator}`; diff --git a/src/transformers/visitors/ownership/errorChecksVisitor.ts b/src/transformers/visitors/ownership/errorChecksVisitor.ts index d589b17d4..dde29a9a8 100644 --- a/src/transformers/visitors/ownership/errorChecksVisitor.ts +++ b/src/transformers/visitors/ownership/errorChecksVisitor.ts @@ -121,6 +121,7 @@ export default { const miniMappingVisitor = (thisNode: any) => { if (thisNode.nodeType !== 'IndexAccess') return; + if (path.isLocalStackVariable(thisNode) || path.isFunctionParameter(thisNode)) return; const key = path.getMappingKeyIdentifier(thisNode); if (!key.referencedDeclaration) return; if (idInLoopExpression.includes(key.referencedDeclaration)) diff --git a/src/transformers/visitors/toOrchestrationVisitor.ts b/src/transformers/visitors/toOrchestrationVisitor.ts index 451a5e93e..e3ab5ede0 100644 --- a/src/transformers/visitors/toOrchestrationVisitor.ts +++ b/src/transformers/visitors/toOrchestrationVisitor.ts @@ -1133,7 +1133,7 @@ const visitor = { } if (node._newASTPointer?.interactsWithSecret && path.getAncestorOfType('ForStatement')) { - (path.getAncestorOfType('ForStatement') || {}).node._newASTPointer.interactsWithSecret = true; + path.getAncestorOfType('ForStatement').node._newASTPointer.interactsWithSecret = true; if(indicator){ path.getAncestorOfType('Block')?.node._newASTPointer.push( buildNode('Assignment', { @@ -1392,6 +1392,7 @@ const visitor = { const newNode = node._newASTPointer; if (newNode.body.statements.some(n => n.interactsWithSecret)) { newNode.initializationExpression.interactsWithSecret = true; + newNode.initializationExpression.isInitializationExpression = true; newNode.loopExpression.interactsWithSecret = true; } } From 9598dfa9083554628ea56a338148691dba65b622 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Fri, 7 Jul 2023 18:57:15 +0100 Subject: [PATCH 2/6] feat: constant arrays as public inputs --- .../nodes/ContractBoilerplateGenerator.ts | 7 ++++ .../nodes/FunctionBoilerplateGenerator.ts | 6 ++-- .../raw/FunctionBoilerplateGenerator.ts | 14 +++++++- .../javascript/raw/toOrchestration.ts | 7 ++-- .../circuit/zokrates/toCircuit.ts | 1 + src/transformers/visitors/toCircuitVisitor.ts | 18 ++++++++++ .../visitors/toOrchestrationVisitor.ts | 36 ++++++++++++++----- src/traverse/NodePath.ts | 25 +++++++++++++ src/traverse/Scope.ts | 2 ++ 9 files changed, 102 insertions(+), 14 deletions(-) diff --git a/src/boilerplate/contract/solidity/nodes/ContractBoilerplateGenerator.ts b/src/boilerplate/contract/solidity/nodes/ContractBoilerplateGenerator.ts index d1eba0fd6..010840cfa 100644 --- a/src/boilerplate/contract/solidity/nodes/ContractBoilerplateGenerator.ts +++ b/src/boilerplate/contract/solidity/nodes/ContractBoilerplateGenerator.ts @@ -154,6 +154,13 @@ class ContractBoilerplateGenerator { if (circuitParamNode.typeName?.members) { newList.push(...circuitParamNode.typeName.members.map(m => `${circuitParamNode.name}.${m.name}`)); break; + } else if (circuitParamNode.typeName?.name.includes(`[`)) { + // TODO arrays of structs/structs of arrays/more robust soln + const arrayLen = circuitParamNode.typeName?.name.match(/(?<=\[)(\d+)(?=\])/); + for (let index = 0; index < +arrayLen[0]; index++) { + newList.push(`${circuitParamNode.name}[${index}]`); + } + break; } else newList.push(circuitParamNode.name); } } diff --git a/src/boilerplate/contract/solidity/nodes/FunctionBoilerplateGenerator.ts b/src/boilerplate/contract/solidity/nodes/FunctionBoilerplateGenerator.ts index 0d6dbae8d..f9df02e14 100644 --- a/src/boilerplate/contract/solidity/nodes/FunctionBoilerplateGenerator.ts +++ b/src/boilerplate/contract/solidity/nodes/FunctionBoilerplateGenerator.ts @@ -96,11 +96,11 @@ class FunctionBoilerplateGenerator { if (path.isStruct(node)) { const structDef = path.getStructDeclaration(node); const names = structDef.members.map((mem: any) => { - return { name: `${node.name}.${mem.name}`, type: mem.typeName.name }; + return { name: `${node.name}.${mem.name}`, type: mem.typeName.name || mem.typeName.baseType.name, isConstantArray: path.isConstantArray(mem) ? mem.typeName.length.value : false }; }); - return { structName: structDef.name, properties: names, isParam: path.isFunctionParameter(node) }; + return { structName: structDef.name, properties: names, isParam: path.isFunctionParameter(node), isConstantArray: path.isConstantArray(node) ? node.typeName.length.value : false }; } - return { name: node.name, type: node.typeName.name, isParam: path.isFunctionParameter(node) }; + return { name: node.name, type: node.typeName.name || node.typeName.baseType.name, isParam: path.isFunctionParameter(node), isConstantArray: path.isConstantArray(node) ? node.typeName.length.value : false }; } const params = path.getFunctionParameters(); diff --git a/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts b/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts index 6869f17c7..c8f67bef9 100644 --- a/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts +++ b/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts @@ -66,7 +66,8 @@ class FunctionBoilerplateGenerator { // prettier-ignore let parameter = [ - ...(customInputs ? customInputs.filter(input => !input.dummy && input.isParam).map(input => input.structName ? `(${input.properties.map(p => p.type)})` : input.type) : []), + ...(customInputs ? customInputs.filter(input => !input.dummy && input.isParam) + .map(input => input.structName ? `(${input.properties.map(p => p.type)})` : input.isConstantArray ? `${input.type}[${input.isConstantArray}]` : input.type) : []), // TODO arrays of structs/ structs of arrays ...(nullifierRootRequired ? [`uint256`] : []), ...(nullifierRootRequired ? [`uint256`] : []), ...(newNullifiers ? [`uint256[]`] : []), @@ -78,6 +79,17 @@ class FunctionBoilerplateGenerator { ].filter(para => para !== undefined); // Added for return parameter customInputs?.forEach((input, i) => { + if (input.isConstantArray) { + const expanded = []; + for (let index = 0; index < +input.isConstantArray; index++) { + expanded[index] = { + name: `${input.name}[${index}]`, + type: input.type, + isParam: input.isParam, + } + } + customInputs[i] = expanded; + } if (input.structName) customInputs[i] = input.properties; }); diff --git a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts index d9762f0b4..528eb4ee4 100644 --- a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts +++ b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts @@ -790,9 +790,12 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { if (node.publicInputs[0]) { node.publicInputs.forEach((input: any) => { if (input.properties) { - lines.push(`[${input.properties.map(p => `${input.name}.${p}.integer`).join(',')}]`) - } else + lines.push(`[${input.properties.map(p => `${input.name}${input.isConstantArray ? '.all' : ''}.${p}.integer`).join(',')}]`) + } else if (input.isConstantArray) { + lines.push(`${input.name}.all.integer`); + } else { lines.push(`${input}.integer`); + } }); lines[lines.length - 1] += `, `; } diff --git a/src/codeGenerators/circuit/zokrates/toCircuit.ts b/src/codeGenerators/circuit/zokrates/toCircuit.ts index f63801080..a31c55d8f 100644 --- a/src/codeGenerators/circuit/zokrates/toCircuit.ts +++ b/src/codeGenerators/circuit/zokrates/toCircuit.ts @@ -218,6 +218,7 @@ function codeGenerator(node: any) { return node.value; case 'IndexAccess': + if (node.isConstantArray) return `${codeGenerator(node.baseExpression)}[${codeGenerator(node.indexExpression).replace('.', 'dot')}]`; return `${codeGenerator(node.baseExpression)}_${codeGenerator(node.indexExpression).replace('.', 'dot')}`; case 'MemberAccess': diff --git a/src/transformers/visitors/toCircuitVisitor.ts b/src/transformers/visitors/toCircuitVisitor.ts index c24646bb2..225e182fe 100644 --- a/src/transformers/visitors/toCircuitVisitor.ts +++ b/src/transformers/visitors/toCircuitVisitor.ts @@ -793,6 +793,23 @@ let childOfSecret = path.getAncestorOfType('ForStatement')?.containsSecret; }, + ArrayTypeName: { + enter(path: NodePath, state: any) { + const { node, parent } = path; + const newNode = buildNode('ElementaryTypeName', { + name: `${node.baseType.name === 'bool' ? 'bool' : 'field'}[${node.length.value}]` + }); + + node._newASTPointer = newNode; + if (Array.isArray(parent._newASTPointer)) { + parent._newASTPointer.push(newNode); + } else { + parent._newASTPointer[path.containerName] = newNode; + } + state.skipSubNodes = true; + } + }, + ElementaryTypeNameExpression: { enter(path: NodePath, state: any) { const { node, parent } = path; @@ -1017,6 +1034,7 @@ let childOfSecret = path.getAncestorOfType('ForStatement')?.containsSecret; if (!state.skipPublicInputs) path.traversePathsFast(publicInputsVisitor, {}); const newNode = buildNode('IndexAccess'); + if (path.isConstantArray(node) && (path.isLocalStackVariable(node) || path.isFunctionParameter(node))) newNode.isConstantArray = true; node._newASTPointer = newNode; parent._newASTPointer[path.containerName] = newNode; }, diff --git a/src/transformers/visitors/toOrchestrationVisitor.ts b/src/transformers/visitors/toOrchestrationVisitor.ts index e3ab5ede0..70404140d 100644 --- a/src/transformers/visitors/toOrchestrationVisitor.ts +++ b/src/transformers/visitors/toOrchestrationVisitor.ts @@ -577,9 +577,9 @@ const visitor = { if (param.isPrivate || param.isSecret || param.interactsWithSecret) { if (param.typeName.isStruct) { param.typeName.properties.forEach((prop: any) => { - newNodes.generateProofNode.parameters.push(`${param.name}.${prop.name}`); + newNodes.generateProofNode.parameters.push(`${param.name}.${prop.name}${param.typeName.isConstantArray ? '.all' : ''}`); }); - } else newNodes.generateProofNode.parameters.push(param.name); + } else newNodes.generateProofNode.parameters.push(`${param.name}${param.typeName.isConstantArray ? '.all' : ''}`); } } @@ -595,11 +595,11 @@ const visitor = { // this adds other values we need in the tx for (const param of node.parameters.parameters) { if (!param.isSecret) { - if (path.isStructDeclaration(param)) { - const newParam = { - name: param.name, - properties: param._newASTPointer.typeName.properties.map(p => p.name) - }; + if (path.isStructDeclaration(param) || path.isConstantArray(param)) { + let newParam: any = {}; + newParam.name = param.name; + if (path.isStructDeclaration(param)) newParam.properties = param._newASTPointer.typeName.properties.map(p => p.name); + if (path.isConstantArray) newParam.isConstantArray = true; newNodes.sendTransactionNode.publicInputs.push(newParam); } else newNodes.sendTransactionNode.publicInputs.push(param.name); } @@ -1239,6 +1239,25 @@ const visitor = { }, }, + ArrayTypeName: { + enter(path: NodePath, state: any) { + const { node, parent } = path; + const newNode = buildNode('ElementaryTypeName', { + name: `[${node.length.value || node.length.name}]` + }); + 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); + } else { + parent._newASTPointer[path.containerName] = newNode; + } + state.skipSubNodes = true; + } + }, + ElementaryTypeName: { enter(path: NodePath) { const { node, parent } = path; @@ -1304,7 +1323,8 @@ const visitor = { IndexAccess: { enter(path: NodePath, state: any) { const { node, parent } = path; - const name = getIndexAccessName(node); + let name = getIndexAccessName(node); + if (path.isConstantArray(node) && (path.isLocalStackVariable(node) || path.isFunctionParameter(node))) name = `${node.baseExpression.name}[${path.scope.getMappingKeyName(node)}]`; const newNode = buildNode('Identifier', { name, subType: node.typeDescriptions.typeString, diff --git a/src/traverse/NodePath.ts b/src/traverse/NodePath.ts index ddef2aed5..9f066d2dd 100644 --- a/src/traverse/NodePath.ts +++ b/src/traverse/NodePath.ts @@ -975,6 +975,31 @@ export default class NodePath { return memberAccNode && memberAccNode.node.baseExpression?.typeDescriptions?.typeIdentifier.includes('array'); } + /** + * Checks whether a node is of an array type. + * @param {node} node (optional - defaults to this.node) + * @returns {Boolean} + */ + isConstantArray(node: any = this.node): boolean { + if (!this.isArray(node)) return false; + let arrLen; + switch (node.nodeType) { + case 'IndexAccess': + arrLen = node.baseExpression.typeDescriptions.typeString.match(/(?<=\[)(\d+)(?=\])/); + break; + case 'Identifier': + default: + arrLen = node.typeDescriptions.typeString.match(/(?<=\[)(\d+)(?=\])/); + break; + } + if (!arrLen) return false; + for (const match of arrLen) { + // tries to convert to a number + if (+match) return true; + } + return false; + } + /** * Checks whether a node is a VariableDeclaration of a Mapping. * @param {node} node (optional - defaults to this.node) diff --git a/src/traverse/Scope.ts b/src/traverse/Scope.ts index 101e8247d..c317fb584 100644 --- a/src/traverse/Scope.ts +++ b/src/traverse/Scope.ts @@ -427,6 +427,8 @@ export class Scope { : 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' From 0847d238134fdc0ec63ffe2c73016147cafbcc8b Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Fri, 7 Jul 2023 19:07:50 +0100 Subject: [PATCH 3/6] fix: fix for if statements inside for loops --- src/transformers/visitors/toOrchestrationVisitor.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/transformers/visitors/toOrchestrationVisitor.ts b/src/transformers/visitors/toOrchestrationVisitor.ts index 70404140d..22a1452ca 100644 --- a/src/transformers/visitors/toOrchestrationVisitor.ts +++ b/src/transformers/visitors/toOrchestrationVisitor.ts @@ -1376,9 +1376,8 @@ const visitor = { state.skipSubNodes = true; return; } - const newNode = buildNode(node.nodeType , { - interactsWithSecret: node.containsSecret - }); + const newNode = buildNode(node.nodeType); + newNode.interactsWithSecret = true; node._newASTPointer = newNode; parent._newASTPointer.push(newNode); }, From ebd447690ab5ec436e40b4907228c5d6f1903f0e Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Mon, 10 Jul 2023 16:19:45 +0100 Subject: [PATCH 4/6] fix: add inCircuit for pub input arrs --- .../contract/solidity/raw/FunctionBoilerplateGenerator.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts b/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts index ce6f9d382..32e0b368d 100644 --- a/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts +++ b/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts @@ -86,6 +86,7 @@ class FunctionBoilerplateGenerator { name: `${input.name}[${index}]`, type: input.type, isParam: input.isParam, + inCircuit: input.inCircuit, } } customInputs[i] = expanded; From 86b9715007d9710ac6e271951a4341bf55f17131 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Thu, 13 Jul 2023 12:51:32 +0100 Subject: [PATCH 5/6] fix: const arrays being labelled as mapping + test --- .../visitors/ownership/errorChecksVisitor.ts | 7 ++++++ src/traverse/Binding.ts | 10 ++++----- src/traverse/Indicator.ts | 12 +++++----- test/contracts/Arrays-input.zol | 22 +++++++++++++++++++ 4 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 test/contracts/Arrays-input.zol diff --git a/src/transformers/visitors/ownership/errorChecksVisitor.ts b/src/transformers/visitors/ownership/errorChecksVisitor.ts index dde29a9a8..3c64648e9 100644 --- a/src/transformers/visitors/ownership/errorChecksVisitor.ts +++ b/src/transformers/visitors/ownership/errorChecksVisitor.ts @@ -22,6 +22,12 @@ export default { VariableDeclaration: { enter(path: NodePath) { const { node, scope } = path; + if (path.isConstantArray() && node.stateVariable && node.isSecret) { + throw new TODOError( + `We can't currently handle secret arrays of constant length. If you want one editable state, try a struct, otherwise use a mapping or dynamic array.`, + node + ); + } if (node.value && scope.scopeType === 'ContractDefinition') { if (!path.getSiblingNodes().some((sib: any) => sib.kind === 'constructor')) throw new SyntaxError(`Your variable ${node.name} is being initialised without any constructor - we can't create a commitment for this value without a circuit present. Consider moving this initial value to the constructor.`); @@ -185,6 +191,7 @@ export default { if(structWarnings.length>0) { logger.warn( ' The following struct properties may cause unconstrained variable errors in the circuit ' , Array.from(new Set(structWarnings))); } + structWarnings = []; // if no errors, we then check everything is nullifiable for (const [, binding] of Object.entries(scope.bindings)) { // TODO find contract level binding and call once diff --git a/src/traverse/Binding.ts b/src/traverse/Binding.ts index 9777fbbb8..a4a38d0ac 100644 --- a/src/traverse/Binding.ts +++ b/src/traverse/Binding.ts @@ -256,7 +256,7 @@ export class VariableBinding extends Binding { // A binding will be updated if (some time after its creation) we encounter an AST node which refers to this binding's variable. // E.g. if we encounter an Identifier node. update(path: NodePath) { - if (this.isMapping) { + if (this.isMapping && path.getAncestorOfType('IndexAccess')) { this.addMappingKey(path).updateProperties(path); } else if (this.isStruct && path.getAncestorOfType('MemberAccess')) { this.addStructProperty(path).updateProperties(path); @@ -365,7 +365,7 @@ export class VariableBinding extends Binding { this.isWholeReason ??= []; this.isWholeReason.push(reason); - if (this.isMapping) { + if (this.isMapping && path.getAncestorOfType('IndexAccess')) { this.addMappingKey(path).isAccessed = true; this.addMappingKey(path).accessedPaths ??= []; this.addMappingKey(path).accessedPaths.push(path); @@ -415,7 +415,7 @@ export class VariableBinding extends Binding { this.isNullified = true; ++this.nullificationCount; this.nullifyingPaths.push(path); - if (this.isMapping) this.addMappingKey(path).addNullifyingPath(path); + if (this.isMapping && path.getAncestorOfType('IndexAccess')) this.addMappingKey(path).addNullifyingPath(path); if (this.isStruct && path.getAncestorOfType('MemberAccess')) this.addStructProperty(path).addNullifyingPath(path); } @@ -533,12 +533,12 @@ export class VariableBinding extends Binding { if (functionDefScope.callerRestriction === 'exclude') { this.updateBlacklist(functionDefScope.callerRestrictionNode); } - if (this.isMapping && this.addMappingKey(path).isMsgSender) { + if (this.isMapping && path.getAncestorOfType('IndexAccess') && this.addMappingKey(path).isMsgSender ) { // if its unassigned, we assign true // if its true, it remains true msgSenderEverywhereMappingKey ??= true; } else if ( - this.isMapping && + this.isMapping && path.getAncestorOfType('IndexAccess') && (path.isMsgSender(path.getCorrespondingRhsNode()) || path.isMsgValue(path.getCorrespondingRhsNode())) ) { msgSenderEverywhereMappingValue ??= true; diff --git a/src/traverse/Indicator.ts b/src/traverse/Indicator.ts index 5c815854f..2e4ade660 100644 --- a/src/traverse/Indicator.ts +++ b/src/traverse/Indicator.ts @@ -291,7 +291,7 @@ export class LocalVariableIndicator extends FunctionDefinitionIndicator { } if (this.isStruct && path.getAncestorOfType('MemberAccess')) { this.addStructProperty(path).updateProperties(path); - } else if (this.isMapping) { + } else if (this.isMapping && path.getAncestorOfType('IndexAccess')) { this.addMappingKey(path).updateProperties(path); } } @@ -476,7 +476,7 @@ export class StateVariableIndicator extends FunctionDefinitionIndicator { // A StateVariableIndicator will be updated if (some time after its creation) we encounter an AST node which refers to this state variable. // E.g. if we encounter an Identifier node. update(path: NodePath) { - if (this.isMapping) { + if (this.isMapping && path.getAncestorOfType('IndexAccess')) { this.addMappingKey(path).updateProperties(path); } else if (this.isStruct && path.getAncestorOfType('MemberAccess')) { this.addStructProperty(path).updateProperties(path); @@ -570,7 +570,7 @@ export class StateVariableIndicator extends FunctionDefinitionIndicator { this.isWholeReason.push(reason); this.accessedPaths ??= []; this.accessedPaths.push(path); - if (this.isMapping) { + if (this.isMapping && path.getAncestorOfType('IndexAccess')) { this.addMappingKey(path).isAccessed = true; this.addMappingKey(path).accessedPaths ??= []; this.addMappingKey(path).accessedPaths.push(path); @@ -668,11 +668,11 @@ export class StateVariableIndicator extends FunctionDefinitionIndicator { if (path.getAncestorOfType('FunctionDefinition')?.node.kind === 'constructor') { this.binding.initialisedInConstructor = true; this.initialisationRequired = true; // we need the dummy nullifier in the constructor - if (this.isMapping) this.addMappingKey(path).initialisationRequired = true; + if (this.isMapping && path.getAncestorOfType('IndexAccess')) this.addMappingKey(path).initialisationRequired = true; if (this.isStruct) this.addStructProperty(path).initialisationRequired = true; } else if(!this.binding.initialisedInConstructor) { this.initialisationRequired = true; - if (this.isMapping) this.addMappingKey(path).initialisationRequired = true; + if (this.isMapping && path.getAncestorOfType('IndexAccess')) this.addMappingKey(path).initialisationRequired = true; if (this.isStruct && path.getAncestorOfType('MemberAccess')) this.addStructProperty(path).initialisationRequired = true; } @@ -691,7 +691,7 @@ export class StateVariableIndicator extends FunctionDefinitionIndicator { ++this.nullificationCount; this.nullifyingPaths.push(path); this.binding.addNullifyingPath(path); - if (this.isMapping) this.addMappingKey(path).addNullifyingPath(path); + if (this.isMapping && path.getAncestorOfType('IndexAccess')) this.addMappingKey(path).addNullifyingPath(path); if (this.isStruct && path.getAncestorOfType('MemberAccess')) this.addStructProperty(path).addNullifyingPath(path); } diff --git a/test/contracts/Arrays-input.zol b/test/contracts/Arrays-input.zol new file mode 100644 index 000000000..2385c9aa5 --- /dev/null +++ b/test/contracts/Arrays-input.zol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: CC0 + +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]; + } + } + + function remove(secret uint256 value) public { + a -= value; + } +} From c8602719eebd9a9b96da3d59284c47220ec39773 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Mon, 17 Jul 2023 10:59:37 +0100 Subject: [PATCH 6/6] fix: custom inputs array flattened + globals inc --- .../contract/solidity/raw/FunctionBoilerplateGenerator.ts | 2 +- src/transformers/visitors/toContractVisitor.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts b/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts index 32e0b368d..fe357fde8 100644 --- a/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts +++ b/src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts @@ -97,7 +97,7 @@ class FunctionBoilerplateGenerator { let msgSigCheck = ([...(isConstructor ? [] : [`bytes4 sig = bytes4(keccak256("${functionName}(${parameter})")) ; \n \t \t \t if (sig == msg.sig)`])]); - customInputs = customInputs?.filter(p => p.inCircuit); + customInputs = customInputs?.flat(Infinity).filter(p => p.inCircuit); return [ ` diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index ba8bf8b78..9e5c6618f 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -61,7 +61,7 @@ const findCustomInputsVisitor = (thisPath: NodePath, thisState: any) => { thisState.customInputs ??= []; const type = binding.node.typeName.nodeType === 'Mapping' ? binding.node.typeName.valueType.name : binding.node.typeName.name; if (!thisState.customInputs.some((input: any) => input.name === indicator?.name)) - thisState.customInputs.push({name: indicator?.name, typeName: {name: type} }); + thisState.customInputs.push({name: indicator?.name, typeName: {name: type}, isConstantArray: thisPath.isConstantArray() ? thisPath.node.typeName.length.value : false, inCircuit: true }); } };