Skip to content

Commit

Permalink
Merge pull request #332 from EYBlockchain/lydia/restoreVariable
Browse files Browse the repository at this point in the history
chore: support back up for commitments with a specific variable name
  • Loading branch information
lydiagarms authored Sep 13, 2024
2 parents d7367ec + 3df2c15 commit 4f45c0c
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 3 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,13 @@ You can also filter these commitments by variable name. Using the example above,
```
as a GET request to `http://localhost:3000/getCommitmentsByVariableName`.

If the commitment database that stores commitment preimages is lost you can restore the DB (by decrypting the onchain encryptions of the preimages) by sending a POST request to `http://localhost:3000/backupDataRetriever`.
If the commitment database that stores commitment preimages is lost you can restore the DB (by decrypting the onchain encryptions of the preimages) by sending a POST request to `http://localhost:3000/backupDataRetriever`. You can also restore only the commitments with a specific variable name. For example, if you want to restore commitments representing variable `a`, send:
```
{
"name": "a"
}
```
as a POST request to `http://localhost:3000/backupVariable`.


#### Using secret states in the constructor
Expand Down
12 changes: 12 additions & 0 deletions src/boilerplate/common/commitment-storage.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ export async function getCommitmentsByState(name, mappingKey = null) {
return commitments;
}

// function to delete commitment with a specified stateName
export async function deleteCommitmentsByState(name, mappingKey = null) {
const connection = await mongo.connection(MONGO_URL);
const db = connection.db(COMMITMENTS_DB);
const query = { name: name };
if (mappingKey) query['mappingKey'] = generalise(mappingKey).integer;
const deleteResult = await db
.collection(COMMITMENTS_COLLECTION)
.deleteMany(query);
return deleteResult;
}

