diff --git a/circuits/lib/linked/linkId.circom b/circuits/lib/linked/linkId.circom index e7ec76f1..c72d486f 100644 --- a/circuits/lib/linked/linkId.circom +++ b/circuits/lib/linked/linkId.circom @@ -6,7 +6,7 @@ include "../../../node_modules/circomlib/circuits/poseidon.circom"; template LinkID() { signal input claimHash; - signal input linkNonce; + signal input linkNonce; // private random nonce to make the commitment unique and secure signal output out; diff --git a/circuits/lib/query/modifiers.circom b/circuits/lib/query/modifiers.circom index b9fa8347..fb931327 100644 --- a/circuits/lib/query/modifiers.circom +++ b/circuits/lib/query/modifiers.circom @@ -7,6 +7,7 @@ include "../../../node_modules/circomlib/circuits/mux4.circom"; /* Modifier/computation operators: 16 - selective disclosure (16 = 10000 binary) + 17 - value commitment (17 = 10001 binary) */ // modifierValidatorOutputSelector validates modifier operation and selects output value @@ -24,7 +25,8 @@ template modifierValidatorOutputSelector() { modifierOpValid.s <== [opBits[0], opBits[1], opBits[2], opBits[3]]; modifierOpValid.c <== [ 1, // valid operator: 16 - selective disclosure (16-16 = index 0) - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 1, // valid operator: 17 - value commitment (17-1 = index 1) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; ForceEqualIfEnabled()( diff --git a/circuits/lib/query/processQueryWithModifiers.circom b/circuits/lib/query/processQueryWithModifiers.circom index 4dd0fb40..5f51432c 100644 --- a/circuits/lib/query/processQueryWithModifiers.circom +++ b/circuits/lib/query/processQueryWithModifiers.circom @@ -3,6 +3,7 @@ pragma circom 2.1.1; include "../../../node_modules/circomlib/circuits/comparators.circom"; include "query.circom"; include "modifiers.circom"; +include "valueCommitment.circom"; include "../utils/claimUtils.circom"; include "../utils/arraySizeValidator.circom"; @@ -18,6 +19,7 @@ template ProcessQueryWithModifiers(claimLevels, maxValueArraySize){ signal input operator; signal input value[maxValueArraySize]; signal input valueArraySize; // actual size of value array - we don't want zero filled arrays to cause false positives for 0 as input to IN/NIN operators + signal input commitNonce; signal input issuerClaim[8]; signal input merklized; @@ -111,6 +113,9 @@ template ProcessQueryWithModifiers(claimLevels, maxValueArraySize){ // selective disclosure // no need to calc anything, fieldValue is just passed as an output + // value commitment + signal valueCommitment <== ValueCommitment()(fieldValue, commitNonce); + ///////////////////////////////////////////////////////////////// // Modifier Operator Validation & Output Preparation ///////////////////////////////////////////////////////////////// @@ -120,7 +125,8 @@ template ProcessQueryWithModifiers(claimLevels, maxValueArraySize){ operator <== operator, modifierOutputs <== [ fieldValue, // 16 - selective disclosure (16-16 = index 0) - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 17-31 - not used + valueCommitment, // 17 - value commitment (17-16 = index 1) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 18-31 - not used ] ); } \ No newline at end of file diff --git a/circuits/lib/query/query.circom b/circuits/lib/query/query.circom index 27216e73..c4c06c4b 100644 --- a/circuits/lib/query/query.circom +++ b/circuits/lib/query/query.circom @@ -23,6 +23,7 @@ include "comparators.circom"; 11 - exists (true / false) Modifier/computation operators: 16 - selective disclosure (16 = 10000 binary) + 17 - value commitment (17 = 10001 binary) */ // Query template works only with Query operators (0-15). diff --git a/circuits/lib/query/valueCommitment.circom b/circuits/lib/query/valueCommitment.circom new file mode 100644 index 00000000..da5a0aff --- /dev/null +++ b/circuits/lib/query/valueCommitment.circom @@ -0,0 +1,27 @@ +pragma circom 2.1.1; + +include "../../../node_modules/circomlib/circuits/mux1.circom"; +include "../../../node_modules/circomlib/circuits/comparators.circom"; +include "../../../node_modules/circomlib/circuits/poseidon.circom"; + +/** +Value commitment circuit allows to commit to a specific value and then +reveal it later or use such a commitment in another circuits to prove that +multiple circuits work with the same value without revealing it. +*/ + +template ValueCommitment() { + signal input value; + signal input commitNonce; // private random nonce to make the commitment unique and secure + + signal output out; + + signal isNonceZero <== IsZero()(commitNonce); + + signal commit <== Poseidon(2)([value, commitNonce]); + + out <== Mux1()( + [commit, 0], + isNonceZero + ); +} diff --git a/circuits/linked/multiQuery.circom b/circuits/linked/multiQuery.circom index c38b6f7b..88bf2859 100644 --- a/circuits/linked/multiQuery.circom +++ b/circuits/linked/multiQuery.circom @@ -26,6 +26,7 @@ template LinkedMultiQuery(N, claimLevels, maxValueArraySize) { signal input operator[N]; signal input value[N][maxValueArraySize]; signal input valueArraySize[N]; + signal input commitNonce[N]; // Outputs signal output linkID; @@ -68,7 +69,7 @@ template LinkedMultiQuery(N, claimLevels, maxValueArraySize) { ///////////////////////////////////////////////////////////////// for (var i=0; i { + const inputs = { value: "5", commitNonce: "7" }; + const expectedOut = { out: "21007229687521157814825902919006068496120320911167801732994749038798743998593" }; + + const witness = await valueCommitmentCircuit.calculateWitness(inputs, true); + await valueCommitmentCircuit.assertOut(witness, expectedOut); + await valueCommitmentCircuit.checkConstraints(witness); + }); + + it("should return zero for a commit with zero nonce", async () => { + const inputs = { value: "10", commitNonce: "0" }; + const expectedOut = { out: "0" }; + + const witness = await valueCommitmentCircuit.calculateWitness(inputs, true); + await valueCommitmentCircuit.assertOut(witness, expectedOut); + await valueCommitmentCircuit.checkConstraints(witness); + }); +}); \ No newline at end of file