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

Add valueArraySize to multiQuery circuit #124

Merged
merged 6 commits into from
Feb 26, 2024
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion circuits/credentialAtomicQueryV3.circom
Original file line number Diff line number Diff line change
@@ -16,7 +16,6 @@ component main{public [requestID,
claimSchema,
slotIndex,
claimPathKey,
claimPathNotExists,
operator,
value,
valueArraySize,
5 changes: 1 addition & 4 deletions circuits/credentialAtomicQueryV3OnChain.circom
Original file line number Diff line number Diff line change
@@ -14,11 +14,8 @@ component main{public [requestID,
issuerID,
issuerClaimNonRevState,
timestamp,
isRevocationChecked,
challenge,
gistRoot,
proofType,
verifierID,
nullifierSessionID,
authEnabled
isBJJAuthEnabled
]} = credentialAtomicQueryV3OnChain(40, 32, 64, 40, 64);
15 changes: 12 additions & 3 deletions circuits/lib/query/processQueryWithModifiers.circom
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ include "../utils/arraySizeValidator.circom";

template ProcessQueryWithModifiers(claimLevels, maxValueArraySize){
signal input enabled;
signal input claimPathNotExists; // 0 for inclusion, 1 for non-inclusion
signal input claimPathMtp[claimLevels];
signal input claimPathMtpNoAux; // 1 if aux node is empty, 0 if non-empty or for inclusion proofs
signal input claimPathMtpAuxHi; // 0 for inclusion proof
@@ -27,11 +26,14 @@ template ProcessQueryWithModifiers(claimLevels, maxValueArraySize){
// Modifier/Computation Operator output ($sd)
signal output operatorOutput;

signal smtEnabled <== AND()(enabled, merklized);
signal operatorNotNoop <== NOT()(IsZero()(operator));
signal merklizedAndEnabled <== AND()(enabled, merklized);

signal claimPathNotExists <== IsEqual()([operator, 12]); // for non-exist operator 1, else 0

// check path/in node exists in merkletree specified by jsonldRoot
SMTVerifier(claimLevels)(
enabled <== smtEnabled, // if merklize flag 0 or enabled 0 skip MTP verification
enabled <== AND()(merklizedAndEnabled, operatorNotNoop), // if merklize flag 0 or enabled 0 or NOOP operation skip MTP verification
fnc <== claimPathNotExists, // inclusion
root <== merklizedRoot,
siblings <== claimPathMtp,
@@ -53,6 +55,13 @@ template ProcessQueryWithModifiers(claimLevels, maxValueArraySize){
merklized
);

// For non-merklized credentials exists / non-exist operators don't work
signal operatorNotEqExistOrNotExist <== NOT()(OR()(IsEqual()([operator, 11]), IsEqual()([operator, 12])));
ForceEqualIfEnabled()(
AND()(enabled, NOT()(merklized)),
[1, operatorNotEqExistOrNotExist]
);

/////////////////////////////////////////////////////////////////
// Query Operator Processing
/////////////////////////////////////////////////////////////////
6 changes: 4 additions & 2 deletions circuits/lib/query/query.circom
Original file line number Diff line number Diff line change
@@ -20,6 +20,8 @@ include "comparators.circom";
8 - greater than or equal
9 - between
10 - not between
11 - exist
12 - not exist
Modifier/computation operators:
16 - selective disclosure (16 = 10000 binary)
*/
@@ -76,8 +78,8 @@ template Query (maxValueArraySize) {
queryOpSatisfied.c[8] <== gte; // gte === !lt
queryOpSatisfied.c[9] <== between; // between
queryOpSatisfied.c[10] <== NOT()(between); // not between
queryOpSatisfied.c[11] <== 0; // not used
queryOpSatisfied.c[12] <== 0; // not used
queryOpSatisfied.c[11] <== 1; // exist; skip execution
queryOpSatisfied.c[12] <== 1; // not exist; skip execution
queryOpSatisfied.c[13] <== 0; // not used
queryOpSatisfied.c[14] <== 0; // not used
queryOpSatisfied.c[15] <== 0; // not used
6 changes: 4 additions & 2 deletions circuits/lib/utils/arraySizeValidator.circom
Original file line number Diff line number Diff line change
@@ -19,6 +19,8 @@ include "../query/comparators.circom";
8 - greater than or equal - 1 element
9 - between - 2 elements
10 - not between - 2 elements
11 - exist - 0 elements
12 - not exist - 0 elements
Modifier/computation operators:
16 - selective disclosure (16 = 10000 binary) - 0 elements
17-31 - 0 elements
@@ -62,8 +64,8 @@ template ArraySizeValidator (maxValueArraySize) {
mux.c[8] <== sizeEqOne; // gte
mux.c[9] <== sizeEqTwo; // between
mux.c[10] <== sizeEqTwo; // not between
mux.c[11] <== sizeEqZero; // not used
mux.c[12] <== sizeEqZero; // not used
mux.c[11] <== sizeEqZero; // exist
mux.c[12] <== sizeEqZero; // not exist
mux.c[13] <== sizeEqZero; // not used
mux.c[14] <== sizeEqZero; // not used
mux.c[15] <== sizeEqZero; // not used
41 changes: 41 additions & 0 deletions circuits/lib/utils/queryHash.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
pragma circom 2.1.1;
include "./spongeHash.circom";

template QueryHash(maxValueArraySize) {
signal input value[maxValueArraySize];
signal input claimSchema;
signal input slotIndex;
signal input operator;
signal input claimPathKey;
signal input claimPathNotExists;
signal input valueArraySize;
signal input merklized;
signal input verifierID;
signal input isRevocationChecked;
signal input nullifierSessionID;

signal output out;

/////////////////////////////////////////////////////////////////
// Calculate query hash
/////////////////////////////////////////////////////////////////
// 4950 constraints (SpongeHash+Poseidon)
signal valueHash <== SpongeHash(maxValueArraySize, 6)(value); // 6 - max size of poseidon hash available on-chain
signal firstPartQueryHash <== Poseidon(6)([
claimSchema,
slotIndex,
operator,
claimPathKey,
claimPathNotExists,
valueHash
]);

out <== Poseidon(6)([
firstPartQueryHash,
valueArraySize,
merklized,
verifierID,
isRevocationChecked,
vmidyllic marked this conversation as resolved.
Show resolved Hide resolved
nullifierSessionID
]);
}
40 changes: 22 additions & 18 deletions circuits/linked/multiQuery.circom
Original file line number Diff line number Diff line change
@@ -5,19 +5,17 @@ include "../lib/query/processQueryWithModifiers.circom";
include "../lib/linked/linkId.circom";
include "../lib/utils/claimUtils.circom";
include "../lib/utils/safeOne.circom";
include "../lib/utils/spongeHash.circom";
include "../lib/utils/queryHash.circom";

// This circuit processes multiple query requests at once for a given claim using linked proof
template LinkedMultiQuery(N, claimLevels, valueArraySize) {
template LinkedMultiQuery(N, claimLevels, maxValueArraySize) {

// linked proof signals
signal input linkNonce;
signal input issuerClaim[8];

// query signals
signal input enabled[N]; // 1 if query non-empty, 0 to skip query check
signal input claimSchema;
signal input claimPathNotExists[N]; // 0 for inclusion, 1 for non-inclusion
signal input claimPathMtp[N][claimLevels];
signal input claimPathMtpNoAux[N]; // 1 if aux node is empty, 0 if non-empty or for inclusion proofs
signal input claimPathMtpAuxHi[N]; // 0 for inclusion proof
@@ -26,7 +24,8 @@ template LinkedMultiQuery(N, claimLevels, valueArraySize) {
signal input claimPathValue[N]; // value in this path in merklized json-ld document
signal input slotIndex[N];
signal input operator[N];
signal input value[N][valueArraySize];
signal input value[N][maxValueArraySize];
signal input valueArraySize[N];

// Outputs
signal output linkID;
@@ -55,25 +54,25 @@ template LinkedMultiQuery(N, claimLevels, valueArraySize) {
// Verify issuerClaim schema
verifyCredentialSchema()(one, issuerClaimHeader.schema, claimSchema); // 3 constraints

signal valueHash[N];
signal queryHash[N];

signal issuerClaimHash, issuerClaimHi, issuerClaimHv;
(issuerClaimHash, issuerClaimHi, issuerClaimHv) <== getClaimHash()(issuerClaim); // 834 constraints
////////////////////////////////////////////////////////////////////////
// calculate linkID
////////////////////////////////////////////////////////////////////////
linkID <== LinkID()(issuerClaimHash, linkNonce); // 243 constraints


signal operatorNotNoop[N];
signal claimPathNotExists[N];
/////////////////////////////////////////////////////////////////
// Query Processing Loop
/////////////////////////////////////////////////////////////////
for (var i=0; i<N; i++) {

operatorNotNoop[i] <== NOT()(IsZero()(operator[i]));

// output value only if modifier operation was selected
operatorOutput[i] <== ProcessQueryWithModifiers(claimLevels, valueArraySize)(
enabled[i],
claimPathNotExists[i],
operatorOutput[i] <== ProcessQueryWithModifiers(claimLevels, maxValueArraySize)(
operatorNotNoop[i], // enabled
claimPathMtp[i],
claimPathMtpNoAux[i],
claimPathMtpAuxHi[i],
@@ -83,6 +82,7 @@ template LinkedMultiQuery(N, claimLevels, valueArraySize) {
slotIndex[i],
operator[i],
value[i],
valueArraySize[i],
issuerClaim,
merklized,
merklize.out
@@ -92,16 +92,20 @@ template LinkedMultiQuery(N, claimLevels, valueArraySize) {
// Calculate query hash
/////////////////////////////////////////////////////////////////
// 4950 constraints (SpongeHash+Poseidon)
valueHash[i] <== SpongeHash(valueArraySize, 6)(value[i]); // 6 - max size of poseidon hash available on-chain
queryHash[i] <== Poseidon(6)([
claimPathNotExists[i] <== IsEqual()([operator[i], 12]); // for non-exist operator 1, else 0
circuitQueryHash[i] <== QueryHash(maxValueArraySize)(
value[i],
claimSchema,
slotIndex[i],
operator[i],
claimPathKey[i],
claimPathNotExists[i],
valueHash[i]
]);

circuitQueryHash[i] <== Mux1()([0, queryHash[i]], enabled[i]);
valueArraySize[i],
merklized,
0,
0,
0
);
}

}
2 changes: 1 addition & 1 deletion circuits/linkedMultiQuery10.circom
Original file line number Diff line number Diff line change
@@ -2,4 +2,4 @@ pragma circom 2.1.1;

include "linked/multiQuery.circom";

component main {public [enabled]} = LinkedMultiQuery(10, 32, 64); // 164791 constraints
component main {public [valueArraySize]} = LinkedMultiQuery(10, 32, 64); // 164791 constraints
27 changes: 0 additions & 27 deletions circuits/offchain/credentialAtomicQueryV3OffChain.circom
Original file line number Diff line number Diff line change
@@ -41,7 +41,6 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, maxValueArra
/** Query */
signal input claimSchema;

signal input claimPathNotExists; // 0 for inclusion, 1 for non-inclusion
signal input claimPathMtp[claimLevels];
signal input claimPathMtpNoAux; // 1 if aux node is empty, 0 if non-empty or for inclusion proofs
signal input claimPathMtpAuxHi; // 0 for inclusion proof
@@ -209,38 +208,12 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, maxValueArra

merklized <== merklize.flag;

// check path/in node exists in merkletree specified by jsonldRoot
signal operatorNotNoop <== NOT()(IsZero()(operator));
SMTVerifier(claimLevels)(
enabled <== AND()(merklize.flag, operatorNotNoop), // if merklize flag 0 or NOOP operator skip MTP verification
fnc <== claimPathNotExists, // inclusion
root <== merklize.out,
siblings <== claimPathMtp,
oldKey <== claimPathMtpAuxHi,
oldValue <== claimPathMtpAuxHv,
isOld0 <== claimPathMtpNoAux,
key <== claimPathKey,
value <== claimPathValue
); // 9585 constraints

// select value from claim by slot index (0-7)
signal slotValue <== getValueByIndex()(issuerClaim, slotIndex);

// select value for query verification,
// if claim is merklized merklizeFlag = `1|2`, take claimPathValue
// if not merklized merklizeFlag = `0`, take value from selected slot
signal fieldValue <== Mux1()(
[slotValue, claimPathValue],
merklize.flag
);

/////////////////////////////////////////////////////////////////
// Process Query with Modifiers
/////////////////////////////////////////////////////////////////
// output value only if modifier operation was selected
operatorOutput <== ProcessQueryWithModifiers(claimLevels, maxValueArraySize)(
one,
claimPathNotExists,
claimPathMtp,
claimPathMtpNoAux,
claimPathMtpAuxHi,
38 changes: 21 additions & 17 deletions circuits/onchain/credentialAtomicQueryV3OnChain.circom
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ include "../lib/query/query.circom";
include "../lib/utils/idUtils.circom";
include "../lib/utils/spongeHash.circom";
include "../offchain/credentialAtomicQueryV3OffChain.circom";
include "../lib/utils/queryHash.circom";

/**
credentialAtomicQueryV3OnChain.circom - query claim value and verify claim issuer signature or mtp:
@@ -31,13 +32,6 @@ idOwnershipLevels - Merkle tree depth level for personal claims
onChainLevels - Merkle tree depth level for Auth claim on-chain
*/
template credentialAtomicQueryV3OnChain(issuerLevels, claimLevels, maxValueArraySize, idOwnershipLevels, onChainLevels) {
// flag indicates if merklized flag set in issuer claim (if set MTP is used to verify that
// claimPathValue and claimPathKey are stored in the merkle tree) and verification is performed
// on root stored in the index or value slot
// if it is not set verification is performed on according to the slotIndex. Value selected from the
// provided slot. For example if slotIndex is `1` value gets from `i_1` slot. If `4` from `v_1`.
signal output merklized;

// userID output signal will be assigned with ProfileID SelectProfile(UserGenesisID, nonce)
// unless nonce == 0, in which case userID will be assigned with userGenesisID
signal output userID;
@@ -113,7 +107,6 @@ template credentialAtomicQueryV3OnChain(issuerLevels, claimLevels, maxValueArray
/** Query */
signal input claimSchema;

signal input claimPathNotExists; // 0 for inclusion, 1 for non-inclusion
signal input claimPathMtp[claimLevels];
signal input claimPathMtpNoAux; // 1 if aux node is empty, 0 if non-empty or for inclusion proofs
signal input claimPathMtpAuxHi; // 0 for inclusion proof
@@ -168,16 +161,23 @@ template credentialAtomicQueryV3OnChain(issuerLevels, claimLevels, maxValueArray
signal output operatorOutput;

// Enabled/disable checkAuthV2 verification
signal input authEnabled;
signal input isBJJAuthEnabled;

// flag indicates if merklized flag set in issuer claim (if set MTP is used to verify that
// claimPathValue and claimPathKey are stored in the merkle tree) and verification is performed
// on root stored in the index or value slot
// if it is not set verification is performed on according to the slotIndex. Value selected from the
// provided slot. For example if slotIndex is `1` value gets from `i_1` slot. If `4` from `v_1`.
signal merklized;

/////////////////////////////////////////////////////////////////
// Auth check
/////////////////////////////////////////////////////////////////

ForceEqualIfEnabled()(NOT()(authEnabled), [profileNonce, 0]);
ForceEqualIfEnabled()(NOT()(isBJJAuthEnabled), [profileNonce, 0]);

checkAuthV2(idOwnershipLevels, onChainLevels)(
authEnabled, // enabled
isBJJAuthEnabled, // enabled
userGenesisID,
userState, // user state
userClaimsTreeRoot,
@@ -222,7 +222,6 @@ template credentialAtomicQueryV3OnChain(issuerLevels, claimLevels, maxValueArray
issuerClaimNonRevState <== issuerClaimNonRevState,
timestamp <== timestamp,
claimSchema <== claimSchema,
claimPathNotExists <== claimPathNotExists,
claimPathMtp <== claimPathMtp,
claimPathMtpNoAux <== claimPathMtpNoAux,
claimPathMtpAuxHi <== claimPathMtpAuxHi,
@@ -260,15 +259,20 @@ template credentialAtomicQueryV3OnChain(issuerLevels, claimLevels, maxValueArray
/////////////////////////////////////////////////////////////////
// Verify query hash matches
/////////////////////////////////////////////////////////////////
signal valueHash <== SpongeHash(maxValueArraySize, 6)(value); // 6 - max size of poseidon hash available on-chain
signal claimPathNotExists <== IsEqual()([operator, 12]); // for non-exist operator 1, else 0

circuitQueryHash <== Poseidon(7)([
circuitQueryHash <== QueryHash(maxValueArraySize)(
value,
claimSchema,
slotIndex,
operator,
claimPathKey,
claimPathNotExists,
valueHash,
valueArraySize
]);
valueArraySize,
merklized,
verifierID,
isRevocationChecked,
nullifierSessionID
);

}
Loading
Loading