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

Linked proof PoCs #115

Merged
merged 11 commits into from
Jan 26, 2024
88 changes: 88 additions & 0 deletions circuits/lib/query/processQueryWithModifiers.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
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 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;

// check path/in node exists in merkletree specified by jsonldRoot
SMTVerifier(claimLevels)(
enabled <== merklized, // if merklize flag 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]);
ForceEqualIfEnabled()(
isQueryOp,
[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
]
);
}
100 changes: 100 additions & 0 deletions circuits/linked/multiQuery.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
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 generates nullifier for a given claim using linked proof
template LinkedMultiQuery(N, claimLevels, valueArraySize) {

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

// query signals
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 merklized;
signal output operatorOutput[N];
signal output circuitQueryHash[N];

/////////////////////////////////////////////////////////////////
// General verifications
/////////////////////////////////////////////////////////////////

// get safe one values to be used in ForceEqualIfEnabled
signal one <== SafeOne()(linkID); // 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 slotValue[N];
signal fieldValue[N];
signal querySatisfied[N];
signal isQueryOp[N];
signal valueHash[N];

/////////////////////////////////////////////////////////////////
// Query Processing Loop
/////////////////////////////////////////////////////////////////
for (var i=0; i<N; i++) {

// output value only if modifier operation was selected
operatorOutput[i] <== ProcessQueryWithModifiers(claimLevels, valueArraySize)(
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

circuitQueryHash[i] <== Poseidon(6)([
claimSchema,
slotIndex[i],
operator[i],
claimPathKey[i],
claimPathNotExists[i],
valueHash[i]
]);
}
}
71 changes: 71 additions & 0 deletions circuits/linked/nullifier.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
pragma circom 2.1.5;

include "../../node_modules/circomlib/circuits/comparators.circom";
include "../lib/linked/linkId.circom";
include "../lib/query/nullify.circom";
OBrezhniev marked this conversation as resolved.
Show resolved Hide resolved
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 linkID;
signal input linkNonce;
signal input issuerClaim[8];

// nullifier signals
signal input userGenesisID;
signal input claimSubjectProfileNonce;
signal input claimSchema;
signal input verifierID;
signal input verifierSessionID;
OBrezhniev marked this conversation as resolved.
Show resolved Hide resolved

signal output nullifier;

// 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

////////////////////////////////////////////////////////////////////////
// verify linkID
////////////////////////////////////////////////////////////////////////
signal calculatedLinkID <== LinkID()(issuerClaimHash, linkNonce); // 243 constraints
ForceEqualIfEnabled()(one, [calculatedLinkID, linkID]);

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

////////////////////////////////////////////////////////////////////////
// calculate nullifier
////////////////////////////////////////////////////////////////////////
nullifier <== Nullify()(
userGenesisID,
claimSubjectProfileNonce,
claimSchema,
verifierID,
verifierSessionID
OBrezhniev marked this conversation as resolved.
Show resolved Hide resolved
); // 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 [linkID]} = LinkedMultiQuery(10, 32, 64); // 163694 constraints
5 changes: 5 additions & 0 deletions circuits/linkedMultiQuery3.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 [linkID]} = LinkedMultiQuery(3, 32, 64); // 49300 constraints
5 changes: 5 additions & 0 deletions circuits/linkedNullifier.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pragma circom 2.1.1;

include "linked/nullifier.circom";

component main {public [linkID, verifierID, verifierSessionID]} = LinkedNullifier();
OBrezhniev marked this conversation as resolved.
Show resolved Hide resolved
55 changes: 17 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 @@ -231,30 +228,25 @@ 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)(
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 @@ -264,19 +256,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
Loading