Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Constant size arrays as public inputs + small fixes #207

Merged
merged 7 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
2 changes: 1 addition & 1 deletion src/boilerplate/common/boilerplate-docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,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, inCircuit: node.interactsWithSecret };
});
return { structName: structDef.name, properties: names, isParam: path.isFunctionParameter(node), inCircuit: node.interactsWithSecret };
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, isParam: path.isFunctionParameter(node), inCircuit: node.interactsWithSecret };
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, inCircuit: node.interactsWithSecret };
}

const params = path.getFunctionParameters();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ class FunctionBoilerplateGenerator {
`verifier = IVerifier(verifierAddress);
for (uint i = 0; i < vk.length; i++) {
vks[i] = vk[i];
}`,
}
newNullifierRoot = Initial_NullifierRoot;`,
];
},

Expand Down Expand Up @@ -64,7 +65,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
...(newNullifiers ? [`uint256`] : []),
...(newNullifiers ? [`uint256`] : []),
...(newNullifiers ? [`uint256[]`] : []),
Expand All @@ -77,6 +79,18 @@ class FunctionBoilerplateGenerator {


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,
inCircuit: input.inCircuit,
}
}
customInputs[i] = expanded;
}
if (input.structName) customInputs[i] = input.properties;
});

Expand Down
15 changes: 6 additions & 9 deletions src/boilerplate/orchestration/javascript/raw/toOrchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -796,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] += `, `;
}
Expand Down
2 changes: 2 additions & 0 deletions src/codeGenerators/circuit/zokrates/toCircuit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
}
Expand Down Expand Up @@ -217,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':
Expand Down
3 changes: 1 addition & 2 deletions src/codeGenerators/orchestration/nodejs/toOrchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)};`;
Expand Down Expand Up @@ -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}`;
Expand Down
8 changes: 8 additions & 0 deletions src/transformers/visitors/ownership/errorChecksVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.`);
Expand Down Expand Up @@ -121,6 +127,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))
Expand Down Expand Up @@ -184,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
Expand Down
18 changes: 18 additions & 0 deletions src/transformers/visitors/toCircuitVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
},
Expand Down
44 changes: 32 additions & 12 deletions src/transformers/visitors/toOrchestrationVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' : ''}`);
}

}
Expand 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);
}
Expand Down Expand Up @@ -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', {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -1356,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);
},
Expand Down Expand Up @@ -1392,6 +1411,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;
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/traverse/Binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
Expand Down
12 changes: 6 additions & 6 deletions src/traverse/Indicator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}

Expand All @@ -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);
}

Expand Down
Loading