Skip to content

Commit

Permalink
Merge pull request #115 from iden3/feature/linked-proof-pocs
Browse files Browse the repository at this point in the history
Linked proof PoCs
  • Loading branch information
OBrezhniev authored Jan 26, 2024
2 parents 1462c0f + 44d399f commit 1a1f212
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 38 deletions.
92 changes: 92 additions & 0 deletions circuits/lib/query/processQueryWithModifiers.circom
Original file line number Diff line number Diff line change
@@ -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
]
);
}
107 changes: 107 additions & 0 deletions circuits/linked/multiQuery.circom
Original file line number Diff line number Diff line change
@@ -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<N; i++) {

// output value only if modifier operation was selected
operatorOutput[i] <== ProcessQueryWithModifiers(claimLevels, valueArraySize)(
enabled[i],
claimPathNotExists[i],
claimPathMtp[i],
claimPathMtpNoAux[i],
claimPathMtpAuxHi[i],
claimPathMtpAuxHv[i],
claimPathKey[i],
claimPathValue[i],
slotIndex[i],
operator[i],
value[i],
issuerClaim,
merklized,
merklize.out
);

/////////////////////////////////////////////////////////////////
// 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)([
claimSchema,
slotIndex[i],
operator[i],
claimPathKey[i],
claimPathNotExists[i],
valueHash[i]
]);

circuitQueryHash[i] <== Mux1()([0, queryHash[i]], enabled[i]);
}
}
70 changes: 70 additions & 0 deletions circuits/linked/nullifier.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
pragma circom 2.1.5;

include "../../node_modules/circomlib/circuits/comparators.circom";
include "../lib/linked/linkId.circom";
include "../lib/utils/nullify.circom";
include "../lib/utils/safeOne.circom";
include "../lib/utils/claimUtils.circom";

// This circuit generates nullifier for a given claim using linked proof
template LinkedNullifier(){

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

// nullifier signals
signal input userGenesisID;
signal input claimSubjectProfileNonce;
signal input claimSchema;
signal input verifierID;
signal input nullifierSessionID;

signal output nullifier;
signal output linkID;

// get safe one values to be used in ForceEqualIfEnabled
signal one <== SafeOne()(userGenesisID); // 7 constraints

////////////////////////////////////////////////////////////////////////
// verify nullifier signals
////////////////////////////////////////////////////////////////////////

component issuerClaimHeader = getClaimHeader(); // 300 constraints
issuerClaimHeader.claim <== issuerClaim;

// Verify issuerClaim schema
verifyCredentialSchema()(one, issuerClaimHeader.schema, claimSchema); // 3 constraints

// Check issuerClaim is issued to provided identity
verifyCredentialSubjectProfile()(
one,
issuerClaim,
issuerClaimHeader.claimFlags,
userGenesisID,
claimSubjectProfileNonce
); // 1236 constraints

signal issuerClaimHash, issuerClaimHi, issuerClaimHv;
(issuerClaimHash, issuerClaimHi, issuerClaimHv) <== getClaimHash()(issuerClaim); // 834 constraints

////////////////////////////////////////////////////////////////////////
// calculate linkID
////////////////////////////////////////////////////////////////////////
linkID <== LinkID()(issuerClaimHash, linkNonce); // 243 constraints

signal linkIDisNotZero <== NOT()(IsZero()(linkID));
ForceEqualIfEnabled()(one, [linkIDisNotZero, one]);

////////////////////////////////////////////////////////////////////////
// calculate nullifier
////////////////////////////////////////////////////////////////////////
nullifier <== Nullify()(
userGenesisID,
claimSubjectProfileNonce,
claimSchema,
verifierID,
nullifierSessionID
); // 330 constraints

}
5 changes: 5 additions & 0 deletions circuits/linkedMultiQuery10.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pragma circom 2.1.1;

include "linked/multiQuery.circom";

component main {public [enabled]} = LinkedMultiQuery(10, 32, 64); // 164791 constraints
56 changes: 18 additions & 38 deletions circuits/offchain/credentialAtomicQueryV3OffChain.circom
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ include "../../node_modules/circomlib/circuits/bitify.circom";
include "../../node_modules/circomlib/circuits/comparators.circom";
include "../auth/authV2.circom";
include "../lib/linked/linkId.circom";
include "../lib/query/comparators.circom";
include "../lib/query/modifiers.circom";
include "../lib/query/query.circom";
include "../lib/query/processQueryWithModifiers.circom";
include "../lib/utils/nullify.circom";
include "../lib/utils/idUtils.circom";
include "../lib/utils/safeOne.circom";
Expand Down Expand Up @@ -50,7 +48,6 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, valueArraySi
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];
Expand Down Expand Up @@ -237,30 +234,26 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, valueArraySi
);

/////////////////////////////////////////////////////////////////
// Query Operator Processing
// Process Query with Modifiers
/////////////////////////////////////////////////////////////////

// verify query
// 1756 constraints (Query+LessThan+ForceEqualIfEnabled)
signal querySatisfied <== Query(valueArraySize)(
in <== fieldValue,
value <== value,
operator <== operator
);

signal isQueryOp <== LessThan(5)([operator, 16]);
ForceEqualIfEnabled()(
isQueryOp,
[querySatisfied, 1]
// output value only if modifier operation was selected
operatorOutput <== ProcessQueryWithModifiers(claimLevels, valueArraySize)(
one,
claimPathNotExists,
claimPathMtp,
claimPathMtpNoAux,
claimPathMtpAuxHi,
claimPathMtpAuxHv,
claimPathKey,
claimPathValue,
slotIndex,
operator,
value,
issuerClaim,
merklized,
merklize.out
);

/////////////////////////////////////////////////////////////////
// Modifier/Computation Operators Processing
/////////////////////////////////////////////////////////////////

// selective disclosure
// no need to calc anything, fieldValue is just passed as an output

// nullifier calculation
nullifier <== Nullify()(
userGenesisID,
Expand All @@ -270,19 +263,6 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, valueArraySi
nullifierSessionID
); // 330 constraints

/////////////////////////////////////////////////////////////////
// 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
]
);

/////////////////////////////////////////////////////////////////
// ProfileID calculation
/////////////////////////////////////////////////////////////////
Expand Down

0 comments on commit 1a1f212

Please sign in to comment.