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

Mappings of Arrays of Structs #194

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ node_modules/
proving-files/
zapps/
test-zapps/
truezapps/
temp-zapps/
76 changes: 51 additions & 25 deletions src/boilerplate/circuit/zokrates/nodes/BoilerplateGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,35 +164,47 @@ class BoilerplateGenerator {
});
}

prepareMappingBoilerplate(mappingKeyName: string, mappingKeyIndicator: MappingKey) {
mappingKeyIndicator.isMapping = true;
this.assignIndicators(mappingKeyIndicator);
if(mappingKeyName == 'msg')
mappingKeyName = mappingKeyName+mappingKeyIndicator.keyPath.parent.memberName.replace('sender','Sender').replace('value','Value');
this.mappingKeyName = mappingKeyName.replace('[', '_').replace(']', '');
if (this.mappingKeyName.split('.').length > 2) this.mappingKeyName = this.mappingKeyName.replace('.', 'dot');

if (mappingKeyIndicator.keyPath.isStruct() && !(mappingKeyIndicator.keyPath.node.nodeType === 'Identifier' && !mappingKeyIndicator.keyPath.node.typeDescriptions.typeString.includes('struct ')))
this.mappingKeyTypeName = mappingKeyIndicator.keyPath.getStructDeclaration().name;

if (!mappingKeyIndicator.keyPath.isMsg() &&
(mappingKeyIndicator.keyPath.node.nodeType === 'Literal'|| mappingKeyIndicator.keyPath.isLocalStackVariable() || !mappingKeyIndicator.keyPath.isSecret || mappingKeyIndicator.keyPath.node.accessedSecretState))
this.mappingKeyTypeName = 'local';

this.mappingName = this.indicators.name;
this.name = //this.name.replaceAll('.', 'dot').replaceAll('[', '_').replaceAll(']', '');
`${this.mappingName}_${mappingKeyName}`.replaceAll('.', 'dot').replaceAll('[', '_').replaceAll(']', '');

if (mappingKeyIndicator.isStruct && mappingKeyIndicator.isParent) {
this.typeName = this.indicators.referencingPaths[0]?.getStructDeclaration()?.name;
this.structProperties = this.indicators.referencingPaths[0]?.getStructDeclaration()?.members.map(m => m.name)
} else if (mappingKeyIndicator.referencingPaths[0]?.node.typeDescriptions.typeString.includes('struct ')) {
// somewhat janky way to include referenced structs not separated by property
this.typeName = mappingKeyIndicator.referencingPaths[0]?.getStructDeclaration()?.name;
}
this.generateBoilerplate();
}

