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

value arr size input #121

Merged
merged 17 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ jobs:
- name: Setup Circom
run: wget https://github.com/iden3/circom/releases/latest/download/circom-linux-amd64 && sudo mv ./circom-linux-amd64 /usr/bin/circom && sudo chmod +x /usr/bin/circom

- name: Generate testvectors
run: cd testvectorgen && go test ./...

- name: Install node_modules
if: steps.modules-cache.outputs.cache-hit != 'true'
run: npm install
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ build
.vscode
.DS_Store
powersOfTau28_hez_final_17.ptau
testvectorgen/**/testdata
1 change: 1 addition & 0 deletions circuits/credentialAtomicQueryV3.circom
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ component main{public [requestID,
claimPathNotExists,
operator,
value,
valueArraySize,
timestamp,
isRevocationChecked,
proofType,
Expand Down
24 changes: 24 additions & 0 deletions circuits/lib/query/comparators.circom
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,30 @@ template IN (valueArraySize){
out <== isEq[valueArraySize];
}

// Same as IN but stops checking on valueArraySize
template InWithDynamicArraySize (maxValueArraySize){
signal input in;
signal input value[maxValueArraySize];
signal input valueArraySize;
signal output out;

assert(maxValueArraySize <= 256);

component eq[maxValueArraySize];
signal isEq[maxValueArraySize+1];
signal lt[maxValueArraySize];
isEq[0] <== 0;
for (var i=0; i<maxValueArraySize; i++) {
lt[i] <== LessThan(8)([i, valueArraySize]);
eq[i] = IsEqual();
eq[i].in[0] <== in;
eq[i].in[1] <== value[i];
isEq[i+1] <== OR()(isEq[i], AND()(eq[i].out, lt[i]));
}

out <== isEq[maxValueArraySize];
}

// As LessThan but for all possible numbers from field (not only 252-bit-max like LessThan)
template LessThan254() {
signal input in[2];
Expand Down
21 changes: 18 additions & 3 deletions circuits/lib/query/processQueryWithModifiers.circom
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ include "../../../node_modules/circomlib/circuits/comparators.circom";
include "query.circom";
include "modifiers.circom";
include "../utils/claimUtils.circom";
include "../utils/arraySizeValidator.circom";

template ProcessQueryWithModifiers(claimLevels, valueArraySize){
template ProcessQueryWithModifiers(claimLevels, maxValueArraySize){
signal input enabled;
signal input claimPathNotExists; // 0 for inclusion, 1 for non-inclusion
signal input claimPathMtp[claimLevels];
Expand All @@ -16,7 +17,8 @@ template ProcessQueryWithModifiers(claimLevels, valueArraySize){
signal input claimPathValue; // value in this path in merklized json-ld document
signal input slotIndex;
signal input operator;
signal input value[valueArraySize];
signal input value[maxValueArraySize];
signal input valueArraySize;

signal input issuerClaim[8];
signal input merklized;
Expand Down Expand Up @@ -55,11 +57,24 @@ template ProcessQueryWithModifiers(claimLevels, valueArraySize){
// Query Operator Processing
/////////////////////////////////////////////////////////////////

// verify value array length
// 802 constraints (ArraySizeValidator+ForceEqualIfEnabled)
signal arrSizeSatisfied <== ArraySizeValidator(maxValueArraySize)(
valueArraySize <== valueArraySize,
operator <== operator
);

ForceEqualIfEnabled()(
enabled,
[arrSizeSatisfied, 1]
);

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

Expand Down
12 changes: 7 additions & 5 deletions circuits/lib/query/query.circom
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ include "comparators.circom";
7 - less than or equal
8 - greater than or equal
9 - between
10 - not between
Modifier/computation operators:
16 - selective disclosure (16 = 10000 binary)
*/

// Query template works only with Query operators (0-15), for the rest returns 0
template Query (valueArraySize) {
template Query (maxValueArraySize) {
// signals
signal input in;
signal input value[valueArraySize];
signal input value[maxValueArraySize];
signal input valueArraySize;
signal input operator;
signal output out;

Expand All @@ -46,8 +48,8 @@ template Query (valueArraySize) {
// gte
signal gte <== NOT()(lt); // gte === !lt

// in
signal inComp <== IN(valueArraySize)(in, value);
// in w/o - 65668, IN - 65860( 192), InWithDynamicArraySize - 66372 (704)
signal inComp <== InWithDynamicArraySize(maxValueArraySize)(in, value, valueArraySize);

// between (value[0] <= in <= value[1])
signal gt2 <== GreaterThan254()([in, value[1]]);
Expand All @@ -73,7 +75,7 @@ template Query (valueArraySize) {
queryOpSatisfied.c[7] <== lte; // lte === !gt
queryOpSatisfied.c[8] <== gte; // gte === !lt
queryOpSatisfied.c[9] <== between; // between
queryOpSatisfied.c[10] <== 0; // not used
queryOpSatisfied.c[10] <== NOT()(between); // not between
queryOpSatisfied.c[11] <== 0; // not used
queryOpSatisfied.c[12] <== 0; // not used
queryOpSatisfied.c[13] <== 0; // not used
Expand Down
79 changes: 79 additions & 0 deletions circuits/lib/utils/arraySizeValidator.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
pragma circom 2.1.1;
include "../../../node_modules/circomlib/circuits/mux1.circom";
include "../../../node_modules/circomlib/circuits/mux4.circom";
include "../../../node_modules/circomlib/circuits/bitify.circom";
include "../../../node_modules/circomlib/circuits/comparators.circom";
include "../../../node_modules/circomlib/circuits/gates.circom";
include "../query/comparators.circom";
/*
Operators:
Query operators - valueArraySize
0 - noop - 0 elements
1 - equals - 1 element
2 - less than - 1 element
3 - greater than - 1 element
4 - in - less or eq than maxValueArraySize
5 - not in - less or eq than maxValueArraySize
6 - not equals - 1 element
7 - less than or equal - 1 element
8 - greater than or equal - 1 element
9 - between - 2 elements
10 - not between - 2 elements
Modifier/computation operators:
16 - selective disclosure (16 = 10000 binary) - 0 elements
17-31 - 0 elements
*/

// ArraySizeValidator template check valueArraySize for query operators
template ArraySizeValidator (maxValueArraySize) {
// signals
signal input valueArraySize;
signal input operator;
signal output out;

signal sizeEqZero <== IsEqual()([valueArraySize, 0]);
signal sizeEqOne <== IsEqual()([valueArraySize, 1]);
signal sizeEqTwo <== IsEqual()([valueArraySize, 2]);
signal sizeLessOrEqMax <== LessThan(8)([valueArraySize, maxValueArraySize + 1]);

signal sizeNotEqZero <== NOT()(sizeEqZero);
signal moreThanZeroLessOrEqMax <== AND()(sizeNotEqZero, sizeLessOrEqMax);

signal opBits[5] <== Num2Bits(5)(operator); // values 0-15 are query operators, 16-31 - modifiers/computations

assert(maxValueArraySize <= 256);

// query operator mux
component mux = Mux4();
mux.s <== [opBits[0], opBits[1], opBits[2], opBits[3]];

// We don't use 5th bit (opBits[4]) here; which specifies whether operator is query or
// modifier/computation operator. It's used in the final mux.
_ <== opBits[4];
OBrezhniev marked this conversation as resolved.
Show resolved Hide resolved

mux.c[0] <== sizeEqZero; // noop; skip execution
mux.c[1] <== sizeEqOne; // equals
mux.c[2] <== sizeEqOne; // lt
mux.c[3] <== sizeEqOne; // gt
mux.c[4] <== moreThanZeroLessOrEqMax; // in
mux.c[5] <== moreThanZeroLessOrEqMax; // nin
mux.c[6] <== sizeEqOne; // neq
mux.c[7] <== sizeEqOne; // lte
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[13] <== sizeEqZero; // not used
mux.c[14] <== sizeEqZero; // not used
mux.c[15] <== sizeEqZero; // not used

// final output mux
out <== Mux1()(
s <== opBits[4], // specifies whether operator is query or modifier/computation operator
c <== [mux.out, sizeEqZero]
);

}


12 changes: 7 additions & 5 deletions circuits/offchain/credentialAtomicQueryV3OffChain.circom
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ include "../lib/utils/nullify.circom";
include "../lib/utils/idUtils.circom";
include "../lib/utils/safeOne.circom";

template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, valueArraySize) {
template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, maxValueArraySize) {
// common outputs for Sig and MTP
signal output merklized;
signal output userID;
Expand Down Expand Up @@ -50,8 +50,8 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, valueArraySi
signal input claimPathValue; // value in this path in merklized json-ld document
signal input slotIndex;
signal input operator;
signal input value[valueArraySize];

signal input value[maxValueArraySize];
signal input valueArraySize;
signal input issuerClaim[8];

// MTP specific
Expand Down Expand Up @@ -210,8 +210,9 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, valueArraySi
merklized <== merklize.flag;

// check path/in node exists in merkletree specified by jsonldRoot
signal operatorNotNoop <== NOT()(IsZero()(operator));
SMTVerifier(claimLevels)(
enabled <== merklize.flag, // if merklize flag 0 skip MTP verification
enabled <== AND()(merklize.flag, operatorNotNoop), // if merklize flag 0 or NOOP operator skip MTP verification
fnc <== claimPathNotExists, // inclusion
root <== merklize.out,
siblings <== claimPathMtp,
Expand All @@ -237,7 +238,7 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, valueArraySi
// Process Query with Modifiers
/////////////////////////////////////////////////////////////////
// output value only if modifier operation was selected
operatorOutput <== ProcessQueryWithModifiers(claimLevels, valueArraySize)(
operatorOutput <== ProcessQueryWithModifiers(claimLevels, maxValueArraySize)(
one,
claimPathNotExists,
claimPathMtp,
Expand All @@ -249,6 +250,7 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, valueArraySi
slotIndex,
operator,
value,
valueArraySize,
issuerClaim,
merklized,
merklize.out
Expand Down
17 changes: 10 additions & 7 deletions circuits/onchain/credentialAtomicQueryV3OnChain.circom
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ checks:
idOwnershipLevels - Merkle tree depth level for personal claims
issuerLevels - Merkle tree depth level for claims issued by the issuer
claimLevels - Merkle tree depth level for claim JSON-LD document
valueArraySize - Number of elements in comparison array for in/notin operation if level = 3 number of values for
maxValueArraySize - Number of elements in comparison array for in/notin operation if level = 3 number of values for
comparison ["1", "2", "3"]
idOwnershipLevels - Merkle tree depth level for personal claims
onChainLevels - Merkle tree depth level for Auth claim on-chain
*/
template credentialAtomicQueryV3OnChain(issuerLevels, claimLevels, valueArraySize, idOwnershipLevels, onChainLevels) {
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
Expand Down Expand Up @@ -123,7 +123,8 @@ template credentialAtomicQueryV3OnChain(issuerLevels, claimLevels, valueArraySiz

signal input slotIndex;
signal input operator;
signal input value[valueArraySize];
signal input value[maxValueArraySize];
signal input valueArraySize;

// MTP specific
signal input issuerClaimMtp[issuerLevels];
Expand Down Expand Up @@ -203,7 +204,7 @@ template credentialAtomicQueryV3OnChain(issuerLevels, claimLevels, valueArraySiz
// Claim checks
/////////////////////////////////////////////////////////////////

(merklized, userID, issuerState, linkID, nullifier, operatorOutput) <== credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, valueArraySize)(
(merklized, userID, issuerState, linkID, nullifier, operatorOutput) <== credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, maxValueArraySize)(
proofType <== proofType,
requestID <== requestID,
userGenesisID <== userGenesisID,
Expand Down Expand Up @@ -231,6 +232,7 @@ template credentialAtomicQueryV3OnChain(issuerLevels, claimLevels, valueArraySiz
slotIndex <== slotIndex,
operator <== operator,
value <== value,
valueArraySize <== valueArraySize,
issuerClaim <== issuerClaim,
issuerClaimMtp <== issuerClaimMtp,
issuerClaimClaimsTreeRoot <== issuerClaimClaimsTreeRoot,
Expand Down Expand Up @@ -258,14 +260,15 @@ template credentialAtomicQueryV3OnChain(issuerLevels, claimLevels, valueArraySiz
/////////////////////////////////////////////////////////////////
// Verify query hash matches
/////////////////////////////////////////////////////////////////
signal valueHash <== SpongeHash(valueArraySize, 6)(value); // 6 - max size of poseidon hash available on-chain
signal valueHash <== SpongeHash(maxValueArraySize, 6)(value); // 6 - max size of poseidon hash available on-chain

circuitQueryHash <== Poseidon(6)([
circuitQueryHash <== Poseidon(7)([
claimSchema,
slotIndex,
operator,
claimPathKey,
claimPathNotExists,
valueHash
valueHash,
valueArraySize
]);
}
5 changes: 5 additions & 0 deletions test/circuits/utils/utils_arraySizeValidatorTest.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pragma circom 2.1.1;

include "../../../circuits/lib/utils/arraySizeValidator.circom";

component main = ArraySizeValidator(64);
23 changes: 22 additions & 1 deletion test/offchain/credentialAtomicQueryV3OffChain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ describe("Test credentialAtomicQueryV3OffChain.circom", function () {
require(`${sigBasePath}/nullify.json`),
require(`${sigBasePath}/revoked_claim_without_revocation_check.json`),
require(`${sigBasePath}/jsonld_non_inclusion.json`),
require(`${sigBasePath}/noop_operator.json`),
require(`${sigBasePath}/not_between_operator.json`),
require(`${sigBasePath}/in_operator.json`),

// mtp
require(`${mtpBasePath}/claimIssuedOnProfileID.json`),
Expand All @@ -54,6 +57,9 @@ describe("Test credentialAtomicQueryV3OffChain.circom", function () {
require(`${mtpBasePath}/selective_disclosure.json`),
require(`${mtpBasePath}/nullify.json`),
require(`${mtpBasePath}/revoked_claim_without_revocation_check.json`),
require(`${mtpBasePath}/noop_operator.json`),
require(`${mtpBasePath}/not_between_operator.json`),
require(`${mtpBasePath}/in_operator.json`),
];

tests.forEach(({ desc, inputs, expOut }) => {
Expand All @@ -67,7 +73,7 @@ describe("Test credentialAtomicQueryV3OffChain.circom", function () {
const failTestCase = [
require(`${sigBasePath}/revoked_claim_with_revocation_check.json`),
require(`${mtpBasePath}/revoked_claim_with_revocation_check.json`),
]
];

failTestCase.forEach(({ desc, inputs, expOut }) => {
it(`${desc}`, async function () {
Expand All @@ -78,4 +84,19 @@ describe("Test credentialAtomicQueryV3OffChain.circom", function () {
expect(error.message).to.include("Error in template checkClaimNotRevoked");
})
});

const failInTestCase = [
require(`${sigBasePath}/in_operator_failed_0.json`),
require(`${mtpBasePath}/in_operator_failed_0.json`),
];

failInTestCase.forEach(({ desc, inputs, expOut }) => {
it(`${desc}`, async function () {
let error;
await circuit.calculateWitness(inputs, true).catch((err) => {
error = err;
});
expect(error.message).to.include("Error in template ProcessQueryWithModifiers");
})
});
});
Loading
Loading