Skip to content

Commit

Permalink
Merge pull request #273 from EYBlockchain/lydia/restoreDB
Browse files Browse the repository at this point in the history
Restore Lost database
  • Loading branch information
SwatiEY authored Jun 26, 2024
2 parents 492bab5 + 3914e46 commit 1bad441
Show file tree
Hide file tree
Showing 23 changed files with 712 additions and 137 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,9 @@ 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`.


#### Using secret states in the constructor

Starlight handles secret initiation in the constructor by creating a proof at the setup stage. Any user supplied inputs will be prompted for in the command line when running `./bin/setup`.
Expand Down
12 changes: 9 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/boilerplate/common/generic-test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('FUNCTION_NAME', async function () {
await startEventFilter('CONTRACT_NAME');
// this calls your function! It returns the tx from the shield contract
// you can replace the values below - numbers are randomly generated
const { tx , encEvent } = await FUNCTION_NAME(FUNCTION_SIG_1);
const { tx , encEvent, encBackupEvent } = await FUNCTION_NAME(FUNCTION_SIG_1);
// prints the tx
console.log(tx);
// reassigns leafIndex to the index of the first commitment added by this function
Expand Down
51 changes: 49 additions & 2 deletions src/boilerplate/common/number-theory.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,34 @@ function compressStarlightKey(publicKeyPoint) {
return publicKey;
}

/**
Encrypt messages encrypted with KEM-DEM
@param {string[]} Plaintext - hex string[]
@param {string} SendersecretKey - hex string
@param {string[2]} RecieverPublicKey - hex string[]
@return {string[]} plainText - int string[]
*/
function encrypt(plaintext, secretKey, recieverPublicKey) {
const encryptedMessages = [];
const sharedSecret = scalarMult(secretKey, [
BigInt(recieverPublicKey[0]),
BigInt(recieverPublicKey[1]),
]);
const key = poseidonHash([
sharedSecret[0],
sharedSecret[1],
BigInt(DOMAIN_KEM),
]);
plaintext.forEach((msg, index) => {
const hash = poseidonHash([key.bigInt, BigInt(DOMAIN_DEM), BigInt(index)]);
encryptedMessages[index] = addMod([BigInt(msg), hash.bigInt], Fp);
while (encryptedMessages[index] < 0n) {
encryptedMessages[index] += Fp;
}
});
return encryptedMessages;
}

/**
Decrypt messages encrypted with KEM-DEM
@param {string[]} encryptedMessages - hex string[]
Expand Down Expand Up @@ -368,7 +396,8 @@ function sbox(state, f, p, r) {
const N = state.length;
state[0] = powerMod(state[0], 5n, q);
for (let i = 1; i < N; i++) {
state[i] = r < f / 2 || r >= f / 2 + p ? powerMod(state[i], 5n, q) : state[i];
state[i] =
r < f / 2 || r >= f / 2 + p ? powerMod(state[i], 5n, q) : state[i];
}
return state;
}
Expand All @@ -391,7 +420,24 @@ function poseidonHash(_inputs) {
const inputs = _inputs;
const N = inputs.length;
const t = N + 1;
const roundsP = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68];
const roundsP = [
56,
57,
56,
60,
60,
63,
64,
63,
60,
66,
60,
65,
70,
60,
64,
68,
];
const f = 8;
const p = roundsP[t - 2];
const c = C[t - 2];
Expand Down Expand Up @@ -421,6 +467,7 @@ export {
scalarMult,
compressStarlightKey,
decompressStarlightKey,
encrypt,
decrypt,
poseidonHash,
sharedSecretKey,
Expand Down
4 changes: 2 additions & 2 deletions src/boilerplate/common/services/generic-api_services.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ export async function service_FUNCTION_NAME (req, res, next){
await startEventFilter('CONTRACT_NAME');
const FUNCTION_SIG;
CONSTRUCTOR_INPUTS;
const { tx , encEvent, _RESPONSE_} = await FUNCTION_NAME(FUNCTION_SIG);
const { tx , encEvent, encBackupEvent, _RESPONSE_} = await FUNCTION_NAME(FUNCTION_SIG);
// prints the tx
console.log(tx);
res.send({tx, encEvent, _RESPONSE_});
res.send({tx, encEvent, encBackupEvent, _RESPONSE_});
// reassigns leafIndex to the index of the first commitment added by this function
if (tx.event) {
leafIndex = tx.returnValues[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class ContractBoilerplateGenerator {
delete parameterList[ paramtype ];
}
const newList: string[] = [];

params?.forEach(circuitParamNode => {
switch (circuitParamNode.bpType) {
case 'nullification':
Expand All @@ -144,8 +145,9 @@ class ContractBoilerplateGenerator {
if (!newList.includes(circuitParamNode.bpType)) newList.push(circuitParamNode.bpType);
break;
case 'encryption':
returnpara['returnParameters'] ??= [];
returnpara['returnParameters'].push(circuitParamNode.bpType);

returnpara['encryptionParameters'] ??= [];
returnpara['encryptionParameters'].push(circuitParamNode.bpType);
break;
case undefined: {
if (
Expand Down Expand Up @@ -176,6 +178,7 @@ class ContractBoilerplateGenerator {
parameterList = {...parameterList, ...returnpara};
}
circuitParams[ functionName ] = parameterList;

}
const constructorContainsSecret = Object.values(this.scope.bindings).some((binding: any) => binding.node.kind === 'constructor');
return { nullifiersRequired, oldCommitmentAccessRequired, newCommitmentsRequired, containsAccessedOnlyState, encryptionRequired, constructorContainsSecret, circuitParams, isjoinSplitCommitmentsFunction};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,17 @@ class FunctionBoilerplateGenerator {

path.node._newASTPointer.body.statements?.forEach((node) => {
if(node.expression?.nodeType === 'InternalFunctionCall'){
if(node.expression.parameters.includes('cipherText') )
internalFunctionEncryptionRequired = true
if(node.expression.encryptionRequired)
internalFunctionEncryptionRequired = true;

}

})


if(path.node.returnParameters.parameters.length === 0 && !indicators.encryptionRequired && !internalFunctionEncryptionRequired) {
if(path.node.returnParameters.parameters.length === 0 && !indicators.encryptionRequired && !internalFunctionEncryptionRequired){
publicParams?.push({ name: 1, type: 'uint256', dummy: true , inCircuit: true });
}

return {
...(publicParams?.length && { customInputs: publicParams }),
functionName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,16 @@ class ContractBoilerplateGenerator {
...(encryptionRequired ? [`
event EncryptedData(uint256[] cipherText, uint256[2] ephPublicKey);`] : []),

...nullifiersRequired ? [`
...(newCommitmentsRequired ? [`
struct BackupDataElement {
string varName;
uint256[] cipherText;
uint256 ephPublicKey;
} \n
event EncryptedBackupData(BackupDataElement[] encPreimages);
`] : []),