initialise(indicators: StateVariableIndicator){
this.indicators = indicators;
if (indicators.isMapping && indicators.mappingKeys) {
for (let [mappingKeyName, mappingKeyIndicator] of Object.entries(indicators.mappingKeys)) {
mappingKeyIndicator.isMapping = true;
this.assignIndicators(mappingKeyIndicator);
if(mappingKeyName == 'msg')
mappingKeyName = mappingKeyName+mappingKeyIndicator.keyPath.parent.memberName.replace('sender','Sender').replace('value','Value');
this.mappingKeyName = mappingKeyName.replace('[', '_').replace(']', '');
if (this.mappingKeyName.split('.').length > 2) this.mappingKeyName = this.mappingKeyName.replace('.', 'dot');

if (mappingKeyIndicator.keyPath.isStruct() && !(mappingKeyIndicator.keyPath.node.nodeType === 'Identifier' && !mappingKeyIndicator.keyPath.node.typeDescriptions.typeString.includes('struct ')))
this.mappingKeyTypeName = mappingKeyIndicator.keyPath.getStructDeclaration().name;

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(']', '');

if (mappingKeyIndicator.isStruct && mappingKeyIndicator.isParent) {
this.typeName = indicators.referencingPaths[0]?.getStructDeclaration()?.name;
this.structProperties = indicators.referencingPaths[0]?.getStructDeclaration()?.members.map(m => m.name)
} else if (mappingKeyIndicator.referencingPaths[0]?.node.typeDescriptions.typeString.includes('struct ')) {
// somewhat janky way to include referenced structs not separated by property
this.typeName = mappingKeyIndicator.referencingPaths[0]?.getStructDeclaration()?.name;
if (mappingKeyIndicator.mappingKeys) {
for (let [outerMappingKeyName, outerMappingKeyIndicator] of Object.entries(mappingKeyIndicator.mappingKeys)) {
this.prepareMappingBoilerplate(mappingKeyName + '_' + outerMappingKeyName, outerMappingKeyIndicator);
}
} else {
this.prepareMappingBoilerplate(mappingKeyName, mappingKeyIndicator);
}
this.generateBoilerplate();
}
} else {
if (indicators instanceof StateVariableIndicator && indicators.structProperties) {
Expand All @@ -205,7 +217,14 @@ class BoilerplateGenerator {
}

refresh(mappingKeyName: string) {
const mappingKeyIndicator = this.indicators.mappingKeys[mappingKeyName];
let mappingKeyIndicator = this.indicators.mappingKeys[mappingKeyName];
if (mappingKeyName.includes(`/`)) {
// nested mapping
mappingKeyName.split(`/`).forEach(name => {
if (name) mappingKeyIndicator = mappingKeyIndicator ? mappingKeyIndicator.mappingKeys[name] : this.indicators.mappingKeys[name];
});
mappingKeyName = mappingKeyName.replace(`/`, `_`);
}
this.assignIndicators(mappingKeyIndicator);
this.mappingKeyName = mappingKeyName.replace('[', '_').replace(']', '');
if (this.mappingKeyName.split('.').length > 2) this.mappingKeyName.replace('.', 'dot');
Expand Down Expand Up @@ -235,8 +254,11 @@ class BoilerplateGenerator {
}

_addBP = (bpType: string, extraParams?: any) => {
const lastModifiedNodeName = this.thisIndicator.isModified ? this.thisIndicator.modifyingPaths[0].scope.getIdentifierMappingKeyName(this.thisIndicator.modifyingPaths[this.thisIndicator.modifyingPaths.length - 1].node) : this.thisIndicator.node.name;
if (this.isPartitioned) {
this.newCommitmentValue = collectIncrements(this).incrementsString;
} else if (lastModifiedNodeName !== this.thisIndicator.node.name) {
this.newCommitmentValue = lastModifiedNodeName;
}
this.bpSections.forEach(bpSection => {
this[bpSection] = this[bpSection]
Expand Down Expand Up @@ -355,7 +377,11 @@ class BoilerplateGenerator {

mapping = (bpSection) => ({
mappingName: this.mappingName,
mappingKeyName: bpSection === 'postStatements' ? this.mappingKeyName : bpSection === 'parameters' ? this.mappingKeyName.split('.')[0] : this.mappingKeyName.replace('.', 'dot'),
mappingKeyName: this.thisIndicator?.keyPath?.isNestedMapping()
? [this.thisIndicator.container.referencedKeyName, this.thisIndicator.referencedKeyName]
: bpSection === 'parameters'
? this.mappingKeyName.split('.')[0]
: bpSection === 'postStatements' ? this.mappingKeyName : this.mappingKeyName.replace('.', 'dot'),
});

/** Partitioned states need boilerplate for an incrementation/decrementation, because it's so weird and different from `a = a - b`. Whole states inherit directly from the AST, so don't need boilerplate here. */
Expand Down
7 changes: 4 additions & 3 deletions src/boilerplate/circuit/zokrates/raw/BoilerplateGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ class BoilerplateGenerator {

postStatements({ name: x, isWhole, isNullified, newCommitmentValue, structProperties, typeName }): string[] {
// if (!isWhole && !newCommitmentValue) throw new Error('PATH');
const y = isWhole ? x : newCommitmentValue;
const y = isWhole && !newCommitmentValue ? x : newCommitmentValue;
const lines: string[] = [];
if (!isWhole && isNullified) {
// decrement
Expand Down Expand Up @@ -429,12 +429,13 @@ class BoilerplateGenerator {
mapping = {
importStatements(): string[] {
return [
`from "./common/hashes/mimc/altbn254/mimc2.zok" import main as mimc2`,
`from "./common/hashes/poseidon/poseidon.zok" import main as poseidon`,
];
},

parameters({ mappingKeyName: k, mappingKeyTypeName: t }): string[] {
if (t === 'local') return [];
if (Array.isArray(k)) return k.map(name => `private ${t ? t : 'field'} ${name}`);
return [
`private ${t ? t : 'field'} ${k}`, // must be a field, in case we need to do arithmetic on it.
];
Expand All @@ -452,7 +453,7 @@ class BoilerplateGenerator {
// const x = `${m}_${k}`;
return [
`
field ${x}_stateVarId_field = mimc2([${m}_mappingId, ${k}])`,
field ${x}_stateVarId_field = poseidon([${m}_mappingId, ${k}])`,
];
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,11 @@ export function buildPrivateStateNode(nodeType: string, fields: any = {}): any {
}
case 'CalculateCommitment': {
const { id, increment, privateStateName, indicator = {} } = fields;
const lastModifiedNodeName = indicator.modifyingPaths[0].scope.getIdentifierMappingKeyName(indicator.modifyingPaths[indicator.modifyingPaths.length - 1].node);
const newCommitmentValue = indicator.isWhole && lastModifiedNodeName !== indicator.node.name ? lastModifiedNodeName : null;
return {
privateStateName,
newCommitmentValue,
stateVarId: id,
increment,
isWhole: indicator.isWhole,
Expand Down Expand Up @@ -128,6 +131,7 @@ export function buildPrivateStateNode(nodeType: string, fields: any = {}): any {
indicator = {},
} = fields;
const structProperties = !indicator.isStruct ? null : indicator.isAccessed ? indicator.referencingPaths[0]?.getStructDeclaration()?.members.map(m => m.name) : Object.keys(indicator.structProperties);
const originalMappingKeyName = id[2] ? [ indicator.container.referencedKeyName, indicator.referencedKeyName] : id[1] ? [ indicator.referencedKeyName ] : [];
return {
privateStateName,
stateVarId: id,
Expand All @@ -142,6 +146,9 @@ export function buildPrivateStateNode(nodeType: string, fields: any = {}): any {
isPartitioned: indicator.isPartitioned,
isOwned: indicator.isOwned,
mappingOwnershipType: indicator.mappingOwnershipType,
originalMappingKeyName: indicator.isMapping
? originalMappingKeyName
: null,
initialisationRequired: indicator.initialisationRequired,
encryptionRequired: indicator.encryptionRequired,
owner: indicator.isOwned
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ class BoilerplateGenerator {

calculateCommitment = {

postStatements({ stateName, stateType, structProperties }): string[] {
postStatements({ stateName, newCommitmentValue, stateType, structProperties }): string[] {
// once per state
switch (stateType) {
case 'increment':
Expand All @@ -383,9 +383,10 @@ class BoilerplateGenerator {
\nlet ${stateName}_2_newCommitment = poseidonHash([BigInt(${stateName}_stateVarId), ${structProperties ? `...${stateName}_change.hex(32).map(v => BigInt(v))` : `BigInt(${stateName}_change.hex(32))`}, BigInt(publicKey.hex(32)), BigInt(${stateName}_2_newSalt.hex(32))],);
\n${stateName}_2_newCommitment = generalise(${stateName}_2_newCommitment.hex(32)); // truncate`];
case 'whole':
const value = structProperties ? structProperties.map(p => `BigInt(${stateName}.${p}.hex(32))`) :` BigInt(${stateName}.hex(32))`;
const name = newCommitmentValue || stateName;
const value = structProperties ? structProperties.map(p => `BigInt(${name}.${p}.hex(32))`) :` BigInt(${name}.hex(32))`;
return [`
\n ${structProperties ? structProperties.map(p => `\n${stateName}.${p} = ${stateName}.${p} ? ${stateName}.${p} : ${stateName}_prev.${p};`).join('') : ''}
\n ${structProperties ? structProperties.map(p => `\n${name}.${p} = ${name}.${p} ? ${name}.${p} : ${stateName}_prev.${p};`).join('') : ''}
\nconst ${stateName}_newSalt = generalise(utils.randomHex(31));
\nlet ${stateName}_newCommitment = poseidonHash([BigInt(${stateName}_stateVarId), ${value}, BigInt(${stateName}_newOwnerPublicKey.hex(32)), BigInt(${stateName}_newSalt.hex(32))],);
\n${stateName}_newCommitment = generalise(${stateName}_newCommitment.hex(32)); // truncate`];
Expand Down
Loading