diff --git a/circuits/lib/query/processQueryWithModifiers.circom b/circuits/lib/query/processQueryWithModifiers.circom new file mode 100644 index 00000000..8636546a --- /dev/null +++ b/circuits/lib/query/processQueryWithModifiers.circom @@ -0,0 +1,92 @@ +pragma circom 2.1.1; + +include "../../../node_modules/circomlib/circuits/comparators.circom"; +include "query.circom"; +include "modifiers.circom"; +include "../utils/claimUtils.circom"; + +template ProcessQueryWithModifiers(claimLevels, valueArraySize){ + 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 + signal input claimPathMtpAuxHv; // 0 for inclusion proof + signal input claimPathKey; // hash of path in merklized json-ld document + signal input claimPathValue; // value in this path in merklized json-ld document + signal input slotIndex; + signal input operator; + signal input value[valueArraySize]; + + signal input issuerClaim[8]; + signal input merklized; + signal input merklizedRoot; + + // Modifier/Computation Operator output ($sd) + signal output operatorOutput; + + signal smtEnabled <== AND()(enabled, merklized); + + // check path/in node exists in merkletree specified by jsonldRoot + SMTVerifier(claimLevels)( + enabled <== smtEnabled, // if merklize flag 0 or enabled 0 skip MTP verification + fnc <== claimPathNotExists, // inclusion + root <== merklizedRoot, + 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], + merklized + ); + + ///////////////////////////////////////////////////////////////// + // Query Operator Processing + ///////////////////////////////////////////////////////////////// + + // verify query + // 1756 constraints (Query+LessThan+ForceEqualIfEnabled) + signal querySatisfied <== Query(valueArraySize)( + in <== fieldValue, + value <== value, + operator <== operator + ); + + signal isQueryOp <== LessThan(5)([operator, 16]); + signal querySatisfiedEnabled <== AND()(enabled, isQueryOp); + ForceEqualIfEnabled()( + querySatisfiedEnabled, + [querySatisfied, 1] + ); + + ///////////////////////////////////////////////////////////////// + // Modifier/Computation Operators Processing + ///////////////////////////////////////////////////////////////// + + // selective disclosure + // no need to calc anything, fieldValue is just passed as an output + + ///////////////////////////////////////////////////////////////// + // Modifier Operator Validation & Output Preparation + ///////////////////////////////////////////////////////////////// + + // output value only if modifier operation was selected + operatorOutput <== modifierValidatorOutputSelector()( + 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 + ] + ); +} \ No newline at end of file diff --git a/circuits/linked/multiQuery.circom b/circuits/linked/multiQuery.circom new file mode 100644 index 00000000..49d3f553 --- /dev/null +++ b/circuits/linked/multiQuery.circom @@ -0,0 +1,107 @@ +pragma circom 2.1.5; + +include "../../node_modules/circomlib/circuits/comparators.circom"; +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"; + +// This circuit processes multiple query requests at once for a given claim using linked proof +template LinkedMultiQuery(N, claimLevels, valueArraySize) { + + // 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 + signal input claimPathMtpAuxHv[N]; // 0 for inclusion proof + signal input claimPathKey[N]; // hash of path in merklized json-ld document + 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]; + + // Outputs + signal output linkID; + signal output merklized; + signal output operatorOutput[N]; + signal output circuitQueryHash[N]; + + ///////////////////////////////////////////////////////////////// + // General verifications + ///////////////////////////////////////////////////////////////// + + // get safe one values to be used in ForceEqualIfEnabled + signal one <== SafeOne()(linkNonce); // 7 constraints + + // get claim header + component issuerClaimHeader = getClaimHeader(); // 300 constraints + issuerClaimHeader.claim <== issuerClaim; + + // get merklized flag & root + component merklize = getClaimMerklizeRoot(); + merklize.claim <== issuerClaim; + merklize.claimFlags <== issuerClaimHeader.claimFlags; + + merklized <== merklize.flag; + + // 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 + + ///////////////////////////////////////////////////////////////// + // Query Processing Loop + ///////////////////////////////////////////////////////////////// + for (var i=0; i