diff --git a/src/boilerplate/circuit/zokrates/nodes/BoilerplateGenerator.ts b/src/boilerplate/circuit/zokrates/nodes/BoilerplateGenerator.ts index a34363a96..a08967495 100644 --- a/src/boilerplate/circuit/zokrates/nodes/BoilerplateGenerator.ts +++ b/src/boilerplate/circuit/zokrates/nodes/BoilerplateGenerator.ts @@ -11,7 +11,7 @@ import MappingKey from '../../../../traverse/MappingKey.js'; const collectIncrements = (bpg: BoilerplateGenerator) => { const stateVarIndicator = bpg.thisIndicator || bpg.indicators; const incrementsArray: any[] = []; - let incrementsString = ''; + let incrementsString: string[] = []; if (stateVarIndicator.isStruct && ( stateVarIndicator instanceof StateVariableIndicator || @@ -27,7 +27,7 @@ const collectIncrements = (bpg: BoilerplateGenerator) => { bpg.thisIndicator = stateVarIndicator; } else { structIncs.incrementsArray[sp] = []; - structIncs.incrementsString[sp] = '0'; + structIncs.incrementsString[sp] = ['0']; } } return structIncs; @@ -35,6 +35,7 @@ const collectIncrements = (bpg: BoilerplateGenerator) => { // TODO sometimes decrements are added to .increments // current fix - prevent duplicates + let counter =-1; for (const inc of stateVarIndicator.increments) { if (inc.nodeType === 'IndexAccess') { const mappingKeyName = NodePath.getPath(inc).scope.getMappingKeyName(inc); @@ -46,19 +47,23 @@ const collectIncrements = (bpg: BoilerplateGenerator) => { if (inc.nodeType === 'MemberAccess') inc.name ??= `${inc.expression.name}.${inc.memberName}`; if (!inc.name) inc.name = inc.value; let modName = inc.modName ? inc.modName : inc.name; - if (incrementsArray.some(existingInc => inc.name === existingInc.name)) + if (incrementsArray.some(existingInc => inc.name === existingInc.name && inc.id === existingInc.id)) continue; incrementsArray.push({ name: inc.name, precedingOperator: inc.precedingOperator, }); - - if (inc === stateVarIndicator.increments[0]) { - incrementsString += `${modName}`; + + if (counter !== inc.counter) { + incrementsString[inc.counter] = `${modName}`; } else { - incrementsString += ` ${inc.precedingOperator} ${modName}`; + incrementsString[inc.counter] += ` ${inc.precedingOperator} ${modName}`; } + counter = inc.counter; } + let incCounter; + (counter === -1) ? incCounter = 0 : incCounter = counter +1; + counter = -1; for (const dec of stateVarIndicator.decrements) { if (dec.nodeType === 'IndexAccess') { const mappingKeyName = NodePath.getPath(dec).scope.getMappingKeyName(dec); @@ -78,13 +83,14 @@ const collectIncrements = (bpg: BoilerplateGenerator) => { precedingOperator: dec.precedingOperator, }); - if (!stateVarIndicator.decrements[1] && !stateVarIndicator.increments[0]) { - incrementsString += `${modName}`; + if (counter != dec.counter) { + incrementsString[dec.counter + incCounter] = `${modName}`; } else { // if we have decrements, this str represents the value we must take away // => it's a positive value with +'s - incrementsString += ` + ${modName}`; + incrementsString[dec.counter + incCounter] += `${dec.precedingOperator} ${modName}`; } + counter = dec.counter; } return { incrementsArray, incrementsString }; @@ -222,8 +228,10 @@ class BoilerplateGenerator { this.assignIndicators(mappingKeyIndicator); this.mappingKeyName = mappingKeyName.replace('[', '_').replace(']', ''); if (this.mappingKeyName.split('.').length > 2) this.mappingKeyName.replace('.', 'dot'); + if(this.mappingKeyName == 'msg') + this.mappingKeyName = this.mappingKeyName+mappingKeyIndicator.keyPath.parent.memberName.replace('sender','Sender').replace('value','Value'); this.mappingName = this.indicators.name; - this.name = `${this.mappingName}_${mappingKeyName}`.replaceAll('.', 'dot').replace('[', '_').replace(']', ''); + this.name = `${this.mappingName}_${this.mappingKeyName}`.replaceAll('.', 'dot').replace('[', '_').replace(']', ''); } generateBoilerplateStatement(bpType: string, extraParams?: any) { @@ -232,7 +240,6 @@ class BoilerplateGenerator { const { mappingKeyName } = extraParams; this.refresh(mappingKeyName); } - return { nodeType: 'BoilerplateStatement', bpSection: 'statements', @@ -376,18 +383,24 @@ class BoilerplateGenerator { }); /** 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. */ - incrementation = () => { + incrementation = (extraParams) => { //const startIndex = this.getIndex({ addendId }); return { // startIndex, + newCommitmentValue: this.newCommitmentValue, + structProperties: (this.isStruct && { structProperties: this.structProperties}), + memberName: extraParams.memberName, addend: {}, }; }; - decrementation = () => { + decrementation = (extraParams) => { //const startIndex = this.getIndex({ subtrahendId }); return { // startIndex, + newCommitmentValue: this.newCommitmentValue, + structProperties: (this.isStruct && { structProperties: this.structProperties}), + memberName: extraParams.memberName, subtrahend: {}, }; }; diff --git a/src/boilerplate/circuit/zokrates/raw/BoilerplateGenerator.ts b/src/boilerplate/circuit/zokrates/raw/BoilerplateGenerator.ts index 30d872680..7253a0f96 100644 --- a/src/boilerplate/circuit/zokrates/raw/BoilerplateGenerator.ts +++ b/src/boilerplate/circuit/zokrates/raw/BoilerplateGenerator.ts @@ -310,19 +310,40 @@ class BoilerplateGenerator { ]; }, - preStatements({ name: x, id, isMapping }): string[] { - if (isMapping) return []; - return [ - ` + preStatements({ name: x, id, isMapping, isWhole, isNullified, typeName, structProperties }): string[] { + if (isMapping && isWhole) return []; + let decLine = ''; + let stateVarIdLine = ``; + if (!isMapping) stateVarIdLine = ` + // We need to hard-code each stateVarId into the circuit: + field ${x}_stateVarId_field = ${id}`; + if (!isWhole && isNullified) { + const i = parseInt(x.slice(-1), 10); + const x0 = x.slice(0, -1) + `${i-2}`; + const x1 = x.slice(0, -1) + `${i-1}`; + const x_name = x.slice(0, -2); + let type = typeName ? typeName : 'field'; + decLine = `${type} ${x_name} = ${structProperties ? `${type} {${structProperties.map(p => ` ${p}: ${x0}.${p} + ${x1}.${p}`)}}` :`${x0} + ${x1}`} `; + } else if (!isWhole){ + const x_name = x.slice(0, -2); + let type = typeName ? typeName : 'field'; + decLine = `${type} ${x_name} = ${structProperties ? `${type} {${structProperties.map(p => ` ${p}: 0`)}}` :`0`} `; + } else{ + return [` // We need to hard-code each stateVarId into the circuit: - field ${x}_stateVarId_field = ${id}`, + field ${x}_stateVarId_field = ${id}`]; + } + return [ + `${decLine} + \n + ${stateVarIdLine}`, // TODO: this results in unnecessary unpacking constraints, but simplifies transpilation effort, for now. ]; }, postStatements({ name: x, isWhole, isNullified, newCommitmentValue, structProperties, structPropertiesTypes, typeName }): string[] { // if (!isWhole && !newCommitmentValue) throw new Error('PATH'); - const y = isWhole ? x : newCommitmentValue; + let y = isWhole ? x : x.slice(0, -2); const lines: string[] = []; if (!isWhole && isNullified) { // decrement @@ -331,18 +352,18 @@ class BoilerplateGenerator { const x1 = x.slice(0, -1) + `${i-1}`; if (!structProperties) { lines.push( - `assert(${x0} + ${x1} > ${y}) + `assert(${y} >0) // TODO: assert no under/overflows - field ${x}_newCommitment_value_field = (${x0} + ${x1}) - (${y})` + field ${x}_newCommitment_value_field = ${y}` ); } else { // TODO types for each structProperty lines.push( - `${structProperties.map(p => newCommitmentValue[p] === '0' ? '' : `assert(${x0}.${p} + ${x1}.${p} >= ${y[p]})`).join('\n')} + `${structProperties.map(p => newCommitmentValue[p] === '0' ? '' : `assert(${y}.${p} > 0)`).join('\n')} // TODO: assert no under/overflows - ${typeName} ${x}_newCommitment_value = ${typeName} { ${structProperties.map(p => ` ${p}: (${x0}.${p} + ${x1}.${p}) - (${y[p]})`)} }` + ${typeName} ${x}_newCommitment_value = ${typeName} { ${structProperties.map(p => ` ${p}: ${y}.${p}`)} }` ); } } else { @@ -355,7 +376,7 @@ class BoilerplateGenerator { } else { - lines.push(`${typeName} ${x}_newCommitment_value = ${typeName} { ${structProperties.map(p => ` ${p}: ${isWhole ? `${y}.${p}` : `${y[p]}`}`)} }\n`); + lines.push(`${typeName} ${x}_newCommitment_value = ${typeName} { ${structProperties.map(p => ` ${p}: ${y}.${p}`)} }\n`); if (structPropertiesTypes) { structPropertiesTypes.forEach(property => { if (property.typeName === 'bool'){ @@ -508,23 +529,22 @@ class BoilerplateGenerator { importStatements(): string[] { return []; // TODO: we might eventually import some underflow/overflow functions. }, - - statements({ name: x}): string[] { - // let y = codeGenerator(addends[0]); - // - // for (const addend of addends) { - // if (addend !== addend[0]) - // y += `${addend.precedingOperator} ${codeGenerator(addend)}`; - // } - - - return [ - `// Skipping incrementation of ${x}` + statements({ name: x, addend, newCommitmentValue, structProperties, memberName}): string[] { + if (addend.incrementType === '+='){ + if (structProperties) { + return [`${x}.${memberName} = ${x}.${memberName} + ${newCommitmentValue}`] + } + return [`${x} = ${x} + ${newCommitmentValue}`]; + } else if (addend.incrementType === '='){ + if (structProperties) { + return [`${x}.${memberName} = ${newCommitmentValue}`] + } + return [`${x} = ${newCommitmentValue}`]; + } + //return [ + // `// Skipping incrementation of ${x}` // ` - // // The below represents the incrementation '${x} = ${x} + ${y}': - // - // field ${x}_${i} = ${y}`, - ]; + //]; }, }; @@ -534,15 +554,27 @@ class BoilerplateGenerator { return []; // TODO: we might eventually import some underflow/overflow functions. }, - statements({ name: x }): string[] { + statements({ name: x, subtrahend, newCommitmentValue, structProperties, memberName}): string[] { + if (subtrahend.decrementType === '-='){ + if (structProperties) { + return [`${x}.${memberName} = ${x}.${memberName} - (${newCommitmentValue})`] + } + return [`${x} = ${x} - (${newCommitmentValue})`]; + } else if (subtrahend.decrementType === '='){ + if (structProperties) { + return [`${x}.${memberName} = ${newCommitmentValue}`] + } + return [`${x} = ${newCommitmentValue}`]; + } + // const y = codeGenerator(subtrahend); // let i = startIndex; // const x0 = `${x}_${i++}`; // const x1 = `${x}_${i++}`; // const x2 = `${x}_${i}`; - return [ - `// Moved decrementation of ${x}` + //return [ + //`// Moved decrementation of ${x}` // ` // // The below represents the decrementation '${x} = ${x} - ${y}': // @@ -550,7 +582,7 @@ class BoilerplateGenerator { // // TODO: assert no under/overflows // // field ${x2} = (${x0} + ${x1}) - ${y}`, - ]; + //]; }, }; internalFunctionCall = { diff --git a/src/boilerplate/contract/solidity/raw/ContractBoilerplateGenerator.ts b/src/boilerplate/contract/solidity/raw/ContractBoilerplateGenerator.ts index 34ba8009f..4d787ae45 100644 --- a/src/boilerplate/contract/solidity/raw/ContractBoilerplateGenerator.ts +++ b/src/boilerplate/contract/solidity/raw/ContractBoilerplateGenerator.ts @@ -210,7 +210,7 @@ class ContractBoilerplateGenerator { uint256[] memory inputs = new uint256[](${[ 'customInputs.length', ...(newNullifiers ? ['newNullifiers.length'] : []), - ...(commitmentRoot ? ['(newNullifiers.length > 0 ? 3 : 0)'] : []), // newNullifiers and commitmentRoot are always submitted together (regardless of use case). It's just that nullifiers aren't always stored (when merely accessing a state). and commitmentRoot are always submitted together (regardless of use case). It's just that nullifiers aren't always stored (when merely accessing a state). + ...(commitmentRoot ? ['(newNullifiers.length > 0 ? 3 : 0) + (((_inputs.nullifierRoot != 0) && (_inputs.latestNullifierRoot == 0)) ? 2 : 0)'] : []), // If there are new nullifiers then we need the nullier root, the latest nullifier root and the commitment root, but if the nullifier root is included but not the latest nullifier root (i.e. there is an accessed only state variable) then we only need 2 inputs. ...(newCommitments ? ['newCommitments.length'] : []), ...(encryptionRequired ? ['encInputsLen'] : []), ].join(' + ')});`, diff --git a/src/boilerplate/orchestration/javascript/nodes/boilerplate-generator.ts b/src/boilerplate/orchestration/javascript/nodes/boilerplate-generator.ts index b7ef765fa..8ec3873fc 100644 --- a/src/boilerplate/orchestration/javascript/nodes/boilerplate-generator.ts +++ b/src/boilerplate/orchestration/javascript/nodes/boilerplate-generator.ts @@ -77,6 +77,7 @@ export function buildPrivateStateNode(nodeType: string, fields: any = {}): any { } case 'MembershipWitness': { const { + id, increment, privateStateName, accessedOnly, @@ -84,10 +85,14 @@ export function buildPrivateStateNode(nodeType: string, fields: any = {}): any { } = fields; return { increment, + stateVarId: id, + isSharedSecret: indicator.isSharedSecret, privateStateName, accessedOnly, isWhole: indicator.isWhole, isPartitioned: indicator.isPartitioned, + structProperties: indicator.isStruct ? Object.keys(indicator.structProperties) : null, + mappingName: indicator.isMapping ? indicator.node?.name : null, }; } case 'CalculateNullifier': { @@ -98,7 +103,6 @@ export function buildPrivateStateNode(nodeType: string, fields: any = {}): any { isSharedSecret: indicator.isSharedSecret, isWhole: indicator.isWhole, isPartitioned: indicator.isPartitioned, - }; } case 'CalculateCommitment': { diff --git a/src/boilerplate/orchestration/javascript/raw/boilerplate-generator.ts b/src/boilerplate/orchestration/javascript/raw/boilerplate-generator.ts index 0e11d9252..bb076de55 100644 --- a/src/boilerplate/orchestration/javascript/raw/boilerplate-generator.ts +++ b/src/boilerplate/orchestration/javascript/raw/boilerplate-generator.ts @@ -105,12 +105,6 @@ class BoilerplateGenerator { accessedOnly, stateVarIds }): string[] { - const stateVarId: string[] = []; - if(stateVarIds.length > 1){ - stateVarId.push((stateVarIds[0].split(" = ")[1]).split(";")[0]); - stateVarId.push(`${stateName}_stateVarId_key`); - } else - stateVarId.push(`${stateName}_stateVarId`); switch (stateType) { case 'increment': @@ -119,15 +113,16 @@ class BoilerplateGenerator { \n\n// read preimage for incremented state ${stateName}_newOwnerPublicKey = ${newOwnerStatment} ${stateVarIds.join('\n')} - \nconst ${stateName}_newCommitmentValue = generalise([${Object.values(increment).map((inc) => `generalise(${inc})`)}]).all; \n + ${structProperties.map(sp => `let ${stateName}_${sp}_newCommitmentValue = generalise(0);\n`).join('')} `]; return [` \n\n// read preimage for incremented state ${stateName}_newOwnerPublicKey = ${newOwnerStatment} ${stateVarIds.join('\n')} - \nconst ${stateName}_newCommitmentValue = generalise(${increment}); - \n`]; + \n + let ${stateName}_newCommitmentValue = generalise(0); + `]; case 'decrement': if (structProperties) return [` @@ -135,70 +130,20 @@ class BoilerplateGenerator { ${stateName}_newOwnerPublicKey = ${newOwnerStatment} ${stateVarIds.join('\n')} \nlet ${stateName}_preimage = await getCommitmentsById(${stateName}_stateVarId); - \nconst ${stateName}_newCommitmentValue = generalise([${Object.values(increment).map((inc) => `generalise(${inc})`)}]).all; - - \nlet [${stateName}_commitmentFlag, ${stateName}_0_oldCommitment, ${stateName}_1_oldCommitment] = getInputCommitments( - publicKey.hex(32), - ${stateName}_newCommitmentValue.integer, - ${stateName}_preimage, - true, - ); - - \nlet ${stateName}_witness_0; - \nlet ${stateName}_witness_1; - - const ${stateName}_0_prevSalt = generalise(${stateName}_0_oldCommitment.preimage.salt); - const ${stateName}_1_prevSalt = generalise(${stateName}_1_oldCommitment.preimage.salt); - const ${stateName}_0_prev = generalise(${stateName}_0_oldCommitment.preimage.value); - const ${stateName}_1_prev = generalise(${stateName}_1_oldCommitment.preimage.value); \n + ${structProperties.map(sp => `let ${stateName}_${sp}_newCommitmentValue = generalise(0);\n`).join('')} + \n + ${structProperties.map(sp => `let ${stateName}_${sp}_newCommitmentValue_inc = generalise(0);\n`).join('')} `]; return [` \n\n// read preimage for decremented state \n${stateName}_newOwnerPublicKey = ${newOwnerStatment} ${stateVarIds.join('\n')} \nlet ${stateName}_preimage = await getCommitmentsById(${stateName}_stateVarId); - \n const ${stateName}_newCommitmentValue = generalise(${increment}); - // First check if required commitments exist or not - \nlet [${stateName}_commitmentFlag, ${stateName}_0_oldCommitment, ${stateName}_1_oldCommitment] = getInputCommitments( - publicKey.hex(32), - ${stateName}_newCommitmentValue.integer, - ${stateName}_preimage, - ); - \nlet ${stateName}_witness_0; - \nlet ${stateName}_witness_1; - - if(${stateName}_1_oldCommitment === null && ${stateName}_commitmentFlag){ - \n${stateName}_witness_0 = await getMembershipWitness('${contractName}', generalise(${stateName}_0_oldCommitment._id).integer); - \n const tx = await splitCommitments('${contractName}', '${mappingName}', ${stateName}_newCommitmentValue, secretKey, publicKey, [${stateVarId.join(' , ')}], ${stateName}_0_oldCommitment, ${stateName}_witness_0, instance, contractAddr, web3); - ${stateName}_preimage = await getCommitmentsById(${stateName}_stateVarId); - - [${stateName}_commitmentFlag, ${stateName}_0_oldCommitment, ${stateName}_1_oldCommitment] = getInputCommitments( - publicKey.hex(32), - ${stateName}_newCommitmentValue.integer, - ${stateName}_preimage, - ); - } - - while(${stateName}_commitmentFlag === false) { - \n${stateName}_witness_0 = await getMembershipWitness('${contractName}', generalise(${stateName}_0_oldCommitment._id).integer); - \n${stateName}_witness_1 = await getMembershipWitness('${contractName}', generalise(${stateName}_1_oldCommitment._id).integer); - - \n const tx = await joinCommitments('${contractName}', '${mappingName}', ${isSharedSecret? `sharedSecretKey, sharedPublicKey`: `secretKey, publicKey`}, [${stateVarId.join(' , ')}], [${stateName}_0_oldCommitment, ${stateName}_1_oldCommitment], [${stateName}_witness_0, ${stateName}_witness_1], instance, contractAddr, web3); - - ${stateName}_preimage = await getCommitmentsById(${stateName}_stateVarId); - - [${stateName}_commitmentFlag, ${stateName}_0_oldCommitment, ${stateName}_1_oldCommitment] = getInputCommitments( - ${isSharedSecret ? `sharedPublicKey.hex(32)` : `publicKey.hex(32)`}, - ${stateName}_newCommitmentValue.integer, - ${stateName}_preimage, - ); - } - const ${stateName}_0_prevSalt = generalise(${stateName}_0_oldCommitment.preimage.salt); - const ${stateName}_1_prevSalt = generalise(${stateName}_1_oldCommitment.preimage.salt); - const ${stateName}_0_prev = generalise(${stateName}_0_oldCommitment.preimage.value); - const ${stateName}_1_prev = generalise(${stateName}_1_oldCommitment.preimage.value); - \n` ]; + \n + let ${stateName}_newCommitmentValue = generalise(0); + \n + let ${stateName}_newCommitmentValue_inc = generalise(0);` ]; case 'whole': switch (reinitialisedOnly) { case true: @@ -237,10 +182,33 @@ class BoilerplateGenerator { membershipWitness = { postStatements({ stateName, contractName, - stateType }): string[] { + stateType, mappingName, structProperties, isSharedSecret, stateVarIds }): string[] { + const stateVarId: string[] = []; + if(stateVarIds.length > 1){ + stateVarId.push((stateVarIds[0].split(" = ")[1]).split(";")[0]); + stateVarId.push(`${stateName}_stateVarId_key`); + } else + stateVarId.push(`${stateName}_stateVarId`); switch (stateType) { case 'partitioned': + if (structProperties) return [` + \n\n// First check if required commitments exist or not + \nconst ${stateName}_newCommitmentValue = generalise([${Object.values(structProperties).map((sp) => `generalise(parseInt(${stateName}_${sp}_newCommitmentValue.integer, 10) - parseInt(${stateName}_${sp}_newCommitmentValue_inc.integer, 10))`)}]).all; + \nlet [${stateName}_commitmentFlag, ${stateName}_0_oldCommitment, ${stateName}_1_oldCommitment] = getInputCommitments( + publicKey.hex(32), + ${stateName}_newCommitmentValue.integer, + ${stateName}_preimage, + true, + ); + + \nlet ${stateName}_witness_0; + \nlet ${stateName}_witness_1; + + const ${stateName}_0_prevSalt = generalise(${stateName}_0_oldCommitment.preimage.salt); + const ${stateName}_1_prevSalt = generalise(${stateName}_1_oldCommitment.preimage.salt); + const ${stateName}_0_prev = generalise(${stateName}_0_oldCommitment.preimage.value); + const ${stateName}_1_prev = generalise(${stateName}_1_oldCommitment.preimage.value); \n\n// generate witness for partitioned state ${stateName}_witness_0 = await getMembershipWitness('${contractName}', generalise(${stateName}_0_oldCommitment._id).integer); ${stateName}_witness_1 = await getMembershipWitness('${contractName}', generalise(${stateName}_1_oldCommitment._id).integer); @@ -249,6 +217,55 @@ class BoilerplateGenerator { const ${stateName}_root = generalise(${stateName}_witness_0.root); const ${stateName}_0_path = generalise(${stateName}_witness_0.path).all; const ${stateName}_1_path = generalise(${stateName}_witness_1.path).all;\n`]; + return [` + \n\n// First check if required commitments exist or not + \n${stateName}_newCommitmentValue = generalise(parseInt(${stateName}_newCommitmentValue.integer, 10) - parseInt(${stateName}_newCommitmentValue_inc.integer, 10)); + \nlet [${stateName}_commitmentFlag, ${stateName}_0_oldCommitment, ${stateName}_1_oldCommitment] = getInputCommitments( + publicKey.hex(32), + ${stateName}_newCommitmentValue.integer, + ${stateName}_preimage, + ); + \nlet ${stateName}_witness_0; + \nlet ${stateName}_witness_1; + + if(${stateName}_1_oldCommitment === null && ${stateName}_commitmentFlag){ + \n${stateName}_witness_0 = await getMembershipWitness('${contractName}', generalise(${stateName}_0_oldCommitment._id).integer); + \n const tx = await splitCommitments('${contractName}', '${mappingName}', ${stateName}_newCommitmentValue, secretKey, publicKey, [${stateVarId.join(' , ')}], ${stateName}_0_oldCommitment, ${stateName}_witness_0, instance, contractAddr, web3); + ${stateName}_preimage = await getCommitmentsById(${stateName}_stateVarId); + + [${stateName}_commitmentFlag, ${stateName}_0_oldCommitment, ${stateName}_1_oldCommitment] = getInputCommitments( + publicKey.hex(32), + ${stateName}_newCommitmentValue.integer, + ${stateName}_preimage, + ); + } + + while(${stateName}_commitmentFlag === false) { + \n${stateName}_witness_0 = await getMembershipWitness('${contractName}', generalise(${stateName}_0_oldCommitment._id).integer); + \n${stateName}_witness_1 = await getMembershipWitness('${contractName}', generalise(${stateName}_1_oldCommitment._id).integer); + + \n const tx = await joinCommitments('${contractName}', '${mappingName}', ${isSharedSecret? `sharedSecretKey, sharedPublicKey`: `secretKey, publicKey`}, [${stateVarId.join(' , ')}], [${stateName}_0_oldCommitment, ${stateName}_1_oldCommitment], [${stateName}_witness_0, ${stateName}_witness_1], instance, contractAddr, web3); + + ${stateName}_preimage = await getCommitmentsById(${stateName}_stateVarId); + + [${stateName}_commitmentFlag, ${stateName}_0_oldCommitment, ${stateName}_1_oldCommitment] = getInputCommitments( + ${isSharedSecret ? `sharedPublicKey.hex(32)` : `publicKey.hex(32)`}, + ${stateName}_newCommitmentValue.integer, + ${stateName}_preimage, + ); + } + const ${stateName}_0_prevSalt = generalise(${stateName}_0_oldCommitment.preimage.salt); + const ${stateName}_1_prevSalt = generalise(${stateName}_1_oldCommitment.preimage.salt); + const ${stateName}_0_prev = generalise(${stateName}_0_oldCommitment.preimage.value); + const ${stateName}_1_prev = generalise(${stateName}_1_oldCommitment.preimage.value); + \n\n// generate witness for partitioned state + ${stateName}_witness_0 = await getMembershipWitness('${contractName}', generalise(${stateName}_0_oldCommitment._id).integer); + ${stateName}_witness_1 = await getMembershipWitness('${contractName}', generalise(${stateName}_1_oldCommitment._id).integer); + const ${stateName}_0_index = generalise(${stateName}_witness_0.index); + const ${stateName}_1_index = generalise(${stateName}_witness_1.index); + const ${stateName}_root = generalise(${stateName}_witness_0.root); + const ${stateName}_0_path = generalise(${stateName}_witness_0.path).all; + const ${stateName}_1_path = generalise(${stateName}_witness_1.path).all;\n`]; case 'whole': return [` \n\n// generate witness for whole state @@ -382,8 +399,13 @@ class BoilerplateGenerator { // once per state switch (stateType) { case 'increment': + let newComValue = ``; + if (structProperties){ + newComValue = `\nconst ${stateName}_newCommitmentValue = generalise([${Object.values(structProperties).map((sp) => `${stateName}_${sp}_newCommitmentValue`)}]).all;`; + } return [` \nconst ${stateName}_newSalt = generalise(utils.randomHex(31)); + ${newComValue} \nlet ${stateName}_newCommitment = poseidonHash([BigInt(${stateName}_stateVarId), ${structProperties ? `...${stateName}_newCommitmentValue.hex(32).map(v => BigInt(v))` : `BigInt(${stateName}_newCommitmentValue.hex(32))`}, BigInt(${stateName}_newOwnerPublicKey.hex(32)), BigInt(${stateName}_newSalt.hex(32))],); \n${stateName}_newCommitment = generalise(${stateName}_newCommitment.hex(32)); // truncate`]; case 'decrement': @@ -588,7 +610,7 @@ encryptBackupPreimage = { ${valueName}]`; varName += ` a`; } else { - plainText = `[BigInt(${saltName}.hex(32)), BigInt(${mappingKey}.hex(32)), + plainText = `[BigInt(${saltName}.hex(32)), BigInt(generalise(${mappingKey}).hex(32)), BigInt(generalise(${stateName}_stateVarIdInit).hex(32)), ${valueName}]`; varName += ` a`; diff --git a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts index ae3dae3ec..a8d141a8f 100644 --- a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts +++ b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts @@ -56,13 +56,14 @@ const stateVariableIds = (node: any) => { const Orchestrationbp = new OrchestrationBP(); export const sendTransactionBoilerplate = (node: any) => { const { privateStates } = node; - const output: string[][] = [[],[],[],[],[],[]]; + const output: string[][] = [[],[],[],[],[],[],[]]; // output[0] = nullifier root(s) - // output[1] = arr of nullifiers - // output[2] = commitments root(s) - // output[3] = arr of commitments - // output[4] = arr of cipherText - // output[5] = arr of enc keys + // output[1] = nullifier root(s) + // output[2] = arr of nullifiers + // output[3] = commitments root(s) + // output[4] = arr of commitments + // output[5] = arr of cipherText + // output[6] = arr of enc keys let privateStateName: string; let stateNode: any; for ([privateStateName, stateNode] of Object.entries(privateStates)) { @@ -71,20 +72,21 @@ export const sendTransactionBoilerplate = (node: any) => { switch (stateNode.nullifierRequired) { case true: // decrement - output[2].push(`${privateStateName}_root.integer`); - output[0].push(`${privateStateName}_nullifierRoot.integer`, `${privateStateName}_newNullifierRoot.integer`); - output[1].push( + output[3].push(`${privateStateName}_root.integer`); + output[0].push(`${privateStateName}_nullifierRoot.integer`); + output[1].push(`${privateStateName}_newNullifierRoot.integer`); + output[2].push( `${privateStateName}_0_nullifier.integer, ${privateStateName}_1_nullifier.integer`, ); - output[3].push(`${privateStateName}_2_newCommitment.integer`); + output[4].push(`${privateStateName}_2_newCommitment.integer`); break; case false: default: // increment - output[3].push(`${privateStateName}_newCommitment.integer`); + output[4].push(`${privateStateName}_newCommitment.integer`); if (stateNode.encryptionRequired) { - output[4].push(`${privateStateName}_cipherText`); - output[5].push(`${privateStateName}_encKey`); + output[5].push(`${privateStateName}_cipherText`); + output[6].push(`${privateStateName}_encKey`); } break; } @@ -93,16 +95,19 @@ export const sendTransactionBoilerplate = (node: any) => { default: // whole if (!stateNode.reinitialisedOnly) - output[2].push(`${privateStateName}_root.integer`); - if (!stateNode.accessedOnly && !stateNode.reinitialisedOnly) { - output[1].push(`${privateStateName}_nullifier.integer`); - output[0].push(`${privateStateName}_nullifierRoot.integer`,`${privateStateName}_newNullifierRoot.integer`); + output[3].push(`${privateStateName}_root.integer`); + if (!stateNode.reinitialisedOnly) { + output[0].push(`${privateStateName}_nullifierRoot.integer`) + if (!stateNode.accessedOnly) { + output[2].push(`${privateStateName}_nullifier.integer`); + output[1].push(`${privateStateName}_newNullifierRoot.integer`); + } } if (!stateNode.accessedOnly && !stateNode.burnedOnly) - output[3].push(`${privateStateName}_newCommitment.integer`); + output[4].push(`${privateStateName}_newCommitment.integer`); if (stateNode.encryptionRequired) { - output[4].push(`${privateStateName}_cipherText`); - output[5].push(`${privateStateName}_encKey`); + output[5].push(`${privateStateName}_cipherText`); + output[6].push(`${privateStateName}_encKey`); } break; @@ -205,16 +210,17 @@ export const generateProofBoilerplate = (node: any) => { case true: // decrement if (stateNode.structProperties) stateNode.increment = Object.values(stateNode.increment).flat(Infinity); - stateNode.increment.forEach((inc: any) => { + // Below has been removed as it does not appear to be needed. Revisit if issues arise. + /*stateNode.increment.forEach((inc: any) => { // +inc.name tries to convert into a number - we don't want to add constants here if ( !output.join().includes(`\t${inc.name}.integer`) && !parameters.includes(`\t${inc.name}.integer,`) && !privateStateNames.includes(inc.name) && !inc.accessed && - !+inc.name + !+inc.name && inc.name ) output.push(`\n\t\t\t\t\t\t\t\t${inc.name}.integer`); - }); + });*/ output.push( Orchestrationbp.generateProof.parameters({ stateName, @@ -241,14 +247,15 @@ export const generateProofBoilerplate = (node: any) => { default: // increment if (stateNode.structProperties) stateNode.increment = Object.values(stateNode.increment).flat(Infinity); - stateNode.increment.forEach((inc: any) => { + // Below has been removed as it does not appear to be needed. Revisit if issues arise. + /*stateNode.increment.forEach((inc: any) => { if ( !output.join().includes(`\t${inc.name}.integer`) && !parameters.includes(`\t${inc.name}.integer,`) && !inc.accessed && !+inc.name ) output.push(`\n\t\t\t\t\t\t\t\t${inc.name}.integer`); - }); + });*/ output.push( Orchestrationbp.generateProof.parameters( { stateName, @@ -690,6 +697,7 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { case 'MembershipWitness': for ([stateName, stateNode] of Object.entries(node.privateStates)) { + const stateVarIds = stateVariableIds({ privateStateName: stateName, stateNode }); if (node.isConstructor) { lines.push([` const ${stateName}_index = generalise(0); @@ -704,6 +712,10 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { stateName, contractName: node.contractName, stateType: 'partitioned', + mappingName: stateNode.mappingName || stateName, + structProperties: stateNode.structProperties, + isSharedSecret: stateNode.isSharedSecret, + stateVarIds })); } @@ -713,6 +725,10 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { stateName, contractName: node.contractName, stateType: 'accessedOnly', + mappingName: stateNode.mappingName || stateName, + structProperties: stateNode.structProperties, + isSharedSecret: stateNode.isSharedSecret, + stateVarIds })); } else if (stateNode.isWhole) { @@ -721,6 +737,10 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { stateName, contractName: node.contractName, stateType: 'whole', + mappingName: stateNode.mappingName || stateName, + structProperties: stateNode.structProperties, + isSharedSecret: stateNode.isSharedSecret, + stateVarIds })); } @@ -906,19 +926,18 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { } params[0] = sendTransactionBoilerplate(node); - if(!node.returnInputs[0] && !params[0][4][0]) returnInputs.push(`1`); // If there are no return, circuit's default return is true + if(!node.returnInputs[0] && !params[0][5][0]) returnInputs.push(`1`); // If there are no return, circuit's default return is true // params[0] = arr of nullifier root(s) // params[1] = arr of commitment root(s) // params[2] = arr of nullifiers // params[3] = arr of commitments - (params[0][0][0]) ? params[0][0][0] = ` ${params[0][0][0]}, ` : params[0][0][0] = ` 0 , ` ; // nullifierRoot - array // Default value for the struct - (params[0][0][1]) ? params[0][0][1] = ` ${params[0][0][1]}, ` : params[0][0][1] = ` 0, `; - (params[0][2][0]) ? params[0][2] = ` ${params[0][2][0]},` : params[0][2] = ` 0 , ` ; // commitmentRoot - array - (params[0][1][0]) ? params[0][1] = ` [${params[0][1]}],` : params[0][1] = ` [], `; // nullifiers - array - (params[0][3][0]) ? params[0][3] = `[${params[0][3]}],` : params[0][3] = ` [], `; // commitments - array - (params[0][4][0]) ? params[0][4] = `[${params[0][4]}],` : params[0][4] = ` [], `; // cipherText - array of arrays - (params[0][5][0]) ? params[0][5] = `[${params[0][5]}],`: params[0][5] = ` [], `;// cipherText - array of arrays - + (params[0][0][0]) ? params[0][0] = ` ${params[0][0][0]}, ` : params[0][0] = ` 0 , ` ; // nullifierRoot - array // Default value for the struct + (params[0][1][0]) ? params[0][1] = ` ${params[0][1][0]}, ` : params[0][1] = ` 0, `; + (params[0][3][0]) ? params[0][3] = ` ${params[0][3][0]},` : params[0][3] = ` 0 , ` ; // commitmentRoot - array + (params[0][2][0]) ? params[0][2] = ` [${params[0][2]}],` : params[0][2] = ` [], `; // nullifiers - array + (params[0][4][0]) ? params[0][4] = `[${params[0][4]}],` : params[0][4] = ` [], `; // commitments - array + (params[0][5][0]) ? params[0][5] = `[${params[0][5]}],` : params[0][5] = ` [], `; // cipherText - array of arrays + (params[0][6][0]) ? params[0][6] = `[${params[0][6]}],`: params[0][6] = ` [], `;// cipherText - array of arrays if (node.functionName === 'cnstrctr') return { statements: [ @@ -926,7 +945,7 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { \nBackupData.forEach((element) => { element.cipherText = element.cipherText.map(ct => generalise(ct).hex(32)); }); - \nconst tx = { proofInput: [{customInputs: [${returnInputs}], nullifierRoot: ${params[0][0][0]} latestNullifierRoot:${params[0][0][1]} newNullifiers: ${params[0][1]} commitmentRoot:${params[0][2]} newCommitments: ${params[0][3]}}, proof, BackupData], nullifiers: ${params[0][1]} isNullfiersAdded: false, ${node.publicInputs?.map(input => `${input}: ${input}.integer,`)}};` + \nconst tx = { proofInput: [{customInputs: [${returnInputs}], nullifierRoot: ${params[0][0]} latestNullifierRoot:${params[0][1]} newNullifiers: ${params[0][2]} commitmentRoot:${params[0][3]} newCommitments: ${params[0][4]}}, proof, BackupData], nullifiers: ${params[0][2]} isNullfiersAdded: false, ${node.publicInputs?.map(input => `${input}: ${input}.integer,`)}};` ] } @@ -936,7 +955,7 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { statements: [ `\n\n// Send transaction to the blockchain: \nconst txData = await instance.methods - .${node.functionName}(${lines.length > 0 ? `${lines},`: ``} {customInputs: [${returnInputs}], nullifierRoot: ${params[0][0][0]} latestNullifierRoot:${params[0][0][1]} newNullifiers: ${params[0][1]} commitmentRoot:${params[0][2]} newCommitments: ${params[0][3]} cipherText:${params[0][4]} encKeys: ${params[0][5]}}, proof, BackupData).encodeABI(); + .${node.functionName}(${lines.length > 0 ? `${lines},`: ``} {customInputs: [${returnInputs}], nullifierRoot: ${params[0][0]} latestNullifierRoot:${params[0][1]} newNullifiers: ${params[0][2]} commitmentRoot:${params[0][3]} newCommitments: ${params[0][4]} cipherText:${params[0][5]} encKeys: ${params[0][6]}}, proof, BackupData).encodeABI(); \n let txParams = { from: config.web3.options.defaultAccount, to: contractAddr, diff --git a/src/codeGenerators/circuit/zokrates/toCircuit.ts b/src/codeGenerators/circuit/zokrates/toCircuit.ts index d37c971ac..62667eaad 100644 --- a/src/codeGenerators/circuit/zokrates/toCircuit.ts +++ b/src/codeGenerators/circuit/zokrates/toCircuit.ts @@ -91,7 +91,6 @@ function codeGenerator(node: any) { } }); } - functionSignature = `def main(\\\n\t${codeGenerator(node.parameters)}\\\n) -> `; node.returnParameters.parameters.forEach((node) => { if((node.isPrivate === true && node.typeName.name != 'bool') || node.typeName.name.includes('EncryptedMsgs')) @@ -331,6 +330,10 @@ function codeGenerator(node: any) { return Circuitbp.generateBoilerplate(node); case 'BoilerplateStatement': { + let newComValue = ''; + if (node.bpType === 'incrementation') newComValue = codeGenerator(node.addend); + if (node.bpType === 'decrementation') newComValue = codeGenerator(node.subtrahend); + node.newCommitmentValue = newComValue; return Circuitbp.generateBoilerplate(node); } diff --git a/src/codeGenerators/orchestration/nodejs/toOrchestration.ts b/src/codeGenerators/orchestration/nodejs/toOrchestration.ts index ed40f283f..9a432fdec 100644 --- a/src/codeGenerators/orchestration/nodejs/toOrchestration.ts +++ b/src/codeGenerators/orchestration/nodejs/toOrchestration.ts @@ -128,6 +128,36 @@ export default function codeGenerator(node: any, options: any = {}): any { if (!node.incrementsSecretState && (node.interactsWithSecret || node.expression?.internalFunctionInteractsWithSecret)){ return `\n${codeGenerator(node.expression)};`; } + if (node.incrementsSecretState && (node.interactsWithSecret || node.expression?.internalFunctionInteractsWithSecret)){ + let privateStateName = node.privateStateName.replace(/\./g, '_'); + let increments; + if (node.expression.operator === '+='){ + increments = codeGenerator(node.expression.rightHandSide); + // Although we have += in the case that the indicator is decremented elsewhere in the function, we need to subtract the increments. + if (!node.indicatorDecremented) return `\n${privateStateName}_newCommitmentValue = generalise(parseInt(${privateStateName}_newCommitmentValue.integer, 10) + ${increments});\n`; + if (node.indicatorDecremented) return `\n${privateStateName}_newCommitmentValue_inc = generalise(parseInt(${privateStateName}_newCommitmentValue_inc.integer, 10) + ${increments});\n`; + } + if (node.expression.operator === '-='){ + increments = codeGenerator(node.expression.rightHandSide); + return `\n${privateStateName}_newCommitmentValue = generalise(parseInt(${privateStateName}_newCommitmentValue.integer, 10) + (${increments}));\n`; + } + if (node.expression.operator === '='){ + increments = codeGenerator(node.expression.rightHandSide); + if (node.decrementsSecretState){ + increments = increments.replace(new RegExp(`${privateStateName}.integer`, 'g'), `0`); + return `\n${privateStateName}_newCommitmentValue = generalise(parseInt(${privateStateName}_newCommitmentValue.integer, 10) - (${increments}));\n`; + } + if (!node.indicatorDecremented) { + increments = increments.replace(new RegExp(privateStateName, 'g'), `${privateStateName}_newCommitmentValue`); + return `\n${privateStateName}_newCommitmentValue = generalise(${increments});\n`; + } + if (node.indicatorDecremented) { + increments = increments.replace(new RegExp(privateStateName, 'g'), `${privateStateName}_newCommitmentValue_inc`); + return `\n${privateStateName}_newCommitmentValue_inc = generalise(\n${increments});\n`; + } + } + } + if (!node.interactsWithSecret) return `\n// non-secret line would go here but has been filtered out`; return `\n// increment would go here but has been filtered out`; diff --git a/src/transformers/visitors/checks/accessedVisitor.ts b/src/transformers/visitors/checks/accessedVisitor.ts index 56d95fc19..80bf7d827 100644 --- a/src/transformers/visitors/checks/accessedVisitor.ts +++ b/src/transformers/visitors/checks/accessedVisitor.ts @@ -26,7 +26,8 @@ export default { state.incrementedDeclaration = path.incrementedDeclaration; } }, - exit(state: any) { + // path is unused here but if unincluded we cannot acccess the state object + exit(path: NodePath, state: any) { state.inIncrementation = false; state.incrementedDeclaration = null; }, @@ -41,7 +42,7 @@ export default { // e.g. a = a + b --> b accessed // e.g. a += 10, a whole --> a accessed // e.g. myMapping[a] = x --> a accessed - const { node, scope } = path; + const { node, scope} = path; if (path.isMsg()) return; // the node represents the special 'msg' type in solidity if (path.isThis()) return; // the node represents the special 'this' type in solidity if (path.isExportedSymbol()) return; // the node represents an external contract name diff --git a/src/transformers/visitors/checks/incrementedVisitor.ts b/src/transformers/visitors/checks/incrementedVisitor.ts index e4bba3ab1..1d1fdea40 100644 --- a/src/transformers/visitors/checks/incrementedVisitor.ts +++ b/src/transformers/visitors/checks/incrementedVisitor.ts @@ -155,7 +155,8 @@ const binOpToIncrements = (path: NodePath, state: any) => { precedingOperator.splice(2, 0, operand.operator); } } - operands.splice(0, 0, operands[operands.length -1]).slice(0, -1); + operands.splice(0, 0, operands[operands.length -1]); + operands.pop(); } // fills an array of operands @@ -170,8 +171,8 @@ const binOpToIncrements = (path: NodePath, state: any) => { } } operands.splice(0, 0, operands[operands.length -1]); + operands.pop(); } - // if we have mixed operators, we may have an underflow or not be able to tell whether this is increasing (incrementation) or decreasing (decrementation) the secret value // Here we give out a warning when we don't use parentheses. if ( @@ -187,6 +188,7 @@ const binOpToIncrements = (path: NodePath, state: any) => { `Whenever you have multiple operands in an expression, such as a = a - b - c + d, it's better to use parentheses for clarity. For example, rewrite it as a = a - (b + c - d). This makes the expression easier to understand. `, ); } + if(assignmentOp === '=' && precedingOperator.length > 2) { if(isTupleExpression) { operands.splice(0, 0, path.node.leftExpression); @@ -435,9 +437,9 @@ export default { if ( nameMatch && precedingOperator[index + 1]?.includes('+') && // we have ... + a + ... - precedingOperator[index]?.includes('+') // otherwise we have a = b - a + (index ===0) + //precedingOperator[index]?.includes('+') // otherwise we have a = b - a ) { - discoveredLHS += 1; isIncremented = { incremented: true, decremented: false }; } @@ -445,17 +447,16 @@ export default { if ( nameMatch && precedingOperator[index + 1]?.includes('-') && // we have ... + a - ... - precedingOperator[index]?.includes('+') // otherwise we have a = b - a + (index ===0) + //precedingOperator[index]?.includes('+') // otherwise we have a = b - a ) { - discoveredLHS += 1; isIncremented = { incremented: true, decremented: true }; } - // a = something - a + if ( - nameMatch && - precedingOperator[index]?.includes('-') // we have a = b - a + nameMatch ) { - discoveredLHS -= 1; + discoveredLHS += 1; } // if none, go to the next operand if (operand.indexExpression?.expression?.name === 'msg') diff --git a/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts b/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts index 3417b712e..273174ad4 100644 --- a/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts +++ b/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts @@ -45,7 +45,7 @@ const internalCallVisitor = { } state.newPreStatementList = cloneDeep(childNode.body.preStatements); state.newPreStatementList.forEach(node => { - if(['InitialisePreimage','ReadPreimage','MembershipWitness'].includes(node.nodeType)) { + if(['InitialisePreimage','ReadPreimage'].includes(node.nodeType)) { let stateName: string; let stateNode: any; let newstateName: string; @@ -75,10 +75,6 @@ const internalCallVisitor = { stateNode.stateVarId[1] = stateNode.stateVarId[1].replace(oldStateName, state.newStateArray[name][index]) break; } - case 'MembershipWitness': { - stateNode.privateStateName = stateNode.privateStateName.replace('_'+oldStateName, '_'+ state.newStateArray[name][index]) - break; - } default : break; } @@ -106,6 +102,20 @@ const internalCallVisitor = { }); state.newPostStatementList = cloneDeep(childNode.body.postStatements); state.newPostStatementList.forEach(node => { + if(node.nodeType === 'MembershipWitness'){ + let stateName: string; + let stateNode: any; + let newstateName: string; + for( [stateName, stateNode] of Object.entries(node.privateStates)){ + for(const [index, oldStateName] of state.oldStateArray[name].entries()) { + newstateName = stateName.replace('_'+oldStateName, '_'+ state.newStateArray[name][index]) + if(newstateName != stateName ){ + node.privateStates[ newstateName ] = node.privateStates[stateName]; + delete(node.privateStates[ stateName ]); + } + } + } + } if(node.nodeType === 'CalculateNullifier'){ let stateName: string; let stateNode: any; @@ -238,22 +248,6 @@ const internalCallVisitor = { }); break; } - case 'MembershipWitness': { - state.newPreStatementList.forEach(statenode => { - if(statenode.nodeType === 'MembershipWitness'){ - Object.keys(node.privateStates).forEach(key => { - Object.keys(statenode.privateStates).forEach(newKey => { - if (key === newKey){ - statenode.privateStates[newKey].accessedOnly = statenode.privateStates[newKey].accessedOnly && node.privateStates[key].accessedOnly; - statenode.privateStates[newKey].nullifierRequired = statenode.privateStates[newKey].nullifierRequired || node.privateStates[key].nullifierRequired; - } - }); - }); - node.privateStates = Object.assign(node.privateStates,statenode.privateStates) - } - }); - break; - } default : break; } @@ -377,6 +371,22 @@ const internalCallVisitor = { childNode.body.postStatements.forEach(node => { switch(node.nodeType) { + case 'MembershipWitness' : { + state.newPostStatementList.forEach(statenode => { + if(statenode.nodeType === 'MembershipWitness'){ + Object.keys(node.privateStates).forEach(key => { + Object.keys(statenode.privateStates).forEach(newKey => { + if (key === newKey){ + statenode.privateStates[newKey].accessedOnly = statenode.privateStates[newKey].accessedOnly && node.privateStates[key].accessedOnly; + statenode.privateStates[newKey].nullifierRequired = statenode.privateStates[newKey].nullifierRequired || node.privateStates[key].nullifierRequired; + } + }); + }); + node.privateStates = Object.assign(node.privateStates,statenode.privateStates); + } + }); + break; + } case 'CalculateNullifier' : { state.newPostStatementList.forEach(statenode => { if(statenode.nodeType === 'CalculateNullifier'){ diff --git a/src/transformers/visitors/toCircuitVisitor.ts b/src/transformers/visitors/toCircuitVisitor.ts index 34a95eda7..db99291ac 100644 --- a/src/transformers/visitors/toCircuitVisitor.ts +++ b/src/transformers/visitors/toCircuitVisitor.ts @@ -905,6 +905,9 @@ const visitor = { lhs.indexExpression?.name || lhs.indexExpression.expression.name, }), // TODO: tidy this + ...(lhsIndicator.isStruct && { + memberName: lhs.memberName + }), }); tempRHSPath.containerName = 'subtrahend'; // a dangerous bodge that works node._newASTPointer = newNode.subtrahend; @@ -919,13 +922,15 @@ const visitor = { lhs.indexExpression?.name || lhs.indexExpression.expression.name, }), // TODO: tidy this + ...(lhsIndicator.isStruct && { + memberName: lhs.memberName + }), }); tempRHSPath.containerName = 'addend'; // a dangerous bodge that works node._newASTPointer = newNode.addend; } // The child of this 'ExpressionStatement' node is an 'Assignment' node. But we've built a newNode to replace the 'Assignment' node of the original tree. The child of this newNode will be the RHS of the original 'Assignment' node. We discard the LHS, so we need to 'skip' the traversal of the 'Assignment' (using skipSubNodes = true), and instead traverse directly into the RHS node. - tempRHSParent._newASTPointer = newNode; // we don't want to add public inputs twice: @@ -940,6 +945,8 @@ const visitor = { state.skipSubNodes = true; parent._newASTPointer.push(newNode); incrementNames(newNode, lhsIndicator); + if (newNode.addend) newNode.addend.incrementType = expression.operator; + if (newNode.subtrahend) newNode.subtrahend.decrementType = expression.operator; return; } default: diff --git a/src/transformers/visitors/toOrchestrationVisitor.ts b/src/transformers/visitors/toOrchestrationVisitor.ts index c1b536b9c..6124d1471 100644 --- a/src/transformers/visitors/toOrchestrationVisitor.ts +++ b/src/transformers/visitors/toOrchestrationVisitor.ts @@ -12,6 +12,7 @@ import { interactsWithSecretVisitor, parentnewASTPointer, initialiseOrchestratio // collects increments and decrements into a string (for new commitment calculation) and array // (for collecting zokrates inputs) +// This function appears to be unused - consider removing it? const collectIncrements = (stateVarIndicator: StateVariableIndicator | MappingKey) => { const incrementsArray: any[] = []; let incrementsString = ''; @@ -814,6 +815,7 @@ const visitor = { newNodes.membershipWitnessNode.privateStates[ name ] = buildPrivateStateNode('MembershipWitness', { + id, privateStateName: name, indicator: stateVarIndicator, accessedOnly, @@ -841,7 +843,6 @@ const visitor = { const keyIndicator = path.scope.getReferencedIndicator(stateVarIndicator[`keyPath`].node, true); if (keyIndicator instanceof LocalVariableIndicator && !keyIndicator.isParam) localMappingKey = true; } - newNodes.generateProofNode.privateStates[ name ] = buildPrivateStateNode('GenerateProof', { @@ -1153,12 +1154,14 @@ const visitor = { // 9 - WritePreimage - all - per state if (newNodes.readPreimageNode) newFunctionDefinitionNode.body.preStatements.push(newNodes.readPreimageNode); + + if(newNodes.VariableDeclarationStatement) + newFunctionDefinitionNode.body.preStatements.push(newNodes.VariableDeclarationStatement); + if (newNodes.membershipWitnessNode) - newFunctionDefinitionNode.body.preStatements.push( - newNodes.membershipWitnessNode, - ); - if(newNodes.VariableDeclarationStatement) - newFunctionDefinitionNode.body.preStatements.push(newNodes.VariableDeclarationStatement); + newFunctionDefinitionNode.body.postStatements.push( + newNodes.membershipWitnessNode, + ); if (newNodes.calculateNullifierNode) newFunctionDefinitionNode.body.postStatements.push( @@ -1476,7 +1479,7 @@ const visitor = { if (!lhs) lhs = node.expression.subExpression; indicator = scope.getReferencedIndicator(lhs, true); - const name = indicator.isMapping + let name = indicator.isMapping ? indicator.name .replace('[', '_') .replace(']', '') @@ -1562,6 +1565,9 @@ const visitor = { return; } } + if (indicator.isMapping && indicator.isStruct){ + name = `${name}_${node.expression.leftHandSide.memberName}`; + } // if its an incrementation, we need to know it happens but not copy it over if (node.expression.isIncremented && indicator.isPartitioned) { const newNode = buildNode(node.nodeType, { @@ -1573,6 +1579,8 @@ const visitor = { decrementsSecretState: node.expression.isDecremented, privateStateName: name, }); + newNode.indicatorIncremented= indicator.isIncremented; + newNode.indicatorDecremented= indicator.isDecremented; node._newASTPointer = newNode; parent._newASTPointer.push(newNode); diff --git a/src/traverse/Indicator.ts b/src/traverse/Indicator.ts index 8486b6a10..25c91c82a 100644 --- a/src/traverse/Indicator.ts +++ b/src/traverse/Indicator.ts @@ -633,10 +633,20 @@ export class StateVariableIndicator extends FunctionDefinitionIndicator { this.isDecremented ||= path.isDecremented; this.increments ??= []; this.decrements ??= []; + let incCounter =0; + if (this.increments[0] && state.increments[0]){ + incCounter = this.increments[this.increments.length -1].counter +1; + } state.increments.forEach((inc: any) => { + inc.counter = incCounter; this.increments?.push(inc); }); + let decCounter =0; + if (this.decrements[0] && state.decrements[0]){ + decCounter = this.decrements[this.decrements.length -1].counter +1; + } state.decrements.forEach((dec: any) => { + dec.counter = decCounter; this.decrements?.push(dec); }); if (this.isMapping) { diff --git a/test/contracts/Assign-type-conversion.zol b/test/contracts/Assign-type-conversion.zol index 7c609e957..4ca3c539c 100644 --- a/test/contracts/Assign-type-conversion.zol +++ b/test/contracts/Assign-type-conversion.zol @@ -13,6 +13,6 @@ contract Assign { } function remove(uint256 remval) public { - b -= uint(uint128(remval)); + b += uint(uint128(remval)); } } diff --git a/test/contracts/KnownUnknown.zol b/test/contracts/KnownUnknown.zol new file mode 100644 index 000000000..939673f63 --- /dev/null +++ b/test/contracts/KnownUnknown.zol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: CC0 +pragma solidity ^0.8.0; +contract Assign { + secret uint256 private a; + secret uint256 private b; + + struct MyStruct { + uint256 prop1; + uint256 prop2; + } + + secret MyStruct public x; + + + function add( uint256 value ) public { + unknown b = b + value +a; + unknown x.prop1 += value + a; + unknown x.prop2 += value; + unknown b += value; + unknown x.prop1 += a; + a+= value; + } + + + function add1( uint256 value ) public { + unknown b = b + value +a; + unknown x.prop1 += value + a; + unknown x.prop2 += value; + unknown b += value; + unknown x.prop1 += a; + a+= value; + unknown b -= 5*value + a; + unknown x.prop1 -= 5*value + a; + unknown x.prop2 -= 2*value; + } + + function remove( uint256 value ) public { + unknown b = b - (value +a); + unknown x.prop1 -= value + a; + unknown x.prop2 -= value; + unknown b -= value; + unknown x.prop1 -= value; + a+= value; + } + +} \ No newline at end of file diff --git a/test/contracts/KnownUnknown1.zol b/test/contracts/KnownUnknown1.zol new file mode 100644 index 000000000..abad2d5e6 --- /dev/null +++ b/test/contracts/KnownUnknown1.zol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: CC0 +pragma solidity ^0.8.0; +contract Assign { + secret uint256 private a; + secret uint256 private b; + secret mapping(uint256 => uint256) private c; + + struct MyStruct { + uint256 prop1; + uint256 prop2; + } + + secret MyStruct public x; + + function add( uint256 value ) public { + a+= value; + unknown b += value +a; + c[0] += value; + } + + function add1(secret uint256 value ) public { + unknown b += value +a; + unknown b += c[0]; + } + + function add2(secret uint256 value ) public { + unknown b = b + a -value; + unknown b += (a - value); + } + + + function remove( uint256 value ) public { + a -= 15; + //unknown b = b - a + value; + //unknown b = b - a - value; + //b -= a - value; + //b -= a + value; + //b -= (a+ value); + b -= (a - value); + } + +} \ No newline at end of file diff --git a/test/error-checks/KnownUnknown3.zol b/test/error-checks/KnownUnknown3.zol new file mode 100644 index 000000000..db1c37215 --- /dev/null +++ b/test/error-checks/KnownUnknown3.zol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: CC0 +pragma solidity ^0.8.0; +contract Assign { + secret uint256 private a; + secret uint256 private b; + + + function add( uint256 value ) public { + unknown b += value +a; + a+= b + value; + //a = a + b + value; + //a = b + value; + } + + +} \ No newline at end of file