...nullifiersRequired ? [`
uint256 public newNullifierRoot;`] : [],

...(oldCommitmentAccessRequired ? [`
Expand Down Expand Up @@ -121,7 +130,6 @@ class ContractBoilerplateGenerator {
let verifyInput: string[] = [];

const verifyInputsMap = (type: string, input: string, counter: any) => {

if(type === 'parameters'){
switch (input) {
case 'nullifierRoot':
Expand Down Expand Up @@ -150,8 +158,7 @@ class ContractBoilerplateGenerator {
break;
}
}
else if(type === 'returnParameters') {

else if(type === 'returnParameters' || type === 'encryptionParameters') {
switch (input) {
case 'encryption':
verifyInput.push( `
Expand Down Expand Up @@ -222,13 +229,13 @@ class ContractBoilerplateGenerator {
newCommitments: 0,
encryption: 0,
};
_inputs.map(i => verifyInputsMap(type, i, counter));
_inputs?.map(i => verifyInputsMap(type, i, counter));



}

if(_params && !(Object.keys(_params).includes('returnParameters'))) verifyInput.push(`
if(_params && !(Object.keys(_params).includes('returnParameters')) &&!(Object.keys(_params).includes('encryptionParameters'))) verifyInput.push(`
inputs[k++] = 1;`)

verifyInputs.push(`
Expand Down
104 changes: 51 additions & 53 deletions src/boilerplate/contract/solidity/raw/FunctionBoilerplateGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ class FunctionBoilerplateGenerator {
if (isConstructor && encryptionRequired) throw new Error(`There shouldn't be any secret states that require sharing encrypted data in the constructor.`)
const visibility = isConstructor ? 'memory' : 'calldata';
return [
...(newNullifiers ? [`uint256 nullifierRoot, uint256 latestNullifierRoot,uint256[] ${visibility} newNullifiers`] : []), // nullifiers and nullifier root exist together
...(commitmentRoot ? [`uint256 commitmentRoot`] : []),
...(newCommitments ? [`uint256[] ${visibility} newCommitments`] : []),
...(encryptionRequired ? [`uint256[][] calldata cipherText`] : []),
...(encryptionRequired ? [`uint256[2][] calldata ephPubKeys`] : []),
...(newCommitments || newNullifiers ? [`uint256[] ${visibility} proof`] : []),
// ...(newNullifiers ? [`uint256 nullifierRoot, uint256 latestNullifierRoot,uint256[] ${visibility} newNullifiers`] : []), // nullifiers and nullifier root exist together
// ...(commitmentRoot ? [`uint256 commitmentRoot`] : []),
// ...(newCommitments ? [`uint256[] ${visibility} newCommitments`] : []),
// ...(encryptionRequired ? [`uint256[][] calldata cipherText`] : []),
// ...(encryptionRequired ? [`uint256[2][] calldata ephPubKeys`] : []),
...(newCommitments || newNullifiers ? [`Inputs ${visibility} inputs, uint256[] ${visibility} proof, BackupDataElement[] memory BackupData`] : []),
];
},

Expand All @@ -64,20 +64,13 @@ class FunctionBoilerplateGenerator {
encryptionRequired
}): string[] {
// prettier-ignore

let parameter = [
...(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[]`] : []),
...(commitmentRoot ? [`uint256`] : []),
...(newCommitments ? [`uint256[]`] : []),
...(encryptionRequired ? [`uint256[][]`] : []),
...(encryptionRequired ? [`uint256[2][]`] : []),
`(uint256,uint256,uint256[],uint256,uint256[],uint256[])`,
`uint256[]`,
`(string,uint256[],uint256)[]`,
].filter(para => para !== undefined); // Added for return parameter

customInputs?.forEach((input, i) => {
if (input.isConstantArray) {
const expanded = [];
Expand All @@ -93,60 +86,65 @@ class FunctionBoilerplateGenerator {
}
if (input.structName) customInputs[i] = input.properties;
});


let msgSigCheck = ([...(isConstructor ? [] : [`bytes4 sig = bytes4(keccak256("${functionName}(${parameter})")) ; \n \t \t \t if (sig == msg.sig)`])]);

customInputs = customInputs?.flat(Infinity).filter(p => p.inCircuit);

customInputs = customInputs?.flat(Infinity).filter(p => (p.inCircuit || p.isReturn));

const addCustomInputs = !customInputs || (customInputs?.length == 1 && customInputs[0].name == '1') ? false : true;
return [
`
Inputs memory inputs;`,

...(customInputs?.length ?
// `
// Inputs memory inputs;`,
...(addCustomInputs ?
[`
inputs.customInputs = new uint[](${customInputs.flat(Infinity).length});
Inputs memory updatedInputs = inputs;
updatedInputs.customInputs = new uint[](${customInputs.flat(Infinity).length});
${customInputs.flat(Infinity).map((input: any, i: number) => {
if (input.type === 'address') return `inputs.customInputs[${i}] = uint256(uint160(address(${input.name})));`;
if ((input.type === 'bool' || input.typeName?.name === 'bool' ) && !['0', '1'].includes(input.name)) return `inputs.customInputs[${i}] = ${input.name} == false ? 0 : 1;`;
return `inputs.customInputs[${i}] = ${input.name};`;
}).join('\n')}`]
: []),
if (input.type === 'address') return `updatedInputs.customInputs[${i}] = uint256(uint160(address(${input.name})));`;
if ((input.type === 'bool' || input.typeName?.name === 'bool' ) && !['0', '1'].includes(input.name)) return `updatedInputs.customInputs[${i}] = ${input.name} == false ? 0 : 1;`;
if (input.isCommitment) return ``;
return `updatedInputs.customInputs[${i}] = ${input.name};`;
}).join('\n')}
${msgSigCheck.join('\n')}
verify(proof, uint(FunctionNames.${functionName}), updatedInputs);`]
: [`${msgSigCheck.join('\n')}
verify(proof, uint(FunctionNames.${functionName}), inputs);`]),

...(newNullifiers ? [`
inputs.nullifierRoot = nullifierRoot; `] : []),
// ...(newNullifiers ? [`
// // inputs.nullifierRoot = nullifierRoot; `] : []),

...(newNullifiers ? [`
inputs.latestNullifierRoot = latestNullifierRoot; `] : []),
// ...(newNullifiers ? [`
// inputs.latestNullifierRoot = latestNullifierRoot; `] : []),


...(newNullifiers ? [`
inputs.newNullifiers = newNullifiers;
`] : []),
// ...(newNullifiers ? [`
// inputs.newNullifiers = newNullifiers;
// `] : []),

...(commitmentRoot ? [`
inputs.commitmentRoot = commitmentRoot;`] : []),
// ...(commitmentRoot ? [`
// inputs.commitmentRoot = commitmentRoot;`] : []),

...(newCommitments ? [`
inputs.newCommitments = newCommitments;`] : []),
// ...(newCommitments ? [`
// inputs.newCommitments = newCommitments;`] : []),

...(encryptionRequired ? [`
inputs.cipherText = cipherText;`] : []),

...(encryptionRequired ? [`
inputs.encKeys = ephPubKeys;`] : []),
`
${msgSigCheck.join('\n')}`,
`
verify(proof, uint(FunctionNames.${functionName}), inputs);`,
// ...(encryptionRequired ? [`
// inputs.cipherText = cipherText;`] : []),

// ...(encryptionRequired ? [`
// inputs.encKeys = ephPubKeys;`] : []),

...(encryptionRequired ? [`
for (uint j; j < cipherText.length; j++) {
for (uint j; j < inputs.cipherText.length; j++) {
// this seems silly (it is) but its the only way to get the event to emit properly
uint256[2] memory ephKeyToEmit = ephPubKeys[j];
uint256[] memory cipherToEmit = cipherText[j];
uint256[2] memory ephKeyToEmit = inputs.encKeys[j];
uint256[] memory cipherToEmit = inputs.cipherText[j];
emit EncryptedData(cipherToEmit, ephKeyToEmit);
}`] : []),
}`]
: []),
...(newCommitments ? [`
// this seems silly (it is) but its the only way to get the event to emit properly
emit EncryptedBackupData(BackupData);`]
: []),
];
},
};
Expand Down
Loading

0 comments on commit 1bad441

Please sign in to comment.