// function to retrieve all known nullified commitments
export async function getNullifiedCommitments() {
const connection = await mongo.connection(MONGO_URL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,18 @@ export function buildBoilerplateNode(nodeType: string, fields: any = {}): any {
};
}

case 'BackupVariableBoilerplate': {
const {
contractName,
privateStates = [],
} = fields;
return {
nodeType,
contractName,
privateStates,
};
}

default:
throw new TypeError(nodeType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,7 @@ integrationApiServicesBoilerplate = {
`
},
preStatements(): string{
return ` import { startEventFilter, getSiblingPath } from './common/timber.mjs';\nimport fs from "fs";\nimport logger from './common/logger.mjs';\nimport { decrypt } from "./common/number-theory.mjs";\nimport { getAllCommitments, getCommitmentsByState, reinstateNullifiers, getBalance, getSharedSecretskeys , getBalanceByState, addConstructorNullifiers } from "./common/commitment-storage.mjs";\nimport { backupDataRetriever } from "./BackupDataRetriever.mjs";\nimport web3 from './common/web3.mjs';\n\n
return ` import { startEventFilter, getSiblingPath } from './common/timber.mjs';\nimport fs from "fs";\nimport logger from './common/logger.mjs';\nimport { decrypt } from "./common/number-theory.mjs";\nimport { getAllCommitments, getCommitmentsByState, reinstateNullifiers, getBalance, getSharedSecretskeys , getBalanceByState, addConstructorNullifiers } from "./common/commitment-storage.mjs";\nimport { backupDataRetriever } from "./BackupDataRetriever.mjs";\nimport { backupVariable } from "./BackupVariable.mjs";\nimport web3 from './common/web3.mjs';\n\n
/**
NOTE: this is the api service file, if you need to call any function use the correct url and if Your input contract has two functions, add() and minus().
minus() cannot be called before an initial add(). */
Expand Down Expand Up @@ -887,6 +887,17 @@ integrationApiServicesBoilerplate = {
res.send({ errors: [err.message] });
}
}
export async function service_backupVariable(req, res, next) {
try {
const { name } = req.body;
await backupVariable(name);
res.send("Complete");
await sleep(10);
} catch (err) {
logger.error(err);
res.send({ errors: [err.message] });
}
}
export async function service_getSharedKeys(req, res, next) {
try {
const { recipientAddress } = req.body;
Expand Down Expand Up @@ -921,7 +932,7 @@ integrationApiRoutesBoilerplate = {
return `router.post('/FUNCTION_NAME', this.serviceMgr.service_FUNCTION_NAME.bind(this.serviceMgr),);`
},
commitmentImports(): string {
return `import { service_allCommitments, service_getCommitmentsByState, service_reinstateNullifiers, service_getSharedKeys, service_getBalance, service_getBalanceByState, service_backupData, } from "./api_services.mjs";\n`;
return `import { service_allCommitments, service_getCommitmentsByState, service_reinstateNullifiers, service_getSharedKeys, service_getBalance, service_getBalanceByState, service_backupData, service_backupVariable,} from "./api_services.mjs";\n`;
},
commitmentRoutes(): string {
return `// commitment getter routes
Expand All @@ -934,6 +945,7 @@ integrationApiRoutesBoilerplate = {
router.post("/getSharedKeys", service_getSharedKeys);
// backup route
router.post("/backupDataRetriever", service_backupData);
router.post("/backupVariable", service_backupVariable);
`;
}
};
Expand Down
187 changes: 187 additions & 0 deletions src/codeGenerators/orchestration/files/toOrchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,187 @@ const prepareStartupScript = (file: localFile, node: any) => {
file.file = file.file.replace(/CONSTRUCTOR_CALL/g, constructorCall);
}

const prepareBackupVariable = (node: any) => {
// import generic test skeleton
let genericApiServiceFile: any = `/* eslint-disable prettier/prettier, camelcase, prefer-const, no-unused-vars */
import config from "config";
import utils from "zkp-utils";
import GN from "general-number";
import fs from "fs";
import mongo from "./common/mongo.mjs";
import {
storeCommitment,
markNullified,
deleteCommitmentsByState
} from "./common/commitment-storage.mjs";
import { getContractInstance, getContractAddress } from "./common/contract.mjs";
import Web3 from "./common/web3.mjs";
import {
decompressStarlightKey,
compressStarlightKey,
encrypt,
decrypt,
poseidonHash,
scalarMult,
} from "./common/number-theory.mjs";
const { generalise } = GN;
const web3 = Web3.connection();
const keyDb = "/app/orchestration/common/db/key.json";
const { MONGO_URL, COMMITMENTS_DB, COMMITMENTS_COLLECTION } = config;
export async function backupVariable(_name) {
let requestedName = _name;
deleteCommitmentsByState(requestedName, null);
const instance = await getContractInstance("AssignShield");
const backDataEvent = await instance.getPastEvents("EncryptedBackupData", {
fromBlock: 0,
toBlock: "latest",
});
const keys = JSON.parse(
fs.readFileSync(keyDb, "utf-8", (err) => {
console.log(err);
})
);
const secretKey = generalise(keys.secretKey);
const publicKey = generalise(keys.publicKey);
let storedCommitments = [];
for (const log of backDataEvent) {
for (let i = 0; i < log.returnValues.encPreimages.length; i++) {
let cipherText = log.returnValues.encPreimages[i].cipherText;
let ephPublicKey = log.returnValues.encPreimages[i].ephPublicKey;
let varName = log.returnValues.encPreimages[i].varName;
let name = varName.replace(" a", "").replace(" s", "").replace(" u", "");
if (requestedName !== name) {
continue;
}
let isArray = false;
let isStruct = false;
if (varName.includes(" a")) {
isArray = true;
} else if (varName.includes(" s")) {
isStruct = true;
}
const plainText = decrypt(
cipherText,
secretKey.hex(32),
[
decompressStarlightKey(generalise(ephPublicKey))[0].hex(32),
decompressStarlightKey(generalise(ephPublicKey))[1].hex(32),
]
);
let mappingKey = null;
let stateVarId;
let value;
console.log("Decrypted pre-image of commitment for variable name: " + name + ": ");
let salt = generalise(plainText[0]);
console.log(\`\\tSalt: \${salt.integer}\`);
if (isArray){
console.log(\`\\tState variable StateVarId: \${plainText[2]}\`);
mappingKey = generalise(plainText[1]);
console.log(\`\\tMapping Key: \${mappingKey.integer}\`);
let reGenStateVarId = generalise(
utils.mimcHash(
[
generalise(plainText[2]).bigInt,
generalise(plainText[1]).bigInt,
],
"ALT_BN_254"
)
);
stateVarId = reGenStateVarId;
console.log(\`Regenerated StateVarId: \${reGenStateVarId.bigInt}\`);
value = generalise(plainText[3]);
console.log(\`\\tValue: \${value.integer}\`);
} else {
stateVarId = generalise(plainText[1]);
console.log(\`\\tStateVarId: \${plainText[1]}\`);
if (isStruct){
value = {};`;

node.privateStates.forEach((stateVar: any) => {
if (stateVar.structProperties){
let propCount = 2;
genericApiServiceFile += `\nif (stateVarId.integer === '${stateVar.stateVarId}') {`
stateVar.structProperties.forEach((prop: any) => {
genericApiServiceFile += `value.${prop} = plainText[${propCount}];\n`;
propCount++;
});
genericApiServiceFile += `}\n`;
}
});

genericApiServiceFile += `console.log(\`\\tValue: \${value}\`);
} else {
value = generalise(plainText[2]);
console.log(\`\\tValue: \${value.integer}\`);
}
}
let newCommitment;
if (isStruct){
let hashInput = [BigInt(stateVarId.hex(32))];
for (let i = 2; i < plainText.length; i++) {
hashInput.push(BigInt(generalise(plainText[i]).hex(32)));
}
hashInput.push(BigInt(publicKey.hex(32)));
hashInput.push(BigInt(salt.hex(32)));
newCommitment = generalise(poseidonHash(hashInput));
} else {
newCommitment = generalise(poseidonHash([
BigInt(stateVarId.hex(32)),
BigInt(value.hex(32)),
BigInt(publicKey.hex(32)),
BigInt(salt.hex(32)),
]));
}
if (!varName.includes(" u")){
let oldCommitments = storedCommitments.filter(
(element) =>
element.stateVarId.integer === stateVarId.integer &&
(!mappingKey || element.mappingKey === mappingKey?.integer)
);
for (const oldCommitment of oldCommitments){
await markNullified(oldCommitment.hash, secretKey.hex(32));
let index = storedCommitments.findIndex(element => element === oldCommitment);
if (index !== -1) {
storedCommitments.splice(index, 1);
}
}
}
await storeCommitment({
hash: newCommitment,
name: name,
mappingKey: mappingKey?.integer,
preimage: {
stateVarId: stateVarId,
value: value,
salt: salt,
publicKey: publicKey,
},
secretKey: secretKey,
isNullified: false,
});
storedCommitments.push({stateVarId: stateVarId, hash: newCommitment, mappingKey: mappingKey?.integer});
}
};
}`;

// replace references to contract and functions with ours
let outputApiServiceFile = genericApiServiceFile.replace(
/CONTRACT_NAME/g,
node.contractName,
);
return outputApiServiceFile;
};

const prepareBackupDataRetriever = (node: any) => {
// import generic test skeleton
let genericApiServiceFile: any = `/* eslint-disable prettier/prettier, camelcase, prefer-const, no-unused-vars */
Expand Down Expand Up @@ -828,6 +1009,12 @@ export default function fileGenerator(node: any) {
const backupDataRetriever = prepareBackupDataRetriever(node);
return backupDataRetriever;
}

case 'BackupVariableBoilerplate': {
const backupVariable = prepareBackupVariable(node);
return backupVariable;
}

case 'IntegrationEncryptedListenerBoilerplate': {
const encryptedListener = prepareIntegrationEncryptedListener(node);
return encryptedListener;
Expand Down
3 changes: 3 additions & 0 deletions src/codeGenerators/orchestration/nodejs/toOrchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,9 @@ export default function codeGenerator(node: any, options: any = {}): any {
case 'BackupDataRetrieverBoilerplate':
// Separate files are handled by the fileGenerator
return fileGenerator(node);
case 'BackupVariableBoilerplate':
// Separate files are handled by the fileGenerator
return fileGenerator(node);
case 'IntegrationEncryptedListenerBoilerplate':
return fileGenerator(node);
case 'InitialisePreimage':
Expand Down
21 changes: 21 additions & 0 deletions src/transformers/visitors/toOrchestrationVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,17 @@ const visitor = {
],
});
node._newASTPointer.push(newNode);
newNode = buildNode('File', {
fileName: 'BackupVariable',
fileExtension: '.mjs',
nodes: [
buildNode('BackupVariableBoilerplate', {
contractName,
privateStates: [],
}),
],
});
node._newASTPointer.push(newNode);
if (scope.indicators.encryptionRequired) {
newNode = buildNode('File', {
fileName: 'encrypted-data-listener',
Expand Down Expand Up @@ -859,6 +870,16 @@ const visitor = {
file.nodes?.[0].privateStates.push(newNode);
}
}
if (file.nodes?.[0].nodeType === 'BackupVariableBoilerplate') {
let newNode = buildPrivateStateNode('EncryptBackupPreimage', {
privateStateName: name,
id,
indicator: stateVarIndicator,
});
if (!file.nodes?.[0].privateStates.some((n: any) => n.stateVarId === newNode.stateVarId)){
file.nodes?.[0].privateStates.push(newNode);
}
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/types/orchestration-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ export default function buildNode(nodeType: string, fields: any = {}): any {
case 'IntegrationApiServicesBoilerplate':
case 'IntegrationApiRoutesBoilerplate':
case 'BackupDataRetrieverBoilerplate':
case 'BackupVariableBoilerplate':
case 'IntegrationEncryptedListenerBoilerplate':
case 'IntegrationTestFunction':
case 'IntegrationApiServiceFunction':
Expand Down

0 comments on commit 4f45c0c

Please sign in to comment.